From 6a8a0d5350a77b6d1e4a8f95c0fe8fbfeef77339 Mon Sep 17 00:00:00 2001 From: PatrikBuhr Date: Mon, 9 Mar 2020 08:01:52 +0100 Subject: [PATCH] REST error codes Added tests of error codes for the policy agent REST API, and fixed problems found. Updated swagger generated API spec. Fixed so that the HttpStatus is propagated back in Dmaap responses also for successful cases. This commit resolves NONRTRIC-155 and NONRTRIC-156 Change-Id: I061c9b22787916c196b2ba1da552413e05d244ca Issue-ID: NONRTRIC-155 Signed-off-by: PatrikBuhr --- policy-agent/docs/api.yaml | 147 ++++++++----- .../java/org/oransc/policyagent/SwaggerConfig.java | 10 +- .../policyagent/clients/A1ClientFactory.java | 18 +- .../policyagent/clients/AsyncRestClient.java | 105 +++++----- .../policyagent/controllers/PolicyController.java | 60 ++++-- .../controllers/RicRepositoryController.java | 22 +- .../policyagent/controllers/ServiceController.java | 32 ++- .../policyagent/controllers/ServiceStatus.java | 6 +- .../policyagent/controllers/StatusController.java | 2 + .../policyagent/dmaap/DmaapMessageConsumer.java | 4 + .../policyagent/dmaap/DmaapMessageHandler.java | 87 ++++---- .../org/oransc/policyagent/ApplicationTest.java | 232 ++++++++++++--------- .../policyagent/clients/AsyncRestClientTest.java | 22 +- .../configuration/ApplicationConfigTest.java | 2 +- .../policyagent/dmaap/DmaapMessageHandlerTest.java | 36 ++-- 15 files changed, 454 insertions(+), 331 deletions(-) diff --git a/policy-agent/docs/api.yaml b/policy-agent/docs/api.yaml index 1c41d420..9c7d96ef 100644 --- a/policy-agent/docs/api.yaml +++ b/policy-agent/docs/api.yaml @@ -1,21 +1,21 @@ swagger: '2.0' info: - description: This page lists all the rest apis for Policy server. + description: This page lists all the rest apis for the service. version: '1.0' - title: Policy server + title: A1 Policy management service host: 'localhost:8081' basePath: / tags: - - name: operation-handler - description: Operation Handler - - name: policy-controller + - name: A1 Policy Management description: Policy Controller - - name: ric-repository-controller + - name: Health check + description: Status Controller + - name: RIC Repository description: Ric Repository Controller - - name: service-controller + - name: Service registry and supervision description: Service Controller - - name: status-controller - description: Status Controller + - name: operation-handler + description: Operation Handler - name: web-mvc-links-handler description: Web Mvc Links Handler paths: @@ -328,7 +328,7 @@ paths: /policies: get: tags: - - policy-controller + - A1 Policy Management summary: Query policies operationId: getPoliciesUsingGET produces: @@ -361,12 +361,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC or type not found + schema: + type: string deprecated: false /policy: get: tags: - - policy-controller + - A1 Policy Management summary: Returns a policy configuration operationId: getPolicyUsingGET produces: @@ -382,20 +384,16 @@ paths: description: Policy found schema: type: object - '204': - description: Policy is not found - schema: - type: string '401': description: Unauthorized '403': description: Forbidden '404': - description: Not Found + description: Policy is not found deprecated: false put: tags: - - policy-controller + - A1 Policy Management summary: Put a policy operationId: putPolicyUsingPUT consumes: @@ -431,21 +429,33 @@ paths: type: string responses: '200': - description: Policy created or updated + description: Policy updated schema: type: string '201': - description: Created + description: Policy created + schema: + type: string '401': description: Unauthorized '403': description: Forbidden '404': - description: Not Found + description: RIC or policy type is not found + schema: + type: string + '405': + description: Change is not allowed + schema: + type: string + '423': + description: RIC is locked + schema: + type: string deprecated: false delete: tags: - - policy-controller + - A1 Policy Management summary: Delete a policy operationId: deletePolicyUsingDELETE produces: @@ -469,11 +479,19 @@ paths: description: Unauthorized '403': description: Forbidden + '404': + description: Policy is not found + schema: + type: string + '423': + description: RIC is locked + schema: + type: string deprecated: false /policy_schema: get: tags: - - policy-controller + - A1 Policy Management summary: Returns one policy type schema definition operationId: getPolicySchemaUsingGET produces: @@ -494,12 +512,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC is not found + schema: + type: string deprecated: false /policy_schemas: get: tags: - - policy-controller + - A1 Policy Management summary: Returns policy type schema definitions operationId: getPolicySchemasUsingGET produces: @@ -522,12 +542,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC is not found + schema: + type: string deprecated: false /policy_status: get: tags: - - policy-controller + - A1 Policy Management summary: Returns a policy status operationId: getPolicyStatusUsingGET produces: @@ -543,21 +565,19 @@ paths: description: Policy status schema: type: object - '204': - description: Policy is not found - schema: - type: string '401': description: Unauthorized '403': description: Forbidden '404': - description: Not Found + description: Policy is not found + schema: + type: string deprecated: false /policy_types: get: tags: - - policy-controller + - A1 Policy Management summary: Query policy type names operationId: getPolicyTypesUsingGET produces: @@ -580,12 +600,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC is not found + schema: + type: string deprecated: false /ric: get: tags: - - ric-repository-controller + - RIC Repository summary: Returns the name of a RIC managing one Mananged Element operationId: getRicUsingGET produces: @@ -613,7 +635,7 @@ paths: /rics: get: tags: - - ric-repository-controller + - RIC Repository summary: Query NearRT RIC information operationId: getRicsUsingGET produces: @@ -636,12 +658,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: Policy type is not found + schema: + type: string deprecated: false /service: put: tags: - - service-controller + - Service registry and supervision summary: Register a service operationId: putServiceUsingPUT consumes: @@ -662,6 +686,10 @@ paths: type: string '201': description: Created + '400': + description: Cannot parse the ServiceRegistrationInfo + schema: + type: string '401': description: Unauthorized '403': @@ -672,7 +700,7 @@ paths: /services: get: tags: - - service-controller + - Service registry and supervision summary: Returns service information operationId: getServicesUsingGET produces: @@ -695,19 +723,21 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: Service is not found + schema: + type: string deprecated: false delete: tags: - - service-controller + - Service registry and supervision summary: Delete a service operationId: deleteServiceUsingDELETE produces: - '*/*' parameters: - - name: serviceName + - name: name in: query - description: serviceName + description: name required: true type: string responses: @@ -716,26 +746,32 @@ paths: schema: type: string '204': - description: No Content + description: OK + schema: + type: string '401': description: Unauthorized '403': description: Forbidden + '404': + description: Service not found + schema: + type: string deprecated: false /services/keepalive: post: tags: - - service-controller - summary: Keep the poilicies alive for a service + - Service registry and supervision + summary: Keep the policies alive for a service operationId: keepAliveServiceUsingPOST consumes: - application/json produces: - '*/*' parameters: - - name: serviceName + - name: name in: query - description: serviceName + description: name required: true type: string responses: @@ -755,7 +791,7 @@ paths: /status: get: tags: - - status-controller + - Health check summary: Returns status and statistics of this service operationId: getStatusUsingGET produces: @@ -786,9 +822,9 @@ definitions: title: 'Map«string,Link»' additionalProperties: $ref: '#/definitions/Link' - Mono«ResponseEntity«Void»»: + Mono«ResponseEntity«object»»: type: object - title: Mono«ResponseEntity«Void»» + title: Mono«ResponseEntity«object»» Mono«ResponseEntity«string»»: type: object title: Mono«ResponseEntity«string»» @@ -822,14 +858,14 @@ definitions: description: O1 identities for managed entities items: type: string - name: - type: string - description: identity of the ric policyTypes: type: array description: supported policy types items: type: string + ricName: + type: string + description: identity of the ric title: RicInfo ServiceRegistrationInfo: type: object @@ -848,6 +884,9 @@ definitions: ServiceStatus: type: object properties: + callbackUrl: + type: string + description: callback for notifying of RIC recovery keepAliveIntervalSeconds: type: integer format: int64 diff --git a/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java b/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java index df5e7b4f..01065f9c 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java @@ -36,17 +36,17 @@ import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** - * Swagger configuration class that uses swagger2 documentation type and scans all the controllers - * under org.oransc.policyagent.controllers package. To access the swagger gui go to - * http://ip:port/swagger-ui.html + * Swagger configuration class that uses swagger2 documentation type and scans + * all the controllers under org.oransc.policyagent.controllers package. To + * access the swagger gui go to http://ip:port/swagger-ui.html * */ @Configuration @EnableSwagger2 public class SwaggerConfig extends WebMvcConfigurationSupport { - static final String API_TITLE = "Policy server"; - static final String DESCRIPTION = "This page lists all the rest apis for Policy server."; + static final String API_TITLE = "A1 Policy management service"; + static final String DESCRIPTION = "This page lists all the rest apis for the service."; static final String VERSION = "1.0"; static final String RESOURCES_PATH = "classpath:/META-INF/resources/"; static final String WEBJARS_PATH = RESOURCES_PATH + "webjars/"; diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java index d9069303..4a61479a 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java @@ -29,7 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired; import reactor.core.publisher.Mono; /** - * Factory for A1 clients that supports four different protocol versions of the A1 api. + * Factory for A1 clients that supports four different protocol versions of the + * A1 api. */ public class A1ClientFactory { @@ -46,16 +47,17 @@ public class A1ClientFactory { * Creates an A1 client with the correct A1 protocol for the provided Ric. * *

- * It detects the protocol version by trial and error, since there is no getVersion method specified in the A1 - * api yet. + * It detects the protocol version by trial and error, since there is no + * getVersion method specified in the A1 api yet. * *

- * As a side effect it also sets the protocol version in the provided Ric. This means that after the first - * successful creation it won't have to try which protocol to use, but can create the client directly. + * As a side effect it also sets the protocol version in the provided Ric. This + * means that after the first successful creation it won't have to try which + * protocol to use, but can create the client directly. * - * @param ric The Ric to get a client for. - * @return a client with the correct protocol, or a ServiceException if none of the protocols are supported by the - * Ric. + * @param ric The RIC to get a client for. + * @return a client with the correct protocol, or a ServiceException if none of + * the protocols are supported by the Ric. */ public Mono createA1Client(Ric ric) { return getProtocolVersion(ric) // diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java index 12c07453..2435ef08 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java @@ -24,9 +24,11 @@ import java.lang.invoke.MethodHandles; 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.lang.Nullable; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; import reactor.core.publisher.Mono; public class AsyncRestClient { @@ -34,79 +36,84 @@ public class AsyncRestClient { private final WebClient client; private final String baseUrl; - public class AsyncRestClientException extends Exception { - - private static final long serialVersionUID = 1L; - - public AsyncRestClientException(String message) { - super(message); - } - } - public AsyncRestClient(String baseUrl) { this.client = WebClient.create(baseUrl); this.baseUrl = baseUrl; } - public Mono post(String uri, String body) { + public Mono> postForEntity(String uri, @Nullable String body) { logger.debug("POST uri = '{}{}''", baseUrl, uri); - return client.post() // + Mono bodyProducer = body != null ? Mono.just(body) : Mono.empty(); + RequestHeadersSpec request = client.post() // .uri(uri) // .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + .body(bodyProducer, String.class); + return retrieve(request); + } + + public Mono post(String uri, @Nullable String body) { + return postForEntity(uri, body) // + .flatMap(this::toBody); } public Mono postWithAuthHeader(String uri, String body, String username, String password) { logger.debug("POST (auth) uri = '{}{}''", baseUrl, uri); - return client.post() // + RequestHeadersSpec request = client.post() // .uri(uri) // .headers(headers -> headers.setBasicAuth(username, password)) // .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + .bodyValue(body); + return retrieve(request) // + .flatMap(this::toBody); } - public Mono put(String uri, String body) { + public Mono> putForEntity(String uri, String body) { logger.debug("PUT uri = '{}{}''", baseUrl, uri); - return client.put() // + RequestHeadersSpec request = client.put() // .uri(uri) // .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + .bodyValue(body); + return retrieve(request); } - public Mono get(String uri) { + public Mono put(String uri, String body) { + return putForEntity(uri, body) // + .flatMap(this::toBody); + } + + public Mono> getForEntity(String uri) { logger.debug("GET uri = '{}{}''", baseUrl, uri); - return client.get() // - .uri(uri) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + RequestHeadersSpec request = client.get().uri(uri); + return retrieve(request); } - public Mono delete(String uri) { + public Mono get(String uri) { + return getForEntity(uri) // + .flatMap(this::toBody); + } + + public Mono> deleteForEntity(String uri) { logger.debug("DELETE uri = '{}{}''", baseUrl, uri); - return client.delete() // - .uri(uri) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + RequestHeadersSpec request = client.delete().uri(uri); + return retrieve(request); + } + + public Mono delete(String uri) { + return deleteForEntity(uri) // + .flatMap(this::toBody); } + + private Mono> retrieve(RequestHeadersSpec request) { + return request.retrieve() // + .toEntity(String.class); + } + + Mono toBody(ResponseEntity entity) { + if (entity.getBody() == null) { + return Mono.just(""); + } else { + return Mono.just(entity.getBody()); + } + } + } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java index 3a49ff12..1af607f4 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java @@ -55,7 +55,7 @@ import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController -@Api(value = "Policy Management API") +@Api(tags = "A1 Policy Management") public class PolicyController { private final Rics rics; @@ -80,7 +80,8 @@ public class PolicyController { @ApiOperation(value = "Returns policy type schema definitions") @ApiResponses( value = { - @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List")}) + @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List"), // + @ApiResponse(code = 404, message = "RIC is not found", response = String.class)}) public ResponseEntity getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) { synchronized (this.policyTypes) { if (ricName == null) { @@ -99,7 +100,10 @@ public class PolicyController { @GetMapping("/policy_schema") @ApiOperation(value = "Returns one policy type schema definition") - @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy schema", response = Object.class)}) + @ApiResponses( + value = { // + @ApiResponse(code = 200, message = "Policy schema", response = Object.class), + @ApiResponse(code = 404, message = "RIC is not found", response = String.class)}) public ResponseEntity getPolicySchema(@RequestParam(name = "id", required = true) String id) { try { PolicyType type = policyTypes.getType(id); @@ -112,11 +116,13 @@ public class PolicyController { @GetMapping("/policy_types") @ApiOperation(value = "Query policy type names") @ApiResponses( - value = {@ApiResponse( - code = 200, - message = "Policy type names", - response = String.class, - responseContainer = "List")}) + value = { + @ApiResponse( + code = 200, + message = "Policy type names", + response = String.class, + responseContainer = "List"), + @ApiResponse(code = 404, message = "RIC is not found", response = String.class)}) public ResponseEntity getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) { synchronized (this.policyTypes) { if (ricName == null) { @@ -138,7 +144,7 @@ public class PolicyController { @ApiResponses( value = { // @ApiResponse(code = 200, message = "Policy found", response = Object.class), // - @ApiResponse(code = 204, message = "Policy is not found")} // + @ApiResponse(code = 404, message = "Policy is not found")} // ) public ResponseEntity getPolicy( // @RequestParam(name = "instance", required = true) String instance) { @@ -146,13 +152,17 @@ public class PolicyController { Policy p = policies.getPolicy(instance); return new ResponseEntity<>(p.json(), HttpStatus.OK); } catch (ServiceException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); } } @DeleteMapping("/policy") @ApiOperation(value = "Delete a policy", response = Object.class) - @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted", response = Object.class)}) + @ApiResponses( + value = { // + @ApiResponse(code = 204, message = "Policy deleted", response = Object.class), + @ApiResponse(code = 404, message = "Policy is not found", response = String.class), + @ApiResponse(code = 423, message = "RIC is locked", response = String.class)}) public Mono> deletePolicy( // @RequestParam(name = "instance", required = true) String id) { Policy policy = policies.get(id); @@ -174,7 +184,13 @@ public class PolicyController { @PutMapping(path = "/policy") @ApiOperation(value = "Put a policy", response = String.class) - @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy created or updated")}) + @ApiResponses( + value = { // + @ApiResponse(code = 201, message = "Policy created"), // + @ApiResponse(code = 200, message = "Policy updated"), // + @ApiResponse(code = 423, message = "RIC is locked", response = String.class), // + @ApiResponse(code = 404, message = "RIC or policy type is not found", response = String.class), // + @ApiResponse(code = 405, message = "Change is not allowed", response = String.class)}) public Mono> putPolicy( // @RequestParam(name = "type", required = true) String typeName, // @RequestParam(name = "instance", required = true) String instanceId, // @@ -183,7 +199,6 @@ public class PolicyController { @RequestBody Object jsonBody) { String jsonString = gson.toJson(jsonBody); - Ric ric = rics.get(ricName); PolicyType type = policyTypes.get(typeName); if (ric != null && type != null && ric.getState() == Ric.RicState.IDLE) { @@ -209,8 +224,8 @@ public class PolicyController { .onErrorResume(t -> Mono.just(new ResponseEntity<>(t.getMessage(), HttpStatus.METHOD_NOT_ALLOWED))); } - return ric == null && type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)) - : Mono.just(new ResponseEntity<>(HttpStatus.CONFLICT)); // Recovering + return ric == null || type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)) + : Mono.just(new ResponseEntity<>(HttpStatus.LOCKED)); // Recovering } private Mono validateModifiedPolicy(Policy policy) { @@ -228,12 +243,19 @@ public class PolicyController { @ApiOperation(value = "Query policies") @ApiResponses( value = { - @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List")}) + @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List"), + @ApiResponse(code = 404, message = "RIC or type not found", response = String.class)}) public ResponseEntity getPolicies( // @RequestParam(name = "type", required = false) String type, // @RequestParam(name = "ric", required = false) String ric, // @RequestParam(name = "service", required = false) String service) // { + if ((type != null && this.policyTypes.get(type) == null)) { + return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND); + } + if ((ric != null && this.rics.get(ric) == null)) { + return new ResponseEntity<>("RIC not found", HttpStatus.NOT_FOUND); + } synchronized (policies) { Collection result = null; @@ -253,7 +275,7 @@ public class PolicyController { try { policiesJson = policiesToJson(result); } catch (ServiceException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity<>(policiesJson, HttpStatus.OK); } @@ -264,7 +286,7 @@ public class PolicyController { @ApiResponses( value = { // @ApiResponse(code = 200, message = "Policy status", response = Object.class), // - @ApiResponse(code = 204, message = "Policy is not found", response = String.class)} // + @ApiResponse(code = 404, message = "Policy is not found", response = String.class)} // ) public Mono> getPolicyStatus( // @RequestParam(name = "instance", required = true) String instance) { @@ -275,7 +297,7 @@ public class PolicyController { .flatMap(client -> client.getPolicyStatus(policy)) // .flatMap(status -> Mono.just(new ResponseEntity<>(status, HttpStatus.OK))); } catch (ServiceException e) { - return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT)); + return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND)); } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java index f6fc8500..a96766d3 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java @@ -32,7 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.repository.PolicyTypes; import org.oransc.policyagent.repository.Ric; import org.oransc.policyagent.repository.Rics; import org.springframework.beans.factory.annotation.Autowired; @@ -43,20 +43,19 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController -@Api(value = "RIC Management API") +@Api(tags = "RIC Repository") public class RicRepositoryController { @Autowired private Rics rics; + @Autowired + PolicyTypes types; + private static Gson gson = new GsonBuilder() // .serializeNulls() // .create(); // - @Autowired - RicRepositoryController(ApplicationConfig appConfig) { - } - /** * Example: http://localhost:8081/rics?managedElementId=kista_1 */ @@ -80,18 +79,21 @@ public class RicRepositoryController { } /** - * @return a Json array of all RIC data - * Example: http://localhost:8081/ric + * @return a Json array of all RIC data Example: http://localhost:8081/ric */ @GetMapping("/rics") @ApiOperation(value = "Query NearRT RIC information") @ApiResponses( value = { // - @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List") // - }) + @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List"), // + @ApiResponse(code = 404, message = "Policy type is not found", response = String.class)}) public ResponseEntity getRics( @RequestParam(name = "policyType", required = false) String supportingPolicyType) { + if ((supportingPolicyType != null) && (this.types.get(supportingPolicyType) == null)) { + return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND); + } + List result = new ArrayList<>(); synchronized (rics) { for (Ric ric : rics.getRics()) { diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java index 464511f4..35348c7d 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java @@ -23,6 +23,7 @@ package org.oransc.policyagent.controllers; 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; @@ -49,6 +50,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController +@Api(tags = "Service registry and supervision") public class ServiceController { private final Services services; @@ -67,10 +69,16 @@ public class ServiceController { @GetMapping("/services") @ApiOperation(value = "Returns service information") @ApiResponses( - value = {@ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List")}) + value = { // + @ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List"), // + @ApiResponse(code = 404, message = "Service is not found", response = String.class)}) public ResponseEntity getServices(// @RequestParam(name = "name", required = false) String name) { + if (name != null && this.services.get(name) == null) { + return new ResponseEntity<>("Service not found", HttpStatus.NOT_FOUND); + } + Collection servicesStatus = new ArrayList<>(); synchronized (this.services) { for (Service s : this.services.getAll()) { @@ -85,11 +93,15 @@ public class ServiceController { } private ServiceStatus toServiceStatus(Service s) { - return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds()); + return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(), + s.getCallbackUrl()); } @ApiOperation(value = "Register a service") - @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = String.class)}) + @ApiResponses( + value = { // + @ApiResponse(code = 200, message = "OK", response = String.class), + @ApiResponse(code = 400, message = "Cannot parse the ServiceRegistrationInfo", response = String.class)}) @PutMapping("/service") public ResponseEntity putService(// @RequestBody ServiceRegistrationInfo registrationInfo) { @@ -97,12 +109,15 @@ public class ServiceController { this.services.put(toService(registrationInfo)); return new ResponseEntity<>("OK", HttpStatus.OK); } catch (Exception e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } } @ApiOperation(value = "Delete a service") - @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")}) + @ApiResponses( + value = { // + @ApiResponse(code = 204, message = "OK"), + @ApiResponse(code = 404, message = "Service not found", response = String.class)}) @DeleteMapping("/services") public ResponseEntity deleteService(// @RequestParam(name = "name", required = true) String serviceName) { @@ -113,13 +128,14 @@ public class ServiceController { removePolicies(service); return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT); } catch (Exception e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); } } - @ApiOperation(value = "Keep the poilicies alive for a service") + @ApiOperation(value = "Keep the policies alive for a service") @ApiResponses( - value = {@ApiResponse(code = 200, message = "Policies timeout supervision refreshed"), + value = { // + @ApiResponse(code = 200, message = "Policies timeout supervision refreshed"), @ApiResponse(code = 404, message = "The service is not found, needs re-registration")}) @PostMapping("/services/keepalive") public ResponseEntity keepAliveService(// diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java index fc80834c..1a9cf327 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java @@ -38,10 +38,14 @@ public class ServiceStatus { @ApiModelProperty(value = "time since last invocation by the service") public final long timeSincePingSeconds; - ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds) { + @ApiModelProperty(value = "callback for notifying of RIC recovery") + public String callbackUrl; + + ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds, String callbackUrl) { this.serviceName = name; this.keepAliveIntervalSeconds = keepAliveIntervalSeconds; this.timeSincePingSeconds = timeSincePingSeconds; + this.callbackUrl = callbackUrl; } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java index cacf64d5..c55d5be2 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java @@ -20,6 +20,7 @@ package org.oransc.policyagent.controllers; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; @@ -31,6 +32,7 @@ import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController +@Api(tags = "Health check") public class StatusController { @GetMapping("/status") diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java index e72fcf0b..3cc1b77d 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java @@ -40,6 +40,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +/** + * The class fetched incoming requests from DMAAP on regular intervals. Each + * received request is proceesed by DmaapMessageHandler. + */ @Component public class DmaapMessageConsumer implements Runnable { diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java index dc979de0..b0c2cafa 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java @@ -17,7 +17,6 @@ * limitations under the License. * ========================LICENSE_END=================================== */ - package org.oransc.policyagent.dmaap; import com.google.gson.Gson; @@ -29,19 +28,25 @@ import java.util.Optional; import org.onap.dmaap.mr.client.MRBatchingPublisher; import org.oransc.policyagent.clients.AsyncRestClient; +import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation; import org.oransc.policyagent.exceptions.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; +/** + * The class handles incoming requests from DMAAP. + *

+ * That means: invoke a REST call towards this services and to send back a + * response though DMAAP + */ public class DmaapMessageHandler { - private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandler.class); - private static Gson gson = new GsonBuilder() // .create(); // - private final MRBatchingPublisher dmaapClient; private final AsyncRestClient agentClient; @@ -60,61 +65,46 @@ public class DmaapMessageHandler { Mono createTask(String msg) { try { DmaapRequestMessage dmaapRequestMessage = gson.fromJson(msg, ImmutableDmaapRequestMessage.class); - return this.invokePolicyAgent(dmaapRequestMessage) // - .onErrorResume(t -> handleAgentCallError(t, msg, dmaapRequestMessage)) // - .flatMap(response -> sendDmaapResponse(response, dmaapRequestMessage, HttpStatus.OK)); + .onErrorResume(t -> handleAgentCallError(t, dmaapRequestMessage)) // + .flatMap( + response -> sendDmaapResponse(response.getBody(), dmaapRequestMessage, response.getStatusCode())); } catch (Exception e) { logger.warn("Received unparsable message from DMAAP: {}", msg); - return Mono.error(e); + return Mono.error(e); // Cannot make any response } } - private Mono handleAgentCallError(Throwable t, String origianalMessage, - DmaapRequestMessage dmaapRequestMessage) { + private Mono> handleAgentCallError(Throwable t, DmaapRequestMessage dmaapRequestMessage) { logger.debug("Agent call failed: {}", t.getMessage()); - if (t instanceof ServiceException) { - String errorMessage = prepareBadOperationErrorMessage(t, origianalMessage); - return sendDmaapResponse(errorMessage, dmaapRequestMessage, HttpStatus.NOT_FOUND) // - .flatMap(notUsed -> Mono.empty()); - } else { - return sendDmaapResponse(t.toString(), dmaapRequestMessage, HttpStatus.NOT_FOUND) // - .flatMap(notUsed -> Mono.empty()); + HttpStatus status = HttpStatus.NOT_FOUND; + String errorMessage = t.getMessage(); + if (t instanceof WebClientResponseException) { + WebClientResponseException exception = (WebClientResponseException) t; + status = exception.getStatusCode(); + errorMessage = exception.getResponseBodyAsString(); } + return sendDmaapResponse(errorMessage, dmaapRequestMessage, status) // + .flatMap(notUsed -> Mono.empty()); } - private String prepareBadOperationErrorMessage(Throwable t, String origianalMessage) { - String badOperation = origianalMessage.substring(origianalMessage.indexOf("operation\":\"") + 12, - origianalMessage.indexOf(",\"url\":")); - String errorMessage = t.getMessage().replace("null", badOperation); - return errorMessage; - } - - private Mono invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) { + private Mono> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) { DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation(); - if (operation == null) { - return Mono.error(new ServiceException("Not implemented operation: " + operation)); - } - Mono result = null; + String uri = dmaapRequestMessage.url(); - switch (operation) { - case DELETE: - result = agentClient.delete(uri); - break; - case GET: - result = agentClient.get(uri); - break; - case PUT: - result = agentClient.put(uri, payload(dmaapRequestMessage)); - break; - case POST: - result = agentClient.post(uri, payload(dmaapRequestMessage)); - break; - default: - // Nothing, can never get here. + if (operation == Operation.DELETE) { + return agentClient.deleteForEntity(uri); + } else if (operation == Operation.GET) { + return agentClient.getForEntity(uri); + } else if (operation == Operation.PUT) { + return agentClient.putForEntity(uri, payload(dmaapRequestMessage)); + } else if (operation == Operation.POST) { + return agentClient.postForEntity(uri, payload(dmaapRequestMessage)); + } else { + return Mono.error(new ServiceException("Not implemented operation: " + operation)); } - return result; + } private String payload(DmaapRequestMessage message) { @@ -129,7 +119,7 @@ public class DmaapMessageHandler { private Mono sendDmaapResponse(String response, DmaapRequestMessage dmaapRequestMessage, HttpStatus status) { - return getDmaapResponseMessage(dmaapRequestMessage, response, status) // + return createDmaapResponseMessage(dmaapRequestMessage, response, status) // .flatMap(this::sendToDmaap) // .onErrorResume(this::handleResponseCallError); } @@ -146,11 +136,11 @@ public class DmaapMessageHandler { } private Mono handleResponseCallError(Throwable t) { - logger.debug("Failed to send respons to DMaaP: {}", t.getMessage()); + logger.debug("Failed to respond: {}", t.getMessage()); return Mono.empty(); } - private Mono getDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response, + private Mono createDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response, HttpStatus status) { DmaapResponseMessage dmaapResponseMessage = ImmutableDmaapResponseMessage.builder() // .status(status.toString()) // @@ -162,7 +152,6 @@ public class DmaapMessageHandler { .timestamp(dmaapRequestMessage.timestamp()) // .build(); String str = gson.toJson(dmaapResponseMessage); - return Mono.just(str); } diff --git a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java index 05485d7c..377ca79e 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java @@ -22,6 +22,7 @@ package org.oransc.policyagent; 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; import com.google.gson.GsonBuilder; @@ -29,7 +30,6 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonParser; -import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -40,6 +40,7 @@ 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.policyagent.clients.AsyncRestClient; import org.oransc.policyagent.configuration.ApplicationConfig; import org.oransc.policyagent.configuration.ImmutableRicConfig; import org.oransc.policyagent.configuration.RicConfig; @@ -70,15 +71,15 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatus.Series; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @@ -141,26 +142,6 @@ public class ApplicationTest { @LocalServerPort private int port; - private final RestTemplate restTemplate = new RestTemplate(); - - public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { - - @Override - public boolean hasError(ClientHttpResponse httpResponse) throws IOException { - return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR - || httpResponse.getStatusCode().series() == Series.SERVER_ERROR); - } - - @Override - public void handleError(ClientHttpResponse httpResponse) throws IOException { - System.out.println("Error " + httpResponse.toString()); - } - } - - private void setRestErrorhandler() { - restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler()); - } - @BeforeEach public void reset() { rics.clear(); @@ -182,14 +163,14 @@ public class ApplicationTest { @Test public void testGetRics() throws Exception { addRic("kista_1"); - String url = baseUrl() + "/rics"; - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println(rsp); + this.addPolicyType("type1", "kista_1"); + String url = "/rics?policyType=type1"; + String rsp = restClient().get(url).block(); assertThat(rsp).contains("kista_1"); - url = baseUrl() + "/rics?policyType=STD_PolicyModelUnconstrained_0.2.0"; - rsp = this.restTemplate.getForObject(url, String.class); - assertThat(rsp).isEqualTo("[]"); + // Non existing policy type + url = "/rics?policyType=XXXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -219,18 +200,19 @@ public class ApplicationTest { String managedElementId = "kista_1"; addRic(ricName, managedElementId); - String url = baseUrl() + "/ric?managedElementId=" + managedElementId; - String rsp = this.restTemplate.getForObject(url, String.class); - + String url = "/ric?managedElementId=" + managedElementId; + String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo(ricName); + + // test GET RIC for ManagedElement that does not exist + url = "/ric?managedElementId=" + "junk"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } - @Test - public void testGetRicForManagedElementThatDoesNotExist() throws Exception { - this.setRestErrorhandler(); - String url = baseUrl() + "/ric?managedElementId=kista_1"; - ResponseEntity entity = this.restTemplate.getForEntity(url, String.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + private String putPolicyUrl(String serviceName, String ricName, String policyTypeName, String policyInstanceId) { + String url = "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName + + "&service=" + serviceName; + return url; } @Test @@ -243,12 +225,11 @@ public class ApplicationTest { putService(serviceName); addPolicyType(policyTypeName, ricName); - String url = baseUrl() + "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName - + "&service=" + serviceName; - final String json = jsonString(); + String url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId); + final String policyBody = jsonString(); this.rics.getRic(ricName).setState(Ric.RicState.IDLE); - this.restTemplate.put(url, createJsonHttpEntity(json)); + restClient().put(url, policyBody).block(); Policy policy = policies.getPolicy(policyInstanceId); assertThat(policy).isNotNull(); @@ -256,10 +237,21 @@ public class ApplicationTest { assertThat(policy.ownerServiceName()).isEqualTo(serviceName); assertThat(policy.ric().name()).isEqualTo("ric1"); - url = baseUrl() + "/policies"; - String rsp = this.restTemplate.getForObject(url, String.class); + url = "/policies"; + String rsp = restClient().get(url).block(); assertThat(rsp.contains(policyInstanceId)).isTrue(); + // Test of error codes + url = putPolicyUrl(serviceName, ricName + "XX", policyTypeName, policyInstanceId); + testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND); + + url = putPolicyUrl(serviceName, ricName, policyTypeName + "XX", policyInstanceId); + testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND); + + url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId); + this.rics.getRic(ricName).setState(Ric.RicState.SYNCHRONIZING); + testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED); + this.rics.getRic(ricName).setState(Ric.RicState.IDLE); } @Test @@ -268,41 +260,40 @@ public class ApplicationTest { // In this case service is attempted to be changed this.addRic("ric1"); this.addRic("ricXXX"); - this.addPolicy("instance1", "type1", "service1", "ric1"); - this.setRestErrorhandler(); - String urlWrongRic = baseUrl() + "/policy?type=type1&instance=instance1&ric=ricXXX&service=service1"; - ResponseEntity entity = this.putForEntity(urlWrongRic, jsonString()); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.METHOD_NOT_ALLOWED); - Policy policy = policies.getPolicy("instance1"); - assertThat(policy.ric().name()).isEqualTo("ric1"); // Not changed + // Try change ric1 -> ricXXX + String urlWrongRic = putPolicyUrl("service1", "ricXXX", "type1", "instance1"); + testErrorCode(restClient().put(urlWrongRic, jsonString()), HttpStatus.METHOD_NOT_ALLOWED); } @Test public void testGetPolicy() throws Exception { - String url = baseUrl() + "/policy?instance=id"; + String url = "/policy?instance=id"; Policy policy = addPolicy("id", "typeName", "service1", "ric1"); { - String rsp = this.restTemplate.getForObject(url, String.class); + String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo(policy.json()); } { policies.remove(policy); - ResponseEntity rsp = this.restTemplate.getForEntity(url, String.class); - assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value()); + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } } @Test public void testDeletePolicy() throws Exception { - String url = baseUrl() + "/policy?instance=id"; addPolicy("id", "typeName", "service1", "ric1"); assertThat(policies.size()).isEqualTo(1); - this.restTemplate.delete(url); + String url = "/policy?instance=id"; + ResponseEntity entity = restClient().deleteForEntity(url).block(); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); assertThat(policies.size()).isEqualTo(0); + + // Delete a non existing policy + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -310,20 +301,23 @@ public class ApplicationTest { addPolicyType("type1", "ric1"); addPolicyType("type2", "ric2"); - String url = baseUrl() + "/policy_schemas"; - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println("*** " + rsp); + String url = "/policy_schemas"; + String rsp = this.restClient().get(url).block(); assertThat(rsp).contains("type1"); assertThat(rsp).contains("[{\"title\":\"type2\"}"); List info = parseSchemas(rsp); assertThat(info.size()).isEqualTo(2); - url = baseUrl() + "/policy_schemas?ric=ric1"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/policy_schemas?ric=ric1"; + rsp = restClient().get(url).block(); assertThat(rsp).contains("type1"); info = parseSchemas(rsp); assertThat(info.size()).isEqualTo(1); + + // Get schema for non existing RIC + url = "/policy_schemas?ric=ric1XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -331,11 +325,15 @@ public class ApplicationTest { addPolicyType("type1", "ric1"); addPolicyType("type2", "ric2"); - String url = baseUrl() + "/policy_schema?id=type1"; - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policy_schema?id=type1"; + String rsp = restClient().get(url).block(); System.out.println(rsp); assertThat(rsp).contains("type1"); assertThat(rsp).contains("title"); + + // Get non existing schema + url = "/policy_schema?id=type1XX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -343,22 +341,26 @@ public class ApplicationTest { addPolicyType("type1", "ric1"); addPolicyType("type2", "ric2"); - String url = baseUrl() + "/policy_types"; - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policy_types"; + String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]"); - url = baseUrl() + "/policy_types?ric=ric1"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/policy_types?ric=ric1"; + rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo("[\"type1\"]"); + + // Get policy types for non existing RIC + url = "/policy_types?ric=ric1XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test public void testGetPolicies() throws Exception { reset(); - String url = baseUrl() + "/policies"; addPolicy("id1", "type1", "service1"); - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policies"; + String rsp = restClient().get(url).block(); System.out.println(rsp); List info = parseList(rsp, PolicyInfo.class); assertThat(info).size().isEqualTo(1); @@ -375,19 +377,27 @@ public class ApplicationTest { addPolicy("id2", "type1", "service2"); addPolicy("id3", "type2", "service1"); - String url = baseUrl() + "/policies?type=type1"; - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policies?type=type1"; + String rsp = restClient().get(url).block(); System.out.println(rsp); assertThat(rsp).contains("id1"); assertThat(rsp).contains("id2"); assertThat(rsp.contains("id3")).isFalse(); - url = baseUrl() + "/policies?type=type1&service=service2"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/policies?type=type1&service=service2"; + rsp = restClient().get(url).block(); System.out.println(rsp); assertThat(rsp.contains("id1")).isFalse(); assertThat(rsp).contains("id2"); assertThat(rsp.contains("id3")).isFalse(); + + // Test get policies for non existing type + url = "/policies?type=type1XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); + + // Test get policies for non existing RIC + url = "/policies?ric=XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -395,9 +405,9 @@ public class ApplicationTest { // PUT putService("name"); - // GET - String url = baseUrl() + "/services?serviceName=name"; - String rsp = this.restTemplate.getForObject(url, String.class); + // GET one service + String url = "/services?name=name"; + String rsp = restClient().get(url).block(); List info = parseList(rsp, ServiceStatus.class); assertThat(info.size()).isEqualTo(1); ServiceStatus status = info.iterator().next(); @@ -405,27 +415,30 @@ public class ApplicationTest { assertThat(status.serviceName).isEqualTo("name"); // GET (all) - url = baseUrl() + "/services"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/services"; + rsp = restClient().get(url).block(); assertThat(rsp.contains("name")).isTrue(); System.out.println(rsp); // Keep alive - url = baseUrl() + "/services/keepalive?name=name"; - ResponseEntity entity = this.restTemplate.postForEntity(url, null, String.class); + url = "/services/keepalive?name=name"; + ResponseEntity entity = restClient().postForEntity(url, null).block(); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - // DELETE + // DELETE service assertThat(services.size()).isEqualTo(1); - url = baseUrl() + "/services?name=name"; - this.restTemplate.delete(url); + url = "/services?name=name"; + restClient().delete(url).block(); assertThat(services.size()).isEqualTo(0); // Keep alive, no registerred service - url = baseUrl() + "/services/keepalive?name=name"; - setRestErrorhandler(); - entity = this.restTemplate.postForEntity(url, null, String.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + testErrorCode(restClient().post("/services/keepalive?name=name", ""), HttpStatus.NOT_FOUND); + + // PUT servive with crap payload + testErrorCode(restClient().put("/service", "junk"), HttpStatus.BAD_REQUEST); + + // GET non existing servive + testErrorCode(restClient().get("/services?name=XXX"), HttpStatus.NOT_FOUND); } @Test @@ -433,9 +446,13 @@ public class ApplicationTest { addPolicy("id", "typeName", "service1", "ric1"); assertThat(policies.size()).isEqualTo(1); - String url = baseUrl() + "/policy_status?instance=id"; - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policy_status?instance=id"; + String rsp = restClient().get(url).block(); assertThat(rsp.equals("OK")).isTrue(); + + // GET non existing policy status + url = "/policy_status?instance=XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException { @@ -462,9 +479,8 @@ public class ApplicationTest { } private void putService(String name) { - String url = baseUrl() + "/service"; - HttpEntity entity = createJsonHttpEntity(createServiceJson(name)); - this.restTemplate.put(url, entity); + String url = "/service"; + restClient().put(url, createServiceJson(name)).block(); } private String baseUrl() { @@ -502,12 +518,12 @@ public class ApplicationTest { private void putPolicy(String name) { String putUrl = baseUrl + "/policy?type=type1&instance=" + name + "&ric=ric1&service=service1"; - this.restTemplate.put(putUrl, createJsonHttpEntity("{}")); + restTemplate.put(putUrl, createJsonHttpEntity("{}")); } private void deletePolicy(String name) { String deleteUrl = baseUrl + "/policy?instance=" + name; - this.restTemplate.delete(deleteUrl); + restTemplate.delete(deleteUrl); } } @@ -530,6 +546,24 @@ public class ApplicationTest { System.out.println("Concurrency test took " + Duration.between(startTime, Instant.now())); } + private AsyncRestClient restClient() { + return new AsyncRestClient(baseUrl()); + } + + private void testErrorCode(Mono request, HttpStatus expStatus) { + StepVerifier.create(request) // + .expectSubscription() // + .expectErrorMatches(t -> checkWebClientError(t, expStatus)) // + .verify(); + } + + private boolean checkWebClientError(Throwable t, HttpStatus expStatus) { + assertTrue(t instanceof WebClientResponseException); + WebClientResponseException e = (WebClientResponseException) t; + assertThat(e.getStatusCode()).isEqualTo(expStatus); + return true; + } + private MockA1Client getA1Client(String ricName) throws ServiceException { return a1ClientFactory.getOrCreateA1Client(ricName); } @@ -574,10 +608,6 @@ public class ApplicationTest { return new HttpEntity(content, headers); } - private ResponseEntity putForEntity(String url, String jsonBody) { - return restTemplate.exchange(url, HttpMethod.PUT, createJsonHttpEntity(jsonBody), String.class); - } - private static List parseList(String jsonString, Class clazz) { List result = new ArrayList<>(); JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray(); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java b/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java index 11f2409c..884b36fe 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java @@ -31,9 +31,9 @@ import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.oransc.policyagent.clients.AsyncRestClient.AsyncRestClientException; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -81,8 +81,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.get(REQUEST_URL); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -100,8 +100,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.put(REQUEST_URL, TEST_JSON); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -117,8 +117,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.delete(REQUEST_URL); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -136,8 +136,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.post(REQUEST_URL, TEST_JSON); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -155,7 +155,7 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.postWithAuthHeader(REQUEST_URL, TEST_JSON, USERNAME, PASSWORD); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } } diff --git a/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java index 17f09974..d30e95a4 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java @@ -62,7 +62,7 @@ public class ApplicationConfigTest { RicConfigUpdate update = appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null).blockFirst(); assertEquals(RicConfigUpdate.Type.ADDED, update.getType()); - assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configuraions."); + assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configurations."); assertEquals(RIC_CONFIG_1, appConfigUnderTest.getRic(RIC_CONFIG_1.name()), "Not correct Ric retrieved from configurations."); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java index 67d00a22..5ba538e8 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java @@ -53,6 +53,8 @@ import org.oransc.policyagent.repository.ImmutablePolicyType; import org.oransc.policyagent.repository.PolicyType; import org.oransc.policyagent.utils.LoggingUtils; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -102,6 +104,11 @@ public class DmaapMessageHandlerTest { return gson.toJson(dmaapRequestMessage(operation)); } + private Mono> okResponse() { + ResponseEntity entity = new ResponseEntity<>("OK", HttpStatus.OK); + return Mono.just(entity); + } + @Test public void testMessageParsing() { String message = dmaapInputMessage(Operation.DELETE); @@ -129,7 +136,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulDelete() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).delete(anyString()); + doReturn(okResponse()).when(agentClient).deleteForEntity(anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -141,7 +148,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).delete(URL); + verify(agentClient).deleteForEntity(URL); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -151,7 +158,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulGet() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).get(anyString()); + doReturn(okResponse()).when(agentClient).getForEntity(anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -161,7 +168,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).get(URL); + verify(agentClient).getForEntity(URL); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -171,7 +178,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulPut() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).put(anyString(), anyString()); + doReturn(okResponse()).when(agentClient).putForEntity(anyString(), anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -181,7 +188,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).put(URL, payloadAsString()); + verify(agentClient).putForEntity(URL, payloadAsString()); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -191,7 +198,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulPost() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).post(anyString(), anyString()); + doReturn(okResponse()).when(agentClient).postForEntity(anyString(), anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -201,7 +208,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).post(URL, payloadAsString()); + verify(agentClient).postForEntity(URL, payloadAsString()); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -211,8 +218,8 @@ public class DmaapMessageHandlerTest { @Test public void exceptionWhenCallingPolicyAgent_thenNotFoundResponse() throws IOException { - String errorCause = "Refused"; - doReturn(Mono.error(new Exception(errorCause))).when(agentClient).put(anyString(), any()); + WebClientResponseException except = new WebClientResponseException(400, "Refused", null, null, null, null); + doReturn(Mono.error(except)).when(agentClient).putForEntity(anyString(), any()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -221,14 +228,13 @@ public class DmaapMessageHandlerTest { .expectSubscription() // .verifyComplete(); // - verify(agentClient).put(anyString(), anyString()); + verify(agentClient).putForEntity(anyString(), anyString()); verifyNoMoreInteractions(agentClient); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(dmaapClient).send(captor.capture()); String actualMessage = captor.getValue(); - assertThat(actualMessage.contains(HttpStatus.NOT_FOUND + "\",\"message\":\"java.lang.Exception: " + errorCause)) - .isTrue(); + assertThat(actualMessage.contains(HttpStatus.BAD_REQUEST.toString())).isTrue(); verify(dmaapClient).sendBatchWithResponse(); verifyNoMoreInteractions(dmaapClient); @@ -245,8 +251,8 @@ public class DmaapMessageHandlerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(dmaapClient).send(captor.capture()); String actualMessage = captor.getValue(); - assertThat(actualMessage - .contains(HttpStatus.NOT_FOUND + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue(); + assertThat(actualMessage.contains(HttpStatus.NOT_FOUND + "\",\"message\":\"Not implemented operation:")) + .isTrue(); verify(dmaapClient).sendBatchWithResponse(); verifyNoMoreInteractions(dmaapClient); -- 2.16.6