From: elinuxhenrik Date: Mon, 19 Oct 2020 08:39:59 +0000 (+0200) Subject: Introduce rAPP Catalogue API X-Git-Tag: 2.1.0~24^2 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F20%2F4920%2F5;p=nonrtric.git Introduce rAPP Catalogue API Change-Id: I0128c5b6405d2efd4de49d4ced200cd9fed6cf96 Issue-ID: NONRTRIC-287 Signed-off-by: elinuxhenrik --- diff --git a/.gitignore b/.gitignore index e5a2f721..26c6731d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,12 @@ docs/_build/ # Eclipse .checkstyle +.classpath +target/ .sts4-cache .project .settings .pydevproject infer-out/ -.vscode \ No newline at end of file +.vscode diff --git a/docs/.project b/docs/.project deleted file mode 100644 index e248a4fa..00000000 --- a/docs/.project +++ /dev/null @@ -1,11 +0,0 @@ - - - docs - - - - - - - - diff --git a/docs/api-docs.rst b/docs/api-docs.rst index 12ff5167..9b0608a6 100644 --- a/docs/api-docs.rst +++ b/docs/api-docs.rst @@ -15,9 +15,10 @@ This is the API-docs of Non-RT RIC. :depth: 3 :local: -The Non-RT RIC consists of two parts, described in the sections below: +The Non-RT RIC consists of three parts, described in the sections below: * The Policy Agent * The SDNC A1 Controller + * The rAPP Catalogue Policy Agent @@ -44,6 +45,26 @@ See :ref:`sdnc-a1-controller-api` for how to use the API. See the README.md file in the nonrtric/sdnc-a1-controller repo for info about how to use it. +rAPP Catalogue +============== + +The Non RT-RIC Service Catalogue provides a way for services to register themselves for other services to discover. + +See `RAC API <./rac-api.html>`_ for how to use the API. + +.. |swagger-icon| image:: ./images/swagger.png + :width: 40px + +.. |yaml-icon| image:: ./images/yaml_logo.png + :width: 40px + + +.. csv-table:: + :header: "API name", "|swagger-icon|", "|yaml-icon|" + :widths: 10,5, 5 + + "RAC API", ":download:`link <../r-app-catalogue/api/rac-api.json>`", ":download:`link <../r-app-catalogue/api/rac-api.yaml>`" + Complementary tools =================== diff --git a/docs/conf.py b/docs/conf.py index d6202898..a6ae7f96 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,9 +7,23 @@ branch = 'latest' linkcheck_ignore = [ 'http://localhost.*', 'http://127.0.0.1.*', - 'https://gerrit.o-ran-sc.org.*' + 'https://gerrit.o-ran-sc.org.*', + './rac-api.html' #Generated file that doesn't exist at link check. ] +extensions = ['sphinxcontrib.redoc', 'sphinx.ext.intersphinx',] + +redoc = [ + { + 'name': 'RAC API', + 'page': 'rac-api', + 'spec': '../r-app-catalogue/api/rac-api.json', + 'embed': True, + } + ] + +redoc_uri = 'https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js' + #intershpinx mapping with other projects intersphinx_mapping = {} diff --git a/docs/images/swagger.png b/docs/images/swagger.png new file mode 100644 index 00000000..f5a9e0c0 Binary files /dev/null and b/docs/images/swagger.png differ diff --git a/docs/images/yaml_logo.png b/docs/images/yaml_logo.png new file mode 100644 index 00000000..0492eb4b Binary files /dev/null and b/docs/images/yaml_logo.png differ diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 09a0c1cb..78db6850 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,5 +1,12 @@ -sphinx -sphinx-rtd-theme -sphinxcontrib-httpdomain -recommonmark -lfdocs-conf +tox +Sphinx>=2,<4 +doc8 +docutils +setuptools +six +sphinx_rtd_theme>=0.4.3 +sphinxcontrib-needs>=0.2.3 +sphinxcontrib-swaggerdoc +sphinx_bootstrap_theme +sphinxcontrib-redoc +lfdocs-conf \ No newline at end of file diff --git a/pom.xml b/pom.xml index cb8302ef..f21502fe 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ policy-agent sdnc-a1-controller enrichment-coordinator-service + r-app-catalogue diff --git a/r-app-catalogue/.gitignore b/r-app-catalogue/.gitignore new file mode 100644 index 00000000..ad56f2de --- /dev/null +++ b/r-app-catalogue/.gitignore @@ -0,0 +1,3 @@ +.swagger-codegen-ignore +.swagger-codegen/ +api/README.md diff --git a/r-app-catalogue/Dockerfile b/r-app-catalogue/Dockerfile new file mode 100644 index 00000000..a85f57d6 --- /dev/null +++ b/r-app-catalogue/Dockerfile @@ -0,0 +1,39 @@ +# +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Nordix Foundation. +# ================================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ============LICENSE_END========================================================= +# +FROM openjdk:11-jre-slim + +ARG JAR + +WORKDIR /opt/app/r-app-catalogue +RUN mkdir -p /var/log/r-app-catalogue + +EXPOSE 8081 8433 + +ADD /config/application.yaml /opt/app/r-app-catalogue/config/application.yaml +ADD target/${JAR} /opt/app/r-app-catalogue/r-app-catalogue.jar + + +RUN chmod -R 777 /opt/app/r-app-catalogue/config/ + +CMD ["java", "-jar", "/opt/app/r-app-catalogue/r-app-catalogue.jar"] + + + + diff --git a/r-app-catalogue/README.md b/r-app-catalogue/README.md new file mode 100644 index 00000000..863713db --- /dev/null +++ b/r-app-catalogue/README.md @@ -0,0 +1,27 @@ +# O-RAN-SC Non-RT RIC rAPP Catalogue + +The O-RAN Non-RT RIC rApp Catalogue provides an OpenApi 3.0 REST API for services to register themselves and discover +other services. + +**NOTE!** The definition of the REST API is done in the `api/rac-api.json` file. The yaml version of the file is +generated during compilation. + +The application is a SpringBoot application generated using the openapitools openapi-generator-maven-plugin. + +To start the application run: +`mvn spring-boot:run` + +## License + +Copyright (C) 2020 Nordix Foundation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/r-app-catalogue/api/rac-api.json b/r-app-catalogue/api/rac-api.json new file mode 100644 index 00000000..3741bdd1 --- /dev/null +++ b/r-app-catalogue/api/rac-api.json @@ -0,0 +1,246 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "rAPP Catalogue API", + "description": "The Non RT-RIC Service Catalogue provides a way for services to register themselves for other services to discover.", + "version": "1.0.0" + }, + "paths": { + "/services": { + "get": { + "summary": "Service names", + "deprecated": false, + "operationId": "getServiceNamesUsingGET", + "responses": { + "200": { + "description": "Service names", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "example": [ + "DroneIdentifier", + "Collector" + ] + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Not used" + } + }, + "tags": [ + "rAPP Catalogue API" + ] + } + }, + "/services/{serviceName}": { + "get": { + "summary": "Individual Service", + "deprecated": false, + "operationId": "getIndividualServiceUsingGET", + "responses": { + "200": { + "description": "EI Job", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/service" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Service is not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error_information" + } + } + } + } + }, + "parameters": [ + { + "in": "path", + "name": "serviceName", + "description": "serviceName", + "schema": { + "type": "string" + }, + "required": true, + "example": "DroneIdentifier" + } + ], + "tags": [ + "rAPP Catalogue API" + ] + }, + "put": { + "summary": "Create or update a Service", + "deprecated": false, + "operationId": "putIndividualServiceUsingPUT", + "responses": { + "200": { + "description": "Service updated" + }, + "201": { + "description": "Service created" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Provided service is not correct", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error_information" + }, + "example": { + "detail": "Service is missing required property version", + "status": 404 + } + } + } + } + }, + "parameters": [ + { + "name": "serviceName", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "example": "DroneIdentifier" + } + ], + "requestBody": { + "description": "Service to create/update", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/service" + } + } + } + }, + "tags": [ + "rAPP Catalogue API" + ] + }, + "delete": { + "summary": "Remove a Service from the catalogue", + "deprecated": false, + "operationId": "deleteIndividualServiceUsingDELETE", + "responses": { + "200": { + "description": "Not used" + }, + "204": { + "description": "Job deleted" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + }, + "404": { + "description": "Service is not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error_information" + } + } + } + } + }, + "parameters": [ + { + "name": "serviceName", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "example": "DroneIdentifier" + } + ], + "tags": [ + "rAPP Catalogue API" + ] + } + } + }, + "components": { + "schemas": { + "service": { + "description": "A Service", + "type": "object", + "title": "service", + "required": [ + "version" + ], + "properties": { + "version": { + "description": "Version of the Service", + "type": "string", + "example": "1.0.0" + }, + "display_name": { + "description": "Display name for the Service", + "type": "string", + "example": "Drone Identifier" + }, + "description": { + "description": "Description of the Service", + "type": "string", + "example": "Detects if a UE is a drone" + } + } + }, + "error_information": { + "description": "Problem as defined in https://tools.ietf.org/html/rfc7807", + "type": "object", + "title": "error_information", + "properties": { + "detail": { + "description": "A human-readable explanation specific to this occurrence of the problem.", + "type": "string", + "example": "Service not found" + }, + "status": { + "format": "int32", + "description": "The HTTP status code generated by the origin server for this occurrence of the problem.", + "type": "integer", + "example": 404 + } + } + } + } + } +} \ No newline at end of file diff --git a/r-app-catalogue/api/rac-api.yaml b/r-app-catalogue/api/rac-api.yaml new file mode 100644 index 00000000..87e2eb97 --- /dev/null +++ b/r-app-catalogue/api/rac-api.yaml @@ -0,0 +1,175 @@ +openapi: 3.0.0 +info: + title: rAPP Catalogue API + description: The Non RT-RIC Service Catalogue provides a way for services to register + themselves for other services to discover. + version: 1.0.0 +servers: +- url: / +paths: + /services: + get: + tags: + - rAPP Catalogue API + summary: Service names + operationId: getServiceNamesUsingGET + responses: + 200: + description: Service names + content: + application/json: + schema: + type: array + items: + type: string + example: + - DroneIdentifier + - Collector + 401: + description: Unauthorized + 403: + description: Forbidden + 404: + description: Not used + deprecated: false + /services/{serviceName}: + get: + tags: + - rAPP Catalogue API + summary: Individual Service + operationId: getIndividualServiceUsingGET + parameters: + - name: serviceName + in: path + description: serviceName + required: true + style: simple + explode: false + schema: + type: string + example: DroneIdentifier + responses: + 200: + description: EI Job + content: + application/json: + schema: + $ref: '#/components/schemas/service' + 401: + description: Unauthorized + 403: + description: Forbidden + 404: + description: Service is not found + content: + application/json: + schema: + $ref: '#/components/schemas/error_information' + deprecated: false + put: + tags: + - rAPP Catalogue API + summary: Create or update a Service + operationId: putIndividualServiceUsingPUT + parameters: + - name: serviceName + in: path + required: true + style: simple + explode: false + schema: + type: string + example: DroneIdentifier + requestBody: + description: Service to create/update + content: + application/json: + schema: + $ref: '#/components/schemas/service' + required: true + responses: + 200: + description: Service updated + 201: + description: Service created + 401: + description: Unauthorized + 403: + description: Forbidden + 404: + description: Provided service is not correct + content: + application/json: + schema: + $ref: '#/components/schemas/error_information' + example: + detail: Service is missing required property version + status: 404 + deprecated: false + delete: + tags: + - rAPP Catalogue API + summary: Remove a Service from the catalogue + operationId: deleteIndividualServiceUsingDELETE + parameters: + - name: serviceName + in: path + required: true + style: simple + explode: false + schema: + type: string + example: DroneIdentifier + responses: + 200: + description: Not used + 204: + description: Job deleted + 401: + description: Unauthorized + 403: + description: Forbidden + 404: + description: Service is not found + content: + application/json: + schema: + $ref: '#/components/schemas/error_information' + deprecated: false +components: + schemas: + service: + title: service + required: + - version + type: object + properties: + version: + type: string + description: Version of the Service + example: 1.0.0 + display_name: + type: string + description: Display name for the Service + example: Drone Identifier + description: + type: string + description: Description of the Service + example: Detects if a UE is a drone + description: A Service + error_information: + title: error_information + type: object + properties: + detail: + type: string + description: A human-readable explanation specific to this occurrence of + the problem. + example: Service not found + status: + type: integer + description: The HTTP status code generated by the origin server for this + occurrence of the problem. + format: int32 + example: 404 + description: Problem as defined in https://tools.ietf.org/html/rfc7807 diff --git a/r-app-catalogue/config/application.yaml b/r-app-catalogue/config/application.yaml new file mode 100644 index 00000000..fadf7d24 --- /dev/null +++ b/r-app-catalogue/config/application.yaml @@ -0,0 +1,4 @@ +spring: + profiles: + active: prod + diff --git a/r-app-catalogue/pom.xml b/r-app-catalogue/pom.xml new file mode 100644 index 00000000..3ac85627 --- /dev/null +++ b/r-app-catalogue/pom.xml @@ -0,0 +1,250 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + org.o-ran-sc.nonrtric + r-app-catalogue + 1.0.0-SNAPSHOT + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + 11 + 1.5.22 + 2.9.2 + 0.2.1 + 4.3.1 + 3.0.11 + 0.30.0 + + + + + io.swagger + swagger-annotations + ${swagger-annotations.version} + + + com.fasterxml.jackson.core + jackson-annotations + + + org.springframework + spring-beans + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework + spring-web + + + org.springframework.boot + spring-boot + + + org.springframework + spring-webmvc + + + org.springframework + spring-context + + + io.springfox + springfox-swagger2 + ${springfox.version} + + + io.springfox + springfox-core + ${springfox.version} + + + io.springfox + springfox-spring-web + ${springfox.version} + + + io.springfox + springfox-spi + ${springfox.version} + + + org.assertj + assertj-core + + + org.apache.tomcat.embed + tomcat-embed-core + + + org.openapitools + jackson-databind-nullable + ${jackson-databind-nullable.version} + + + javax.validation + validation-api + + + com.fasterxml.jackson.core + jackson-databind + + + org.yaml + snakeyaml + runtime + + + + org.springframework + spring-test + test + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.openapitools + openapi-generator-maven-plugin + ${openapi-generator-maven-plugin.version} + + + + generate + + + ${project.basedir}/api/rac-api.json + spring + org.oransc.rappcatalogue.api + org.oransc.rappcatalogue.model + + true + + + + + + + io.swagger.codegen.v3 + swagger-codegen-maven-plugin + ${swagger-codegen-maven-plugin.version} + + + + generate + + + ${project.basedir}/api/rac-api.json + openapi-yaml + ${project.basedir}/api/ + + rac-api.yaml + + + + + + + io.fabric8 + docker-maven-plugin + ${docker-maven-plugin.version} + false + + + generate-r-app-catalogue-image + package + + build + + + ${env.CONTAINER_PULL_REGISTRY} + + + o-ran-sc/nonrtric-r-app-catalogue:${project.version} + + try + ${basedir} + Dockerfile + + ${project.build.finalName}.jar + + + ${project.version} + + + + + + + + push-r-app-catalogue-image + + build + push + + + ${env.CONTAINER_PULL_REGISTRY} + ${env.CONTAINER_PUSH_REGISTRY} + + + o-ran-sc/nonrtric-r-app-catalogue:${project.version} + + ${basedir} + Dockerfile + + ${project.build.finalName}.jar + + + ${project.version} + latest + + + + + + + + + + + \ No newline at end of file diff --git a/r-app-catalogue/src/main/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImpl.java b/r-app-catalogue/src/main/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImpl.java new file mode 100644 index 00000000..701f1d8b --- /dev/null +++ b/r-app-catalogue/src/main/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImpl.java @@ -0,0 +1,34 @@ +package org.oransc.rappcatalogue.api; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +@org.springframework.stereotype.Service +public class ServicesApiDelegateImpl implements ServicesApiDelegate { + + @Override + public ResponseEntity deleteIndividualServiceUsingDELETE(String serviceName) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + // @Override + // public ResponseEntity getIndividualServiceUsingGET(String serviceName) { + // return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + // } + + @Override + public ResponseEntity> getServiceNamesUsingGET() { + List services = Arrays.asList("a", "b"); + return ResponseEntity.ok(services); + } + + // @Override + // public ResponseEntity putIndividualServiceUsingPUT(String serviceName, Service service) { + // return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + // } +} diff --git a/r-app-catalogue/src/test/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImplTest.java b/r-app-catalogue/src/test/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImplTest.java new file mode 100644 index 00000000..53dfc1a5 --- /dev/null +++ b/r-app-catalogue/src/test/java/org/oransc/rappcatalogue/api/ServicesApiDelegateImplTest.java @@ -0,0 +1,33 @@ +package org.oransc.rappcatalogue.api; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +class ServicesApiDelegateImplTest { + + @Test + void putValidService_shouldBeOk() { + ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(); + + ResponseEntity> response = delegateUnderTest.getServiceNamesUsingGET(); + } + + @Test + void getServices_shouldProvideArrayOfServices() throws Exception { + ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl(); + + ResponseEntity> response = delegateUnderTest.getServiceNamesUsingGET(); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(Arrays.asList("a", "b")); + } +} diff --git a/tox.ini b/tox.ini index 4491722a..2705e16e 100644 --- a/tox.ini +++ b/tox.ini @@ -24,23 +24,14 @@ skipsdist = true [testenv:docs] basepython = python3 -deps = - sphinx - sphinx-rtd-theme - sphinxcontrib-httpdomain - recommonmark - lfdocs-conf +deps = -r{toxinidir}/docs/requirements-docs.txt commands = - sphinx-build -W -b html -n -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/html + sphinx-build -W -b html -n -d {envtmpdir}/docs/doctrees ./docs/ {toxinidir}/docs/_build/html echo "Generated docs available in {toxinidir}/docs/_build/html" whitelist_externals = echo [testenv:docs-linkcheck] basepython = python3 -deps = sphinx - sphinx-rtd-theme - sphinxcontrib-httpdomain - recommonmark - lfdocs-conf +deps = -r{toxinidir}/docs/requirements-docs.txt commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/linkcheck