NBI documentation using swagger UI 66/2366/2
authorPatrikBuhr <patrik.buhr@est.tech>
Tue, 28 Jan 2020 16:04:16 +0000 (17:04 +0100)
committerPatrikBuhr <patrik.buhr@est.tech>
Wed, 29 Jan 2020 10:37:08 +0000 (11:37 +0100)
Change-Id: I2a0561e06f1c92a61609e065478bbabdd241f744
Issue-ID: NONRTRIC-84
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
16 files changed:
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java
policy-agent/README.md
policy-agent/docs/api.doc [new file with mode: 0644]
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/PolicyInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.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/ServiceRegistrationInfo.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/repository/Service.java
policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java
policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java

index 19865e1..c0dde9b 100644 (file)
@@ -43,7 +43,10 @@ import org.oransc.ric.portal.dashboard.model.PolicyTypes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.RestTemplate;
@@ -55,14 +58,14 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
     RestTemplate restTemplate = new RestTemplate();
 
     private static com.google.gson.Gson gson = new GsonBuilder() //
-            .serializeNulls() //
-            .create(); //
+        .serializeNulls() //
+        .create(); //
 
     private final String urlPrefix;
 
     @Autowired
     public PolicyAgentApiImpl(
-            @org.springframework.beans.factory.annotation.Value("${policycontroller.url.prefix}") final String urlPrefix) {
+        @org.springframework.beans.factory.annotation.Value("${policycontroller.url.prefix}") final String urlPrefix) {
         logger.debug("ctor prefix '{}'", urlPrefix);
         this.urlPrefix = urlPrefix;
     }
@@ -82,15 +85,16 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
 
     @Override
     public ResponseEntity<String> getAllPolicyTypes() {
-        String url = baseUrl() + "/policy_schemas";
-        ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
-        if (!rsp.getStatusCode().is2xxSuccessful()) {
-            return rsp;
-        }
-
-        PolicyTypes result = new PolicyTypes();
-        JsonParser jsonParser = new JsonParser();
         try {
+            String url = baseUrl() + "/policy_schemas";
+            ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
+            if (!rsp.getStatusCode().is2xxSuccessful()) {
+                return rsp;
+            }
+
+            PolicyTypes result = new PolicyTypes();
+            JsonParser jsonParser = new JsonParser();
+
             JsonArray schemas = jsonParser.parse(rsp.getBody()).getAsJsonArray();
             for (JsonElement schema : schemas) {
                 JsonObject schemaObj = schema.getAsJsonObject();
@@ -115,8 +119,7 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
         }
 
         try {
-            Type listType = new TypeToken<List<ImmutablePolicyInfo>>() {
-            }.getType();
+            Type listType = new TypeToken<List<ImmutablePolicyInfo>>() {}.getType();
             List<PolicyInfo> rspParsed = gson.fromJson(rsp.getBody(), listType);
             PolicyInstances result = new PolicyInstances();
             for (PolicyInfo p : rspParsed) {
@@ -138,16 +141,16 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
 
     @Override
     public ResponseEntity<String> putPolicy(String policyTypeIdString, String policyInstanceId, String json,
-            String ric) {
+        String ric) {
         String url = baseUrl() + "/policy?type={type}&instance={instance}&ric={ric}&service={service}";
         Map<String, ?> uriVariables = Map.of( //
-                "type", policyTypeIdString, //
-                "instance", policyInstanceId, //
-                "ric", ric, //
-                "service", "dashboard");
+            "type", policyTypeIdString, //
+            "instance", policyInstanceId, //
+            "ric", ric, //
+            "service", "dashboard");
 
         try {
-            this.restTemplate.put(url, json, uriVariables);
+            this.restTemplate.put(url, createJsonHttpEntity(json), uriVariables);
             return new ResponseEntity<>(HttpStatus.OK);
         } catch (Exception e) {
             return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
@@ -184,8 +187,7 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
         String rsp = this.restTemplate.getForObject(url, String.class, uriVariables);
 
         try {
-            Type listType = new TypeToken<List<ImmutableRicInfo>>() {
-            }.getType();
+            Type listType = new TypeToken<List<ImmutableRicInfo>>() {}.getType();
             List<RicInfo> rspParsed = gson.fromJson(rsp, listType);
             Collection<String> result = new Vector<>(rspParsed.size());
             for (RicInfo ric : rspParsed) {
@@ -197,4 +199,10 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
         }
     }
 
+    private HttpEntity<String> createJsonHttpEntity(String content) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return new HttpEntity<String>(content, headers);
+    }
+
 }
index b9f1635..ecb9640 100644 (file)
@@ -21,6 +21,7 @@ package org.oransc.ric.portal.dashboard.controller;
 
 import java.lang.invoke.MethodHandles;
 import java.net.URI;
+
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.onap.portalsdk.core.onboarding.util.PortalApiConstants;
index 6077a4b..655000e 100644 (file)
@@ -24,7 +24,7 @@ The REST API is published on port 8081 and it is started by command:
 mvn -Dtest=MockPolicyAgent test
 
 The backend server publishes live API documentation at the
-URL `http://your-host-name-here:8080/swagger-ui.html`
+URL `http://your-host-name-here:8081/swagger-ui.html`
 
 ## License
 
diff --git a/policy-agent/docs/api.doc b/policy-agent/docs/api.doc
new file mode 100644 (file)
index 0000000..e3f44ed
--- /dev/null
@@ -0,0 +1,516 @@
+swagger: '2.0'
+info:
+  description: Api Documentation
+  version: '1.0'
+  title: Api Documentation
+  termsOfService: 'urn:tos'
+  contact: {}
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0'
+host: 'localhost:8081'
+basePath: /
+tags:
+  - name: policy-controller
+    description: Policy Controller
+  - name: ric-repository-controller
+    description: Ric Repository Controller
+  - name: service-controller
+    description: Service Controller
+  - name: status-controller
+    description: Status Controller
+paths:
+  /policies:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns the policies
+      operationId: getPoliciesUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: type
+          in: query
+          description: type
+          required: false
+          type: string
+        - name: ric
+          in: query
+          description: ric
+          required: false
+          type: string
+        - name: service
+          in: query
+          description: service
+          required: false
+          type: string
+      responses:
+        '200':
+          description: Policies
+          schema:
+            type: array
+            items:
+              $ref: '#/definitions/PolicyInfo'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /policy:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns a policy configuration
+      operationId: getPolicyUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: instance
+          in: query
+          description: instance
+          required: true
+          type: string
+      responses:
+        '200':
+          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
+    put:
+      tags:
+        - policy-controller
+      summary: Put a policy
+      operationId: putPolicyUsingPUT
+      consumes:
+        - application/json
+      produces:
+        - '*/*'
+      parameters:
+        - name: type
+          in: query
+          description: type
+          required: true
+          type: string
+        - name: instance
+          in: query
+          description: instance
+          required: true
+          type: string
+        - name: ric
+          in: query
+          description: ric
+          required: true
+          type: string
+        - name: service
+          in: query
+          description: service
+          required: true
+          type: string
+        - in: body
+          name: jsonBody
+          description: jsonBody
+          required: true
+          schema:
+            type: object
+      responses:
+        '200':
+          description: Policy created or updated
+          schema:
+            type: string
+        '201':
+          description: Created
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+    delete:
+      tags:
+        - policy-controller
+      summary: Deletes the policy
+      operationId: deletePolicyUsingDELETE
+      produces:
+        - '*/*'
+      parameters:
+        - name: instance
+          in: query
+          description: instance
+          required: true
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            $ref: '#/definitions/Mono«ResponseEntity«Void»»'
+        '204':
+          description: Policy deleted
+          schema:
+            $ref: '#/definitions/Mono«ResponseEntity«Void»»'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+  /policy_schema:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns one policy type schema definition
+      operationId: getPolicySchemaUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: id
+          in: query
+          description: id
+          required: true
+          type: string
+      responses:
+        '200':
+          description: Policy schema
+          schema:
+            type: object
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /policy_schemas:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns policy type schema definitions
+      operationId: getPolicySchemasUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: ric
+          in: query
+          description: ric
+          required: false
+          type: string
+      responses:
+        '200':
+          description: Policy schemas
+          schema:
+            type: array
+            items:
+              type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /policy_types:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns policy types
+      operationId: getPolicyTypesUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: ric
+          in: query
+          description: ric
+          required: false
+          type: string
+      responses:
+        '200':
+          description: Policy type names
+          schema:
+            type: array
+            items:
+              type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /ric:
+    get:
+      tags:
+        - ric-repository-controller
+      summary: Returns the name of a RIC managing one Mananged Element
+      operationId: getRicUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: managedElementId
+          in: query
+          description: managedElementId
+          required: false
+          type: string
+      responses:
+        '200':
+          description: RIC is fond
+          schema:
+            type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: RIC is not fond
+          schema:
+            type: string
+  /rics:
+    get:
+      tags:
+        - ric-repository-controller
+      summary: Returns NearRT RIC information
+      operationId: getRicsUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: policyType
+          in: query
+          description: policyType
+          required: false
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: array
+            items:
+              $ref: '#/definitions/RicInfo'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /service:
+    put:
+      tags:
+        - service-controller
+      summary: Register a service
+      operationId: putServiceUsingPUT
+      consumes:
+        - application/json
+      produces:
+        - '*/*'
+      parameters:
+        - in: body
+          name: registrationInfo
+          description: registrationInfo
+          required: true
+          schema:
+            $ref: '#/definitions/ServiceRegistrationInfo'
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: string
+        '201':
+          description: Created
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /services:
+    get:
+      tags:
+        - service-controller
+      summary: Returns service information
+      operationId: getServicesUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: name
+          in: query
+          description: name
+          required: false
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: array
+            items:
+              $ref: '#/definitions/ServiceStatus'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+    delete:
+      tags:
+        - service-controller
+      summary: Delete a service
+      operationId: deleteServiceUsingDELETE
+      produces:
+        - '*/*'
+      parameters:
+        - name: name
+          in: query
+          description: name
+          required: true
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: string
+        '204':
+          description: No Content
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+  /services/keepalive:
+    post:
+      tags:
+        - service-controller
+      summary: Keep the poilicies alive for a service
+      operationId: keepAliveServiceUsingPOST
+      consumes:
+        - application/json
+      produces:
+        - '*/*'
+      parameters:
+        - name: name
+          in: query
+          description: name
+          required: true
+          type: string
+      responses:
+        '200':
+          description: Policies timeout supervision refreshed
+          schema:
+            type: string
+        '201':
+          description: Created
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: 'The service is not found, needs re-registration'
+  /status:
+    get:
+      tags:
+        - status-controller
+      summary: Returns status and statistics of the service
+      operationId: getStatusUsingGET
+      produces:
+        - '*/*'
+      responses:
+        '200':
+          description: Service is living
+          schema:
+            type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+definitions:
+  Mono«ResponseEntity«Void»»:
+    type: object
+    title: Mono«ResponseEntity«Void»»
+  Mono«ResponseEntity«string»»:
+    type: object
+    title: Mono«ResponseEntity«string»»
+  PolicyInfo:
+    type: object
+    properties:
+      id:
+        type: string
+        description: identity of the policy
+        allowEmptyValue: false
+      json:
+        type: string
+        description: the configuration of the policy
+        allowEmptyValue: false
+      lastModified:
+        type: string
+        description: 'timestamp, last modification time'
+        allowEmptyValue: false
+      ric:
+        type: string
+        description: identity the target NearRT RIC
+        allowEmptyValue: false
+      service:
+        type: string
+        description: the name of the service owning the policy
+        allowEmptyValue: false
+      type:
+        type: string
+        description: name of the policy type
+        allowEmptyValue: false
+    title: PolicyInfo
+  RicInfo:
+    type: object
+    properties:
+      managedElementIds:
+        type: array
+        description: O1 identities for managed entities
+        allowEmptyValue: false
+        items:
+          type: string
+      name:
+        type: string
+        description: identity of the ric
+        allowEmptyValue: false
+      policyTypes:
+        type: array
+        description: supported policy types
+        allowEmptyValue: false
+        items:
+          type: string
+    title: RicInfo
+  ServiceRegistrationInfo:
+    type: object
+    properties:
+      callbackUrl:
+        type: string
+        description: callback for notifying of RIC recovery
+        allowEmptyValue: false
+      keepAliveIntervalSeconds:
+        type: integer
+        format: int64
+        description: keep alive interval for policies owned by the service. 0 means no timeout supervision. Polcies that are not refreshed within this time are removed
+        allowEmptyValue: false
+      name:
+        type: string
+        description: identity of the service
+        allowEmptyValue: false
+    title: ServiceRegistrationInfo
+  ServiceStatus:
+    type: object
+    properties:
+      keepAliveIntervalSeconds:
+        type: integer
+        format: int64
+        description: policy keep alive timeout
+        allowEmptyValue: false
+      name:
+        type: string
+        description: identity of the service
+        allowEmptyValue: false
+      timeSincePingSeconds:
+        type: integer
+        format: int64
+        description: time since last invocation by the service
+        allowEmptyValue: false
+    title: ServiceStatus
+
index 2574217..3fd16d0 100644 (file)
@@ -49,7 +49,7 @@ public class AsyncRestClient {
         return client.post() //
             .uri(uri) //
             .contentType(MediaType.APPLICATION_JSON) //
-            .syncBody(body) //
+            .bodyValue(body) //
             .retrieve() //
             .onStatus(HttpStatus::isError,
                 response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
@@ -61,7 +61,7 @@ public class AsyncRestClient {
         return client.put() //
             .uri(uri) //
             .contentType(MediaType.APPLICATION_JSON) //
-            .syncBody(body) //
+            .bodyValue(body) //
             .retrieve() //
             .onStatus(HttpStatus::isError,
                 response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
index 97fe2a5..12deb81 100644 (file)
@@ -76,7 +76,9 @@ public class PolicyController {
 
     @GetMapping("/policy_schemas")
     @ApiOperation(value = "Returns policy type schema definitions")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy Types found")})
+    @ApiResponses(
+        value = {
+            @ApiResponse(code = 200, message = "Policy schemas", response = String.class, responseContainer = "List")})
     public ResponseEntity<String> getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) {
         synchronized (this.policyTypes) {
             if (ricName == null) {
@@ -95,7 +97,7 @@ public class PolicyController {
 
     @GetMapping("/policy_schema")
     @ApiOperation(value = "Returns one policy type schema definition")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy Type found")})
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy schema", response = Object.class)})
     public ResponseEntity<String> getPolicySchema(@RequestParam(name = "id", required = true) String id) {
         try {
             PolicyType type = policyTypes.getType(id);
@@ -107,7 +109,12 @@ public class PolicyController {
 
     @GetMapping("/policy_types")
     @ApiOperation(value = "Returns policy types")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy Types found")})
+    @ApiResponses(
+        value = {@ApiResponse(
+            code = 200,
+            message = "Policy type names",
+            response = String.class,
+            responseContainer = "List")})
     public ResponseEntity<String> getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) {
         synchronized (this.policyTypes) {
             if (ricName == null) {
@@ -125,10 +132,13 @@ public class PolicyController {
     }
 
     @GetMapping("/policy")
-    @ApiOperation(value = "Returns the policy")
+    @ApiOperation(value = "Returns a policy configuration") //
     @ApiResponses(
-        value = {@ApiResponse(code = 200, message = "Policy found"),
-            @ApiResponse(code = 204, message = "Policy is not found")})
+        value = { //
+            @ApiResponse(code = 200, message = "Policy found", response = Object.class), //
+            @ApiResponse(code = 204, message = "Policy is not found")} //
+    )
+
     public ResponseEntity<String> getPolicy( //
         @RequestParam(name = "instance", required = true) String instance) {
         try {
@@ -140,8 +150,8 @@ public class PolicyController {
     }
 
     @DeleteMapping("/policy")
-    @ApiOperation(value = "Deletes the policy")
-    @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted")})
+    @ApiOperation(value = "Deletes the policy", response = Object.class)
+    @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted", response = Object.class)})
     public Mono<ResponseEntity<Void>> deletePolicy( //
         @RequestParam(name = "instance", required = true) String id) {
         Policy policy = policies.get(id);
@@ -158,21 +168,23 @@ public class PolicyController {
     }
 
     @PutMapping(path = "/policy")
-    @ApiOperation(value = "Create the policy")
-    @ApiResponses(value = {@ApiResponse(code = 201, message = "Policy created")})
+    @ApiOperation(value = "Put a policy", response = String.class)
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy created or updated")})
     public Mono<ResponseEntity<String>> putPolicy( //
         @RequestParam(name = "type", required = true) String typeName, //
         @RequestParam(name = "instance", required = true) String instanceId, //
         @RequestParam(name = "ric", required = true) String ricName, //
         @RequestParam(name = "service", required = true) String service, //
-        @RequestBody String jsonBody) {
+        @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) {
             Policy policy = ImmutablePolicy.builder() //
                 .id(instanceId) //
-                .json(jsonBody) //
+                .json(jsonString) //
                 .type(type) //
                 .ric(ric) //
                 .ownerServiceName(service) //
@@ -182,7 +194,7 @@ public class PolicyController {
                 .flatMap(client -> client.putPolicy(policy)) //
                 .doOnNext(notUsed -> policies.put(policy)) //
                 .flatMap(notUsed -> {
-                    return Mono.just(new ResponseEntity<>(HttpStatus.CREATED));
+                    return Mono.just(new ResponseEntity<>(HttpStatus.OK));
                 });
         }
         return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND));
@@ -190,7 +202,9 @@ public class PolicyController {
 
     @GetMapping("/policies")
     @ApiOperation(value = "Returns the policies")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policies found")})
+    @ApiResponses(
+        value = {
+            @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List")})
     public ResponseEntity<String> getPolicies( //
         @RequestParam(name = "type", required = false) String type, //
         @RequestParam(name = "ric", required = false) String ric, //
@@ -237,14 +251,16 @@ public class PolicyController {
     private String policiesToJson(Collection<Policy> policies) {
         Vector<PolicyInfo> v = new Vector<>(policies.size());
         for (Policy p : policies) {
-            PolicyInfo policyInfo = ImmutablePolicyInfo.builder() //
-                .json(p.json()) //
-                .id(p.id()) //
-                .ric(p.ric().name()) //
-                .type(p.type().name()) //
-                .service(p.ownerServiceName()) //
-                .lastModified(p.lastModified()) //
-                .build();
+            PolicyInfo policyInfo = new PolicyInfo();
+            policyInfo.id = p.id();
+            policyInfo.json = p.json();
+            policyInfo.ric = p.ric().name();
+            policyInfo.type = p.type().name();
+            policyInfo.service = p.ownerServiceName();
+            policyInfo.lastModified = p.lastModified();
+            if (!policyInfo.validate()) {
+                throw new RuntimeException("BUG, all fields must be set");
+            }
             v.add(policyInfo);
         }
         return gson.toJson(v);
index ed705dc..bd13062 100644 (file)
 
 package org.oransc.policyagent.controllers;
 
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
 import org.immutables.gson.Gson;
-import org.immutables.value.Value;
 
-@Value.Immutable
 @Gson.TypeAdapters
-interface PolicyInfo {
+@ApiModel(value = "PolicyInfo")
+public class PolicyInfo {
+
+    @ApiModelProperty(value = "identity of the policy")
+    public String id;
+
+    @ApiModelProperty(value = "name of the policy type")
+    public String type;
 
-    public String id();
+    @ApiModelProperty(value = "identity the target NearRT RIC")
+    public String ric;
 
-    public String type();
+    @ApiModelProperty(value = "the configuration of the policy")
+    public String json;
 
-    public String ric();
+    @ApiModelProperty(value = "the name of the service owning the policy")
+    public String service;
 
-    public String json();
+    @ApiModelProperty(value = "timestamp, last modification time")
+    public String lastModified;
 
-    public String service();
+    PolicyInfo() {
+    }
 
-    public String lastModified();
+    public boolean validate() {
+        return id != null && type != null && ric != null && json != null && service != null && lastModified != null;
+    }
 
 }
index cbb205c..73103b8 100644 (file)
 
 package org.oransc.policyagent.controllers;
 
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
 import java.util.Collection;
 
 import org.immutables.gson.Gson;
-import org.immutables.value.Value;
 
-@Value.Immutable
 @Gson.TypeAdapters
-interface RicInfo {
+@ApiModel(value = "RicInfo")
+class RicInfo {
+    @ApiModelProperty(value = "identity of the ric")
+    public final String name;
 
-    public String name();
+    @ApiModelProperty(value = "O1 identities for managed entities")
+    public final Collection<String> managedElementIds;
 
-    public Collection<String> managedElementIds();
+    @ApiModelProperty(value = "supported policy types")
+    public final Collection<String> policyTypes;
 
-    public Collection<String> policyTypes();
+    RicInfo(String name, Collection<String> managedElementIds, Collection<String> policyTypes) {
+        this.name = name;
+        this.managedElementIds = managedElementIds;
+        this.policyTypes = policyTypes;
+    }
 }
index 0d49833..a566781 100644 (file)
@@ -60,11 +60,11 @@ public class RicRepositoryController {
      * Example: http://localhost:8080/rics?managedElementId=kista_1
      */
     @GetMapping("/ric")
-    @ApiOperation(value = "Returns the name of a RIC managing one Mananged Element", response = String.class)
+    @ApiOperation(value = "Returns the name of a RIC managing one Mananged Element")
     @ApiResponses(
         value = { //
-            @ApiResponse(code = 200, message = "RIC is fond"), //
-            @ApiResponse(code = 404, message = "RIC is not fond") //
+            @ApiResponse(code = 200, message = "RIC is fond", response = String.class), //
+            @ApiResponse(code = 404, message = "RIC is not fond", response = String.class) //
         })
     public ResponseEntity<String> getRic(
         @RequestParam(name = "managedElementId", required = false, defaultValue = "") String managedElementId) {
@@ -83,10 +83,10 @@ public class RicRepositoryController {
      *         Example: http://localhost:8080/ric
      */
     @GetMapping("/rics")
-    @ApiOperation(value = "Returns defined NearRT RIC:s as Json", response = RicInfo.class)
+    @ApiOperation(value = "Returns NearRT RIC information")
     @ApiResponses(
         value = { //
-            @ApiResponse(code = 200, message = "OK", response = RicInfo.class) //
+            @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List") //
         })
     public ResponseEntity<String> getRics(
         @RequestParam(name = "policyType", required = false) String supportingPolicyType) {
@@ -95,11 +95,7 @@ public class RicRepositoryController {
         synchronized (rics) {
             for (Ric ric : rics.getRics()) {
                 if (supportingPolicyType == null || ric.isSupportingType(supportingPolicyType)) {
-                    result.add(ImmutableRicInfo.builder() //
-                        .name(ric.name()) //
-                        .managedElementIds(ric.getManagedElementIds()) //
-                        .policyTypes(ric.getSupportedPolicyTypeNames()) //
-                        .build());
+                    result.add(new RicInfo(ric.name(), ric.getManagedElementIds(), ric.getSupportedPolicyTypeNames()));
                 }
             }
         }
index 8996376..012d40a 100644 (file)
@@ -41,6 +41,7 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -63,8 +64,9 @@ public class ServiceController {
     }
 
     @GetMapping("/services")
-    @ApiOperation(value = "Returns service information", response = ServiceStatus.class)
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")})
+    @ApiOperation(value = "Returns service information")
+    @ApiResponses(
+        value = {@ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List")})
     public ResponseEntity<String> getServices( //
         @RequestParam(name = "name", required = false) String name) {
 
@@ -82,25 +84,24 @@ public class ServiceController {
     }
 
     private ServiceStatus toServiceStatus(Service s) {
-        return ImmutableServiceStatus.builder() //
-            .name(s.getName()) //
-            .keepAliveInterval(s.getKeepAliveInterval().toSeconds()) //
-            .timeSincePing(s.timeSinceLastPing().toSeconds()) //
-            .build();
+        return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds());
     }
 
+    @ApiOperation(value = "Register a service")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = String.class)})
     @PutMapping("/service")
     public ResponseEntity<String> putService( //
-        @RequestBody String jsonBody) {
+        @RequestBody ServiceRegistrationInfo registrationInfo) {
         try {
-            ServiceRegistrationInfo s = gson.fromJson(jsonBody, ImmutableServiceRegistrationInfo.class);
-            this.services.put(toService(s));
+            this.services.put(toService(registrationInfo));
             return new ResponseEntity<String>("OK", HttpStatus.OK);
         } catch (Exception e) {
             return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
         }
     }
 
+    @ApiOperation(value = "Delete a service")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")})
     @DeleteMapping("/services")
     public ResponseEntity<String> deleteService( //
         @RequestParam(name = "name", required = true) String name) {
@@ -115,6 +116,21 @@ public class ServiceController {
         }
     }
 
+    @ApiOperation(value = "Keep the poilicies alive for a service")
+    @ApiResponses(
+        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( //
+        @RequestParam(name = "name", required = true) String name) {
+        try {
+            services.getService(name).ping();
+            return new ResponseEntity<String>("OK", HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NOT_FOUND);
+        }
+    }
+
     private Service removeService(String name) throws ServiceException {
         synchronized (this.services) {
             Service service = this.services.getService(name);
@@ -133,7 +149,7 @@ public class ServiceController {
     }
 
     private Service toService(ServiceRegistrationInfo s) {
-        return new Service(s.name(), Duration.ofSeconds(s.keepAliveInterval()), s.callbackUrl());
+        return new Service(s.name, Duration.ofSeconds(s.keepAliveIntervalSeconds), s.callbackUrl);
     }
 
 }
index e0d3849..145fdb0 100644 (file)
 
 package org.oransc.policyagent.controllers;
 
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
 import org.immutables.gson.Gson;
-import org.immutables.value.Value;
 
-@Value.Immutable
 @Gson.TypeAdapters
-public interface ServiceRegistrationInfo {
+@ApiModel(value = "ServiceRegistrationInfo")
+public class ServiceRegistrationInfo {
+
+    @ApiModelProperty(value = "identity of the service")
+    public String name;
+
+    @ApiModelProperty(
+        value = "keep alive interval for policies owned by the service. 0 means no timeout supervision."
+            + " Polcies that are not refreshed within this time are removed")
+    public long keepAliveIntervalSeconds;
 
-    public String name();
+    @ApiModelProperty(value = "callback for notifying of RIC recovery")
+    public String callbackUrl;
 
-    public long keepAliveInterval();
+    public ServiceRegistrationInfo() {
+    }
 
-    public String callbackUrl();
+    public ServiceRegistrationInfo(String name, long keepAliveIntervalSeconds, String callbackUrl) {
+        this.name = name;
+        this.keepAliveIntervalSeconds = keepAliveIntervalSeconds;
+        this.callbackUrl = callbackUrl;
+    }
 
 }
index 3063c6b..c30cce1 100644 (file)
 
 package org.oransc.policyagent.controllers;
 
-import com.google.gson.annotations.SerializedName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 
 import org.immutables.gson.Gson;
-import org.immutables.value.Value;
 
-@Value.Immutable
 @Gson.TypeAdapters
-public interface ServiceStatus {
+@ApiModel(value = "ServiceStatus")
+public class ServiceStatus {
 
-    @SerializedName("name")
-    public String name();
+    @ApiModelProperty(value = "identity of the service")
+    public final String name;
 
-    @SerializedName("keepAlive")
-    public long keepAliveInterval();
+    @ApiModelProperty(value = "policy keep alive timeout")
+    public final long keepAliveIntervalSeconds;
 
-    @SerializedName("timeSincePing")
-    public long timeSincePing();
+    @ApiModelProperty(value = "time since last invocation by the service")
+    public final long timeSincePingSeconds;
+
+    ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds) {
+        this.name = name;
+        this.keepAliveIntervalSeconds = keepAliveIntervalSeconds;
+        this.timeSincePingSeconds = timeSincePingSeconds;
+    }
 
 }
index 226ee73..1219a0d 100644 (file)
@@ -37,7 +37,7 @@ public class StatusController {
     @ApiOperation(value = "Returns status and statistics of the service")
     @ApiResponses(
         value = { //
-            @ApiResponse(code = 200, message = "Service is living") //
+            @ApiResponse(code = 200, message = "Service is living", response = String.class) //
         })
     public Mono<ResponseEntity<String>> getStatus() {
         Mono<ResponseEntity<String>> response = Mono.just(new ResponseEntity<>("hunky dory", HttpStatus.OK));
index bcdc2ee..f0863a5 100644 (file)
@@ -43,12 +43,12 @@ public class Service {
         return this.keepAliveInterval;
     }
 
-    private synchronized void ping() {
+    public synchronized void ping() {
         this.lastPing = Instant.now();
     }
 
     public synchronized boolean isExpired() {
-        return timeSinceLastPing().compareTo(this.keepAliveInterval) > 0;
+        return this.keepAliveInterval.getSeconds() > 0 && timeSinceLastPing().compareTo(this.keepAliveInterval) > 0;
     }
 
     public synchronized Duration timeSinceLastPing() {
index d9a4b5c..eec9c70 100644 (file)
@@ -30,6 +30,7 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonParser;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
@@ -39,8 +40,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.configuration.ImmutableRicConfig;
 import org.oransc.policyagent.configuration.RicConfig;
-import org.oransc.policyagent.controllers.ImmutableServiceRegistrationInfo;
-import org.oransc.policyagent.controllers.ImmutableServiceStatus;
+import org.oransc.policyagent.controllers.PolicyInfo;
 import org.oransc.policyagent.controllers.ServiceRegistrationInfo;
 import org.oransc.policyagent.controllers.ServiceStatus;
 import org.oransc.policyagent.exceptions.ServiceException;
@@ -53,6 +53,7 @@ import org.oransc.policyagent.repository.PolicyTypes;
 import org.oransc.policyagent.repository.Ric;
 import org.oransc.policyagent.repository.Rics;
 import org.oransc.policyagent.repository.Services;
+import org.oransc.policyagent.repository.Ric.RicState;
 import org.oransc.policyagent.tasks.RepositorySupervision;
 import org.oransc.policyagent.utils.MockA1Client;
 import org.oransc.policyagent.utils.MockA1ClientFactory;
@@ -63,11 +64,19 @@ import org.springframework.boot.test.context.TestConfiguration;
 import org.springframework.boot.web.server.LocalServerPort;
 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.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 static org.awaitility.Awaitility.await;
+
 @ExtendWith(SpringExtension.class)
 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
 public class ApplicationTest {
@@ -141,11 +150,26 @@ public class ApplicationTest {
 
     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 reset() {
         rics.clear();
         policies.clear();
         policyTypes.clear();
         assertThat(policies.size()).isEqualTo(0);
+        restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
     }
 
     @Test
@@ -172,6 +196,8 @@ public class ApplicationTest {
 
         Policy policy = addPolicy("policyId", "typeName", "service", "ric"); // This should be created in the RIC
         supervision.checkAllRics(); // The created policy should be put in the RIC
+        await().untilAsserted(() -> RicState.IDLE.equals(rics.getRic("ric").getState()));
+
         Policies ricPolicies = getA1Client("ric").getPolicies();
         assertThat(ricPolicies.size()).isEqualTo(1);
         Policy ricPolicy = ricPolicies.get("policyId");
@@ -191,21 +217,20 @@ public class ApplicationTest {
 
         String rsp = this.restTemplate.getForObject(url, String.class);
         System.out.println(rsp);
-
         assertThat(rsp).isEqualTo("ric1");
     }
 
     @Test
     public void testPutPolicy() throws Exception {
+        reset();
         putService("service1");
+        addPolicyType("type1", "ric1");
 
         String url = baseUrl() + "/policy?type=type1&instance=instance1&ric=ric1&service=service1";
-        String json = "{}";
-        addPolicyType("type1", "ric1");
+        final String json = jsonString();
         this.rics.getRic("ric1").setState(Ric.RicState.IDLE);
 
-        this.restTemplate.put(url, json);
-
+        this.restTemplate.put(url, createJsonHttpEntity(json));
         Policy policy = policies.getPolicy("instance1");
 
         assertThat(policy).isNotNull();
@@ -243,10 +268,33 @@ public class ApplicationTest {
         return ric;
     }
 
+    private String createServiceJson(String name) {
+        ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, 1, "callbackUrl");
+
+        String json = gson.toJson(service);
+        return json;
+    }
+
+    HttpEntity<String> createJsonHttpEntity(String content) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return new HttpEntity<String>(content, headers);
+    }
+
+    private void putService(String name) {
+        String url = baseUrl() + "/service";
+        HttpEntity<String> entity = createJsonHttpEntity(createServiceJson(name));
+        this.restTemplate.put(url, entity);
+    }
+
+    private String jsonString() {
+        return "{\n  \"servingCellNrcgi\": \"1\"\n }";
+    }
+
     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
         addRic(ric);
         Policy p = ImmutablePolicy.builder().id(id) //
-            .json("{}") //
+            .json(jsonString()) //
             .ownerServiceName(service) //
             .ric(rics.getRic(ric)) //
             .type(addPolicyType(typeName, ric)) //
@@ -346,12 +394,16 @@ public class ApplicationTest {
     public void testGetPolicies() throws Exception {
         String url = baseUrl() + "/policies";
         addPolicy("id1", "type1", "service1");
-        addPolicy("id2", "type2", "service2");
 
         String rsp = this.restTemplate.getForObject(url, String.class);
         System.out.println(rsp);
-        assertThat(rsp).contains("id1");
-        assertThat(rsp).contains("id2");
+        List<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
+        assertThat(info).size().isEqualTo(1);
+        PolicyInfo policyInfo = info.get(0);
+        assert (policyInfo.validate());
+        assertThat(policyInfo.id).isEqualTo("id1");
+        assertThat(policyInfo.type).isEqualTo("type1");
+        assertThat(policyInfo.service).isEqualTo("service1");
     }
 
     @Test
@@ -375,34 +427,20 @@ public class ApplicationTest {
         assertFalse(rsp.contains("id3"));
     }
 
-    private String createServiceJson(String name) {
-        ServiceRegistrationInfo service = ImmutableServiceRegistrationInfo.builder() //
-            .keepAliveInterval(1) //
-            .name(name) //
-            .callbackUrl("callbackUrl") //
-            .build();
-        String json = gson.toJson(service);
-        return json;
-    }
-
-    private void putService(String name) {
-        String url = baseUrl() + "/service";
-        this.restTemplate.put(url, createServiceJson(name));
-    }
-
     @Test
     public void testPutAndGetService() throws Exception {
+        reset();
         // PUT
         putService("name");
 
         // GET
         String url = baseUrl() + "/services?name=name";
         String rsp = this.restTemplate.getForObject(url, String.class);
-        List<ImmutableServiceStatus> info = parseList(rsp, ImmutableServiceStatus.class);
+        List<ServiceStatus> info = parseList(rsp, ServiceStatus.class);
         assertThat(info.size() == 1);
         ServiceStatus status = info.iterator().next();
-        assertThat(status.keepAliveInterval() == 1);
-        assertThat(status.name().equals("name"));
+        assertThat(status.keepAliveIntervalSeconds == 1);
+        assertThat(status.name.equals("name"));
 
         // GET (all)
         url = baseUrl() + "/services";
@@ -410,11 +448,21 @@ public class ApplicationTest {
         assertThat(rsp.contains("name"));
         System.out.println(rsp);
 
+        // Keep alive
+        url = baseUrl() + "/services/keepalive?name=name";
+        rsp = this.restTemplate.postForObject(url, null, String.class);
+        assertThat(rsp.contains("OK"));
+
         // DELETE
         assertThat(services.size() == 1);
         url = baseUrl() + "/services?name=name";
         this.restTemplate.delete(url);
         assertThat(services.size() == 0);
+
+        // Keep alive, no registerred service
+        url = baseUrl() + "/services/keepalive?name=nameXXX";
+        ResponseEntity<String> entity = this.restTemplate.postForEntity(url, null, String.class);
+        assertThat(entity.getStatusCode().equals(HttpStatus.NOT_FOUND));
     }
 
     private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
index b635cb7..314c44c 100644 (file)
@@ -45,11 +45,6 @@ public class MockA1ClientFactory extends A1ClientFactory {
         return getOrCreateA1Client(ric.name());
     }
 
-    @Override
-    protected A1Client createControllerA1Client(Ric ric) {
-        return getOrCreateA1Client(ric.name());
-    }
-
     public MockA1Client getOrCreateA1Client(String ricName) {
         if (!clients.containsKey(ricName)) {
             logger.debug("Creating client for RIC: {}", ricName);