Enrichment Service
Added producer API
Added producer simulator for tests
Added a mock
Change-Id: Ic627e9e608cd55cb193f72bf1194f5ae1c5a56e2
Issue-ID: NONRTRIC-173
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
diff --git a/enrichment-coordinator-service/config/application.yaml b/enrichment-coordinator-service/config/application.yaml
index 6279a3e..db7d3af 100644
--- a/enrichment-coordinator-service/config/application.yaml
+++ b/enrichment-coordinator-service/config/application.yaml
@@ -17,7 +17,7 @@
org.springframework: ERROR
org.springframework.data: ERROR
org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR
- org.oransc.policyagent: INFO
+ org.oransc.enrichment: INFO
file: /var/log/policy-agent/application.log
server:
port : 8433
diff --git a/enrichment-coordinator-service/docs/api.yaml b/enrichment-coordinator-service/docs/api.yaml
index a5c07e7..bb34e2b 100644
--- a/enrichment-coordinator-service/docs/api.yaml
+++ b/enrichment-coordinator-service/docs/api.yaml
@@ -8,6 +8,10 @@
tags:
- name: A1-E Enrichment Data Consumer API
description: Consumer Controller
+ - name: Enrichment Data Producer API
+ description: Producer Controller
+ - name: Producer Simulator
+ description: Producer Simulator Controller
paths:
/A1-EI/v1/eitypes:
get:
@@ -246,9 +250,279 @@
schema:
$ref: '#/definitions/error_information'
deprecated: false
+ /ei-producer/v1/eiproducers:
+ get:
+ tags:
+ - Enrichment Data Producer API
+ summary: Query EI producer identifiers
+ description: DETAILS TBD
+ operationId: getEiProducerIdentifiersUsingGET
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: EI producer identifiers
+ schema:
+ type: array
+ items:
+ type: string
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Not Found
+ deprecated: false
+ '/ei-producer/v1/eiproducers/{eiProducerId}':
+ get:
+ tags:
+ - Enrichment Data Producer API
+ summary: Job definition for an individual EI producer
+ description: Query EI jobs
+ operationId: getEiProducerUsingGET
+ produces:
+ - application/json
+ parameters:
+ - name: eiProducerId
+ in: path
+ description: eiProducerId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: EI Jobs
+ schema:
+ $ref: '#/definitions/producer_ei_type_info'
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information producer is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ put:
+ tags:
+ - Enrichment Data Producer API
+ summary: Definitions for an individual EI producer
+ description: Put EI producer
+ operationId: putEiProducerUsingPUT
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - name: eiProducerId
+ in: path
+ description: eiProducerId
+ required: true
+ type: string
+ - in: body
+ name: registrationInfo
+ description: registrationInfo
+ required: true
+ schema:
+ $ref: '#/definitions/producer_registration_info'
+ responses:
+ '200':
+ description: Producer updated
+ schema:
+ type: object
+ '201':
+ description: Producer created
+ schema:
+ type: object
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Not Found
+ deprecated: false
+ delete:
+ tags:
+ - Enrichment Data Producer API
+ summary: Individual EI Producer
+ description: Delete an EI Producer
+ operationId: deleteEiProducerUsingDELETE
+ produces:
+ - application/json
+ parameters:
+ - name: eiProducerId
+ in: path
+ description: eiProducerId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: Not used
+ schema:
+ type: object
+ '204':
+ description: Producer deleted
+ schema:
+ type: object
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Producer is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ '/ei-producer/v1/eiproducers/{eiProducerId}/eijobs':
+ get:
+ tags:
+ - Enrichment Data Producer API
+ summary: Job definition for an individual EI producer
+ description: Query EI producer jobs
+ operationId: getEiProducerJobsUsingGET
+ produces:
+ - application/json
+ parameters:
+ - name: eiProducerId
+ in: path
+ description: eiProducerId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: EI jobs
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/producer_ei_job_request'
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information producer is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ /ei-producer/v1/eitypes:
+ get:
+ tags:
+ - Enrichment Data Producer API
+ summary: Query EI type identifiers
+ description: DETAILS TBD
+ operationId: getEiTypeIdentifiersUsingGET_1
+ 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
+ '/ei-producer/v1/eitypes/{eiTypeId}':
+ get:
+ tags:
+ - Enrichment Data Producer API
+ summary: Definitions for an individual EI Type
+ description: Query EI type
+ operationId: getEiTypeUsingGET_1
+ produces:
+ - application/json
+ parameters:
+ - name: eiTypeId
+ in: path
+ description: eiTypeId
+ required: true
+ type: string
+ responses:
+ '200':
+ description: EI type
+ schema:
+ $ref: '#/definitions/producer_ei_type_info'
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Enrichment Information type is not found
+ schema:
+ $ref: '#/definitions/error_information'
+ deprecated: false
+ /producer_simulator/job_created:
+ post:
+ tags:
+ - Producer Simulator
+ summary: Callback for job creation
+ operationId: jobCreatedCallbackUsingPOST
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: request
+ description: request
+ required: true
+ schema:
+ $ref: '#/definitions/producer_ei_job_request'
+ responses:
+ '200':
+ description: OK
+ schema:
+ type: object
+ '201':
+ description: Created
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Not Found
+ deprecated: false
+ /producer_simulator/job_deleted:
+ post:
+ tags:
+ - Producer Simulator
+ summary: Callback for job deletion
+ operationId: jobDeletedCallbackUsingPOST
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: request
+ description: request
+ required: true
+ schema:
+ $ref: '#/definitions/producer_ei_job_request'
+ responses:
+ '200':
+ description: OK
+ schema:
+ type: object
+ '201':
+ description: Created
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Not Found
+ deprecated: false
definitions:
ei_job_info:
type: object
+ required:
+ - job_data
+ - owner
properties:
job_data:
type: object
@@ -256,13 +530,12 @@
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
+ required:
+ - operational_state
properties:
operational_state:
type: string
@@ -297,4 +570,66 @@
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'
+ producer_ei_job_request:
+ type: object
+ required:
+ - identity
+ properties:
+ identity:
+ type: string
+ description: Json for the job data
+ job_data:
+ type: object
+ description: Json for the job data
+ type_identity:
+ type: string
+ description: Type idenitity for the job
+ title: producer_ei_job_request
+ description: Information EI job start
+ producer_ei_type_info:
+ type: object
+ properties:
+ job_data_schema:
+ type: object
+ description: Json schema for the job data
+ producer_ids:
+ type: array
+ description: Registered producers
+ items:
+ type: string
+ title: producer_ei_type_info
+ description: Information for an EI type
+ producer_ei_type_registration_info:
+ type: object
+ required:
+ - ei_type_identity
+ properties:
+ ei_type_identity:
+ type: string
+ description: EI type identity
+ job_data_schema:
+ type: object
+ description: Json schema for the job data
+ title: producer_ei_type_registration_info
+ description: Information for an EI type
+ producer_registration_info:
+ type: object
+ required:
+ - job_creation_callback_url
+ - job_deletion_callback_url
+ - supported_ei_types
+ properties:
+ job_creation_callback_url:
+ type: string
+ description: callback for job creation
+ job_deletion_callback_url:
+ type: string
+ description: callback for job deletion
+ supported_ei_types:
+ type: array
+ description: Supported EI types
+ items:
+ $ref: '#/definitions/producer_ei_type_registration_info'
+ title: producer_registration_info
+ description: Information for an EI Producer
diff --git a/enrichment-coordinator-service/pom.xml b/enrichment-coordinator-service/pom.xml
index e1884fc..3f156a7 100644
--- a/enrichment-coordinator-service/pom.xml
+++ b/enrichment-coordinator-service/pom.xml
@@ -30,8 +30,8 @@
<relativePath />
</parent>
<groupId>org.o-ran-sc.nonrtric</groupId>
- <artifactId>policy-agent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <artifactId>enrichment-coordinator-service</artifactId>
+ <version>0.0.0-SNAPSHOT</version>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
@@ -54,8 +54,8 @@
<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>
+ <formatter-maven-plugin.version>2.12.2</formatter-maven-plugin.version>
+ <spotless-maven-plugin.version>1.24.3</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>
@@ -293,7 +293,7 @@
<inherited>false</inherited>
<executions>
<execution>
- <id>generate-policy-agent-image</id>
+ <id>generate-enrichment-coordinator-service-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
@@ -302,7 +302,7 @@
<pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
<images>
<image>
- <name>o-ran-sc/nonrtric-policy-agent:${project.version}</name>
+ <name>o-ran-sc/nonrtric-enrichment-coordinator-service:${project.version}</name>
<build>
<cleanup>try</cleanup>
<contextDir>${basedir}</contextDir>
@@ -319,7 +319,7 @@
</configuration>
</execution>
<execution>
- <id>push-policy-agent-image</id>
+ <id>push-enrichment-coordinator-service-image</id>
<goals>
<goal>build</goal>
<goal>push</goal>
@@ -329,7 +329,7 @@
<pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
<images>
<image>
- <name>o-ran-sc/nonrtric-policy-agent:${project.version}</name>
+ <name>o-ran-sc/nonrtric-enrichment-coordinator-service:${project.version}</name>
<build>
<contextDir>${basedir}</contextDir>
<dockerFile>Dockerfile</dockerFile>
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
index 7b868f5..9e2142e 100644
--- a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/BeanFactory.java
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/BeanFactory.java
@@ -23,8 +23,10 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.catalina.connector.Connector;
+import org.oransc.enrichment.clients.ProducerCallbacks;
import org.oransc.enrichment.configuration.ApplicationConfig;
import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiProducers;
import org.oransc.enrichment.repository.EiTypes;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
@@ -65,10 +67,20 @@
}
@Bean
+ public EiProducers eiProducers() {
+ return new EiProducers();
+ }
+
+ @Bean
public ApplicationConfig getApplicationConfig() {
return this.applicationConfig;
}
+ @Bean
+ public ProducerCallbacks getProducerCallbacks() {
+ return new ProducerCallbacks();
+ }
+
private static Connector getHttpConnector(int httpPort) {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
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
index 07dbefd..ee80183 100644
--- a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/SwaggerConfig.java
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/SwaggerConfig.java
@@ -23,7 +23,10 @@
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Predicates;
+import org.oransc.enrichment.clients.ProducerJobInfo;
import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
+import org.oransc.enrichment.controllers.producer.ProducerEiTypeInfo;
+import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
@@ -65,6 +68,9 @@
return new Docket(DocumentationType.SWAGGER_2) //
.apiInfo(apiInfo()) //
.additionalModels(resolver.resolve(ConsumerEiJobInfo.class)) //
+ .additionalModels(resolver.resolve(ProducerRegistrationInfo.class)) //
+ .additionalModels(resolver.resolve(ProducerEiTypeInfo.class)) //
+ .additionalModels(resolver.resolve(ProducerJobInfo.class)) //
.select() //
.apis(RequestHandlerSelectors.any()) //
.paths(PathSelectors.any()) //
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/ProducerCallbacks.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/ProducerCallbacks.java
new file mode 100644
index 0000000..a77b772
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/ProducerCallbacks.java
@@ -0,0 +1,94 @@
+/*-
+ * ========================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.clients;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.lang.invoke.MethodHandles;
+
+import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
+import org.oransc.enrichment.configuration.WebClientConfig;
+import org.oransc.enrichment.repository.EiJob;
+import org.oransc.enrichment.repository.EiProducer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Callbacks to the EiProducer
+ */
+@SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string ..
+public class ProducerCallbacks {
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private static Gson gson = new GsonBuilder() //
+ .serializeNulls() //
+ .create(); //
+
+ @Autowired
+ ApplicationConfig applicationConfig;
+
+ public void notifyProducersJobCreated(EiJob eiJob) {
+ for (EiProducer producer : eiJob.type().getProducers()) {
+ notifyProducerJobStarted(producer, eiJob);
+ }
+ }
+
+ public void notifyProducersJobDeleted(EiJob eiJob) {
+ AsyncRestClient restClient = restClient(false);
+ ProducerJobInfo request = new ProducerJobInfo(eiJob.jobData(), eiJob, eiJob.type());
+ String body = gson.toJson(request);
+ for (EiProducer producer : eiJob.type().getProducers()) {
+ restClient.post(producer.jobDeletionCallbackUrl(), body) //
+ .subscribe(notUsed -> logger.debug("Job subscription started OK {}", producer.id()), //
+ throwable -> logger.warn("Job subscription failed {}", producer.id(), throwable.toString()), null);
+ }
+ }
+
+ public void notifyProducerJobStarted(EiProducer producer, EiJob eiJob) {
+ AsyncRestClient restClient = restClient(false);
+ ProducerJobInfo request = new ProducerJobInfo(eiJob.jobData(), eiJob, eiJob.type());
+ String body = gson.toJson(request);
+
+ restClient.post(producer.jobCreationCallbackUrl(), body) //
+ .subscribe(notUsed -> logger.debug("Job subscription started OK {}", producer.id()), //
+ throwable -> logger.warn("Job subscription failed {}", producer.id(), throwable.toString()), null);
+
+ }
+
+ 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("", config);
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/ProducerJobInfo.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/ProducerJobInfo.java
new file mode 100644
index 0000000..60762b9
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/clients/ProducerJobInfo.java
@@ -0,0 +1,65 @@
+/*-
+ * ========================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.clients;
+
+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;
+import org.oransc.enrichment.repository.EiJob;
+import org.oransc.enrichment.repository.EiType;
+
+@Gson.TypeAdapters
+@ApiModel(value = "producer_ei_job_request", description = "Information EI job start")
+public class ProducerJobInfo {
+
+ @ApiModelProperty(value = "Json for the job data", required = true)
+ @SerializedName("identity")
+ @JsonProperty("identity")
+ public String id;
+
+ @ApiModelProperty(value = "Type idenitity for the job")
+ @SerializedName("type_identity")
+ @JsonProperty("type_identity")
+ public String typeId;
+
+ @ApiModelProperty(value = "Json for the job data")
+ @SerializedName("job_data")
+ @JsonProperty("job_data")
+ public Object jobData;
+
+ public ProducerJobInfo(Object jobData, String id, String typeId) {
+ this.id = id;
+ this.jobData = jobData;
+ this.typeId = typeId;
+ }
+
+ public ProducerJobInfo(Object jobData, EiJob job, EiType type) {
+ this(jobData, job.id(), type.getId());
+ }
+
+ public ProducerJobInfo() {
+ }
+
+}
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
index 1df2df7..edaa3bb 100644
--- 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
@@ -83,24 +83,19 @@
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);
+ return Mono.just(create(e, code));
}
- public static ResponseEntity<Object> create(String text, HttpStatus code) {
- ErrorInfo p = new ErrorInfo(text, code.value());
+ public static ResponseEntity<Object> create(Exception e, HttpStatus code) {
+ if (e instanceof RuntimeException) {
+ code = HttpStatus.INTERNAL_SERVER_ERROR;
+ }
+ ErrorInfo p = new ErrorInfo(e.toString(), 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
index d38d6dc..dcddef3 100644
--- 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
@@ -22,7 +22,7 @@
public class ConsumerConsts {
- public static final String A1E_API_ROOT = "/A1-EI/v1";
+ public static final String 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";
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
index 7dfaeca..c43a495 100644
--- 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
@@ -22,6 +22,7 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -29,15 +30,20 @@
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
+import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
+import org.oransc.enrichment.clients.ProducerCallbacks;
+import org.oransc.enrichment.configuration.ApplicationConfig;
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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -49,21 +55,30 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
+@SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string ..
@RestController("ConsumerController")
@Api(tags = {ConsumerConsts.CONSUMER_API_NAME})
public class ConsumerController {
+ private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Autowired
+ ApplicationConfig applicationConfig;
+
@Autowired
private EiJobs eiJobs;
@Autowired
private EiTypes eiTypes;
+ @Autowired
+ ProducerCallbacks producerCallbacks;
+
private static Gson gson = new GsonBuilder() //
.serializeNulls() //
.create(); //
- @GetMapping(path = ConsumerConsts.A1E_API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
+ @GetMapping(path = ConsumerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Query EI type identifiers", notes = "DETAILS TBD")
@ApiResponses(
value = { //
@@ -77,13 +92,13 @@
) {
List<String> result = new ArrayList<>();
for (EiType eiType : this.eiTypes.getAllEiTypes()) {
- result.add(eiType.id());
+ result.add(eiType.getId());
}
return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
}
- @GetMapping(path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
+ @GetMapping(path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Definitions for an individual EI Type", notes = "Query EI type")
@ApiResponses(
value = { //
@@ -104,7 +119,7 @@
}
@GetMapping(
- path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs",
+ path = ConsumerConsts.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(
@@ -138,7 +153,7 @@
}
@GetMapping(
- path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}",
+ path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}",
produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Individual EI Job", notes = "")
@ApiResponses(
@@ -161,7 +176,7 @@
}
@GetMapping(
- path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}/status",
+ path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}/status",
produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "EI Job status", notes = "")
@ApiResponses(
@@ -184,11 +199,12 @@
}
private ConsumerEiJobStatus toEiJobStatus(EiJob job) {
+ // TODO
return new ConsumerEiJobStatus(ConsumerEiJobStatus.OperationalState.ENABLED);
}
@DeleteMapping(
- path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}",
+ path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}",
produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Individual EI Job", notes = "Delete EI job")
@ApiResponses(
@@ -203,7 +219,9 @@
@PathVariable("eiTypeId") String eiTypeId, //
@PathVariable("eiJobId") String eiJobId) {
try {
- this.eiJobs.remove(eiJobId);
+ EiJob job = this.eiJobs.getJob(eiJobId);
+ this.eiJobs.remove(job);
+ this.producerCallbacks.notifyProducersJobDeleted(job);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
@@ -211,7 +229,7 @@
}
@PutMapping(
- path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", //
+ path = ConsumerConsts.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")
@@ -228,28 +246,38 @@
@PathVariable("eiJobId") String eiJobId, //
@RequestBody ConsumerEiJobInfo eiJobInfo) {
try {
- this.eiTypes.getType(eiTypeId); // Just to check that the type exists
+ EiType eiType = this.eiTypes.getType(eiTypeId);
+ validateJobData(eiType.getJobDataSchema(), eiJobInfo.jobData);
final boolean newJob = this.eiJobs.get(eiJobId) == null;
- this.eiJobs.put(toEiJob(eiJobInfo, eiJobId, eiTypeId));
+ EiJob eiJob = toEiJob(eiJobInfo, eiJobId, eiType);
+ this.eiJobs.put(eiJob);
+ this.producerCallbacks.notifyProducersJobCreated(eiJob);
return new ResponseEntity<>(newJob ? HttpStatus.CREATED : HttpStatus.OK);
} catch (Exception e) {
return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
}
}
+ private void validateJobData(Object schemaObj, Object json) {
+ if (schemaObj instanceof JsonObject) {
+ JsonObject schema = (JsonObject) schemaObj;
+ logger.debug("schema {} json {}", schema, json);
+ }
+ }
+
// Status TBD
- private EiJob toEiJob(ConsumerEiJobInfo info, String id, String typeId) {
+ private EiJob toEiJob(ConsumerEiJobInfo info, String id, EiType type) {
return ImmutableEiJob.builder() //
.id(id) //
- .typeId(typeId) //
+ .type(type) //
.owner(info.owner) //
.jobData(info.jobData) //
.build();
}
private ConsumerEiTypeInfo toEiTypeInfo(EiType t) {
- return new ConsumerEiTypeInfo(t.jobDataSchema());
+ return new ConsumerEiTypeInfo(t.getJobDataSchema());
}
private ConsumerEiJobInfo toEiJobInfo(EiJob s) {
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
index 08d3cae..515c616 100644
--- 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
@@ -32,14 +32,14 @@
@ApiModel(value = "ei_job_info", description = "Information for a Enrichment Information Job")
public class ConsumerEiJobInfo {
- @ApiModelProperty(value = "Identity of the owner of the job")
+ @ApiModelProperty(value = "Identity of the owner of the job", required = true)
@SerializedName("owner")
- @JsonProperty("owner")
+ @JsonProperty(value = "owner", required = true)
public String owner;
- @ApiModelProperty(value = "EI Type specific job data")
+ @ApiModelProperty(value = "EI Type specific job data", required = true)
@SerializedName("job_data")
- @JsonProperty("job_data")
+ @JsonProperty(value = "job_data", required = true)
public Object jobData;
public ConsumerEiJobInfo() {
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
index dbdd1a3..282f44d 100644
--- 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
@@ -42,9 +42,9 @@
+ "ENABLED: TBD\n" //
+ "DISABLED: TBD.";
- @ApiModelProperty(value = OPERATIONAL_STATE_DESCRIPTION, name = "operational_state")
+ @ApiModelProperty(value = OPERATIONAL_STATE_DESCRIPTION, name = "operational_state", required = true)
@SerializedName("operational_state")
- @JsonProperty("operational_state")
+ @JsonProperty(value = "operational_state", required = true)
public final OperationalState state;
public ConsumerEiJobStatus(OperationalState state) {
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerConsts.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerConsts.java
new file mode 100644
index 0000000..9e56197
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerConsts.java
@@ -0,0 +1,31 @@
+/*-
+ * ========================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.producer;
+
+public class ProducerConsts {
+
+ public static final String API_ROOT = "/ei-producer/v1";
+ public static final String PRODUCER_API_NAME = "Enrichment Data Producer API";
+
+ private ProducerConsts() {
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerController.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerController.java
new file mode 100644
index 0000000..b88e047
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerController.java
@@ -0,0 +1,306 @@
+/*-
+ * ========================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.producer;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.oransc.enrichment.clients.ProducerCallbacks;
+import org.oransc.enrichment.clients.ProducerJobInfo;
+import org.oransc.enrichment.controllers.ErrorResponse;
+import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
+import org.oransc.enrichment.repository.EiJob;
+import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiProducer;
+import org.oransc.enrichment.repository.EiProducers;
+import org.oransc.enrichment.repository.EiType;
+import org.oransc.enrichment.repository.EiTypes;
+import org.oransc.enrichment.repository.ImmutableEiProducer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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;
+
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+@RestController("ProducerController")
+@Api(tags = {ProducerConsts.PRODUCER_API_NAME})
+public class ProducerController {
+
+ private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static Gson gson = new GsonBuilder() //
+ .serializeNulls() //
+ .create(); //
+
+ @Autowired
+ private EiJobs eiJobs;
+
+ @Autowired
+ private EiTypes eiTypes;
+
+ @Autowired
+ private EiProducers eiProducers;
+
+ @Autowired
+ ProducerCallbacks producerCallbacks;
+
+ @GetMapping(path = ProducerConsts.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.getId());
+ }
+
+ return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+ }
+
+ @GetMapping(path = ProducerConsts.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 = ProducerEiTypeInfo.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);
+ ProducerEiTypeInfo info = toEiTypeInfo(t);
+ return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @GetMapping(path = ProducerConsts.API_ROOT + "/eiproducers", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Query EI producer identifiers", notes = "DETAILS TBD")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(
+ code = 200,
+ message = "EI producer identifiers",
+ response = String.class,
+ responseContainer = "List"), //
+ })
+ public ResponseEntity<Object> getEiProducerIdentifiers( //
+ ) {
+ List<String> result = new ArrayList<>();
+ for (EiProducer eiProducer : this.eiProducers.getAllProducers()) {
+ result.add(eiProducer.id());
+ }
+
+ return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+ }
+
+ @GetMapping(
+ path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Definition for an individual EI producer", notes = "Query EI jobs")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "EI Jobs", response = ProducerEiTypeInfo.class), //
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information producer is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> getEiProducer( //
+ @PathVariable("eiProducerId") String eiProducerId) {
+ try {
+ EiProducer p = this.eiProducers.getProducer(eiProducerId);
+ ProducerRegistrationInfo info = toEiProducerRegistrationInfo(p);
+ return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @GetMapping(
+ path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}/eijobs",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Job definitions for an individual EI producer", notes = "Query EI producer jobs")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "EI jobs", response = ProducerJobInfo.class, responseContainer = "List"), //
+ @ApiResponse(
+ code = 404,
+ message = "Enrichment Information producer is not found",
+ response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> getEiProducerJobs( //
+ @PathVariable("eiProducerId") String eiProducerId) {
+ try {
+ EiProducer producer = this.eiProducers.getProducer(eiProducerId);
+ Collection<ProducerJobInfo> producerJobs = new ArrayList<>();
+ for (EiType type : producer.eiTypes()) {
+ for (EiJob eiJob : this.eiJobs.getJobsForType(type)) {
+ ProducerJobInfo request = new ProducerJobInfo(eiJob.jobData(), eiJob, eiJob.type());
+ producerJobs.add(request);
+ }
+ }
+
+ return new ResponseEntity<>(gson.toJson(producerJobs), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @PutMapping(
+ path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Individual EI producer", notes = "Put EI producer")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 201, message = "Producer created", response = void.class), //
+ @ApiResponse(code = 200, message = "Producer updated", response = void.class)}//
+ )
+ public ResponseEntity<Object> putEiProducer( //
+ @PathVariable("eiProducerId") String eiProducerId, //
+ @RequestBody ProducerRegistrationInfo registrationInfo) {
+ try {
+ final EiProducer previousDefinition = this.eiProducers.get(eiProducerId);
+ if (previousDefinition != null) {
+ deregisterProducer(previousDefinition, false);
+ }
+
+ registerProducer(eiProducerId, registrationInfo);
+
+ return new ResponseEntity<>(previousDefinition == null ? HttpStatus.CREATED : HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @DeleteMapping(
+ path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
+ produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Individual EI Producer", notes = "Delete an EI Producer")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "Not used", response = void.class),
+ @ApiResponse(code = 204, message = "Producer deleted", response = void.class),
+ @ApiResponse(code = 404, message = "Producer is not found", response = ErrorResponse.ErrorInfo.class)})
+ public ResponseEntity<Object> deleteEiProducer(@PathVariable("eiProducerId") String eiProducerId) {
+ try {
+ final EiProducer producer = this.eiProducers.getProducer(eiProducerId);
+ deregisterProducer(producer, true);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ private EiType registerType(ProducerEiTypeRegistrationInfo typeInfo) {
+ EiType type = this.eiTypes.get(typeInfo.eiTypeId);
+ if (type == null) {
+ type = new EiType(typeInfo.eiTypeId, typeInfo.jobDataSchema);
+ this.eiTypes.put(type);
+ }
+ return type;
+
+ }
+
+ EiProducer createProducer(Collection<EiType> types, String producerId, ProducerRegistrationInfo registrationInfo) {
+ return ImmutableEiProducer.builder() //
+ .id(producerId) //
+ .eiTypes(types) //
+ .jobCreationCallbackUrl(registrationInfo.jobCreationCallbackUrl) //
+ .jobDeletionCallbackUrl(registrationInfo.jobDeletionCallbackUrl) //
+ .build();
+ }
+
+ private void registerProducer(String producerId, ProducerRegistrationInfo registrationInfo) {
+ ArrayList<EiType> types = new ArrayList<>();
+ for (ProducerEiTypeRegistrationInfo typeInfo : registrationInfo.types) {
+ types.add(registerType(typeInfo));
+ }
+ EiProducer producer = createProducer(types, producerId, registrationInfo);
+ this.eiProducers.put(producer);
+
+ for (EiType type : types) {
+ for (EiJob job : this.eiJobs.getJobsForType(type)) {
+ this.producerCallbacks.notifyProducerJobStarted(producer, job);
+ }
+ type.addProducer(producer);
+ }
+ }
+
+ private void deregisterProducer(EiProducer producer, boolean deleteJobs) {
+ this.eiProducers.remove(producer);
+ for (EiType type : producer.eiTypes()) {
+ boolean removed = type.removeProducer(producer) != null;
+ if (!removed) {
+ this.logger.error("Bug, no producer found");
+ }
+ if (type.getProducerIds().isEmpty() && deleteJobs) {
+ this.eiTypes.remove(type);
+ for (EiJob job : this.eiJobs.getJobsForType(type.getId())) {
+ this.eiJobs.remove(job);
+ this.logger.warn("Deleted job {} because no producers left", job.id());
+ }
+ }
+ }
+ }
+
+ ProducerRegistrationInfo toEiProducerRegistrationInfo(EiProducer p) {
+ Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
+ for (EiType type : p.eiTypes()) {
+ types.add(toEiTypeRegistrationInfo(type));
+ }
+ return new ProducerRegistrationInfo(types, p.jobCreationCallbackUrl(), p.jobDeletionCallbackUrl());
+ }
+
+ private ProducerEiTypeRegistrationInfo toEiTypeRegistrationInfo(EiType type) {
+ return new ProducerEiTypeRegistrationInfo(type.getJobDataSchema(), type.getId());
+ }
+
+ private ProducerEiTypeInfo toEiTypeInfo(EiType t) {
+ return new ProducerEiTypeInfo(t.getJobDataSchema(), t.getProducerIds());
+ }
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerEiTypeInfo.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerEiTypeInfo.java
new file mode 100644
index 0000000..1c9167b
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerEiTypeInfo.java
@@ -0,0 +1,55 @@
+/*-
+ * ========================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.producer;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Collection;
+
+import org.immutables.gson.Gson;
+
+@Gson.TypeAdapters
+@ApiModel(value = "producer_ei_type_info", description = "Information for an EI type")
+public class ProducerEiTypeInfo {
+
+ @ApiModelProperty(value = "Json schema for the job data")
+ @SerializedName("job_data_schema")
+ @JsonProperty("job_data_schema")
+ public Object jobDataSchema;
+
+ @ApiModelProperty(value = "Registered producers")
+ @SerializedName("producer_ids")
+ @JsonProperty(value = "producer_ids", required = true)
+ public Collection<String> producerIds;
+
+ public ProducerEiTypeInfo(Object jobDataSchema, Collection<String> producers) {
+ this.jobDataSchema = jobDataSchema;
+ this.producerIds = producers;
+ }
+
+ public ProducerEiTypeInfo() {
+ }
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerRegistrationInfo.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerRegistrationInfo.java
new file mode 100644
index 0000000..c48c716
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/producer/ProducerRegistrationInfo.java
@@ -0,0 +1,85 @@
+/*-
+ * ========================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.producer;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Collection;
+
+import org.immutables.gson.Gson;
+
+@Gson.TypeAdapters
+@ApiModel(value = "producer_registration_info", description = "Information for an EI Producer")
+public class ProducerRegistrationInfo {
+
+ @Gson.TypeAdapters
+ @ApiModel(value = "producer_ei_type_registration_info", description = "Information for an EI type")
+ public static class ProducerEiTypeRegistrationInfo {
+
+ @ApiModelProperty(value = "EI type identity", required = true)
+ @SerializedName("ei_type_identity")
+ @JsonProperty(value = "ei_type_identity", required = true)
+ public String eiTypeId;
+
+ @ApiModelProperty(value = "Json schema for the job data")
+ @SerializedName("job_data_schema")
+ @JsonProperty("job_data_schema")
+ public Object jobDataSchema;
+
+ public ProducerEiTypeRegistrationInfo(Object jobDataSchema, String eiTypeId) {
+ this.jobDataSchema = jobDataSchema;
+ this.eiTypeId = eiTypeId;
+ }
+
+ public ProducerEiTypeRegistrationInfo() {
+ }
+ }
+
+ @ApiModelProperty(value = "Supported EI types", required = true)
+ @SerializedName("supported_ei_types")
+ @JsonProperty(value = "supported_ei_types", required = true)
+ public Collection<ProducerEiTypeRegistrationInfo> types;
+
+ @ApiModelProperty(value = "callback for job creation", required = true)
+ @SerializedName("job_creation_callback_url")
+ @JsonProperty(value = "job_creation_callback_url", required = true)
+ public String jobCreationCallbackUrl;
+
+ @ApiModelProperty(value = "callback for job deletion", required = true)
+ @SerializedName("job_deletion_callback_url")
+ @JsonProperty(value = "job_deletion_callback_url", required = true)
+ public String jobDeletionCallbackUrl;
+
+ public ProducerRegistrationInfo(Collection<ProducerEiTypeRegistrationInfo> types, String jobCreationCallbackUrl,
+ String jobDeletionCallbackUrl) {
+ this.types = types;
+ this.jobCreationCallbackUrl = jobCreationCallbackUrl;
+ this.jobDeletionCallbackUrl = jobDeletionCallbackUrl;
+ }
+
+ public ProducerRegistrationInfo() {
+ }
+
+}
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
index 79f62f8..7df4a50 100644
--- 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
@@ -32,7 +32,7 @@
String id();
- String typeId();
+ EiType type();
String owner();
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
index bb2e40f..9326195 100644
--- 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
@@ -21,7 +21,6 @@
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;
@@ -33,11 +32,12 @@
*/
public class EiJobs {
private Map<String, EiJob> allEiJobs = new HashMap<>();
- private Map<String, Map<String, EiJob>> jobsByType = new HashMap<>();
+
+ private MultiMap<EiJob> jobsByType = new MultiMap<>();
public synchronized void put(EiJob job) {
allEiJobs.put(job.id(), job);
- multiMapPut(this.jobsByType, job.typeId(), job);
+ jobsByType.put(job.type().getId(), job.id(), job);
}
public synchronized Collection<EiJob> getJobs() {
@@ -53,7 +53,11 @@
}
public synchronized Collection<EiJob> getJobsForType(String typeId) {
- return multiMapGet(this.jobsByType, typeId);
+ return jobsByType.get(typeId);
+ }
+
+ public synchronized Collection<EiJob> getJobsForType(EiType type) {
+ return jobsByType.get(type.getId());
}
public synchronized EiJob get(String id) {
@@ -70,7 +74,7 @@
public synchronized void remove(EiJob job) {
this.allEiJobs.remove(job.id());
- multiMapRemove(this.jobsByType, job.typeId(), job);
+ jobsByType.remove(job.type().getId(), job.id());
}
public synchronized int size() {
@@ -79,28 +83,7 @@
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());
+ this.jobsByType.clear();
}
}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducer.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducer.java
new file mode 100644
index 0000000..30e62de
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducer.java
@@ -0,0 +1,39 @@
+/*-
+ * ========================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 org.immutables.gson.Gson;
+import org.immutables.value.Value;
+
+@Value.Immutable
+@Gson.TypeAdapters
+public interface EiProducer {
+ public String id();
+
+ public Collection<EiType> eiTypes();
+
+ public String jobCreationCallbackUrl();
+
+ public String jobDeletionCallbackUrl();
+
+}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducers.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducers.java
new file mode 100644
index 0000000..483850d
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/EiProducers.java
@@ -0,0 +1,72 @@
+/*-
+ * ========================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 Rics in the system.
+ */
+public class EiProducers {
+ private Map<String, EiProducer> allEiProducers = new HashMap<>();
+
+ public synchronized void put(EiProducer producer) {
+ allEiProducers.put(producer.id(), producer);
+
+ }
+
+ public synchronized Collection<EiProducer> getAllProducers() {
+ return new Vector<>(allEiProducers.values());
+ }
+
+ public synchronized EiProducer getProducer(String id) throws ServiceException {
+ EiProducer p = allEiProducers.get(id);
+ if (p == null) {
+ throw new ServiceException("Could not find EI producer: " + id);
+ }
+ return p;
+ }
+
+ public synchronized EiProducer get(String id) {
+ return allEiProducers.get(id);
+ }
+
+ public synchronized void remove(String id) {
+ this.allEiProducers.remove(id);
+ }
+
+ public synchronized void remove(EiProducer producer) {
+ this.allEiProducers.remove(producer.id());
+ }
+
+ public synchronized int size() {
+ return allEiProducers.size();
+ }
+
+ public synchronized void clear() {
+ this.allEiProducers.clear();
+ }
+}
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
index 997484d..803fcba 100644
--- 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
@@ -20,13 +20,40 @@
package org.oransc.enrichment.repository;
-import org.immutables.gson.Gson;
-import org.immutables.value.Value;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
-@Value.Immutable
-@Gson.TypeAdapters
-public interface EiType {
- public String id();
+import lombok.Getter;
- public Object jobDataSchema();
+public class EiType {
+ @Getter
+ private final String id;
+
+ @Getter
+ private final Object jobDataSchema;
+
+ private final Map<String, EiProducer> producers = new HashMap<>();
+
+ public EiType(String id, Object jobDataSchema) {
+ this.id = id;
+ this.jobDataSchema = jobDataSchema;
+ }
+
+ public synchronized Collection<EiProducer> getProducers() {
+ return Collections.unmodifiableCollection(producers.values());
+ }
+
+ public synchronized Collection<String> getProducerIds() {
+ return Collections.unmodifiableCollection(producers.keySet());
+ }
+
+ public synchronized void addProducer(EiProducer producer) {
+ this.producers.put(producer.id(), producer);
+ }
+
+ public synchronized EiProducer removeProducer(EiProducer producer) {
+ return this.producers.remove(producer.id());
+ }
}
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
index 7668ff1..5454e8a 100644
--- 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
@@ -34,7 +34,7 @@
Map<String, EiType> allEiTypes = new HashMap<>();
public synchronized void put(EiType type) {
- allEiTypes.put(type.id(), type);
+ allEiTypes.put(type.getId(), type);
}
public synchronized Collection<EiType> getAllEiTypes() {
@@ -44,7 +44,7 @@
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);
+ throw new ServiceException("Could not find EI type: " + id);
}
return type;
}
@@ -57,6 +57,10 @@
allEiTypes.remove(id);
}
+ public synchronized void remove(EiType type) {
+ this.remove(type.getId());
+ }
+
public synchronized int size() {
return allEiTypes.size();
}
diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/MultiMap.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/MultiMap.java
new file mode 100644
index 0000000..e64ea22
--- /dev/null
+++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/repository/MultiMap.java
@@ -0,0 +1,63 @@
+/*-
+ * ========================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;
+
+/**
+ * Dynamic representation of all Rics in the system.
+ */
+
+public class MultiMap<T> {
+
+ private final Map<String, Map<String, T>> map = new HashMap<>();
+
+ public void put(String key, String id, T value) {
+ this.map.computeIfAbsent(key, k -> new HashMap<>()).put(id, value);
+ }
+
+ public void remove(String key, String id) {
+ Map<String, T> innerMap = this.map.get(key);
+ if (innerMap != null) {
+ innerMap.remove(id);
+ if (innerMap.isEmpty()) {
+ this.map.remove(key);
+ }
+ }
+ }
+
+ public Collection<T> get(String key) {
+ Map<String, T> innerMap = this.map.get(key);
+ if (innerMap == null) {
+ return Collections.emptyList();
+ }
+ return new Vector<>(innerMap.values());
+ }
+
+ public void clear() {
+ this.map.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
index d871427..cf34076 100644
--- a/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/ApplicationTest.java
+++ b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/ApplicationTest.java
@@ -21,6 +21,7 @@
package org.oransc.enrichment;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.gson.Gson;
@@ -29,29 +30,44 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.jupiter.api.AfterEach;
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.clients.ProducerJobInfo;
import org.oransc.enrichment.configuration.ApplicationConfig;
import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
import org.oransc.enrichment.configuration.WebClientConfig;
+import org.oransc.enrichment.controller.ProducerSimulatorController;
import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
+import org.oransc.enrichment.controllers.producer.ProducerConsts;
+import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
+import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
import org.oransc.enrichment.repository.EiJob;
import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiProducer;
+import org.oransc.enrichment.repository.EiProducers;
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.oransc.enrichment.repository.ImmutableEiProducer;
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.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -81,8 +97,14 @@
EiTypes eiTypes;
@Autowired
+ EiProducers eiProducers;
+
+ @Autowired
ApplicationConfig applicationConfig;
+ @Autowired
+ ProducerSimulatorController producerSimulator;
+
private static Gson gson = new GsonBuilder() //
.serializeNulls() //
.create(); //
@@ -92,7 +114,10 @@
*/
@TestConfiguration
static class TestBeanFactory {
-
+ @Bean
+ public ServletWebServerFactory servletContainer() {
+ return new TomcatServletWebServerFactory();
+ }
}
@LocalServerPort
@@ -102,12 +127,19 @@
void reset() {
this.eiJobs.clear();
this.eiTypes.clear();
+ this.eiProducers.clear();
+ this.producerSimulator.getTestResults().reset();
+ }
+
+ @AfterEach
+ void check() {
+ assertThat(this.producerSimulator.getTestResults().errorFound).isFalse();
}
@Test
void getEiTypes() throws Exception {
addEiType("test");
- String url = "/eitypes";
+ String url = ConsumerConsts.API_ROOT + "/eitypes";
String rsp = restClient().get(url).block();
assertThat(rsp).isEqualTo("[\"test\"]");
}
@@ -115,35 +147,35 @@
@Test
void getEiType() throws Exception {
addEiType("test");
- String url = "/eitypes/test";
+ String url = ConsumerConsts.API_ROOT + "/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");
+ String url = ConsumerConsts.API_ROOT + "/eitypes/junk";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
}
@Test
void getEiJobsIds() throws Exception {
addEiJob("typeId", "jobId");
- String url = "/eitypes/typeId/eijobs";
+ String url = ConsumerConsts.API_ROOT + "/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");
+ String url = ConsumerConsts.API_ROOT + "/eitypes/junk/eijobs";
+ testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND, "Could not find EI type: junk");
}
@Test
void getEiJob() throws Exception {
addEiJob("typeId", "jobId");
- String url = "/eitypes/typeId/eijobs/jobId";
+ String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
String rsp = restClient().get(url).block();
assertThat(rsp).contains("job_data");
}
@@ -151,7 +183,7 @@
@Test
void getEiJobStatus() throws Exception {
addEiJob("typeId", "jobId");
- String url = "/eitypes/typeId/eijobs/jobId/status";
+ String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId/status";
String rsp = restClient().get(url).block();
assertThat(rsp).contains("ENABLED");
}
@@ -162,38 +194,131 @@
void deleteEiJob() throws Exception {
addEiJob("typeId", "jobId");
assertThat(this.eiJobs.size()).isEqualTo(1);
- String url = "/eitypes/typeId/eijobs/jobId";
+ String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId";
restClient().delete(url).block();
assertThat(this.eiJobs.size()).isEqualTo(0);
+
+ ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
+ await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
+ assertThat(simulatorResults.jobsStopped.get(0).id).isEqualTo("jobId");
}
@Test
void putEiJob() throws Exception {
addEiType("typeId");
- String url = "/eitypes/typeId/eijobs/jobId";
+ String url = ConsumerConsts.API_ROOT + "/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);
+ ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
+ await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
+ ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
+ assertThat(request.id).isEqualTo("jobId");
+
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
+ void getEiProducerTypes() throws Exception {
+ this.addEiJob("typeId", "jobId");
+ String url = ProducerConsts.API_ROOT + "/eitypes";
+
+ ResponseEntity<String> resp = restClient().getForEntity(url).block();
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
}
- // @Test
- @SuppressWarnings("squid:S2699")
- void runMock() throws Exception {
- logger.info("Keeping server alive! " + this.port);
- synchronized (this) {
- this.wait();
- }
+ @Test
+ void putEiProducer() throws Exception {
+ String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
+ String body = gson.toJson(producerEiRegistratioInfo());
+
+ ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
+
+ assertThat(this.eiTypes.size()).isEqualTo(1);
+ EiType type = this.eiTypes.getType("typeId");
+ assertThat(type.getProducerIds().contains("eiProducerId")).isTrue();
+ assertThat(this.eiProducers.size()).isEqualTo(1);
+ assertThat(this.eiProducers.get("eiProducerId").eiTypes().iterator().next().getId().equals("typeId")).isTrue();
+
+ resp = restClient().putForEntity(url, body).block();
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+ }
+
+ @Test
+ void putEiProducerExistingJob() throws Exception {
+ this.addEiJob("typeId", "jobId");
+ String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
+ String body = gson.toJson(producerEiRegistratioInfo());
+
+ ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
+
+ ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
+ await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1));
+ ProducerJobInfo request = simulatorResults.jobsStarted.get(0);
+ assertThat(request.id).isEqualTo("jobId");
+ }
+
+ @Test
+ void getEiJobsForProducer() {
+ this.addEiJob("typeId", "jobId1");
+ this.addEiJob("typeId", "jobId2");
+
+ // PUT a consumer
+ String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
+ String body = gson.toJson(producerEiRegistratioInfo());
+ restClient().putForEntity(url, body).block();
+
+ url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs";
+ ResponseEntity<String> resp = restClient().getForEntity(url).block();
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+ ProducerJobInfo[] parsedResp = gson.fromJson(resp.getBody(), ProducerJobInfo[].class);
+ assertThat(parsedResp[0].typeId).isEqualTo("typeId");
+ assertThat(parsedResp[1].typeId).isEqualTo("typeId");
+ }
+
+ @Test
+ void deleteEiProducer() throws Exception {
+ String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
+ String url2 = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId2";
+ String body = gson.toJson(producerEiRegistratioInfo());
+ restClient().putForEntity(url, body).block();
+ restClient().putForEntity(url2, body).block();
+ assertThat(this.eiProducers.size()).isEqualTo(2);
+ EiType type = this.eiTypes.getType("typeId");
+ assertThat(type.getProducerIds().contains("eiProducerId")).isTrue();
+ assertThat(type.getProducerIds().contains("eiProducerId2")).isTrue();
+
+ restClient().deleteForEntity(url).block();
+ assertThat(this.eiProducers.size()).isEqualTo(1);
+ assertThat(this.eiTypes.getType("typeId").getProducerIds().contains("eiProducerId")).isFalse();
+
+ restClient().deleteForEntity(url2).block();
+ assertThat(this.eiProducers.size()).isEqualTo(0);
+ assertThat(this.eiTypes.size()).isEqualTo(0);
+ }
+
+ ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo() {
+ return new ProducerEiTypeRegistrationInfo(jsonObject(), "typeId");
+ }
+
+ ProducerRegistrationInfo producerEiRegistratioInfo() {
+ Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
+ types.add(producerEiTypeRegistrationInfo());
+ return new ProducerRegistrationInfo(types, baseUrl() + ProducerSimulatorController.JOB_CREATED_URL,
+ baseUrl() + ProducerSimulatorController.JOB_DELETED_URL);
+ }
+
+ ConsumerEiJobInfo eiJobInfo() {
+ return new ConsumerEiJobInfo(jsonObject(), "owner");
}
JsonObject jsonObject() {
@@ -204,10 +329,10 @@
}
private EiJob addEiJob(String typeId, String jobId) {
- addEiType(typeId);
+ EiType type = addEiType(typeId);
EiJob job = ImmutableEiJob.builder() //
.id(jobId) //
- .typeId(typeId) //
+ .type(type) //
.owner("owner") //
.jobData(jsonObject()) //
.build();
@@ -216,16 +341,21 @@
}
private EiType addEiType(String typeId) {
- EiType t = ImmutableEiType.builder() //
- .id(typeId) //
- .jobDataSchema(jsonObject()) //
+ EiType eiType = new EiType(typeId, jsonObject());
+ this.eiTypes.put(eiType); //
+ EiProducer producer = ImmutableEiProducer.builder() //
+ .id("producerId") //
+ .eiTypes(Arrays.asList(eiType)) //
+ .jobCreationCallbackUrl(baseUrl() + ProducerSimulatorController.JOB_CREATED_URL) //
+ .jobDeletionCallbackUrl(baseUrl() + ProducerSimulatorController.JOB_DELETED_URL) //
.build();
- this.eiTypes.put(t);
- return t;
+ this.eiProducers.put(producer);
+ eiType.addProducer(producer);
+ return eiType;
}
private String baseUrl() {
- return "https://localhost:" + this.port + ConsumerConsts.A1E_API_ROOT;
+ return "https://localhost:" + this.port;
}
private AsyncRestClient restClient(boolean useTrustValidation) {
diff --git a/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/MockEnrichmentService.java b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/MockEnrichmentService.java
new file mode 100644
index 0000000..eaaf543
--- /dev/null
+++ b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/MockEnrichmentService.java
@@ -0,0 +1,62 @@
+/*-
+ * ========================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 org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+@TestPropertySource(
+ properties = { //
+ "server.ssl.key-store=./config/keystore.jks", //
+ "app.webclient.trust-store=./config/truststore.jks"})
+class MockEnrichmentService {
+ private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
+
+ /**
+ * Overrides the BeanFactory.
+ */
+ @TestConfiguration
+ static class TestBeanFactory {
+
+ }
+
+ @LocalServerPort
+ private int port;
+
+ @Test
+ @SuppressWarnings("squid:S2699")
+ void runMock() throws Exception {
+ logger.warn("**************** Keeping server alive! " + this.port);
+ synchronized (this) {
+ this.wait();
+ }
+ }
+}
diff --git a/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/controller/ProducerSimulatorController.java b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/controller/ProducerSimulatorController.java
new file mode 100644
index 0000000..307d409
--- /dev/null
+++ b/enrichment-coordinator-service/src/test/java/org/oransc/enrichment/controller/ProducerSimulatorController.java
@@ -0,0 +1,112 @@
+/*-
+ * ========================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.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.Getter;
+
+import org.oransc.enrichment.clients.ProducerJobInfo;
+import org.oransc.enrichment.controllers.ErrorResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController("ProducerSimulatorController")
+@Api(tags = {"Producer Simulator"})
+public class ProducerSimulatorController {
+
+ private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String JOB_CREATED_URL = "/producer_simulator/job_created";
+ public static final String JOB_DELETED_URL = "/producer_simulator/job_deleted";
+
+ public static class TestResults {
+
+ public List<ProducerJobInfo> jobsStarted = Collections.synchronizedList(new ArrayList<ProducerJobInfo>());
+ public List<ProducerJobInfo> jobsStopped = Collections.synchronizedList(new ArrayList<ProducerJobInfo>());
+ public boolean errorFound = false;
+
+ public TestResults() {
+ }
+
+ public void reset() {
+ jobsStarted.clear();
+ jobsStopped.clear();
+ this.errorFound = false;
+ }
+ }
+
+ @Getter
+ private TestResults testResults = new TestResults();
+
+ @PostMapping(path = JOB_CREATED_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Callback for job creation", notes = "")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "OK", response = void.class)}//
+ )
+ public ResponseEntity<Object> jobCreatedCallback( //
+ @RequestBody ProducerJobInfo request) {
+ try {
+ this.testResults.jobsStarted.add(request);
+ logger.info("Job started callback {}", request.id);
+ if (request.id == null) {
+ throw new NullPointerException("Illegal argument");
+ }
+ return new ResponseEntity<>(HttpStatus.OK);
+ } catch (Exception e) {
+ this.testResults.errorFound = true;
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @PostMapping(path = JOB_DELETED_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Callback for job deletion", notes = "")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "OK", response = void.class)}//
+ )
+ public ResponseEntity<Object> jobDeletedCallback( //
+ @RequestBody ProducerJobInfo request) {
+ try {
+ logger.info("Job deleted callback {}", request.id);
+ this.testResults.jobsStopped.add(request);
+ return new ResponseEntity<>(HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
+ }
+ }
+
+}