REST error codes 12/2712/3
authorPatrikBuhr <patrik.buhr@est.tech>
Mon, 9 Mar 2020 07:01:52 +0000 (08:01 +0100)
committerPatrikBuhr <patrik.buhr@est.tech>
Tue, 10 Mar 2020 12:36:21 +0000 (13:36 +0100)
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 <patrik.buhr@est.tech>
15 files changed:
policy-agent/docs/api.yaml
policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java
policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java
policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java
policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java
policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java
policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java
policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java

index 1c41d42..9c7d96e 100644 (file)
@@ -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
index df5e7b4..01065f9 100644 (file)
@@ -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/";
index d906930..4a61479 100644 (file)
@@ -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.
      *
      * <p>
-     * 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.
      *
      * <p>
-     * 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<A1Client> createA1Client(Ric ric) {
         return getProtocolVersion(ric) //
index 12c0745..2435ef0 100644 (file)
@@ -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<String> post(String uri, String body) {
+    public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
         logger.debug("POST uri = '{}{}''", baseUrl, uri);
-        return client.post() //
+        Mono<String> 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<String> post(String uri, @Nullable String body) {
+        return postForEntity(uri, body) //
+            .flatMap(this::toBody);
     }
 
     public Mono<String> postWithAuthHeader(String uri, String body, String username, String password) {
         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<String> put(String uri, String body) {
+    public Mono<ResponseEntity<String>> 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<String> get(String uri) {
+    public Mono<String> put(String uri, String body) {
+        return putForEntity(uri, body) //
+            .flatMap(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> 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<String> delete(String uri) {
+    public Mono<String> get(String uri) {
+        return getForEntity(uri) //
+            .flatMap(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> 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<String> delete(String uri) {
+        return deleteForEntity(uri) //
+            .flatMap(this::toBody);
     }
+
+    private Mono<ResponseEntity<String>> retrieve(RequestHeadersSpec<?> request) {
+        return request.retrieve() //
+            .toEntity(String.class);
+    }
+
+    Mono<String> toBody(ResponseEntity<String> entity) {
+        if (entity.getBody() == null) {
+            return Mono.just("");
+        } else {
+            return Mono.just(entity.getBody());
+        }
+    }
+
 }
index 3a49ff1..1af607f 100644 (file)
@@ -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<String> 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<String> 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<String> 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<String> 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<ResponseEntity<Object>> 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<ResponseEntity<Object>> 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<Object> 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<String> 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<Policy> 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<ResponseEntity<String>> 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));
         }
     }
 
index f6fc850..a96766d 100644 (file)
@@ -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<String> 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<RicInfo> result = new ArrayList<>();
         synchronized (rics) {
             for (Ric ric : rics.getRics()) {
index 464511f..35348c7 100644 (file)
@@ -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<String> 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<ServiceStatus> 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<String> 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<String> 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<String> keepAliveService(//
index fc80834..1a9cf32 100644 (file)
@@ -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;
     }
 
 }
index cacf64d..c55d5be 100644 (file)
@@ -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")
index e72fcf0..3cc1b77 100644 (file)
@@ -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 {
 
index dc979de..b0c2caf 100644 (file)
@@ -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.
+ * <p>
+ * 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<String> 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<String> handleAgentCallError(Throwable t, String origianalMessage,
-        DmaapRequestMessage dmaapRequestMessage) {
+    private Mono<ResponseEntity<String>> 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<String> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
+    private Mono<ResponseEntity<String>> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
         DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation();
-        if (operation == null) {
-            return Mono.error(new ServiceException("Not implemented operation: " + operation));
-        }
-        Mono<String> 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<String> 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<String> 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<String> getDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response,
+    private Mono<String> 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);
 
     }
index 05485d7..377ca79 100644 (file)
@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<PolicyInfo> 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<ServiceStatus> 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<String> entity = this.restTemplate.postForEntity(url, null, String.class);
+        url = "/services/keepalive?name=name";
+        ResponseEntity<String> 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<String> 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<String>(content, headers);
     }
 
-    private ResponseEntity<String> putForEntity(String url, String jsonBody) {
-        return restTemplate.exchange(url, HttpMethod.PUT, createJsonHttpEntity(jsonBody), String.class);
-    }
-
     private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
         List<T> result = new ArrayList<>();
         JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray();
index 11f2409..884b36f 100644 (file)
@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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();
     }
 }
index 17f0997..d30e95a 100644 (file)
@@ -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.");
index 67d00a2..5ba538e 100644 (file)
@@ -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<ResponseEntity<String>> okResponse() {
+        ResponseEntity<String> 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<String> 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<String> 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);