Merge "Fixed broken unittest in policy agent"
diff --git a/enrichment-coordinator-service/config/README b/enrichment-coordinator-service/config/README
new file mode 100644
index 0000000..140927f
--- /dev/null
+++ b/enrichment-coordinator-service/config/README
@@ -0,0 +1,41 @@
+The keystore.jks and truststore.jks files are created by using the following commands (note that this is an example):
+
+1) Create a CA certificate and a private key:
+
+openssl genrsa -des3 -out CA-key.pem 2048
+openssl req -new -key CA-key.pem -x509 -days 1000 -out CA-cert.pem
+
+2) Create a keystore with a private key entry that is signed by the CA:
+
+keytool -genkeypair -alias policy_agent -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 3650 -storepass policy_agent
+keytool -certreq -alias policy_agent -file request.csr -keystore keystore.jks -ext san=dns:your.domain.com -storepass policy_agent
+openssl x509 -req -days 365 -in request.csr -CA CA-cert.pem -CAkey CA-key.pem -CAcreateserial -out ca_signed-cert.pem
+keytool -importcert -alias ca_cert -file CA-cert.pem -keystore keystore.jks -trustcacerts -storepass policy_agent
+keytool -importcert -alias policy_agent -file ca_signed-cert.pem -keystore keystore.jks -trustcacerts -storepass policy_agent
+
+
+3) Create a trust store containing the CA cert (to trust all certs signed by the CA):
+
+keytool -genkeypair -alias not_used -keyalg RSA -keysize 2048 -keystore truststore.jks -validity 3650 -storepass policy_agent
+keytool -importcert -alias ca_cert -file CA-cert.pem -keystore truststore.jks -trustcacerts -storepass policy_agent
+
+
+4) Command for listing of the contents of jks files, examples:
+keytool -list -v -keystore keystore.jks -storepass policy_agent
+keytool -list -v -keystore truststore.jks -storepass policy_agent
+
+## License
+
+Copyright (C) 2020 Nordix Foundation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
diff --git a/enrichment-coordinator-service/config/application.yaml b/enrichment-coordinator-service/config/application.yaml
new file mode 100644
index 0000000..6279a3e
--- /dev/null
+++ b/enrichment-coordinator-service/config/application.yaml
@@ -0,0 +1,37 @@
+spring:
+ profiles:
+ active: prod
+ main:
+ allow-bean-definition-overriding: true
+ aop:
+ auto: false
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "loggers,logfile,health,info,metrics,threaddump,heapdump"
+
+logging:
+ level:
+ ROOT: ERROR
+ org.springframework: ERROR
+ org.springframework.data: ERROR
+ org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR
+ org.oransc.policyagent: INFO
+ file: /var/log/policy-agent/application.log
+server:
+ port : 8433
+ http-port: 8081
+ ssl:
+ key-store-type: JKS
+ key-store-password: policy_agent
+ key-store: /opt/app/enrichment-coordinator-service/etc/cert/keystore.jks
+ key-password: policy_agent
+ key-alias: policy_agent
+app:
+ filepath: /opt/app/enrichment-coordinator-service/data/application_configuration.json
+ webclient:
+ trust-store-used: false
+ trust-store-password: policy_agent
+ trust-store: /opt/app/enrichment-coordinator-service/etc/cert/truststore.jks
+
diff --git a/enrichment-coordinator-service/config/keystore.jks b/enrichment-coordinator-service/config/keystore.jks
new file mode 100644
index 0000000..122997a
--- /dev/null
+++ b/enrichment-coordinator-service/config/keystore.jks
Binary files differ
diff --git a/enrichment-coordinator-service/config/truststore.jks b/enrichment-coordinator-service/config/truststore.jks
new file mode 100644
index 0000000..60d6288
--- /dev/null
+++ b/enrichment-coordinator-service/config/truststore.jks
Binary files differ
diff --git a/enrichment-coordinator-service/docs/api.yaml b/enrichment-coordinator-service/docs/api.yaml
new file mode 100644
index 0000000..a5c07e7
--- /dev/null
+++ b/enrichment-coordinator-service/docs/api.yaml
@@ -0,0 +1,300 @@
+swagger: '2.0'
+info:
+ description: This page lists all the rest apis for the service.
+ version: '1.0'
+ title: Enrichment Data service
+host: 'localhost:8081'
+basePath: /
+tags:
+ - name: A1-E Enrichment Data Consumer API
+ description: Consumer Controller
+paths:
+ /A1-EI/v1/eitypes:
+ get:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: Query EI type identifiers
+ description: DETAILS TBD
+ operationId: getEiTypeIdentifiersUsingGET
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: EI type identifiers
+ schema:
+ type: array
+ items:
+ type: string
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Not Found
+ deprecated: false
+ '/A1-EI/v1/eitypes/{eiTypeId}':
+ get:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: Definitions for an individual EI Type
+ description: Query EI type
+ operationId: getEiTypeUsingGET
+ produces:
+ - application/json
+ parameters:
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: EI type
+ schema:
+ $ref: '#/definitions/ei_type_info'
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ '/A1-EI/v1/eitypes/{eiTypeId}/eijobs':
+ get:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: Query EI job identifiers
+ description: Returns the identifiers for an EI Type
+ operationId: getEiJobIdsUsingGET
+ produces:
+ - application/json
+ parameters:
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ - in: body
+ name: owner
+ description: identifies the owner of the job
+ required: false
+ schema:
+ type: string
+ responses:
+ '200':
+ description: EI type
+ schema:
+ type: array
+ items:
+ type: string
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ '/A1-EI/v1/eitypes/{eiTypeId}/eijobs/{eiJobId}':
+ get:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: Individual EI Job
+ operationId: getIndividualEiJobUsingGET
+ produces:
+ - application/json
+ parameters:
+ - name: eiJobId
+ in: path
+ description: eiJobId
+ required: true
+ type: string
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: EI Job
+ schema:
+ $ref: '#/definitions/ei_job_info'
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type or job is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ put:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: Individual EI Job
+ description: Create or update an EI Job
+ operationId: putIndividualEiJobUsingPUT
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - name: eiJobId
+ in: path
+ description: eiJobId
+ required: true
+ type: string
+ - in: body
+ name: eiJobInfo
+ description: eiJobInfo
+ required: true
+ schema:
+ $ref: '#/definitions/ei_job_info'
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: Job updated
+ schema:
+ type: object
+ '201':
+ description: Job created
+ schema:
+ type: object
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ delete:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: Individual EI Job
+ description: Delete an EI job
+ operationId: deleteIndividualEiJobUsingDELETE
+ produces:
+ - application/json
+ parameters:
+ - name: eiJobId
+ in: path
+ description: eiJobId
+ required: true
+ type: string
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: Not used
+ schema:
+ type: object
+ '204':
+ description: Job deleted
+ schema:
+ type: object
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type or job is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ '/A1-EI/v1/eitypes/{eiTypeId}/eijobs/{eiJobId}/status':
+ get:
+ tags:
+ - A1-E Enrichment Data Consumer API
+ summary: EI Job status
+ operationId: getEiJobStatusUsingGET
+ produces:
+ - application/json
+ parameters:
+ - name: eiJobId
+ in: path
+ description: eiJobId
+ required: true
+ type: string
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: EI Job status
+ schema:
+ $ref: '#/definitions/ei_job_status'
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type or job is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+definitions:
+ ei_job_info:
+ type: object
+ properties:
+ job_data:
+ type: object
+ description: EI Type specific job data
+ owner:
+ type: string
+ description: Identity of the owner of the job
+ result_target:
+ type: string
+ description: the deliver information for the EI. This is typically a URL.
+ title: ei_job_info
+ description: Information for a Enrichment Information Job
+ ei_job_status:
+ type: object
+ properties:
+ operational_state:
+ type: string
+ description: |-
+ Operational state, values:
+ ENABLED: TBD
+ DISABLED: TBD.
+ enum:
+ - ENABLED
+ - DISABLED
+ title: ei_job_status
+ description: Status for an EI Job
+ ei_type_info:
+ type: object
+ properties:
+ job_data_schema:
+ type: object
+ description: Json schema for the job data
+ title: ei_type_info
+ description: Information for an EI type
+ error_information:
+ type: object
+ properties:
+ detail:
+ type: string
+ example: EI job type not found
+ description: ' A human-readable explanation specific to this occurrence of the problem.'
+ status:
+ type: integer
+ format: int32
+ example: 503
+ description: 'The HTTP status code generated by the origin server for this occurrence of the problem. '
+ title: error_information
+ description: 'Problem as defined in https://tools.ietf.org/html/rfc7807'
+
diff --git a/enrichment-coordinator-service/eclipse-formatter.xml b/enrichment-coordinator-service/eclipse-formatter.xml
new file mode 100644
index 0000000..c8cca2e
--- /dev/null
+++ b/enrichment-coordinator-service/eclipse-formatter.xml
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ -->
+<profiles version="13">
+<profile kind="CodeFormatterProfile" name="java-formatter" version="12">
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+</profile>
+</profiles>
diff --git a/enrichment-coordinator-service/pom.xml b/enrichment-coordinator-service/pom.xml
new file mode 100644
index 0000000..e1884fc
--- /dev/null
+++ b/enrichment-coordinator-service/pom.xml
@@ -0,0 +1,382 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ========================LICENSE_START=================================
+* O-RAN-SC
+* %%
+* Copyright (C) 2019 Nordix Foundation
+* %%
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* ========================LICENSE_END===================================
+-->
+<project
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-parent</artifactId>
+ <version>2.2.7.RELEASE</version>
+ <relativePath />
+ </parent>
+ <groupId>org.o-ran-sc.nonrtric</groupId>
+ <artifactId>policy-agent</artifactId>
+ <version>2.1.0-SNAPSHOT</version>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+ <repositories>
+ <repository>
+ <id>onap-releases</id>
+ <name>onap-releases</name>
+ <url>https://nexus.onap.org/content/repositories/releases/</url>
+ </repository>
+ </repositories>
+ <properties>
+ <java.version>11</java.version>
+ <springfox.version>2.9.2</springfox.version>
+ <immutable.version>2.8.2</immutable.version>
+ <sdk.version>1.1.6</sdk.version>
+ <swagger.version>2.0.0</swagger.version>
+ <json.version>20190722</json.version>
+ <commons-net.version>3.6</commons-net.version>
+ <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+ <formatter-maven-plugin.version>2.8.1</formatter-maven-plugin.version>
+ <spotless-maven-plugin.version>1.18.0</spotless-maven-plugin.version>
+ <docker-maven-plugin>0.30.0</docker-maven-plugin>
+ <version.dmaap>1.1.11</version.dmaap>
+ <javax.ws.rs-api.version>2.1.1</javax.ws.rs-api.version>
+ <sonar-maven-plugin.version>3.7.0.1746</sonar-maven-plugin.version>
+ <jacoco-maven-plugin.version>0.8.5</jacoco-maven-plugin.version>
+ <exec.skip>true</exec.skip>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-thymeleaf</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-webflux</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-devtools</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webflux</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger.core.v3</groupId>
+ <artifactId>swagger-jaxrs2</artifactId>
+ <version>${swagger.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger.core.v3</groupId>
+ <artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
+ <version>${swagger.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.immutables</groupId>
+ <artifactId>value</artifactId>
+ <version>${immutable.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.immutables</groupId>
+ <artifactId>gson</artifactId>
+ <version>${immutable.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>${json.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ <version>${commons-net.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-configuration-processor</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.dcaegen2.services.sdk.rest.services</groupId>
+ <artifactId>cbs-client</artifactId>
+ <version>${sdk.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onap.dmaap.messagerouter.dmaapclient</groupId>
+ <artifactId>dmaapClient</artifactId>
+ <version>${version.dmaap}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <version>${javax.ws.rs-api.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.inject</groupId>
+ <artifactId>jersey-hk2</artifactId>
+ </dependency>
+ <!-- Actuator dependencies -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
+ <!--REQUIRED TO GENERATE DOCUMENTATION -->
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger2</artifactId>
+ <version>${springfox.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>${springfox.version}</version>
+ </dependency>
+ <!-- TEST -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>mockwebserver</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>net.revelc.code.formatter</groupId>
+ <artifactId>formatter-maven-plugin</artifactId>
+ <version>${formatter-maven-plugin.version}</version>
+ <configuration>
+ <configFile>${project.basedir}/eclipse-formatter.xml</configFile>
+ </configuration>
+ <!-- https://code.revelc.net/formatter-maven-plugin/ use mvn formatter:format
+ spotless:apply process-sources -->
+ </plugin>
+ <plugin>
+ <groupId>com.diffplug.spotless</groupId>
+ <artifactId>spotless-maven-plugin</artifactId>
+ <version>${spotless-maven-plugin.version}</version>
+ <configuration>
+ <java>
+ <removeUnusedImports />
+ <importOrder>
+ <order>com,java,javax,org</order>
+ </importOrder>
+ </java>
+ </configuration>
+ <!-- https://github.com/diffplug/spotless/tree/master/plugin-maven use
+ mvn spotless:apply to rewrite source files use mvn spotless:check to validate
+ source files -->
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>false</skipTests>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/annotations/</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>${jacoco-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <id>default-prepare-agent</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>default-report</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <version>${docker-maven-plugin}</version>
+ <inherited>false</inherited>
+ <executions>
+ <execution>
+ <id>generate-policy-agent-image</id>
+ <phase>package</phase>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ <configuration>
+ <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+ <images>
+ <image>
+ <name>o-ran-sc/nonrtric-policy-agent:${project.version}</name>
+ <build>
+ <cleanup>try</cleanup>
+ <contextDir>${basedir}</contextDir>
+ <dockerFile>Dockerfile</dockerFile>
+ <args>
+ <JAR>${project.build.finalName}.jar</JAR>
+ </args>
+ <tags>
+ <tag>${project.version}</tag>
+ </tags>
+ </build>
+ </image>
+ </images>
+ </configuration>
+ </execution>
+ <execution>
+ <id>push-policy-agent-image</id>
+ <goals>
+ <goal>build</goal>
+ <goal>push</goal>
+ </goals>
+ <configuration>
+ <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+ <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
+ <images>
+ <image>
+ <name>o-ran-sc/nonrtric-policy-agent:${project.version}</name>
+ <build>
+ <contextDir>${basedir}</contextDir>
+ <dockerFile>Dockerfile</dockerFile>
+ <args>
+ <JAR>${project.build.finalName}.jar</JAR>
+ </args>
+ <tags>
+ <tag>${project.version}</tag>
+ <tag>latest</tag>
+ </tags>
+ </build>
+ </image>
+ </images>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- support sonar in multi-module project -->
+ <plugin>
+ <groupId>org.sonarsource.scanner.maven</groupId>
+ <artifactId>sonar-maven-plugin</artifactId>
+ <version>${sonar-maven-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>run-test-script</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <executable>bash</executable>
+ <arguments>
+ <argument>run_test.sh</argument>
+ </arguments>
+ <workingDirectory>../test/jenkins/</workingDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <issueManagement>
+ <system>JIRA</system>
+ <url>https://jira.o-ran-sc.org/</url>
+ </issueManagement>
+</project>
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/Application.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/Application.java
new file mode 100644
index 0000000..3c0156c
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/Application.java
@@ -0,0 +1,33 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class);
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/BeanFactory.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/BeanFactory.java
new file mode 100644
index 0000000..7b868f5
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/BeanFactory.java
@@ -0,0 +1,80 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.catalina.connector.Connector;
+import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiTypes;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class BeanFactory {
+
+ @Value("${server.http-port}")
+ private int httpPort = 0;
+
+ private final ApplicationConfig applicationConfig = new ApplicationConfig();
+
+ @Bean
+ public ObjectMapper mapper() {
+ return new ObjectMapper();
+ }
+
+ @Bean
+ public ServletWebServerFactory servletContainer() {
+ TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
+ if (httpPort > 0) {
+ tomcat.addAdditionalTomcatConnectors(getHttpConnector(httpPort));
+ }
+ return tomcat;
+ }
+
+ @Bean
+ public EiJobs eiJobs() {
+ return new EiJobs();
+ }
+
+ @Bean
+ public EiTypes eiTypes() {
+ return new EiTypes();
+ }
+
+ @Bean
+ public ApplicationConfig getApplicationConfig() {
+ return this.applicationConfig;
+ }
+
+ private static Connector getHttpConnector(int httpPort) {
+ Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
+ connector.setScheme("http");
+ connector.setPort(httpPort);
+ connector.setSecure(false);
+ return connector;
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/SwaggerConfig.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/SwaggerConfig.java
new file mode 100644
index 0000000..07dbefd
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/SwaggerConfig.java
@@ -0,0 +1,95 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment;
+
+import com.fasterxml.classmate.TypeResolver;
+import com.google.common.base.Predicates;
+
+import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * Swagger configuration class that uses swagger2 documentation type and scans
+ * all the controllers. To access the swagger gui go to
+ * http://ip:port/swagger-ui.html
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig extends WebMvcConfigurationSupport {
+
+ static final String API_TITLE = "Enrichment Data service";
+ static final String DESCRIPTION = "This page lists all the rest apis for the service.";
+ static final String VERSION = "1.0";
+ @SuppressWarnings("squid:S1075") // Refactor your code to get this URI from a customizable parameter.
+ static final String RESOURCES_PATH = "classpath:/META-INF/resources/";
+ static final String WEBJARS_PATH = RESOURCES_PATH + "webjars/";
+ static final String SWAGGER_UI = "swagger-ui.html";
+ static final String WEBJARS = "/webjars/**";
+
+ /**
+ * Gets the API info.
+ *
+ * @return the API info.
+ */
+ @Bean
+ public Docket api(TypeResolver resolver) {
+ return new Docket(DocumentationType.SWAGGER_2) //
+ .apiInfo(apiInfo()) //
+ .additionalModels(resolver.resolve(ConsumerEiJobInfo.class)) //
+ .select() //
+ .apis(RequestHandlerSelectors.any()) //
+ .paths(PathSelectors.any()) //
+ .paths(Predicates.not(PathSelectors.regex("/error"))) //
+ // this endpoint is not implemented, but was visible for Swagger
+ .paths(Predicates.not(PathSelectors.regex("/actuator.*"))) //
+ // this endpoint is implemented by spring framework, exclude for now
+ .build();
+ }
+
+ private static ApiInfo apiInfo() {
+ return new ApiInfoBuilder() //
+ .title(API_TITLE) //
+ .description(DESCRIPTION) //
+ .version(VERSION) //
+ .build();
+ }
+
+ @Override
+ protected void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler(SWAGGER_UI) //
+ .addResourceLocations(RESOURCES_PATH);
+
+ registry.addResourceHandler(WEBJARS) //
+ .addResourceLocations(WEBJARS_PATH);
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/AsyncRestClient.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/AsyncRestClient.java
new file mode 100644
index 0000000..2782f94
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/AsyncRestClient.java
@@ -0,0 +1,334 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.clients;
+
+import io.netty.channel.ChannelOption;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.handler.timeout.WriteTimeoutHandler;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+import javax.net.ssl.KeyManagerFactory;
+
+import org.oransc.enrichment.configuration.WebClientConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.lang.Nullable;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.reactive.function.client.ExchangeStrategies;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.resources.ConnectionProvider;
+import reactor.netty.tcp.TcpClient;
+
+/**
+ * Generic reactive REST client.
+ */
+public class AsyncRestClient {
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private WebClient webClient = null;
+ private final String baseUrl;
+ private static final AtomicInteger sequenceNumber = new AtomicInteger();
+ private final WebClientConfig clientConfig;
+ static KeyStore clientTrustStore = null;
+ private boolean sslEnabled = true;
+
+ public AsyncRestClient(String baseUrl) {
+ this(baseUrl, null);
+ this.sslEnabled = false;
+ }
+
+ public AsyncRestClient(String baseUrl, WebClientConfig config) {
+ this.baseUrl = baseUrl;
+ this.clientConfig = config;
+ }
+
+ public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
+ Object traceTag = createTraceTag();
+ logger.debug("{} POST uri = '{}{}''", traceTag, baseUrl, uri);
+ logger.trace("{} POST body: {}", traceTag, body);
+ Mono<String> bodyProducer = body != null ? Mono.just(body) : Mono.empty();
+ return getWebClient() //
+ .flatMap(client -> {
+ RequestHeadersSpec<?> request = client.post() //
+ .uri(uri) //
+ .contentType(MediaType.APPLICATION_JSON) //
+ .body(bodyProducer, String.class);
+ return retrieve(traceTag, request);
+ });
+ }
+
+ public Mono<String> post(String uri, @Nullable String body) {
+ return postForEntity(uri, body) //
+ .flatMap(this::toBody);
+ }
+
+ public Mono<String> postWithAuthHeader(String uri, String body, String username, String password) {
+ Object traceTag = createTraceTag();
+ logger.debug("{} POST (auth) uri = '{}{}''", traceTag, baseUrl, uri);
+ logger.trace("{} POST body: {}", traceTag, body);
+ return getWebClient() //
+ .flatMap(client -> {
+ RequestHeadersSpec<?> request = client.post() //
+ .uri(uri) //
+ .headers(headers -> headers.setBasicAuth(username, password)) //
+ .contentType(MediaType.APPLICATION_JSON) //
+ .bodyValue(body);
+ return retrieve(traceTag, request) //
+ .flatMap(this::toBody);
+ });
+ }
+
+ public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
+ Object traceTag = createTraceTag();
+ logger.debug("{} PUT uri = '{}{}''", traceTag, baseUrl, uri);
+ logger.trace("{} PUT body: {}", traceTag, body);
+ return getWebClient() //
+ .flatMap(client -> {
+ RequestHeadersSpec<?> request = client.put() //
+ .uri(uri) //
+ .contentType(MediaType.APPLICATION_JSON) //
+ .bodyValue(body);
+ return retrieve(traceTag, request);
+ });
+ }
+
+ public Mono<ResponseEntity<String>> putForEntity(String uri) {
+ Object traceTag = createTraceTag();
+ logger.debug("{} PUT uri = '{}{}''", traceTag, baseUrl, uri);
+ logger.trace("{} PUT body: <empty>", traceTag);
+ return getWebClient() //
+ .flatMap(client -> {
+ RequestHeadersSpec<?> request = client.put() //
+ .uri(uri);
+ return retrieve(traceTag, request);
+ });
+ }
+
+ public Mono<String> put(String uri, String body) {
+ return putForEntity(uri, body) //
+ .flatMap(this::toBody);
+ }
+
+ public Mono<ResponseEntity<String>> getForEntity(String uri) {
+ Object traceTag = createTraceTag();
+ logger.debug("{} GET uri = '{}{}''", traceTag, baseUrl, uri);
+ return getWebClient() //
+ .flatMap(client -> {
+ RequestHeadersSpec<?> request = client.get().uri(uri);
+ return retrieve(traceTag, request);
+ });
+ }
+
+ public Mono<String> get(String uri) {
+ return getForEntity(uri) //
+ .flatMap(this::toBody);
+ }
+
+ public Mono<ResponseEntity<String>> deleteForEntity(String uri) {
+ Object traceTag = createTraceTag();
+ logger.debug("{} DELETE uri = '{}{}''", traceTag, baseUrl, uri);
+ return getWebClient() //
+ .flatMap(client -> {
+ RequestHeadersSpec<?> request = client.delete().uri(uri);
+ return retrieve(traceTag, request);
+ });
+ }
+
+ public Mono<String> delete(String uri) {
+ return deleteForEntity(uri) //
+ .flatMap(this::toBody);
+ }
+
+ private Mono<ResponseEntity<String>> retrieve(Object traceTag, RequestHeadersSpec<?> request) {
+ final Class<String> clazz = String.class;
+ return request.retrieve() //
+ .toEntity(clazz) //
+ .doOnNext(entity -> logger.trace("{} Received: {}", traceTag, entity.getBody())) //
+ .doOnError(throwable -> onHttpError(traceTag, throwable));
+ }
+
+ private static Object createTraceTag() {
+ return sequenceNumber.incrementAndGet();
+ }
+
+ private void onHttpError(Object traceTag, Throwable t) {
+ if (t instanceof WebClientResponseException) {
+ WebClientResponseException exception = (WebClientResponseException) t;
+ logger.debug("{} HTTP error status = '{}', body '{}'", traceTag, exception.getStatusCode(),
+ exception.getResponseBodyAsString());
+ } else {
+ logger.debug("{} HTTP error", traceTag, t);
+ }
+ }
+
+ private Mono<String> toBody(ResponseEntity<String> entity) {
+ if (entity.getBody() == null) {
+ return Mono.just("");
+ } else {
+ return Mono.just(entity.getBody());
+ }
+ }
+
+ private boolean isCertificateEntry(KeyStore trustStore, String alias) {
+ try {
+ return trustStore.isCertificateEntry(alias);
+ } catch (KeyStoreException e) {
+ logger.error("Error reading truststore {}", e.getMessage());
+ return false;
+ }
+ }
+
+ private Certificate getCertificate(KeyStore trustStore, String alias) {
+ try {
+ return trustStore.getCertificate(alias);
+ } catch (KeyStoreException e) {
+ logger.error("Error reading truststore {}", e.getMessage());
+ return null;
+ }
+ }
+
+ private static synchronized KeyStore getTrustStore(String trustStorePath, String trustStorePass)
+ throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+ if (clientTrustStore == null) {
+ KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
+ store.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
+ clientTrustStore = store;
+ }
+ return clientTrustStore;
+ }
+
+ private SslContext createSslContextRejectingUntrustedPeers(String trustStorePath, String trustStorePass,
+ KeyManagerFactory keyManager)
+ throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+
+ final KeyStore trustStore = getTrustStore(trustStorePath, trustStorePass);
+ List<Certificate> certificateList = Collections.list(trustStore.aliases()).stream() //
+ .filter(alias -> isCertificateEntry(trustStore, alias)) //
+ .map(alias -> getCertificate(trustStore, alias)) //
+ .collect(Collectors.toList());
+ final X509Certificate[] certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
+
+ return SslContextBuilder.forClient() //
+ .keyManager(keyManager) //
+ .trustManager(certificates) //
+ .build();
+ }
+
+ private SslContext createSslContext(KeyManagerFactory keyManager)
+ throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
+ if (this.clientConfig.isTrustStoreUsed()) {
+ return createSslContextRejectingUntrustedPeers(this.clientConfig.trustStore(),
+ this.clientConfig.trustStorePassword(), keyManager);
+ } else {
+ // Trust anyone
+ return SslContextBuilder.forClient() //
+ .keyManager(keyManager) //
+ .trustManager(InsecureTrustManagerFactory.INSTANCE) //
+ .build();
+ }
+ }
+
+ private TcpClient createTcpClientSecure(SslContext sslContext) {
+ return TcpClient.create(ConnectionProvider.newConnection()) //
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
+ .secure(c -> c.sslContext(sslContext)) //
+ .doOnConnected(connection -> {
+ connection.addHandlerLast(new ReadTimeoutHandler(30));
+ connection.addHandlerLast(new WriteTimeoutHandler(30));
+ });
+ }
+
+ private TcpClient createTcpClientInsecure() {
+ return TcpClient.create(ConnectionProvider.newConnection()) //
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
+ .doOnConnected(connection -> {
+ connection.addHandlerLast(new ReadTimeoutHandler(30));
+ connection.addHandlerLast(new WriteTimeoutHandler(30));
+ });
+ }
+
+ private WebClient createWebClient(String baseUrl, TcpClient tcpClient) {
+ HttpClient httpClient = HttpClient.from(tcpClient);
+ ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
+ ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder() //
+ .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)) //
+ .build();
+ return WebClient.builder() //
+ .clientConnector(connector) //
+ .baseUrl(baseUrl) //
+ .exchangeStrategies(exchangeStrategies) //
+ .build();
+ }
+
+ private Mono<WebClient> getWebClient() {
+ if (this.webClient == null) {
+ try {
+ if (this.sslEnabled) {
+ final KeyManagerFactory keyManager =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ final KeyStore keyStore = KeyStore.getInstance(this.clientConfig.keyStoreType());
+ final String keyStoreFile = this.clientConfig.keyStore();
+ final String keyStorePassword = this.clientConfig.keyStorePassword();
+ final String keyPassword = this.clientConfig.keyPassword();
+ try (final InputStream inputStream = new FileInputStream(keyStoreFile)) {
+ keyStore.load(inputStream, keyStorePassword.toCharArray());
+ }
+ keyManager.init(keyStore, keyPassword.toCharArray());
+ SslContext sslContext = createSslContext(keyManager);
+ TcpClient tcpClient = createTcpClientSecure(sslContext);
+ this.webClient = createWebClient(this.baseUrl, tcpClient);
+ } else {
+ TcpClient tcpClient = createTcpClientInsecure();
+ this.webClient = createWebClient(this.baseUrl, tcpClient);
+ }
+ } catch (Exception e) {
+ logger.error("Could not create WebClient {}", e.getMessage());
+ return Mono.error(e);
+ }
+ }
+ return Mono.just(this.webClient);
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/ApplicationConfig.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/ApplicationConfig.java
new file mode 100644
index 0000000..225b83a
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/ApplicationConfig.java
@@ -0,0 +1,72 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.configuration;
+
+import javax.validation.constraints.NotEmpty;
+
+import lombok.Getter;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+@EnableConfigurationProperties
+@ConfigurationProperties()
+public class ApplicationConfig {
+ @NotEmpty
+ @Getter
+ @Value("${app.filepath}")
+ private String localConfigurationFilePath;
+
+ @Value("${server.ssl.key-store-type}")
+ private String sslKeyStoreType = "";
+
+ @Value("${server.ssl.key-store-password}")
+ private String sslKeyStorePassword = "";
+
+ @Value("${server.ssl.key-store}")
+ private String sslKeyStore = "";
+
+ @Value("${server.ssl.key-password}")
+ private String sslKeyPassword = "";
+
+ @Value("${app.webclient.trust-store-used}")
+ private boolean sslTrustStoreUsed = false;
+
+ @Value("${app.webclient.trust-store-password}")
+ private String sslTrustStorePassword = "";
+
+ @Value("${app.webclient.trust-store}")
+ private String sslTrustStore = "";
+
+ public WebClientConfig getWebClientConfig() {
+ return ImmutableWebClientConfig.builder() //
+ .keyStoreType(this.sslKeyStoreType) //
+ .keyStorePassword(this.sslKeyStorePassword) //
+ .keyStore(this.sslKeyStore) //
+ .keyPassword(this.sslKeyPassword) //
+ .isTrustStoreUsed(this.sslTrustStoreUsed) //
+ .trustStore(this.sslTrustStore) //
+ .trustStorePassword(this.sslTrustStorePassword) //
+ .build();
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/WebClientConfig.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/WebClientConfig.java
new file mode 100644
index 0000000..61d0f5a
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/configuration/WebClientConfig.java
@@ -0,0 +1,45 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.configuration;
+
+import org.immutables.value.Value;
+
+@Value.Immutable
+@Value.Style(redactedMask = "####")
+public interface WebClientConfig {
+ public String keyStoreType();
+
+ @Value.Redacted
+ public String keyStorePassword();
+
+ public String keyStore();
+
+ @Value.Redacted
+ public String keyPassword();
+
+ public boolean isTrustStoreUsed();
+
+ @Value.Redacted
+ public String trustStorePassword();
+
+ public String trustStore();
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/ErrorResponse.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/ErrorResponse.java
new file mode 100644
index 0000000..1df2df7
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/ErrorResponse.java
@@ -0,0 +1,106 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.controllers;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import reactor.core.publisher.Mono;
+
+public class ErrorResponse {
+ private static Gson gson = new GsonBuilder() //
+ .create(); //
+
+ // Returned as body for all failed REST calls
+ @ApiModel(value = "error_information", description = "Problem as defined in https://tools.ietf.org/html/rfc7807")
+ public static class ErrorInfo {
+ @SerializedName("type")
+ private String type = "about:blank";
+
+ @SerializedName("title")
+ private String title = null;
+
+ @SerializedName("status")
+ private final Integer status;
+
+ @SerializedName("detail")
+ private String detail = null;
+
+ @SerializedName("instance")
+ private String instance = null;
+
+ public ErrorInfo(String detail, Integer status) {
+ this.detail = detail;
+ this.status = status;
+ }
+
+ @ApiModelProperty(
+ example = "503",
+ value = "The HTTP status code generated by the origin server for this occurrence of the problem.")
+ public Integer getStatus() {
+ return status;
+ }
+
+ @ApiModelProperty(
+ example = "EI job type not found",
+ value = "A human-readable explanation specific to this occurrence of the problem.")
+ public String getDetail() {
+ return this.detail;
+ }
+
+ }
+
+ @ApiModelProperty(value = "message")
+ public final String message;
+
+ ErrorResponse(String message) {
+ this.message = message;
+ }
+
+ public static Mono<ResponseEntity<Object>> createMono(String text, HttpStatus code) {
+ return Mono.just(create(text, code));
+ }
+
+ public static Mono<ResponseEntity<Object>> createMono(Exception e, HttpStatus code) {
+ return createMono(e.toString(), code);
+ }
+
+ public static ResponseEntity<Object> create(String text, HttpStatus code) {
+ ErrorInfo p = new ErrorInfo(text, code.value());
+ String json = gson.toJson(p);
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_PROBLEM_JSON);
+ return new ResponseEntity<>(json, headers, code);
+ }
+
+ public static ResponseEntity<Object> create(Exception e, HttpStatus code) {
+ return create(e.toString(), code);
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerConsts.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerConsts.java
new file mode 100644
index 0000000..d38d6dc
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerConsts.java
@@ -0,0 +1,32 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.controllers.consumer;
+
+public class ConsumerConsts {
+
+ public static final String A1E_API_ROOT = "/A1-EI/v1";
+ public static final String CONSUMER_API_NAME = "A1-E Enrichment Data Consumer API";
+ public static final String OWNER_PARAM = "owner";
+ public static final String OWNER_PARAM_DESCRIPTION = "identifies the owner of the job";
+
+ private ConsumerConsts() {
+ }
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java
new file mode 100644
index 0000000..7dfaeca
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java
@@ -0,0 +1,258 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.controllers.consumer;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.oransc.enrichment.controllers.ErrorResponse;
+import org.oransc.enrichment.repository.EiJob;
+import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiType;
+import org.oransc.enrichment.repository.EiTypes;
+import org.oransc.enrichment.repository.ImmutableEiJob;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController("ConsumerController")
+@Api(tags = {ConsumerConsts.CONSUMER_API_NAME})
+public class ConsumerController {
+
+ @Autowired
+ private EiJobs eiJobs;
+
+ @Autowired
+ private EiTypes eiTypes;
+
+ private static Gson gson = new GsonBuilder() //
+ .serializeNulls() //
+ .create(); //
+
+ @GetMapping(path = ConsumerConsts.A1E_API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Query EI type identifiers", notes = "DETAILS TBD")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(
+ code = 200,
+ message = "EI type identifiers",
+ response = String.class,
+ responseContainer = "List"), //
+ })
+ public ResponseEntity<Object> getEiTypeIdentifiers( //
+ ) {
+ List<String> result = new ArrayList<>();
+ for (EiType eiType : this.eiTypes.getAllEiTypes()) {
+ result.add(eiType.id());
+ }
+
+ return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+ }
+
+ @GetMapping(path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Definitions for an individual EI Type", notes = "Query EI type")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "EI type", response = ConsumerEiTypeInfo.class), //
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information type is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> getEiType( //
+ @PathVariable("eiTypeId") String eiTypeId) {
+ try {
+ EiType t = this.eiTypes.getType(eiTypeId);
+ ConsumerEiTypeInfo info = toEiTypeInfo(t);
+ return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @GetMapping(
+ path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Query EI job identifiers", notes = "Returns the EI Job identifiers for an EI Type")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(
+ code = 200,
+ message = "EI job identifiers",
+ response = String.class,
+ responseContainer = "List"), //
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information type is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> getEiJobIds( //
+ @PathVariable("eiTypeId") String eiTypeId, //
+ @ApiParam(
+ name = ConsumerConsts.OWNER_PARAM,
+ required = false, //
+ value = ConsumerConsts.OWNER_PARAM_DESCRIPTION) //
+ String owner) {
+ try {
+ this.eiTypes.getType(eiTypeId); // Just to check that the type exists
+ List<String> result = new ArrayList<>();
+ for (EiJob job : this.eiJobs.getJobsForType(eiTypeId)) {
+ result.add(job.id());
+ }
+ return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @GetMapping(
+ path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Individual EI Job", notes = "")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "EI Job", response = ConsumerEiJobInfo.class), //
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information type or job is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> getIndividualEiJob( //
+ @PathVariable("eiTypeId") String eiTypeId, //
+ @PathVariable("eiJobId") String eiJobId) {
+ try {
+ this.eiTypes.getType(eiTypeId); // Just to check that the type exists
+ EiJob job = this.eiJobs.getJob(eiJobId);
+ return new ResponseEntity<>(gson.toJson(toEiJobInfo(job)), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @GetMapping(
+ path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}/status",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "EI Job status", notes = "")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "EI Job status", response = ConsumerEiJobStatus.class), //
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information type or job is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> getEiJobStatus( //
+ @PathVariable("eiTypeId") String eiTypeId, //
+ @PathVariable("eiJobId") String eiJobId) {
+ try {
+ this.eiTypes.getType(eiTypeId); // Just to check that the type exists
+ EiJob job = this.eiJobs.getJob(eiJobId);
+ return new ResponseEntity<>(gson.toJson(toEiJobStatus(job)), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ private ConsumerEiJobStatus toEiJobStatus(EiJob job) {
+ return new ConsumerEiJobStatus(ConsumerEiJobStatus.OperationalState.ENABLED);
+ }
+
+ @DeleteMapping(
+ path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Individual EI Job", notes = "Delete EI job")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "Not used", response = void.class),
+ @ApiResponse(code = 204, message = "Job deleted", response = void.class),
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information type or job is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> deleteIndividualEiJob( //
+ @PathVariable("eiTypeId") String eiTypeId, //
+ @PathVariable("eiJobId") String eiJobId) {
+ try {
+ this.eiJobs.remove(eiJobId);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @PutMapping(
+ path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", //
+ produces = MediaType.APPLICATION_JSON_VALUE, //
+ consumes = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Individual EI Job", notes = "Create or update an EI Job")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 201, message = "Job created", response = void.class), //
+ @ApiResponse(code = 200, message = "Job updated", response = void.class), // ,
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information type is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> putIndividualEiJob( //
+ @PathVariable("eiTypeId") String eiTypeId, //
+ @PathVariable("eiJobId") String eiJobId, //
+ @RequestBody ConsumerEiJobInfo eiJobInfo) {
+ try {
+ this.eiTypes.getType(eiTypeId); // Just to check that the type exists
+ final boolean newJob = this.eiJobs.get(eiJobId) == null;
+ this.eiJobs.put(toEiJob(eiJobInfo, eiJobId, eiTypeId));
+ return new ResponseEntity<>(newJob ? HttpStatus.CREATED : HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ // Status TBD
+
+ private EiJob toEiJob(ConsumerEiJobInfo info, String id, String typeId) {
+ return ImmutableEiJob.builder() //
+ .id(id) //
+ .typeId(typeId) //
+ .owner(info.owner) //
+ .jobData(info.jobData) //
+ .build();
+ }
+
+ private ConsumerEiTypeInfo toEiTypeInfo(EiType t) {
+ return new ConsumerEiTypeInfo(t.jobDataSchema());
+ }
+
+ private ConsumerEiJobInfo toEiJobInfo(EiJob s) {
+ return new ConsumerEiJobInfo(s.jobData(), s.owner());
+ }
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiJobInfo.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiJobInfo.java
new file mode 100644
index 0000000..08d3cae
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiJobInfo.java
@@ -0,0 +1,52 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.controllers.consumer;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import org.immutables.gson.Gson;
+
+@Gson.TypeAdapters
+@ApiModel(value = "ei_job_info", description = "Information for a Enrichment Information Job")
+public class ConsumerEiJobInfo {
+
+ @ApiModelProperty(value = "Identity of the owner of the job")
+ @SerializedName("owner")
+ @JsonProperty("owner")
+ public String owner;
+
+ @ApiModelProperty(value = "EI Type specific job data")
+ @SerializedName("job_data")
+ @JsonProperty("job_data")
+ public Object jobData;
+
+ public ConsumerEiJobInfo() {
+ }
+
+ public ConsumerEiJobInfo(Object jobData, String owner) {
+ this.jobData = jobData;
+ this.owner = owner;
+ }
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiJobStatus.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiJobStatus.java
new file mode 100644
index 0000000..dbdd1a3
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiJobStatus.java
@@ -0,0 +1,54 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.controllers.consumer;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import org.immutables.gson.Gson;
+
+@Gson.TypeAdapters
+@ApiModel(value = "ei_job_status", description = "Status for an EI Job")
+public class ConsumerEiJobStatus {
+
+ @Gson.TypeAdapters
+ @ApiModel(value = "operational_state", description = "Represents the operational states for a EI Job")
+ public enum OperationalState {
+ ENABLED, DISABLED
+ }
+
+ private static final String OPERATIONAL_STATE_DESCRIPTION = "Operational state, values:\n" //
+ + "ENABLED: TBD\n" //
+ + "DISABLED: TBD.";
+
+ @ApiModelProperty(value = OPERATIONAL_STATE_DESCRIPTION, name = "operational_state")
+ @SerializedName("operational_state")
+ @JsonProperty("operational_state")
+ public final OperationalState state;
+
+ public ConsumerEiJobStatus(OperationalState state) {
+ this.state = state;
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiTypeInfo.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiTypeInfo.java
new file mode 100644
index 0000000..05d2326
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerEiTypeInfo.java
@@ -0,0 +1,43 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.controllers.consumer;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import org.immutables.gson.Gson;
+
+@Gson.TypeAdapters
+@ApiModel(value = "ei_type_info", description = "Information for an EI type")
+public class ConsumerEiTypeInfo {
+
+ @ApiModelProperty(value = "Json schema for the job data")
+ @SerializedName("job_data_schema")
+ @JsonProperty("job_data_schema")
+ public final Object jobDataSchema;
+
+ ConsumerEiTypeInfo(Object jobDataSchema) {
+ this.jobDataSchema = jobDataSchema;
+ }
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/exceptions/ServiceException.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/exceptions/ServiceException.java
new file mode 100644
index 0000000..a14e8de
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/exceptions/ServiceException.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.oransc.enrichment.exceptions;
+
+public class ServiceException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public ServiceException(String message) {
+ super(message);
+ }
+
+ public ServiceException(String message, Exception originalException) {
+ super(message, originalException);
+ }
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJob.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJob.java
new file mode 100644
index 0000000..79f62f8
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJob.java
@@ -0,0 +1,40 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.repository;
+
+import org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+/**
+ * Represents the dynamic information about a Near-RT RIC.
+ */
+@Value.Immutable
+@Gson.TypeAdapters
+public interface EiJob {
+
+ String id();
+
+ String typeId();
+
+ String owner();
+
+ Object jobData();
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJobs.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJobs.java
new file mode 100644
index 0000000..bb2e40f
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiJobs.java
@@ -0,0 +1,106 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.repository;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import org.oransc.enrichment.exceptions.ServiceException;
+
+/**
+ * Dynamic representation of all EI Jobs in the system.
+ */
+public class EiJobs {
+ private Map<String, EiJob> allEiJobs = new HashMap<>();
+ private Map<String, Map<String, EiJob>> jobsByType = new HashMap<>();
+
+ public synchronized void put(EiJob job) {
+ allEiJobs.put(job.id(), job);
+ multiMapPut(this.jobsByType, job.typeId(), job);
+ }
+
+ public synchronized Collection<EiJob> getJobs() {
+ return new Vector<>(allEiJobs.values());
+ }
+
+ public synchronized EiJob getJob(String id) throws ServiceException {
+ EiJob ric = allEiJobs.get(id);
+ if (ric == null) {
+ throw new ServiceException("Could not find EI Job: " + id);
+ }
+ return ric;
+ }
+
+ public synchronized Collection<EiJob> getJobsForType(String typeId) {
+ return multiMapGet(this.jobsByType, typeId);
+ }
+
+ public synchronized EiJob get(String id) {
+ return allEiJobs.get(id);
+ }
+
+ public synchronized EiJob remove(String id) {
+ EiJob job = allEiJobs.get(id);
+ if (job != null) {
+ remove(job);
+ }
+ return job;
+ }
+
+ public synchronized void remove(EiJob job) {
+ this.allEiJobs.remove(job.id());
+ multiMapRemove(this.jobsByType, job.typeId(), job);
+ }
+
+ public synchronized int size() {
+ return allEiJobs.size();
+ }
+
+ public synchronized void clear() {
+ this.allEiJobs.clear();
+ }
+
+ private void multiMapPut(Map<String, Map<String, EiJob>> multiMap, String key, EiJob value) {
+ multiMap.computeIfAbsent(key, k -> new HashMap<>()).put(value.id(), value);
+ }
+
+ private void multiMapRemove(Map<String, Map<String, EiJob>> multiMap, String key, EiJob value) {
+ Map<String, EiJob> map = multiMap.get(key);
+ if (map != null) {
+ map.remove(value.id());
+ if (map.isEmpty()) {
+ multiMap.remove(key);
+ }
+ }
+ }
+
+ private Collection<EiJob> multiMapGet(Map<String, Map<String, EiJob>> multiMap, String key) {
+ Map<String, EiJob> map = multiMap.get(key);
+ if (map == null) {
+ return Collections.emptyList();
+ }
+ return new Vector<>(map.values());
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiType.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiType.java
new file mode 100644
index 0000000..997484d
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiType.java
@@ -0,0 +1,32 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.repository;
+
+import org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+@Value.Immutable
+@Gson.TypeAdapters
+public interface EiType {
+ public String id();
+
+ public Object jobDataSchema();
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiTypes.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiTypes.java
new file mode 100644
index 0000000..7668ff1
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiTypes.java
@@ -0,0 +1,68 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment.repository;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import org.oransc.enrichment.exceptions.ServiceException;
+
+/**
+ * Dynamic representation of all EI Types in the system.
+ */
+public class EiTypes {
+ Map<String, EiType> allEiTypes = new HashMap<>();
+
+ public synchronized void put(EiType type) {
+ allEiTypes.put(type.id(), type);
+ }
+
+ public synchronized Collection<EiType> getAllEiTypes() {
+ return new Vector<>(allEiTypes.values());
+ }
+
+ public synchronized EiType getType(String id) throws ServiceException {
+ EiType type = allEiTypes.get(id);
+ if (type == null) {
+ throw new ServiceException("Could not find EI Job: " + id);
+ }
+ return type;
+ }
+
+ public synchronized EiType get(String id) {
+ return allEiTypes.get(id);
+ }
+
+ public synchronized void remove(String id) {
+ allEiTypes.remove(id);
+ }
+
+ public synchronized int size() {
+ return allEiTypes.size();
+ }
+
+ public synchronized void clear() {
+ this.allEiTypes.clear();
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/ApplicationTest.java b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/ApplicationTest.java
new file mode 100644
index 0000000..d871427
--- /dev/null
+++ b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/ApplicationTest.java
@@ -0,0 +1,275 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
+ * ======================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.enrichment;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.oransc.enrichment.clients.AsyncRestClient;
+import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
+import org.oransc.enrichment.configuration.WebClientConfig;
+import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
+import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
+import org.oransc.enrichment.repository.EiJob;
+import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiType;
+import org.oransc.enrichment.repository.EiTypes;
+import org.oransc.enrichment.repository.ImmutableEiJob;
+import org.oransc.enrichment.repository.ImmutableEiType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.context.ApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@TestPropertySource(
+ properties = { //
+ "server.ssl.key-store=./config/keystore.jks", //
+ "app.webclient.trust-store=./config/truststore.jks"})
+class ApplicationTest {
+ private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
+
+ @Autowired
+ ApplicationContext context;
+
+ @Autowired
+ EiJobs eiJobs;
+
+ @Autowired
+ EiTypes eiTypes;
+
+ @Autowired
+ ApplicationConfig applicationConfig;
+
+ private static Gson gson = new GsonBuilder() //
+ .serializeNulls() //
+ .create(); //
+
+ /**
+ * Overrides the BeanFactory.
+ */
+ @TestConfiguration
+ static class TestBeanFactory {
+
+ }
+
+ @LocalServerPort
+ private int port;
+
+ @BeforeEach
+ void reset() {
+ this.eiJobs.clear();
+ this.eiTypes.clear();
+ }
+
+ @Test
+ void getEiTypes() throws Exception {
+ addEiType("test");
+ String url = "/eitypes";
+ String rsp = restClient().get(url).block();
+ assertThat(rsp).isEqualTo("[\"test\"]");
+ }
+
+ @Test
+ void getEiType() throws Exception {
+ addEiType("test");
+ String url = "/eitypes/test";
+ String rsp = restClient().get(url).block();
+ assertThat(rsp).contains("job_data_schema");
+ }
+
+ @Test
+ void getEiTypeNotFound() throws Exception {
+ String url = "/eitypes/junk";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI Job: junk");
+ }
+
+ @Test
+ void getEiJobsIds() throws Exception {
+ addEiJob("typeId", "jobId");
+ String url = "/eitypes/typeId/eijobs";
+ String rsp = restClient().get(url).block();
+ assertThat(rsp).isEqualTo("[\"jobId\"]");
+ }
+
+ @Test
+ void getEiJobTypeNotFound() throws Exception {
+ String url = "/eitypes/junk/eijobs";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI Job: junk");
+ }
+
+ @Test
+ void getEiJob() throws Exception {
+ addEiJob("typeId", "jobId");
+ String url = "/eitypes/typeId/eijobs/jobId";
+ String rsp = restClient().get(url).block();
+ assertThat(rsp).contains("job_data");
+ }
+
+ @Test
+ void getEiJobStatus() throws Exception {
+ addEiJob("typeId", "jobId");
+ String url = "/eitypes/typeId/eijobs/jobId/status";
+ String rsp = restClient().get(url).block();
+ assertThat(rsp).contains("ENABLED");
+ }
+
+ // Status TBD
+
+ @Test
+ void deleteEiJob() throws Exception {
+ addEiJob("typeId", "jobId");
+ assertThat(this.eiJobs.size()).isEqualTo(1);
+ String url = "/eitypes/typeId/eijobs/jobId";
+ restClient().delete(url).block();
+ assertThat(this.eiJobs.size()).isEqualTo(0);
+ }
+
+ @Test
+ void putEiJob() throws Exception {
+ addEiType("typeId");
+
+ String url = "/eitypes/typeId/eijobs/jobId";
+ String body = gson.toJson(eiJobInfo());
+ ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
+ assertThat(this.eiJobs.size()).isEqualTo(1);
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
+
+ resp = restClient().putForEntity(url, body).block();
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+ EiJob job = this.eiJobs.getJob("jobId");
+ assertThat(job.owner()).isEqualTo("owner");
+ }
+
+ ConsumerEiJobInfo eiJobInfo() {
+ return new ConsumerEiJobInfo(jsonObject(), "owner");
+ }
+
+ // @Test
+ @SuppressWarnings("squid:S2699")
+ void runMock() throws Exception {
+ logger.info("Keeping server alive! " + this.port);
+ synchronized (this) {
+ this.wait();
+ }
+ }
+
+ JsonObject jsonObject() {
+ JsonObject jsonObj = new JsonObject();
+ JsonElement e = new JsonPrimitive(111);
+ jsonObj.add("param", e);
+ return jsonObj;
+ }
+
+ private EiJob addEiJob(String typeId, String jobId) {
+ addEiType(typeId);
+ EiJob job = ImmutableEiJob.builder() //
+ .id(jobId) //
+ .typeId(typeId) //
+ .owner("owner") //
+ .jobData(jsonObject()) //
+ .build();
+ this.eiJobs.put(job);
+ return job;
+ }
+
+ private EiType addEiType(String typeId) {
+ EiType t = ImmutableEiType.builder() //
+ .id(typeId) //
+ .jobDataSchema(jsonObject()) //
+ .build();
+ this.eiTypes.put(t);
+ return t;
+ }
+
+ private String baseUrl() {
+ return "https://localhost:" + this.port + ConsumerConsts.A1E_API_ROOT;
+ }
+
+ private AsyncRestClient restClient(boolean useTrustValidation) {
+ WebClientConfig config = this.applicationConfig.getWebClientConfig();
+ config = ImmutableWebClientConfig.builder() //
+ .keyStoreType(config.keyStoreType()) //
+ .keyStorePassword(config.keyStorePassword()) //
+ .keyStore(config.keyStore()) //
+ .keyPassword(config.keyPassword()) //
+ .isTrustStoreUsed(useTrustValidation) //
+ .trustStore(config.trustStore()) //
+ .trustStorePassword(config.trustStorePassword()) //
+ .build();
+
+ return new AsyncRestClient(baseUrl(), config);
+ }
+
+ private AsyncRestClient restClient() {
+ return restClient(false);
+ }
+
+ private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
+ testErrorCode(request, expStatus, responseContains, true);
+ }
+
+ private void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
+ boolean expectApplicationProblemJsonMediaType) {
+ StepVerifier.create(request) //
+ .expectSubscription() //
+ .expectErrorMatches(
+ t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
+ .verify();
+ }
+
+ private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
+ boolean expectApplicationProblemJsonMediaType) {
+ assertTrue(throwable instanceof WebClientResponseException);
+ WebClientResponseException responseException = (WebClientResponseException) throwable;
+ assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
+ assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
+ if (expectApplicationProblemJsonMediaType) {
+ assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
+ }
+ return true;
+ }
+
+}