DCAEMOD updates for J release
[dcaegen2.git] / docs / sections / sdk / apis.rst
1 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
2 .. http://creativecommons.org/licenses/by/4.0
3
4 Available APIs
5 ==============
6
7
8 .. toctree::
9     :depth: 3
10
11 .. _config_binding_service_sdk:
12
13 cbs-client - a Config Binding Service client
14 --------------------------------------------
15 CbsClientFactory can be used to lookup for CBS in your application. Returned CbsClient can then be used to get a configuration, poll for configuration or poll for configuration changes.
16
17 The following CBS endpoints are supported by means of different CbsRequests:
18  - get-configuration created by CbsRequests.getConfiguration method - returns the service configuration
19  - get-by-key created by CbsRequests.getByKey method - returns componentName:key entry from Consul
20  - get-all created by CbsRequests.getAll method - returns everything which relates to the service (configuration, policies, etc.)
21
22 Sample usage:
23
24 .. code-block:: java
25
26     // Generate RequestID and InvocationID which will be used when logging and in HTTP requests
27     final RequestDiagnosticContext diagnosticContext = RequestDiagnosticContext.create();
28     final CbsRequest request = CbsRequests.getConfiguration(diagnosticContext);
29
30     // Read necessary properties from the environment
31     final CbsClientConfiguration config = CbsClientConfiguration.fromEnvironment();
32
33     // Create the client and use it to get the configuration
34     CbsClientFactory.createCbsClient(config)
35             .flatMap(cbsClient -> cbsClient.get(request))
36             .subscribe(
37                     jsonObject -> {
38                         // do a stuff with your JSON configuration using GSON API
39                         final int port = Integer.parseInt(jsonObject.get("collector.listen_port").getAsString());
40                         // ...
41                     },
42                     throwable -> {
43                         logger.warn("Ooops", throwable);
44                     });
45
46
47 Note that a subscribe handler can/will be called in separate thread asynchronously after CBS address lookup succeeds and CBS service call returns a result.
48
49 If you are interested in calling CBS periodically and react only when the configuration changed you can use updates method:
50
51 .. code-block:: java
52
53     // Generate RequestID and InvocationID which will be used when logging and in HTTP requests
54     final RequestDiagnosticContext diagnosticContext = RequestDiagnosticContext.create();
55     final CbsRequest request = CbsRequests.getConfiguration(diagnosticContext);
56
57     // Read necessary configuration from the environment
58     final CbsClientConfiguration config = CbsClientConfiguration.fromEnvironment();
59
60     // Polling properties
61     final Duration initialDelay = Duration.ofSeconds(5);
62     final Duration period = Duration.ofMinutes(1);
63
64     // Create the client and use it to get the configuration
65     CbsClientFactory.createCbsClient(config)
66             .flatMapMany(cbsClient -> cbsClient.updates(request, initialDelay, period))
67             .subscribe(
68                     jsonObject -> {
69                         // do a stuff with your JSON configuration using GSON API
70                         final int port = Integer.parseInt(jsonObject.get("collector.listen_port").getAsString());
71                         // ...
72                     },
73                     throwable -> {
74                         logger.warn("Ooops", throwable);
75                     });
76
77 The most significant change is in line 14. We are using flatMapMany since we want to map one CbsClient to many JsonObject updates. After 5 seconds CbsClient will call CBS every minute. If the configuration has changed it will pass the JsonObject downstream - in our case consumer of JsonObject will be called.
78
79 Parsing streams' definitions:
80
81 - CBS configuration response contains various service-specific entries. It also contains a standardized DCAE streams definitions as streams_publishes and streams_subscribes JSON objects. CBS Client API provides a way of parsing this part of configuration so you can use Java objects instead of low-level GSON API.
82 - Because streams definitions are a simple value objects we were not able to provide you a nice polymorphic API. Instead you have 2-level API at your disposal:
83     - You can extract raw streams  by means of DataStreams.namedSinks (for streams_publishes) and DataStreams.namedSources (for streams_subscribes).
84     - Then you will be able to parse the specific entry from returned collection to a desired stream type by means of parsers built by StreamFromGsonParsers factory.
85
86 - Sample usage:
87
88     .. code-block:: java
89
90         final CbsRequest request = CbsRequests.getConfiguration(RequestDiagnosticContext.create());
91         final StreamFromGsonParser<MessageRouterSink> mrSinkParser = StreamFromGsonParsers.messageRouterSinkParser();
92
93         CbsClientFactory.createCbsClient(CbsClientConfiguration.fromEnvironment())
94             .flatMapMany(cbsClient -> cbsClient.updates(request, Duration.ofSeconds(5), Duration.ofMinutes(1)))
95             .map(DataStreams::namedSinks)
96             .map(sinks -> sinks.filter(StreamPredicates.streamOfType(MESSAGE_ROUTER)).map(mrSinkParser::unsafeParse).toList())
97             .subscribe(
98                 mrSinks -> mrSinks.forEach(mrSink -> {
99                     logger.info(mrSink.name()); // name = the configuration key
100                     logger.info(mrSink.aafCredentials().username()); // = aaf_username
101                     logger.info(mrSink.topicUrl());
102                     // ...
103                 }),
104                 throwable -> logger.warn("Ooops", throwable)
105         );
106
107     For details and sample usage please refer to JavaDoc and unit and integration tests. Especially `CbsClientImplIT <https://gerrit.onap.org/r/gitweb?p=dcaegen2/services/sdk.git;a=blob;f=rest-services/cbs-client/src/test/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/impl/CbsClientImplIT.java;hb=HEAD>`_, `MessageRouterSinksIT <https://gerrit.onap.org/r/gitweb?p=dcaegen2/services/sdk.git;a=blob;f=rest-services/cbs-client/src/test/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/streams/MessageRouterSinksIT.java;hb=HEAD>`_ and  `MixedDmaapStreamsIT <https://gerrit.onap.org/r/gitweb?p=dcaegen2/services/sdk.git;a=blob;f=rest-services/cbs-client/src/test/java/org/onap/dcaegen2/services/sdk/rest/services/cbs/client/api/streams/MixedDmaapStreamsIT.java;hb=HEAD>`_ might be useful.
108
109 - INFO
110     Results of these parsers (MessageRouterSink, MessageRouterSource) can be directly used to connect to DMaaP MR by means of dmaap-client API described below.
111
112 crypt-password - an utility for BCrypt passwords
113 ------------------------------------------------
114 Library to generate and match cryptography password using BCrypt algorithm
115
116 .. code-block:: java
117
118     java -jar crypt-password-${sdk.version}.jar password_to_crypt
119
120     $2a$10$iDEKdKknakPqH5XZb6wEmeBP2SMRwwiWHy8RNioUTNycIomjIqCAO
121
122 Can be used like maven dependency to match generated password.
123
124 dmaap-client - a DMaaP MR client
125 --------------------------------
126 After parsing CBS sink definitions you will get a Source or Sink value object. It can be then directly used to communicate with DMaaP Message Router REST API.
127
128 Writing message publisher
129
130 .. code-block:: java
131
132     final MessageRouterPublisher publisher = DmaapClientFactory.createMessageRouterPublisher();
133     final MessageRouterSink sinkDefinition; //... Sink definition obtained by parsing CBS response
134     final MessageRouterPublishRequest request = ImmutableMessageRouterPublishRequest.builder()
135             .sinkDefinition(sinkDefinition)
136             .build();
137
138     Flux.just(1, 2, 3)
139             .map(JsonPrimitive::new)
140             .transform(input -> publisher.put(request, input))
141             .subscribe(resp -> {
142                         if (resp.successful()) {
143                             logger.debug("Sent a batch of messages to the MR");
144                         } else {
145                             logger.warn("Message sending has failed: {}", resp.failReason());
146                         }
147                     },
148                     ex -> {
149                         logger.warn("An unexpected error while sending messages to DMaaP", ex);
150                     });
151
152 Note that we are using Reactor transform operator. As an alternative you could assign Flux of JSON values to the variable and then invoke publisher.put on it. The important performance-related thing to remember is that you should feed the put method with a stream of messages instead of multiple calls with single messages. This way the client API will be able to send them in batches which should significantly improve performance (at least on transfer level).
153
154 Writing message subscriber
155
156 .. code-block:: java
157
158     final MessageRouterSource sourceDefinition; //... Source definition obtained by parsing CBS response
159     final MessageRouterSubscribeRequest request = ImmutableMessageRouterSubscribeRequest.builder()
160             .sourceDefinition(sourceDefinition)
161             .build();
162
163     cut.subscribeForElements(request, Duration.ofMinutes(1))
164             .map(JsonElement::getAsJsonObject)
165             .subscribe(json -> {
166                     // application logic
167                 },
168                 ex -> {
169                     logger.warn("An unexpected error while receiving messages from DMaaP", ex);
170                 });
171
172 ******************************************
173 Configure timeout when talking to DMaaP-MR
174 ******************************************
175
176 * publisher:
177
178 .. code-block:: java
179
180    final MessageRouterPublishRequest request = ImmutableMessageRouterPublishRequest.builder()
181                 .timeoutConfig(ImmutableDmaapTimeoutConfig.builder()
182                         .timeout(Duration.ofSeconds(2))
183                         .build())
184                 .
185                 .
186                 .
187                 .build();
188
189 * subscriber:
190
191 .. code-block:: java
192
193   final MessageRouterSubscribeRequest request = ImmutableMessageRouterSubscribeRequest.builder()
194                 .timeoutConfig(ImmutableDmaapTimeoutConfig.builder()
195                         .timeout(Duration.ofSeconds(2))
196                         .build())
197                 .
198                 .
199                 .
200                 .build();
201
202 The default timeout value (4 seconds) can be used:
203
204 .. code-block:: java
205
206    ImmutableDmaapTimeoutConfig.builder().build()
207
208 For timeout exception following message is return as failReason in DmaapResponse:
209
210 .. code-block:: RST
211
212    408 Request Timeout
213    {"requestError":{"serviceException":{"messageId":"SVC0001","text":"Client timeout exception occurred, Error code is %1","variables":["408"]}}}
214
215 *************************
216 Configure retry mechanism
217 *************************
218
219 * publisher:
220
221 .. code-block:: java
222
223        final MessageRouterPublisherConfig config = ImmutableMessageRouterPublisherConfig.builder()
224                 .retryConfig(ImmutableDmaapRetryConfig.builder()
225                         .retryIntervalInSeconds(2)
226                         .retryCount(2)
227                         .build())
228                 .
229                 .
230                 .
231                 .build();
232        final MessageRouterPublisher publisher = DmaapClientFactory.createMessageRouterPublisher(config);
233
234 * subscriber:
235
236 .. code-block:: java
237
238     final MessageRouterSubscriberConfig config = ImmutableMessageRouterSubscriberConfig.builder()
239                 .retryConfig(ImmutableDmaapRetryConfig.builder()
240                         .retryIntervalInSeconds(2)
241                         .retryCount(2)
242                         .build())
243                 .
244                 .
245                 .
246                 .build();
247     final MessageRouterSubscriber subscriber = DmaapClientFactory.createMessageRouterSubscriber(config);
248
249 The default retry config (retryCount=3, retryIntervalInSeconds=1) can be used:
250
251 .. code-block:: java
252
253     ImmutableDmaapRetryConfig.builder().build()
254
255 Retry functionality works for:
256  - DMaaP MR HTTP response status codes: 404, 408, 413, 429, 500, 502, 503, 504
257  - Java Exception classes: ReadTimeoutException, ConnectException
258
259 **************************************
260 Configure custom persistent connection
261 **************************************
262
263 * publisher:
264
265 .. code-block:: java
266
267        final MessageRouterPublisherConfig connectionPoolConfiguration = ImmutableMessageRouterPublisherConfig.builder()
268                  .connectionPoolConfig(ImmutableDmaapConnectionPoolConfig.builder()
269                         .connectionPool(16)
270                         .maxIdleTime(10) //in seconds
271                         .maxLifeTime(20) //in seconds
272                         .build())
273                 .build();
274        final MessageRouterPublisher publisher = DmaapClientFactory.createMessageRouterPublisher(connectionPoolConfiguration);
275
276 * subscriber:
277
278 .. code-block:: java
279
280     final MessageRouterSubscriberConfig connectionPoolConfiguration = ImmutableMessageRouterSubscriberConfig.builder()
281                     .connectionPoolConfig(ImmutableDmaapConnectionPoolConfig.builder()
282                         .connectionPool(16)
283                         .maxIdleTime(10) //in seconds
284                         .maxLifeTime(20) //in seconds
285                         .build())
286                 .build();
287     final MessageRouterSubscriber subscriber = DmaapClientFactory.createMessageRouterSubscriber(connectionPoolConfiguration);
288
289 The default custom persistent connection configuration (connectionPool=16, maxLifeTime=2147483647, maxIdleTime=2147483647) can be used:
290
291 .. code-block:: java
292
293     ImmutableDmaapConnectionPoolConfig.builder().build()
294
295 ***************************************
296 Configure request for authorized topics
297 ***************************************
298
299 * publisher:
300
301 .. code-block:: java
302
303     final MessageRouterSink sink = ImmutableMessageRouterSink.builder()
304                 .aafCredentials(ImmutableAafCredentials.builder()
305                         .username("username")
306                         .password("password").build())
307                 .
308                 .
309                 .
310                 .build();
311
312     final MessageRouterPublishRequest request = ImmutableMessageRouterPublishRequest.builder()
313                 .sinkDefinition(sink)
314                 .
315                 .
316                 .
317                 .build();
318
319 * subscriber:
320
321 .. code-block:: java
322
323     final MessageRouterSource sourceDefinition = ImmutableMessageRouterSource.builder()
324                 .aafCredentials(ImmutableAafCredentials.builder()
325                         .username("username")
326                         .password("password")
327                         .build())
328                 .
329                 .
330                 .
331                 .build();
332
333     final MessageRouterSubscribeRequest request = ImmutableMessageRouterSubscribeRequest.builder()
334                 .sourceDefinition(sourceDefinition)
335                 .
336                 .
337                 .
338                 .build();
339
340 AAF Credentials are optional for subscribe/publish requests.
341 Username and password are used for basic authentication header during sending HTTP request to dmaap-mr.
342
343 hvvesclient-producer - a reference Java implementation of High Volume VES Collector client
344 ------------------------------------------------------------------------------------------
345 This library is used in xNF simulator which helps us test HV VES Collector in CSIT tests. You may use it as a reference when implementing your code in non-JVM language or directly when using Java/Kotlin/etc.
346
347 Sample usage:
348
349 .. code-block:: java
350
351     final ProducerOptions producerOptions = ImmutableProducerOptions.builder()
352             .collectorAddresses(HashSet.of(
353                     InetSocketAddress.createUnresolved("dcae-hv-ves-collector", 30222)))
354             .build();
355     final HvVesProducer hvVesProducer = HvVesProducerFactory.create(producerOptions);
356
357     Flux<VesEvent> events; // ...
358     Mono.from(hvVesProducer.send(events))
359             .doOnSuccess(() -> logger.info("All events has been sent"))
360             .doOnError(ex -> logger.warn("Failed to send one or more events", ex))
361             .subscribe();
362
363 external-schema-manager - JSON Validator with schema mapping
364 ------------------------------------------------------------
365 This library can be used to validate any JSON content incoming as JsonNode. What differs it from other validation
366 libraries is mapping of externally located schemas to local schema files.
367
368 Validated JSON must have one field that will refer to an external schema, which will be mapped to local file and then
369 validation of any chosen part of JSON is executed using local schema.
370
371 Mapping file is cached on validator creation, so it's not read every time validation is performed.
372 Schemas' content couldn't be cached due to external library restrictions (OpenAPI4j).
373
374 Example JSON:
375
376 .. code-block:: json
377
378     {
379         "schemaReference": "https://forge.3gpp.org/rep/sa5/data-models/blob/REL-16/OpenAPI/faultMnS.yaml",
380         "data":
381         {
382             "exampleData: "SAMPLE_VALUE"
383         }
384     }
385
386 Interface:
387
388 There are two methods, that are interface of this sub-project.
389
390 Validator builder:
391
392 .. code-block:: java
393
394     new StndDefinedValidator.ValidatorBuilder()
395             .mappingFilePath(mappingFilePath)
396             .schemasPath(schemasPath)
397             .schemaRefPath(schemaRefPath)
398             .stndDefinedDataPath(stndDefinedDataPath)
399             .build();
400
401
402 Validator usage:
403
404 .. code-block:: java
405
406     stndDefinedValidator.validate(event);
407
408 There are 4 string parameters of the builder:
409
410 .. csv-table:: **String parameters of the builder**
411     :header: "Name", "Description", "Example", "Note"
412     :widths: 10, 20, 20, 20
413
414     "mappingFilePath", "This should be a local filesystem path to file with mappings of public URLs to local URLs. Format of the schema mapping file is a JSON file with list of mappings", "etc/externalRepo/schema-map.json", " "
415     "schemasPath", "Schemas path is a directory under which external-schema-manager will search for local schemas", "./etc/externalRepo/ and first mapping from example mappingFile is taken, validator will look for schema under the path: ./etc/externalRepo/3gpp/rep/sa5/data-models/blob/REL-16/OpenAPI/faultMnS.yaml", " "
416     "schemaRefPath", "This is an internal path from validated JSON. It should define which field will be taken as public schema reference, which is later mapped", "/event/stndDefinedFields/schemaReference", "In SDK version 1.4.2 this path doesn't use JSON path notation (with . signs). It might change in further versions"
417     "stndDefinedDataPath", "This is path to stndDefined data in JSON. This fields will be validated during stndDefined validation.", "/event/stndDefinedFields/data", "In SDK version 1.4.2 this path doesn't use JSON path notation (with . signs). It might change in further versions."
418
419
420 Format of the schema mapping file is a JSON file with list of mappings, as shown in the example below.
421
422 .. code-block:: json
423
424     [
425         {
426             "publicURL": "https://forge.3gpp.org/rep/sa5/data-models/blob/REL-16/OpenAPI/faultMnS.yaml",
427             "localURL": "3gpp/rep/sa5/data-models/blob/REL-16/OpenAPI/faultMnS.yaml"
428         },
429         {
430             "publicURL": "https://forge.3gpp.org/rep/sa5/data-models/blob/REL-16/OpenAPI/heartbeatNtf.yaml",
431             "localURL": "3gpp/rep/sa5/data-models/blob/REL-16/OpenAPI/heartbeatNtf.yaml"
432         },
433         {
434             "publicURL": "https://forge.3gpp.org/rep/sa5/data-models/blob/REL-16/OpenAPI/PerDataFileReportMnS.yaml",
435             "localURL": "3gpp/rep/sa5/data-models/blob/REL-16/OpenAPI/PerDataFileReportMnS.yaml"
436         },
437         {
438             "publicURL": "https://forge.3gpp.org/rep/sa5/data-models/blob/master/OpenAPI/provMnS.yaml",
439             "localURL": "3gpp/rep/sa5/data-models/blob/REL-16/OpenAPI/provMnS.yaml"
440         }
441     ]
442
443 **Possible scenarios when using external-schema-manger:**
444
445 When the schema-map file, the schema and the sent event are correct, then the validation is successful and the log
446 shows "Validation of stndDefinedDomain has been successful".
447
448 Errors in the schema-map - none of the mappings are cached:
449
450 - When no schema-map file exists, "Unable to read mapping file. Mapping file path: {}" is logged.
451 - When a schema-map file exists, but has an incorrect format, a warning is logged: "Schema mapping file has incorrect format. Mapping file path: {}"
452
453 Errors in one of the mappings in schema-map - this mapping is not cached, a warning is logged "Mapping for publicURL ({}) will not be cached to validator.":
454
455 - When the local url in the schema-map file references a file that does not exist, the warning "Local schema resource missing. Schema file with path {} has not been found."
456 - When the schema file is empty, the information "Schema file is empty. Schema path: {}" is logged
457 - When a schema file has an incorrect format (not a yaml), the following information is logged: Schema has incorrect YAML structure. Schema path: {} "
458
459 Errors in schemaRefPath returns errors:
460
461 - If in the schemaRef path in the event we provide an url that refers to an existing schema, but the part after # refers to a non-existent part of it, then an error is thrown: IncorrectInternalFileReferenceException ("Schema reference refer to existing file, but internal reference (after #) is incorrect.") "
462 - When in the schemaRef path in the event, we provide a url that refers to a non-existent mapping from public to localURL, a NoLocalReferenceException is thrown, which logs to the console: "Couldn't find mapping for public url. PublicURL: {}"