From 57b7c521ca02753b12e995ec02f3a84dddd147ce Mon Sep 17 00:00:00 2001 From: PatrikBuhr Date: Fri, 18 Sep 2020 15:13:03 +0200 Subject: [PATCH] Enrichment cordinator service, added job validation Added validation of EI job data agains the registerred json schema. Improved tests. Improved documentation. Change-Id: I86fe031b22aa0378d82181a019e8f0743f283e8a Issue-ID: NONRTRIC-173 Signed-off-by: PatrikBuhr --- enrichment-coordinator-service/docs/api.yaml | 89 ++++----- enrichment-coordinator-service/pom.xml | 6 + .../java/org/oransc/enrichment/SwaggerConfig.java | 16 +- .../oransc/enrichment/clients/ProducerJobInfo.java | 18 +- .../enrichment/controllers/ErrorResponse.java | 3 +- .../controllers/consumer/ConsumerController.java | 37 +++- .../controllers/consumer/ConsumerEiJobInfo.java | 4 +- .../controllers/producer/ProducerController.java | 16 +- .../controllers/producer/ProducerEiTypeInfo.java | 8 +- .../producer/ProducerRegistrationInfo.java | 14 +- .../org/oransc/enrichment/repository/MultiMap.java | 4 +- .../org/oransc/enrichment/ApplicationTest.java | 213 +++++++++++++-------- .../controller/ProducerSimulatorController.java | 4 +- 13 files changed, 247 insertions(+), 185 deletions(-) diff --git a/enrichment-coordinator-service/docs/api.yaml b/enrichment-coordinator-service/docs/api.yaml index bb34e2bc..2fbf934e 100644 --- a/enrichment-coordinator-service/docs/api.yaml +++ b/enrichment-coordinator-service/docs/api.yaml @@ -17,8 +17,7 @@ paths: get: tags: - A1-E Enrichment Data Consumer API - summary: Query EI type identifiers - description: DETAILS TBD + summary: EI type identifiers operationId: getEiTypeIdentifiersUsingGET produces: - application/json @@ -40,8 +39,7 @@ paths: get: tags: - A1-E Enrichment Data Consumer API - summary: Definitions for an individual EI Type - description: Query EI type + summary: Individual EI type operationId: getEiTypeUsingGET produces: - application/json @@ -69,8 +67,7 @@ paths: get: tags: - A1-E Enrichment Data Consumer API - summary: Query EI job identifiers - description: Returns the identifiers for an EI Type + summary: EI job identifiers operationId: getEiJobIdsUsingGET produces: - application/json @@ -88,7 +85,7 @@ paths: type: string responses: '200': - description: EI type + description: EI job identifiers schema: type: array items: @@ -139,7 +136,6 @@ paths: tags: - A1-E Enrichment Data Consumer API summary: Individual EI Job - description: Create or update an EI Job operationId: putIndividualEiJobUsingPUT consumes: - application/json @@ -184,7 +180,6 @@ paths: tags: - A1-E Enrichment Data Consumer API summary: Individual EI Job - description: Delete an EI job operationId: deleteIndividualEiJobUsingDELETE produces: - application/json @@ -254,8 +249,7 @@ paths: get: tags: - Enrichment Data Producer API - summary: Query EI producer identifiers - description: DETAILS TBD + summary: EI producer identifiers operationId: getEiProducerIdentifiersUsingGET produces: - application/json @@ -277,8 +271,7 @@ paths: get: tags: - Enrichment Data Producer API - summary: Job definition for an individual EI producer - description: Query EI jobs + summary: Individual EI producer operationId: getEiProducerUsingGET produces: - application/json @@ -292,7 +285,7 @@ paths: '200': description: EI Jobs schema: - $ref: '#/definitions/producer_ei_type_info' + $ref: '#/definitions/producer_registration_info' '401': description: Unauthorized '403': @@ -305,8 +298,7 @@ paths: put: tags: - Enrichment Data Producer API - summary: Definitions for an individual EI producer - description: Put EI producer + summary: Individual EI producer operationId: putEiProducerUsingPUT consumes: - application/json @@ -343,8 +335,7 @@ paths: delete: tags: - Enrichment Data Producer API - summary: Individual EI Producer - description: Delete an EI Producer + summary: Individual EI producer operationId: deleteEiProducerUsingDELETE produces: - application/json @@ -376,8 +367,8 @@ paths: get: tags: - Enrichment Data Producer API - summary: Job definition for an individual EI producer - description: Query EI producer jobs + summary: EI job definitions + description: EI job definitions for one EI producer operationId: getEiProducerJobsUsingGET produces: - application/json @@ -407,8 +398,7 @@ paths: get: tags: - Enrichment Data Producer API - summary: Query EI type identifiers - description: DETAILS TBD + summary: EI type identifiers operationId: getEiTypeIdentifiersUsingGET_1 produces: - application/json @@ -430,8 +420,7 @@ paths: get: tags: - Enrichment Data Producer API - summary: Definitions for an individual EI Type - description: Query EI type + summary: Individual EI Type operationId: getEiTypeUsingGET_1 produces: - application/json @@ -459,7 +448,7 @@ paths: post: tags: - Producer Simulator - summary: Callback for job creation + summary: Callback for EI job creation operationId: jobCreatedCallbackUsingPOST consumes: - application/json @@ -490,7 +479,7 @@ paths: post: tags: - Producer Simulator - summary: Callback for job deletion + summary: Callback for EI job deletion operationId: jobDeletedCallbackUsingPOST consumes: - application/json @@ -522,12 +511,12 @@ definitions: type: object required: - job_data - - owner + - job_owner properties: job_data: type: object description: EI Type specific job data - owner: + job_owner: type: string description: Identity of the owner of the job title: ei_job_info @@ -540,8 +529,8 @@ definitions: operational_state: type: string description: |- - Operational state, values: - ENABLED: TBD + Operational state, values: + ENABLED: TBD DISABLED: TBD. enum: - ENABLED @@ -562,37 +551,37 @@ definitions: detail: type: string example: EI job type not found - description: ' A human-readable explanation specific to this occurrence of the problem.' + 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. ' + example: 404 + 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 + - ei_job_identity properties: - identity: - type: string - description: Json for the job data - job_data: + ei_job_data: type: object description: Json for the job data - type_identity: + ei_job_identity: + type: string + description: Idenitity of the EI job + ei_type_identity: type: string description: Type idenitity for the job title: producer_ei_job_request - description: Information EI job start + description: The body of the EI producer callbacks for EI job creation and deletion producer_ei_type_info: type: object properties: - job_data_schema: + ei_job_data_schema: type: object description: Json schema for the job data - producer_ids: + ei_producer_ids: type: array description: Registered producers items: @@ -604,25 +593,25 @@ definitions: required: - ei_type_identity properties: + ei_job_data_schema: + type: object + description: Json schema for the job data 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 + - ei_job_creation_callback_url + - ei_job_deletion_callback_url - supported_ei_types properties: - job_creation_callback_url: + ei_job_creation_callback_url: type: string description: callback for job creation - job_deletion_callback_url: + ei_job_deletion_callback_url: type: string description: callback for job deletion supported_ei_types: @@ -631,5 +620,5 @@ definitions: items: $ref: '#/definitions/producer_ei_type_registration_info' title: producer_registration_info - description: Information for an EI Producer + description: Information for an EI producer diff --git a/enrichment-coordinator-service/pom.xml b/enrichment-coordinator-service/pom.xml index 3f156a7a..a069c0da 100644 --- a/enrichment-coordinator-service/pom.xml +++ b/enrichment-coordinator-service/pom.xml @@ -153,6 +153,12 @@ org.glassfish.jersey.inject jersey-hk2 + + + com.github.erosb + everit-json-schema + 1.12.1 + org.springframework.boot 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 ee80183b..6fa38aa1 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,10 +23,6 @@ package org.oransc.enrichment; 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; @@ -67,17 +63,13 @@ public class SwaggerConfig extends WebMvcConfigurationSupport { public Docket api(TypeResolver resolver) { 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()) // - .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 + .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(); } 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 index 60762b91..ff912215 100644 --- 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 @@ -31,22 +31,24 @@ 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") +@ApiModel( + value = "producer_ei_job_request", + description = "The body of the EI producer callbacks for EI job creation and deletion") public class ProducerJobInfo { - @ApiModelProperty(value = "Json for the job data", required = true) - @SerializedName("identity") - @JsonProperty("identity") + @ApiModelProperty(value = "Idenitity of the EI job", required = true) + @SerializedName("ei_job_identity") + @JsonProperty("ei_job_identity") public String id; @ApiModelProperty(value = "Type idenitity for the job") - @SerializedName("type_identity") - @JsonProperty("type_identity") + @SerializedName("ei_type_identity") + @JsonProperty("ei_type_identity") public String typeId; @ApiModelProperty(value = "Json for the job data") - @SerializedName("job_data") - @JsonProperty("job_data") + @SerializedName("ei_job_data") + @JsonProperty("ei_job_data") public Object jobData; public ProducerJobInfo(Object jobData, String id, String typeId) { 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 edaa3bb4..c90a6491 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 @@ -61,7 +61,7 @@ public class ErrorResponse { } @ApiModelProperty( - example = "503", + example = "404", value = "The HTTP status code generated by the origin server for this occurrence of the problem.") public Integer getStatus() { return status; @@ -73,7 +73,6 @@ public class ErrorResponse { public String getDetail() { return this.detail; } - } @ApiModelProperty(value = "message") 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 c43a4957..a74e4874 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 @@ -20,9 +20,9 @@ package org.oransc.enrichment.controllers.consumer; +import com.fasterxml.jackson.databind.ObjectMapper; 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; @@ -34,9 +34,13 @@ import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.List; +import org.everit.json.schema.Schema; +import org.everit.json.schema.loader.SchemaLoader; +import org.json.JSONObject; import org.oransc.enrichment.clients.ProducerCallbacks; import org.oransc.enrichment.configuration.ApplicationConfig; import org.oransc.enrichment.controllers.ErrorResponse; +import org.oransc.enrichment.exceptions.ServiceException; import org.oransc.enrichment.repository.EiJob; import org.oransc.enrichment.repository.EiJobs; import org.oransc.enrichment.repository.EiType; @@ -79,7 +83,7 @@ public class ConsumerController { .create(); // @GetMapping(path = ConsumerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Query EI type identifiers", notes = "DETAILS TBD") + @ApiOperation(value = "EI type identifiers", notes = "") @ApiResponses( value = { // @ApiResponse( @@ -99,7 +103,7 @@ public class ConsumerController { } @GetMapping(path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Definitions for an individual EI Type", notes = "Query EI type") + @ApiOperation(value = "Individual EI type", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "EI type", response = ConsumerEiTypeInfo.class), // @@ -121,7 +125,7 @@ public class ConsumerController { @GetMapping( 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") + @ApiOperation(value = "EI job identifiers", notes = "") @ApiResponses( value = { // @ApiResponse( @@ -206,7 +210,7 @@ public class ConsumerController { @DeleteMapping( path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Individual EI Job", notes = "Delete EI job") + @ApiOperation(value = "Individual EI Job", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "Not used", response = void.class), @@ -232,7 +236,7 @@ public class ConsumerController { 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") + @ApiOperation(value = "Individual EI Job", notes = "") @ApiResponses( value = { // @ApiResponse(code = 201, message = "Job created", response = void.class), // @@ -258,11 +262,24 @@ public class ConsumerController { } } - private void validateJobData(Object schemaObj, Object json) { - if (schemaObj instanceof JsonObject) { - JsonObject schema = (JsonObject) schemaObj; - logger.debug("schema {} json {}", schema, json); + private void validateJobData(Object schemaObj, Object object) throws ServiceException { + if (schemaObj == null) { + return; // schema is optional for now + } + try { + ObjectMapper mapper = new ObjectMapper(); + + String schemaAsString = mapper.writeValueAsString(schemaObj); + JSONObject schemaJSON = new JSONObject(schemaAsString); + Schema schema = SchemaLoader.load(schemaJSON); + + String objectAsString = mapper.writeValueAsString(object); + JSONObject json = new JSONObject(objectAsString); + schema.validate(json); + } catch (Exception e) { + throw new ServiceException("Json validation failure", e); } + } // Status TBD 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 515c6169..8255c43b 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 @@ -33,8 +33,8 @@ import org.immutables.gson.Gson; public class ConsumerEiJobInfo { @ApiModelProperty(value = "Identity of the owner of the job", required = true) - @SerializedName("owner") - @JsonProperty(value = "owner", required = true) + @SerializedName("job_owner") + @JsonProperty(value = "job_owner", required = true) public String owner; @ApiModelProperty(value = "EI Type specific job data", required = true) 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 index b88e047f..c24e5593 100644 --- 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 @@ -81,7 +81,7 @@ public class ProducerController { ProducerCallbacks producerCallbacks; @GetMapping(path = ProducerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Query EI type identifiers", notes = "DETAILS TBD") + @ApiOperation(value = "EI type identifiers", notes = "") @ApiResponses( value = { // @ApiResponse( @@ -101,7 +101,7 @@ public class ProducerController { } @GetMapping(path = ProducerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Definitions for an individual EI Type", notes = "Query EI type") + @ApiOperation(value = "Individual EI Type", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "EI type", response = ProducerEiTypeInfo.class), // @@ -121,7 +121,7 @@ public class ProducerController { } @GetMapping(path = ProducerConsts.API_ROOT + "/eiproducers", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Query EI producer identifiers", notes = "DETAILS TBD") + @ApiOperation(value = "EI producer identifiers", notes = "") @ApiResponses( value = { // @ApiResponse( @@ -143,10 +143,10 @@ public class ProducerController { @GetMapping( path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Definition for an individual EI producer", notes = "Query EI jobs") + @ApiOperation(value = "Individual EI producer", notes = "") @ApiResponses( value = { // - @ApiResponse(code = 200, message = "EI Jobs", response = ProducerEiTypeInfo.class), // + @ApiResponse(code = 200, message = "EI Jobs", response = ProducerRegistrationInfo.class), // @ApiResponse( code = 404, message = "Enrichment Information producer is not found", @@ -165,7 +165,7 @@ public class ProducerController { @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") + @ApiOperation(value = "EI job definitions", notes = "EI job definitions for one EI producer") @ApiResponses( value = { // @ApiResponse(code = 200, message = "EI jobs", response = ProducerJobInfo.class, responseContainer = "List"), // @@ -194,7 +194,7 @@ public class ProducerController { @PutMapping( path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Individual EI producer", notes = "Put EI producer") + @ApiOperation(value = "Individual EI producer", notes = "") @ApiResponses( value = { // @ApiResponse(code = 201, message = "Producer created", response = void.class), // @@ -220,7 +220,7 @@ public class ProducerController { @DeleteMapping( path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Individual EI Producer", notes = "Delete an EI Producer") + @ApiOperation(value = "Individual EI producer", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "Not used", response = void.class), 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 index 1c9167bf..9a821619 100644 --- 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 @@ -35,13 +35,13 @@ import org.immutables.gson.Gson; public class ProducerEiTypeInfo { @ApiModelProperty(value = "Json schema for the job data") - @SerializedName("job_data_schema") - @JsonProperty("job_data_schema") + @SerializedName("ei_job_data_schema") + @JsonProperty("ei_job_data_schema") public Object jobDataSchema; @ApiModelProperty(value = "Registered producers") - @SerializedName("producer_ids") - @JsonProperty(value = "producer_ids", required = true) + @SerializedName("ei_producer_ids") + @JsonProperty(value = "ei_producer_ids", required = true) public Collection producerIds; public ProducerEiTypeInfo(Object jobDataSchema, Collection producers) { 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 index c48c716f..1ef2ed4c 100644 --- 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 @@ -31,7 +31,7 @@ import java.util.Collection; import org.immutables.gson.Gson; @Gson.TypeAdapters -@ApiModel(value = "producer_registration_info", description = "Information for an EI Producer") +@ApiModel(value = "producer_registration_info", description = "Information for an EI producer") public class ProducerRegistrationInfo { @Gson.TypeAdapters @@ -44,8 +44,8 @@ public class ProducerRegistrationInfo { public String eiTypeId; @ApiModelProperty(value = "Json schema for the job data") - @SerializedName("job_data_schema") - @JsonProperty("job_data_schema") + @SerializedName("ei_job_data_schema") + @JsonProperty("ei_job_data_schema") public Object jobDataSchema; public ProducerEiTypeRegistrationInfo(Object jobDataSchema, String eiTypeId) { @@ -63,13 +63,13 @@ public class ProducerRegistrationInfo { public Collection types; @ApiModelProperty(value = "callback for job creation", required = true) - @SerializedName("job_creation_callback_url") - @JsonProperty(value = "job_creation_callback_url", required = true) + @SerializedName("ei_job_creation_callback_url") + @JsonProperty(value = "ei_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) + @SerializedName("ei_job_deletion_callback_url") + @JsonProperty(value = "ei_job_deletion_callback_url", required = true) public String jobDeletionCallbackUrl; public ProducerRegistrationInfo(Collection types, String jobCreationCallbackUrl, 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 index e64ea223..c2b82704 100644 --- 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 @@ -27,9 +27,9 @@ import java.util.Map; import java.util.Vector; /** - * Dynamic representation of all Rics in the system. + * A map, where each key can be bound to may values (where each value has an own + * ID) */ - public class MultiMap { private final Map> map = new HashMap<>(); 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 cf340764..e92705e9 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 @@ -24,14 +24,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; 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 com.google.gson.JsonParser; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import org.junit.jupiter.api.AfterEach; @@ -49,14 +48,12 @@ 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.exceptions.ServiceException; 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.ImmutableEiProducer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -86,6 +83,8 @@ import reactor.test.StepVerifier; "app.webclient.trust-store=./config/truststore.jks"}) class ApplicationTest { private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class); + private final String EI_TYPE_ID = "typeId"; + private final String EI_JOB_PROPERTY = "\"property1\""; @Autowired ApplicationContext context; @@ -137,52 +136,55 @@ class ApplicationTest { } @Test - void getEiTypes() throws Exception { - addEiType("test"); + void testGetEiTypes() throws Exception { + putEiProducerWithOneType("test"); String url = ConsumerConsts.API_ROOT + "/eitypes"; String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo("[\"test\"]"); } @Test - void getEiType() throws Exception { - addEiType("test"); + void testGetEiType() throws Exception { + putEiProducerWithOneType("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 { + void testGetEiTypeNotFound() throws Exception { 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"); + void testGetEiJobsIds() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId"); String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs"; String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo("[\"jobId\"]"); } @Test - void getEiJobTypeNotFound() throws Exception { + void testGetEiJobTypeNotFound() throws Exception { 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"); + void testGetEiJob() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId"); String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId"; String rsp = restClient().get(url).block(); assertThat(rsp).contains("job_data"); } @Test - void getEiJobStatus() throws Exception { - addEiJob("typeId", "jobId"); + void testGetEiJobStatus() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId"); String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId/status"; String rsp = restClient().get(url).block(); assertThat(rsp).contains("ENABLED"); @@ -191,8 +193,9 @@ class ApplicationTest { // Status TBD @Test - void deleteEiJob() throws Exception { - addEiJob("typeId", "jobId"); + void testDeleteEiJob() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId"); assertThat(this.eiJobs.size()).isEqualTo(1); String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId"; restClient().delete(url).block(); @@ -204,8 +207,8 @@ class ApplicationTest { } @Test - void putEiJob() throws Exception { - addEiType("typeId"); + void testPutEiJob() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId"; String body = gson.toJson(eiJobInfo()); @@ -225,8 +228,21 @@ class ApplicationTest { } @Test - void getEiProducerTypes() throws Exception { - this.addEiJob("typeId", "jobId"); + void testPutEiJob_jsonSchemavalidationError() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + + String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId"; + // The element with name "property1" is mandatory in the schema + ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo(jsonObject("{ \"XXstring\" : \"value\" }"), "owner"); + String body = gson.toJson(jobInfo); + + testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "Json validation failure"); + } + + @Test + void testGetEiProducerTypes() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId"); String url = ProducerConsts.API_ROOT + "/eitypes"; ResponseEntity resp = restClient().getForEntity(url).block(); @@ -234,31 +250,53 @@ class ApplicationTest { } @Test - void putEiProducer() throws Exception { + void testPutEiProducer() throws Exception { String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId"; - String body = gson.toJson(producerEiRegistratioInfo()); + String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID)); ResponseEntity resp = restClient().putForEntity(url, body).block(); assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED); assertThat(this.eiTypes.size()).isEqualTo(1); - EiType type = this.eiTypes.getType("typeId"); + EiType type = this.eiTypes.getType(EI_TYPE_ID); assertThat(type.getProducerIds().contains("eiProducerId")).isTrue(); assertThat(this.eiProducers.size()).isEqualTo(1); - assertThat(this.eiProducers.get("eiProducerId").eiTypes().iterator().next().getId().equals("typeId")).isTrue(); + assertThat(this.eiProducers.get("eiProducerId").eiTypes().iterator().next().getId().equals(EI_TYPE_ID)) + .isTrue(); resp = restClient().putForEntity(url, body).block(); assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK); + + resp = restClient().getForEntity(url).block(); + assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(resp.getBody()).isEqualTo(body); } @Test - void putEiProducerExistingJob() throws Exception { - this.addEiJob("typeId", "jobId"); + void testPutEiProducerExistingJob() throws Exception { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId"); String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId"; - String body = gson.toJson(producerEiRegistratioInfo()); + String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID)); + restClient().putForEntity(url, body).block(); - ResponseEntity 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(2)); + ProducerJobInfo request = simulatorResults.jobsStarted.get(0); + assertThat(request.id).isEqualTo("jobId"); + } + + @Test + void testPutProducerAndEiJob() throws Exception { + String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId"; + String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID)); + restClient().putForEntity(url, body).block(); + assertThat(this.eiTypes.size()).isEqualTo(1); + this.eiTypes.getType(EI_TYPE_ID); + + url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/jobId"; + body = gson.toJson(eiJobInfo()); + restClient().putForEntity(url, body).block(); ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults(); await().untilAsserted(() -> assertThat(simulatorResults.jobsStarted.size()).isEqualTo(1)); @@ -267,13 +305,14 @@ class ApplicationTest { } @Test - void getEiJobsForProducer() { - this.addEiJob("typeId", "jobId1"); - this.addEiJob("typeId", "jobId2"); + void testGetEiJobsForProducer() throws JsonMappingException, JsonProcessingException, ServiceException { + putEiProducerWithOneType(EI_TYPE_ID); + putEiJob(EI_TYPE_ID, "jobId1"); + putEiJob(EI_TYPE_ID, "jobId2"); // PUT a consumer String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId"; - String body = gson.toJson(producerEiRegistratioInfo()); + String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID)); restClient().putForEntity(url, body).block(); url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId/eijobs"; @@ -281,77 +320,95 @@ class ApplicationTest { 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"); + assertThat(parsedResp[0].typeId).isEqualTo(EI_TYPE_ID); + assertThat(parsedResp[1].typeId).isEqualTo(EI_TYPE_ID); } @Test - void deleteEiProducer() throws Exception { + void testDeleteEiProducer() throws Exception { String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId"; String url2 = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId2"; - String body = gson.toJson(producerEiRegistratioInfo()); + String body = gson.toJson(producerEiRegistratioInfo(EI_TYPE_ID)); restClient().putForEntity(url, body).block(); restClient().putForEntity(url2, body).block(); assertThat(this.eiProducers.size()).isEqualTo(2); - EiType type = this.eiTypes.getType("typeId"); + EiType type = this.eiTypes.getType(EI_TYPE_ID); 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(); + assertThat(this.eiTypes.getType(EI_TYPE_ID).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"); + ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId) + throws JsonMappingException, JsonProcessingException { + return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId); } - ProducerRegistrationInfo producerEiRegistratioInfo() { + ProducerRegistrationInfo producerEiRegistratioInfo(String typeId) + throws JsonMappingException, JsonProcessingException { Collection types = new ArrayList<>(); - types.add(producerEiTypeRegistrationInfo()); + types.add(producerEiTypeRegistrationInfo(typeId)); return new ProducerRegistrationInfo(types, baseUrl() + ProducerSimulatorController.JOB_CREATED_URL, baseUrl() + ProducerSimulatorController.JOB_DELETED_URL); } - ConsumerEiJobInfo eiJobInfo() { + ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException { return new ConsumerEiJobInfo(jsonObject(), "owner"); } - JsonObject jsonObject() { - JsonObject jsonObj = new JsonObject(); - JsonElement e = new JsonPrimitive(111); - jsonObj.add("param", e); - return jsonObj; + Object jsonObject(String json) { + try { + return JsonParser.parseString(json).getAsJsonObject(); + } catch (Exception e) { + throw new NullPointerException(e.toString()); + } } - private EiJob addEiJob(String typeId, String jobId) { - EiType type = addEiType(typeId); - EiJob job = ImmutableEiJob.builder() // - .id(jobId) // - .type(type) // - .owner("owner") // - .jobData(jsonObject()) // - .build(); - this.eiJobs.put(job); - return job; - } - - private EiType addEiType(String typeId) { - 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.eiProducers.put(producer); - eiType.addProducer(producer); - return eiType; + Object jsonSchemaObject() { + // a json schema with one mandatory property named "string" + String schemaStr = "{" // + + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," // + + "\"type\": \"object\"," // + + "\"properties\": {" // + + EI_JOB_PROPERTY + " : {" // + + " \"type\": \"string\"" // + + " }" // + + "}," // + + "\"required\": [" // + + EI_JOB_PROPERTY // + + "]" // + + "}"; // + return jsonObject(schemaStr); + } + + Object jsonObject() { + return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }"); + } + + private EiJob putEiJob(String eiTypeId, String jobId) + throws JsonMappingException, JsonProcessingException, ServiceException { + + String url = ConsumerConsts.API_ROOT + "/eitypes/typeId/eijobs/" + jobId; + String body = gson.toJson(eiJobInfo()); + restClient().putForEntity(url, body).block(); + + return this.eiJobs.getJob(jobId); + } + + private EiType putEiProducerWithOneType(String eiTypeId) + throws JsonMappingException, JsonProcessingException, ServiceException { + String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId"; + String body = gson.toJson(producerEiRegistratioInfo(eiTypeId)); + + restClient().putForEntity(url, body).block(); + assertThat(this.eiTypes.size()).isEqualTo(1); + return this.eiTypes.getType(eiTypeId); } private String baseUrl() { 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 index 307d4091..b6b8bc34 100644 --- 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 @@ -72,7 +72,7 @@ public class ProducerSimulatorController { private TestResults testResults = new TestResults(); @PostMapping(path = JOB_CREATED_URL, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Callback for job creation", notes = "") + @ApiOperation(value = "Callback for EI job creation", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "OK", response = void.class)}// @@ -93,7 +93,7 @@ public class ProducerSimulatorController { } @PostMapping(path = JOB_DELETED_URL, produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Callback for job deletion", notes = "") + @ApiOperation(value = "Callback for EI job deletion", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "OK", response = void.class)}// -- 2.16.6