From f26d17f375a8ab4d521549543d4fcc36bdc98865 Mon Sep 17 00:00:00 2001 From: PatrikBuhr Date: Tue, 28 Jan 2020 17:04:16 +0100 Subject: [PATCH] NBI documentation using swagger UI Change-Id: I2a0561e06f1c92a61609e065478bbabdd241f744 Issue-ID: NONRTRIC-84 Signed-off-by: PatrikBuhr --- .../policyagentapi/PolicyAgentApiImpl.java | 50 +- .../controller/PortalRestCentralServiceTest.java | 1 + policy-agent/README.md | 2 +- policy-agent/docs/api.doc | 516 +++++++++++++++++++++ .../policyagent/clients/AsyncRestClient.java | 4 +- .../policyagent/controllers/PolicyController.java | 60 ++- .../oransc/policyagent/controllers/PolicyInfo.java | 33 +- .../oransc/policyagent/controllers/RicInfo.java | 22 +- .../controllers/RicRepositoryController.java | 16 +- .../policyagent/controllers/ServiceController.java | 38 +- .../controllers/ServiceRegistrationInfo.java | 28 +- .../policyagent/controllers/ServiceStatus.java | 26 +- .../policyagent/controllers/StatusController.java | 2 +- .../org/oransc/policyagent/repository/Service.java | 4 +- .../org/oransc/policyagent/ApplicationTest.java | 106 +++-- .../policyagent/utils/MockA1ClientFactory.java | 5 - 16 files changed, 778 insertions(+), 135 deletions(-) create mode 100644 policy-agent/docs/api.doc diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java index 19865e18..c0dde9bd 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java @@ -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 getAllPolicyTypes() { - String url = baseUrl() + "/policy_schemas"; - ResponseEntity 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 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>() { - }.getType(); + Type listType = new TypeToken>() {}.getType(); List 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 putPolicy(String policyTypeIdString, String policyInstanceId, String json, - String ric) { + String ric) { String url = baseUrl() + "/policy?type={type}&instance={instance}&ric={ric}&service={service}"; Map 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>() { - }.getType(); + Type listType = new TypeToken>() {}.getType(); List rspParsed = gson.fromJson(rsp, listType); Collection result = new Vector<>(rspParsed.size()); for (RicInfo ric : rspParsed) { @@ -197,4 +199,10 @@ public class PolicyAgentApiImpl implements PolicyAgentApi { } } + private HttpEntity createJsonHttpEntity(String content) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + return new HttpEntity(content, headers); + } + } diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java index b9f16358..ecb9640d 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java @@ -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; diff --git a/policy-agent/README.md b/policy-agent/README.md index 6077a4b6..655000ec 100644 --- a/policy-agent/README.md +++ b/policy-agent/README.md @@ -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 index 00000000..e3f44ed2 --- /dev/null +++ b/policy-agent/docs/api.doc @@ -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 + diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java index 25742176..3fd16d07 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java @@ -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()))) // diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java index 97fe2a5b..12deb819 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java @@ -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 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 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 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 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> 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> 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 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 policies) { Vector 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); diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java index ed705dc7..bd130629 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java @@ -20,23 +20,38 @@ 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; + } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java index cbb205cc..73103b80 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java @@ -20,18 +20,28 @@ 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 managedElementIds; - public Collection managedElementIds(); + @ApiModelProperty(value = "supported policy types") + public final Collection policyTypes; - public Collection policyTypes(); + RicInfo(String name, Collection managedElementIds, Collection policyTypes) { + this.name = name; + this.managedElementIds = managedElementIds; + this.policyTypes = policyTypes; + } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java index 0d49833f..a5667816 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java @@ -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 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 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())); } } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java index 8996376c..012d40a9 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java @@ -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 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 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("OK", HttpStatus.OK); } catch (Exception e) { return new ResponseEntity(e.getMessage(), HttpStatus.NO_CONTENT); } } + @ApiOperation(value = "Delete a service") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")}) @DeleteMapping("/services") public ResponseEntity 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 keepAliveService( // + @RequestParam(name = "name", required = true) String name) { + try { + services.getService(name).ping(); + return new ResponseEntity("OK", HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity(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); } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java index e0d38496..145fdb02 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java @@ -20,17 +20,33 @@ 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; + } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java index 3063c6bc..c30cce15 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java @@ -20,22 +20,28 @@ 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; + } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java index 226ee736..1219a0d8 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java @@ -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> getStatus() { Mono> response = Mono.just(new ResponseEntity<>("hunky dory", HttpStatus.OK)); diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java index bcdc2ee0..f0863a5e 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java @@ -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() { diff --git a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java index d9a4b5cf..eec9c70e 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java @@ -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 createJsonHttpEntity(String content) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + return new HttpEntity(content, headers); + } + + private void putService(String name) { + String url = baseUrl() + "/service"; + HttpEntity 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 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 info = parseList(rsp, ImmutableServiceStatus.class); + List 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 entity = this.restTemplate.postForEntity(url, null, String.class); + assertThat(entity.getStatusCode().equals(HttpStatus.NOT_FOUND)); } private static List parseList(String jsonString, Class clazz) { diff --git a/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java b/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java index b635cb73..314c44c1 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java @@ -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); -- 2.16.6