From b41c6e789ed381a31e2bd40d98e608d1d2dced50 Mon Sep 17 00:00:00 2001 From: PatrikBuhr Date: Fri, 2 Oct 2020 12:47:00 +0200 Subject: [PATCH] Added status controller Change-Id: I6ee63a6d9a0cb5b0003a8f2b907abdcc14374faf Issue-ID: NONRTRIC-173 Signed-off-by: PatrikBuhr --- enrichment-coordinator-service/docs/api.json | 47 +++++++++- enrichment-coordinator-service/docs/api.yaml | 43 ++++++++- .../enrichment/controllers/StatusController.java | 99 ++++++++++++++++++++ .../org/oransc/enrichment/ApplicationTest.java | 101 +++++++++++---------- 4 files changed, 242 insertions(+), 48 deletions(-) create mode 100644 enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/StatusController.java diff --git a/enrichment-coordinator-service/docs/api.json b/enrichment-coordinator-service/docs/api.json index e4f7660f..76be3a4a 100644 --- a/enrichment-coordinator-service/docs/api.json +++ b/enrichment-coordinator-service/docs/api.json @@ -285,6 +285,22 @@ }], "tags": ["Enrichment Data Producer API"] }}, + "/status": {"get": { + "summary": "Returns status and statistics of this service", + "deprecated": false, + "produces": ["application/json"], + "operationId": "getStatusUsingGET", + "responses": { + "200": { + "schema": {"$ref": "#/definitions/status_info"}, + "description": "Service is living" + }, + "401": {"description": "Unauthorized"}, + "403": {"description": "Forbidden"}, + "404": {"description": "Not Found"} + }, + "tags": ["Service status"] + }}, "/producer_simulator/job_created_error": {"post": { "summary": "Callback for EI job creation, returns error", "deprecated": false, @@ -554,7 +570,7 @@ "consumes": ["application/json"] }} }, - "host": "localhost:43453", + "host": "localhost:40973", "definitions": { "EiType": { "description": "Information for an EI type", @@ -652,6 +668,31 @@ } } }, + "status_info": { + "type": "object", + "title": "status_info", + "properties": { + "no_of_producers": { + "format": "int32", + "description": "Number of EI producers", + "type": "integer" + }, + "no_of_jobs": { + "format": "int32", + "description": "Number of EI jobs", + "type": "integer" + }, + "no_of_types": { + "format": "int32", + "description": "Number of EI types", + "type": "integer" + }, + "status": { + "description": "status text", + "type": "string" + } + } + }, "EiJobStatus": { "description": "Status for an EI Job", "type": "object", @@ -749,6 +790,10 @@ { "name": "Producer Simulator", "description": "Producer Simulator Controller" + }, + { + "name": "Service status", + "description": "Status Controller" } ] } \ No newline at end of file diff --git a/enrichment-coordinator-service/docs/api.yaml b/enrichment-coordinator-service/docs/api.yaml index f3fa767b..48a65dd9 100644 --- a/enrichment-coordinator-service/docs/api.yaml +++ b/enrichment-coordinator-service/docs/api.yaml @@ -317,6 +317,26 @@ paths: required: true tags: - Enrichment Data Producer API + /status: + get: + summary: Returns status and statistics of this service + deprecated: false + produces: + - application/json + operationId: getStatusUsingGET + responses: + '200': + schema: + $ref: '#/definitions/status_info' + description: Service is living + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + tags: + - Service status /producer_simulator/job_created_error: post: summary: 'Callback for EI job creation, returns error' @@ -599,7 +619,7 @@ paths: - Producer Simulator consumes: - application/json -host: 'localhost:43453' +host: 'localhost:40973' definitions: EiType: description: Information for an EI type @@ -677,6 +697,25 @@ definitions: ei_job_data_schema: description: Json schema for the job data type: object + status_info: + type: object + title: status_info + properties: + no_of_producers: + format: int32 + description: Number of EI producers + type: integer + no_of_jobs: + format: int32 + description: Number of EI jobs + type: integer + no_of_types: + format: int32 + description: Number of EI types + type: integer + status: + description: status text + type: string EiJobStatus: description: Status for an EI Job type: object @@ -761,4 +800,6 @@ tags: description: Producer Controller - name: Producer Simulator description: Producer Simulator Controller + - name: Service status + description: Status Controller diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/StatusController.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/StatusController.java new file mode 100644 index 00000000..a210aa59 --- /dev/null +++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/StatusController.java @@ -0,0 +1,99 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2020 Nordix Foundation. All rights reserved. + * ====================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +package org.oransc.enrichment.controllers; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import org.immutables.gson.Gson; +import org.oransc.enrichment.repository.EiJobs; +import org.oransc.enrichment.repository.EiProducers; +import org.oransc.enrichment.repository.EiTypes; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +import org.springframework.beans.factory.annotation.Autowired; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.annotations.SerializedName; + +@RestController("StatusController") +@Api(tags = "Service status") +public class StatusController { + + @Autowired + private EiJobs eiJobs; + + @Autowired + private EiTypes eiTypes; + + @Autowired + private EiProducers eiProducers; + + @Gson.TypeAdapters + @ApiModel(value = "status_info") + public static class StatusInfo { + @ApiModelProperty(value = "status text") + @SerializedName("status") + @JsonProperty(value = "status", required = true) + public final String status; + + @ApiModelProperty(value = "Number of EI producers") + @SerializedName("no_of_producers") + @JsonProperty(value = "no_of_producers", required = true) + public final int noOfProducers; + + @ApiModelProperty(value = "Number of EI types") + @SerializedName("no_of_types") + @JsonProperty(value = "no_of_types", required = true) + public final int noOfTypes; + + @ApiModelProperty(value = "Number of EI jobs") + @SerializedName("no_of_jobs") + @JsonProperty(value = "no_of_jobs", required = true) + public final int noOfJobs; + + public StatusInfo(String status, EiProducers producers, EiTypes types, EiJobs jobs) { + this.status = status; + this.noOfJobs = jobs.size(); + this.noOfProducers = producers.size(); + this.noOfTypes = types.size(); + } + } + + @GetMapping(path = "/status", produces = MediaType.APPLICATION_JSON_VALUE) + @ApiOperation(value = "Returns status and statistics of this service") + @ApiResponses(value = { // + @ApiResponse(code = 200, message = "Service is living", response = StatusInfo.class) // + }) + public Mono> getStatus() { + StatusInfo info = new StatusInfo("hunky dory", this.eiProducers, this.eiTypes, this.eiJobs); + return Mono.just(new ResponseEntity<>(info, HttpStatus.OK)); + } + +} 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 67b0d8a4..d8a0b32e 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 @@ -82,10 +82,9 @@ import reactor.test.StepVerifier; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) -@TestPropertySource( - properties = { // +@TestPropertySource(properties = { // "server.ssl.key-store=./config/keystore.jks", // - "app.webclient.trust-store=./config/truststore.jks"}) + "app.webclient.trust-store=./config/truststore.jks" }) class ApplicationTest { private final String EI_TYPE_ID = "typeId"; private final String EI_PRODUCER_ID = "producerId"; @@ -113,8 +112,8 @@ class ApplicationTest { ProducerSupervision producerSupervision; private static Gson gson = new GsonBuilder() // - .serializeNulls() // - .create(); // + .serializeNulls() // + .create(); // /** * Overrides the BeanFactory. @@ -284,8 +283,8 @@ class ApplicationTest { 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", "targetUri"); + ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo(jsonObject("{ \"XXstring\" : \"value\" }"), "owner", + "targetUri"); String body = gson.toJson(jobInfo); testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure"); @@ -329,7 +328,7 @@ class ApplicationTest { String url = ConsumerConsts.API_ROOT + "/eitypes/typeId2/eijobs/jobId"; String body = gson.toJson(eiJobInfo()); testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, - "Not allowed to change type for existing EI job"); + "Not allowed to change type for existing EI job"); } @Test @@ -448,7 +447,7 @@ class ApplicationTest { } private void assertProducerOpState(String producerId, - ProducerStatusInfo.OperationalState expectedOperationalState) { + ProducerStatusInfo.OperationalState expectedOperationalState) { String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status"; ResponseEntity resp = restClient().getForEntity(statusUrl).block(); ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class); @@ -474,29 +473,39 @@ class ApplicationTest { assertThat(this.eiTypes.size()).isEqualTo(0); } + @Test + void testGetStatus() throws JsonMappingException, JsonProcessingException, ServiceException { + putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID); + putEiProducerWithOneTypeRejecting("simulateProducerError2", EI_TYPE_ID); + + String url = "/status"; + ResponseEntity resp = restClient().getForEntity(url).block(); + assertThat(resp.getBody()).contains("hunky dory"); + } + ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId) - throws JsonMappingException, JsonProcessingException { + throws JsonMappingException, JsonProcessingException { return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId); } ProducerRegistrationInfo producerEiRegistratioInfoRejecting(String typeId) - throws JsonMappingException, JsonProcessingException { + throws JsonMappingException, JsonProcessingException { Collection types = new ArrayList<>(); types.add(producerEiTypeRegistrationInfo(typeId)); return new ProducerRegistrationInfo(types, // - baseUrl() + ProducerSimulatorController.JOB_CREATED_ERROR_URL, - baseUrl() + ProducerSimulatorController.JOB_DELETED_ERROR_URL, - baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL); + baseUrl() + ProducerSimulatorController.JOB_CREATED_ERROR_URL, + baseUrl() + ProducerSimulatorController.JOB_DELETED_ERROR_URL, + baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL); } ProducerRegistrationInfo producerEiRegistratioInfo(String typeId) - throws JsonMappingException, JsonProcessingException { + throws JsonMappingException, JsonProcessingException { Collection types = new ArrayList<>(); types.add(producerEiTypeRegistrationInfo(typeId)); return new ProducerRegistrationInfo(types, // - baseUrl() + ProducerSimulatorController.JOB_CREATED_URL, - baseUrl() + ProducerSimulatorController.JOB_DELETED_URL, - baseUrl() + ProducerSimulatorController.SUPERVISION_URL); + baseUrl() + ProducerSimulatorController.JOB_CREATED_URL, + baseUrl() + ProducerSimulatorController.JOB_DELETED_URL, + baseUrl() + ProducerSimulatorController.SUPERVISION_URL); } ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException { @@ -514,17 +523,17 @@ class ApplicationTest { 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 // - + "]" // - + "}"; // + + "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," // + + "\"type\": \"object\"," // + + "\"properties\": {" // + + EI_JOB_PROPERTY + " : {" // + + " \"type\": \"string\"" // + + " }" // + + "}," // + + "\"required\": [" // + + EI_JOB_PROPERTY // + + "]" // + + "}"; // return jsonObject(schemaStr); } @@ -533,7 +542,7 @@ class ApplicationTest { } private EiJob putEiJob(String eiTypeId, String jobId) - throws JsonMappingException, JsonProcessingException, ServiceException { + throws JsonMappingException, JsonProcessingException, ServiceException { String url = ConsumerConsts.API_ROOT + "/eitypes/" + eiTypeId + "/eijobs/" + jobId; String body = gson.toJson(eiJobInfo()); @@ -543,7 +552,7 @@ class ApplicationTest { } private EiType putEiProducerWithOneTypeRejecting(String producerId, String eiTypeId) - throws JsonMappingException, JsonProcessingException, ServiceException { + throws JsonMappingException, JsonProcessingException, ServiceException { String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId; String body = gson.toJson(producerEiRegistratioInfoRejecting(eiTypeId)); @@ -552,7 +561,7 @@ class ApplicationTest { } private EiType putEiProducerWithOneType(String producerId, String eiTypeId) - throws JsonMappingException, JsonProcessingException, ServiceException { + throws JsonMappingException, JsonProcessingException, ServiceException { String url = ProducerConsts.API_ROOT + "/eiproducers/" + producerId; String body = gson.toJson(producerEiRegistratioInfo(eiTypeId)); @@ -567,14 +576,14 @@ class ApplicationTest { 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(); + .keyStoreType(config.keyStoreType()) // + .keyStorePassword(config.keyStorePassword()) // + .keyStore(config.keyStore()) // + .keyPassword(config.keyPassword()) // + .isTrustStoreUsed(useTrustValidation) // + .trustStore(config.trustStore()) // + .trustStorePassword(config.trustStorePassword()) // + .build(); return new AsyncRestClient(baseUrl(), config); } @@ -588,16 +597,16 @@ class ApplicationTest { } private void testErrorCode(Mono request, HttpStatus expStatus, String responseContains, - boolean expectApplicationProblemJsonMediaType) { + boolean expectApplicationProblemJsonMediaType) { StepVerifier.create(request) // - .expectSubscription() // - .expectErrorMatches( - t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) // - .verify(); + .expectSubscription() // + .expectErrorMatches( + t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) // + .verify(); } private boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains, - boolean expectApplicationProblemJsonMediaType) { + boolean expectApplicationProblemJsonMediaType) { assertTrue(throwable instanceof WebClientResponseException); WebClientResponseException responseException = (WebClientResponseException) throwable; assertThat(responseException.getStatusCode()).isEqualTo(expStatus); -- 2.16.6