From: elinuxhenrik Date: Mon, 3 Feb 2020 15:02:21 +0000 (+0100) Subject: Uplift Amber for maintenance release X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F06%2F2406%2F1;p=nonrtric.git Uplift Amber for maintenance release Change-Id: Idd92e8193c7fc88c0e00d14f63a8c4e0dcbd012a Signed-off-by: elinuxhenrik --- diff --git a/.gitignore b/.gitignore index 67d6d0f1..90c40a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ .tox docs/_build/ .DS_STORE + +# Eclipse +.checkstyle +.sts4-cache \ No newline at end of file diff --git a/dashboard/.gitignore b/dashboard/.gitignore index 675d063c..1fe9c87d 100644 --- a/dashboard/.gitignore +++ b/dashboard/.gitignore @@ -14,7 +14,7 @@ /.classpath /.project /.settings -/target/ +target /.mvn/wrapper/maven-wrapper.jar /.tox diff --git a/dashboard/a1-controller-client/.gitignore b/dashboard/a1-controller-client/.gitignore deleted file mode 100644 index 212de161..00000000 --- a/dashboard/a1-controller-client/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear - -# exclude jar for gradle wrapper -!gradle/wrapper/*.jar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -# build files -**/target -target -.gradle -build - -logs/ -bin/ diff --git a/dashboard/a1-controller-client/README.md b/dashboard/a1-controller-client/README.md deleted file mode 100644 index b93a390b..00000000 --- a/dashboard/a1-controller-client/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# A1 Controller Client Generator - -This projects generates a REST client library from the OpenAPI specification -file stored here and packages it in a jar. - -## Eclipse and STS Users - -The Swagger Codegen maven plugin is not supported in Eclipse/STS. You can -limp along by taking these steps: - -1. Generate the code using maven: - mvn install -2. Add this folder to the project build path: - target/generated-sources/swagger/src/main/java - -## License - -Copyright (C) 2019 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. diff --git a/dashboard/a1-controller-client/pom.xml b/dashboard/a1-controller-client/pom.xml deleted file mode 100644 index 4011bfb6..00000000 --- a/dashboard/a1-controller-client/pom.xml +++ /dev/null @@ -1,177 +0,0 @@ - - - - 4.0.0 - - org.o-ran-sc.nonrt.ric-dashboard - ric-dash-parent - 1.0.0-SNAPSHOT - - - org.o-ran-sc.ric.plt.a1controller.client - a1-controller-client - RIC A1 Controller client - 0.1.0-SNAPSHOT - - UTF-8 - UTF-8 - - 0 - - org.oransc.ric.a1controller.client - - - - - - javax.annotation - javax.annotation-api - - - io.swagger.core.v3 - swagger-annotations - 2.0.8 - - - org.springframework - spring-context - - - - org.springframework - spring-web - - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - - - - org.junit.jupiter - junit-jupiter-api - test - - - org.slf4j - slf4j-api - - - - - - - io.swagger.codegen.v3 - swagger-codegen-maven-plugin - 3.0.8 - - - - generate - - - ${project.basedir}/src/main/resources/a1_controller_0.1.0.yaml - java - ${client.base.package.name} - ${client.base.package.name}.model - ${client.base.package.name}.api - ${client.base.package.name}.invoker - - ${project.groupId} - ${project.artifactId} - ${project.version} - resttemplate - true - java8 - Apache 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - - - ${project.version}-b${build.number} - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - io.swagger.codegen.v3 - swagger-codegen-maven-plugin - [1.0,) - - generate - - - - - - - - - - - - - - diff --git a/dashboard/a1-controller-client/src/main/resources/a1_controller_0.1.0.yaml b/dashboard/a1-controller-client/src/main/resources/a1_controller_0.1.0.yaml deleted file mode 100644 index 86bb8234..00000000 --- a/dashboard/a1-controller-client/src/main/resources/a1_controller_0.1.0.yaml +++ /dev/null @@ -1,555 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 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. -# ================================================================================== -openapi: 3.0.0 -info: - version: 1.0.0 - title: RIC A1 -paths: - '/A1-ADAPTER-API:getNearRT-RICs': - post: - description: > - Get a list of all nearRT-RICs - tags: - - A1 Controller - operationId: a1.controller.get_all_nearrt_rics - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_NRRids_list_code_schema" - - '/A1-ADAPTER-API:getHealthCheck': - post: - description: > - Get health status for a Near-RT-RIC. true - health ok, false - health is not ok. - tags: - - A1 Controller - operationId: a1.controller.get_healthcheck - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_healthstatus_code_schema" - - '/A1-ADAPTER-API:getPolicyTypes': - post: - description: > - Get a list of all registered policy-type-ids. - tags: - - A1 Controller - operationId: a1.controller.get_all_policy_types - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_PTids_list_code_schema" - - '/A1-ADAPTER-API:createPolicyType': - post: - description: > - Create a policy type. - tags: - - A1 Controller - operationId: a1.controller.create_policy_type - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_desc_name_PT_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_code_schema" - - '/A1-ADAPTER-API:getPolicyType': - post: - description: > - Get a policy type. - tags: - - A1 Controller - operationId: a1.controller.get_policy_type - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_desc_name_PT_code_schema" - - '/A1-ADAPTER-API:deletePolicyType': - post: - description: > - Delete a policy type. - tags: - - A1 Controller - operationId: a1.controller.delete_policy_type - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_code_schema" - - '/A1-ADAPTER-API:getPolicyInstances': - post: - description: > - Get a list of all policy-instance-ids for this policy-type-id. - tags: - - A1 Controller - operationId: a1.controller.get_all_instances_for_type - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_PIids_list_code_schema" - - '/A1-ADAPTER-API:createPolicyInstance': - post: - description: > - Create a policy instance. - tags: - - A1 Controller - operationId: a1.controller.create_policy_instance - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_PIid_PI_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_code_schema" - - '/A1-ADAPTER-API:getPolicyInstance': - post: - description: > - Get a policy instance. - tags: - - A1 Controller - operationId: a1.controller.get_policy_instance - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_PIid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_PI_code_schema" - - '/A1-ADAPTER-API:deletePolicyInstance': - post: - description: > - Delete a policy instance. - tags: - - A1 Controller - operationId: a1.controller.delete_policy_instance - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_PIid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_code_schema" - - '/A1-ADAPTER-API:getStatus': - post: - description: > - Get the status for a policy instance. - tags: - - A1 Controller - operationId: a1.controller.get_policy_instance_status - requestBody: - required: true - content: - application/json: - schema: - "$ref": "#/components/schemas/input_NRRid_PTid_PIid_schema" - responses: - '200': - description: > - Successfully got the response. - content: - application/json: - schema: - "$ref": "#/components/schemas/output_status_code_schema" - -components: - schemas: - input_NRRid_schema: - type: object - required: - - input - additionalProperties: false - properties: - input: - type: object - required: - - near-rt-ric-id - additionalProperties: false - properties: - near-rt-ric-id: - "$ref": "#/components/schemas/near_rt_ric_id" - - input_NRRid_PTid_schema: - type: object - required: - - input - additionalProperties: false - properties: - input: - type: object - required: - - near-rt-ric-id - - policy-type-id - additionalProperties: false - properties: - near-rt-ric-id: - "$ref": "#/components/schemas/near_rt_ric_id" - policy-type-id: - "$ref": "#/components/schemas/policy_type_id" - - input_NRRid_PTid_PIid_schema: - type: object - required: - - input - additionalProperties: false - properties: - input: - type: object - required: - - near-rt-ric-id - - policy-type-id - - policy-instance-id - additionalProperties: false - properties: - near-rt-ric-id: - "$ref": "#/components/schemas/near_rt_ric_id" - policy-type-id: - "$ref": "#/components/schemas/policy_type_id" - policy-instance-id: - "$ref": "#/components/schemas/policy_instance_id" - - input_NRRid_PTid_PIid_PI_schema: - type: object - required: - - input - additionalProperties: false - properties: - input: - type: object - required: - - near-rt-ric-id - - policy-type-id - - policy-instance-id - - policy-instance - additionalProperties: false - properties: - near-rt-ric-id: - "$ref": "#/components/schemas/near_rt_ric_id" - policy-type-id: - "$ref": "#/components/schemas/policy_type_id" - policy-instance-id: - "$ref": "#/components/schemas/policy_instance_id" - policy-instance: - "$ref": "#/components/schemas/policy_instance" - - input_NRRid_PTid_desc_name_PT_schema: - type: object - required: - - input - additionalProperties: false - properties: - input: - type: object - required: - - near-rt-ric-id - - policy-type-id - - description - - name - - policy-type - additionalProperties: false - properties: - near-rt-ric-id: - "$ref": "#/components/schemas/near_rt_ric_id" - policy-type-id: - "$ref": "#/components/schemas/policy_type_id" - description: - type: string - name: - type: string - policy-type: - "$ref": "#/components/schemas/policy_type" - - output_NRRids_list_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - near-rt-ric-id-list - - code - additionalProperties: false - properties: - near-rt-ric-id-list: - type: array - items: - "$ref": "#/components/schemas/near_rt_ric_id" - code: - type: string - - output_healthstatus_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - health-status - - code - additionalProperties: false - properties: - health-status: - type: boolean - code: - type: string - - output_desc_name_PT_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - description - - name - - policy_type - - code - additionalProperties: false - properties: - description: - type: string - name: - type: string - policy-type: - "$ref": "#/components/schemas/policy_type" - code: - type: string - - output_PTids_list_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - policy-type-id-list - - code - additionalProperties: false - properties: - policy-type-id-list: - type: array - items: - "$ref": "#/components/schemas/policy_type_id" - code: - type: string - - output_PIids_list_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - policy-instance-id-list - - code - additionalProperties: false - properties: - policy-instance-id-list: - type: array - items: - "$ref": "#/components/schemas/policy_instance_id" - code: - type: string - - output_PI_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - policy-instance - - code - additionalProperties: false - properties: - policy-instance: - "$ref": "#/components/schemas/policy_instance" - code: - type: string - - output_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - code - additionalProperties: false - properties: - code: - type: string - - output_status_code_schema: - type: object - required: - - output - additionalProperties: false - properties: - output: - type: object - required: - - status - - code - additionalProperties: false - properties: - status: - type: string - code: - type: string - - near_rt_ric_id: - description: > - represents a near RT RIC identifier. Currently this can be any string. - type: string - example: near-rt-ric-1 - - policy_type_id: - description: > - represents a policy type identifier. Currently this is an integer. - type: integer - example: 20000 - - policy_instance_id: - description: > - represents a policy instance identifier. UUIDs are advisable but can be any string - type: string - example: 3d2157af-6a8f-4a7c-810f-38c2f824bf12 - - policy_type: - description: > - represents a policy type. String is used for now to represent this - type: string - example: - "{type: A}" - - policy_instance: - description: > - represents a policy instance. String is used for now to represent this - type: string - example: - "{slice_id: slice-1, priority_level: high}" - - securitySchemes: - basicAuth: - type: http - scheme: basic - -security: - - basicAuth: [] \ No newline at end of file diff --git a/dashboard/a1-controller-client/src/test/java/org/oransc/ric/portal/dashboard/a1controller/client/test/A1ControllerClientTest.java b/dashboard/a1-controller-client/src/test/java/org/oransc/ric/portal/dashboard/a1controller/client/test/A1ControllerClientTest.java deleted file mode 100644 index cbafba42..00000000 --- a/dashboard/a1-controller-client/src/test/java/org/oransc/ric/portal/dashboard/a1controller/client/test/A1ControllerClientTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * O-RAN-SC - * %% - * Copyright (C) 2019 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. - * ========================LICENSE_END=================================== - */ -package org.oransc.ric.portal.dashboard.a1controller.client.test; - -import java.lang.invoke.MethodHandles; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.oransc.ric.a1controller.client.api.A1ControllerApi; -import org.oransc.ric.a1controller.client.invoker.ApiClient; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidPISchema; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidPISchemaInput; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidSchema; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.client.RestClientException; - -/** - * Demonstrates use of the generated A1 controller client. - * - * The tests fail because no server is available. - */ -public class A1ControllerClientTest { - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Test - public void demo() { - ApiClient apiClient = new ApiClient(); - apiClient.setBasePath("http://localhost:30099/"); - A1ControllerApi a1Api = new A1ControllerApi(apiClient); - try { - Object o = a1Api.a1ControllerGetPolicyInstance(new InputNRRidPTidPIidSchema()); - logger.info( - "getPolicy answered code {}, content {} ", apiClient.getStatusCode().toString(), o.toString()); - Assertions.assertTrue(apiClient.getStatusCode().is2xxSuccessful()); - } catch (RestClientException e) { - logger.error("getPolicy failed: {}", e.toString()); - } - try { - String policy = "{}"; - InputNRRidPTidPIidPISchema body = new InputNRRidPTidPIidPISchema(); - InputNRRidPTidPIidPISchemaInput input = new InputNRRidPTidPIidPISchemaInput(); - input.setNearRtRicId("1"); - input.setPolicyTypeId(1); - input.setPolicyInstanceId("1"); - input.setPolicyInstance("{}"); - body.setInput(input); - a1Api.a1ControllerCreatePolicyInstance(body); - logger.info("putPolicy answered: {}", apiClient.getStatusCode().toString()); - Assertions.assertTrue(apiClient.getStatusCode().is2xxSuccessful()); - } catch (RestClientException e) { - logger.error("getPolicy failed: {}", e.toString()); - } - } -} diff --git a/dashboard/pom.xml b/dashboard/pom.xml index 1473449f..ca7673a1 100644 --- a/dashboard/pom.xml +++ b/dashboard/pom.xml @@ -33,7 +33,7 @@ limitations under the License. ric-dash-parent NonRT RIC Dashboard project pom - 1.0.0-SNAPSHOT + 1.0.1-SNAPSHOT 11 @@ -44,8 +44,7 @@ limitations under the License. ========================LICENSE_START================================= ========================LICENSE_END=================================== - - a1-controller-client + webapp-frontend webapp-backend diff --git a/dashboard/webapp-backend/eclipse-formatter.xml b/dashboard/webapp-backend/eclipse-formatter.xml new file mode 100644 index 00000000..7339434a --- /dev/null +++ b/dashboard/webapp-backend/eclipse-formatter.xml @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dashboard/webapp-backend/pom.xml b/dashboard/webapp-backend/pom.xml index 215ec758..bbbf2a40 100644 --- a/dashboard/webapp-backend/pom.xml +++ b/dashboard/webapp-backend/pom.xml @@ -1,5 +1,8 @@ - - - 4.0.0 - - org.o-ran-sc.nonrt.ric-dashboard - ric-dash-parent - 1.0.0-SNAPSHOT - - ric-dash-be - NonRT RIC Dashboard Webapp backend - - 2.9.2 - - 0 - - - - onap-releases - ONAP - Release Repository - https://nexus.onap.org/content/repositories/releases - - - - - - org.o-ran-sc.ric.plt.a1controller.client - a1-controller-client - 0.1.0-SNAPSHOT - - - org.onap.portal.sdk - epsdk-fw - 2.6.0 - - - commons-logging - commons-logging - - - log4j - log4j - - - log4j - apache-log4j-extras - - - org.slf4j - slf4j-log4j12 - - - junit - junit - - - commons-fileupload - commons-fileupload - - - commons-beanutils - commons-beanutils - - - - org.powermock - powermock-module-junit4 - - - - org.powermock - powermock-api-mockito - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - org.slf4j - slf4j-api - - - - org.slf4j - jcl-over-slf4j - - - ch.qos.logback - logback-classic - - - ch.qos.logback - logback-core - - - io.springfox - springfox-swagger2 - ${springfox.version} - - - io.springfox - springfox-swagger-ui - ${springfox.version} - - - - - org.mockito - mockito-core - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - - 1.4.2 - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.codehaus.mojo - license-maven-plugin - - - src - - - **/*.json - - - - + 0 + + + + onap-releases + ONAP - Release Repository + https://nexus.onap.org/content/repositories/releases + + + + + + org.onap.portal.sdk + epsdk-fw + 2.6.0 + + + commons-logging + commons-logging + + + log4j + log4j + + + log4j + apache-log4j-extras + + + org.slf4j + slf4j-log4j12 + + + junit + junit + + + commons-fileupload + commons-fileupload + + + commons-beanutils + commons-beanutils + + + + org.powermock + powermock-module-junit4 + + + + org.powermock + powermock-api-mockito + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.slf4j + slf4j-api + + + + org.slf4j + jcl-over-slf4j + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + io.springfox + springfox-swagger2 + ${springfox.version} + + + io.springfox + springfox-swagger-ui + ${springfox.version} + + + org.immutables + value + ${immutable.version} + provided + + + org.immutables + gson + ${immutable.version} + + + + + org.mockito + mockito-core + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-launcher + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + net.revelc.code.formatter + formatter-maven-plugin + ${formatter-maven-plugin.version} + + ${project.basedir}/eclipse-formatter.xml + + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless-maven-plugin.version} + + + + + com,java,javax,org + + + + + + + + org.codehaus.mojo + license-maven-plugin + + + src + + + **/*.json + + + + - - org.apache.maven.plugins - maven-jar-plugin - - - - true - - - ${project.version}-b${build.number} - - - - - - maven-resources-plugin - - - copy-resources - validate - - copy-resources - - - ${project.build.directory}/classes/resources/ - - - ${project.parent.basedir}/webapp-frontend/dist/dashApp/ - - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.jacoco - jacoco-maven-plugin - 0.8.4 - - - default-prepare-agent - - prepare-agent - - - - default-report - prepare-package - - report - - - - - - - io.fabric8 - docker-maven-plugin - 0.30.0 - - true - - ${env.CONTAINER_PULL_REGISTRY} - ${env.CONTAINER_PUSH_REGISTRY} - - - - - o-ran-sc/nonrtric-dashboard:${project.version} - - openjdk:11-jre-slim - - - ${project.version} - - - artifact - - - - mkdir /logs - chmod -R 777 /logs - - - - - java - -Xms128m - -Xmx256m - -cp - maven:maven/${project.artifactId}-${project.version}.${project.packaging} - -Dloader.main=org.oransc.ric.portal.dashboard.DashboardApplication - -Djava.security.egd=file:/dev/./urandom - org.springframework.boot.loader.PropertiesLauncher - - - - - - - - - - - build - push - - - - - - + + org.apache.maven.plugins + maven-jar-plugin + + + + true + + + ${project.version}-b${build.number} + + + + + + maven-resources-plugin + + + copy-resources + validate + + copy-resources + + + ${project.build.directory}/classes/resources/ + + + ${project.parent.basedir}/webapp-frontend/dist/dashApp/ + + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.jacoco + jacoco-maven-plugin + 0.8.4 + + + default-prepare-agent + + prepare-agent + + + + default-report + prepare-package + + report + + + + + + + io.fabric8 + docker-maven-plugin + 0.30.0 + + true + + ${env.CONTAINER_PULL_REGISTRY} + ${env.CONTAINER_PUSH_REGISTRY} + + + + + o-ran-sc/nonrtric-dashboard:${project.version} + + openjdk:11-jre-slim + + + ${project.version} + + + artifact + + + + mkdir /logs + chmod -R 777 /logs + + + + + java + -Xms128m + -Xmx256m + -cp + maven:maven/${project.artifactId}-${project.version}.${project.packaging} + -Dloader.main=org.oransc.ric.portal.dashboard.DashboardApplication + -Djava.security.egd=file:/dev/./urandom + org.springframework.boot.loader.PropertiesLauncher + + + + + + + + + + + build + push + + + + + + diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java index 333c5322..2550b8ed 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java @@ -7,9 +7,9 @@ * 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. @@ -34,27 +34,27 @@ import org.springframework.context.annotation.ComponentScan; @ComponentScan("org.oransc.ric.portal.dashboard") public class DashboardApplication { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - public static void main(String[] args) throws IOException { - SpringApplication.run(DashboardApplication.class, args); - // Ensure this appears on the console by using level WARN - logger.warn("main: version '{}' successful start", - getImplementationVersion(MethodHandles.lookup().lookupClass())); - } - - /** - * Gets version details for the specified class. - * - * @param clazz - * Class to get the version - * - * @return the value of the MANIFEST.MF property Implementation-Version as - * written by maven when packaged in a jar; 'unknown' otherwise. - */ - public static String getImplementationVersion(Class clazz) { - String classPath = clazz.getResource(clazz.getSimpleName() + ".class").toString(); - return classPath.startsWith("jar") ? clazz.getPackage().getImplementationVersion() : "unknown-not-jar"; - } + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public static void main(String[] args) throws IOException { + SpringApplication.run(DashboardApplication.class, args); + // Ensure this appears on the console by using level WARN + logger.warn("main: version '{}' successful start", + getImplementationVersion(MethodHandles.lookup().lookupClass())); + } + + /** + * Gets version details for the specified class. + * + * @param clazz + * Class to get the version + * + * @return the value of the MANIFEST.MF property Implementation-Version as + * written by maven when packaged in a jar; 'unknown' otherwise. + */ + public static String getImplementationVersion(Class clazz) { + String classPath = clazz.getResource(clazz.getSimpleName() + ".class").toString(); + return classPath.startsWith("jar") ? clazz.getPackage().getImplementationVersion() : "unknown-not-jar"; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java index d61ce1dd..4b5d9a4a 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java @@ -21,24 +21,24 @@ package org.oransc.ric.portal.dashboard; public abstract class DashboardConstants { - private DashboardConstants() { - // Sonar insists on hiding the constructor - } + private DashboardConstants() { + // Sonar insists on hiding the constructor + } - public static final String ENDPOINT_PREFIX = "/api"; - // Factor out method names used in multiple controllers - public static final String VERSION_METHOD = "version"; - public static final String APP_NAME_AC = "AC"; - public static final String APP_NAME_MC = "MC"; - // The role names are defined by ONAP Portal. - // The prefix "ROLE_" is required by Spring. - // These are used in Java code annotations that require constants. - public static final String ROLE_NAME_STANDARD = "Standard_User"; - public static final String ROLE_NAME_ADMIN = "System_Administrator"; - private static final String ROLE_PREFIX = "ROLE_"; - public static final String ROLE_ADMIN = ROLE_PREFIX + ROLE_NAME_ADMIN; - public static final String ROLE_STANDARD = ROLE_PREFIX + ROLE_NAME_STANDARD; - public static final String A1_CONTROLLER_USERNAME = "admin"; - public static final String A1_CONTROLLER_PASSWORD = "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"; + public static final String ENDPOINT_PREFIX = "/api"; + // Factor out method names used in multiple controllers + public static final String VERSION_METHOD = "version"; + public static final String APP_NAME_AC = "AC"; + public static final String APP_NAME_MC = "MC"; + // The role names are defined by ONAP Portal. + // The prefix "ROLE_" is required by Spring. + // These are used in Java code annotations that require constants. + public static final String ROLE_NAME_STANDARD = "Standard_User"; + public static final String ROLE_NAME_ADMIN = "System_Administrator"; + private static final String ROLE_PREFIX = "ROLE_"; + public static final String ROLE_ADMIN = ROLE_PREFIX + ROLE_NAME_ADMIN; + public static final String ROLE_STANDARD = ROLE_PREFIX + ROLE_NAME_STANDARD; + public static final String POLICY_CONTROLLER_USERNAME = "admin"; + public static final String POLICY_CONTROLLER_PASSWORD = "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"; } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java index c5fd1014..ffb7396f 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java @@ -7,9 +7,9 @@ * 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. @@ -19,6 +19,11 @@ */ package org.oransc.ric.portal.dashboard; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; @@ -33,152 +38,146 @@ import org.onap.portalsdk.core.restful.domain.EcompUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; - /** * Provides simple user-management services. - * + * * This first implementation serializes user details to a file. - * + * * TODO: migrate to a database. */ public class DashboardUserManager { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - // This default value is only useful for development and testing. - public static final String USER_FILE_PATH = "dashboard-users.json"; - - private final File userFile; - private final List users; - - /** - * Development/test-only constructor that uses default file path. - * - * @param clear - * If true, start empty and remove any existing file. - * - * @throws IOException - * On file error - */ - public DashboardUserManager(boolean clear) throws IOException { - this(USER_FILE_PATH); - if (clear) { - logger.debug("ctor: removing file {}", userFile.getAbsolutePath()); - File f = new File(DashboardUserManager.USER_FILE_PATH); - if (f.exists()) - f.delete(); - users.clear(); - } - } - - /** - * Constructur that accepts a file path - * - * @param userFilePath - * File path - * @throws IOException - * If file cannot be read - */ - public DashboardUserManager(final String userFilePath) throws IOException { - logger.debug("ctor: userfile {}", userFilePath); - if (userFilePath == null) - throw new IllegalArgumentException("Missing or empty user file property"); - userFile = new File(userFilePath); - logger.debug("ctor: managing users in file {}", userFile.getAbsolutePath()); - if (userFile.exists()) { - final ObjectMapper mapper = new ObjectMapper(); - users = mapper.readValue(userFile, new TypeReference>() { - }); - } else { - users = new ArrayList<>(); - } - } - - /** - * Gets the current users. - * - * @return List of EcompUser objects, possibly empty - */ - public List getUsers() { - return this.users; - } - - /** - * Gets the user with the specified login Id - * - * @param loginId - * Desired login Id - * @return User object; null if Id is not known - */ - public EcompUser getUser(String loginId) { - for (EcompUser u : this.users) { - if (u.getLoginId().equals(loginId)) { - logger.debug("getUser: match on {}", loginId); - return u; - } - } - logger.debug("getUser: no match on {}", loginId); - return null; - } - - private void saveUsers() throws JsonGenerationException, JsonMappingException, IOException { - final ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(userFile, users); - } - - /* - * Allow at most one thread to create a user at one time. - */ - public synchronized void createUser(EcompUser user) throws PortalAPIException { - logger.debug("createUser: loginId is " + user.getLoginId()); - if (users.contains(user)) - throw new PortalAPIException("User exists: " + user.getLoginId()); - users.add(user); - try { - saveUsers(); - } catch (Exception ex) { - throw new PortalAPIException("Save failed", ex); - } - } - - /* - * Allow at most one thread to modify a user at one time. We still have - * last-edit-wins of course. - */ - public synchronized void updateUser(String loginId, EcompUser user) throws PortalAPIException { - logger.debug("editUser: loginId is " + loginId); - int index = users.indexOf(user); - if (index < 0) - throw new PortalAPIException("User does not exist: " + user.getLoginId()); - users.remove(index); - users.add(user); - try { - saveUsers(); - } catch (Exception ex) { - throw new PortalAPIException("Save failed", ex); - } - } - - // Test infrastructure - public static void main(String[] args) throws Exception { - DashboardUserManager dum = new DashboardUserManager(false); - EcompUser user = new EcompUser(); - user.setActive(true); - user.setLoginId("demo"); - user.setFirstName("First"); - user.setLastName("Last"); - EcompRole role = new EcompRole(); - role.setId(1L); - role.setName(DashboardConstants.ROLE_NAME_ADMIN); - Set roles = new HashSet<>(); - roles.add(role); - user.setRoles(roles); - dum.createUser(user); - logger.debug("Created user {}", user); - } + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + // This default value is only useful for development and testing. + public static final String USER_FILE_PATH = "dashboard-users.json"; + + private final File userFile; + private final List users; + + /** + * Development/test-only constructor that uses default file path. + * + * @param clear + * If true, start empty and remove any existing file. + * + * @throws IOException + * On file error + */ + public DashboardUserManager(boolean clear) throws IOException { + this(USER_FILE_PATH); + if (clear) { + logger.debug("ctor: removing file {}", userFile.getAbsolutePath()); + File f = new File(DashboardUserManager.USER_FILE_PATH); + if (f.exists()) + f.delete(); + users.clear(); + } + } + + /** + * Constructur that accepts a file path + * + * @param userFilePath + * File path + * @throws IOException + * If file cannot be read + */ + public DashboardUserManager(final String userFilePath) throws IOException { + logger.debug("ctor: userfile {}", userFilePath); + if (userFilePath == null) + throw new IllegalArgumentException("Missing or empty user file property"); + userFile = new File(userFilePath); + logger.debug("ctor: managing users in file {}", userFile.getAbsolutePath()); + if (userFile.exists()) { + final ObjectMapper mapper = new ObjectMapper(); + users = mapper.readValue(userFile, new TypeReference>() {}); + } else { + users = new ArrayList<>(); + } + } + + /** + * Gets the current users. + * + * @return List of EcompUser objects, possibly empty + */ + public List getUsers() { + return this.users; + } + + /** + * Gets the user with the specified login Id + * + * @param loginId + * Desired login Id + * @return User object; null if Id is not known + */ + public EcompUser getUser(String loginId) { + for (EcompUser u : this.users) { + if (u.getLoginId().equals(loginId)) { + logger.debug("getUser: match on {}", loginId); + return u; + } + } + logger.debug("getUser: no match on {}", loginId); + return null; + } + + private void saveUsers() throws JsonGenerationException, JsonMappingException, IOException { + final ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(userFile, users); + } + + /* + * Allow at most one thread to create a user at one time. + */ + public synchronized void createUser(EcompUser user) throws PortalAPIException { + logger.debug("createUser: loginId is " + user.getLoginId()); + if (users.contains(user)) + throw new PortalAPIException("User exists: " + user.getLoginId()); + users.add(user); + try { + saveUsers(); + } catch (Exception ex) { + throw new PortalAPIException("Save failed", ex); + } + } + + /* + * Allow at most one thread to modify a user at one time. We still have + * last-edit-wins of course. + */ + public synchronized void updateUser(String loginId, EcompUser user) throws PortalAPIException { + logger.debug("editUser: loginId is " + loginId); + int index = users.indexOf(user); + if (index < 0) + throw new PortalAPIException("User does not exist: " + user.getLoginId()); + users.remove(index); + users.add(user); + try { + saveUsers(); + } catch (Exception ex) { + throw new PortalAPIException("Save failed", ex); + } + } + + // Test infrastructure + public static void main(String[] args) throws Exception { + DashboardUserManager dum = new DashboardUserManager(false); + EcompUser user = new EcompUser(); + user.setActive(true); + user.setLoginId("demo"); + user.setFirstName("First"); + user.setLastName("Last"); + EcompRole role = new EcompRole(); + role.setId(1L); + role.setName(DashboardConstants.ROLE_NAME_ADMIN); + Set roles = new HashSet<>(); + roles.add(role); + user.setRoles(roles); + dum.createUser(user); + logger.debug("Created user {}", user); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1ControllerConfiguration.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1ControllerConfiguration.java deleted file mode 100644 index ffdcacd8..00000000 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1ControllerConfiguration.java +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * O-RAN-SC - * %% - * Copyright (C) 2019 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. - * ========================LICENSE_END=================================== - */ -package org.oransc.ric.portal.dashboard.config; - -import java.lang.invoke.MethodHandles; -import org.oransc.ric.a1controller.client.api.A1ControllerApi; -import org.oransc.ric.a1controller.client.invoker.ApiClient; -import org.oransc.ric.portal.dashboard.DashboardConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriBuilderFactory; - -/** - * Creates an A1 controller client as a bean to be managed by the Spring - * container. - */ -@Configuration -@Profile("!test") -public class A1ControllerConfiguration { - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public static final String A1_CONTROLLER_USERNAME = DashboardConstants.A1_CONTROLLER_USERNAME; - public static final String A1_CONTROLLER_PASSWORD = DashboardConstants.A1_CONTROLLER_PASSWORD; - - // Populated by the autowired constructor - private final String a1ControllerUrl; - - @Autowired - public A1ControllerConfiguration(@Value("${a1controller.url.prefix}") final String urlPrefix, // - @Value("${a1controller.url.suffix}") final String urlSuffix) { - logger.debug("ctor prefix '{}' suffix '{}'", urlPrefix, urlSuffix); - a1ControllerUrl = new DefaultUriBuilderFactory(urlPrefix.trim()).builder().path(urlSuffix.trim()).build().normalize() - .toString(); - logger.info("Configuring A1 Controller at URL {}", a1ControllerUrl); - } - - private ApiClient apiClient() { - ApiClient apiClient = new ApiClient(new RestTemplate()); - apiClient.setBasePath(a1ControllerUrl); - apiClient.setUsername(A1_CONTROLLER_USERNAME); - apiClient.setPassword(A1_CONTROLLER_PASSWORD); - return apiClient; - } - - @Bean - // The bean (method) name must be globally unique - public A1ControllerApi a1ControllerApi() { - return new A1ControllerApi(apiClient()); - } - -} diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AdminConfiguration.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AdminConfiguration.java index 696d74f6..8c646fec 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AdminConfiguration.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AdminConfiguration.java @@ -38,21 +38,21 @@ import org.springframework.context.annotation.Profile; @Profile("!test") public class AdminConfiguration { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - // Populated by the autowired constructor - private final String userfile; - - @Autowired - public AdminConfiguration(@Value("${userfile}") final String userfile) { - logger.debug("ctor userfile '{}'", userfile); - this.userfile = userfile; - } - - @Bean - // The bean (method) name must be globally unique - public DashboardUserManager userManager() throws IOException { - return new DashboardUserManager(userfile); - } + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + // Populated by the autowired constructor + private final String userfile; + + @Autowired + public AdminConfiguration(@Value("${userfile}") final String userfile) { + logger.debug("ctor userfile '{}'", userfile); + this.userfile = userfile; + } + + @Bean + // The bean (method) name must be globally unique + public DashboardUserManager userManager() throws IOException { + return new DashboardUserManager(userfile); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/PortalApiConfiguration.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/PortalApiConfiguration.java index f318cad5..520f27ff 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/PortalApiConfiguration.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/PortalApiConfiguration.java @@ -7,9 +7,9 @@ * 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. @@ -34,24 +34,24 @@ import org.springframework.context.annotation.Profile; @Profile("!test") public class PortalApiConfiguration { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - /** - * Instantiates the EPSDK-FW servlet that implements the API called by Portal. - * Needed because this app is not configured to scan the EPSDK-FW packages; - * there's also a chance that Spring-Boot does not automatically - * process @WebServlet annotations. - * - * @return Servlet registration bean for the Portal Rest API proxy servlet. - */ - @Bean - public ServletRegistrationBean portalApiProxyServletBean() { - logger.debug("portalApiProxyServletBean"); - PortalRestAPIProxy servlet = new PortalRestAPIProxy(); - final ServletRegistrationBean servletBean = new ServletRegistrationBean<>(servlet, - PortalApiConstants.API_PREFIX + "/*"); - servletBean.setName("PortalRestApiProxyServlet"); - return servletBean; - } + /** + * Instantiates the EPSDK-FW servlet that implements the API called by Portal. + * Needed because this app is not configured to scan the EPSDK-FW packages; + * there's also a chance that Spring-Boot does not automatically + * process @WebServlet annotations. + * + * @return Servlet registration bean for the Portal Rest API proxy servlet. + */ + @Bean + public ServletRegistrationBean portalApiProxyServletBean() { + logger.debug("portalApiProxyServletBean"); + PortalRestAPIProxy servlet = new PortalRestAPIProxy(); + final ServletRegistrationBean servletBean = + new ServletRegistrationBean<>(servlet, PortalApiConstants.API_PREFIX + "/*"); + servletBean.setName("PortalRestApiProxyServlet"); + return servletBean; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java index 3a877824..a2f3d8f8 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java @@ -30,15 +30,15 @@ import org.springframework.stereotype.Component; @Component public class SpringContextCache implements ApplicationContextAware { - private static ApplicationContext applicationContext = null; + private static ApplicationContext applicationContext = null; - @Override - public void setApplicationContext(final ApplicationContext appContext) throws BeansException { - applicationContext = appContext; - } + @Override + public void setApplicationContext(final ApplicationContext appContext) throws BeansException { + applicationContext = appContext; + } - public static ApplicationContext getApplicationContext() { - return applicationContext; - } + public static ApplicationContext getApplicationContext() { + return applicationContext; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SwaggerConfiguration.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SwaggerConfiguration.java index b4bb0a31..435414af 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SwaggerConfiguration.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SwaggerConfiguration.java @@ -40,29 +40,29 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 public class SwaggerConfiguration { - /** - * @return new Docket - */ - @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2).select() // - .apis(RequestHandlerSelectors.basePackage(DashboardApplication.class.getPackage().getName())) // - .paths(PathSelectors.any()) // - .build() // - .apiInfo(apiInfo()); - } + /** + * @return new Docket + */ + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2).select() // + .apis(RequestHandlerSelectors.basePackage(DashboardApplication.class.getPackage().getName())) // + .paths(PathSelectors.any()) // + .build() // + .apiInfo(apiInfo()); + } - private ApiInfo apiInfo() { - final String version = DashboardApplication.class.getPackage().getImplementationVersion(); - return new ApiInfoBuilder() // - .title("RIC Dashboard backend") // - .description("Proxies access to RIC services.")// - .termsOfServiceUrl("Terms of service") // - .contact(new Contact("RIC Dashboard Dev Team", // - "http://no-docs-yet.org/", // - "noreply@O-RAN-SC.org")) // - .license("Apache 2.0 License").licenseUrl("http://www.apache.org/licenses/LICENSE-2.0") // - .version(version == null ? "version not available" : version) // - .build(); - } + private ApiInfo apiInfo() { + final String version = DashboardApplication.class.getPackage().getImplementationVersion(); + return new ApiInfoBuilder() // + .title("RIC Dashboard backend") // + .description("Proxies access to RIC services.")// + .termsOfServiceUrl("Terms of service") // + .contact(new Contact("RIC Dashboard Dev Team", // + "http://no-docs-yet.org/", // + "noreply@O-RAN-SC.org")) // + .license("Apache 2.0 License").licenseUrl("http://www.apache.org/licenses/LICENSE-2.0") // + .version(version == null ? "version not available" : version) // + .build(); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java index 0a7f02ca..b43f8a97 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java @@ -23,9 +23,10 @@ package org.oransc.ric.portal.dashboard.config; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationTargetException; + import org.onap.portalsdk.core.onboarding.util.PortalApiConstants; import org.oransc.ric.portal.dashboard.DashboardUserManager; -import org.oransc.ric.portal.dashboard.controller.A1Controller; +import org.oransc.ric.portal.dashboard.controller.PolicyController; import org.oransc.ric.portal.dashboard.controller.SimpleErrorController; import org.oransc.ric.portal.dashboard.portalapi.PortalAuthManager; import org.oransc.ric.portal.dashboard.portalapi.PortalAuthenticationFilter; @@ -50,77 +51,77 @@ import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @Profile("!test") public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - // Although constructor arguments are recommended over field injection, - // this results in fewer lines of code. - @Value("${portalapi.security}") - private Boolean portalapiSecurity; - @Value("${portalapi.appname}") - private String appName; - @Value("${portalapi.username}") - private String userName; - @Value("${portalapi.password}") - private String password; - @Value("${portalapi.decryptor}") - private String decryptor; - @Value("${portalapi.usercookie}") - private String userCookie; + // Although constructor arguments are recommended over field injection, + // this results in fewer lines of code. + @Value("${portalapi.security}") + private Boolean portalapiSecurity; + @Value("${portalapi.appname}") + private String appName; + @Value("${portalapi.username}") + private String userName; + @Value("${portalapi.password}") + private String password; + @Value("${portalapi.decryptor}") + private String decryptor; + @Value("${portalapi.usercookie}") + private String userCookie; - @Autowired - DashboardUserManager userManager; + @Autowired + DashboardUserManager userManager; - @Override + @Override protected void configure(HttpSecurity http) throws Exception { - logger.debug("configure: portalapi.username {}", userName); - // A chain of ".and()" always baffles me - http.authorizeRequests().anyRequest().authenticated(); - http.headers().frameOptions().disable(); - http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); - http.addFilterBefore(portalAuthenticationFilterBean(), BasicAuthenticationFilter.class); - } + logger.debug("configure: portalapi.username {}", userName); + // A chain of ".and()" always baffles me + http.authorizeRequests().anyRequest().authenticated(); + http.headers().frameOptions().disable(); + http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); + http.addFilterBefore(portalAuthenticationFilterBean(), BasicAuthenticationFilter.class); + } - /** - * Resource paths that do not require authentication, especially including - * Swagger-generated documentation. - */ - public static final String[] OPEN_PATHS = { // - "/v2/api-docs", // - "/swagger-resources/**", // - "/swagger-ui.html", // - "/webjars/**", // - PortalApiConstants.API_PREFIX + "/**", // - A1Controller.CONTROLLER_PATH + "/" + A1Controller.VERSION_METHOD, // - SimpleErrorController.ERROR_PATH }; + /** + * Resource paths that do not require authentication, especially including + * Swagger-generated documentation. + */ + public static final String[] OPEN_PATHS = { // + "/v2/api-docs", // + "/swagger-resources/**", // + "/swagger-ui.html", // + "/webjars/**", // + PortalApiConstants.API_PREFIX + "/**", // + PolicyController.CONTROLLER_PATH + "/" + PolicyController.VERSION_METHOD, // + SimpleErrorController.ERROR_PATH}; - @Override - public void configure(WebSecurity web) throws Exception { - // This disables Spring security, but not the app's filter. - web.ignoring().antMatchers(OPEN_PATHS); - } + @Override + public void configure(WebSecurity web) throws Exception { + // This disables Spring security, but not the app's filter. + web.ignoring().antMatchers(OPEN_PATHS); + } - @Bean - public PortalAuthManager portalAuthManagerBean() - throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - return new PortalAuthManager(appName, userName, password, decryptor, userCookie); - } + @Bean + public PortalAuthManager portalAuthManagerBean() + throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + return new PortalAuthManager(appName, userName, password, decryptor, userCookie); + } - /* - * If this is annotated with @Bean, it is created automatically AND REGISTERED, - * and Spring processes annotations in the source of the class. However, the - * filter is added in the chain apparently in the wrong order. Alternately, with - * no @Bean and added to the chain up in the configure() method in the desired - * order, the ignoring() matcher pattern configured above causes Spring to - * bypass this filter, which seems to me means the filter participates - * correctly. - */ - public PortalAuthenticationFilter portalAuthenticationFilterBean() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, - IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { - PortalAuthenticationFilter portalAuthenticationFilter = new PortalAuthenticationFilter(portalapiSecurity, - portalAuthManagerBean(), this.userManager); - return portalAuthenticationFilter; - } + /* + * If this is annotated with @Bean, it is created automatically AND REGISTERED, + * and Spring processes annotations in the source of the class. However, the + * filter is added in the chain apparently in the wrong order. Alternately, with + * no @Bean and added to the chain up in the configure() method in the desired + * order, the ignoring() matcher pattern configured above causes Spring to + * bypass this filter, which seems to me means the filter participates + * correctly. + */ + public PortalAuthenticationFilter portalAuthenticationFilterBean() + throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, + IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { + PortalAuthenticationFilter portalAuthenticationFilter = + new PortalAuthenticationFilter(portalapiSecurity, portalAuthManagerBean(), this.userManager); + return portalAuthenticationFilter; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1Controller.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1Controller.java deleted file mode 100644 index 08b485e8..00000000 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1Controller.java +++ /dev/null @@ -1,250 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * O-RAN-SC - * %% - * Copyright (C) 2019 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. - * ========================LICENSE_END=================================== - */ -package org.oransc.ric.portal.dashboard.controller; - -import java.lang.invoke.MethodHandles; -import java.util.List; -import javax.servlet.http.HttpServletResponse; -import org.oransc.ric.a1controller.client.api.A1ControllerApi; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidPISchema; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidPISchemaInput; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidSchema; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidSchemaInput; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidSchema; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidSchemaInput; -import org.oransc.ric.a1controller.client.model.InputNRRidSchema; -import org.oransc.ric.a1controller.client.model.InputNRRidSchemaInput; -import org.oransc.ric.a1controller.client.model.OutputCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputDescNamePTCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputDescNamePTCodeSchemaOutput; -import org.oransc.ric.a1controller.client.model.OutputPICodeSchema; -import org.oransc.ric.a1controller.client.model.OutputPIidsListCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputPTidsListCodeSchema; -import org.oransc.ric.portal.dashboard.DashboardApplication; -import org.oransc.ric.portal.dashboard.DashboardConstants; -import org.oransc.ric.portal.dashboard.exceptions.HttpBadRequestException; -import org.oransc.ric.portal.dashboard.exceptions.HttpInternalServerErrorException; -import org.oransc.ric.portal.dashboard.exceptions.HttpNotFoundException; -import org.oransc.ric.portal.dashboard.exceptions.HttpNotImplementedException; -import org.oransc.ric.portal.dashboard.model.PolicyInstance; -import org.oransc.ric.portal.dashboard.model.PolicyInstances; -import org.oransc.ric.portal.dashboard.model.PolicyType; -import org.oransc.ric.portal.dashboard.model.PolicyTypes; -import org.oransc.ric.portal.dashboard.model.SuccessTransport; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.security.access.annotation.Secured; -import org.springframework.util.Assert; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import io.swagger.annotations.ApiOperation; - -/** - * Proxies calls from the front end to the A1 Controller via the A1 Mediator - * API. - * - * If a method throws RestClientResponseException, it is handled by - * {@link CustomResponseEntityExceptionHandler#handleProxyMethodException(Exception, - * org.springframework.web.context.request.WebRequest)} - * which returns status 502. All other exceptions are handled by Spring which - * returns status 500. - */ -@RestController -@RequestMapping(value = A1Controller.CONTROLLER_PATH, produces = MediaType.APPLICATION_JSON_VALUE) -public class A1Controller { - - private static final String NEAR_RT_RIC_ID = "NearRtRic1"; - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - // Publish paths in constants so tests are easy to write - public static final String CONTROLLER_PATH = DashboardConstants.ENDPOINT_PREFIX + "/policy"; - // Endpoints - public static final String VERSION_METHOD = DashboardConstants.VERSION_METHOD; - public static final String POLICY_TYPES_METHOD = "policytypes"; - public static final String POLICY_TYPE_ID_NAME = "policy_type_id"; - public static final String POLICIES_NAME = "policies"; - public static final String POLICY_INSTANCE_ID_NAME = "policy_instance_id"; - - // Populated by the autowired constructor - private final A1ControllerApi a1ControllerApi; - - @Autowired - public A1Controller(final A1ControllerApi A1ControllerApi) { - Assert.notNull(A1ControllerApi, "API must not be null"); - this.a1ControllerApi = A1ControllerApi; - if (logger.isDebugEnabled()) - logger.debug("ctor: configured with client type {}", A1ControllerApi.getClass().getName()); - } - - @ApiOperation(value = "Gets the A1 client library MANIFEST.MF property Implementation-Version.", - response = SuccessTransport.class) - @GetMapping(VERSION_METHOD) - // No role required - public SuccessTransport getA1ControllerClientVersion() { - return new SuccessTransport(200, DashboardApplication.getImplementationVersion(A1ControllerApi.class)); - } - - /* - * The fields are defined in the A1Control Typescript interface. - */ - @ApiOperation(value = "Gets the policy types from Near Realtime-RIC via the A1 Controller API") - @GetMapping(POLICY_TYPES_METHOD) - @Secured({ DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD }) - public Object getAllPolicyTypes(HttpServletResponse response) { - logger.debug("getAllPolicyTypes"); - InputNRRidSchemaInput nrrid = new InputNRRidSchemaInput(); - nrrid.setNearRtRicId(NEAR_RT_RIC_ID); - InputNRRidSchema inputSchema = new InputNRRidSchema(); - inputSchema.setInput(nrrid); - OutputPTidsListCodeSchema outputPTidsListCodeSchema = - a1ControllerApi.a1ControllerGetAllPolicyTypes(inputSchema); - checkHttpError(outputPTidsListCodeSchema.getOutput().getCode()); - List policyTypeIds = outputPTidsListCodeSchema.getOutput().getPolicyTypeIdList(); - PolicyTypes policyTypes = new PolicyTypes(); - InputNRRidPTidSchema typeSchema = new InputNRRidPTidSchema(); - InputNRRidPTidSchemaInput typeId = new InputNRRidPTidSchemaInput(); - typeId.setNearRtRicId(NEAR_RT_RIC_ID); - for (Integer policyTypeId : policyTypeIds) { - typeId.setPolicyTypeId(policyTypeId); - typeSchema.setInput(typeId); - OutputDescNamePTCodeSchema controllerGetPolicyType = - a1ControllerApi.a1ControllerGetPolicyType(typeSchema); - checkHttpError(controllerGetPolicyType.getOutput().getCode()); - OutputDescNamePTCodeSchemaOutput policyTypeSchema = controllerGetPolicyType.getOutput(); - PolicyType type = new PolicyType(policyTypeId, policyTypeSchema.getName(), - policyTypeSchema.getPolicyType().toString()); - policyTypes.add(type); - } - return policyTypes; - } - - @ApiOperation(value = "Returns the policy instances for the given policy type.") - @GetMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME) - @Secured({ DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD }) - public Object getPolicyInstances(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString) { - logger.debug("getPolicyInstances {}", policyTypeIdString); - InputNRRidPTidSchemaInput typeIdInput = new InputNRRidPTidSchemaInput(); - typeIdInput.setNearRtRicId(NEAR_RT_RIC_ID); - Integer policyTypeId = Integer.decode(policyTypeIdString); - typeIdInput.setPolicyTypeId(policyTypeId); - InputNRRidPTidSchema inputSchema = new InputNRRidPTidSchema(); - inputSchema.setInput(typeIdInput); - OutputPIidsListCodeSchema controllerGetAllInstancesForType = - a1ControllerApi.a1ControllerGetAllInstancesForType(inputSchema); - checkHttpError(controllerGetAllInstancesForType.getOutput().getCode()); - List instancesForType = controllerGetAllInstancesForType.getOutput().getPolicyInstanceIdList(); - PolicyInstances instances = new PolicyInstances(); - InputNRRidPTidPIidSchemaInput instanceIdInput = new InputNRRidPTidPIidSchemaInput(); - instanceIdInput.setNearRtRicId(NEAR_RT_RIC_ID); - instanceIdInput.setPolicyTypeId(policyTypeId); - InputNRRidPTidPIidSchema instanceInputSchema = new InputNRRidPTidPIidSchema(); - for (String instanceId : instancesForType) { - instanceIdInput.setPolicyInstanceId(instanceId); - instanceInputSchema.setInput(instanceIdInput); - OutputPICodeSchema policyInstance = - a1ControllerApi.a1ControllerGetPolicyInstance(instanceInputSchema); - checkHttpError(policyInstance.getOutput().getCode()); - PolicyInstance instance = - new PolicyInstance(instanceId, policyInstance.getOutput().getPolicyInstance()); - instances.add(instance); - } - return instances; - } - - @ApiOperation(value = "Returns a policy instance of a type") - @GetMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME + "/{" - + POLICY_INSTANCE_ID_NAME + "}") - @Secured({ DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD }) - public Object getPolicyInstance(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString, - @PathVariable(POLICY_INSTANCE_ID_NAME) String policyInstanceId) { - logger.debug("getPolicyInstance {}:{}", policyTypeIdString, policyInstanceId); - InputNRRidPTidPIidSchemaInput instanceIdInput = new InputNRRidPTidPIidSchemaInput(); - instanceIdInput.setNearRtRicId(NEAR_RT_RIC_ID); - instanceIdInput.setPolicyTypeId(Integer.decode(policyTypeIdString)); - instanceIdInput.setPolicyInstanceId(policyInstanceId); - InputNRRidPTidPIidSchema inputSchema = new InputNRRidPTidPIidSchema(); - inputSchema.setInput(instanceIdInput); - OutputPICodeSchema policyInstance = a1ControllerApi.a1ControllerGetPolicyInstance(inputSchema); - checkHttpError(policyInstance.getOutput().getCode()); - return policyInstance.getOutput().getPolicyInstance(); - } - - @ApiOperation(value = "Creates the policy instances for the given policy type.") - @PutMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME + "/{" - + POLICY_INSTANCE_ID_NAME + "}") - @Secured({ DashboardConstants.ROLE_ADMIN }) - public void putPolicyInstance(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString, - @PathVariable(POLICY_INSTANCE_ID_NAME) String policyInstanceId, @RequestBody String instance) { - logger.debug("putPolicyInstance typeId: {}, instanceId: {}, instance: {}", policyTypeIdString, - policyInstanceId, instance); - InputNRRidPTidPIidPISchemaInput createInstanceInput = new InputNRRidPTidPIidPISchemaInput(); - createInstanceInput.setNearRtRicId(NEAR_RT_RIC_ID); - createInstanceInput.setPolicyTypeId(Integer.decode(policyTypeIdString)); - createInstanceInput.setPolicyInstanceId(policyInstanceId); - createInstanceInput.setPolicyInstance(instance); - InputNRRidPTidPIidPISchema inputSchema = new InputNRRidPTidPIidPISchema(); - inputSchema.setInput(createInstanceInput); - OutputCodeSchema outputCodeSchema = a1ControllerApi.a1ControllerCreatePolicyInstance(inputSchema); - checkHttpError(outputCodeSchema.getOutput().getCode()); - } - - @ApiOperation(value = "Deletes the policy instances for the given policy type.") - @DeleteMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME + "/{" - + POLICY_INSTANCE_ID_NAME + "}") - @Secured({ DashboardConstants.ROLE_ADMIN }) - public void deletePolicyInstance(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString, - @PathVariable(POLICY_INSTANCE_ID_NAME) String policyInstanceId) { - logger.debug("deletePolicyInstance typeId: {}, instanceId: {}", policyTypeIdString, policyInstanceId); - InputNRRidPTidPIidSchemaInput instanceIdInput = new InputNRRidPTidPIidSchemaInput(); - instanceIdInput.setNearRtRicId(NEAR_RT_RIC_ID); - instanceIdInput.setPolicyTypeId(Integer.decode(policyTypeIdString)); - instanceIdInput.setPolicyInstanceId(policyInstanceId); - InputNRRidPTidPIidSchema inputSchema = new InputNRRidPTidPIidSchema(); - inputSchema.setInput(instanceIdInput); - OutputCodeSchema outputCodeSchema = a1ControllerApi.a1ControllerDeletePolicyInstance(inputSchema); - checkHttpError(outputCodeSchema.getOutput().getCode()); - } - - private void checkHttpError(String httpCode) { - logger.debug("Http Response Code: {}", httpCode); - if (httpCode.equals(String.valueOf(HttpStatus.NOT_FOUND.value()))) { - logger.error("Caught HttpNotFoundException"); - throw new HttpNotFoundException("Not Found Exception"); - } else if (httpCode.equals(String.valueOf(HttpStatus.BAD_REQUEST.value()))) { - logger.error("Caught HttpBadRequestException"); - throw new HttpBadRequestException("Bad Request Exception"); - } else if (httpCode.equals(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()))) { - logger.error("Caught HttpInternalServerErrorException"); - throw new HttpInternalServerErrorException("Internal Server Error Exception"); - } else if (httpCode.equals(String.valueOf(HttpStatus.NOT_IMPLEMENTED.value()))) { - logger.error("Caught HttpNotImplementedException"); - throw new HttpNotImplementedException("Not Implemented Exception"); - } - } -} diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java index f5ecd104..52a51fb0 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java @@ -7,9 +7,9 @@ * 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. @@ -36,7 +36,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep /** * Catches certain exceptions. This controller advice factors out try-catch * blocks in many controller methods. - * + * * Also see:
* https://www.baeldung.com/exception-handling-for-rest-with-spring * https://www.springboottutorial.com/spring-boot-exception-handling-for-rest-services @@ -44,39 +44,39 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep @ControllerAdvice public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { - // Superclass has "logger" that is exposed here, so use a different name - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + // Superclass has "logger" that is exposed here, so use a different name + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - /** - * Logs the error and generates a JSON response when a REST controller method - * takes a RestClientResponseException. This is thrown by the Http client when a - * remote method returns a non-2xx code. All the controller methods are proxies - * in that they just forward the request along to a remote system, so if that - * remote system fails, return 502 plus some details about the failure, rather - * than the generic 500 that Spring-Boot will return on an uncaught exception. - * - * Why 502? I quote:
HTTP server received an invalid response from a - * server it consulted when acting as a proxy or gateway.
- * - * @param ex - * The exception - * - * @param request - * The original request - * - * @return A response entity with status code 502 plus some details in the body. - */ - @ExceptionHandler({ RestClientResponseException.class }) - public final ResponseEntity handleProxyMethodException(Exception ex, WebRequest request) { - // Capture the full stack trace in the log. - log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false), ex); - if (ex instanceof HttpStatusCodeException) { - HttpStatusCodeException hsce = (HttpStatusCodeException) ex; - return new ResponseEntity<>(new ErrorTransport(hsce.getRawStatusCode(), hsce.getResponseBodyAsString(), - ex.toString(), request.getDescription(false)), HttpStatus.BAD_GATEWAY); - } else { - return new ResponseEntity<>(new ErrorTransport(500, ex), HttpStatus.BAD_GATEWAY); - } - } + /** + * Logs the error and generates a JSON response when a REST controller method + * takes a RestClientResponseException. This is thrown by the Http client when a + * remote method returns a non-2xx code. All the controller methods are proxies + * in that they just forward the request along to a remote system, so if that + * remote system fails, return 502 plus some details about the failure, rather + * than the generic 500 that Spring-Boot will return on an uncaught exception. + * + * Why 502? I quote:
HTTP server received an invalid response from a + * server it consulted when acting as a proxy or gateway.
+ * + * @param ex + * The exception + * + * @param request + * The original request + * + * @return A response entity with status code 502 plus some details in the body. + */ + @ExceptionHandler({RestClientResponseException.class}) + public final ResponseEntity handleProxyMethodException(Exception ex, WebRequest request) { + // Capture the full stack trace in the log. + log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false), ex); + if (ex instanceof HttpStatusCodeException) { + HttpStatusCodeException hsce = (HttpStatusCodeException) ex; + return new ResponseEntity<>(new ErrorTransport(hsce.getRawStatusCode(), hsce.getResponseBodyAsString(), + ex.toString(), request.getDescription(false)), HttpStatus.BAD_GATEWAY); + } else { + return new ResponseEntity<>(new ErrorTransport(500, ex), HttpStatus.BAD_GATEWAY); + } + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java index 7fb6e674..5d805381 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java @@ -8,9 +8,9 @@ * 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. @@ -39,30 +39,31 @@ import org.springframework.web.bind.annotation.RequestMethod; @Controller public class Html5PathsController { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - /** - * Forwards the browser to the index (main) page upon request of a known route. - * This unfortunately requires duplication of the Angular route strings in the - * path mappings on this method. Could switch to a regex pattern instead someday - * if the routes change too often. - * - * https://stackoverflow.com/questions/44692781/configure-spring-boot-to-redirect-404-to-a-single-page-app - * - * @param request - * HttpServletRequest - * @param response - * HttpServletResponse - * @throws IOException - * On error - */ - @RequestMapping(method = { RequestMethod.OPTIONS, RequestMethod.GET }, // - path = { "/policy", "/user" }) - public void forwardAngularRoutes(HttpServletRequest request, HttpServletResponse response) throws IOException { - URL url = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), "/index.html"); - if (logger.isDebugEnabled()) - logger.debug("forwardAngularRoutes: {} redirected to {}", request.getRequestURI(), url); - response.sendRedirect(url.toString()); - } + /** + * Forwards the browser to the index (main) page upon request of a known route. + * This unfortunately requires duplication of the Angular route strings in the + * path mappings on this method. Could switch to a regex pattern instead someday + * if the routes change too often. + * + * https://stackoverflow.com/questions/44692781/configure-spring-boot-to-redirect-404-to-a-single-page-app + * + * @param request + * HttpServletRequest + * @param response + * HttpServletResponse + * @throws IOException + * On error + */ + @RequestMapping( + method = {RequestMethod.OPTIONS, RequestMethod.GET}, // + path = {"/policy", "/user"}) + public void forwardAngularRoutes(HttpServletRequest request, HttpServletResponse response) throws IOException { + URL url = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), "/index.html"); + if (logger.isDebugEnabled()) + logger.debug("forwardAngularRoutes: {} redirected to {}", request.getRequestURI(), url); + response.sendRedirect(url.toString()); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/PolicyController.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/PolicyController.java new file mode 100644 index 00000000..c01c7c65 --- /dev/null +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/PolicyController.java @@ -0,0 +1,139 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ +package org.oransc.ric.portal.dashboard.controller; + +import io.swagger.annotations.ApiOperation; + +import java.lang.invoke.MethodHandles; + +import javax.servlet.http.HttpServletResponse; + +import org.oransc.ric.portal.dashboard.DashboardConstants; +import org.oransc.ric.portal.dashboard.policyagentapi.PolicyAgentApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Proxies calls from the front end to the Policy agent API. + * + * If a method throws RestClientResponseException, it is handled by + * {@link CustomResponseEntityExceptionHandler#handleProxyMethodException(Exception, org.springframework.web.context.request.WebRequest)} + * which returns status 502. All other exceptions are handled by Spring which + * returns status 500. + */ +@RestController +@RequestMapping(value = PolicyController.CONTROLLER_PATH, produces = MediaType.APPLICATION_JSON_VALUE) +public class PolicyController { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + // Publish paths in constants so tests are easy to write + public static final String CONTROLLER_PATH = DashboardConstants.ENDPOINT_PREFIX + "/policy"; + // Endpoints + public static final String VERSION_METHOD = DashboardConstants.VERSION_METHOD; + public static final String POLICY_TYPES_METHOD = "policytypes"; + public static final String POLICY_TYPE_ID_NAME = "policy_type_id"; + public static final String POLICIES_NAME = "policies"; + public static final String POLICY_INSTANCE_ID_NAME = "policy_instance_id"; + + // Populated by the autowired constructor + private final PolicyAgentApi policyAgentApi; + + @Autowired + public PolicyController(final PolicyAgentApi policyAgentApi) { + Assert.notNull(policyAgentApi, "API must not be null"); + this.policyAgentApi = policyAgentApi; + logger.debug("ctor: configured with client type {}", policyAgentApi.getClass().getName()); + } + + /* + * The fields are defined in the Policy Control Typescript interface. + */ + @ApiOperation(value = "Gets the policy types from Near Realtime-RIC") + @GetMapping(POLICY_TYPES_METHOD) + @Secured({DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD}) + public ResponseEntity getAllPolicyTypes(HttpServletResponse response) { + logger.debug("getAllPolicyTypes"); + return this.policyAgentApi.getAllPolicyTypes(); + } + + @ApiOperation(value = "Returns the policy instances for the given policy type.") + @GetMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME) + @Secured({DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD}) + public ResponseEntity getPolicyInstances(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString) { + logger.debug("getPolicyInstances {}", policyTypeIdString); + return this.policyAgentApi.getPolicyInstancesForType(policyTypeIdString); + } + + @ApiOperation(value = "Returns a policy instance of a type") + @GetMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME + "/{" + POLICY_INSTANCE_ID_NAME + + "}") + @Secured({DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD}) + public ResponseEntity getPolicyInstance(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString, + @PathVariable(POLICY_INSTANCE_ID_NAME) String policyInstanceId) { + logger.debug("getPolicyInstance {}:{}", policyTypeIdString, policyInstanceId); + return this.policyAgentApi.getPolicyInstance(policyInstanceId); + } + + @ApiOperation(value = "Creates the policy instances for the given policy type.") + @PutMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME + "/{" + POLICY_INSTANCE_ID_NAME + + "}") + @Secured({DashboardConstants.ROLE_ADMIN}) + public ResponseEntity putPolicyInstance(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString, + @RequestParam(name = "ric", required = true) String ric, + @PathVariable(POLICY_INSTANCE_ID_NAME) String policyInstanceId, @RequestBody String instance) { + logger.debug("putPolicyInstance typeId: {}, instanceId: {}, instance: {}", policyTypeIdString, policyInstanceId, + instance); + return this.policyAgentApi.putPolicy(policyTypeIdString, policyInstanceId, instance, ric); + } + + @ApiOperation(value = "Deletes the policy instances for the given policy type.") + @DeleteMapping(POLICY_TYPES_METHOD + "/{" + POLICY_TYPE_ID_NAME + "}/" + POLICIES_NAME + "/{" + + POLICY_INSTANCE_ID_NAME + "}") + @Secured({DashboardConstants.ROLE_ADMIN}) + public ResponseEntity deletePolicyInstance(@PathVariable(POLICY_TYPE_ID_NAME) String policyTypeIdString, + @PathVariable(POLICY_INSTANCE_ID_NAME) String policyInstanceId) { + logger.debug("deletePolicyInstance typeId: {}, instanceId: {}", policyTypeIdString, policyInstanceId); + return this.policyAgentApi.deletePolicy(policyInstanceId); + } + + @ApiOperation(value = "Returns the rics supporting the given policy type.") + @GetMapping("/rics") + @Secured({DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD}) + public ResponseEntity getRicsSupportingType( + @RequestParam(name = "policyType", required = true) String supportingPolicyType) { + logger.debug("getRicsSupportingType {}", supportingPolicyType); + + return this.policyAgentApi.getRicsSupportingType(supportingPolicyType); + } +} diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java index e2248e64..63b78ec1 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java @@ -7,9 +7,9 @@ * 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. @@ -35,7 +35,6 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.ServletWebRequest; - import springfox.documentation.annotations.ApiIgnore; /** @@ -43,12 +42,12 @@ import springfox.documentation.annotations.ApiIgnore; * context, including page not found, and redirects the caller to a custom error * page. The caller is also redirected to this page if a REST controller takes * an uncaught exception. - * + * * If trace is requested via request parameter ("?trace=true") and available, * adds stack trace information to the standard JSON error response. - * + * * Excluded from Swagger API documentation. - * + * * https://stackoverflow.com/questions/25356781/spring-boot-remove-whitelabel-error-page * https://www.baeldung.com/spring-boot-custom-error-page */ @@ -58,36 +57,36 @@ import springfox.documentation.annotations.ApiIgnore; @RequestMapping(value = SimpleErrorController.ERROR_PATH, produces = MediaType.APPLICATION_JSON_VALUE) public class SimpleErrorController implements ErrorController { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public static final String ERROR_PATH = "/error"; + public static final String ERROR_PATH = "/error"; - private final ErrorAttributes errorAttributes; + private final ErrorAttributes errorAttributes; - @Autowired - public SimpleErrorController(ErrorAttributes errorAttributes) { - this.errorAttributes = errorAttributes; - } + @Autowired + public SimpleErrorController(ErrorAttributes errorAttributes) { + this.errorAttributes = errorAttributes; + } - @Override - public String getErrorPath() { - logger.warn("getErrorPath"); - return ERROR_PATH; - } + @Override + public String getErrorPath() { + logger.warn("getErrorPath"); + return ERROR_PATH; + } - @GetMapping - public String handleError(HttpServletRequest request) { - ServletWebRequest servletWebRequest = new ServletWebRequest(request); - Throwable t = errorAttributes.getError(servletWebRequest); - if (t != null) - logger.warn("handleError", t); - Map attributes = errorAttributes.getErrorAttributes(servletWebRequest, true); - attributes.forEach((attribute, value) -> { - logger.warn("handleError: {} -> {}", attribute, value); - }); - // Return the name of the page INCLUDING suffix, which I guess is a "view" name. - // Just "error" is not enough, but don't seem to need a ModelAndView object. - return "error.html"; - } + @GetMapping + public String handleError(HttpServletRequest request) { + ServletWebRequest servletWebRequest = new ServletWebRequest(request); + Throwable t = errorAttributes.getError(servletWebRequest); + if (t != null) + logger.warn("handleError", t); + Map attributes = errorAttributes.getErrorAttributes(servletWebRequest, true); + attributes.forEach((attribute, value) -> { + logger.warn("handleError: {} -> {}", attribute, value); + }); + // Return the name of the page INCLUDING suffix, which I guess is a "view" name. + // Just "error" is not enough, but don't seem to need a ModelAndView object. + return "error.html"; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/k8sapi/SimpleKubernetesClient.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/k8sapi/SimpleKubernetesClient.java index bcfae626..d3add601 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/k8sapi/SimpleKubernetesClient.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/k8sapi/SimpleKubernetesClient.java @@ -34,23 +34,22 @@ import org.springframework.web.util.DefaultUriBuilderFactory; */ public class SimpleKubernetesClient { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private final String k8sUrl; - - public SimpleKubernetesClient(String baseUrl) { - logger.debug("ctor: baseUrl {}", baseUrl); - k8sUrl = baseUrl; - } - - public String listPods(String namespace) { - logger.debug("listPods for namespace {}", namespace); - String podsUrl = new DefaultUriBuilderFactory(k8sUrl.trim()).builder().pathSegment("v1") - .pathSegment("namespaces").pathSegment(namespace.trim()).pathSegment("pods").build().normalize() - .toString(); - RestTemplate rt = new RestTemplate(); - ResponseEntity podsResponse = rt.getForEntity(podsUrl, String.class); - return podsResponse.getBody(); - } + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final String k8sUrl; + + public SimpleKubernetesClient(String baseUrl) { + logger.debug("ctor: baseUrl {}", baseUrl); + k8sUrl = baseUrl; + } + + public String listPods(String namespace) { + logger.debug("listPods for namespace {}", namespace); + String podsUrl = new DefaultUriBuilderFactory(k8sUrl.trim()).builder().pathSegment("v1") + .pathSegment("namespaces").pathSegment(namespace.trim()).pathSegment("pods").build().normalize().toString(); + RestTemplate rt = new RestTemplate(); + ResponseEntity podsResponse = rt.getForEntity(podsUrl, String.class); + return podsResponse.getBody(); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java index 7bc9f004..3a082062 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java @@ -32,54 +32,54 @@ import org.springframework.security.core.userdetails.UserDetails; public class EcompUserDetails implements UserDetails { - private static final long serialVersionUID = 1L; - private final EcompUser ecompUser; + private static final long serialVersionUID = 1L; + private final EcompUser ecompUser; - // This is the default Spring role-name prefix. - private static final String ROLEP = "ROLE_"; + // This is the default Spring role-name prefix. + private static final String ROLEP = "ROLE_"; - public EcompUserDetails(EcompUser ecompUser) { - this.ecompUser = ecompUser; - } + public EcompUserDetails(EcompUser ecompUser) { + this.ecompUser = ecompUser; + } - /* - * Gets a list of authorities (roles) for this user. To keep Spring happy, every - * item has prefix ROLE_. - */ - public Collection getAuthorities() { - List roleList = new ArrayList<>(); - Iterator roleIter = ecompUser.getRoles().iterator(); - while (roleIter.hasNext()) { - EcompRole role = roleIter.next(); - // Add the prefix if the ONAP portal doesn't supply it. - final String roleName = role.getName().startsWith(ROLEP) ? role.getName() : ROLEP + role.getName(); - roleList.add(new SimpleGrantedAuthority(roleName)); - } - return roleList; - } + /* + * Gets a list of authorities (roles) for this user. To keep Spring happy, every + * item has prefix ROLE_. + */ + public Collection getAuthorities() { + List roleList = new ArrayList<>(); + Iterator roleIter = ecompUser.getRoles().iterator(); + while (roleIter.hasNext()) { + EcompRole role = roleIter.next(); + // Add the prefix if the ONAP portal doesn't supply it. + final String roleName = role.getName().startsWith(ROLEP) ? role.getName() : ROLEP + role.getName(); + roleList.add(new SimpleGrantedAuthority(roleName)); + } + return roleList; + } - public String getPassword() { - return null; - } + public String getPassword() { + return null; + } - public String getUsername() { - return ecompUser.getLoginId(); - } + public String getUsername() { + return ecompUser.getLoginId(); + } - public boolean isAccountNonExpired() { - return true; - } + public boolean isAccountNonExpired() { + return true; + } - public boolean isAccountNonLocked() { - return true; - } + public boolean isAccountNonLocked() { + return true; + } - public boolean isCredentialsNonExpired() { - return true; - } + public boolean isCredentialsNonExpired() { + return true; + } - public boolean isEnabled() { - return ecompUser.isActive(); - } + public boolean isEnabled() { + return ecompUser.isActive(); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/ErrorTransport.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/ErrorTransport.java index 516e3c8e..f1250c18 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/ErrorTransport.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/ErrorTransport.java @@ -7,9 +7,9 @@ * 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. @@ -28,106 +28,106 @@ import java.time.Instant; */ public class ErrorTransport implements IDashboardResponse { - private Instant timestamp; - private Integer status; - private String error; - private String message; - private String path; - - /** - * Builds an empty object. - */ - public ErrorTransport() { - // no-arg constructor - } - - /** - * Convenience constructor for minimal value set. - * - * @param status - * Integer value like 400 - * @param error - * Error message - */ - public ErrorTransport(int status, String error) { - this(status, error, null, null); - } - - /** - * Convenience constructor for populating an error from an exception - * - * @param status - * Integer value like 400 - * @param throwable - * The caught exception/throwable to convert to String with - * an upper bound on characters - */ - public ErrorTransport(int status, Throwable throwable) { - this.timestamp = Instant.now(); - this.status = status; - final int enough = 256; - String exString = throwable.toString(); - this.error = exString.length() > enough ? exString.substring(0, enough) : exString; - } - - /** - * Builds an object with all fields - * - * @param status - * Integer value like 500 - * @param error - * Explanation - * @param message - * Additional explanation - * @param path - * Requested path - */ - public ErrorTransport(int status, String error, String message, String path) { - this.timestamp = Instant.now(); - this.status = status; - this.error = error; - this.message = message; - this.path = path; - } - - public Integer getStatus() { - return status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - public String getMessage() { - return message; - } - - public void setMessage(String error) { - this.message = error; - } - - public Instant getTimestamp() { - return timestamp; - } - - public void setTimestamp(Instant timestamp) { - this.timestamp = timestamp; - } - - public String getError() { - return error; - } - - public void setError(String error) { - this.error = error; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } + private Instant timestamp; + private Integer status; + private String error; + private String message; + private String path; + + /** + * Builds an empty object. + */ + public ErrorTransport() { + // no-arg constructor + } + + /** + * Convenience constructor for minimal value set. + * + * @param status + * Integer value like 400 + * @param error + * Error message + */ + public ErrorTransport(int status, String error) { + this(status, error, null, null); + } + + /** + * Convenience constructor for populating an error from an exception + * + * @param status + * Integer value like 400 + * @param throwable + * The caught exception/throwable to convert to String with + * an upper bound on characters + */ + public ErrorTransport(int status, Throwable throwable) { + this.timestamp = Instant.now(); + this.status = status; + final int enough = 256; + String exString = throwable.toString(); + this.error = exString.length() > enough ? exString.substring(0, enough) : exString; + } + + /** + * Builds an object with all fields + * + * @param status + * Integer value like 500 + * @param error + * Explanation + * @param message + * Additional explanation + * @param path + * Requested path + */ + public ErrorTransport(int status, String error, String message, String path) { + this.timestamp = Instant.now(); + this.status = status; + this.error = error; + this.message = message; + this.path = path; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String error) { + this.message = error; + } + + public Instant getTimestamp() { + return timestamp; + } + + public void setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstance.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInfo.java similarity index 56% rename from dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstance.java rename to dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInfo.java index b699b3c1..30cfd2aa 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstance.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInfo.java @@ -19,30 +19,23 @@ */ package org.oransc.ric.portal.dashboard.model; -public class PolicyInstance implements IDashboardResponse { - private String instanceId; - private Object instance; - - public PolicyInstance(String id, Object instance) { - this.instanceId = id; - this.instance = instance; - } - - public String getInstanceId() { - return instanceId; - } - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } - public Object getInstance() { - return instance; - } - public void setInstance(Object instance) { - this.instance = instance; - } - - @Override - public String toString() { - return PolicyInstance.class.getName() + ": [id:" + instanceId + ", instance: " + instance + "]"; - } +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface PolicyInfo { + + public String id(); + + public String type(); + + public String ric(); + + public String json(); + + public String service(); + + public String lastModified(); + } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstances.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstances.java index c7504874..c6faf9b1 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstances.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstances.java @@ -21,8 +21,8 @@ package org.oransc.ric.portal.dashboard.model; import java.util.ArrayList; -public class PolicyInstances extends ArrayList { +public class PolicyInstances extends ArrayList { - private static final long serialVersionUID = -928428052502491021L; + private static final long serialVersionUID = -928428052502491021L; } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyType.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyType.java index efe40107..f0ca285b 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyType.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyType.java @@ -23,47 +23,35 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class PolicyType { - @JsonProperty("policy_type_id") - Integer policyTypeId; + @JsonProperty("name") + String name; - @JsonProperty("name") - String name; + @JsonProperty("schema") + String schema; - @JsonProperty("schema") - String schema; + public PolicyType(String name, String schema) { + this.name = name; + this.schema = schema; + } - public PolicyType(Integer policyId, String name, String schema) { - this.policyTypeId = policyId; - this.name = name; - this.schema = schema; - } + public String getName() { + return name; + } - public Integer getPolicyTypeId() { - return policyTypeId; - } + public void setName(String name) { + this.name = name; + } - public void setPolicyTypeId(Integer policyTypeId) { - this.policyTypeId = policyTypeId; - } + public String getSchema() { + return schema; + } - public String getName() { - return name; - } + public void setSchema(String schema) { + this.schema = schema; + } - public void setName(String name) { - this.name = name; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - @Override - public String toString() { - return "[policy_type_id:" + policyTypeId + ", name:" + name + ", schema:" + schema + "]"; - } + @Override + public String toString() { + return "[name:" + name + ", schema:" + schema + "]"; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyTypes.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyTypes.java index 3165d1bc..43a6383e 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyTypes.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyTypes.java @@ -23,6 +23,6 @@ import java.util.ArrayList; public class PolicyTypes extends ArrayList { - private static final long serialVersionUID = -928428052502491021L; + private static final long serialVersionUID = -928428052502491021L; } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/SuccessTransport.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/SuccessTransport.java index 9e137890..65b39eb4 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/SuccessTransport.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/SuccessTransport.java @@ -7,9 +7,9 @@ * 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. @@ -21,43 +21,43 @@ package org.oransc.ric.portal.dashboard.model; public class SuccessTransport implements IDashboardResponse { - private int status; - private Object data; - - /** - * Builds an empty object - */ - public SuccessTransport() { - // no-arg constructor - } - - /** - * Builds an object with the specified values. - * - * @param status - * Status code - * @param data - * Data to transport - */ - public SuccessTransport(int status, Object data) { - this.status = status; - this.data = data; - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - public Object getData() { - return data; - } - - public void setData(Object data) { - this.data = data; - } + private int status; + private Object data; + + /** + * Builds an empty object + */ + public SuccessTransport() { + // no-arg constructor + } + + /** + * Builds an object with the specified values. + * + * @param status + * Status code + * @param data + * Data to transport + */ + public SuccessTransport(int status, Object data) { + this.status = status; + this.data = data; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java new file mode 100644 index 00000000..ff254d22 --- /dev/null +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java @@ -0,0 +1,39 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 AT&T Intellectual Property + * %% + * 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. + * ========================LICENSE_END=================================== + */ +package org.oransc.ric.portal.dashboard.policyagentapi; + +import org.springframework.http.ResponseEntity; + +public interface PolicyAgentApi { + + public ResponseEntity getAllPolicyTypes(); + + public ResponseEntity getPolicyInstancesForType(String type); + + public ResponseEntity getPolicyInstance(String id); + + public ResponseEntity putPolicy(String policyTypeIdString, String policyInstanceId, String json, + String ric); + + public ResponseEntity deletePolicy(String policyInstanceId); + + public ResponseEntity getRicsSupportingType(String typeName); + +} 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 new file mode 100644 index 00000000..c0dde9bd --- /dev/null +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java @@ -0,0 +1,208 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 AT&T Intellectual Property + * %% + * 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. + * ========================LICENSE_END=================================== + */ +package org.oransc.ric.portal.dashboard.policyagentapi; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; +import org.oransc.ric.portal.dashboard.model.ImmutablePolicyInfo; +import org.oransc.ric.portal.dashboard.model.PolicyInfo; +import org.oransc.ric.portal.dashboard.model.PolicyInstances; +import org.oransc.ric.portal.dashboard.model.PolicyType; +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; + +@Component("PolicyAgentApi") +public class PolicyAgentApiImpl implements PolicyAgentApi { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + RestTemplate restTemplate = new RestTemplate(); + + private static com.google.gson.Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + private final String urlPrefix; + + @Autowired + public PolicyAgentApiImpl( + @org.springframework.beans.factory.annotation.Value("${policycontroller.url.prefix}") final String urlPrefix) { + logger.debug("ctor prefix '{}'", urlPrefix); + this.urlPrefix = urlPrefix; + } + + private String baseUrl() { + return urlPrefix; + } + + @Value.Immutable + @Gson.TypeAdapters + interface PolicyTypeInfo { + + public String name(); + + public String schema(); + } + + @Override + public ResponseEntity getAllPolicyTypes() { + 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(); + String title = schemaObj.get("title").getAsString(); + String schemaAsStr = schemaObj.toString(); + PolicyType pt = new PolicyType(title, schemaAsStr); + result.add(pt); + } + return new ResponseEntity<>(gson.toJson(result), rsp.getStatusCode()); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity getPolicyInstancesForType(String type) { + String url = baseUrl() + "/policies?type={type}"; + Map uriVariables = Map.of("type", type); + ResponseEntity rsp = this.restTemplate.getForEntity(url, String.class, uriVariables); + if (!rsp.getStatusCode().is2xxSuccessful()) { + return rsp; + } + + try { + Type listType = new TypeToken>() {}.getType(); + List rspParsed = gson.fromJson(rsp.getBody(), listType); + PolicyInstances result = new PolicyInstances(); + for (PolicyInfo p : rspParsed) { + result.add(p); + } + return new ResponseEntity<>(gson.toJson(result), rsp.getStatusCode()); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity getPolicyInstance(String id) { + String url = baseUrl() + "/policy?instance={id}"; + Map uriVariables = Map.of("id", id); + + return this.restTemplate.getForEntity(url, String.class, uriVariables); + } + + @Override + public ResponseEntity putPolicy(String policyTypeIdString, String policyInstanceId, String json, + 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"); + + try { + this.restTemplate.put(url, createJsonHttpEntity(json), uriVariables); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + public ResponseEntity deletePolicy(String policyInstanceId) { + String url = baseUrl() + "/policy?instance={instance}"; + Map uriVariables = Map.of("instance", policyInstanceId); + try { + this.restTemplate.delete(url, uriVariables); + return new ResponseEntity<>(HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); + } + + } + + @Value.Immutable + @Gson.TypeAdapters + interface RicInfo { + public String name(); + + public Collection nodeNames(); + + public Collection policyTypes(); + } + + @Override + public ResponseEntity getRicsSupportingType(String typeName) { + String url = baseUrl() + "/rics?policyType={typeName}"; + Map uriVariables = Map.of("typeName", typeName); + String rsp = this.restTemplate.getForObject(url, String.class, uriVariables); + + try { + Type listType = new TypeToken>() {}.getType(); + List rspParsed = gson.fromJson(rsp, listType); + Collection result = new Vector<>(rspParsed.size()); + for (RicInfo ric : rspParsed) { + result.add(ric.name()); + } + return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + 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/main/java/org/oransc/ric/portal/dashboard/portalapi/IPortalSdkDecryptor.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/IPortalSdkDecryptor.java index 34d80c94..552d7836 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/IPortalSdkDecryptor.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/IPortalSdkDecryptor.java @@ -7,9 +7,9 @@ * 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. @@ -27,15 +27,15 @@ import org.onap.portalsdk.core.onboarding.exception.CipherUtilException; */ public interface IPortalSdkDecryptor { - /** - * Decrypts the specified value using a known key. - * - * @param cipherText - * Encrypted value - * @return Clear text on success, null otherwise. - * @throws CipherUtilException - * if any decryption step fails - */ - String decrypt(String cipherText) throws CipherUtilException; + /** + * Decrypts the specified value using a known key. + * + * @param cipherText + * Encrypted value + * @return Clear text on success, null otherwise. + * @throws CipherUtilException + * if any decryption step fails + */ + String decrypt(String cipherText) throws CipherUtilException; } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java index dc70f7e7..f4daa5c2 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java @@ -7,9 +7,9 @@ * 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. @@ -38,83 +38,83 @@ import org.slf4j.LoggerFactory; */ public class PortalAuthManager { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - final Map credentialsMap; - private final IPortalSdkDecryptor portalSdkDecryptor; - private final String userIdCookieName; + final Map credentialsMap; + private final IPortalSdkDecryptor portalSdkDecryptor; + private final String userIdCookieName; - public PortalAuthManager(final String appName, final String username, final String password, - final String decryptorClassName, final String userCookie) - throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, - InvocationTargetException, NoSuchMethodException, SecurityException { - credentialsMap = new HashMap<>(); - credentialsMap.put(IPortalRestCentralService.CREDENTIALS_APP, appName); - credentialsMap.put(IPortalRestCentralService.CREDENTIALS_USER, username); - credentialsMap.put(IPortalRestCentralService.CREDENTIALS_PASS, password); - this.userIdCookieName = userCookie; - // Instantiate here so configuration errors are detected at app-start time - logger.debug("ctor: using decryptor class {}", decryptorClassName); - Class decryptorClass = Class.forName(decryptorClassName); - portalSdkDecryptor = (IPortalSdkDecryptor) decryptorClass.getDeclaredConstructor().newInstance(); - } + public PortalAuthManager(final String appName, final String username, final String password, + final String decryptorClassName, final String userCookie) + throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException, SecurityException { + credentialsMap = new HashMap<>(); + credentialsMap.put(IPortalRestCentralService.CREDENTIALS_APP, appName); + credentialsMap.put(IPortalRestCentralService.CREDENTIALS_USER, username); + credentialsMap.put(IPortalRestCentralService.CREDENTIALS_PASS, password); + this.userIdCookieName = userCookie; + // Instantiate here so configuration errors are detected at app-start time + logger.debug("ctor: using decryptor class {}", decryptorClassName); + Class decryptorClass = Class.forName(decryptorClassName); + portalSdkDecryptor = (IPortalSdkDecryptor) decryptorClass.getDeclaredConstructor().newInstance(); + } - /** - * @return A map of key-value pairs with application name, user name and - * password. - */ - public Map getAppCredentials() { - return credentialsMap; - } + /** + * @return A map of key-value pairs with application name, user name and + * password. + */ + public Map getAppCredentials() { + return credentialsMap; + } - /** - * Searches the request for a cookie with the specified name. - * - * @param request - * HttpServletRequest - * @param cookieName - * Cookie name - * @return Cookie, or null if not found. - */ - private Cookie getCookie(HttpServletRequest request, String cookieName) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) - for (Cookie cookie : cookies) - if (cookie.getName().equals(cookieName)) - return cookie; - return null; - } + /** + * Searches the request for a cookie with the specified name. + * + * @param request + * HttpServletRequest + * @param cookieName + * Cookie name + * @return Cookie, or null if not found. + */ + private Cookie getCookie(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) + for (Cookie cookie : cookies) + if (cookie.getName().equals(cookieName)) + return cookie; + return null; + } - /** - * Validates whether the ECOMP Portal sign-on process has completed. Checks for - * the ECOMP cookie first, then the user cookie. - * - * @param request - * HttpServletRequest - * @return User ID if the ECOMP cookie is present and the sign-on process - * established a user ID; else null. - */ - public String validateEcompSso(HttpServletRequest request) { - // Check ECOMP Portal cookie - Cookie ep = getCookie(request, PortalApiConstants.EP_SERVICE); - if (ep == null) { - logger.debug("validateEcompSso: cookie not found: {}", PortalApiConstants.EP_SERVICE); - return null; - } - logger.trace("validateEcompSso: found cookie {}", PortalApiConstants.EP_SERVICE); - Cookie user = getCookie(request, userIdCookieName); - if (user == null) { - logger.debug("validateEcompSso: cookie not found: {}", userIdCookieName); - return null; - } - logger.trace("validateEcompSso: user cookie {}", userIdCookieName); - String userid = null; - try { - userid = portalSdkDecryptor.decrypt(user.getValue()); - } catch (CipherUtilException e) { - throw new IllegalArgumentException("validateEcompSso failed", e); - } - return userid; - } + /** + * Validates whether the ECOMP Portal sign-on process has completed. Checks for + * the ECOMP cookie first, then the user cookie. + * + * @param request + * HttpServletRequest + * @return User ID if the ECOMP cookie is present and the sign-on process + * established a user ID; else null. + */ + public String validateEcompSso(HttpServletRequest request) { + // Check ECOMP Portal cookie + Cookie ep = getCookie(request, PortalApiConstants.EP_SERVICE); + if (ep == null) { + logger.debug("validateEcompSso: cookie not found: {}", PortalApiConstants.EP_SERVICE); + return null; + } + logger.trace("validateEcompSso: found cookie {}", PortalApiConstants.EP_SERVICE); + Cookie user = getCookie(request, userIdCookieName); + if (user == null) { + logger.debug("validateEcompSso: cookie not found: {}", userIdCookieName); + return null; + } + logger.trace("validateEcompSso: user cookie {}", userIdCookieName); + String userid = null; + try { + userid = portalSdkDecryptor.decrypt(user.getValue()); + } catch (CipherUtilException e) { + throw new IllegalArgumentException("validateEcompSso failed", e); + } + return userid; + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java index 4b6de914..711761a4 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java @@ -7,9 +7,9 @@ * 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. @@ -71,211 +71,211 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA * Portal knows where to forward the request to once the Portal Session is * created and EPService cookie is set. * - * + * * TODO: What about sessions? Will this be stateless? - * + * * This filter uses no annotations to avoid Spring's automatic registration, * which add this filter in the chain in the wrong order. */ public class PortalAuthenticationFilter implements Filter { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - // Unfortunately not all file names are defined as constants - private static final String[] securityPropertyFiles = { KeyProperties.PROPERTY_FILE_NAME, - PortalApiProperties.PROPERTY_FILE_NAME, DefaultSecurityConfiguration.DEFAULT_RESOURCE_FILE, - "validation.properties" }; + // Unfortunately not all file names are defined as constants + private static final String[] securityPropertyFiles = + {KeyProperties.PROPERTY_FILE_NAME, PortalApiProperties.PROPERTY_FILE_NAME, + DefaultSecurityConfiguration.DEFAULT_RESOURCE_FILE, "validation.properties"}; - public static final String REDIRECT_URL_KEY = "redirectUrl"; + public static final String REDIRECT_URL_KEY = "redirectUrl"; - private final boolean enforcePortalSecurity; - private final PortalAuthManager authManager; + private final boolean enforcePortalSecurity; + private final PortalAuthManager authManager; - private final DashboardUserManager userManager; + private final DashboardUserManager userManager; - public PortalAuthenticationFilter(boolean portalSecurity, PortalAuthManager authManager, - DashboardUserManager userManager) { - this.enforcePortalSecurity = portalSecurity; - this.authManager = authManager; - this.userManager = userManager; - if (portalSecurity) { - // Throw if security is requested and prerequisites are not met - for (String pf : securityPropertyFiles) { - InputStream in = MethodHandles.lookup().lookupClass().getClassLoader().getResourceAsStream(pf); - if (in == null) { - String msg = "Failed to find property file on classpath: " + pf; - logger.error(msg); - throw new RuntimeException(msg); - } else { - try { - in.close(); - } catch (IOException ex) { - logger.warn("Failed to close stream", ex); - } - } - } - } - } + public PortalAuthenticationFilter(boolean portalSecurity, PortalAuthManager authManager, + DashboardUserManager userManager) { + this.enforcePortalSecurity = portalSecurity; + this.authManager = authManager; + this.userManager = userManager; + if (portalSecurity) { + // Throw if security is requested and prerequisites are not met + for (String pf : securityPropertyFiles) { + InputStream in = MethodHandles.lookup().lookupClass().getClassLoader().getResourceAsStream(pf); + if (in == null) { + String msg = "Failed to find property file on classpath: " + pf; + logger.error(msg); + throw new RuntimeException(msg); + } else { + try { + in.close(); + } catch (IOException ex) { + logger.warn("Failed to close stream", ex); + } + } + } + } + } - @Override - public void init(FilterConfig filterConfig) throws ServletException { - // complain loudly if this key property is missing - String url = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); - logger.debug("init: Portal redirect URL {}", url); - if (url == null) - logger.error( - "init: Failed to find property in portal.properties: " + PortalApiConstants.ECOMP_REDIRECT_URL); - } + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // complain loudly if this key property is missing + String url = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); + logger.debug("init: Portal redirect URL {}", url); + if (url == null) + logger + .error("init: Failed to find property in portal.properties: " + PortalApiConstants.ECOMP_REDIRECT_URL); + } - @Override - public void destroy() { - // No resources to release - } + @Override + public void destroy() { + // No resources to release + } - /** - * Requests for pages ignored in the web security config do not hit this filter. - */ - @Override - public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) - throws IOException, ServletException { - if (enforcePortalSecurity) - doFilterEPSDKFW(req, res, chain); - else - doFilterMockUserAdminRole(req, res, chain); - } + /** + * Requests for pages ignored in the web security config do not hit this filter. + */ + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + if (enforcePortalSecurity) + doFilterEPSDKFW(req, res, chain); + else + doFilterMockUserAdminRole(req, res, chain); + } - /* - * Populates security context with a mock user in the admin role. - * - */ - private void doFilterMockUserAdminRole(ServletRequest req, ServletResponse res, FilterChain chain) - throws IOException, ServletException { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth == null || auth.getAuthorities().isEmpty()) { - if (logger.isDebugEnabled()) { - logger.debug("doFilter adding auth to request URI {}", - (req instanceof HttpServletRequest) ? ((HttpServletRequest) req).getRequestURL() : req); - } - EcompRole admin = new EcompRole(); - admin.setId(1L); - admin.setName(DashboardConstants.ROLE_ADMIN); - HashSet roles = new HashSet<>(); - roles.add(admin); - EcompUser user = new EcompUser(); - user.setLoginId("fakeLoginId"); - user.setRoles(roles); - user.setActive(true); - EcompUserDetails userDetails = new EcompUserDetails(user); - PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails, - "fakeCredentials", userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authToken); - } else { - logger.debug("doFilter: authorities {}", auth.getAuthorities()); - } - chain.doFilter(req, res); - } + /* + * Populates security context with a mock user in the admin role. + * + */ + private void doFilterMockUserAdminRole(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null || auth.getAuthorities().isEmpty()) { + if (logger.isDebugEnabled()) { + logger.debug("doFilter adding auth to request URI {}", + (req instanceof HttpServletRequest) ? ((HttpServletRequest) req).getRequestURL() : req); + } + EcompRole admin = new EcompRole(); + admin.setId(1L); + admin.setName(DashboardConstants.ROLE_ADMIN); + HashSet roles = new HashSet<>(); + roles.add(admin); + EcompUser user = new EcompUser(); + user.setLoginId("fakeLoginId"); + user.setRoles(roles); + user.setActive(true); + EcompUserDetails userDetails = new EcompUserDetails(user); + PreAuthenticatedAuthenticationToken authToken = + new PreAuthenticatedAuthenticationToken(userDetails, "fakeCredentials", userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + } else { + logger.debug("doFilter: authorities {}", auth.getAuthorities()); + } + chain.doFilter(req, res); + } - /* - * Checks for valid cookies and allows request to be served if found; redirects - * to Portal otherwise. - */ - private void doFilterEPSDKFW(ServletRequest req, ServletResponse res, FilterChain chain) - throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) req; - HttpServletResponse response = (HttpServletResponse) res; - if (logger.isTraceEnabled()) - logger.trace("doFilter: req {}", request.getRequestURI()); - // Need to authenticate the request - final String userId = authManager.validateEcompSso(request); - final EcompUser ecompUser = (userId == null ? null : userManager.getUser(userId)); - if (userId == null || ecompUser == null) { - logger.debug("doFilter: unauthorized user requests URI {}, serving login page", request.getRequestURI()); - StringBuffer sb = request.getRequestURL(); - sb.append(request.getQueryString() == null ? "" : "?" + request.getQueryString()); - String body = generateLoginRedirectPage(sb.toString()); - response.setContentType(MediaType.TEXT_HTML_VALUE); - response.getWriter().print(body); - response.getWriter().flush(); - } else { - EcompUserDetails userDetails = new EcompUserDetails(ecompUser); - // Using portal session as credentials is a hack - PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails, - getPortalSessionId(request), userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authToken); - // Pass request back down the filter chain - chain.doFilter(request, response); - } - } + /* + * Checks for valid cookies and allows request to be served if found; redirects + * to Portal otherwise. + */ + private void doFilterEPSDKFW(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + if (logger.isTraceEnabled()) + logger.trace("doFilter: req {}", request.getRequestURI()); + // Need to authenticate the request + final String userId = authManager.validateEcompSso(request); + final EcompUser ecompUser = (userId == null ? null : userManager.getUser(userId)); + if (userId == null || ecompUser == null) { + logger.debug("doFilter: unauthorized user requests URI {}, serving login page", request.getRequestURI()); + StringBuffer sb = request.getRequestURL(); + sb.append(request.getQueryString() == null ? "" : "?" + request.getQueryString()); + String body = generateLoginRedirectPage(sb.toString()); + response.setContentType(MediaType.TEXT_HTML_VALUE); + response.getWriter().print(body); + response.getWriter().flush(); + } else { + EcompUserDetails userDetails = new EcompUserDetails(ecompUser); + // Using portal session as credentials is a hack + PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(userDetails, + getPortalSessionId(request), userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + // Pass request back down the filter chain + chain.doFilter(request, response); + } + } - /** - * Generates a page with text only, absolutely no references to any webapp - * resources, so this can be served to an unauthenticated user without - * triggering a new authentication attempt. The page has a link to the Portal - * URL from configuration, with a return URL that is the original request. - * - * @param appUrl - * Original requested URL - * @return HTML - * @throws UnsupportedEncodingException - * On error - */ - private static String generateLoginRedirectPage(String appUrl) throws UnsupportedEncodingException { - String encodedAppUrl = URLEncoder.encode(appUrl, "UTF-8"); - String portalBaseUrl = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); - String redirectUrl = portalBaseUrl + "?" + PortalAuthenticationFilter.REDIRECT_URL_KEY + "=" + encodedAppUrl; - String aHref = ""; - // If only Java had "here" documents. - String body = String.join(// - System.getProperty("line.separator"), // - "", // - "", // - "RIC Dashboard", // - "", // - "", // - "", // - "

RIC Dashboard

", // - "

Please log in.

", // - "

", // - aHref, "Click here to authenticate at the ONAP Portal", // - "

", // - "", // - ""); - return body; - } + /** + * Generates a page with text only, absolutely no references to any webapp + * resources, so this can be served to an unauthenticated user without + * triggering a new authentication attempt. The page has a link to the Portal + * URL from configuration, with a return URL that is the original request. + * + * @param appUrl + * Original requested URL + * @return HTML + * @throws UnsupportedEncodingException + * On error + */ + private static String generateLoginRedirectPage(String appUrl) throws UnsupportedEncodingException { + String encodedAppUrl = URLEncoder.encode(appUrl, "UTF-8"); + String portalBaseUrl = PortalApiProperties.getProperty(PortalApiConstants.ECOMP_REDIRECT_URL); + String redirectUrl = portalBaseUrl + "?" + PortalAuthenticationFilter.REDIRECT_URL_KEY + "=" + encodedAppUrl; + String aHref = ""; + // If only Java had "here" documents. + String body = String.join(// + System.getProperty("line.separator"), // + "", // + "", // + "RIC Dashboard", // + "", // + "", // + "", // + "

RIC Dashboard

", // + "

Please log in.

", // + "

", // + aHref, "Click here to authenticate at the ONAP Portal", // + "

", // + "", // + ""); + return body; + } - /** - * Searches the request for a cookie with the specified name. - * - * @param request - * HttpServletRequest - * @param cookieName - * Cookie name - * @return Cookie, or null if not found. - */ - private Cookie getCookie(HttpServletRequest request, String cookieName) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) - for (Cookie cookie : cookies) - if (cookie.getName().equals(cookieName)) - return cookie; - return null; - } + /** + * Searches the request for a cookie with the specified name. + * + * @param request + * HttpServletRequest + * @param cookieName + * Cookie name + * @return Cookie, or null if not found. + */ + private Cookie getCookie(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) + for (Cookie cookie : cookies) + if (cookie.getName().equals(cookieName)) + return cookie; + return null; + } - /** - * Gets the ECOMP Portal service cookie value. - * - * @param request - * @return Cookie value, or null if not found. - */ - private String getPortalSessionId(HttpServletRequest request) { - Cookie ep = getCookie(request, PortalApiConstants.EP_SERVICE); - if (ep == null) - return null; - return ep.getValue(); - } + /** + * Gets the ECOMP Portal service cookie value. + * + * @param request + * @return Cookie value, or null if not found. + */ + private String getPortalSessionId(HttpServletRequest request) { + Cookie ep = getCookie(request, PortalApiConstants.EP_SERVICE); + if (ep == null) + return null; + return ep.getValue(); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java index 581ca25d..a2fae9f0 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java @@ -7,9 +7,9 @@ * 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. @@ -38,52 +38,52 @@ import org.springframework.context.ApplicationContext; * Implements the contract used by the Portal to transmit user details to this * on-boarded application. The requests are intercepted first by a servlet in * the EPSDK-FW library, which proxies the calls to these methods. - * + * * An instance of this class is created upon first request to the API. But this * class is found and instantiated via Class.forName(), so cannot use Spring * annotations. */ public class PortalRestCentralServiceImpl implements IPortalRestCentralService { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final PortalAuthManager authManager; - private final DashboardUserManager userManager; + private final PortalAuthManager authManager; + private final DashboardUserManager userManager; - public PortalRestCentralServiceImpl() throws IOException, PortalAPIException { - final ApplicationContext context = SpringContextCache.getApplicationContext(); - authManager = (PortalAuthManager) context.getBean(PortalAuthManager.class); - userManager = (DashboardUserManager) context.getBean(DashboardUserManager.class); - } + public PortalRestCentralServiceImpl() throws IOException, PortalAPIException { + final ApplicationContext context = SpringContextCache.getApplicationContext(); + authManager = context.getBean(PortalAuthManager.class); + userManager = context.getBean(DashboardUserManager.class); + } - /* - * Answers the Portal API credentials. - */ - @Override - public Map getAppCredentials() throws PortalAPIException { - logger.debug("getAppCredentials"); - return authManager.getAppCredentials(); - } + /* + * Answers the Portal API credentials. + */ + @Override + public Map getAppCredentials() throws PortalAPIException { + logger.debug("getAppCredentials"); + return authManager.getAppCredentials(); + } - /* - * Extracts the user ID from a cookie in the header - */ - @Override - public String getUserId(HttpServletRequest request) throws PortalAPIException { - logger.debug("getuserId"); - return authManager.validateEcompSso(request); - } + /* + * Extracts the user ID from a cookie in the header + */ + @Override + public String getUserId(HttpServletRequest request) throws PortalAPIException { + logger.debug("getuserId"); + return authManager.validateEcompSso(request); + } - @Override - public void pushUser(EcompUser user) throws PortalAPIException { - logger.debug("pushUser: {}", user); - userManager.createUser(user); - } + @Override + public void pushUser(EcompUser user) throws PortalAPIException { + logger.debug("pushUser: {}", user); + userManager.createUser(user); + } - @Override - public void editUser(String loginId, EcompUser user) throws PortalAPIException { - logger.debug("editUser: {}", user); - userManager.updateUser(loginId, user); - } + @Override + public void editUser(String loginId, EcompUser user) throws PortalAPIException { + logger.debug("editUser: {}", user); + userManager.updateUser(loginId, user); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java index 279f1dd8..f62ea5f6 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java @@ -24,9 +24,9 @@ import org.onap.portalsdk.core.onboarding.util.CipherUtil; public class PortalSdkDecryptorAes implements IPortalSdkDecryptor { - @SuppressWarnings("deprecation") - public String decrypt(String cipherText) throws CipherUtilException { - return CipherUtil.decrypt(cipherText); - } + @SuppressWarnings("deprecation") + public String decrypt(String cipherText) throws CipherUtilException { + return CipherUtil.decrypt(cipherText); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorPkc.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorPkc.java index 0127267f..10205277 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorPkc.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorPkc.java @@ -24,8 +24,8 @@ import org.onap.portalsdk.core.onboarding.util.CipherUtil; public class PortalSdkDecryptorPkc implements IPortalSdkDecryptor { - public String decrypt(String cipherText) throws CipherUtilException { - return CipherUtil.decryptPKC(cipherText); - } + public String decrypt(String cipherText) throws CipherUtilException { + return CipherUtil.decryptPKC(cipherText); + } } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java index a97ed7b4..d5f2cbfb 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java @@ -7,9 +7,9 @@ * 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. @@ -35,48 +35,52 @@ import javax.net.ssl.X509TrustManager; * Disables and enables certificate and host-name checking in * HttpsURLConnection, the default JVM implementation of the HTTPS/TLS protocol. * Has no effect on implementations such as Apache Http Client, Ok Http. - * + * * https://stackoverflow.com/questions/23504819/how-to-disable-ssl-certificate-checking-with-spring-resttemplate/58291331#58291331 */ public final class HttpsURLConnectionUtils { - private static final HostnameVerifier jvmHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); + private static final HostnameVerifier jvmHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); - private static final HostnameVerifier trivialHostnameVerifier = new HostnameVerifier() { - public boolean verify(String hostname, SSLSession sslSession) { - return true; - } - }; + private static final HostnameVerifier trivialHostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession sslSession) { + return true; + } + }; - private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[] { new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } + private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[] {new X509TrustManager() { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } }; + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + }}; - public static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException { - HttpsURLConnection.setDefaultHostnameVerifier(trivialHostnameVerifier); - // Install the all-trusting trust manager - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, UNQUESTIONING_TRUST_MANAGER, null); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - } + public static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException { + HttpsURLConnection.setDefaultHostnameVerifier(trivialHostnameVerifier); + // Install the all-trusting trust manager + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, UNQUESTIONING_TRUST_MANAGER, null); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } - public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException { - HttpsURLConnection.setDefaultHostnameVerifier(jvmHostnameVerifier); - // Return it to the initial state (discovered by reflection, now hardcoded) - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, null, null); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - } + public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException { + HttpsURLConnection.setDefaultHostnameVerifier(jvmHostnameVerifier); + // Return it to the initial state (discovered by reflection, now hardcoded) + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, null, null); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } - private HttpsURLConnectionUtils() { - throw new UnsupportedOperationException("Do not instantiate libraries."); - } + private HttpsURLConnectionUtils() { + throw new UnsupportedOperationException("Do not instantiate libraries."); + } } diff --git a/dashboard/webapp-backend/src/main/resources/application.properties b/dashboard/webapp-backend/src/main/resources/application.properties index 3aa2406f..cfb1f8cd 100644 --- a/dashboard/webapp-backend/src/main/resources/application.properties +++ b/dashboard/webapp-backend/src/main/resources/application.properties @@ -42,11 +42,10 @@ portalapi.username = portalapi.password = # endpoint URLs must be supplied at deployment time -# NOTE: change a1controller.url.prefix to http://localhost:8282 when running +# NOTE: change policycontroller.url.prefix to http://localhost:8081 when running # dashboard locally (i.e., not inside the docker container) -# A1 Controller -a1controller.url.prefix = http://a1-controller-container:8181 -a1controller.url.suffix = /restconf/operations +policycontroller.url.prefix = http://policy-agent-container:8081 + # Kubernetes API via https://github.com/nokia/caas-ingress # Set insecure=true to disable SSL certificate and hostname checking diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java index df1a51c8..e19890be 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java @@ -19,7 +19,10 @@ */ package org.oransc.ric.portal.dashboard; +import static org.junit.jupiter.api.Assertions.assertEquals; + import java.lang.invoke.MethodHandles; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,7 +30,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; /** @@ -45,25 +47,25 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; */ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) -@ActiveProfiles("test") public class DashboardTestServer { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - /* - * Keeps the test server alive forever. Use a guard so this test is never run by - * Jenkins. - */ - @EnabledIfSystemProperty(named = "org.oransc.ric.portal.dashboard", matches = "mock") - @Test - public void keepServerAlive() { - logger.warn("Keeping server alive!"); - try { - synchronized (this) { - this.wait(); - } - } catch (Exception ex) { - logger.warn(ex.toString()); - } - } + /* + * Keeps the test server alive forever. Use a guard so this test is never run by + * Jenkins. + */ + @EnabledIfSystemProperty(named = "org.oransc.ric.portal.dashboard", matches = "mock") + @Test + public void keepServerAlive() { + logger.warn("Keeping server alive!"); + try { + synchronized (this) { + this.wait(); + } + } catch (Exception ex) { + logger.warn(ex.toString()); + } + assertEquals(1, 2); + } } diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/A1ControllerMockConfiguration.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/A1ControllerMockConfiguration.java deleted file mode 100644 index 5706a913..00000000 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/A1ControllerMockConfiguration.java +++ /dev/null @@ -1,333 +0,0 @@ -/*- - * ========================LICENSE_START================================= - * O-RAN-SC - * %% - * Copyright (C) 2019 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. - * ========================LICENSE_END=================================== - */ -package org.oransc.ric.portal.dashboard.config; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import com.fasterxml.jackson.core.JsonProcessingException; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.invoke.MethodHandles; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.oransc.ric.a1controller.client.api.A1ControllerApi; -import org.oransc.ric.a1controller.client.invoker.ApiClient; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidPISchema; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidPIidSchema; -import org.oransc.ric.a1controller.client.model.InputNRRidPTidSchema; -import org.oransc.ric.a1controller.client.model.InputNRRidSchema; -import org.oransc.ric.a1controller.client.model.OutputCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputCodeSchemaOutput; -import org.oransc.ric.a1controller.client.model.OutputDescNamePTCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputDescNamePTCodeSchemaOutput; -import org.oransc.ric.a1controller.client.model.OutputPICodeSchema; -import org.oransc.ric.a1controller.client.model.OutputPICodeSchemaOutput; -import org.oransc.ric.a1controller.client.model.OutputPIidsListCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputPIidsListCodeSchemaOutput; -import org.oransc.ric.a1controller.client.model.OutputPTidsListCodeSchema; -import org.oransc.ric.a1controller.client.model.OutputPTidsListCodeSchemaOutput; -import org.oransc.ric.portal.dashboard.model.PolicyType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpStatus; - -/** - * Creates a mock implementation of the A1 controller client API. - */ -@Profile("test") -@Configuration -public class A1ControllerMockConfiguration { - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - // A "control" is an element in the XApp descriptor - public static final String AC_CONTROL_NAME = "admission_control_policy"; - - // Simulate remote method delay for UI testing - @Value("${mock.config.delay:0}") - private int delayMs; - - public A1ControllerMockConfiguration() { - logger.info("Configuring mock A1 Mediator"); - } - - private ApiClient apiClient() { - ApiClient mockClient = mock(ApiClient.class); - when(mockClient.getStatusCode()).thenReturn(HttpStatus.OK); - return mockClient; - } - - @Bean - // Use the same name as regular configuration - public A1ControllerApi a1ControllerApi() { - ApiClient apiClient = apiClient(); - A1ControllerApi mockApi = mock(A1ControllerApi.class); - - when(mockApi.getApiClient()).thenReturn(apiClient); - - doAnswer(inv -> { - if (delayMs > 0) { - logger.debug("a1ControllerGetHandler sleeping {}", delayMs); - Thread.sleep(delayMs); - } - List types = database.getTypes(); - OutputPTidsListCodeSchemaOutput output = new OutputPTidsListCodeSchemaOutput(); - output.setPolicyTypeIdList(types); - output.setCode(String.valueOf(HttpStatus.OK.value())); - OutputPTidsListCodeSchema outputSchema = new OutputPTidsListCodeSchema(); - outputSchema.setOutput(output); - return outputSchema; - }).when(mockApi).a1ControllerGetAllPolicyTypes(any(InputNRRidSchema.class)); - - doAnswer(inv -> { - if (delayMs > 0) { - logger.debug("a1ControllerGetPolicyType sleeping {}", delayMs); - Thread.sleep(delayMs); - } - InputNRRidPTidSchema input = inv.getArgument(0); - PolicyType policyType = database.getPolicyType(input.getInput().getPolicyTypeId()); - OutputDescNamePTCodeSchemaOutput type = new OutputDescNamePTCodeSchemaOutput(); - type.setName(policyType.getName()); - type.setPolicyType(database.normalize(policyType.getSchema())); - type.setCode(String.valueOf(HttpStatus.OK.value())); - OutputDescNamePTCodeSchema outputSchema = new OutputDescNamePTCodeSchema(); - outputSchema.setOutput(type); - return outputSchema; - }).when(mockApi).a1ControllerGetPolicyType(any(InputNRRidPTidSchema.class)); - - doAnswer(inv -> { - if (delayMs > 0) { - logger.debug("a1ControllerGetHandler sleeping {}", delayMs); - Thread.sleep(delayMs); - } - InputNRRidPTidSchema input = inv.getArgument(0); - List instances = database.getInstances(Optional.of(input.getInput().getPolicyTypeId())); - OutputPIidsListCodeSchemaOutput instancesOutput = new OutputPIidsListCodeSchemaOutput(); - instancesOutput.setPolicyInstanceIdList(instances); - instancesOutput.setCode(String.valueOf(HttpStatus.OK.value())); - OutputPIidsListCodeSchema outputSchema = new OutputPIidsListCodeSchema(); - outputSchema.setOutput(instancesOutput); - return outputSchema; - }).when(mockApi).a1ControllerGetAllInstancesForType(any(InputNRRidPTidSchema.class)); - - doAnswer(inv -> { - if (delayMs > 0) { - logger.debug("a1ControllerGetHandler sleeping {}", delayMs); - Thread.sleep(delayMs); - } - InputNRRidPTidPIidSchema input = inv.getArgument(0); - Integer polcyTypeId = input.getInput().getPolicyTypeId(); - String instanceId = input.getInput().getPolicyInstanceId(); - String instance = database.normalize(database.getInstance(polcyTypeId, instanceId)); - OutputPICodeSchemaOutput instanceOutput = new OutputPICodeSchemaOutput(); - instanceOutput.setPolicyInstance(instance); - instanceOutput.setCode(String.valueOf(HttpStatus.OK.value())); - OutputPICodeSchema outputSchema = new OutputPICodeSchema(); - outputSchema.setOutput(instanceOutput); - return outputSchema; - }).when(mockApi).a1ControllerGetPolicyInstance(any(InputNRRidPTidPIidSchema.class)); - - doAnswer(inv -> { - if (delayMs > 0) { - logger.debug("a1ControllerGetHandler sleeping {}", delayMs); - Thread.sleep(delayMs); - } - InputNRRidPTidPIidPISchema input = inv.getArgument(0); - Integer polcyTypeId = input.getInput().getPolicyTypeId(); - String instanceId = input.getInput().getPolicyInstanceId(); - String instance = input.getInput().getPolicyInstance(); - database.putInstance(polcyTypeId, instanceId, instance); - OutputCodeSchemaOutput outputCodeSchemaOutput = new OutputCodeSchemaOutput(); - outputCodeSchemaOutput.setCode(String.valueOf(HttpStatus.CREATED.value())); - OutputCodeSchema outputCodeSchema = new OutputCodeSchema(); - outputCodeSchema.setOutput(outputCodeSchemaOutput); - return outputCodeSchema; - }).when(mockApi).a1ControllerCreatePolicyInstance(any(InputNRRidPTidPIidPISchema.class)); - - doAnswer(inv -> { - if (delayMs > 0) { - logger.debug("a1ControllerGetHandler sleeping {}", delayMs); - Thread.sleep(delayMs); - } - InputNRRidPTidPIidSchema input = inv.getArgument(0); - Integer polcyTypeId = input.getInput().getPolicyTypeId(); - String instanceId = input.getInput().getPolicyInstanceId(); - database.deleteInstance(polcyTypeId, instanceId); - OutputCodeSchemaOutput outputCodeSchemaOutput = new OutputCodeSchemaOutput(); - outputCodeSchemaOutput.setCode(String.valueOf(HttpStatus.NO_CONTENT.value())); - OutputCodeSchema outputCodeSchema = new OutputCodeSchema(); - outputCodeSchema.setOutput(outputCodeSchemaOutput); - return outputCodeSchema; - }).when(mockApi).a1ControllerDeletePolicyInstance(any(InputNRRidPTidPIidSchema.class)); - - return mockApi; - } - - class Database { - - public class PolicyException extends Exception { - - private static final long serialVersionUID = 1L; - - public PolicyException(String message) { - super(message); - System.out.println("**** Exception " + message); - } - } - - private class PolicyTypeHolder { - PolicyTypeHolder(PolicyType pt) { - this.policyType = pt; - } - - String getInstance(String instanceId) throws PolicyException { - String instance = instances.get(instanceId); - if (instance == null) { - throw new PolicyException("Instance not found: " + instanceId); - } - return instance; - } - - PolicyType getPolicyType() { - return policyType; - } - - void putInstance(String id, String data) { - instances.put(id, data); - } - - void deleteInstance(String id) { - instances.remove(id); - } - - List getInstances() { - return new ArrayList<>(instances.keySet()); - } - - private final PolicyType policyType; - private Map instances = new HashMap<>(); - } - - Database() { - String schema = getStringFromFile("anr-policy-schema.json"); - PolicyType policy = new PolicyType(1, "ANR", schema); - types.put(1, new PolicyTypeHolder(policy)); - - schema = getStringFromFile("demo-policy-schema-1.json"); - policy = new PolicyType(2, "type2", schema); - types.put(2, new PolicyTypeHolder(policy)); - - schema = getStringFromFile("demo-policy-schema-2.json"); - policy = new PolicyType(3, "type3", schema); - types.put(3, new PolicyTypeHolder(policy)); - - schema = getStringFromFile("demo-policy-schema-3.json"); - policy = new PolicyType(4, "type4", schema); - types.put(4, new PolicyTypeHolder(policy)); - try { - putInstance(1, "ANR-1", getStringFromFile("anr-policy-instance.json")); - } catch (JsonProcessingException | PolicyException e) { - // Nothing - } - } - - private String getStringFromFile(String path) { - try { - InputStream inputStream = MethodHandles.lookup().lookupClass().getClassLoader() - .getResourceAsStream(path); - return new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n")); - } catch (Exception e) { - logger.error("Cannot read file :" + path, e); - return ""; - } - } - - String normalize(String str) { - return str.replace('\n', ' '); - } - - void putInstance(Integer typeId, String instanceId, String instanceData) - throws JsonProcessingException, PolicyException { - PolicyTypeHolder type = getTypeHolder(typeId); - type.putInstance(instanceId, instanceData); - } - - void deleteInstance(Integer typeId, String instanceId) throws JsonProcessingException, PolicyException { - PolicyTypeHolder type = getTypeHolder(typeId); - type.deleteInstance(instanceId); - } - - String getInstance(Integer typeId, String instanceId) throws JsonProcessingException, PolicyException { - return getTypeHolder(typeId).getInstance(instanceId); - } - - List getTypes() { - return new ArrayList<>(types.keySet()); - } - - List getInstances(Optional typeId) throws PolicyException { - if (typeId.isPresent()) { - return getTypeHolder(typeId.get()).getInstances(); - } else { - Set res = new HashSet(); - for (Iterator i = types.values().iterator(); i.hasNext();) { - res.addAll(i.next().getInstances()); - } - return new ArrayList<>(res); - } - } - - private PolicyTypeHolder getTypeHolder(Integer typeId) throws PolicyException { - PolicyTypeHolder typeHolder = types.get(typeId); - if (typeHolder == null) { - throw new PolicyException("Type not found: " + typeId); - } - return typeHolder; - } - - private PolicyType getPolicyType(Integer typeId) throws PolicyException { - PolicyTypeHolder typeHolder = getTypeHolder(typeId); - return typeHolder.getPolicyType(); - } - - private Map types = new HashMap<>(); - - } - - private final Database database = new Database(); -} diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java new file mode 100644 index 00000000..2268fe06 --- /dev/null +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java @@ -0,0 +1,192 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ +package org.oransc.ric.portal.dashboard.config; + +import com.google.gson.GsonBuilder; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Vector; +import java.util.stream.Collectors; + +import org.oransc.ric.portal.dashboard.model.ImmutablePolicyInfo; +import org.oransc.ric.portal.dashboard.model.PolicyInfo; +import org.oransc.ric.portal.dashboard.model.PolicyInstances; +import org.oransc.ric.portal.dashboard.model.PolicyType; +import org.oransc.ric.portal.dashboard.model.PolicyTypes; +import org.oransc.ric.portal.dashboard.policyagentapi.PolicyAgentApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClientException; + +/** + * Creates a mock implementation of the policy controller client API. + */ +@TestConfiguration +public class PolicyControllerMockConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static com.google.gson.Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + @Bean + public PolicyAgentApi policyAgentApi() { + MockPolicyAgentApi apiClient = new MockPolicyAgentApi(); + return apiClient; + } + + class MockPolicyAgentApi implements PolicyAgentApi { + private final Database database = new Database(); + + @Override + public ResponseEntity getPolicyInstance(String id) { + return new ResponseEntity<>(database.getInstance(id), HttpStatus.OK); + } + + @Override + public ResponseEntity putPolicy(String policyTypeIdString, String policyInstanceId, String json, + String ric) { + database.putInstance(policyTypeIdString, policyInstanceId, json, ric); + return new ResponseEntity<>("Policy was put successfully", HttpStatus.OK); + } + + @Override + public ResponseEntity deletePolicy(String policyInstanceId) { + database.deleteInstance(policyInstanceId); + return new ResponseEntity<>("Policy was deleted successfully", HttpStatus.NO_CONTENT); + } + + @Override + public ResponseEntity getAllPolicyTypes() { + PolicyTypes result = new PolicyTypes(); + result.addAll(database.getTypes()); + return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK); + } + + @Override + public ResponseEntity getPolicyInstancesForType(String type) { + PolicyInstances result = new PolicyInstances(); + List inst = database.getInstances(Optional.of(type)); + result.addAll(inst); + return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK); + } + + @Override + public ResponseEntity getRicsSupportingType(String typeName) { + Vector res = new Vector<>(); + res.add("ric_1"); + res.add("ric_2"); + res.add("ric_3"); + return new ResponseEntity<>(gson.toJson(res), HttpStatus.OK); + } + } + + class Database { + + Database() { + String schema = getStringFromFile("demo-policy-schema-1.json"); + PolicyType policyType = new PolicyType("type2", schema); + types.put("type2", policyType); + + schema = getStringFromFile("demo-policy-schema-2.json"); + policyType = new PolicyType("type3", schema); + types.put("type3", policyType); + + schema = getStringFromFile("demo-policy-schema-3.json"); + policyType = new PolicyType("type4", schema); + types.put("type4", policyType); + } + + private String getStringFromFile(String path) { + try { + InputStream inputStream = MethodHandles.lookup().lookupClass().getClassLoader() + .getResourceAsStream(path); + return new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n")); + } catch (Exception e) { + logger.error("Cannot read file :" + path, e); + return ""; + } + } + + String normalize(String str) { + return str.replace('\n', ' '); + } + + private String getTimeStampUTC() { + return java.time.Instant.now().toString(); + } + + void putInstance(String typeId, String instanceId, String instanceData, String ric) { + PolicyInfo i = ImmutablePolicyInfo.builder().json(instanceData).lastModified(getTimeStampUTC()) + .id(instanceId).ric(ric).service("service").type(typeId).build(); + instances.put(instanceId, i); + } + + public void deleteInstance(String instanceId) { + instances.remove(instanceId); + } + + String getInstance(String id) throws RestClientException { + PolicyInfo i = instances.get(id); + if (i == null) { + throw new RestClientException("Type not found: " + id); + } + return i.json(); + } + + public Collection getTypes() { + return types.values(); + } + + public List getInstances(Optional typeId) { + ArrayList result = new ArrayList<>(); + for (PolicyInfo i : instances.values()) { + if (typeId.isPresent()) { + if (i.type().equals(typeId.get())) { + result.add(i); + } + + } else { + result.add(i); + } + } + return result; + } + + private Map types = new HashMap<>(); + private Map instances = new HashMap<>(); + + } + +} diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PortalApIMockConfiguration.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PortalApIMockConfiguration.java index a3d05bec..01265d92 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PortalApIMockConfiguration.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PortalApIMockConfiguration.java @@ -43,41 +43,41 @@ import org.springframework.context.annotation.Profile; @Profile("test") public class PortalApIMockConfiguration { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - // Unfortunately EPSDK-FW does not define these as constants - public static final String PORTAL_USERNAME_HEADER_KEY = "username"; - public static final String PORTAL_PASSWORD_HEADER_KEY = "password"; + // Unfortunately EPSDK-FW does not define these as constants + public static final String PORTAL_USERNAME_HEADER_KEY = "username"; + public static final String PORTAL_PASSWORD_HEADER_KEY = "password"; - @Bean - public ServletRegistrationBean portalApiProxyServlet() { - PortalRestAPIProxy servlet = new PortalRestAPIProxy(); - final ServletRegistrationBean servletBean = new ServletRegistrationBean<>(servlet, - PortalApiConstants.API_PREFIX + "/*"); - servletBean.setName("PortalRestApiProxyServlet"); - return servletBean; - } + @Bean + public ServletRegistrationBean portalApiProxyServlet() { + PortalRestAPIProxy servlet = new PortalRestAPIProxy(); + final ServletRegistrationBean servletBean = + new ServletRegistrationBean<>(servlet, PortalApiConstants.API_PREFIX + "/*"); + servletBean.setName("PortalRestApiProxyServlet"); + return servletBean; + } - @Bean - public PortalAuthManager portalAuthManager() throws Exception { - PortalAuthManager mockManager = mock(PortalAuthManager.class); - final Map credentialsMap = new HashMap<>(); - credentialsMap.put("appName", "appName"); - credentialsMap.put(PORTAL_USERNAME_HEADER_KEY, PORTAL_USERNAME_HEADER_KEY); - credentialsMap.put(PORTAL_PASSWORD_HEADER_KEY, PORTAL_PASSWORD_HEADER_KEY); - doAnswer(inv -> { - logger.debug("getAppCredentials"); - return credentialsMap; - }).when(mockManager).getAppCredentials(); - doAnswer(inv -> { - logger.debug("getUserId"); - return "userId"; - }).when(mockManager).validateEcompSso(any(HttpServletRequest.class)); - doAnswer(inv -> { - logger.debug("getAppCredentials"); - return credentialsMap; - }).when(mockManager).getAppCredentials(); - return mockManager; - } + @Bean + public PortalAuthManager portalAuthManager() throws Exception { + PortalAuthManager mockManager = mock(PortalAuthManager.class); + final Map credentialsMap = new HashMap<>(); + credentialsMap.put("appName", "appName"); + credentialsMap.put(PORTAL_USERNAME_HEADER_KEY, PORTAL_USERNAME_HEADER_KEY); + credentialsMap.put(PORTAL_PASSWORD_HEADER_KEY, PORTAL_PASSWORD_HEADER_KEY); + doAnswer(inv -> { + logger.debug("getAppCredentials"); + return credentialsMap; + }).when(mockManager).getAppCredentials(); + doAnswer(inv -> { + logger.debug("getUserId"); + return "userId"; + }).when(mockManager).validateEcompSso(any(HttpServletRequest.class)); + doAnswer(inv -> { + logger.debug("getAppCredentials"); + return credentialsMap; + }).when(mockManager).getAppCredentials(); + return mockManager; + } } diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java index 80cde661..3e756060 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java @@ -42,43 +42,43 @@ import org.springframework.security.crypto.password.PasswordEncoder; @Profile("test") public class WebSecurityMockConfiguration extends WebSecurityConfigurerAdapter { - public static final String TEST_CRED_ADMIN = "admin"; - public static final String TEST_CRED_STANDARD = "standard"; + public static final String TEST_CRED_ADMIN = "admin"; + public static final String TEST_CRED_STANDARD = "standard"; - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public WebSecurityMockConfiguration(@Value("${userfile}") final String userFilePath) { - logger.debug("ctor: user file path {}", userFilePath); - } + public WebSecurityMockConfiguration(@Value("${userfile}") final String userFilePath) { + logger.debug("ctor: user file path {}", userFilePath); + } - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); - auth.inMemoryAuthentication() // - .passwordEncoder(encoder) // - // The admin user has the admin AND standard roles - .withUser(TEST_CRED_ADMIN) // - .password(encoder.encode(TEST_CRED_ADMIN)) - .roles(DashboardConstants.ROLE_NAME_ADMIN, DashboardConstants.ROLE_NAME_STANDARD)// - .and()// - // The standard user has only the standard role - .withUser(TEST_CRED_STANDARD) // - .password(encoder.encode(TEST_CRED_STANDARD)) // - .roles(DashboardConstants.ROLE_NAME_STANDARD); - } + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + auth.inMemoryAuthentication() // + .passwordEncoder(encoder) // + // The admin user has the admin AND standard roles + .withUser(TEST_CRED_ADMIN) // + .password(encoder.encode(TEST_CRED_ADMIN)) + .roles(DashboardConstants.ROLE_NAME_ADMIN, DashboardConstants.ROLE_NAME_STANDARD)// + .and()// + // The standard user has only the standard role + .withUser(TEST_CRED_STANDARD) // + .password(encoder.encode(TEST_CRED_STANDARD)) // + .roles(DashboardConstants.ROLE_NAME_STANDARD); + } - @Override - protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().anyRequest().authenticated()// - .and().httpBasic() // - .and().csrf().disable(); - } + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated()// + .and().httpBasic() // + .and().csrf().disable(); + } - @Override - public void configure(WebSecurity web) throws Exception { - // This disables Spring security, but not the app's filter. - web.ignoring().antMatchers(WebSecurityConfiguration.OPEN_PATHS); - web.ignoring().antMatchers("/", "/csrf"); // allow swagger-ui to load - } + @Override + public void configure(WebSecurity web) throws Exception { + // This disables Spring security, but not the app's filter. + web.ignoring().antMatchers(WebSecurityConfiguration.OPEN_PATHS); + web.ignoring().antMatchers("/", "/csrf"); // allow swagger-ui to load + } } diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AbstractControllerTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AbstractControllerTest.java index d4163f0d..09d3a869 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AbstractControllerTest.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AbstractControllerTest.java @@ -7,9 +7,9 @@ * 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. @@ -44,69 +44,69 @@ import org.springframework.web.util.UriComponentsBuilder; @ActiveProfiles("test") public class AbstractControllerTest { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - // Created by Spring black magic - // https://spring.io/guides/gs/testing-web/ - @LocalServerPort - private int localServerPort; + // Created by Spring black magic + // https://spring.io/guides/gs/testing-web/ + @LocalServerPort + private int localServerPort; - @Autowired - protected TestRestTemplate restTemplate; + @Autowired + protected TestRestTemplate restTemplate; - /** - * Flexible URI builder. - * - * @param queryParams - * Map of string-string query parameters - * @param path - * Array of path components. If a component has an - * embedded slash, the string is split and each - * subcomponent is added individually. - * @return URI - */ - protected URI buildUri(final Map queryParams, final String... path) { - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://localhost:" + localServerPort + "/"); - for (int p = 0; p < path.length; ++p) { - if (path[p] == null || path[p].isEmpty()) { - throw new IllegalArgumentException("Unexpected null or empty at path index " + Integer.toString(p)); - } else if (path[p].contains("/")) { - String[] subpaths = path[p].split("/"); - for (String s : subpaths) - if (!s.isEmpty()) - builder.pathSegment(s); - } else { - builder.pathSegment(path[p]); - } - } - if (queryParams != null && queryParams.size() > 0) { - for (Map.Entry entry : queryParams.entrySet()) { - if (entry.getKey() == null || entry.getValue() == null) - throw new IllegalArgumentException("Unexpected null key or value"); - else - builder.queryParam(entry.getKey(), entry.getValue()); - } - } - return builder.build().encode().toUri(); - } + /** + * Flexible URI builder. + * + * @param queryParams + * Map of string-string query parameters + * @param path + * Array of path components. If a component has an + * embedded slash, the string is split and each + * subcomponent is added individually. + * @return URI + */ + protected URI buildUri(final Map queryParams, final String... path) { + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://localhost:" + localServerPort + "/"); + for (int p = 0; p < path.length; ++p) { + if (path[p] == null || path[p].isEmpty()) { + throw new IllegalArgumentException("Unexpected null or empty at path index " + Integer.toString(p)); + } else if (path[p].contains("/")) { + String[] subpaths = path[p].split("/"); + for (String s : subpaths) + if (!s.isEmpty()) + builder.pathSegment(s); + } else { + builder.pathSegment(path[p]); + } + } + if (queryParams != null && queryParams.size() > 0) { + for (Map.Entry entry : queryParams.entrySet()) { + if (entry.getKey() == null || entry.getValue() == null) + throw new IllegalArgumentException("Unexpected null key or value"); + else + builder.queryParam(entry.getKey(), entry.getValue()); + } + } + return builder.build().encode().toUri(); + } - // Because I put the annotations on this parent class, - // must define at least one test here. - @Test - public void contextLoads() { - // Silence Sonar warning about missing assertion. - Assertions.assertTrue(logger.isWarnEnabled()); - logger.info("Context loads on mock profile"); - } + // Because I put the annotations on this parent class, + // must define at least one test here. + @Test + public void contextLoads() { + // Silence Sonar warning about missing assertion. + Assertions.assertTrue(logger.isWarnEnabled()); + logger.info("Context loads on mock profile"); + } - public TestRestTemplate testRestTemplateAdminRole() { - return restTemplate.withBasicAuth(WebSecurityMockConfiguration.TEST_CRED_ADMIN, - WebSecurityMockConfiguration.TEST_CRED_ADMIN); - } + public TestRestTemplate testRestTemplateAdminRole() { + return restTemplate.withBasicAuth(WebSecurityMockConfiguration.TEST_CRED_ADMIN, + WebSecurityMockConfiguration.TEST_CRED_ADMIN); + } - public TestRestTemplate testRestTemplateStandardRole() { - return restTemplate.withBasicAuth(WebSecurityMockConfiguration.TEST_CRED_STANDARD, - WebSecurityMockConfiguration.TEST_CRED_STANDARD); - } + public TestRestTemplate testRestTemplateStandardRole() { + return restTemplate.withBasicAuth(WebSecurityMockConfiguration.TEST_CRED_STANDARD, + WebSecurityMockConfiguration.TEST_CRED_STANDARD); + } } diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/DefaultContextTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/DefaultContextTest.java index 48c59315..643f15e2 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/DefaultContextTest.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/DefaultContextTest.java @@ -36,13 +36,13 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @SpringBootTest public class DefaultContextTest { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - @Test - public void contextLoads() { - // Silence Sonar warning about missing assertion. - Assertions.assertTrue(logger.isWarnEnabled()); - logger.info("Context loads on default profile"); - } + @Test + public void contextLoads() { + // Silence Sonar warning about missing assertion. + Assertions.assertTrue(logger.isWarnEnabled()); + logger.info("Context loads on default profile"); + } } 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 dc80968b..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 @@ -7,9 +7,9 @@ * 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. @@ -21,97 +21,91 @@ package org.oransc.ric.portal.dashboard.controller; import java.lang.invoke.MethodHandles; import java.net.URI; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.onap.portalsdk.core.onboarding.util.PortalApiConstants; -import org.onap.portalsdk.core.restful.domain.EcompRole; -import org.onap.portalsdk.core.restful.domain.EcompUser; -import org.oransc.ric.portal.dashboard.DashboardConstants; -import org.oransc.ric.portal.dashboard.config.PortalApIMockConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; public class PortalRestCentralServiceTest extends AbstractControllerTest { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - @Test - public void getAnalyticsTest() { - // paths are hardcoded here exactly like the EPSDK-FW library :( - URI uri = buildUri(null, PortalApiConstants.API_PREFIX, "/analytics"); - logger.info("Invoking {}", uri); - ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class); - // No Portal is available so this always fails - Assertions.assertTrue(response.getStatusCode().is4xxClientError()); - } + @Test + public void getAnalyticsTest() { + // paths are hardcoded here exactly like the EPSDK-FW library :( + URI uri = buildUri(null, PortalApiConstants.API_PREFIX, "/analytics"); + logger.info("Invoking {}", uri); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class); + // No Portal is available so this always fails + Assertions.assertTrue(response.getStatusCode().is4xxClientError()); + } - @Test - public void getErrorPageTest() { - // Send unauthorized request - URI uri = buildUri(null, "/favicon.ico"); - logger.info("Invoking {}", uri); - ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class); - Assertions.assertTrue(response.getStatusCode().is4xxClientError()); - Assertions.assertTrue(response.getBody().contains("Static error page")); - } + @Test + public void getErrorPageTest() { + // Send unauthorized request + URI uri = buildUri(null, "/favicon.ico"); + logger.info("Invoking {}", uri); + ResponseEntity response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class); + Assertions.assertTrue(response.getStatusCode().is4xxClientError()); + Assertions.assertTrue(response.getBody().contains("Static error page")); + } - private HttpEntity getEntityWithHeaders(Object body) { - HttpHeaders headers = new HttpHeaders(); - headers.set(PortalApIMockConfiguration.PORTAL_USERNAME_HEADER_KEY, - PortalApIMockConfiguration.PORTAL_USERNAME_HEADER_KEY); - headers.set(PortalApIMockConfiguration.PORTAL_PASSWORD_HEADER_KEY, - PortalApIMockConfiguration.PORTAL_PASSWORD_HEADER_KEY); - HttpEntity entity = new HttpEntity<>(body, headers); - return entity; - } - - private EcompUser createEcompUser(String loginId) { - EcompUser user = new EcompUser(); - user.setLoginId(loginId); - EcompRole role = new EcompRole(); - role.setRoleFunctions(Collections.EMPTY_SET); - role.setId(1L); - role.setName(DashboardConstants.ROLE_NAME_ADMIN); - Set roles = new HashSet<>(); - roles.add(role); - user.setRoles(roles); - return user; - } - -/* @Test - public void createUserTest() { - final String loginId = "login1"; - URI create = buildUri(null, PortalApiConstants.API_PREFIX, "user"); - logger.info("Invoking {}", create); - HttpEntity requestEntity = getEntityWithHeaders(createEcompUser(loginId)); - ResponseEntity response = restTemplate.exchange(create, HttpMethod.POST, requestEntity, String.class); - Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); - } - - @Test - public void updateUserTest() { - final String loginId = "login2"; - URI create = buildUri(null, PortalApiConstants.API_PREFIX, "user"); - EcompUser user = createEcompUser(loginId); - logger.info("Invoking {}", create); - HttpEntity requestEntity = getEntityWithHeaders(user); - // Create - ResponseEntity response = restTemplate.exchange(create, HttpMethod.POST, requestEntity, String.class); - Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); - URI update = buildUri(null, PortalApiConstants.API_PREFIX, "user", loginId); - user.setEmail("user@company.org"); - requestEntity = getEntityWithHeaders(user); - response = restTemplate.exchange(update, HttpMethod.POST, requestEntity, String.class); - Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); - } -*/ + /* + * private HttpEntity getEntityWithHeaders(Object body) { + * HttpHeaders headers = new HttpHeaders(); + * headers.set(PortalApIMockConfiguration.PORTAL_USERNAME_HEADER_KEY, + * PortalApIMockConfiguration.PORTAL_USERNAME_HEADER_KEY); + * headers.set(PortalApIMockConfiguration.PORTAL_PASSWORD_HEADER_KEY, + * PortalApIMockConfiguration.PORTAL_PASSWORD_HEADER_KEY); + * HttpEntity entity = new HttpEntity<>(body, headers); + * return entity; + * } + * + * private EcompUser createEcompUser(String loginId) { + * EcompUser user = new EcompUser(); + * user.setLoginId(loginId); + * EcompRole role = new EcompRole(); + * role.setRoleFunctions(Collections.EMPTY_SET); + * role.setId(1L); + * role.setName(DashboardConstants.ROLE_NAME_ADMIN); + * Set roles = new HashSet<>(); + * roles.add(role); + * user.setRoles(roles); + * return user; + * } + * + * @Test + * + * @Test + * public void createUserTest() { + * final String loginId = "login1"; + * URI create = buildUri(null, PortalApiConstants.API_PREFIX, "user"); + * logger.info("Invoking {}", create); + * HttpEntity requestEntity = getEntityWithHeaders(createEcompUser(loginId)); + * ResponseEntity response = restTemplate.exchange(create, HttpMethod.POST, requestEntity, String.class); + * Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); + * } + * + * @Test + * public void updateUserTest() { + * final String loginId = "login2"; + * URI create = buildUri(null, PortalApiConstants.API_PREFIX, "user"); + * EcompUser user = createEcompUser(loginId); + * logger.info("Invoking {}", create); + * HttpEntity requestEntity = getEntityWithHeaders(user); + * // Create + * ResponseEntity response = restTemplate.exchange(create, HttpMethod.POST, requestEntity, String.class); + * Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); + * URI update = buildUri(null, PortalApiConstants.API_PREFIX, "user", loginId); + * user.setEmail("user@company.org"); + * requestEntity = getEntityWithHeaders(user); + * response = restTemplate.exchange(update, HttpMethod.POST, requestEntity, String.class); + * Assertions.assertTrue(response.getStatusCode().is2xxSuccessful()); + * } + */ } diff --git a/dashboard/webapp-backend/src/test/resources/anr-policy-instance.json b/dashboard/webapp-backend/src/test/resources/anr-policy-instance.json deleted file mode 100644 index 0d7315eb..00000000 --- a/dashboard/webapp-backend/src/test/resources/anr-policy-instance.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "servingCellNrcgi": "Cell1", - "neighborCellNrpci": "NCell1", - "neighborCellNrcgi": "Ncell1", - "flagNoHo": true, - "flagNoXn": true, - "flagNoRemove": true -} \ No newline at end of file diff --git a/dashboard/webapp-backend/src/test/resources/anr-policy-schema.json b/dashboard/webapp-backend/src/test/resources/anr-policy-schema.json deleted file mode 100644 index 6e0263d0..00000000 --- a/dashboard/webapp-backend/src/test/resources/anr-policy-schema.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ANR", - "description": "ANR Neighbour Cell Relation Policy", - "type": "object", - "properties": { - "servingCellNrcgi": { - "type": "string", - "description": "Serving Cell Identifier (NR CGI)" - }, - "neighborCellNrpci": { - "type": "string", - "description": "Neighbor Cell Identifier (NR PCI)" - }, - "neighborCellNrcgi": { - "type": "string", - "description": "Neighbor Cell Identifier (NR CGI)" - }, - "flagNoHo": { - "type": "boolean", - "description": "Flag for HANDOVER NOT ALLOWED" - }, - "flagNoXn": { - "type": "boolean", - "description": "Flag for Xn CONNECTION NOT ALLOWED" - }, - "flagNoRemove": { - "type": "boolean", - "description": "Flag for DELETION NOT ALLOWED" - } - }, - "required": [ - "servingCellNrcgi", - "neighborCellNrpci", - "neighborCellNrcgi", - "flagNoHo", - "flagNoXn", - "flagNoRemove" - ] -} \ No newline at end of file diff --git a/dashboard/webapp-frontend/package-lock.json b/dashboard/webapp-frontend/package-lock.json index b82a67cc..75dfaddf 100644 --- a/dashboard/webapp-frontend/package-lock.json +++ b/dashboard/webapp-frontend/package-lock.json @@ -5,12 +5,12 @@ "requires": true, "dependencies": { "@angular-devkit/architect": { - "version": "0.803.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.8.tgz", - "integrity": "sha512-eenVmiFz7CXHkPT0sboaqHHQ48y43racZP648VMuvPJK5wHZhdwyfe5Whz4qnuEHca1fB0FbVDKP10okwVYfPQ==", + "version": "0.803.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.23.tgz", + "integrity": "sha512-BRDbnmdULrle2l7WFZHEW/OAwS8RRg08+jiNG3gEP0BxDN6QMNMKmWhxmX67pgq3e/xMvu2DH0z71mAPNtJDAw==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.8", + "@angular-devkit/core": "8.3.23", "rxjs": "6.4.0" }, "dependencies": { @@ -26,31 +26,31 @@ } }, "@angular-devkit/build-angular": { - "version": "0.803.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.803.8.tgz", - "integrity": "sha512-SKhUcTu2ZfjicUfyyef4SZwmLJoyRFPqA8wceJeqnrkGrkSoHHKgCeqL01fIBVOUaRoIsZQJPCG56YDTEjLKLQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.803.8", - "@angular-devkit/build-optimizer": "0.803.8", - "@angular-devkit/build-webpack": "0.803.8", - "@angular-devkit/core": "8.3.8", - "@babel/core": "7.5.5", - "@babel/preset-env": "7.5.5", - "@ngtools/webpack": "8.3.8", + "version": "0.803.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.803.23.tgz", + "integrity": "sha512-hlaDMuScRbgdsH3Toyze5G5NhmJypWIPGcIt4CAcXAnVdSltrBPKzu5Psr+ACcDLH3TYtlMKBrkAG9xXS3it1g==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.803.23", + "@angular-devkit/build-optimizer": "0.803.23", + "@angular-devkit/build-webpack": "0.803.23", + "@angular-devkit/core": "8.3.23", + "@babel/core": "7.7.5", + "@babel/preset-env": "7.7.6", + "@ngtools/webpack": "8.3.23", "ajv": "6.10.2", "autoprefixer": "9.6.1", - "browserslist": "4.6.6", + "browserslist": "4.8.3", "cacache": "12.0.2", - "caniuse-lite": "1.0.30000989", + "caniuse-lite": "1.0.30001019", "circular-dependency-plugin": "5.2.0", "clean-css": "4.2.1", - "copy-webpack-plugin": "5.0.4", + "copy-webpack-plugin": "5.1.1", "core-js": "3.2.1", + "coverage-istanbul-loader": "2.0.3", "file-loader": "4.2.0", "find-cache-dir": "3.0.0", "glob": "7.1.4", - "istanbul-instrumenter-loader": "3.0.1", "jest-worker": "24.9.0", "karma-source-map-support": "1.4.0", "less": "3.9.0", @@ -77,12 +77,12 @@ "style-loader": "1.0.0", "stylus": "0.54.5", "stylus-loader": "3.0.2", - "terser": "4.1.4", - "terser-webpack-plugin": "1.4.1", - "tree-kill": "1.2.1", + "terser": "4.3.9", + "terser-webpack-plugin": "1.4.3", + "tree-kill": "1.2.2", "webpack": "4.39.2", - "webpack-dev-middleware": "3.7.0", - "webpack-dev-server": "3.8.0", + "webpack-dev-middleware": "3.7.2", + "webpack-dev-server": "3.9.0", "webpack-merge": "4.2.1", "webpack-sources": "1.4.3", "webpack-subresource-integrity": "1.1.0-rc.6", @@ -157,9 +157,9 @@ } }, "@angular-devkit/build-optimizer": { - "version": "0.803.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.803.8.tgz", - "integrity": "sha512-UiMxl1wI3acqIoRkC0WA0qpab+ni6SlCaB4UIwfD1H/FdzU80P04AIUuJS7StxjbwVkVtA05kcfgmqzP8yBMVg==", + "version": "0.803.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.803.23.tgz", + "integrity": "sha512-0MJAnGjpmE1hNrwDBi/7b9G1qyt2qN/wcZOj6QseZeWuoxIVXIWgdM6gBpJdgB7HI7vv4l4LpyFX9Doq+2r7Xg==", "dev": true, "requires": { "loader-utils": "1.2.3", @@ -170,15 +170,14 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.803.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.803.8.tgz", - "integrity": "sha512-NFG2suQG4w6uUf1bbduLi/sQw94J1nB3D9heNh5o6ov0Ps1fTA4YEDg3T0RQ8ljmfaLb+wHsxajztzOG/RRnZw==", + "version": "0.803.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.803.23.tgz", + "integrity": "sha512-ttsvUpoMHAr84I3YQmr2Yyu1qPIjw3m+aYgeEh1cAN+Ck8/F/q+Z+nWsmcgIXEC2f8xN7uZWy4PIkCZR8YETOg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.803.8", - "@angular-devkit/core": "8.3.8", - "rxjs": "6.4.0", - "webpack-merge": "4.2.1" + "@angular-devkit/architect": "0.803.23", + "@angular-devkit/core": "8.3.23", + "rxjs": "6.4.0" }, "dependencies": { "rxjs": { @@ -193,9 +192,9 @@ } }, "@angular-devkit/core": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.8.tgz", - "integrity": "sha512-HwlMRr6qANwhOJS+5rGgQ2lmP4nj2C4cbUc0LlA09Cdbq0RnDquUFVqHF6h81FUKFW1D5qDehWYHNOVq8+gTkQ==", + "version": "8.3.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.23.tgz", + "integrity": "sha512-y++LN6R/fu+obPUKEMDSKZ5FzeWN5rV0Z8vrdC+uF02VJLv/5QI/dUx3ROKFzJO3m2LU6EAuo5b/TLAPq4ving==", "dev": true, "requires": { "ajv": "6.10.2", @@ -229,12 +228,12 @@ } }, "@angular-devkit/schematics": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.8.tgz", - "integrity": "sha512-1KnluRj86QO6fDE++iNbUHq1nNHpz0ZQDs/siy+tDtenO5TxAO/vegHYNKvsIcMMUF9z2kHA0qwUbq5oN8K85g==", + "version": "8.3.23", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.23.tgz", + "integrity": "sha512-O8i/vn6YfqbT0q7o4jsVOTnWE07T1tcvk2zJ4O/1ete2z+Z2aw1YtIddwXEGJNCDpeE0B7f2sUHoLOS4Jc4O9w==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.8", + "@angular-devkit/core": "8.3.23", "rxjs": "6.4.0" }, "dependencies": { @@ -250,9 +249,9 @@ } }, "@angular/animations": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.9.tgz", - "integrity": "sha512-l30AF0d9P5okTPM1wieUHgcnDyGSNvyaBcxXSOkT790wAP2v5zs7VrKq9Lm+ICu4Nkx07KrOr5XLUHhqsg3VXA==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.14.tgz", + "integrity": "sha512-3Vc9TnNpKdtvKIXcWDFINSsnwgEMiDmLzjceWg1iYKwpeZGQahUXPoesLwQazBMmxJzQiA4HOMj0TTXKZ+Jzkg==", "requires": { "tslib": "^1.9.0" } @@ -275,16 +274,16 @@ } }, "@angular/cli": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.8.tgz", - "integrity": "sha512-JmumKB21XKyQwe3fSeaaEGTWuv39mtrNQ4CWIXzLKY+oWdpBy+G82JRjXM3OMLmKGrmxiAjTc6kP0oRYaq25JA==", + "version": "8.3.23", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.23.tgz", + "integrity": "sha512-umr5puS6j8elTIhhsjyb/psTmwL00oeBbsnnz5K3fkbWB2wgdMsJvLi9aR/oAyh2NlSA2ZzgB62I38VjoDR0yQ==", "dev": true, "requires": { - "@angular-devkit/architect": "0.803.8", - "@angular-devkit/core": "8.3.8", - "@angular-devkit/schematics": "8.3.8", - "@schematics/angular": "8.3.8", - "@schematics/update": "0.803.8", + "@angular-devkit/architect": "0.803.23", + "@angular-devkit/core": "8.3.23", + "@angular-devkit/schematics": "8.3.23", + "@schematics/angular": "8.3.23", + "@schematics/update": "0.803.23", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "^4.1.1", @@ -295,6 +294,7 @@ "open": "6.4.0", "pacote": "9.5.5", "read-package-tree": "5.3.1", + "rimraf": "3.0.0", "semver": "6.3.0", "symbol-observable": "1.2.0", "universal-analytics": "^0.4.20", @@ -322,6 +322,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -331,25 +340,25 @@ } }, "@angular/common": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-8.2.9.tgz", - "integrity": "sha512-76WDU1USlI5vAzqCJ3gxCQGuu57aJEggNk/xoWmQEXipiFTFBh2wSKn/dE6Txr/q3COTPIcrmb9OCeal5kQPIA==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-8.2.14.tgz", + "integrity": "sha512-Qmt+aX2quUW54kaNT7QH7WGXnFxr/cC2C6sf5SW5SdkZfDQSiz8IaItvieZfXVQUbBOQKFRJ7TlSkt0jI/yjvw==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.9.tgz", - "integrity": "sha512-oQho19DnOhEDNerCOGuGK95tcZ2oy4dSA5SykJmmniRnZzPM2++bJD32qJehXHy1K+3hv2zN9x7HPhqT3ljT6g==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.14.tgz", + "integrity": "sha512-ABZO4E7eeFA1QyJ2trDezxeQM5ZFa1dXw1Mpl/+1vuXDKNjJgNyWYwKp/NwRkLmrsuV0yv4UDCDe4kJOGbPKnw==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-8.2.9.tgz", - "integrity": "sha512-tqGBKPf3SRYNEGGJbmjom//U/eAjnecDhGUw6o+VkYE/wxYd9pPcLmcEwwyXBpIPJAsN8RsjTikPuH0gcNE8bw==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-8.2.14.tgz", + "integrity": "sha512-XDrTyrlIZM+0NquVT+Kbg5bn48AaWFT+B3bAT288PENrTdkuxuF9AhjFRZj8jnMdmaE4O2rioEkXBtl6z3zptA==", "dev": true, "requires": { "canonical-path": "1.0.0", @@ -446,12 +455,6 @@ "ansi-regex": "^4.1.0" } }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, "yargs": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.1.0.tgz", @@ -484,33 +487,33 @@ } }, "@angular/core": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.9.tgz", - "integrity": "sha512-GpHAuLOlN9iioELCQBmAsjETTUCyFgVUI3LXwh3e63jnpd+ZuuZcZbjfTYhtgYVNMetn7cVEO6p88eb7qvpUWQ==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.14.tgz", + "integrity": "sha512-zeePkigi+hPh3rN7yoNENG/YUBUsIvUXdxx+AZq+QPaFeKEA2FBSrKn36ojHFrdJUjKzl0lPMEiGC2b6a6bo6g==", "requires": { "tslib": "^1.9.0" } }, "@angular/flex-layout": { - "version": "7.0.0-beta.19", - "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.19.tgz", - "integrity": "sha512-MXq+zZ6/s5/+GsL9fZ42mKL0LjZ/+L0sVU5FaQuSAJ57soLl5QAGWvdxVmROtqcHd3Htp35R49nKSZBJ0nfAjg==", + "version": "7.0.0-beta.24", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.24.tgz", + "integrity": "sha512-ll6sK0nLGxqI/f5+z4jbd+pve1QITzgehv2AuGvfSDgIjPMeqUDB5YZqQmIGM/dQRk/vIio5KCW5LQPJWzMMYQ==", "requires": { "tslib": "^1.7.1" } }, "@angular/forms": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-8.2.9.tgz", - "integrity": "sha512-kAdBuApC9PPOdPI8BmNhxCraAkXGbX/PkVan8pQ5xdumvgGqvVjbJvLaUSbJROPtgCRlQyiEDrHFd4gk/WU76A==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-8.2.14.tgz", + "integrity": "sha512-zhyKL3CFIqcyHJ/TQF/h1OZztK611a6rxuPHCrt/5Sn1SuBTJJQ1pPTkOYIDy6IrCrtyANc8qB6P17Mao71DNQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/language-service": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-8.2.9.tgz", - "integrity": "sha512-F6ReN0cToHIkCjEM2ECkBxCTsvFjVae8FpIr3Fz8IHZHOOYcS5mx/BWdEO7odI5/tQKl+cCWol7NjvJYV0zolg==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-8.2.14.tgz", + "integrity": "sha512-7EhN9JJbAJcH2xCa+rIOmekjiEuB0qwPdHuD5qn/wwMfRzMZo+Db4hHbR9KHrLH6H82PTwYKye/LLpDaZqoHOA==", "dev": true }, "@angular/material": { @@ -522,52 +525,52 @@ } }, "@angular/platform-browser": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.9.tgz", - "integrity": "sha512-k3aNZy0OTqGn7HlHHV52QF6ZAP/VlQhWGD2u5e1dWIWMq39kdkdSCNu5tiuAf5hIzMBiSQ0tjnuVWA4MuDBYIQ==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.14.tgz", + "integrity": "sha512-MtJptptyKzsE37JZ2VB/tI4cvMrdAH+cT9pMBYZd66YSZfKjIj5s+AZo7z8ncoskQSB1o3HMfDjSK7QXGx1mLQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.2.9.tgz", - "integrity": "sha512-GbE4TUy4n/a8yp8fLWwdG/QnjUPZZ8VufItZ7GvOpoyknzegvka111dLctvMoPzSAsrKyShL6cryuyDC5PShUA==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.2.14.tgz", + "integrity": "sha512-mO2JPR5kLU/A3AQngy9+R/Q5gaF9csMStBQjwsCRI0wNtlItOIGL6+wTYpiTuh/ux+WVN1F2sLcEYU4Zf1ud9A==", "requires": { "tslib": "^1.9.0" } }, "@angular/router": { - "version": "8.2.9", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-8.2.9.tgz", - "integrity": "sha512-4P60CWNB/jxGjDBEuYN0Jobt76QlebAQeFBTDswRVwRlq/WJT4QhL3a8AVIRsHn9bQII0LUt/ZQBBPxn7h9lSA==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-8.2.14.tgz", + "integrity": "sha512-DHA2BhODqV7F0g6ZKgFaZgbsqzHHWRcfWchCOrOVKu2rYiKUTwwHVLBgZAhrpNeinq2pWanVYSIhMr7wy+LfEA==", "requires": { "tslib": "^1.9.0" } }, "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "dev": true, "requires": { "@babel/highlight": "^7.0.0" } }, "@babel/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", - "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.5.tgz", + "integrity": "sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", - "@babel/helpers": "^7.5.5", - "@babel/parser": "^7.5.5", - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.5.5", - "@babel/types": "^7.5.5", - "convert-source-map": "^1.1.0", + "@babel/generator": "^7.7.4", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", + "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", "lodash": "^4.17.13", @@ -576,69 +579,6 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.3.tgz", - "integrity": "sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw==", - "dev": true, - "requires": { - "@babel/types": "^7.6.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@babel/parser": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.3.tgz", - "integrity": "sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg==", - "dev": true - }, - "@babel/traverse": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", - "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.3", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.3", - "@babel/types": "^7.6.3", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -657,12 +597,6 @@ "minimist": "^1.2.0" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -676,9 +610,9 @@ "dev": true }, "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -693,24 +627,17 @@ } }, "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", + "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.7.4", "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "lodash": "^4.17.13", + "source-map": "^0.5.0" }, "dependencies": { - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -720,279 +647,160 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", - "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", "dev": true, "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", - "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-call-delegate": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", - "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/helper-define-map": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz", - "integrity": "sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/types": "^7.5.5", - "lodash": "^4.17.13" + "@babel/types": "^7.8.3" }, "dependencies": { "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true } } }, - "@babel/helper-explode-assignable-expression": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", - "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", - "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz", - "integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==", + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", "dev": true, "requires": { - "@babel/types": "^7.5.5" + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" }, "dependencies": { "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true } } }, - "@babel/helper-module-imports": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz", - "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==", + "@babel/helper-call-delegate": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz", + "integrity": "sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/template": "^7.4.4", - "@babel/types": "^7.5.5", - "lodash": "^4.17.13" + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" }, "dependencies": { - "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "esutils": "^2.0.2", + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", + "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" + "source-map": "^0.5.0" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", - "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", - "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", - "dev": true, - "requires": { - "lodash": "^4.17.13" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", - "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-wrap-function": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-replace-supers": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz", - "integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.5.5", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.5.5", - "@babel/types": "^7.5.5" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, - "@babel/generator": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.3.tgz", - "integrity": "sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw==", + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@babel/types": "^7.6.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.6.1" + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.3.tgz", - "integrity": "sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", "dev": true }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, "@babel/traverse": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", - "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", + "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.3", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.3", - "@babel/types": "^7.6.3", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1009,10 +817,10 @@ "ms": "^2.1.1" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "ms": { @@ -1022,116 +830,95 @@ "dev": true }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } }, - "@babel/helper-simple-access": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", - "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", - "dev": true, - "requires": { - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/helper-wrap-function": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", - "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "@babel/helper-create-regexp-features-plugin": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz", + "integrity": "sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.2.0" + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.6.0" } }, - "@babel/helpers": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.2.tgz", - "integrity": "sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==", + "@babel/helper-define-map": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", "dev": true, "requires": { - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.2", - "@babel/types": "^7.6.0" + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", + "lodash": "^4.17.13" }, "dependencies": { - "@babel/generator": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.3.tgz", - "integrity": "sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@babel/types": "^7.6.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.6.1" + "@babel/highlight": "^7.8.3" } }, - "@babel/parser": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.3.tgz", - "integrity": "sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg==", - "dev": true + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } }, - "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" + "@babel/types": "^7.8.3" } }, - "@babel/traverse": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", - "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.3", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.3", - "@babel/types": "^7.6.3", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - } + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1139,39 +926,1028 @@ "to-fast-properties": "^2.0.0" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "ms": "^2.1.1" + "@babel/highlight": "^7.8.3" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "ms": { + "@babel/generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", + "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", + "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-module-transforms": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz", + "integrity": "sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", + "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", + "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz", + "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", + "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", + "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4" + } + }, + "@babel/helper-wrap-function": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", + "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", + "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helpers": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.3.tgz", + "integrity": "sha512-LmU3q9Pah/XyZU89QvBgGt+BCsTPoQa+73RxAQh8fb8qkDyIfeQnmgs+hvzhTCKTzqOyk7JTkS3MS1S8Mq5yrQ==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", + "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", + "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } }, "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { "chalk": "^2.0.0", @@ -1188,685 +1964,701 @@ } }, "@babel/parser": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", - "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz", + "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", - "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz", - "integrity": "sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", + "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", - "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz", - "integrity": "sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.6.2.tgz", - "integrity": "sha512-NxHETdmpeSCtiatMRYWVJo7266rrvAC3DTeG5exQBIH/fMIUK7ejDNznBbn3HQl/o9peymRRg7Yqkx6PdUXmMw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", + "integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - } + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-dynamic-import": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", - "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", - "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", + "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz", - "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0" + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", - "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz", - "integrity": "sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-plugin-utils": "^7.8.3", "lodash": "^4.17.13" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } } }, "@babel/plugin-transform-classes": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz", - "integrity": "sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-define-map": "^7.5.5", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.5.5", - "@babel/helper-split-export-declaration": "^7.4.4", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz", + "integrity": "sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-define-map": "^7.8.3", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", "globals": "^11.1.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } } }, "@babel/plugin-transform-computed-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", - "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-destructuring": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", - "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz", + "integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.6.2.tgz", - "integrity": "sha512-KGKT9aqKV+9YMZSkowzYoYEiHqgaDhGmPNZlZxX6UeHC4z30nC1J9IrZuGqbYFB1jaIGdv91ujpze0exiVK8bA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - } + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz", - "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", - "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-for-of": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", - "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.3.tgz", + "integrity": "sha512-ZjXznLNTxhpf4Q5q3x1NsngzGA38t9naWH8Gt+0qYZEJAcvPI9waSStSh56u19Ofjr7QmD0wUsQ8hw8s/p1VnA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-function-name": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", - "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", + "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "dev": true + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } } }, "@babel/plugin-transform-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", - "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", - "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz", - "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz", + "integrity": "sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz", - "integrity": "sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz", + "integrity": "sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz", - "integrity": "sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz", + "integrity": "sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", - "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz", + "integrity": "sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.3.tgz", - "integrity": "sha512-jTkk7/uE6H2s5w6VlMHeWuH+Pcy2lmdwFoeWCVnvIrDUnB5gQqTVI8WfmEAhF2CDEarGrknZcmSFg1+bkfCoSw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", "dev": true, "requires": { - "regexpu-core": "^4.6.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - } + "@babel/helper-create-regexp-features-plugin": "^7.8.3" } }, "@babel/plugin-transform-new-target": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", - "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-object-super": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz", - "integrity": "sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.5.5" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" } }, "@babel/plugin-transform-parameters": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", - "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.3.tgz", + "integrity": "sha512-/pqngtGb54JwMBZ6S/D3XYylQDFtGjWrnoCF4gXZOUpFV/ujbxnoNGNvDGu6doFWRPBveE72qTx/RRU44j5I/Q==", "dev": true, "requires": { - "@babel/helper-call-delegate": "^7.4.4", - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-call-delegate": "^7.8.3", + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + }, + "dependencies": { + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/plugin-transform-property-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", - "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-regenerator": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", - "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz", + "integrity": "sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA==", "dev": true, "requires": { "regenerator-transform": "^0.14.0" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", - "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", + "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", - "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-spread": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz", - "integrity": "sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", - "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" } }, "@babel/plugin-transform-template-literals": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", - "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", - "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.3.tgz", + "integrity": "sha512-3TrkKd4LPqm4jHs6nPtSDI/SV9Cm5PRJkHLUgTcqRQQTMAZ44ZaAdDZJtvWFSaRcvT0a1rTmJ5ZA5tDKjleF3g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.6.2.tgz", - "integrity": "sha512-orZI6cWlR3nk2YmYdb0gImrgCUwb5cBUwjf6Ks6dvNVvXERkwtJWOQaEOjPiu0Gu1Tq6Yq/hruCZZOOi9F34Dw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", - "dev": true - }, - "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - } - } + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/preset-env": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.5.5.tgz", - "integrity": "sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A==", + "version": "7.7.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz", + "integrity": "sha512-k5hO17iF/Q7tR9Jv8PdNBZWYW6RofxhnxKjBMc0nG4JTaWvOTiPoO/RLFwAKcA4FpmuBFm6jkoqaRJLGi0zdaQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-module-imports": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-dynamic-import": "^7.5.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-dynamic-import": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.5.0", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.5.5", - "@babel/plugin-transform-classes": "^7.5.5", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.5.0", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/plugin-transform-duplicate-keys": "^7.5.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.4.4", - "@babel/plugin-transform-function-name": "^7.4.4", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-member-expression-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.5.0", - "@babel/plugin-transform-modules-commonjs": "^7.5.0", - "@babel/plugin-transform-modules-systemjs": "^7.5.0", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", - "@babel/plugin-transform-new-target": "^7.4.4", - "@babel/plugin-transform-object-super": "^7.5.5", - "@babel/plugin-transform-parameters": "^7.4.4", - "@babel/plugin-transform-property-literals": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.4.5", - "@babel/plugin-transform-reserved-words": "^7.2.0", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.4.4", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.5.5", + "@babel/plugin-proposal-async-generator-functions": "^7.7.4", + "@babel/plugin-proposal-dynamic-import": "^7.7.4", + "@babel/plugin-proposal-json-strings": "^7.7.4", + "@babel/plugin-proposal-object-rest-spread": "^7.7.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.7.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.7.4", + "@babel/plugin-syntax-async-generators": "^7.7.4", + "@babel/plugin-syntax-dynamic-import": "^7.7.4", + "@babel/plugin-syntax-json-strings": "^7.7.4", + "@babel/plugin-syntax-object-rest-spread": "^7.7.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.7.4", + "@babel/plugin-syntax-top-level-await": "^7.7.4", + "@babel/plugin-transform-arrow-functions": "^7.7.4", + "@babel/plugin-transform-async-to-generator": "^7.7.4", + "@babel/plugin-transform-block-scoped-functions": "^7.7.4", + "@babel/plugin-transform-block-scoping": "^7.7.4", + "@babel/plugin-transform-classes": "^7.7.4", + "@babel/plugin-transform-computed-properties": "^7.7.4", + "@babel/plugin-transform-destructuring": "^7.7.4", + "@babel/plugin-transform-dotall-regex": "^7.7.4", + "@babel/plugin-transform-duplicate-keys": "^7.7.4", + "@babel/plugin-transform-exponentiation-operator": "^7.7.4", + "@babel/plugin-transform-for-of": "^7.7.4", + "@babel/plugin-transform-function-name": "^7.7.4", + "@babel/plugin-transform-literals": "^7.7.4", + "@babel/plugin-transform-member-expression-literals": "^7.7.4", + "@babel/plugin-transform-modules-amd": "^7.7.5", + "@babel/plugin-transform-modules-commonjs": "^7.7.5", + "@babel/plugin-transform-modules-systemjs": "^7.7.4", + "@babel/plugin-transform-modules-umd": "^7.7.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", + "@babel/plugin-transform-new-target": "^7.7.4", + "@babel/plugin-transform-object-super": "^7.7.4", + "@babel/plugin-transform-parameters": "^7.7.4", + "@babel/plugin-transform-property-literals": "^7.7.4", + "@babel/plugin-transform-regenerator": "^7.7.5", + "@babel/plugin-transform-reserved-words": "^7.7.4", + "@babel/plugin-transform-shorthand-properties": "^7.7.4", + "@babel/plugin-transform-spread": "^7.7.4", + "@babel/plugin-transform-sticky-regex": "^7.7.4", + "@babel/plugin-transform-template-literals": "^7.7.4", + "@babel/plugin-transform-typeof-symbol": "^7.7.4", + "@babel/plugin-transform-unicode-regex": "^7.7.4", + "@babel/types": "^7.7.4", "browserslist": "^4.6.0", - "core-js-compat": "^3.1.1", + "core-js-compat": "^3.4.7", "invariant": "^2.2.2", "js-levenshtein": "^1.1.3", "semver": "^5.5.0" - }, - "dependencies": { - "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } } }, "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/traverse": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", - "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/types": "^7.4.4", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.11" + "lodash": "^4.17.13" }, "dependencies": { "debug": { @@ -1878,12 +2670,6 @@ "ms": "^2.1.1" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1893,28 +2679,26 @@ } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "dev": true, "requires": { "esutils": "^2.0.2", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@fortawesome/fontawesome-free": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.9.0.tgz", - "integrity": "sha512-g795BBEzM/Hq2SYNPm/NQTIp3IWd4eXSH0ds87Na2jnrAUFX3wkyZAI4Gwj9DOaWMuz2/01i8oWI7P7T/XLkhg==" + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.12.0.tgz", + "integrity": "sha512-vKDJUuE2GAdBERaQWmmtsciAMzjwNrROXA5KTGSZvayAsmuTGjam5z6QNqNPCwDfVljLWuov1nEC3mEQf/n6fQ==" + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true }, "@kubernetes/client-node": { "version": "0.10.3", @@ -2061,15 +2845,15 @@ } }, "@ngtools/webpack": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.3.8.tgz", - "integrity": "sha512-jLN4/Abue+Ro/K2SF0TpHOXnFHGuaHQ4aL6QG++moZXavBxRdc2E+PDjtuaMaS1llLHs5C5GX+Ve9ueEFhWoeQ==", + "version": "8.3.23", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.3.23.tgz", + "integrity": "sha512-+XekeThky6+Upped3hOwjHwYTsXJiDuCA5ZZLmGHkTxGzjB4ZHSlBaj75yTS+s+/Ab1WgdRo2P2BxOUS7oogtw==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.8", + "@angular-devkit/core": "8.3.23", "enhanced-resolve": "4.1.0", "rxjs": "6.4.0", - "tree-kill": "1.2.1", + "tree-kill": "1.2.2", "webpack-sources": "1.4.3" }, "dependencies": { @@ -2084,24 +2868,30 @@ } } }, + "@nguniversal/express-engine": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@nguniversal/express-engine/-/express-engine-8.2.6.tgz", + "integrity": "sha512-IKUKTpesgjYyB0Xg+fFhSbwbGBJhG0Wfn8MkQAi9RgSi8QsrSMkI3oUXc86Z7fpQL55D/ZIH7PekoC0Fmh/kxA==", + "dev": true + }, "@schematics/angular": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.8.tgz", - "integrity": "sha512-TOueA7gfQ5P7iDS593EBtLtqqgl6Pvg6snWqbMaW74VUxd5euAuPMPKkoJPddKF+cWjGYZgF09tp7G0kbuLGqw==", + "version": "8.3.23", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.23.tgz", + "integrity": "sha512-yisP1iCLGC4VnZNC3kOnYyTS5cmfKEnLM9bMzhZGMWwov9RRfdxKKeSnG9FJNwHxI0WjQ0UWwfiz1dj0YacG3g==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.8", - "@angular-devkit/schematics": "8.3.8" + "@angular-devkit/core": "8.3.23", + "@angular-devkit/schematics": "8.3.23" } }, "@schematics/update": { - "version": "0.803.8", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.8.tgz", - "integrity": "sha512-1gf59IbRPsOeoo997B+tHJ1YlCIHUCUqiYDCgxcoV+4EjYtCLNC/9cWIehZ9adqan9o6o1ma7MJznVNzvFyCSQ==", + "version": "0.803.23", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.23.tgz", + "integrity": "sha512-pLd5PseFTYF3VZ+IgMeNEFATQY5A80ylot7Dcg9FDeihqr5R9Rd1maCWIR43oKXvtK5C5+ackwR0QaPBAZ9bdw==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.8", - "@angular-devkit/schematics": "8.3.8", + "@angular-devkit/core": "8.3.23", + "@angular-devkit/schematics": "8.3.23", "@yarnpkg/lockfile": "1.1.0", "ini": "1.3.5", "pacote": "9.5.5", @@ -2138,9 +2928,12 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, "@types/chart.js": { - "version": "2.7.54", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.54.tgz", - "integrity": "sha512-BxIUR4mfk0zOqOPEu4gxLP5herra6INQLyFmgVE6JVRNNB+r36g2cd67nDUEEdD/EShZvaR33xausxOGv1+nbw==" + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.11.tgz", + "integrity": "sha512-xuDh5pZWci1Z5DUkiGTTLIBymxUe8KMfo1JYM5HTY7LXURSCej458uMrD4eYn4v+BTYTZfKlTRNIk8jW4nTaOg==", + "requires": { + "moment": "^2.10.2" + } }, "@types/events": { "version": "3.0.0", @@ -2166,9 +2959,9 @@ "dev": true }, "@types/jasminewd2": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.6.tgz", - "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz", + "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==", "dev": true, "requires": { "@types/jasmine": "*" @@ -2220,9 +3013,9 @@ } }, "@types/selenium-webdriver": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.14.tgz", - "integrity": "sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.16.tgz", + "integrity": "sha512-lMC2G0ItF2xv4UCiwbJGbnJlIuUixHrioOhNGHSCsYCJ8l4t9hMCUimCytvFv7qy6AfSzRxhRHoGa+UqaqwyeA==", "dev": true }, "@types/source-list-map": { @@ -2242,9 +3035,9 @@ "integrity": "sha512-SwbHKB2DPIDlvYqtK5O+0LFtZAyrUSw4c0q+HWwmH1Ve3KMQ0/5PlV3RX97+3dP7yMrnNQ8/bCWWvQpPl03Mug==" }, "@types/webpack-sources": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz", - "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.6.tgz", + "integrity": "sha512-FtAWR7wR5ocJ9+nP137DV81tveD/ZgB1sadnJ/axUGM3BUVfRPx8oQNMtv3JNfTeHx3VP7cXiyfR/jmtEsVHsQ==", "dev": true, "requires": { "@types/node": "*", @@ -2483,9 +3276,9 @@ } }, "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "dev": true }, "adm-zip": { @@ -2501,9 +3294,9 @@ "dev": true }, "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -2586,12 +3379,12 @@ "dev": true }, "ansi-escapes": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", - "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", "dev": true, "requires": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" } }, "ansi-html": { @@ -2803,14 +3596,6 @@ "dev": true, "requires": { "lodash": "^4.17.14" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } } }, "async-each": { @@ -2907,45 +3692,6 @@ } } }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, "babel-plugin-dynamic-import-node": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", @@ -2955,88 +3701,6 @@ "object.assign": "^4.1.0" } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, - "dependencies": { - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -3229,9 +3893,9 @@ } }, "bootstrap": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", - "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz", + "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==" }, "brace-expansion": { "version": "1.1.11", @@ -3349,29 +4013,29 @@ } }, "browserslist": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz", - "integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.3.tgz", + "integrity": "sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000984", - "electron-to-chromium": "^1.3.191", - "node-releases": "^1.1.25" + "caniuse-lite": "^1.0.30001017", + "electron-to-chromium": "^1.3.322", + "node-releases": "^1.1.44" } }, "browserstack": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", - "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz", + "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==", "dev": true, "requires": { "https-proxy-agent": "^2.2.1" } }, "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, "requires": { "base64-js": "^1.0.2", @@ -3472,15 +4136,15 @@ }, "dependencies": { "bluebird": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz", - "integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3583,9 +4247,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30000989", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz", - "integrity": "sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==", + "version": "1.0.30001019", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001019.tgz", + "integrity": "sha512-6ljkLtF1KM5fQ+5ZN0wuyVvvebJxgJPTmScOMaFuQN2QuOzvRJnWSKfzQskQU5IOU4Gap3zasYPIinzwUjoj/g==", "dev": true }, "canonical-path": { @@ -3628,9 +4292,9 @@ "dev": true }, "chart.js": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.8.0.tgz", - "integrity": "sha512-Di3wUL4BFvqI5FB5K26aQ+hvWh8wnP9A3DWGvXHVkO13D3DSnaSsdZx29cXlEsYKVkn1E2az+ZYFS4t0zi8x0w==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", + "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", "requires": { "chartjs-color": "^2.1.0", "moment": "^2.10.2" @@ -3661,24 +4325,119 @@ } }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } } }, "chownr": { @@ -3831,12 +4590,6 @@ "mimic-response": "^1.0.0" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -3844,14 +4597,14 @@ "dev": true }, "codelyzer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.1.2.tgz", - "integrity": "sha512-1z7mtpwxcz5uUqq0HLO0ifj/tz2dWEmeaK+8c5TEZXAwwVxrjjg0118ODCOCCOcpfYaaEHxStNCaWVYo9FUPXw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.1.tgz", + "integrity": "sha512-awBZXFcJUyC5HMYXiHzjr3D24tww2l1D1OqtfA9vUhEtYr32a65A+Gblm/OvsO+HuKLYzn8EDMw1inSM3VbxWA==", "dev": true, "requires": { "app-root-path": "^2.2.1", "aria-query": "^3.0.0", - "axobject-query": "^2.0.2", + "axobject-query": "2.0.2", "css-selector-tokenizer": "^0.7.1", "cssauron": "^1.4.0", "damerau-levenshtein": "^1.0.4", @@ -3934,9 +4687,9 @@ "dev": true }, "compare-versions": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.0.tgz", - "integrity": "sha512-hX+4kt2Rcwu+x1U0SsEFCn1quURjEjPEGH/cPBlpME/IidGimAdwfMU+B+xDr7et/KTR7VH2+ZqWGerv4NGs2w==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz", + "integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==", "dev": true }, "component-bind": { @@ -3958,18 +4711,18 @@ "dev": true }, "compressible": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", - "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "requires": { - "mime-db": ">= 1.40.0 < 2" + "mime-db": ">= 1.43.0 < 2" }, "dependencies": { "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", "dev": true } } @@ -4048,13 +4801,10 @@ "dev": true }, "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true }, "constants-browserify": { "version": "1.0.0", @@ -4078,9 +4828,9 @@ "dev": true }, "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -4119,12 +4869,12 @@ "dev": true }, "copy-webpack-plugin": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz", - "integrity": "sha512-YBuYGpSzoCHSSDGyHy6VJ7SHojKp6WHT4D7ItcQFNAYx2hrwkMe56e97xfVR0/ovDuMTrMffXUiltvQljtAGeg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", "dev": true, "requires": { - "cacache": "^11.3.3", + "cacache": "^12.0.3", "find-cache-dir": "^2.1.0", "glob-parent": "^3.1.0", "globby": "^7.1.1", @@ -4132,22 +4882,22 @@ "loader-utils": "^1.2.3", "minimatch": "^3.0.4", "normalize-path": "^3.0.0", - "p-limit": "^2.2.0", + "p-limit": "^2.2.1", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", + "serialize-javascript": "^2.1.2", "webpack-log": "^2.0.0" }, "dependencies": { "bluebird": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz", - "integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "cacache": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz", - "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -4155,6 +4905,7 @@ "figgy-pudding": "^3.5.1", "glob": "^7.1.4", "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "mkdirp": "^0.5.1", @@ -4178,9 +4929,9 @@ } }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -4224,24 +4975,24 @@ } }, "core-js": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", - "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" }, "core-js-compat": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.2.1.tgz", - "integrity": "sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", + "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", "dev": true, "requires": { - "browserslist": "^4.6.6", - "semver": "^6.3.0" + "browserslist": "^4.8.3", + "semver": "7.0.0" }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", "dev": true } } @@ -4263,6 +5014,49 @@ "parse-json": "^4.0.0" } }, + "coverage-istanbul-loader": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-2.0.3.tgz", + "integrity": "sha512-LiGRvyIuzVYs3M1ZYK1tF0HekjH0DJ8zFdUwAZq378EJzqOgToyb1690dp3TAUlP6Y+82uu42LRjuROVeJ54CA==", + "dev": true, + "requires": { + "convert-source-map": "^1.7.0", + "istanbul-lib-instrument": "^4.0.0", + "loader-utils": "^1.2.3", + "merge-source-map": "^1.1.0", + "schema-utils": "^2.6.1" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "schema-utils": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", + "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -4347,6 +5141,40 @@ "cssesc": "^0.1.0", "fastparse": "^1.1.1", "regexpu-core": "^1.0.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } } }, "cssauron": { @@ -4396,12 +5224,6 @@ "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", "dev": true }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4437,9 +5259,9 @@ } }, "deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.0.tgz", - "integrity": "sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", "dev": true, "requires": { "is-arguments": "^1.0.4", @@ -4467,14 +5289,6 @@ "dev": true, "requires": { "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } } }, "define-properties": { @@ -4563,30 +5377,6 @@ } } }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -4613,9 +5403,9 @@ "dev": true }, "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -4628,15 +5418,6 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "detect-node": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", @@ -4761,15 +5542,15 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.279", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.279.tgz", - "integrity": "sha512-iiBT/LeUWKnhd7d/n4IZsx/NIacs7gjFgAT1q5/i0POiS+5d0rVnbbyCRMmsBW7vaQJOUhWyh4PsyIVZb/Ax5Q==", + "version": "1.3.342", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.342.tgz", + "integrity": "sha512-An/MLhGLIG/g7lZ5vqs4lar96zv74agd3ZcADDHLpjAa16T7Y/pO/33Q31JOwpmHeyjithtHtUcn7XLuaz78lw==", "dev": true }, "elliptic": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", - "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -4926,27 +5707,28 @@ } }, "es-abstract": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.15.0.tgz", - "integrity": "sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -4955,9 +5737,9 @@ } }, "es6-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", - "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { @@ -5025,9 +5807,9 @@ "dev": true }, "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", "dev": true }, "eventsource": { @@ -5273,18 +6055,18 @@ } }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", "dev": true }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "dev": true, "requires": { - "mime-db": "1.40.0" + "mime-db": "1.43.0" } }, "negotiator": { @@ -5497,9 +6279,9 @@ "dev": true }, "figures": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.0.0.tgz", - "integrity": "sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", + "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -5516,21 +6298,27 @@ }, "dependencies": { "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, "schema-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.4.1.tgz", - "integrity": "sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", + "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -6484,9 +7272,9 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -6570,9 +7358,9 @@ "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, "has-to-string-tag-x": { @@ -6760,12 +7548,12 @@ "dev": true }, "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { - "agent-base": "^4.1.0", + "agent-base": "^4.3.0", "debug": "^3.1.0" }, "dependencies": { @@ -6779,9 +7567,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -6942,9 +7730,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "is-fullwidth-code-point": { @@ -6953,21 +7741,26 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "string-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", - "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "strip-ansi": { @@ -6977,6 +7770,14 @@ "dev": true, "requires": { "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } } } @@ -7099,9 +7900,9 @@ "dev": true }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, "is-data-descriptor": { @@ -7125,9 +7926,9 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, "is-descriptor": { @@ -7167,15 +7968,6 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -7217,27 +8009,27 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" }, "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "^2.1.0" } }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "^1.0.2" } }, "is-plain-obj": { @@ -7261,12 +8053,12 @@ "dev": true }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, "requires": { - "has": "^1.0.1" + "has": "^1.0.3" } }, "is-retry-allowed": { @@ -7280,12 +8072,12 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "is-typedarray": { @@ -7361,21 +8153,6 @@ "once": "^1.4.0" }, "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "istanbul-lib-coverage": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", @@ -7397,99 +8174,18 @@ "semver": "^6.0.0" } }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, "semver": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", - "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==", - "dev": true - } - } - }, - "istanbul-instrumenter-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", - "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", - "dev": true, - "requires": { - "convert-source-map": "^1.5.0", - "istanbul-lib-instrument": "^1.7.3", - "loader-utils": "^1.1.0", - "schema-utils": "^0.3.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "^5.0.0" - } } } }, "istanbul-lib-coverage": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", - "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, "istanbul-lib-hook": { @@ -7502,18 +8198,26 @@ } }, "istanbul-lib-instrument": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", - "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz", + "integrity": "sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.1", - "semver": "^5.3.0" + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "istanbul-lib-report": { @@ -7532,28 +8236,6 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true } } }, @@ -7585,34 +8267,12 @@ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7800,56 +8460,15 @@ } }, "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.2.tgz", + "integrity": "sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==", "dev": true, "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", + "lie": "~3.3.0", "pako": "~1.0.2", - "readable-stream": "~2.0.6" - }, - "dependencies": { - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" } }, "karma": { @@ -7888,17 +8507,49 @@ "useragent": "2.3.0" }, "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, "mime": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true } } }, @@ -7913,12 +8564,12 @@ } }, "karma-coverage-istanbul-reporter": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.5.tgz", - "integrity": "sha512-yPvAlKtY3y+rKKWbOo0CzBMVTvJEeMOgbMXuVv3yWvS8YtYKC98AU9vFF0mVBZ2RP1E9SgS90+PT6Kf14P3S4w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz", + "integrity": "sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A==", "dev": true, "requires": { - "istanbul-api": "^2.1.1", + "istanbul-api": "^2.1.6", "minimatch": "^3.0.4" } }, @@ -8031,9 +8682,9 @@ } }, "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "requires": { "immediate": "~3.0.5" @@ -8067,9 +8718,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash-es": { "version": "4.17.15", @@ -8082,12 +8733,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, "log4js": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", @@ -8119,9 +8764,9 @@ } }, "loglevel": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.4.tgz", - "integrity": "sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", + "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==", "dev": true }, "long": { @@ -8193,16 +8838,16 @@ "dev": true }, "make-fetch-happen": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz", - "integrity": "sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", "dev": true, "requires": { "agentkeepalive": "^3.4.1", "cacache": "^12.0.0", "http-cache-semantics": "^3.8.1", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", + "https-proxy-agent": "^2.2.3", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "node-fetch-npm": "^2.0.2", @@ -8307,6 +8952,23 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -8503,9 +9165,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -8641,10 +9303,18 @@ "resolved": "https://registry.npmjs.org/ng2-completer/-/ng2-completer-2.0.8.tgz", "integrity": "sha512-WzxJ4u3vAHsfBUaFCloEBoirPZrnDabtWEKyDok7dtjhS1ZvcbwQ4asdXuDO0hZ0T1QC66U/PwLhKfkG501hVg==" }, + "ngx-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ngx-cookie/-/ngx-cookie-4.1.2.tgz", + "integrity": "sha512-BU3q+116mSQZvf8WsnKDxyWFy10LtxSvZz1YIjD7pmaSFpiKdWmHTHn0qLgm3OoIL9TfInQ7Ij46rKJWPD+4Kw==", + "requires": { + "tslib": "^1.9.0" + } + }, "ngx-toastr": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-10.0.4.tgz", - "integrity": "sha512-iN+zr2Msae5wV334c1dytRhSYNdUz467jwv1NE91lMmllsMkpUzZlu8VdFCeTFt+/R4TWzz19xBRqhpp+OAuVA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-10.2.0.tgz", + "integrity": "sha512-6ASr5bcvQmtNKb4D2VEsQjCXyROq6GwberBWO0bVt+xcBYPUea4aRTgX8in9apX9buaTafzG+h3HlnIraspoPg==", "requires": { "tslib": "^1.9.0" } @@ -8736,9 +9406,9 @@ } }, "node-releases": { - "version": "1.1.35", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.35.tgz", - "integrity": "sha512-JGcM/wndCN/2elJlU0IGdVEJQQnJwsLbgPCFd2pY7V0mxf17bZ0Gb/lgOtL29ZQhvEX5shnVhxQyZz3ex94N8w==", + "version": "1.1.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.47.tgz", + "integrity": "sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA==", "dev": true, "requires": { "semver": "^6.3.0" @@ -8765,9 +9435,9 @@ }, "dependencies": { "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -8801,9 +9471,18 @@ } }, "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true }, "npm-package-arg": { @@ -8819,13 +9498,14 @@ } }, "npm-packlist": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.6.tgz", - "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==", + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npm-pick-manifest": { @@ -8956,15 +9636,15 @@ "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" }, "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", "dev": true }, "object-is": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", "dev": true }, "object-keys": { @@ -8995,13 +9675,13 @@ } }, "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "object.pick": { @@ -9201,9 +9881,9 @@ "dev": true }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -9335,9 +10015,9 @@ } }, "pako": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", - "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, "parallel-transform": { @@ -9484,6 +10164,12 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -9514,20 +10200,29 @@ } }, "portfinder": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.24.tgz", - "integrity": "sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg==", + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", "dev": true, "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" }, "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -9671,9 +10366,9 @@ } }, "protractor": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz", - "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.3.tgz", + "integrity": "sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA==", "dev": true, "requires": { "@types/q": "^0.0.32", @@ -9741,6 +10436,30 @@ "pinkie-promise": "^2.0.0" } }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -9775,9 +10494,9 @@ "dev": true }, "webdriver-manager": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz", - "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==", + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", "dev": true, "requires": { "adm-zip": "^0.4.9", @@ -9967,21 +10686,27 @@ }, "dependencies": { "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, "schema-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.4.1.tgz", - "integrity": "sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", + "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -10008,16 +10733,16 @@ } }, "read-package-json": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.0.tgz", - "integrity": "sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", + "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", "dev": true, "requires": { "glob": "^7.1.1", "graceful-fs": "^4.1.2", "json-parse-better-errors": "^1.0.1", "normalize-package-data": "^2.0.0", - "slash": "^1.0.0" + "npm-normalize-package-bin": "^1.0.0" } }, "read-package-tree": { @@ -10123,35 +10848,39 @@ } }, "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", "dev": true, "requires": { - "define-properties": "^1.1.2" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", + "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.1.0", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" } }, "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", "dev": true }, "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz", + "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -10183,15 +10912,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -10552,9 +11272,9 @@ } }, "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, "serve-index": { @@ -10598,10 +11318,16 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -10687,9 +11413,9 @@ "dev": true }, "smart-buffer": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", - "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", "dev": true }, "snapdragon": { @@ -10908,9 +11634,9 @@ } }, "sockjs-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", - "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", "dev": true, "requires": { "debug": "^3.2.5", @@ -10948,13 +11674,13 @@ } }, "socks": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", - "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", "dev": true, "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" + "ip": "1.1.5", + "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { @@ -10965,6 +11691,17 @@ "requires": { "agent-base": "~4.2.1", "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } } }, "sort-keys": { @@ -11035,9 +11772,9 @@ "dev": true }, "sourcemap-codec": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", - "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.7.tgz", + "integrity": "sha512-RuN23NzhAOuUtaivhcrjXx1OPXsFeH9m5sI373/U7+tGLKihjUyboZAzOadytMjnqHp1f45RGk1IzDKCpDpSYA==", "dev": true }, "spdx-correct": { @@ -11132,9 +11869,9 @@ "dev": true }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -11253,9 +11990,9 @@ } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, "streamroller": { @@ -11320,9 +12057,9 @@ } }, "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", "dev": true, "requires": { "define-properties": "^1.1.3", @@ -11330,9 +12067,9 @@ } }, "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", "dev": true, "requires": { "define-properties": "^1.1.3", @@ -11356,6 +12093,12 @@ "ansi-regex": "^2.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -11372,21 +12115,27 @@ }, "dependencies": { "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, "schema-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.4.1.tgz", - "integrity": "sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", + "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -11490,9 +12239,9 @@ } }, "terser": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.1.4.tgz", - "integrity": "sha512-+ZwXJvdSwbd60jG0Illav0F06GDJF0R4ydZ21Q3wGAFKoBGyJGo34F63vzJHgvYxc1ukOtIjvwEvl9MkjzM6Pg==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz", + "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==", "dev": true, "requires": { "commander": "^2.20.0", @@ -11501,9 +12250,9 @@ }, "dependencies": { "commander": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", - "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "source-map": { @@ -11513,9 +12262,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -11525,16 +12274,16 @@ } }, "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", @@ -11577,9 +12326,9 @@ } }, "thunky": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", - "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, "timed-out": { @@ -11688,15 +12437,9 @@ } }, "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "ts-node": { @@ -11788,9 +12531,9 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, "type-is": { @@ -11816,20 +12559,20 @@ "dev": true }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.4.tgz", + "integrity": "sha512-tinYWE8X1QfCHxS1lBS8yiDekyhSXOO6R66yNOCdUJeojxxw+PX2BHAz/BWyW7PQ7pkiWVxJfIEbiDxyLWvUGg==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.0", + "commander": "~2.20.3", "source-map": "~0.6.1" }, "dependencies": { "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "optional": true }, @@ -11882,38 +12625,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-filename": { @@ -12009,9 +12729,9 @@ } }, "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, "uri-js": { @@ -12155,9 +12875,9 @@ } }, "vm-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", - "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, "void-elements": { @@ -12175,6 +12895,34 @@ "chokidar": "^2.0.2", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0" + }, + "dependencies": { + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } } }, "wbuf": { @@ -12228,16 +12976,22 @@ }, "dependencies": { "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true } } }, @@ -12269,13 +13023,14 @@ } }, "webpack-dev-middleware": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz", - "integrity": "sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", "dev": true, "requires": { "memory-fs": "^0.4.1", - "mime": "^2.4.2", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", "range-parser": "^1.2.1", "webpack-log": "^2.0.0" }, @@ -12295,41 +13050,41 @@ } }, "webpack-dev-server": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.0.tgz", - "integrity": "sha512-Hs8K9yI6pyMvGkaPTeTonhD6JXVsigXDApYk9JLW4M7viVBspQvb1WdAcWxqtmttxNW4zf2UFLsLNe0y87pIGQ==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", + "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", "dev": true, "requires": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", - "chokidar": "^2.1.6", + "chokidar": "^2.1.8", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", "debug": "^4.1.1", "del": "^4.1.1", "express": "^4.17.1", "html-entities": "^1.2.1", - "http-proxy-middleware": "^0.19.1", + "http-proxy-middleware": "0.19.1", "import-local": "^2.0.0", "internal-ip": "^4.3.0", "ip": "^1.1.5", - "is-absolute-url": "^3.0.0", + "is-absolute-url": "^3.0.3", "killable": "^1.0.1", - "loglevel": "^1.6.3", + "loglevel": "^1.6.4", "opn": "^5.5.0", "p-retry": "^3.0.1", - "portfinder": "^1.0.21", + "portfinder": "^1.0.25", "schema-utils": "^1.0.0", - "selfsigned": "^1.10.4", + "selfsigned": "^1.10.7", "semver": "^6.3.0", "serve-index": "^1.9.1", "sockjs": "0.3.19", - "sockjs-client": "1.3.0", + "sockjs-client": "1.4.0", "spdy": "^4.0.1", "strip-ansi": "^3.0.1", "supports-color": "^6.1.0", "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.0", + "webpack-dev-middleware": "^3.7.2", "webpack-log": "^2.0.0", "ws": "^6.2.1", "yargs": "12.0.5" @@ -12382,12 +13137,6 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", @@ -12549,13 +13298,13 @@ } }, "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", "dev": true, "requires": { "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "xmlbuilder": "~11.0.0" }, "dependencies": { "sax": { @@ -12567,9 +13316,9 @@ } }, "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true }, "xmlhttprequest-ssl": { diff --git a/dashboard/webapp-frontend/package.json b/dashboard/webapp-frontend/package.json index 9f2cc86e..840ef049 100644 --- a/dashboard/webapp-frontend/package.json +++ b/dashboard/webapp-frontend/package.json @@ -11,54 +11,56 @@ }, "private": true, "dependencies": { - "@angular/animations": "^8.2.9", + "@angular/animations": "^8.2.14", "@angular/cdk": "^7.3.7", - "@angular/common": "^8.2.9", - "@angular/compiler": "^8.2.9", - "@angular/core": "^8.2.9", - "@angular/flex-layout": "^7.0.0-beta.19", - "@angular/forms": "^8.2.9", + "@angular/common": "^8.2.14", + "@angular/compiler": "^8.2.14", + "@angular/core": "^8.2.14", + "@angular/flex-layout": "^7.0.0-beta.24", + "@angular/forms": "^8.2.14", "@angular/material": "~7.2.0", - "@angular/platform-browser": "^8.2.9", - "@angular/platform-browser-dynamic": "^8.2.9", - "@angular/router": "^8.2.9", - "@fortawesome/fontawesome-free": "^5.9.0", + "@angular/platform-browser": "^8.2.14", + "@angular/platform-browser-dynamic": "^8.2.14", + "@angular/router": "^8.2.14", + "@fortawesome/fontawesome-free": "^5.12.0", "@kubernetes/client-node": "^0.10.3", "@material/radio": "^2.3.0", - "@types/chart.js": "^2.7.54", + "@types/chart.js": "^2.9.11", "angular-bootstrap-md": "^7.5.4", "angular6-json-schema-form": "^7.3.0", - "bootstrap": "^4.3.1", - "chart.js": "^2.8.0", - "core-js": "^2.6.9", + "bootstrap": "^4.4.1", + "chart.js": "^2.9.3", + "core-js": "^2.6.11", "hammerjs": "^2.0.8", "lodash-es": "^4.17.15", "material-design-icons": "^3.0.1", "ng2-charts": "^1.6.0", "ng2-completer": "^2.0.8", - "ngx-toastr": "^10.0.4", + "ngx-cookie": "^4.1.2", + "ngx-toastr": "^10.2.0", "rxjs": "6.5.3", "rxjs-compat": "6.3.3", "tslib": "^1.10.0", "zone.js": "~0.9.1" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.803.8", - "@angular/cli": "^8.3.8", - "@angular/compiler-cli": "^8.2.9", - "@angular/language-service": "^8.2.9", + "@angular-devkit/build-angular": "^0.803.23", + "@angular/cli": "^8.3.23", + "@angular/compiler-cli": "^8.2.14", + "@angular/language-service": "^8.2.14", + "@nguniversal/express-engine": "^8.2.6", "@types/jasmine": "^2.8.16", - "@types/jasminewd2": "~2.0.3", + "@types/jasminewd2": "^2.0.8", "@types/node": "~8.9.4", - "codelyzer": "^5.0.1", + "codelyzer": "^5.2.1", "jasmine-core": "~2.99.1", "jasmine-spec-reporter": "~4.2.1", "karma": "~3.1.1", "karma-chrome-launcher": "~2.2.0", - "karma-coverage-istanbul-reporter": "^2.0.5", + "karma-coverage-istanbul-reporter": "^2.1.1", "karma-jasmine": "~1.1.2", "karma-jasmine-html-reporter": "^0.2.2", - "protractor": "~5.4.0", + "protractor": "^5.4.3", "ts-node": "~7.0.0", "tslint": "~5.11.0", "typescript": "~3.5.3" diff --git a/dashboard/webapp-frontend/pom.xml b/dashboard/webapp-frontend/pom.xml index 2888f73b..e316a6cf 100644 --- a/dashboard/webapp-frontend/pom.xml +++ b/dashboard/webapp-frontend/pom.xml @@ -25,7 +25,7 @@ limitations under the License. org.o-ran-sc.nonrt.ric-dashboard ric-dash-parent - 1.0.0-SNAPSHOT + 1.0.1-SNAPSHOT ric-dash-fe NonRT RIC Dashboard Webapp frontend diff --git a/dashboard/webapp-frontend/src/app/footer/footer.component.scss b/dashboard/webapp-frontend/src/app/footer/footer.component.scss index c06dc010..fb87ba5f 100644 --- a/dashboard/webapp-frontend/src/app/footer/footer.component.scss +++ b/dashboard/webapp-frontend/src/app/footer/footer.component.scss @@ -21,6 +21,7 @@ color: gray; letter-spacing: 0.1rem; font-size: 10px; + margin-left: 10px; } .copyright__text-dark { diff --git a/dashboard/webapp-frontend/src/app/interfaces/policy.types.ts b/dashboard/webapp-frontend/src/app/interfaces/policy.types.ts index e694bb6e..b6ecce62 100644 --- a/dashboard/webapp-frontend/src/app/interfaces/policy.types.ts +++ b/dashboard/webapp-frontend/src/app/interfaces/policy.types.ts @@ -21,15 +21,17 @@ // Models of data used by the Policy Control export interface PolicyType { - policy_type_id: number; name: string; schema: string; schemaObject: any; } export interface PolicyInstance { - instanceId: string; - instance: string; + id: string; + json: string; + ric: string; + service: string; + lastModified: string; } export interface PolicyInstanceAck { diff --git a/dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html b/dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html index 776d503e..d447c70d 100644 --- a/dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html +++ b/dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html @@ -36,7 +36,19 @@
{{jsonSchemaObject.description}}
+ + + + + Select RIC + + + {{ric}} + + + +

{{isVisible.form ? 'expand_less' : 'expand_more'}} Properties @@ -51,8 +63,8 @@


- +

{ this.darkMode = isDark; }); + if (!this.policyInstanceId) { + this.fetchRics(); + } } ngAfterViewInit() { @@ -115,7 +133,7 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { } const policyJson: string = this.prettyLiveFormData; const self: PolicyInstanceDialogComponent = this; - this.dataService.putPolicy(this.policyTypeId, this.policyInstanceId, policyJson).subscribe( + this.dataService.putPolicy(this.policyTypeName, this.policyInstanceId, policyJson, this.ric).subscribe( { next(value) { self.notificationService.success('Policy ' + self.policyTypeName + ':' + self.policyInstanceId + ' submitted'); @@ -194,11 +212,11 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit { } export function getPolicyDialogProperties(policyType: PolicyType, instance: PolicyInstance, darkMode: boolean): MatDialogConfig { - const policyTypeId = policyType.policy_type_id; const createSchema = policyType.schemaObject; - const instanceId = instance ? instance.instanceId : null; - const instanceJson = instance ? instance.instance : null; + const instanceId = instance ? instance.id : null; + const instanceJson = instance ? instance.json : null; const name = policyType.name; + const ric = instance ? instance.ric : null; return { maxWidth: '1200px', maxHeight: '900px', @@ -207,11 +225,11 @@ export function getPolicyDialogProperties(policyType: PolicyType, instance: Poli disableClose: false, panelClass: darkMode ? 'dark-theme' : '', data: { - policyTypeId, createSchema, instanceId, instanceJson, - name + name, + ric } }; } diff --git a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.html b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.html index e1a67dd5..8c305e41 100644 --- a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.html +++ b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.html @@ -22,7 +22,25 @@ Instance - {{element.instanceId}} + {{element.id}} + + + + + Ric + {{element.ric}} + + + + + Owner + {{element.service}} + + + + + Last modified + {{toLocalTime(element.lastModified)}} @@ -42,9 +60,10 @@ No records found. - + - + diff --git a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.scss b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.scss index b6a29a4a..6a18c342 100644 --- a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.scss +++ b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.scss @@ -19,13 +19,21 @@ */ .instances-table { - width: 60%; - min-width: 600px; + width: 90%; + min-width: 1200px; margin-top: 10px; margin-bottom: 10px; background-color: grayscale($color: #eeeaea); } +.mat-column-instanceId { + word-wrap: break-word; + white-space: unset; + flex: 0 0 28%; + width: 28%; +} + + .table-dark { background-color: #2d2d3d; } diff --git a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.ts b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.ts index b62deb4f..ecf6f36c 100644 --- a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.ts +++ b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.ts @@ -76,9 +76,9 @@ export class PolicyInstanceComponent implements OnInit, AfterViewInit { } modifyInstance(instance: PolicyInstance): void { - this.policySvc.getPolicy(this.policyType.policy_type_id, instance.instanceId).subscribe( + this.policySvc.getPolicy(this.policyType.name, instance.id).subscribe( (refreshedJson: any) => { - instance.instance = JSON.stringify(refreshedJson); + instance.json = JSON.stringify(refreshedJson); this.dialog.open(PolicyInstanceDialogComponent, getPolicyDialogProperties(this.policyType, instance, this.darkMode)); }, (httpError: HttpErrorResponse) => { @@ -92,13 +92,20 @@ export class PolicyInstanceComponent implements OnInit, AfterViewInit { return this.instanceDataSource.rowCount > 0; } + toLocalTime(utcTime: string): string { + const date = new Date(utcTime); + const toutc = date.toUTCString(); + return new Date(toutc + " UTC").toLocaleString(); + + } + deleteInstance(instance: PolicyInstance): void { this.confirmDialogService .openConfirmDialog('Are you sure you want to delete this policy instance?') .afterClosed().subscribe( (res: any) => { if (res) { - this.policySvc.deletePolicy(this.policyType.policy_type_id, instance.instanceId) + this.policySvc.deletePolicy(this.policyType.name, instance.id) .subscribe( (response: HttpResponse) => { switch (response.status) { diff --git a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.datasource.ts b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.datasource.ts index 53a6239e..b82ee753 100644 --- a/dashboard/webapp-frontend/src/app/policy-control/policy-instance.datasource.ts +++ b/dashboard/webapp-frontend/src/app/policy-control/policy-instance.datasource.ts @@ -51,7 +51,7 @@ export class PolicyInstanceDataSource extends DataSource { loadTable() { this.loadingSubject.next(true); - this.policySvc.getPolicyInstances(this.policyType.policy_type_id) + this.policySvc.getPolicyInstances(this.policyType.name) .pipe( catchError((her: HttpErrorResponse) => { this.notificationService.error('Failed to get policy instances: ' + her.message); @@ -88,7 +88,10 @@ export class PolicyInstanceDataSource extends DataSource { return data.sort((a, b) => { const isAsc = this.sort.direction === 'asc'; switch (this.sort.active) { - case 'instanceId': return compare(a.instanceId, b.instanceId, isAsc); + case 'instanceId': return compare(a.id, b.id, isAsc); + case 'ric': return compare(a.ric, b.ric, isAsc); + case 'service': return compare(a.service, b.service, isAsc); + case 'lastModified': return compare(a.lastModified, b.lastModified, isAsc) default: return 0; } }); diff --git a/dashboard/webapp-frontend/src/app/policy-control/policy-type.datasource.ts b/dashboard/webapp-frontend/src/app/policy-control/policy-type.datasource.ts index 8d9dec75..97d792e8 100644 --- a/dashboard/webapp-frontend/src/app/policy-control/policy-type.datasource.ts +++ b/dashboard/webapp-frontend/src/app/policy-control/policy-type.datasource.ts @@ -60,7 +60,12 @@ export class PolicyTypeDataSource extends DataSource { this.rowCount = types.length; for (let i = 0; i < types.length; i++) { const policyType = types[i]; - policyType.schemaObject = JSON.parse(policyType.schema); + try { + policyType.schemaObject = JSON.parse(policyType.schema); + } catch (jsonError) { + console.error('Could not parse schema: ' + policyType.schema); + policyType.schemaObject = { description: 'Incorrect schema: ' + jsonError }; + } } this.policyTypeSubject.next(types); }); diff --git a/dashboard/webapp-frontend/src/app/rd-routing.module.ts b/dashboard/webapp-frontend/src/app/rd-routing.module.ts index 520f9a53..57b9d57c 100644 --- a/dashboard/webapp-frontend/src/app/rd-routing.module.ts +++ b/dashboard/webapp-frontend/src/app/rd-routing.module.ts @@ -22,20 +22,20 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Routes, RouterModule } from '@angular/router'; import { MainComponent } from './main/main.component'; -import { PolicyControlComponent} from './policy-control/policy-control.component'; +import { PolicyControlComponent } from './policy-control/policy-control.component'; const routes: Routes = [ - {path: '', component: MainComponent}, - {path: 'policy', component: PolicyControlComponent} + { path: '', component: MainComponent }, + { path: 'policy', component: PolicyControlComponent } ]; @NgModule({ - imports: [ - CommonModule, - RouterModule.forRoot(routes)], - exports: [ - RouterModule + imports: [ + CommonModule, + RouterModule.forRoot(routes)], + exports: [ + RouterModule ], declarations: [] }) diff --git a/dashboard/webapp-frontend/src/app/rd.component.ts b/dashboard/webapp-frontend/src/app/rd.component.ts index f7acff84..a6078a07 100644 --- a/dashboard/webapp-frontend/src/app/rd.component.ts +++ b/dashboard/webapp-frontend/src/app/rd.component.ts @@ -19,6 +19,7 @@ */ import { Component, OnInit } from '@angular/core'; import { UiService } from './services/ui/ui.service'; +import { CookieService } from 'ngx-cookie'; @Component({ selector: 'rd-root', @@ -26,13 +27,19 @@ import { UiService } from './services/ui/ui.service'; styleUrls: ['./rd.component.scss'] }) export class RdComponent implements OnInit { - showMenu = false; - darkMode: boolean; + private showMenu = false; + private darkMode: boolean; + private 'DARK_MODE_COOKIE' = 'darkMode'; - constructor(private ui: UiService) { + constructor(private cookieService: CookieService, private ui: UiService) { } ngOnInit() { + const dark = this.cookieService.get(this.DARK_MODE_COOKIE); + if (dark) { + this.ui.darkModeState.next(dark === 'yes'); + } + this.ui.darkModeState.subscribe((value) => { this.darkMode = value; }); @@ -44,6 +51,7 @@ export class RdComponent implements OnInit { modeToggleSwitch() { this.ui.darkModeState.next(!this.darkMode); + this.cookieService.put(this.DARK_MODE_COOKIE, this.darkMode ? 'yes' : 'no'); } } diff --git a/dashboard/webapp-frontend/src/app/rd.module.ts b/dashboard/webapp-frontend/src/app/rd.module.ts index 3ba8a408..d44ffe54 100644 --- a/dashboard/webapp-frontend/src/app/rd.module.ts +++ b/dashboard/webapp-frontend/src/app/rd.module.ts @@ -20,13 +20,15 @@ */ import { BrowserModule } from '@angular/platform-browser'; // tslint:disable-next-line:max-line-length -import {MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, - MatDialogModule, MatExpansionModule, MatFormFieldModule, MatGridListModule, - MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatPaginatorModule, - MatProgressSpinnerModule, MatSelectModule, MatSidenavModule, MatSliderModule, - MatSlideToggleModule, MatSnackBarModule, MatSortModule, MatTableModule, - MatTabsModule, MatToolbarModule} from '@angular/material'; -import { BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import { + MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, + MatDialogModule, MatExpansionModule, MatFormFieldModule, MatGridListModule, + MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatPaginatorModule, + MatProgressSpinnerModule, MatSelectModule, MatSidenavModule, MatSliderModule, + MatSlideToggleModule, MatSnackBarModule, MatSortModule, MatTableModule, + MatTabsModule, MatToolbarModule +} from '@angular/material'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; import { MatRadioModule } from '@angular/material/radio'; @@ -55,9 +57,10 @@ import { RdComponent } from './rd.component'; import { RdRoutingModule } from './rd-routing.module'; import { SidenavListComponent } from './navigation/sidenav-list/sidenav-list.component'; import { UiService } from './services/ui/ui.service'; +import { CookieModule } from 'ngx-cookie'; @NgModule({ - declarations: [ + declarations: [ ConfirmDialogComponent, ErrorDialogComponent, FooterComponent, @@ -69,7 +72,7 @@ import { UiService } from './services/ui/ui.service'; PolicyInstanceComponent, PolicyInstanceDialogComponent, RdComponent, - SidenavListComponent, + SidenavListComponent, ], imports: [ BrowserModule, @@ -107,7 +110,8 @@ import { UiService } from './services/ui/ui.service'; MDBBootstrapModule.forRoot(), RdRoutingModule, ReactiveFormsModule, - ToastrModule.forRoot() + ToastrModule.forRoot(), + CookieModule.forRoot(), ], exports: [ ErrorDialogComponent, @@ -127,8 +131,8 @@ import { UiService } from './services/ui/ui.service'; MatSlideToggleModule, MatTabsModule ], - entryComponents: [ - ConfirmDialogComponent, + entryComponents: [ + ConfirmDialogComponent, ErrorDialogComponent, LoadingDialogComponent, PolicyInstanceDialogComponent diff --git a/dashboard/webapp-frontend/src/app/services/policy/policy.service.ts b/dashboard/webapp-frontend/src/app/services/policy/policy.service.ts index 1c870818..6af517f4 100644 --- a/dashboard/webapp-frontend/src/app/services/policy/policy.service.ts +++ b/dashboard/webapp-frontend/src/app/services/policy/policy.service.ts @@ -66,7 +66,7 @@ export class PolicyService { return this.httpClient.get(url); } - getPolicyInstances(policyTypeId: number): Observable { + getPolicyInstances(policyTypeId: string): Observable { const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath); return this.httpClient.get(url); } @@ -75,7 +75,7 @@ export class PolicyService { * Gets policy parameters. * @returns Observable that should yield a policy instance */ - getPolicy(policyTypeId: number, policyInstanceId: string): Observable { + getPolicy(policyTypeId: string, policyInstanceId: string): Observable { const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId); return this.httpClient.get(url); } @@ -87,8 +87,8 @@ export class PolicyService { * @param policyJson Json with the policy content * @returns Observable that should yield a response code, no data */ - putPolicy(policyTypeId: number, policyInstanceId: string, policyJson: string): Observable { - const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId); + putPolicy(policyTypeId: string, policyInstanceId: string, policyJson: string, ric: string): Observable { + const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId) + "?ric=" + ric; return this.httpClient.put(url, policyJson, { observe: 'response' }); } @@ -97,8 +97,14 @@ export class PolicyService { * @param policyTypeId * @returns Observable that should yield a response code, no data */ - deletePolicy(policyTypeId: number, policyInstanceId: string): Observable { + deletePolicy(policyTypeId: string, policyInstanceId: string): Observable { const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId); return this.httpClient.delete(url, { observe: 'response' }); } + + + getRics(policyTypeId: string): Observable { + const url = this.buildPath('rics') + '?policyType=' + policyTypeId; + return this.httpClient.get(url); + } } diff --git a/docs/api-docs.rst b/docs/api-docs.rst index 193a8281..5177088b 100644 --- a/docs/api-docs.rst +++ b/docs/api-docs.rst @@ -2,7 +2,7 @@ .. http://creativecommons.org/licenses/by/4.0 - +======== API-Docs ======== @@ -12,11 +12,18 @@ This is the API-docs of Non-RT RIC. :depth: 3 :local: -API Introduction ------------------ +The Non-RT RIC consists of four parts, described in the sections below: + * The Dashboard + * The Policy Agent + * The Near-RT RIC simulator + * The Sdnc A1 Controller + +Dashboard +========= The Non-RT RIC dashboard is an interface that allows human users to create, edit and delete policy instances, for each existing policy type. The policy types are owned by the Near-RT RIC, Non-RT RIC can just query them, so it's not possible to act on them. +See the README.md file in the nonrtric/dashboard repo for info about how to use it. API Functions ------------- @@ -25,7 +32,7 @@ To run the dashboard locally, you can follow these steps: - Fetch the latest code from `gerrit`_ -.. _gerrit: https://gerrit.nordix.org/c/oransc/nonrtric/+/2747/ +.. _gerrit: https://gerrit.o-ran-sc.org/r/admin/repos/nonrtric - Before compiling, run the following commands:: @@ -48,4 +55,33 @@ From the main page, click on the "Policy Control" card. From here, it is possibl When the instances are listed, it is possible to edit or delete each instance from the expanded view. -.. image:: ./images/non-RT_RIC_dashboard.png \ No newline at end of file +.. image:: ./images/non-RT_RIC_dashboard.png + +Policy Agent +============ +The Policy Agent provides common functionality useful for R-Apps, for instance: + * A repository of available Near-T RICs, their policy types and policy instances. + * An A1 connection to he Near-RT RICs. + +See the README.md file in the nonrtric/policy-agent repo for info about how to use it. + +API Functins +------------ +See the following document for the Policy Agent API: nonrtric/policy-agent/docs/api.doc. + +Near-RT RIC Simulator +===================== +A simulator that simulates a Near-RT RIC, with a termination of the A1 interface. It also provides an administrative API to manage types and instances so it can be programatically set up for use in tests. + +See the README.md file in the nonrtric/near-rt-ric-simulator repo for info about how to use it. + +API Functions +------------- +See the admnistrative API in: nonrtric/near-rt-ric-simulator/ric-plt/a1/main.py. + +Sdnc A1 Controller +================== +An ONAP SDNC Controller for the A1 interface. + +See the README.md file in the nonrtric/sdnc-a1-controller repo for info about how to use it. + diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst index aea9f07c..0b98cc55 100644 --- a/docs/developer-guide.rst +++ b/docs/developer-guide.rst @@ -1,10 +1,6 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. SPDX-License-Identifier: CC-BY-4.0 -A1 Mediation: -1. SDNC Controller -2. Near-RT RIC Simulator - SDNC A1 Controller Developer Guide ================================== @@ -43,6 +39,22 @@ The SDNC url to access the Northbound API, Credentials: admin/Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U +Policy Agent +===================================== +The O-RAN NonRT RIC PolicyAgent provides a REST API for management of policices. It provides support for: + + * Supervision of clients (R-APPs) to eliminate stray policies in case of failure + * Consistency monitoring of the SMO view of policies and the actual situation in the RICs + * Consistency monitoring of RIC capabilities (policy types) + * Policy configuration. This includes: + + * One REST API towards all RICs in the network + * Query functions that can find all policies in a RIC, all policies owned by a service (R-APP), all policies of a type etc. + * Maps O1 resources (ManagedElement) as defined in O1 to the controlling RIC. + +The PolicyAgent can be accessed over the REST API or through the DMaaP Interface. The REST API is documented in the *nonrtric/policy-agent/docs/api.yaml* file. Please Refer README file of PolicyAgent to know more about the API's. + + Near-RT RIC Simulator Developer Guide ===================================== @@ -71,4 +83,4 @@ End-to-end call In order to make a complete end-to-end call, follow the instructions given in this `guide`_. -.. _guide: https://wiki.o-ran-sc.org/pages/viewpage.action?pageId=12157166 \ No newline at end of file +.. _guide: https://wiki.o-ran-sc.org/pages/viewpage.action?pageId=12157166 diff --git a/docs/index.rst b/docs/index.rst index 3f421d33..d33d2f9b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,4 +20,4 @@ Non-RT RIC * :ref:`genindex` * :ref:`modindex` -* :ref:`search` \ No newline at end of file +* :ref:`search` diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 14a24903..e4cbc8bf 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -1,20 +1,20 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. http://creativecommons.org/licenses/by/4.0 - +============= Release-Notes ============= -This document provides the release notes for Amber release of Non-RT RIC. +This document provides the release notes for the release of the different parts of the Non-RT RIC. .. contents:: :depth: 3 :local: -Version history ---------------- +Version history Dashboard +========================= +--------------------+--------------------+--------------------+--------------------+ | **Date** | **Ver.** | **Author** | **Comment** | @@ -29,7 +29,9 @@ Version history | | 1.0 | | | | | | | | +--------------------+--------------------+--------------------+--------------------+ - +| 2020-02-03 | 1.1 | Henrik Andersson | Amber Maintenance | +| | | | Release | ++--------------------+--------------------+--------------------+--------------------+ Summary ------- @@ -40,26 +42,97 @@ The Non-RT RIC is not yet fully compliant with the A1 specifications for this re .. _here: a1_policy_procedure +Version history Policy Agent +============================ + ++--------------------+--------------------+--------------------+--------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| 2020-02-03 | 1.0.0 | Henrik Andersson | First version | +| | | | Amber Maintenance | +| | | | Release | ++--------------------+--------------------+--------------------+--------------------+ + + +Version history Near-RT RIC Simulator +===================================== + ++--------------------+--------------------+--------------------+--------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| 2019-11-12 | 0.1.0 | Maxime Bonneau | First draft | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| | 0.1.1 | | | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| | 1.0 | | | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| 2020-02-03 | 1.1 | Henrik Andersson | Amber Maintenance | +| | | | Release | ++--------------------+--------------------+--------------------+--------------------+ + +Summary +------- +First version with support for R-apps to register and send A1 commands to Near-RT RICs. + + +Version history SDNC A1 Controller +================================== + ++--------------------+--------------------+--------------------+--------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| 2019-11-12 | 1.7.3 | Maxime Bonneau | | +| | | | | ++--------------------+--------------------+--------------------+--------------------+ +| 2020-02-03 | 1.7.4 | Henrik Andersson | Amber Maintenance | +| | | | Release | ++--------------------+--------------------+--------------------+--------------------+ + Release Data ------------- +============ +Amber +----- +--------------------------------------+--------------------------------------+ -| **Project** | Non-RT RIC | +| **Project** | Non-RT RIC | | | | +--------------------------------------+--------------------------------------+ -| **Repo/commit-ID** | TBC | +| **Repo/commit-ID** | nonrtric/ec382949 | | | | +--------------------------------------+--------------------------------------+ | **Release designation** | Amber | | | | +--------------------------------------+--------------------------------------+ -| **Release date** | 2019-XX-XX | +| **Release date** | 2019-11-22 | | | | +--------------------------------------+--------------------------------------+ -| **Purpose of the delivery** | Introducing Non-RT RIC | +| **Purpose of the delivery** | Introducing Non-RT RIC | | | | +--------------------------------------+--------------------------------------+ - +Amber Maintenance +----------------- ++--------------------------------------+--------------------------------------+ +| **Project** | Non-RT RIC | +| | | ++--------------------------------------+--------------------------------------+ +| **Repo/commit-ID** | nonrtric/ | +| | | ++--------------------------------------+--------------------------------------+ +| **Release designation** | Amber Maintenance Release | +| | | ++--------------------------------------+--------------------------------------+ +| **Release date** | 2020-02-03 | +| | | ++--------------------------------------+--------------------------------------+ +| **Purpose of the delivery** | Introducing Policy Agent and | +| | updating to latest A1 spec | ++--------------------------------------+--------------------------------------+ diff --git a/near-rt-ric-simulator/.gitignore b/near-rt-ric-simulator/.gitignore old mode 100755 new mode 100644 index 264db967..ba53c6e7 --- a/near-rt-ric-simulator/.gitignore +++ b/near-rt-ric-simulator/.gitignore @@ -29,3 +29,4 @@ classes out/ .DS_STORE .metadata +__pycache__ diff --git a/near-rt-ric-simulator/README.md b/near-rt-ric-simulator/README.md index 298b9882..03d9a560 100644 --- a/near-rt-ric-simulator/README.md +++ b/near-rt-ric-simulator/README.md @@ -2,10 +2,42 @@ The O-RAN SC Near-RealTime RIC simulates the A1 as an generic REST API which can receive and send northbound messages. The simulator validates the payload and applies policy. -Please see the documentation in the docs/ folder - -The backend server publishes live API documentation at the -URL `http://your-host-name-here:8080/swagger-ui.html` +The simulator handles the requests that are defined in the A1 open API yaml file. All these requests are simulated in the a1.py file. The available requests and the addresses are currently: + - GET all policy identities (respectively for a policy type if query parameter used): http://localhost:8085/A1-P/v1/policies?policyTypeId={policyTypeId} + - PUT a policy instance(create or update it): http://localhost:8085/A1-P/v1/policies/{policyId}?policyTypeId={policyTypeId} + - GET a policy: http://localhost:8085/A1-P/v1/policies/{policyId} + - DELETE a policy instance: http://localhost:8085/A1-P/v1/policies/{policyId} + - GET a policy status: http://localhost:8085/A1-P/v1/policystatus + - GET all policy types: http://localhost:8085/A1-P/v1/policytypes + - GET the schemas for a policy type: http://localhost:8085/A1-P/v1/policytypes/{policyTypeId} + +Nota Bene: It could happen that this page is not updated as soon as the yaml file. The yaml file can be found under /near-rt-ric-simulator/ric-plt/a1/a1-openapi.yaml. + +Additionally, there are requests that are defined in main.py as an administrative API. The goal is to handle information that couldn't be handled using the A1 interface. The available requests and the addresses are currently: + - GET, a basic healthcheck: http://localhost:8085/ + - PUT a policy type: http://localhost:8085/policytypes/{policyTypeId} + - DELETE a policy type: http://localhost:8085/policytypes/{policyTypeId} + - DELETE all policy instances: http://localhost:8085/deleteinstances + - DELETE all policy types: http://localhost:8085/deletetypes + - PUT a status to a policy instance with an enforceStatus parameter only: http://localhost:8085/{policyId}/{enforceStatus} + - PUT a status to a policy instance with both enforceStatus and enforceReason: http://localhost:8085/{policyId}/{enforceStatus}/{enforceReason} + +The backend server publishes live API documentation at the URL `http://your-host-name-here:8080/swagger-ui.html` + +# Starting up the simulator +First, download the nonrtric repo on gerrit: +git clone "https://gerrit.o-ran-sc.org/r/nonrtric" + +Then, build the docker container: +docker build -t simulator . + +To run it, use the command: +docker run -it -p 8085:8085 simulator + +Note: -p 8085:8085 allows to map the port inside the container to any port you choose. One can for example choose -p 8084:8085; in that case, all the addresses mentioned above should be modified accordingly. + +Let the simulator run in one terminal; in another terminal, one can run the command ./commands.sh. It contains the main requests, and will eventually leave the user with a policy type STD_QoSNudging_0.2.0 and a policy instance pi1 with an enforceStatus set to NOT_ENFORCED and an enforce Reason set to 300. +All the response codes should be 20X, otherwise something went wrong. ## License diff --git a/near-rt-ric-simulator/a1-med-api/.gitignore b/near-rt-ric-simulator/a1-med-api/.gitignore deleted file mode 100644 index b83d2226..00000000 --- a/near-rt-ric-simulator/a1-med-api/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target/ diff --git a/near-rt-ric-simulator/a1-med-api/pom.xml b/near-rt-ric-simulator/a1-med-api/pom.xml deleted file mode 100644 index 4030c1c5..00000000 --- a/near-rt-ric-simulator/a1-med-api/pom.xml +++ /dev/null @@ -1,171 +0,0 @@ - - - - 4.0.0 - - org.oran.nearric - nearric-simulator - 1.0.0-SNAPSHOT - - - - a1-med-api - ${project.artifactId} - - - UTF-8 - UTF-8 - 1.8.2 - 1.3.6 - org.oransc.ric.a1med.api - - - - javax.annotation - javax.annotation-api - - - io.swagger.core.v3 - swagger-annotations - 2.0.8 - - - io.swagger - swagger-codegen-maven-plugin - 2.4.8 - - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - - - com.squareup.okhttp - okhttp - 2.7.5 - - - com.squareup.okhttp - logging-interceptor - 2.7.5 - - - io.gsonfire - gson-fire - ${gson-fire-version} - - - org.threeten - threetenbp - ${threetenbp-version} - - - - - - io.swagger.codegen.v3 - swagger-codegen-maven-plugin - 3.0.11 - - - generate-sources-server - generate-sources - - generate - - - ${project.basedir}/src/main/resources/a1_mediator_0.11.0.yaml - java - ${project.basedir}/target/generated-sources/a1med - - ${generated.package.api} - ${generated.package.api}.model - true - true - true - true - true - src/gen/java/main - true - - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - true - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - io.swagger.codegen.v3 - swagger-codegen-maven-plugin - [1.0,) - - generate - - - - - - - - - - - - - - diff --git a/near-rt-ric-simulator/a1-med-api/src/main/resources/a1_mediator_0.11.0.yaml b/near-rt-ric-simulator/a1-med-api/src/main/resources/a1_mediator_0.11.0.yaml deleted file mode 100644 index b8b8f91c..00000000 --- a/near-rt-ric-simulator/a1-med-api/src/main/resources/a1_mediator_0.11.0.yaml +++ /dev/null @@ -1,388 +0,0 @@ -# ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. -# -# 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. -# ================================================================================== -openapi: 3.0.0 -info: - version: 0.11.0 - title: RIC A1 -paths: - '/a1-p/healthcheck': - get: - description: > - Perform a healthcheck on a1 - tags: - - A1 Mediator - operationId: a1.controller.get_healthcheck - responses: - 200: - description: > - A1 is healthy. - Anything other than a 200 should be considered a1 as failing - - '/a1-p/policytypes/': - get: - description: "Get a list of all registered policy type ids" - tags: - - A1 Mediator - operationId: a1.controller.get_all_policy_types - responses: - 200: - description: "list of all registered policy type ids" - content: - application/json: - schema: - type: array - items: - "$ref": "#/components/schemas/policy_type_id" - example: [20000, 20020] - - '/a1-p/policytypes/{policy_type_id}': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - get: - description: > - Get this policy type - tags: - - A1 Mediator - operationId: a1.controller.get_policy_type - responses: - '200': - description: "policy type successfully found" - content: - application/json: - schema: - "$ref": "#/components/schemas/policy_type_schema" - '404': - description: > - policy type not found - delete: - description: > - Delete this policy type. Can only be performed if there are no instances of this type - tags: - - A1 Mediator - operationId: a1.controller.delete_policy_type - responses: - '204': - description: > - policy type successfully deleted - '400': - description: > - Policy type cannot be deleted because there are instances - All instances must be removed before a policy type can be deleted - '404': - description: > - policy type not found - put: - description: > - Create a new policy type . - Replace is not currently allowed; to replace, for now do a DELETE and then a PUT again. - - tags: - - A1 Mediator - operationId: a1.controller.create_policy_type - requestBody: - content: - application/json: - schema: - "$ref": "#/components/schemas/policy_type_schema" - example: - name: admission_control_policy - description: various parameters to control admission of dual connection - policy_type_id: 20000 - create_schema: - $schema: 'http://json-schema.org/draft-07/schema#' - type: object - properties: - enforce: - type: boolean - default: true - window_length: - type: integer - default: 1 - minimum: 1 - maximum: 60 - description: Sliding window length (in minutes) - blocking_rate: - type: number - default: 10 - minimum: 1 - maximum: 100 - description: '% Connections to block' - trigger_threshold: - type: integer - default: 10 - minimum: 1 - description: Minimum number of events in window to trigger blocking - additionalProperties: false - - responses: - '201': - description: "policy type successfully created" - '400': - description: "illegal ID, or object already existed" - - '/a1-p/policytypes/{policy_type_id}/policies': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - get: - description: "get a list of all policy instance ids for this policy type id" - tags: - - A1 Mediator - operationId: a1.controller.get_all_instances_for_type - responses: - 200: - description: "list of all policy instance ids for this policy type id" - content: - application/json: - schema: - type: array - items: - "$ref": "#/components/schemas/policy_instance_id" - example: ["3d2157af-6a8f-4a7c-810f-38c2f824bf12", "06911bfc-c127-444a-8eb1-1bffad27cc3d"] - - - '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - - - name: policy_instance_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_instance_id" - - get: - description: > - Retrieve the policy instance - - tags: - - A1 Mediator - operationId: a1.controller.get_policy_instance - responses: - '200': - description: > - The policy instance. - the schema of this object is defined by the create_schema field of the policy type - content: - application/json: - schema: - type: object - '404': - description: > - there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - - delete: - description: > - Delete this policy instance - - tags: - - A1 Mediator - operationId: a1.controller.delete_policy_instance - responses: - '204': - description: > - policy instance successfully deleted - '404': - description: > - there is no policy instance with this policy_instance_id - or there is no policy type with this policy_type_id - - put: - description: > - Create or replace a policy instance of type policy_type_id. - The schema of the PUT body is defined by the create_schema field of the policy type. - - tags: - - A1 Mediator - operationId: a1.controller.create_or_replace_policy_instance - requestBody: - content: - application/json: - schema: - type: object - description: > - the schema of this object is defined by the create_schema field of the policy type - example: - enforce: true - window_length: 10 - blocking_rate: 20 - trigger_threshold: 10 - - responses: - '201': - description: > - Policy instance created - '400': - description: > - Bad PUT body for this policy instance - '404': - description: > - There is no policy type with this policy_type_id - - '/a1-p/policytypes/{policy_type_id}/policies/{policy_instance_id}/status': - parameters: - - name: policy_type_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_type_id" - - - name: policy_instance_id - in: path - required: true - schema: - "$ref": "#/components/schemas/policy_instance_id" - - get: - description: > - Retrieve the policy instance status across all handlers of the policy - - tags: - - A1 Mediator - operationId: a1.controller.get_policy_instance_status - responses: - '200': - description: > - The policy instance status. - Returns a vector of statuses, where each contains a handler_id (opaque id of a RIC component that implements this policy) and the policy status as returned by that handler - content: - application/json: - schema: - type: array - items: - type: object - properties: - handler_id: - type: string - status: - type: string - example: - [{"handler_id": "1234-5678", "status" : "OK"}, {"handler_id": "abc-def", "status" : "NOT IMPLEMENTED"}] - '404': - description: > - there is no policy instance with this policy_instance_id or there is no policy type with this policy_type_id - - -components: - schemas: - policy_type_schema: - type: object - required: - - name - - description - - policy_type_id - - create_schema - additionalProperties: false - properties: - name: - type: string - description: name of the policy type - description: - type: string - description: description of the policy type - policy_type_id: - description: the integer of the policy type - type: integer - create_schema: - type: object - description: > - jsonschema (following http://json-schema.org/draft-07/schema) of the CREATE payload to be sent to handlers of this policy - - policy_type_id: - description: > - represents a policy type identifier. Currently this is restricted to an integer range. - type: integer - minimum: 20000 - maximum: 21024 - - policy_instance_id: - description: > - represents a policy instance identifier. UUIDs are advisable but can be any string - type: string - example: "3d2157af-6a8f-4a7c-810f-38c2f824bf12" - - downstream_message_schema: - type: object - required: - - operation - - policy_type_id - - policy_instance_id - - payload - additionalProperties: false - properties: - operation: - description: the operation being performed - type: string - enum: - - CREATE - - DELETE - - UPDATE - - READ - policy_type_id: - "$ref": "#/components/schemas/policy_type_id" - policy_instance_id: - "$ref": "#/components/schemas/policy_instance_id" - payload: - description: payload for this operation - type: object - example: - operation: CREATE - policy_type_id: 12345678 - policy_instance_id: 3d2157af-6a8f-4a7c-810f-38c2f824bf12 - payload: - enforce: true - window_length: 10 - blocking_rate: 20 - trigger_threshold: 10 - - downstream_notification_schema: - type: object - required: - - policy_type_id - - policy_instance_id - - handler_id - - status - additionalProperties: false - properties: - policy_type_id: - "$ref": "#/components/schemas/policy_type_id" - policy_instance_id: - "$ref": "#/components/schemas/policy_instance_id" - handler_id: - description: > - id of the policy handler - type: string - status: - description: > - the status of this policy instance in this handler - type: string - example: - policy_type_id: 12345678 - policy_instance_id: 3d2157af-6a8f-4a7c-810f-38c2f824bf12 - handler_id: 1234-5678 - status: OK diff --git a/near-rt-ric-simulator/auto-test/.gitignore b/near-rt-ric-simulator/auto-test/.gitignore new file mode 100644 index 00000000..1c5ae4ee --- /dev/null +++ b/near-rt-ric-simulator/auto-test/.gitignore @@ -0,0 +1,2 @@ +logs +.tmp_* \ No newline at end of file diff --git a/near-rt-ric-simulator/auto-test/Dockerize_test.sh b/near-rt-ric-simulator/auto-test/Dockerize_test.sh new file mode 100755 index 00000000..611523e2 --- /dev/null +++ b/near-rt-ric-simulator/auto-test/Dockerize_test.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +TC_ONELINE_DESCR="dockerirze the test, setup docker container for policy agent, cbs, consul, near realtime ric simulator" + +. ../common/testcase_common.sh $1 + +#### TEST BEGIN #### + +clean_containers + +start_simulators + +consul_config_app "../simulator-group/consul_cbs/config.json" + +start_policy_agent + +check_policy_agent_logs + +#### TEST COMPLETE #### + +store_logs END diff --git a/near-rt-ric-simulator/auto-test/FTC1.sh b/near-rt-ric-simulator/auto-test/FTC1.sh new file mode 100755 index 00000000..80d12194 --- /dev/null +++ b/near-rt-ric-simulator/auto-test/FTC1.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +TC_ONELINE_DESCR="Auto test for policy agent refreshing configurations from consul/cbs" + +. ../common/testcase_common.sh $1 + +#### TEST BEGIN #### + +clean_containers + +start_simulators + +consul_config_app "../simulator-group/consul_cbs/config.json" + +start_policy_agent + +check_policy_agent_logs + +#### TEST COMPLETE #### + +store_logs END + diff --git a/near-rt-ric-simulator/auto-test/README.md b/near-rt-ric-simulator/auto-test/README.md new file mode 100644 index 00000000..319137af --- /dev/null +++ b/near-rt-ric-simulator/auto-test/README.md @@ -0,0 +1,70 @@ + +## Automated test Description +This auto-test repo stores test script for automated test cases for policy agent. +Each of the testcase script will bring up a containerized test enviroment for Policy Agent, +CBS, consul, and simulator(TBD) + +### Overveiw + +Right now, test cases are written in bash scripts. \ +Each test case script(ex. `FTC1.sh)` will call functions defined in `../common`. \ +The environment vriables are set in`test_env.sh`. \ +The automated test support both local build policy agent image testing and remote image stored in Nexus. +``` +# Lcal image +export POLICY_AGENT_LOCAL_IMAGE=o-ran-sc/nonrtric-policy-agent +# Remote image +export POLICY_AGENT_REMOTE_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent +``` +### Test Cases Description(more TBD) +`FTC1.sh`: Test policy-agent can refresh configurations from consul + +### Logs +All log files are stored at `logs/`. \ +The logs include the application.log and the container log from policy agent, the container logs from each simulator and the +test case log (same as the screen output). \ +In the test cases the logs are stored with a prefix so the logs can be stored at different steps during the test. +All test cases contains an entry to save all logs with prefix 'END' at the end of each test case. + +### Manual +Test case command: +``` +./.sh local | remote + +Discription: +local: test image: POLICY_AGENT_LOCAL_IMAGE=o-ran-sc/nonrtric-policy-agent +remote: test image: nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent +``` + +### Test case file +A test case file contains a number of steps to verify a certain functionality. +A description of the test case should be given to the ``TC_ONELINE_DESCR`` var. The description will be printed in +the test result. + +The empty template for a test case files looks like this: + +(Only the parts noted with < and > shall be changed.) + +----------------------------------------------------------- +``` +#!/usr/bin/env bash + +TC_ONELINE_DESCR="" + +. ../common/testcase_common.sh $1 + +#### TEST BEGIN #### + + + + + +#### TEST COMPLETE #### + +store_logs END + +``` +----------------------------------------------------------- + +The ../common/testcase_common.sh contains all functions needed for the test case file. See the README.md file in +the ../common dir for a description of all available functions. \ No newline at end of file diff --git a/near-rt-ric-simulator/common/README.md b/near-rt-ric-simulator/common/README.md new file mode 100644 index 00000000..37d81ec3 --- /dev/null +++ b/near-rt-ric-simulator/common/README.md @@ -0,0 +1,32 @@ +## Description for common test scripts + +`test_env.sh` \ +Common env variables for test in the auto-test dir. +Used by the auto test cases/suites but could be used for other test script as well. + +`testcase_common.sh` \ +Common functions for auto test cases in the auto-test dir. +A subset of the functions could be used in other test scripts as well. + +###Descriptions of functions in testcase_common.sh + +`clean_containers` \ +Stop and remove all containers including policy agent apps and simulators + +`start_simulators` \ +Start all simulators in the simulator group + +`consul_config_app ` \ +Configure consul with json file with app config for a policy agent instance using the policy agent +instance id and the json file. + +`start_policy_agent` \ +Start the policy agent application. + +`check_policy_agent_logs` +Check the Policy agent application log for WARN and ERR messages and print the count. +`store_logs` +Store all Policy agent app and simulators log to the test case log dir. All logs get a prefix to +separate logs stored at different steps in the test script. +If logs need to be stored in several locations, use different prefix to easily identify the location +when the logs where taken. diff --git a/near-rt-ric-simulator/common/test_env.sh b/near-rt-ric-simulator/common/test_env.sh new file mode 100755 index 00000000..b307a690 --- /dev/null +++ b/near-rt-ric-simulator/common/test_env.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Set the images for the Policy agent app to use for the auto tests. Do not add the image tag. +# +# Local image and tag, shall point to locally built image (non-nexus path) +export POLICY_AGENT_LOCAL_IMAGE=o-ran-sc/nonrtric-policy-agent +# Remote image +export POLICY_AGENT_REMOTE_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent + +# Common env var for auto-test. + +POLICY_AGENT_PORT=8081 +POLICY_AGENT_LOGPATH="/var/log/policy-agent/application.log" #Path the application log in the policy agent container +DOCKER_SIM_NWNAME="nonrtric-docker-net" #Name of docker private network +CONSUL_HOST="consul-server" #Host name of consul +CONSUL_PORT=8500 #Port number of consul +CONFIG_BINDING_SERVICE="config-binding-service" #Host name of CBS +PA_APP_BASE="policy-agent" #Base name for policy agent container diff --git a/near-rt-ric-simulator/common/testcase_common.sh b/near-rt-ric-simulator/common/testcase_common.sh new file mode 100755 index 00000000..ae6563d0 --- /dev/null +++ b/near-rt-ric-simulator/common/testcase_common.sh @@ -0,0 +1,293 @@ +#!/usr/bin/env bash + +. ../common/test_env.sh + +echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 + +# This is a script that contains all the functions needed for auto test +# Arg: local | remote + +STARTED_POLICY_AGENT="" #Policy agent app names added to this var to keep track of started container in the script +START_ARG=$1 +IMAGE_TAG="1.0.0-SNAPSHOT" +IMAGE_TAG_REMOTE="1.0.0" + +if [ $# -lt 1 ] || [ $# -gt 2 ]; then + echo "Expected arg: local | remote " + exit 1 +elif [ $1 == "local" ]; then + if [ -z $POLICY_AGENT_LOCAL_IMAGE ]; then + echo "POLICY_AGENT_LOCAL_IMAGE not set in test_env" + exit 1 + fi + POLICY_AGENT_IMAGE=$POLICY_AGENT_LOCAL_IMAGE":"$IMAGE_TAG +elif [ $1 == "remote" ]; then + if [ -z $POLICY_AGENT_REMOTE_IMAGE ]; then + echo "POLICY_AGENT_REMOTE_IMAGE not set in test_env" + exit 1 + fi + POLICY_AGENT_IMAGE=$POLICY_AGENT_REMOTE_IMAGE":"$IMAGE_TAG_REMOTE +fi + +# Set a description string for the test case +if [ -z "$TC_ONELINE_DESCR" ]; then + TC_ONELINE_DESCR="" + echo "No test case description found, TC_ONELINE_DESCR should be set on in the test script , using "$TC_ONELINE_DESCR +fi + +ATC=$(basename "${BASH_SOURCE[$i+1]}" .sh) + + +# Create the logs dir if not already created in the current dir +if [ ! -d "logs" ]; then + mkdir logs +fi + +TESTLOGS=$PWD/logs + +mkdir -p $TESTLOGS/$ATC + +TCLOG=$TESTLOGS/$ATC/TC.log +exec &> >(tee ${TCLOG}) + +#Variables for counting tests as well as passed and failed tests +RES_TEST=0 +RES_PASS=0 +RES_FAIL=0 +TCTEST_START=$SECONDS + +echo "-------------------------------------------------------------------------------------------------" +echo "----------------------------------- Test case: "$ATC +echo "----------------------------------- Started: "$(date) +echo "-------------------------------------------------------------------------------------------------" +echo "-- Description: "$TC_ONELINE_DESCR +echo "-------------------------------------------------------------------------------------------------" +echo "----------------------------------- Test case setup -----------------------------------" + + +if [ -z "$SIM_GROUP" ]; then + SIM_GROUP=$PWD/../simulator-group + if [ ! -d $SIM_GROUP ]; then + echo "Trying to set env var SIM_GROUP to dir 'simulator-group' in the integration repo, but failed." + echo "Please set the SIM_GROUP manually in the test_env.sh" + exit 1 + else + echo "SIM_GROUP auto set to: " $SIM_GROUP + fi +elif [ $SIM_GROUP = *simulator_group ]; then + echo "Env var SIM_GROUP does not seem to point to dir 'simulator-group' in the integration repo, check test_env.sh" + exit 1 +fi + +echo "" + +if [ $1 != "manual-container" ] && [ $1 != "manual-app" ]; then + #echo -e "Policy agent image tag set to: \033[1m" $IMAGE_TAG"\033[0m" + echo "Configured image for policy agent app(s) (${1}): "$POLICY_AGENT_IMAGE + tmp_im=$(docker images ${POLICY_AGENT_IMAGE} | grep -v REPOSITORY) + + if [ $1 == "local" ]; then + if [ -z "$tmp_im" ]; then + echo "Local image (non nexus) "$POLICY_AGENT_IMAGE" does not exist in local registry, need to be built" + exit 1 + else + echo -e "Policy agent local image: \033[1m"$tmp_im"\033[0m" + echo "If the policy agent image seem outdated, rebuild the image and run the test again." + fi + elif [ $1 == "remote" ]; then + if [ -z "$tmp_im" ]; then + echo "Pulling policy agent image from nexus: "$POLICY_AGENT_IMAGE + docker pull $POLICY_AGENT_IMAGE > /dev/null + tmp_im=$(docker images ${POLICY_AGENT_IMAGE} | grep -v REPOSITORY) + if [ -z "$tmp_im" ]; then + echo "Image could not be pulled" + exit 1 + fi + echo -e "Policy Agent image: \033[1m"$tmp_im"\033[0m" + else + echo -e "Policy Agent image: \033[1m"$tmp_im"\033[0m" + echo "!! If the Policy agent image seem outdated, consider removing it from your docker registry and run the test again." + fi + fi +fi + +echo "" + +echo "Building images for the simulators" +curdir=$PWD +cd $SIM_GROUP +cd ../ric-plt/a1 +docker build -t ric-simulator:latest . &> /dev/null +cd $curdir + +echo "" + +echo "Local registry images for simulators:" +echo "Consul: " $(docker images | grep consul) +echo "CBS: " $(docker images | grep platform.configbinding.app) +echo "RIC: " $(docker images | grep ric-simulator) +echo "" + + +__consul_config() { + + appname=$PA_APP_BASE + + echo "Configuring consul for " $appname " from " $1 + curl -s http://127.0.0.1:${CONSUL_PORT}/v1/kv/${appname}?dc=dc1 -X PUT -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'X-Requested-With: XMLHttpRequest' --data-binary "@"$1 >/dev/null +} + + +consul_config_app() { + + __consul_config $1 + +} + +# Start all simulators in the simulator group +start_simulators() { + + echo "Starting all simulators" + curdir=$PWD + cd $SIM_GROUP + $SIM_GROUP/simulators-start.sh + cd $curdir + echo "" +} + +clean_containers() { + echo "Stopping all containers, policy agent app(s) and simulators with name prefix 'policy_agent'" + docker stop $(docker ps -q --filter name=/policy-agent) &> /dev/null + echo "Removing all containers, policy agent app and simulators with name prefix 'policy_agent'" + docker rm $(docker ps -a -q --filter name=/policy-agent) &> /dev/null + echo "Removing unused docker networks with substring 'policy agent' in network name" + docker network rm $(docker network ls -q --filter name=nonrtric) + echo "" +} + +start_policy_agent() { + + appname=$PA_APP_BASE + + if [ $START_ARG == "local" ] || [ $START_ARG == "remote" ]; then + __start_policy_agent_image $appname + fi +} + +__start_policy_agent_image() { + + appname=$1 + localport=$POLICY_AGENT_PORT + + echo "Creating docker network $DOCKER_SIM_NWNAME, if needed" + + docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME + + echo "Starting policy agent: " $appname " with ports mapped to " $localport " in docker network "$DOCKER_SIM_NWNAME + docker run -d -p $localport":8081" --network=$DOCKER_SIM_NWNAME -e CONSUL_HOST=$CONSUL_HOST -e CONSUL_PORT=$CONSUL_PORT -e CONFIG_BINDING_SERVICE=$CONFIG_BINDING_SERVICE -e HOSTNAME=$appname --name $appname $POLICY_AGENT_IMAGE + #docker run -d -p 8081:8081 --network=nonrtric-docker-net -e CONSUL_HOST=CONSUL_HOST=$CONSUL_HOST -e CONSUL_PORT=$CONSUL_PORT -e CONFIG_BINDING_SERVICE=$CONFIG_BINDING_SERVICE -e HOSTNAME=policy-agent + sleep 3 + set +x + pa_started=false + for i in {1..10}; do + if [ $(docker inspect --format '{{ .State.Running }}' $appname) ] + then + echo " Image: $(docker inspect --format '{{ .Config.Image }}' ${appname})" + echo "Policy Agent container ${appname} running" + pa_started=true + break + else + sleep $i + fi + done + if ! [ $pa_started ]; then + echo "Policy Agent container ${appname} could not be started" + exit 1 + fi + + pa_st=false + echo "Waiting for Policy Agent ${appname} service status..." + for i in {1..10}; do + result="$(__do_curl http://127.0.0.1:${localport}/status)" + if [ $? -eq 0 ]; then + echo "Policy Agent ${appname} responds to service status: " $result + echo "Policy Agent is alive." + pa_st=true + break + else + sleep $i + fi + done + + if [ "$pa_st" = "false" ]; then + echo "Policy Agent ${appname} did not respond to service status" + exit 1 + fi +} + +check_policy_agent_logs() { + + appname=$PA_APP_BASE + tmp=$(docker ps | grep $appname) + if ! [ -z "$tmp" ]; then #Only check logs for running policy agent apps + __check_policy_agent_log $appname + fi + +} + +__check_policy_agent_log() { + echo "Checking $1 log $POLICY_AGENT_LOGPATH for WARNINGs and ERRORs" + foundentries=$(docker exec -it $1 grep WARN /var/log/policy-agent/application.log | wc -l) + if [ $? -ne 0 ];then + echo " Problem to search $1 log $POLICY_AGENT_LOGPATH" + else + if [ $foundentries -eq 0 ]; then + echo " No WARN entries found in $1 log $POLICY_AGENT_LOGPATH" + else + echo -e " Found \033[1m"$foundentries"\033[0m WARN entries in $1 log $POLICY_AGENT_LOGPATH" + fi + fi + foundentries=$(docker exec -it $1 grep ERR $POLICY_AGENT_LOGPATH | wc -l) + if [ $? -ne 0 ];then + echo " Problem to search $1 log $POLICY_AGENT_LOGPATH" + else + if [ $foundentries -eq 0 ]; then + echo " No ERR entries found in $1 log $POLICY_AGENT_LOGPATH" + else + echo -e " Found \033[1m"$foundentries"\033[0m ERR entries in $1 log $POLICY_AGENT_LOGPATH" + fi + fi +} + +store_logs() { + if [ $# != 1 ]; then + __print_err "need one arg, " + exit 1 + fi + echo "Storing all container logs and policy agent app log using prefix: "$1 + + docker logs polman_consul > $TESTLOGS/$ATC/$1_consul.log 2>&1 + docker logs polman_cbs > $TESTLOGS/$ATC/$1_cbs.log 2>&1 +} + +__do_curl() { + res=$(curl -skw "%{http_code}" $1) + http_code="${res:${#res}-3}" + if [ ${#res} -eq 3 ]; then + echo "" + return 1 + else + if [ $http_code -lt 200 ] && [ $http_code -gt 299]; then + echo "" + return 1 + fi + if [ $# -eq 2 ]; then + echo "${res:0:${#res}-3}" | xargs + else + echo "${res:0:${#res}-3}" + fi + + return 0 + fi +} + diff --git a/near-rt-ric-simulator/nearric-service/pom.xml b/near-rt-ric-simulator/nearric-service/pom.xml index d142bedf..dd21d574 100644 --- a/near-rt-ric-simulator/nearric-service/pom.xml +++ b/near-rt-ric-simulator/nearric-service/pom.xml @@ -176,7 +176,7 @@ - o-ran-sc/nearric-simulator:${project.version} + o-ran-sc/nonrtric-nearric-simulator:${project.version} openjdk:11-jre-slim diff --git a/near-rt-ric-simulator/pom.xml b/near-rt-ric-simulator/pom.xml index ce73b7ac..4b666344 100644 --- a/near-rt-ric-simulator/pom.xml +++ b/near-rt-ric-simulator/pom.xml @@ -18,65 +18,92 @@ SPDX-License-Identifier: Apache-2.0 ============LICENSE_END========================================================= --> - - 4.0.0 - org.oran.nearric - nearric-simulator - 1.0.0-SNAPSHOT - pom - ${project.artifactId} - - org.springframework.boot - spring-boot-starter-parent - 2.1.9.RELEASE - - + + 4.0.0 + org.oran.nearric + nearric-simulator + 1.0.1-SNAPSHOT + pom + ${project.artifactId} - - 2.1.9.RELEASE - 11 - + + org.springframework.boot + spring-boot-starter-parent + 2.1.9.RELEASE + + - - - - org.springframework.boot - spring-boot-dependencies - ${spring.boot.version} - pom - import - - - + + 2.1.9.RELEASE + 11 + 0.30.0 + - - - org.springframework - spring-context - - - org.springframework - spring-web - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - ${java.version} - - - - + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + - - a1-med-api - nearric-service - - \ No newline at end of file + + + org.springframework + spring-context + + + org.springframework + spring-web + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + + + + io.fabric8 + docker-maven-plugin + ${docker-maven-plugin.version} + false + + + push-near-rt-ric-simulator-image + + build + push + + + ${env.CONTAINER_PULL_REGISTRY} + ${env.CONTAINER_PUSH_REGISTRY} + + + o-ran-sc/near-rt-ric-simulator:${project.version} + + ${basedir}/ric-plt/a1 + Dockerfile + + ${project.version} + + + + + + + + + + + diff --git a/near-rt-ric-simulator/ric-plt/a1/Dockerfile b/near-rt-ric-simulator/ric-plt/a1/Dockerfile new file mode 100644 index 00000000..db06a210 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/Dockerfile @@ -0,0 +1,21 @@ +FROM python:3 + +WORKDIR /usr/src/app + +RUN pip install connexion[swagger-ui] + +COPY policy_instance_1_STD_QoSNudging_0.1.0.json policy_instance_1_STD_QoSNudging_0.1.0.json +COPY policy_instance_1_bis_STD_QoSNudging_0.1.0.json policy_instance_1_bis_STD_QoSNudging_0.1.0.json +COPY policy_instance_2_STD_QoSNudging_0.1.0.json policy_instance_2_STD_QoSNudging_0.1.0.json +COPY policy_type_STD_QoSNudging_0.1.0.json policy_type_STD_QoSNudging_0.1.0.json + +COPY a1.py a1.py +COPY main.py main.py +COPY var_declaration.py var_declaration.py + +COPY commands.sh commands.sh +COPY run_me.sh run_me.sh + +COPY a1-openapi.yaml a1-openapi.yaml + +CMD ["/bin/bash", "./run_me.sh"] diff --git a/near-rt-ric-simulator/ric-plt/a1/a1-openapi.yaml b/near-rt-ric-simulator/ric-plt/a1/a1-openapi.yaml new file mode 100644 index 00000000..223d1bd5 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/a1-openapi.yaml @@ -0,0 +1,318 @@ +openapi: 3.0.0 +info: + title: 'A1-P Policy Management Service' + version: 1.1.x-alpha.2 + description: | + API for Policy Management Service. + © 2019, O-RAN Alliance. + All rights reserved. +externalDocs: + description: 'ORAN-WG2.A1.AP-v01.01 A1 interface: Application protocol' + url: 'https://www.o-ran.org/specifications' +servers: + - url: '{apiRoot}/A1-P/v1' + variables: + apiRoot: + default: 'https://example.com' + description: 'apiRoot as defined in clause 4.2.1 in ORAN-WG2.A1.AP' +paths: + '/policies': + get: + operationId: a1.get_all_policy_identities + description: 'Get all policy identities' + tags: + - All Policy Identities + parameters: + - name: policyTypeId + in: query + required: false + schema: + "$ref": "#/components/schemas/PolicyTypeId" + responses: + 200: + description: 'Array of all policy identities' + content: + application/json: + schema: + type: array + items: + "$ref": "#/components/schemas/PolicyId" + minItems: 0 + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + + '/policies/{policyId}': + parameters: + - name: policyId + in: path + required: true + schema: + "$ref": "#/components/schemas/PolicyId" + put: + operationId: a1.put_policy + description: 'Create, or update, a policy' + tags: + - Individual Policy Object + parameters: + - name: policyTypeId + in: query + required: false + schema: + "$ref": "#/components/schemas/PolicyTypeId" + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/PolicyObject" + responses: + 200: + description: 'The policy was updated' + content: + application/json: + schema: + "$ref": "#/components/schemas/PolicyObject" + 201: + description: 'The policy was created' + content: + application/json: + schema: + "$ref": "#/components/schemas/PolicyObject" + headers: + Location: + description: 'Contains the URI of the created policy' + required: true + schema: + type: string + 400: + "$ref": "#/components/responses/400-BadRequest" + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + 507: + "$ref": "#/components/responses/507-InsufficientStorage" + get: + operationId: a1.get_policy + description: 'Query a policy' + tags: + - Individual Policy Object + responses: + 200: + description: 'The requested policy' + content: + application/json: + schema: + "$ref": "#/components/schemas/PolicyObject" + 404: + "$ref": "#/components/responses/404-NotFound" + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + delete: + operationId: a1.delete_policy + description: 'Delete a policy' + tags: + - Individual Policy Object + responses: + 204: + description: 'The policy was deleted' + 404: + "$ref": "#/components/responses/404-NotFound" + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + + '/policystatus/{policyId}': + parameters: + - name: policyId + in: path + required: true + schema: + "$ref": "#/components/schemas/PolicyId" + get: + operationId: a1.get_policy_status + description: 'Query a policy status' + tags: + - Individual Policy Status Object + responses: + 200: + description: 'The requested policy status' + content: + application/json: + schema: + "$ref": "#/components/schemas/PolicyStatusObject" + 404: + "$ref": "#/components/responses/404-NotFound" + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + + '/policytypes': + get: + operationId: a1.get_all_policytypes_identities + description: 'Get all policy type identities' + tags: + - All Policy Type Identities + responses: + 200: + description: 'Array of all policy type identities' + content: + application/json: + schema: + type: array + items: + "$ref": "#/components/schemas/PolicyTypeId" + minItems: 0 + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + + '/policytypes/{policyTypeId}': + parameters: + - name: policyTypeId + in: path + required: true + schema: + "$ref": "#/components/schemas/PolicyTypeId" + get: + operationId: a1.get_policytypes + description: 'Get the schemas for a policy type' + tags: + - Individual Policy Type + responses: + 200: + description: 'The policy type schemas' + content: + application/json: + schema: + "$ref": "#/components/schemas/PolicyTypeObject" + 404: + "$ref": "#/components/responses/404-NotFound" + 429: + "$ref": "#/components/responses/429-TooManyRequests" + 503: + "$ref": "#/components/responses/503-ServiceUnavailable" + +components: + schemas: + # + # Representation objects + # + PolicyObject: + description: 'A generic policy object that can be used to transport any policy. Additionally, a policy shall be valid according to the schema of its specific policy type.' + type: object + + PolicyStatusObject: + description: 'A generic policy status object that can be used to transport any policy status. Additionally, a policy status shall be valid according to the schema of its specific policy type.' + type: object + + PolicyTypeObject: + description: 'A definition of a policy type, i.e. the schemas for a policy respectively its status' + type: object + properties: + policySchema: + "$ref": "#/components/schemas/JsonSchema" + statusSchema: + "$ref": "#/components/schemas/JsonSchema" + required: + - policySchema + + ProblemDetails: + description: 'A problem detail to carry details in a HTTP response according to RFC 7807 extended with A1 specific attributes' + type: object + properties: + type: + type: string + title: + type: string + status: + type: number + detail: + type: string + instance: + type: string + cause: + type: string + invalidParams: + type: array + items: + "$ref": "#/components/schemas/InvalidParam" + minItems: 1 + + # + # Structured data types + # + InvalidParam: + description: 'Used in a ProblemDetails to indicate a specific invalid parameter' + type: object + properties: + param: + type: string + reason: + type: string + required: + - param + + # + # Simple data types + # + JsonSchema: + description: 'A JSON schema following http://json-schema.org/draft-07/schema' + type: object + + PolicyId: + description: 'Policy identifier assigned by the A1-P Consumer when a policy is created' + type: string + + PolicyTypeId: + description: 'Policy type identifier assigned by the A1-P Provider' + type: string + + responses: + 400-BadRequest: + description: 'Object in payload not properly formulated or not related to the method' + content: + application/problem+json: + schema: + "$ref": "#/components/schemas/ProblemDetails" + + 404-NotFound: + description: 'No resource found at the URI' + content: + application/problem+json: + schema: + "$ref": "#/components/schemas/ProblemDetails" + + 405-MethodNotAllowed: + description: 'Method not allowed for the URI' + content: + application/problem+json: + schema: + "$ref": "#/components/schemas/ProblemDetails" + + 429-TooManyRequests: + description: 'Too many requests have been sent in a given amount of time' + content: + application/problem+json: + schema: + "$ref": "#/components/schemas/ProblemDetails" + + 503-ServiceUnavailable: + description: 'The provider is currently unable to handle the request due to a temporary overload' + content: + application/problem+json: + schema: + "$ref": "#/components/schemas/ProblemDetails" + + 507-InsufficientStorage: + description: 'The method could not be performed on the resource because the provider is unable to store the representation needed to successfully complete the request' + content: + application/problem+json: + schema: + "$ref": "#/components/schemas/ProblemDetails" diff --git a/near-rt-ric-simulator/ric-plt/a1/a1.py b/near-rt-ric-simulator/ric-plt/a1/a1.py new file mode 100644 index 00000000..76f51b83 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/a1.py @@ -0,0 +1,171 @@ +#!/u:sr/bin/env python3 +import copy +import datetime +import json +import logging +#import requests + +from connexion import NoContent +from flask import Flask, escape, request, make_response +from jsonschema import validate +from random import random, choice +from var_declaration import policy_instances, policy_types, policy_status, policy_type_per_instance + +def get_all_policy_identities(): + if len(request.args) == 0: + return(list(policy_instances.keys()), 200) + elif 'policyTypeId' in request.args: + policyTypeId = request.args.get('policyTypeId') + if policyTypeId not in list(policy_types.keys()): + return(set_error(None, "The policy type provided does not exist.", 400, "The policy type " + data["policyTypeId"] + " is not defined as a policy type.", None, None, "policyTypeId", None)) + else: + return(list({key for key in policy_instances.keys() if policy_type_per_instance[key]==policyTypeId}), 200) + else: + return(send_error_code(request.args)) + +def put_policy(policyId): + data = request.data.decode("utf-8") + data = data.replace("'", "\"") + data = json.loads(data) + ps = {} + if 'policyTypeId' in request.args: + policyTypeId = request.args.get('policyTypeId') + + if policyTypeId not in list(policy_types.keys()): + return(set_error(None, "The policy type provided does not exist.", 400, "The policy type " + policyTypeId + " is not defined as a policy type.", None, None, "policyTypeId", None)) + + policy_schema = policy_types[policyTypeId]["policySchema"] + try: + validate(instance=data, schema=policy_schema) + except: + return(set_error(None, "The json does not validate against the schema.", 400, None, None, None, None, None)) + + for i in list(policy_instances.keys()): + if policyId != i and \ + data == policy_instances[i] and \ + policyTypeId == policy_type_per_instance[i]: + return(set_error(None, "The policy already exists with a different id.", 400, "No action has been taken. The id of the existing policy instance is: " + i + ".", None, None, None, None)) + + if policyId in list(policy_instances.keys()): + if data["scope"] != policy_instances[policyId]["scope"]: + return(set_error(None, "The policy already exists with a different scope.", 400, "The policy put involves a modification of the existing scope, which is not allowed.", None, None, "scope", None)) + + if 'code' in request.args: + return(send_error_code(request.args)) + + policy_instances[policyId] = data + policy_status[policyId] = set_status("UNDEFINED") + if 'policyTypeId' in request.args: + status_schema = policy_types[policyTypeId]["statusSchema"] + try: + validate(instance=policy_status[policyId], schema=status_schema) + except: + return(set_error(None, "The json does not validate against the status schema.", 400, None, None, None, None, None)) + policy_type_per_instance[policyId] = policyTypeId + else: + policy_type_per_instance[policyId] = "UNDEFINED" + + if policyId in policy_instances.keys(): + code = 201 + else: + code = 200 + + response = make_response(policy_instances[policyId], code) + if code == 201: + response.headers['Location'] = "http://localhost:8085/A1-P/v1/policies/" + policyId + return response + +def set_status(*args): + ps = {} + ps["enforceStatus"] = args[0] + if len(args) == 2: + ps["enforceReason"] = args[1] + if len(args) > 2: + return(set_error(None, "Too many arguments", 400, "There should be no more than two status arguments: enforceStatus and enforceReason", None, None, None, None)) + return ps + +def get_policy(policyId): + if len(request.args) == 0: + if policyId in policy_instances.keys(): + res = policy_instances[policyId] + res["enforceStatus"] = policy_status[policyId]["enforceStatus"] + return(res, 200) + else: + return(set_error(None, "The requested policy does not exist.", 404, None, None, None, "policyId", None)) + else: + return(send_error_code(request.args)) + +def delete_policy(policyId): + if len(request.args) == 0: + if policyId in policy_instances.keys(): + policy_instances.pop(policyId) + policy_status.pop(policyId) + policy_type_per_instance.pop(policyId) + return(None, 204) + else: + return(set_error(None, "The policy identity does not exist.", 404, "No policy instance has been deleted.", None, None, "policyId", None)) + else: + return(send_error_code(request.args)) + +def get_policy_status(policyId): + if len(request.args) == 0: + if policyId in policy_instances.keys(): + return(policy_status[policyId], 200) + else: + return(set_error(None, "The policy identity does not exist.", 404, "There is no existing policy instance with the identity: " + policyId, None, None, "policyId", None)) + else: + return(send_error_code(request.args)) + +def get_all_policytypes_identities(): + if len(request.args) == 0: + return(list(policy_types.keys()), 200) + else: + return(send_error_code(request.args)) + +def get_policytypes(policyTypeId): + if len(request.args) == 0: + if policyTypeId in policy_types.keys(): + return(policy_types[policyTypeId], 200) + else: + return(set_error(None, "The requested policy type does not exist.", 404, None, None, None, "policyTypeId", None)) + else: + return(send_error_code(request.args)) + +def set_error(type_of, title, status, detail, instance, cause, param, reason): + error = {} + params = {} + if type_of is not None: + error["type"] = type_of + if title is not None: + error["title"] = title + if status is not None: + error["status"] = status + if detail is not None: + error["detail"] = detail + if instance is not None: + error["instance"] = instance + if cause is not None: + error["cause"] = cause + if param is not None: + params["param"] = param + if reason is not None: + params["reason"] = reason + if params: + error["invalidParams"] = params + return(error, error["status"]) + +def send_error_code(args): + if 'code' in args.keys(): + code = args['code'] + if code == '405': + return(set_error(None, "Method not allowed", 405, "Method not allowed for the URI", None, None, None, None)) + elif code == '429': + return(set_error(None, "Too many requests", 429, "Too many requests have been sent in a given amount of time", None, None, None, None)) + elif code == '507': + return(set_error(None, "Insufficient storage", 507, "The method could not be performed on the resource because the provider is unable to store the representation needed to successfully complete the request", None, None, None, None)) + elif code == '503': + return(set_error(None, "Service unavailable", 503, "The provider is currently unable to handle the request due to a temporary overload", None, None, None, None)) + else: + return(set_error(None, "Not found", 400, "No resource found at the URI", None, None, None, None)) + else: + return(set_error(None, "Not found", 400, "No resource found at the URI", None, None, None, None)) diff --git a/near-rt-ric-simulator/ric-plt/a1/commands.sh b/near-rt-ric-simulator/ric-plt/a1/commands.sh new file mode 100755 index 00000000..4e873dba --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/commands.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Different commands for the simulator. +# By running this, nothing should return an error. + +# Make a test +curl -v "http://localhost:8085/" + +# PUT a policy type STD_QoSNudging_0.2.0 +curl -X PUT -v "http://localhost:8085/policytypes/STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @policy_type_STD_QoSNudging_0.2.0.json + +# GET policy types +curl -v "http://localhost:8085/A1-P/v1/policytypes" + +# GET policy type STD_QoSNudging_0.2.0 +curl -v "http://localhost:8085/A1-P/v1/policytypes/STD_QoSNudging_0.2.0" + +# PUT a policy instance pi1 +curl -X PUT -v "http://localhost:8085/A1-P/v1/policies/pi1?policyTypeId=STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @policy_instance_1_STD_QoSNudging_0.2.0.json + +# PUT a policy instance pi2 +curl -X PUT -v "http://localhost:8085/A1-P/v1/policies/pi2?policyTypeId=STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @policy_instance_2_STD_QoSNudging_0.2.0.json + +# SET status for pi1 and pi2 +curl -X PUT "http://localhost:8085/pi1/NOT_ENFORCED/300" +curl -X PUT "http://localhost:8085/pi2/ENFORCED" + +# GET policies +curl -v "http://localhost:8085/A1-P/v1/policies" + +# DELETE policy instance pi2 +curl -X DELETE -v "http://localhost:8085/A1-P/v1/policies/pi2" + +# PUT a different policy instance pi1 (i.e. update it) +curl -X PUT -v "http://localhost:8085/A1-P/v1/policies/pi1?policyTypeId=STD_QoSNudging_0.2.0" -H "accept: application/json" -H "Content-Type: application/json" --data-binary @policy_instance_1_bis_STD_QoSNudging_0.2.0.json + +# GET policy instance pi1 +curl -v "http://localhost:8085/A1-P/v1/policies/pi1" + +# GET policy status for pi1 +curl -v "http://localhost:8085/A1-P/v1/policystatus/pi1" diff --git a/near-rt-ric-simulator/ric-plt/a1/main.py b/near-rt-ric-simulator/ric-plt/a1/main.py new file mode 100644 index 00000000..48b7f2ea --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/main.py @@ -0,0 +1,98 @@ +import connexion +import fileinput +import json +import sys + +from flask import Flask, escape, request, make_response +from jsonschema import validate +from var_declaration import policy_instances, policy_types, policy_status, policy_type_per_instance + +app = connexion.App(__name__, specification_dir='.') + +@app.route('/policytypes/', methods=['PUT','DELETE']) +def policy_type(policyTypeId): + if request.method == 'PUT': + data = request.data.decode("utf-8") + data = data.replace("'", "\"") + data = json.loads(data) + policy_types[policyTypeId] = data + return ('The policy type was either created or updated for policy type id: ' + policyTypeId) + elif request.method == 'DELETE': + if policyTypeId in policy_types.keys(): + policy_types.pop(policyTypeId) + return make_response("policy type successfully deleted for policy type id: " + policyTypeId, 200) + else: + return make_response("No policy type defined for the specified id", 404) + +@app.route('/', methods=['GET']) +def test(): + return("Everything is fine", 200) + +@app.route('/deleteinstances', methods=['DELETE']) +def delete_instances(): + global policy_instances + global policy_status + global policy_type_per_instance + policy_instances.clear() + policy_status.clear() + policy_type_per_instance.clear() + return("All policy instances deleted", 200) + +@app.route('/deletetypes', methods=['DELETE']) +def delete_types(): + global policy_types + policy_types.clear() + return("All policy types deleted", 200) + +@app.route('//', methods=['PUT']) +def set_status(policyId, enforceStatus): + if policyId in policy_instances.keys(): + if policy_type_per_instance[policyId] == "UNDEFINED": + ps = {} + ps["policyId"] = policyId + ps["enforceStatus"] = enforceStatus + else: + policy_type_id = policy_type_per_instance[policyId] + status_schema = policy_types[policy_type_id]["statusSchema"] + ps = {} + ps["policyId"] = policyId + ps["enforceStatus"] = enforceStatus + try: + validate(instance=ps, schema=status_schema) + except: + return(set_error(None, "The json does not validate against the status schema.", 400, None, None, None, None, None)) + policy_status.pop(policyId) + policy_status[policyId] = ps + return("Status updated for policy: " + policyId, 200) + +@app.route('///', methods=['PUT']) +def set_status_with_reason(policyId, enforceStatus, enforceReason): + if policyId in policy_instances.keys(): + if policy_type_per_instance[policyId] == "UNDEFINED": + ps = {} + ps["policyId"] = policyId + ps["enforceStatus"] = enforceStatus + ps["enforceReason"] = enforceReason + else: + policy_type_id = policy_type_per_instance[policyId] + status_schema = policy_types[policy_type_id]["statusSchema"] + ps = {} + ps["policyId"] = policyId + ps["enforceStatus"] = enforceStatus + ps["enforceReason"] = enforceReason + try: + validate(instance=ps, schema=status_schema) + except: + return(set_error(None, "The json does not validate against the status schema.", 400, None, None, None, None, None)) + policy_status.pop(policyId) + policy_status[policyId] = ps + return("Status updated for policy: " + policyId, 200) + +port_number = 8085 +if len(sys.argv) >= 2: + if isinstance(sys.argv[1], int): + port_number = sys.argv[1] + +app.add_api('a1-openapi.yaml') +app.run(port=port_number) + diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.1.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.1.0.json new file mode 100644 index 00000000..8d1cdd68 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.1.0.json @@ -0,0 +1,18 @@ +{ + "policyId": "pi1", + "policyTypeId": "STD_QoSNudging_0.1.0", + "policyClause": { + "scope": { + "ueId": "ue1", + "groupId": "group1", + "sliceId": "slice1", + "qosId": "qos1", + "cellId": "cell1" + }, + "statement": { + "priorityLevel": 5 + } + }, + "notificationDestination": "http://localhost:8085/policynotifications" +} + diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.2.0.json new file mode 100644 index 00000000..74f22c75 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.2.0.json @@ -0,0 +1,13 @@ +{ + "scope": { + "ueId": "ue1", + "groupId": "group1", + "sliceId": "slice1", + "qosId": "qos1", + "cellId": "cell1" + }, + "statement": { + "priorityLevel": 5 + } +} + diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.1.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.1.0.json new file mode 100644 index 00000000..f43ca1a0 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.1.0.json @@ -0,0 +1,16 @@ +{ + "policyId": "pi1", + "policyTypeId": "STD_QoSNudging_0.1.0", + "policyClause": { + "scope": { + "ueId": "ue1", + "groupId": "group1", + "sliceId": "slice1", + "qosId": "qos1", + "cellId": "cell1"}, + "statement": { + "priorityLevel": 4 + } + }, + "notificationDestination": "http://localhost:8085/policynotifications" +} diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.2.0.json new file mode 100644 index 00000000..8d2e9856 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.2.0.json @@ -0,0 +1,11 @@ +{ + "scope": { + "ueId": "ue1", + "groupId": "group1", + "sliceId": "slice1", + "qosId": "qos1", + "cellId": "cell1"}, + "statement": { + "priorityLevel": 4 + } +} diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.1.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.1.0.json new file mode 100644 index 00000000..6890d050 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.1.0.json @@ -0,0 +1,17 @@ +{ + "policyId": "pi2", + "policyTypeId": "STD_QoSNudging_0.1.0", + "policyClause": { + "scope": { + "ueId": "ue2", + "groupId": "group2", + "sliceId": "slice2", + "qosId": "qos2", + "cellId": "cell2" + }, + "statement": { + "priorityLevel": 5 + } + }, + "notificationDestination": "http://localhost:8085/policynotifications" +} diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.2.0.json new file mode 100644 index 00000000..257298ca --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.2.0.json @@ -0,0 +1,12 @@ +{ + "scope": { + "ueId": "ue2", + "groupId": "group2", + "sliceId": "slice2", + "qosId": "qos2", + "cellId": "cell2" + }, + "statement": { + "priorityLevel": 5 + } +} diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.1.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.1.0.json new file mode 100644 index 00000000..8be17bb9 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.1.0.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "STD_QoSNudging_0.1.0", + "description": "QoS policy type with ueId and qosId scope, version 0.1.0", + "type": "object", + "properties": { + "scope": { + "type": "object", + "properties": { + "ueId": {"type": "string"}, + "qosId": {"type": "string"} + }, + "additionalProperties": false, + "required": ["ueId", "qosId"] + }, + "statement": { + "type": "object", + "properties": { + "priorityLevel": {"type": "number"} + }, + "additionalProperties": false, + "required": ["priorityLevel"] + } + } +} diff --git a/near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.2.0.json b/near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.2.0.json new file mode 100644 index 00000000..baf1bbec --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.2.0.json @@ -0,0 +1,46 @@ +{ + "policySchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "STD_QoSNudging_0.2.0", + "description": "QoS policy type", + "type": "object", + "properties": { + "scope": { + "type": "object", + "properties": { + "ueId": {"type": "string"}, + "qosId": {"type": "string"} + }, + "additionalProperties": true, + "required": ["ueId", "qosId"] + }, + "statement": { + "type": "object", + "properties": { + "priorityLevel": {"type": "number"} + }, + "additionalProperties": false, + "required": ["priorityLevel"] + } + } + }, + "statusSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "statusSchema", + "description": "statusSchema", + "type": "object", + "properties": { + "enforceStatus": { + "type": "string", + "enum": ["UNDEFINED", "ENFORCED", "NOT_ENFORCED"] + }, + "enforceReason": { + "type": "string", + "enum": ["100", "200", "300", "800"] + }, + "additionalProperties": false + }, + "if": {"properties": {"enforceStatus": {"const": "NOT_ENFORCED"}}}, + "then": {"required": ["enforceReason"]} + } +} diff --git a/near-rt-ric-simulator/ric-plt/a1/run_me.sh b/near-rt-ric-simulator/ric-plt/a1/run_me.sh new file mode 100755 index 00000000..9052ecf3 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/run_me.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# One argument can be used along with the script call: it is the port on which one wish to run the simulator. + +if [ $# -eq 0 ] +then + python3 ./main.py +else + python3 ./main.py $1 +fi diff --git a/near-rt-ric-simulator/ric-plt/a1/var_declaration.py b/near-rt-ric-simulator/ric-plt/a1/var_declaration.py new file mode 100644 index 00000000..03ee5270 --- /dev/null +++ b/near-rt-ric-simulator/ric-plt/a1/var_declaration.py @@ -0,0 +1,6 @@ +#!/u:sr/bin/env python3 + +policy_instances = {} +policy_types = {} +policy_status = {} +policy_type_per_instance = {} diff --git a/near-rt-ric-simulator/simulator-group/consul_cbs/config.json b/near-rt-ric-simulator/simulator-group/consul_cbs/config.json new file mode 100644 index 00000000..4dc0f6c5 --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/consul_cbs/config.json @@ -0,0 +1,13 @@ +{ + "//description": "Application configuration", + "ric": [ + { + "name": "ric3", + "baseUrl": "http://ric3:8085/", + "managedElementIds": [ + "kista_5", + "kista_6" + ] + } + ] +} diff --git a/near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_config.hcl b/near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_config.hcl new file mode 100644 index 00000000..9a9c0c4d --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_config.hcl @@ -0,0 +1,13 @@ +#server = true +#bootstrap = true +#client_addr = "0.0.0.0" + +service { + # Name for CBS in consul, env var CONFIG_BINDING_SERVICE + # should be passed to policy agent app with this value + Name = "config-binding-service" + # Host name where CBS is running + Address = "config-binding-service" + # Port number where CBS is running + Port = 10000 +} \ No newline at end of file diff --git a/near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_localhost_config.hcl b/near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_localhost_config.hcl new file mode 100644 index 00000000..f6f3dac2 --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_localhost_config.hcl @@ -0,0 +1,11 @@ +service { + # Name for CBS in consul, env var CONFIG_BINDING_SERVICE + # should be passed to policy agent app with this value + # This is only to be used when contacting cbs via local host + # (typicall when policy agent is executed as an application without a container) + Name = "config-binding-service-localhost" + # Host name where CBS is running + Address = "localhost" + # Port number where CBS is running + Port = 10000 +} \ No newline at end of file diff --git a/near-rt-ric-simulator/simulator-group/consul_cbs/docker-compose-template.yml b/near-rt-ric-simulator/simulator-group/consul_cbs/docker-compose-template.yml new file mode 100644 index 00000000..2f7ac3ed --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/consul_cbs/docker-compose-template.yml @@ -0,0 +1,38 @@ +version: '3' + +networks: + nonrtric-docker-net: + external: + name: nonrtric-docker-net + +services: + + consul-server: + networks: + - nonrtric-docker-net + container_name: polman_consul + image: docker.io/consul:1.4.4 + ports: + - "8500:8500" + volumes: + - ./consul/:/consul/config + + config-binding-service: + networks: + - nonrtric-docker-net + container_name: polman_cbs + image: nexus3.onap.org:10001/onap/org.onap.dcaegen2.platform.configbinding.app-app:2.3.0 + ports: + - "10000:10000" + environment: + - CONSUL_HOST=consul-server + depends_on: + - consul-server + + ric-simulator: + networks: + - nonrtric-docker-net + container_name: ric3 + image: ric-simulator:latest + ports: + - "8085:8085" diff --git a/near-rt-ric-simulator/simulator-group/consul_cbs/start.sh b/near-rt-ric-simulator/simulator-group/consul_cbs/start.sh new file mode 100755 index 00000000..ed4f9e60 --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/consul_cbs/start.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +DOCKER_SIM_NWNAME="nonrtric-docker-net" +echo "Creating docker network $DOCKER_SIM_NWNAME, if needed" +docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME + +docker-compose -f docker-compose-template.yml config > docker-compose.yml + +docker-compose up -d + +CONSUL_PORT=8500 + +APP="policy-agent" +JSON_FILE="config.json" + +curl -s -v http://127.0.0.1:${CONSUL_PORT}/v1/kv/${APP}?dc=dc1 -X PUT -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'X-Requested-With: XMLHttpRequest' --data-binary "@"$JSON_FILE \ No newline at end of file diff --git a/near-rt-ric-simulator/simulator-group/simulators-start.sh b/near-rt-ric-simulator/simulator-group/simulators-start.sh new file mode 100755 index 00000000..7f833973 --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/simulators-start.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +DOCKER_SIM_NWNAME="nonrtric-docker-net" +echo "Creating docker network $DOCKER_SIM_NWNAME, if needed" +docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME + +docker-compose -f consul_cbs/docker-compose-template.yml config > docker-compose.yml + +docker-compose up -d \ No newline at end of file diff --git a/policy-agent/.gitignore b/policy-agent/.gitignore new file mode 100644 index 00000000..ca6a2f3e --- /dev/null +++ b/policy-agent/.gitignore @@ -0,0 +1,7 @@ +#Eclipse +.project +.classpath +.settings +target +.checkstyle +policy-agent.iml \ No newline at end of file diff --git a/policy-agent/Dockerfile b/policy-agent/Dockerfile new file mode 100644 index 00000000..26ce5317 --- /dev/null +++ b/policy-agent/Dockerfile @@ -0,0 +1,40 @@ +# +# ============LICENSE_START======================================================= +# Copyright (C) 2019 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/policy-agent +RUN mkdir -p /var/log/policy-agent +RUN mkdir -p /opt/app/policy-agent/etc/cert/ + +EXPOSE 8081 + +ADD /config/* /opt/app/policy-agent/config/ +ADD target/${JAR} /opt/app/policy-agent/policy-agent.jar + + +RUN chmod -R 777 /opt/app/policy-agent/config/ + +CMD ["java", "-jar", "/opt/app/policy-agent/policy-agent.jar"] + + + + diff --git a/policy-agent/README.md b/policy-agent/README.md new file mode 100644 index 00000000..bbd6fcce --- /dev/null +++ b/policy-agent/README.md @@ -0,0 +1,67 @@ +# O-RAN-SC NonRT RIC Dashboard Web Application + +The O-RAN NonRT RIC PolicyAgent provides a REST API for management of policices. +It provides support for: + -Supervision of clients (R-APPs) to eliminate stray policies in case of failure + -Consistency monitoring of the SMO view of policies and the actual situation in the RICs + -Consistency monitoring of RIC capabilities (policy types) + -Policy configuration. This includes: + -One REST API towards all RICs in the network + -Query functions that can find all policies in a RIC, all policies owned by a service (R-APP), + all policies of a type etc. + -Maps O1 resources (ManagedElement) as defined in O1 to the controlling RIC + +To Run Policy Agent in Local: +In the folder /opt/app/policy-agent/config/, create a soft link with below command, +ln -s application_configuration.json + +To Run Policy Agent in Local with the DMaaP polling turned on: +In the folder /opt/app/policy-agent/config/, create a soft link with below command, +ln -s application_configuration.json + +The agent can be run stand alone in a simulated test mode. Then it simulates RICs. +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:8081/swagger-ui.html` + +PolicyAgent uses A1-POLICY-AGENT-READ & A1-POLICY-AGENT-WRITE topic for subscribe & Publish to the DMaap. +Sample Request Message to DMaaP: +{ + "type": "request", + "target": "policy-agent", + "timestamp": "2019-05-14T11:44:51.36Z", + "operation": "GET", + "correlationId": "c09ac7d1-de62-0016-2000-e63701125557-201", + "apiVersion": "1.0", + "originatorId": "849e6c6b420", + "requestId": "23343221", + "url": "/policies?type=type1&ric=ric1&service=service1" +} + +Sample Response Message to DMaaP: +{ + "type": "response", + "timestamp": "2019-05-14T11:44:51.36Z", + "correlationId": "c09ac7d1-de62-0016-2000-e63701125557-201", + "originatorId": "849e6c6b420", + "requestId": "23343221", + "status": "200 OK", + "message": [] +} + +## License + +Copyright (C) 2019 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/policy-agent/config/application.yaml b/policy-agent/config/application.yaml new file mode 100644 index 00000000..90b73a46 --- /dev/null +++ b/policy-agent/config/application.yaml @@ -0,0 +1,27 @@ +spring: + profiles: + active: prod + main: + allow-bean-definition-overriding: true +management: + endpoints: + web: + exposure: + include: "loggers,logfile,health,info,metrics" + +logging: + level: + ROOT: ERROR + org.springframework: ERROR + org.springframework.data: ERROR + org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR + org.oransc.policyagent: WARN + file: /var/log/policy-agent/application.log +app: + filepath: /opt/app/policy-agent/config/application_configuration.json + a1ControllerBaseUrl: http://sdnc.onap:8282 + a1ControllerUsername: admin + a1ControllerPassword: Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U + +server: + port : 8081 diff --git a/policy-agent/config/application_configuration.json b/policy-agent/config/application_configuration.json new file mode 100644 index 00000000..8f3f80b2 --- /dev/null +++ b/policy-agent/config/application_configuration.json @@ -0,0 +1,15 @@ +{ + "config": { + "//description": "Application configuration", + "ric": [ + { + "name": "ric1", + "baseUrl": "http://ric1:8085/", + "managedElementIds": [ + "kista_1", + "kista_2" + ] + } + ] + } +} \ No newline at end of file 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/dpo/blueprints/k8s-policy-agent.yaml b/policy-agent/dpo/blueprints/k8s-policy-agent.yaml new file mode 100644 index 00000000..0121d27f --- /dev/null +++ b/policy-agent/dpo/blueprints/k8s-policy-agent.yaml @@ -0,0 +1,111 @@ + +#description: Docker application of policy agent managing policies +#blueprint_version: 1.0.0 +--- +tosca_definitions_version: cloudify_dsl_1_3 +description: Docker application to collect log file from PNF +imports: + - http://www.getcloudify.org/spec/cloudify/4.3.1/types.yaml + - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R5/k8splugin/1.6.0/k8splugin_types.yaml + - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.ccsdk.platform.plugins/type_files/dmaap/dmaap.yaml +inputs: + policy-agent_cpu_limit: + type: string + default: "250m" + policy-agent_cpu_request: + type: string + default: "250m" + policy-agent_memory_limit: + type: string + default: "256Mi" + policy-agent_memory_request: + type: string + default: "256Mi" + envs: + default: {} + external_port: + type: string + default: ":0" + publish_topic_name: + type: string + default: "A1-POLICY-AGENT-WRITE" + subscribe_topic_name: + type: string + default: "A1-POLICY-AGENT-READ" + consumer_group: + type: string + default: "users" + consumer_id: + type: string + default: "policy-agent" + log_directory: + type: string + default: "/var/log/policy-agent" + replicas: + type: integer + description: number of instances + default: 1 + tag_version: + type: string + default: "nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent:1.0.0" +node_templates: + policy-agent: + type: dcae.nodes.ContainerizedServiceComponentUsingDmaap + interfaces: + cloudify.interfaces.lifecycle: + start: + inputs: + envs: + get_input: envs + properties: + application_config: + streams_publishes: + dmaap_publisher: + dmaap_info: + topic_url: { concat: ['https://message-router:3905/events/',{ get_input: publish_topic_name }] } + type: message_router + streams_subscribes: + dmaap_subscriber: + dmaap_info: + topic_url: { concat: ['https://message-router:3905/events/',{ get_input: subscribe_topic_name }, '/', { get_input: consumer_group }, "/", { get_input: consumer_id }] } + type: message_router + ric: + - name: ric1 + baseUrl: http://localhost:8083/ + managedElementIds: + - kista_1 + - kista_2 + - name: ric2 + baseUrl: http://localhost:8085/ + managedElementIds: + - kista_3 + - kista_4 + docker_config: + healthcheck: + interval: 15s + timeout: 1s + type: http + endpoint: /status + ports: + - concat: ["8081", {get_input: external_port}] + image: + get_input: tag_version + service_component_type: policy-agent + log_info: + log_directory: + get_input: log_directory + replicas: + get_input: replicas + resource_config: + limits: + cpu: + get_input: policy-agent_cpu_limit + memory: + get_input: policy-agent_memory_limit + requests: + cpu: + get_input: policy-agent_cpu_request + memory: + get_input: policy-agent_memory_request + + diff --git a/policy-agent/eclipse-formatter.xml b/policy-agent/eclipse-formatter.xml new file mode 100644 index 00000000..7339434a --- /dev/null +++ b/policy-agent/eclipse-formatter.xml @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/policy-agent/pom.xml b/policy-agent/pom.xml new file mode 100644 index 00000000..311229fd --- /dev/null +++ b/policy-agent/pom.xml @@ -0,0 +1,325 @@ + + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.4.RELEASE + + + org.o-ran-sc.nonrtric + policy-agent + 1.0.0-SNAPSHOT + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + onap-releases + onap-releases + https://nexus.onap.org/content/repositories/releases/ + + + + 11 + 2.8.0 + 2.7.1 + 1.1.6 + 2.0.0 + 20180130 + 3.3 + 4.0.1 + 3.8.0 + 2.8.1 + 1.18.0 + 0.30.0 + 1.1.9 + 2.1.1 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework + spring-webflux + + + io.swagger.core.v3 + swagger-jaxrs2 + ${swagger.version} + + + io.swagger.core.v3 + swagger-jaxrs2-servlet-initializer + ${swagger.version} + + + javax.xml.bind + jaxb-api + + + org.immutables + value + ${immutable.version} + provided + + + org.immutables + gson + ${immutable.version} + + + org.json + json + ${json.version} + + + commons-net + commons-net + ${commons-net.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.onap.dcaegen2.services.sdk.rest.services + cbs-client + ${sdk.version} + + + org.onap.dcaegen2.services.sdk.rest.services + dmaap-client + ${sdk.version} + + + org.projectlombok + lombok + provided + + + + io.springfox + springfox-swagger2 + ${springfox.version} + + + io.springfox + springfox-swagger-ui + ${springfox.version} + + + + org.awaitility + awaitility + ${awaitility.version} + test + + + io.projectreactor + reactor-test + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.mockito + mockito-junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.onap.dmaap.messagerouter.dmaapclient + dmaapClient + ${version.dmaap} + + + javax.ws.rs + javax.ws.rs-api + ${javax.ws.rs-api.version} + + + org.glassfish.jersey.inject + jersey-hk2 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + net.revelc.code.formatter + formatter-maven-plugin + ${formatter-maven-plugin.version} + + ${project.basedir}/eclipse-formatter.xml + + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless-maven-plugin.version} + + + + + com,java,javax,org + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + maven-failsafe-plugin + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/annotations/ + + + + + + + io.fabric8 + docker-maven-plugin + ${docker-maven-plugin} + false + + + generate-policy-agent-image + package + + build + + + ${env.CONTAINER_PULL_REGISTRY} + + + o-ran-sc/nonrtric-policy-agent:${project.version} + + try + ${basedir} + Dockerfile + + ${project.build.finalName}.jar + + + ${project.version} + + + + + + + + push-policy-agent-image + + build + push + + + ${env.CONTAINER_PULL_REGISTRY} + ${env.CONTAINER_PUSH_REGISTRY} + + + o-ran-sc/nonrtric-policy-agent:${project.version} + + ${basedir} + Dockerfile + + ${project.build.finalName}.jar + + + ${project.version} + + + + + + + + + + + + JIRA + https://jira.o-ran-sc.org/ + + diff --git a/policy-agent/src/main/java/org/oransc/policyagent/Application.java b/policy-agent/src/main/java/org/oransc/policyagent/Application.java new file mode 100644 index 00000000..6d8cd99e --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/Application.java @@ -0,0 +1,55 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent; + +import org.oransc.policyagent.tasks.StartupService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class Application { + + @Autowired + private StartupService startupService; + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + /** + * Starts the service and reads the configuration. + * + * @param ctx the application context. + * + * @return the command line runner performing tasks at startup. + */ + @Bean + public CommandLineRunner commandLineRunner(ApplicationContext ctx) { + return args -> { + + startupService.startup(); + }; + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java b/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java new file mode 100644 index 00000000..8297b221 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java @@ -0,0 +1,71 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.repository.Services; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class BeanFactory { + @Bean + public Policies getPolicies() { + return new Policies(); + } + + @Bean + public PolicyTypes getPolicyTypes() { + return new PolicyTypes(); + } + + @Bean + public Rics getRics() { + return new Rics(); + } + + @Bean + public ApplicationConfig getApplicationConfig() { + return new ApplicationConfig(); + } + + @Bean + Services getServices() { + return new Services(); + } + + @Bean + A1ClientFactory getA1ClientFactory() { + return new A1ClientFactory(getApplicationConfig()); + } + + @Bean + public ObjectMapper mapper() { + return new ObjectMapper(); + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java b/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java new file mode 100644 index 00000000..f3b4a5a4 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java @@ -0,0 +1,48 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger configuration class that uses swagger2 documentation type and scans all the controllers + * under org.oransc.policyagent.controllers package. To access the swagger gui go to + * http://ip:port/swagger-ui.html + * + */ +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2).select() // + .apis(RequestHandlerSelectors.basePackage("org.oransc.policyagent.controllers")) // + .paths(PathSelectors.any()) // + .build(); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1Client.java new file mode 100644 index 00000000..0a2bc8ed --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1Client.java @@ -0,0 +1,50 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import java.util.List; + +import org.oransc.policyagent.repository.Policy; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface A1Client { + + public static enum A1ProtocolType { + UNKNOWN, STD_V1, OSC_V1, SDNC_OSC, SDNR_ONAP + } + + public Mono getProtocolVersion(); + + public Mono> getPolicyTypeIdentities(); + + public Mono> getPolicyIdentities(); + + public Mono getPolicyTypeSchema(String policyTypeId); + + public Mono putPolicy(Policy policy); + + public Mono deletePolicy(Policy policy); + + public Flux deleteAllPolicies(); + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java new file mode 100644 index 00000000..c150c089 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java @@ -0,0 +1,94 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import org.oransc.policyagent.clients.A1Client.A1ProtocolType; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.exceptions.ServiceException; +import org.oransc.policyagent.repository.Ric; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import reactor.core.publisher.Mono; + +public class A1ClientFactory { + + private static final Logger logger = LoggerFactory.getLogger(A1ClientFactory.class); + + private final ApplicationConfig appConfig; + + @Autowired + public A1ClientFactory(ApplicationConfig appConfig) { + this.appConfig = appConfig; + } + + public Mono createA1Client(Ric ric) { + return getProtocolVersion(ric) // + .flatMap(version -> createA1Client(ric, version)); + } + + private Mono createA1Client(Ric ric, A1ProtocolType version) { + if (version == A1ProtocolType.STD_V1) { + return Mono.just(createStdA1ClientImpl(ric)); + } else if (version == A1ProtocolType.OSC_V1) { + return Mono.just(new OscA1Client(ric.getConfig())); + } else if (version == A1ProtocolType.SDNC_OSC) { + return Mono.just(createSdncOscA1Client(ric)); + } else if (version == A1ProtocolType.SDNR_ONAP) { + return Mono.just(createSdnrOnapA1Client(ric)); + } + return Mono.error(new ServiceException("Not supported protocoltype: " + version)); + } + + private Mono getProtocolVersion(Ric ric) { + if (ric.getProtocolVersion() == A1ProtocolType.UNKNOWN) { + return fetchVersion(ric, createSdnrOnapA1Client(ric)) // + .onErrorResume(err -> fetchVersion(ric, createSdncOscA1Client(ric))) + .onErrorResume(err -> fetchVersion(ric, new OscA1Client(ric.getConfig()))) + .onErrorResume(err -> fetchVersion(ric, createStdA1ClientImpl(ric))) + .doOnNext(version -> ric.setProtocolVersion(version)) + .doOnNext(version -> logger.debug("Recover ric: {}, protocol version:{}", ric.name(), version)) // + .doOnError(t -> logger.warn("Could not get protocol version from RIC: {}", ric.name())); // + } else { + return Mono.just(ric.getProtocolVersion()); + } + } + + protected A1Client createStdA1ClientImpl(Ric ric) { + return new StdA1Client(ric.getConfig()); + } + + protected A1Client createSdncOscA1Client(Ric ric) { + return new SdncOscA1Client(ric.getConfig(), appConfig.getA1ControllerBaseUrl(), + appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword()); + } + + protected A1Client createSdnrOnapA1Client(Ric ric) { + return new SdnrOnapA1Client(ric.getConfig(), appConfig.getA1ControllerBaseUrl(), + appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword()); + } + + private Mono fetchVersion(Ric ric, A1Client a1Client) { + return Mono.just(a1Client) // + .flatMap(client -> a1Client.getProtocolVersion()); + } + +} 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 new file mode 100644 index 00000000..b10cf279 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java @@ -0,0 +1,102 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ +package org.oransc.policyagent.clients; + +import java.lang.invoke.MethodHandles; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +public class AsyncRestClient { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private final WebClient client; + + private static class AsyncRestClientException extends Exception { + + private static final long serialVersionUID = 1L; + + public AsyncRestClientException(String message) { + super(message); + } + } + + public AsyncRestClient(String baseUrl) { + this.client = WebClient.create(baseUrl); + } + + public Mono post(String uri, String body) { + return client.post() // + .uri(uri) // + .contentType(MediaType.APPLICATION_JSON) // + .bodyValue(body) // + .retrieve() // + .onStatus(HttpStatus::isError, + response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // + .bodyToMono(String.class); + } + + public Mono postWithAuthHeader(String uri, String body, String username, String password) { + return client.post() // + .uri(uri) // + .headers(headers -> headers.setBasicAuth(username, password)) // + .contentType(MediaType.APPLICATION_JSON) // + .bodyValue(body) // + .retrieve() // + .onStatus(HttpStatus::isError, + response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // + .bodyToMono(String.class); + } + + public Mono put(String uri, String body) { + logger.debug("PUT uri = '{}''", uri); + return client.put() // + .uri(uri) // + .contentType(MediaType.APPLICATION_JSON) // + .bodyValue(body) // + .retrieve() // + .onStatus(HttpStatus::isError, + response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // + .bodyToMono(String.class); + } + + public Mono get(String uri) { + logger.debug("GET uri = '{}''", uri); + return client.get() // + .uri(uri) // + .retrieve() // + .onStatus(HttpStatus::isError, + response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // + .bodyToMono(String.class); + } + + public Mono delete(String uri) { + logger.debug("DELETE uri = '{}''", uri); + return client.delete() // + .uri(uri) // + .retrieve() // + .onStatus(HttpStatus::isError, + response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // + .bodyToMono(String.class); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/OscA1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/OscA1Client.java new file mode 100644 index 00000000..dd77504d --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/OscA1Client.java @@ -0,0 +1,133 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.Policy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class OscA1Client implements A1Client { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final AsyncRestClient restClient; + + public OscA1Client(RicConfig ricConfig) { + String baseUrl = ricConfig.baseUrl() + "/a1-p"; + this.restClient = new AsyncRestClient(baseUrl); + logger.debug("OscA1Client for ric: {}", ricConfig.name()); + } + + @Override + public Mono> getPolicyTypeIdentities() { + return restClient.get("/policytypes") // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono> getPolicyIdentities() { + return getPolicyTypeIdentities() // + .flatMapMany(types -> Flux.fromIterable(types)) // + .flatMap(type -> getPolicyIdentities(type)) // + .flatMap(policyIds -> Flux.fromIterable(policyIds)) // + .collectList(); + } + + private Mono> getPolicyIdentities(String typeId) { + return restClient.get("/policytypes/" + typeId + "/policies") // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + return restClient.get("/policytypes/" + policyTypeId) // + .flatMap(response -> getCreateSchema(response, policyTypeId)); + } + + private Mono getCreateSchema(String policyTypeResponse, String policyTypeId) { + try { + JSONObject obj = new JSONObject(policyTypeResponse); + JSONObject schemaObj = obj.getJSONObject("create_schema"); + schemaObj.put("title", policyTypeId); + return Mono.just(schemaObj.toString()); + } catch (Exception e) { + logger.error("Unexcpected response for policy type: {}", policyTypeResponse, e); + return Mono.error(e); + } + } + + @Override + public Mono putPolicy(Policy policy) { + return restClient.put("/policytypes/" + policy.type().name() + "/policies/" + policy.id(), policy.json()); + } + + @Override + public Mono deletePolicy(Policy policy) { + return deletePolicy(policy.type().name(), policy.id()); + } + + private Mono deletePolicy(String typeId, String policyId) { + return restClient.delete("/policytypes/" + typeId + "/policies/" + policyId); + } + + @Override + public Mono getProtocolVersion() { + return restClient.get("/healthcheck") // + .flatMap(resp -> Mono.just(A1ProtocolType.OSC_V1)); + } + + @Override + public Flux deleteAllPolicies() { + return getPolicyTypeIdentities() // + .flatMapMany(types -> Flux.fromIterable(types)) // + .flatMap(typeId -> deletePoliciesForType(typeId)); // + } + + private Flux deletePoliciesForType(String typeId) { + return getPolicyIdentities(typeId) // + .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) // + .flatMap(policyId -> deletePolicy(typeId, policyId)); // + } + + private Mono> parseJsonArrayOfString(String inputString) { + try { + List arrayList = new ArrayList<>(); + JSONArray jsonArray = new JSONArray(inputString); + for (int i = 0; i < jsonArray.length(); i++) { + arrayList.add(jsonArray.getString(i)); + } + logger.debug("A1 client: received list = {}", arrayList); + return Mono.just(arrayList); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java new file mode 100644 index 00000000..2f8fab48 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java @@ -0,0 +1,203 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.Policy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class SdncOscA1Client implements A1Client { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private String a1ControllerBaseUrl; + private String a1ControllerUsername; + private String a1ControllerPassword; + private final RicConfig ricConfig; + private final AsyncRestClient restClient; + + public SdncOscA1Client(RicConfig ricConfig, String baseUrl, String username, String password) { + this.ricConfig = ricConfig; + this.a1ControllerBaseUrl = baseUrl; + this.a1ControllerUsername = username; + this.a1ControllerPassword = password; + this.restClient = new AsyncRestClient(a1ControllerBaseUrl + "/restconf/operations"); + logger.debug("SdncOscA1Client for ric: {}, a1ControllerBaseUrl: {}", this.ricConfig.name(), + a1ControllerBaseUrl); + } + + @Override + public Mono> getPolicyTypeIdentities() { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-url", ricConfig.baseUrl()); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST getPolicyTypeIdentities inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:getPolicyTypeIdentities", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "policy-type-id-list")) // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono> getPolicyIdentities() { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-url", ricConfig.baseUrl()); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST getPolicyIdentities inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:getPolicyIdentities", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "policy-id-list")) // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-url", ricConfig.baseUrl()); + paramsJson.put("policy-type-id", policyTypeId); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST getPolicyType inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:getPolicyType", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "policy-type")) // + .flatMap(this::extractPolicySchema); + } + + @Override + public Mono putPolicy(Policy policy) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-url", ricConfig.baseUrl()); + paramsJson.put("policy-id", policy.id()); + paramsJson.put("policy-type-id", policy.type().name()); + paramsJson.put("policy", policy.json()); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST putPolicy inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:putPolicy", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "returned-policy")) // + .flatMap(this::validateJson); + } + + @Override + public Mono deletePolicy(Policy policy) { + return deletePolicy(policy.id()); + } + + @Override + public Flux deleteAllPolicies() { + return getPolicyIdentities() // + .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) // ) + .flatMap(policyId -> deletePolicy(policyId)); // + } + + public Mono deletePolicy(String policyId) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-url", ricConfig.baseUrl()); + paramsJson.put("policy-id", policyId); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST deletePolicy inputJsonString = {}", inputJsonString); + + return restClient.postWithAuthHeader("/A1-ADAPTER-API:deletePolicy", inputJsonString, a1ControllerUsername, + a1ControllerPassword); + } + + @Override + public Mono getProtocolVersion() { + return getPolicyTypeIdentities() // + .flatMap(x -> Mono.just(A1ProtocolType.SDNC_OSC)); + } + + private String createInputJsonString(JSONObject paramsJson) { + JSONObject inputJson = new JSONObject(); + inputJson.put("input", paramsJson); + return inputJson.toString(); + } + + private Mono getValueFromResponse(String response, String key) { + logger.debug("A1 client: response = {}", response); + try { + JSONObject outputJson = new JSONObject(response); + JSONObject responseParams = outputJson.getJSONObject("output"); + if (!responseParams.has(key)) { + return Mono.just(""); + } + String value = responseParams.get(key).toString(); + return Mono.just(value); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono> parseJsonArrayOfString(String inputString) { + try { + List arrayList = new ArrayList<>(); + if (inputString.isEmpty()) { + return Mono.just(arrayList); + } + JSONArray jsonArray = new JSONArray(inputString); + for (int i = 0; i < jsonArray.length(); i++) { + arrayList.add(jsonArray.getString(i)); + } + logger.debug("A1 client: received list = {}", arrayList); + return Mono.just(arrayList); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono extractPolicySchema(String inputString) { + try { + JSONObject jsonObject = new JSONObject(inputString); + JSONObject schemaObject = jsonObject.getJSONObject("policySchema"); + String schemaString = schemaObject.toString(); + return Mono.just(schemaString); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono validateJson(String inputString) { + try { + new JSONObject(inputString); + return Mono.just(inputString); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdnrOnapA1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdnrOnapA1Client.java new file mode 100644 index 00000000..1993be76 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdnrOnapA1Client.java @@ -0,0 +1,208 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.Policy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class SdnrOnapA1Client implements A1Client { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private String a1ControllerBaseUrl; + private String a1ControllerUsername; + private String a1ControllerPassword; + private final RicConfig ricConfig; + private final AsyncRestClient restClient; + + public SdnrOnapA1Client(RicConfig ricConfig, String baseUrl, String username, String password) { + this.ricConfig = ricConfig; + this.a1ControllerBaseUrl = baseUrl; + this.a1ControllerUsername = username; + this.a1ControllerPassword = password; + this.restClient = new AsyncRestClient(a1ControllerBaseUrl + "/restconf/operations"); + logger.debug("SdnrOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", this.ricConfig.name(), + a1ControllerBaseUrl); + } + + @Override + public Mono> getPolicyTypeIdentities() { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-id", ricConfig.baseUrl()); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST getPolicyTypeIdentities inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:getPolicyTypes", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "policy-type-id-list")) // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono> getPolicyIdentities() { + return getPolicyTypeIdentities() // + .flatMapMany(types -> Flux.fromIterable(types)) // + .flatMap(type -> getPolicyIdentities(type)) // + .flatMap(policyIds -> Flux.fromIterable(policyIds)) // + .collectList(); + } + + public Mono> getPolicyIdentities(String policyTypeId) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-id", ricConfig.baseUrl()); + paramsJson.put("policy-type-id", policyTypeId); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST getPolicyIdentities inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:getPolicyInstances", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "policy-instance-id-list")) // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-id", ricConfig.baseUrl()); + paramsJson.put("policy-type-id", policyTypeId); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST getPolicyType inputJsonString = {}", inputJsonString); + + return restClient + .postWithAuthHeader("/A1-ADAPTER-API:getPolicyType", inputJsonString, a1ControllerUsername, + a1ControllerPassword) // + .flatMap(response -> getValueFromResponse(response, "policy-type")) // + .flatMap(this::extractPolicySchema); + } + + @Override + public Mono putPolicy(Policy policy) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-id", ricConfig.baseUrl()); + paramsJson.put("policy-instance-id", policy.id()); + paramsJson.put("policy-type-id", policy.type().name()); + paramsJson.put("policy-instance", policy.json()); + paramsJson.put("properties", new JSONArray()); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST putPolicy inputJsonString = {}", inputJsonString); + + return restClient.postWithAuthHeader("/A1-ADAPTER-API:createPolicyInstance", inputJsonString, + a1ControllerUsername, a1ControllerPassword); + } + + public Mono deletePolicy(String policyTypeId, String policyId) { + JSONObject paramsJson = new JSONObject(); + paramsJson.put("near-rt-ric-id", ricConfig.baseUrl()); + paramsJson.put("policy-instance-id", policyId); + paramsJson.put("policy-type-id", policyTypeId); + String inputJsonString = createInputJsonString(paramsJson); + logger.debug("POST deletePolicy inputJsonString = {}", inputJsonString); + + return restClient.postWithAuthHeader("/A1-ADAPTER-API:deletePolicyInstance", inputJsonString, + a1ControllerUsername, a1ControllerPassword); + } + + @Override + public Mono deletePolicy(Policy policy) { + return deletePolicy(policy.type().name(), policy.id()); + } + + @Override + public Flux deleteAllPolicies() { + return getPolicyTypeIdentities() // + .flatMapMany(types -> Flux.fromIterable(types)) // + .flatMap(typeId -> deletePoliciesForType(typeId)); // + } + + private Flux deletePoliciesForType(String typeId) { + return getPolicyIdentities(typeId) // + .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) // + .flatMap(policyId -> deletePolicy(typeId, policyId)); // + } + + @Override + public Mono getProtocolVersion() { + return getPolicyTypeIdentities() // + .flatMap(x -> Mono.just(A1ProtocolType.SDNR_ONAP)); + } + + private String createInputJsonString(JSONObject paramsJson) { + JSONObject inputJson = new JSONObject(); + inputJson.put("input", paramsJson); + return inputJson.toString(); + } + + private Mono getValueFromResponse(String response, String key) { + logger.debug("A1 client: response = {}", response); + try { + JSONObject outputJson = new JSONObject(response); + JSONObject responseParams = outputJson.getJSONObject("output"); + if (!responseParams.has(key)) { + return Mono.just(""); + } + String value = responseParams.get(key).toString(); + return Mono.just(value); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono> parseJsonArrayOfString(String inputString) { + try { + List arrayList = new ArrayList<>(); + if (inputString.isEmpty()) { + return Mono.just(arrayList); + } + JSONArray jsonArray = new JSONArray(inputString); + for (int i = 0; i < jsonArray.length(); i++) { + arrayList.add(jsonArray.getString(i)); + } + logger.debug("A1 client: received list = {}", arrayList); + return Mono.just(arrayList); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono extractPolicySchema(String inputString) { + try { + JSONObject jsonObject = new JSONObject(inputString); + JSONObject schemaObject = jsonObject.getJSONObject("policySchema"); + String schemaString = schemaObject.toString(); + return Mono.just(schemaString); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/StdA1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/StdA1Client.java new file mode 100644 index 00000000..1c40308a --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/StdA1Client.java @@ -0,0 +1,133 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.Policy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class StdA1Client implements A1Client { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final AsyncRestClient restClient; + + public StdA1Client(RicConfig ricConfig) { + String baseUrl = ricConfig.baseUrl() + "/A1-P/v1"; + this.restClient = new AsyncRestClient(baseUrl); + } + + public StdA1Client(RicConfig ricConfig, AsyncRestClient restClient) { + this.restClient = restClient; + } + + @Override + public Mono> getPolicyIdentities() { + return restClient.get("/policies") // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono putPolicy(Policy policy) { + String url = "/policies/" + policy.id() + "?policyTypeId=" + policy.type().name(); + return restClient.put(url, policy.json()) // + .flatMap(this::validateJson); + } + + @Override + public Mono> getPolicyTypeIdentities() { + return restClient.get("/policytypes") // + .flatMap(this::parseJsonArrayOfString); + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + return restClient.get("/policytypes/" + policyTypeId) // + .flatMap(this::extractPolicySchema); + } + + @Override + public Mono deletePolicy(Policy policy) { + return deletePolicy(policy.id()); + } + + @Override + public Flux deleteAllPolicies() { + return getPolicyIdentities() // + .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) // ) + .flatMap(policyId -> deletePolicy(policyId)); // + } + + @Override + public Mono getProtocolVersion() { + return getPolicyTypeIdentities() // + .flatMap(x -> Mono.just(A1ProtocolType.STD_V1)); + } + + private Mono deletePolicy(String policyId) { + return restClient.delete("/policies/" + policyId); + } + + private Mono> parseJsonArrayOfString(String inputString) { + try { + List arrayList = new ArrayList<>(); + JSONArray jsonArray = new JSONArray(inputString); + for (int i = 0; i < jsonArray.length(); i++) { + arrayList.add(jsonArray.getString(i)); + } + logger.debug("A1 client: received list = {}", arrayList); + return Mono.just(arrayList); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono extractPolicySchema(String inputString) { + try { + JSONObject jsonObject = new JSONObject(inputString); + JSONObject schemaObject = jsonObject.getJSONObject("policySchema"); + String schemaString = schemaObject.toString(); + return Mono.just(schemaString); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + + private Mono validateJson(String inputString) { + try { + new JSONObject(inputString); + return Mono.just(inputString); + } catch (JSONException ex) { // invalid json + return Mono.error(ex); + } + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfig.java b/policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfig.java new file mode 100644 index 00000000..1e8c6d47 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfig.java @@ -0,0 +1,172 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.configuration; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +import lombok.Getter; + +import org.oransc.policyagent.exceptions.ServiceException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@EnableConfigurationProperties +@ConfigurationProperties("app") +public class ApplicationConfig { + @NotEmpty + private String filepath; + + @NotEmpty + private String a1ControllerBaseUrl; + + @NotEmpty + private String a1ControllerUsername; + + @NotEmpty + private String a1ControllerPassword; + + private Collection observers = new Vector<>(); + private Map ricConfigs = new HashMap<>(); + @Getter + private Properties dmaapPublisherConfig; + @Getter + private Properties dmaapConsumerConfig; + + @Autowired + public ApplicationConfig() { + } + + public String getLocalConfigurationFilePath() { + return this.filepath; + } + + public String getA1ControllerBaseUrl() { + return this.a1ControllerBaseUrl; + } + + public String getA1ControllerUsername() { + return this.a1ControllerUsername; + } + + public String getA1ControllerPassword() { + return this.a1ControllerPassword; + } + + /* + * Do not remove, used by framework! + */ + public synchronized void setFilepath(String filepath) { + this.filepath = filepath; + } + + public synchronized void setA1ControllerBaseUrl(String a1ControllerBaseUrl) { + this.a1ControllerBaseUrl = a1ControllerBaseUrl; + } + + public synchronized void setA1ControllerUsername(String a1ControllerUsername) { + this.a1ControllerUsername = a1ControllerUsername; + } + + public synchronized void setA1ControllerPassword(String a1ControllerPassword) { + this.a1ControllerPassword = a1ControllerPassword; + } + + public synchronized Collection getRicConfigs() { + return this.ricConfigs.values(); + } + + public RicConfig getRic(String ricName) throws ServiceException { + for (RicConfig ricConfig : getRicConfigs()) { + if (ricConfig.name().equals(ricName)) { + return ricConfig; + } + } + throw new ServiceException("Could not find ric: " + ricName); + } + + public static enum RicConfigUpdate { + ADDED, CHANGED, REMOVED + } + + public interface Observer { + void onRicConfigUpdate(RicConfig ric, RicConfigUpdate event); + } + + public void addObserver(Observer o) { + this.observers.add(o); + } + + private class Notification { + final RicConfig ric; + final RicConfigUpdate event; + + Notification(RicConfig ric, RicConfigUpdate event) { + this.ric = ric; + this.event = event; + } + } + + public void setConfiguration(@NotNull Collection ricConfigs, Properties dmaapPublisherConfig, + Properties dmaapConsumerConfig) { + Collection notifications = new Vector<>(); + synchronized (this) { + Map newRicConfigs = new HashMap<>(); + for (RicConfig newConfig : ricConfigs) { + RicConfig oldConfig = this.ricConfigs.get(newConfig.name()); + if (oldConfig == null) { + newRicConfigs.put(newConfig.name(), newConfig); + notifications.add(new Notification(newConfig, RicConfigUpdate.ADDED)); + this.ricConfigs.remove(newConfig.name()); + } else if (!newConfig.equals(oldConfig)) { + notifications.add(new Notification(newConfig, RicConfigUpdate.CHANGED)); + newRicConfigs.put(newConfig.name(), newConfig); + this.ricConfigs.remove(newConfig.name()); + } else { + newRicConfigs.put(oldConfig.name(), oldConfig); + } + } + for (RicConfig deletedConfig : this.ricConfigs.values()) { + notifications.add(new Notification(deletedConfig, RicConfigUpdate.REMOVED)); + } + this.ricConfigs = newRicConfigs; + } + notifyObservers(notifications); + + this.dmaapPublisherConfig = dmaapPublisherConfig; + this.dmaapConsumerConfig = dmaapConsumerConfig; + } + + private void notifyObservers(Collection notifications) { + for (Observer observer : this.observers) { + for (Notification notif : notifications) { + observer.onRicConfigUpdate(notif.ric, notif.event); + } + } + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfigParser.java b/policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfigParser.java new file mode 100644 index 00000000..1ffe9c8e --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfigParser.java @@ -0,0 +1,168 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.configuration; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; +import javax.validation.constraints.NotNull; +import lombok.Getter; +import org.onap.dmaap.mr.test.clients.ProtocolTypeConstants; +import org.oransc.policyagent.exceptions.ServiceException; +import org.springframework.http.MediaType; + +public class ApplicationConfigParser { + + private static final String CONFIG = "config"; + + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + @Getter + private Vector ricConfigs; + @Getter + private Properties dmaapPublisherConfig; + @Getter + private Properties dmaapConsumerConfig; + + public void parse(JsonObject root) throws ServiceException { + JsonObject agentConfigJson = root.getAsJsonObject(CONFIG); + ricConfigs = parseRics(agentConfigJson); + JsonObject dmaapPublisherConfigJson = agentConfigJson.getAsJsonObject("streams_publishes"); + if (dmaapPublisherConfigJson == null) { + dmaapPublisherConfig = new Properties(); + } else { + dmaapPublisherConfig = parseDmaapConfig(dmaapPublisherConfigJson); + } + JsonObject dmaapConsumerConfigJson = agentConfigJson.getAsJsonObject("streams_subscribes"); + if (dmaapConsumerConfigJson == null) { + dmaapConsumerConfig = new Properties(); + } else { + dmaapConsumerConfig = parseDmaapConfig(dmaapConsumerConfigJson); + } + } + + private Vector parseRics(JsonObject config) throws ServiceException { + Vector result = new Vector(); + for (JsonElement ricElem : getAsJsonArray(config, "ric")) { + result.add(gson.fromJson(ricElem.getAsJsonObject(), ImmutableRicConfig.class)); + } + return result; + } + + private static JsonElement get(JsonObject obj, String memberName) throws ServiceException { + JsonElement elem = obj.get(memberName); + if (elem == null) { + throw new ServiceException("Could not find member: " + memberName + " in: " + obj); + } + return elem; + } + + private JsonArray getAsJsonArray(JsonObject obj, String memberName) throws ServiceException { + return get(obj, memberName).getAsJsonArray(); + } + + private Properties parseDmaapConfig(JsonObject streamCfg) throws ServiceException { + Set> streamConfigEntries = streamCfg.entrySet(); + if (streamConfigEntries.size() != 1) { + throw new ServiceException( + "Invalid configuration. Number of streams must be one, config: " + streamConfigEntries); + } + JsonObject streamConfigEntry = streamConfigEntries.iterator().next().getValue().getAsJsonObject(); + JsonObject dmaapInfo = get(streamConfigEntry, "dmaap_info").getAsJsonObject(); + String topicUrl = getAsString(dmaapInfo, "topic_url"); + + Properties dmaapProps = new Properties(); + try { + URL url = new URL(topicUrl); + String passwd = ""; + String userName = ""; + if (url.getUserInfo() != null) { + String[] userInfo = url.getUserInfo().split(":"); + userName = userInfo[0]; + passwd = userInfo[1]; + } + String urlPath = url.getPath(); + DmaapUrlPath path = parseDmaapUrlPath(urlPath); + + dmaapProps.put("ServiceName", url.getHost() + ":" + url.getPort() + "/events"); + dmaapProps.put("topic", path.dmaapTopicName); + dmaapProps.put("host", url.getHost() + ":" + url.getPort()); + dmaapProps.put("contenttype", MediaType.APPLICATION_JSON.toString()); + dmaapProps.put("userName", userName); + dmaapProps.put("password", passwd); + dmaapProps.put("group", path.consumerGroup); + dmaapProps.put("id", path.consumerId); + dmaapProps.put("TransportType", ProtocolTypeConstants.HTTPNOAUTH.toString()); + dmaapProps.put("timeout", 15000); + dmaapProps.put("limit", 100); + dmaapProps.put("maxBatchSize", "10"); + dmaapProps.put("maxAgeMs", "10000"); + dmaapProps.put("compress", true); + dmaapProps.put("MessageSentThreadOccurance", "2"); + } catch (MalformedURLException e) { + throw new ServiceException("Could not parse the URL", e); + } + + return dmaapProps; + } + + private static @NotNull String getAsString(JsonObject obj, String memberName) throws ServiceException { + return get(obj, memberName).getAsString(); + } + + private class DmaapUrlPath { + final String dmaapTopicName; + final String consumerGroup; + final String consumerId; + + DmaapUrlPath(String dmaapTopicName, String consumerGroup, String consumerId) { + this.dmaapTopicName = dmaapTopicName; + this.consumerGroup = consumerGroup; + this.consumerId = consumerId; + } + } + + private DmaapUrlPath parseDmaapUrlPath(String urlPath) throws ServiceException { + String[] tokens = urlPath.split("/"); // /events/A1-P/users/sdnc1 + if (!(tokens.length == 3 ^ tokens.length == 5)) { + throw new ServiceException("The path has incorrect syntax: " + urlPath); + } + + final String dmaapTopicName = tokens[2]; // /events/A1-P + String consumerGroup = ""; // users + String consumerId = ""; // sdnc1 + if (tokens.length == 5) { + consumerGroup = tokens[3]; + consumerId = tokens[4]; + } + return new DmaapUrlPath(dmaapTopicName, consumerGroup, consumerId); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/configuration/AsyncConfiguration.java b/policy-agent/src/main/java/org/oransc/policyagent/configuration/AsyncConfiguration.java new file mode 100644 index 00000000..882f30c4 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/configuration/AsyncConfiguration.java @@ -0,0 +1,45 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.configuration; + +import java.util.concurrent.Executor; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableAsync +public class AsyncConfiguration implements AsyncConfigurer { + + @Override + @Bean(name = "threadPoolTaskExecutor") + public Executor getAsyncExecutor() { + // Set this configuration value from common properties file + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(10); + executor.setQueueCapacity(25); + return executor; + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/configuration/RicConfig.java b/policy-agent/src/main/java/org/oransc/policyagent/configuration/RicConfig.java new file mode 100644 index 00000000..d446f94c --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/configuration/RicConfig.java @@ -0,0 +1,37 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.configuration; + +import java.util.Vector; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface RicConfig { + public String name(); + + public String baseUrl(); + + public Vector managedElementIds(); + +} 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 new file mode 100644 index 00000000..12deb819 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java @@ -0,0 +1,296 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.Collection; +import java.util.Vector; + +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.exceptions.ServiceException; +import org.oransc.policyagent.repository.ImmutablePolicy; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Rics; +import org.springframework.beans.factory.annotation.Autowired; +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.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +@RestController +@Api(value = "Policy Management API") +public class PolicyController { + + private final Rics rics; + private final PolicyTypes policyTypes; + private final Policies policies; + private final A1ClientFactory a1ClientFactory; + + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + @Autowired + PolicyController(ApplicationConfig config, PolicyTypes types, Policies policies, Rics rics, + A1ClientFactory a1ClientFactory) { + this.policyTypes = types; + this.policies = policies; + this.rics = rics; + this.a1ClientFactory = a1ClientFactory; + } + + @GetMapping("/policy_schemas") + @ApiOperation(value = "Returns policy type schema definitions") + @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) { + Collection types = this.policyTypes.getAll(); + return new ResponseEntity(toPolicyTypeSchemasJson(types), HttpStatus.OK); + } else { + try { + Collection types = rics.getRic(ricName).getSupportedPolicyTypes(); + return new ResponseEntity(toPolicyTypeSchemasJson(types), HttpStatus.OK); + } catch (ServiceException e) { + return new ResponseEntity(e.toString(), HttpStatus.NOT_FOUND); + } + } + } + } + + @GetMapping("/policy_schema") + @ApiOperation(value = "Returns one policy type schema definition") + @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); + return new ResponseEntity(type.schema(), HttpStatus.OK); + } catch (ServiceException e) { + return new ResponseEntity(e.toString(), HttpStatus.NOT_FOUND); + } + } + + @GetMapping("/policy_types") + @ApiOperation(value = "Returns policy types") + @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) { + Collection types = this.policyTypes.getAll(); + return new ResponseEntity(toPolicyTypeIdsJson(types), HttpStatus.OK); + } else { + try { + Collection types = rics.getRic(ricName).getSupportedPolicyTypes(); + return new ResponseEntity(toPolicyTypeIdsJson(types), HttpStatus.OK); + } catch (ServiceException e) { + return new ResponseEntity(e.toString(), HttpStatus.NOT_FOUND); + } + } + } + } + + @GetMapping("/policy") + @ApiOperation(value = "Returns a policy configuration") // + @ApiResponses( + 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 { + Policy p = policies.getPolicy(instance); + return new ResponseEntity(p.json(), HttpStatus.OK); + } catch (ServiceException e) { + return new ResponseEntity(e.getMessage(), HttpStatus.NO_CONTENT); + } + } + + @DeleteMapping("/policy") + @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); + if (policy != null && policy.ric().getState() == Ric.RicState.IDLE) { + policies.remove(policy); + return a1ClientFactory.createA1Client(policy.ric()) // + .flatMap(client -> client.deletePolicy(policy)) // + .flatMap(notUsed -> { + return Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)); + }); + } else { + return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + } + + @PutMapping(path = "/policy") + @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 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(jsonString) // + .type(type) // + .ric(ric) // + .ownerServiceName(service) // + .lastModified(getTimeStampUTC()) // + .build(); + return a1ClientFactory.createA1Client(ric) // + .flatMap(client -> client.putPolicy(policy)) // + .doOnNext(notUsed -> policies.put(policy)) // + .flatMap(notUsed -> { + return Mono.just(new ResponseEntity<>(HttpStatus.OK)); + }); + } + return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + + @GetMapping("/policies") + @ApiOperation(value = "Returns the policies") + @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, // + @RequestParam(name = "service", required = false) String service) // + { + synchronized (policies) { + Collection result = null; + + if (type != null) { + result = policies.getForType(type); + result = filter(result, null, ric, service); + } else if (service != null) { + result = policies.getForService(service); + result = filter(result, type, ric, null); + } else if (ric != null) { + result = policies.getForRic(ric); + result = filter(result, type, null, service); + } else { + result = policies.getAll(); + } + + return new ResponseEntity(policiesToJson(result), HttpStatus.OK); + } + } + + private boolean include(String filter, String value) { + return filter == null || value.equals(filter); + } + + private Collection filter(Collection collection, String type, String ric, String service) { + if (type == null && ric == null && service == null) { + return collection; + } + Vector filtered = new Vector<>(); + for (Policy p : collection) { + if (include(type, p.type().name()) && include(ric, p.ric().name()) + && include(service, p.ownerServiceName())) { + filtered.add(p); + } + } + return filtered; + } + + private String policiesToJson(Collection policies) { + Vector v = new Vector<>(policies.size()); + for (Policy p : policies) { + 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); + } + + private String toPolicyTypeSchemasJson(Collection types) { + StringBuilder result = new StringBuilder(); + result.append("["); + boolean first = true; + for (PolicyType t : types) { + if (!first) { + result.append(","); + } + first = false; + result.append(t.schema()); + } + result.append("]"); + return result.toString(); + } + + private String toPolicyTypeIdsJson(Collection types) { + Vector v = new Vector<>(types.size()); + for (PolicyType t : types) { + v.add(t.name()); + } + return gson.toJson(v); + } + + private String getTimeStampUTC() { + return java.time.Instant.now().toString(); + } + +} 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 new file mode 100644 index 00000000..bd130629 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java @@ -0,0 +1,57 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import org.immutables.gson.Gson; + +@Gson.TypeAdapters +@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; + + @ApiModelProperty(value = "identity the target NearRT RIC") + public String ric; + + @ApiModelProperty(value = "the configuration of the policy") + public String json; + + @ApiModelProperty(value = "the name of the service owning the policy") + public String service; + + @ApiModelProperty(value = "timestamp, last modification time") + public String lastModified; + + PolicyInfo() { + } + + 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 new file mode 100644 index 00000000..73103b80 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java @@ -0,0 +1,47 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Collection; + +import org.immutables.gson.Gson; + +@Gson.TypeAdapters +@ApiModel(value = "RicInfo") +class RicInfo { + @ApiModelProperty(value = "identity of the ric") + public final String name; + + @ApiModelProperty(value = "O1 identities for managed entities") + public final Collection managedElementIds; + + @ApiModelProperty(value = "supported policy types") + public final 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 new file mode 100644 index 00000000..a5667816 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java @@ -0,0 +1,106 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.util.Optional; +import java.util.Vector; + +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Rics; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Api(value = "RIC Management API") +public class RicRepositoryController { + + @Autowired + private Rics rics; + + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + @Autowired + RicRepositoryController(ApplicationConfig appConfig) { + } + + /** + * Example: http://localhost:8080/rics?managedElementId=kista_1 + */ + @GetMapping("/ric") + @ApiOperation(value = "Returns the name of a RIC managing one Mananged Element") + @ApiResponses( + value = { // + @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) { + + Optional ric = this.rics.lookupRicForManagedElement(managedElementId); + + if (ric.isPresent()) { + return new ResponseEntity<>(ric.get().name(), HttpStatus.OK); + } else { + return new ResponseEntity<>("", HttpStatus.NOT_FOUND); + } + } + + /** + * @return a Json array of all RIC data + * Example: http://localhost:8080/ric + */ + @GetMapping("/rics") + @ApiOperation(value = "Returns NearRT RIC information") + @ApiResponses( + value = { // + @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List") // + }) + public ResponseEntity getRics( + @RequestParam(name = "policyType", required = false) String supportingPolicyType) { + + Vector result = new Vector<>(); + synchronized (rics) { + for (Ric ric : rics.getRics()) { + if (supportingPolicyType == null || ric.isSupportingType(supportingPolicyType)) { + result.add(new RicInfo(ric.name(), ric.getManagedElementIds(), ric.getSupportedPolicyTypeNames())); + } + } + } + + return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK); + } + +} 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 new file mode 100644 index 00000000..012d40a9 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java @@ -0,0 +1,155 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import java.time.Duration; +import java.util.Collection; +import java.util.Vector; + +import org.oransc.policyagent.exceptions.ServiceException; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.Service; +import org.oransc.policyagent.repository.Services; +import org.springframework.beans.factory.annotation.Autowired; +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; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ServiceController { + + private final Services services; + private final Policies policies; + + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + @Autowired + ServiceController(Services services, Policies policies) { + this.services = services; + this.policies = policies; + } + + @GetMapping("/services") + @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) { + + Collection servicesStatus = new Vector<>(); + synchronized (this.services) { + for (Service s : this.services.getAll()) { + if (name == null || name.equals(s.getName())) { + servicesStatus.add(toServiceStatus(s)); + } + } + } + + String res = gson.toJson(servicesStatus); + return new ResponseEntity(res, HttpStatus.OK); + } + + private ServiceStatus toServiceStatus(Service s) { + 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 ServiceRegistrationInfo registrationInfo) { + try { + 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) { + try { + Service service = removeService(name); + // Remove the policies from the repo and let the consistency monitoring + // do the rest. + removePolicies(service); + return new ResponseEntity("OK", HttpStatus.NO_CONTENT); + } catch (Exception e) { + return new ResponseEntity(e.getMessage(), HttpStatus.NO_CONTENT); + } + } + + @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); + this.services.remove(service.getName()); + return service; + } + } + + private void removePolicies(Service service) { + synchronized (this.policies) { + Vector policyList = new Vector<>(this.policies.getForService(service.getName())); + for (Policy policy : policyList) { + this.policies.remove(policy); + } + } + } + + private Service toService(ServiceRegistrationInfo s) { + 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 new file mode 100644 index 00000000..145fdb02 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java @@ -0,0 +1,52 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import org.immutables.gson.Gson; + +@Gson.TypeAdapters +@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; + + @ApiModelProperty(value = "callback for notifying of RIC recovery") + public String callbackUrl; + + public ServiceRegistrationInfo() { + } + + 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 new file mode 100644 index 00000000..c30cce15 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java @@ -0,0 +1,47 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import org.immutables.gson.Gson; + +@Gson.TypeAdapters +@ApiModel(value = "ServiceStatus") +public class ServiceStatus { + + @ApiModelProperty(value = "identity of the service") + public final String name; + + @ApiModelProperty(value = "policy keep alive timeout") + public final long keepAliveIntervalSeconds; + + @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 new file mode 100644 index 00000000..1219a0d8 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java @@ -0,0 +1,47 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.controllers; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +@RestController +public class StatusController { + + @GetMapping("/status") + @ApiOperation(value = "Returns status and statistics of the service") + @ApiResponses( + value = { // + @ApiResponse(code = 200, message = "Service is living", response = String.class) // + }) + public Mono> getStatus() { + Mono> response = Mono.just(new ResponseEntity<>("hunky dory", HttpStatus.OK)); + return response; + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java new file mode 100644 index 00000000..c132fa28 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java @@ -0,0 +1,58 @@ + +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.dmaap; + +/** + * The Dmaap consumer which has the base methods to be implemented by any class which implements this interface + * + */ +public interface DmaapMessageConsumer { + + /** + * The init method creates the MRConsumer with the properties passed from the Application Config + * + * @param properties + */ + public void init(); + + /** + * This method process the message and call the respective Controller + * + * @param msg + * @throws Exception + */ + public abstract void processMsg(String msg) throws Exception; + + /** + * To check whether the DMAAP Listner is alive + * + * @return boolean + */ + public boolean isAlive(); + + /** + * It's a infinite loop run every configured seconds to fetch the message from DMAAP. This method can be stop by + * setting the alive flag to false + */ + public void run(); + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerImpl.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerImpl.java new file mode 100644 index 00000000..db4956ab --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerImpl.java @@ -0,0 +1,133 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.dmaap; + +import com.google.common.collect.Iterables; +import java.io.IOException; +import java.util.Properties; +import org.onap.dmaap.mr.client.MRBatchingPublisher; +import org.onap.dmaap.mr.client.MRClientFactory; +import org.onap.dmaap.mr.client.MRConsumer; +import org.onap.dmaap.mr.client.response.MRConsumerResponse; +import org.oransc.policyagent.clients.AsyncRestClient; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@EnableScheduling +public class DmaapMessageConsumerImpl implements DmaapMessageConsumer { + + private static final Logger logger = LoggerFactory.getLogger(DmaapMessageConsumerImpl.class); + + private boolean alive = false; + private final ApplicationConfig applicationConfig; + protected MRConsumer consumer; + private MRBatchingPublisher producer; + + @Value("${server.port}") + private int localServerPort; + + @Autowired + public DmaapMessageConsumerImpl(ApplicationConfig applicationConfig) { + this.applicationConfig = applicationConfig; + } + + @Scheduled(fixedRate = 1000 * 40) + @Override + public void run() { + if (!alive) { + init(); + } + if (this.alive) { + try { + Iterable dmaapMsgs = fetchAllMessages(); + if (dmaapMsgs != null && Iterables.size(dmaapMsgs) > 0) { + logger.debug("Fetched all the messages from DMAAP and will start to process the messages"); + for (String msg : dmaapMsgs) { + processMsg(msg); + } + } + } catch (Exception e) { + logger.error("{}: cannot fetch because of ", this, e.getMessage(), e); + } + } + } + + private Iterable fetchAllMessages() { + MRConsumerResponse response = null; + try { + response = consumer.fetchWithReturnConsumerResponse(); + } catch (Exception e) { + logger.error("Failed to get message from DMAAP", e); + } + if (response == null || !"200".equals(response.getResponseCode())) { + logger.warn("{}: DMaaP NULL response received", this); + } else { + logger.debug("DMaaP consumer received {} : {}", response.getResponseCode(), response.getResponseMessage()); + } + return response.getActualMessages(); + } + + @Override + public void init() { + Properties dmaapConsumerProperties = applicationConfig.getDmaapConsumerConfig(); + Properties dmaapPublisherProperties = applicationConfig.getDmaapPublisherConfig(); + // No need to start if there is no configuration. + if (dmaapConsumerProperties == null || dmaapPublisherProperties == null || dmaapConsumerProperties.size() == 0 + || dmaapPublisherProperties.size() == 0) { + logger.error("DMaaP properties Failed to Load"); + return; + } + try { + logger.debug("Creating DMAAP Client"); + logger.debug("dmaapConsumerProperties---> {}", dmaapConsumerProperties.getProperty("topic")); + logger.debug("dmaapPublisherProperties---> {}", dmaapPublisherProperties.getProperty("topic")); + consumer = MRClientFactory.createConsumer(dmaapConsumerProperties); + producer = MRClientFactory.createBatchingPublisher(dmaapPublisherProperties); + this.alive = true; + } catch (IOException e) { + logger.error("Exception occurred while creating Dmaap Consumer", e); + } + } + + @Override + public void processMsg(String msg) throws Exception { + logger.debug("Message Reveived from DMAAP : {}", msg); + createDmaapMessageHandler().handleDmaapMsg(msg); + } + + protected DmaapMessageHandler createDmaapMessageHandler() { + String agentBaseUrl = "http://localhost:" + this.localServerPort; + AsyncRestClient agentClient = new AsyncRestClient(agentBaseUrl); + return new DmaapMessageHandler(this.producer, this.applicationConfig, agentClient); + } + + @Override + public boolean isAlive() { + return alive; + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java new file mode 100644 index 00000000..2d963c35 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java @@ -0,0 +1,141 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.dmaap; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.IOException; +import org.onap.dmaap.mr.client.MRBatchingPublisher; +import org.oransc.policyagent.clients.AsyncRestClient; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import reactor.core.publisher.Mono; + +public class DmaapMessageHandler { + + private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandler.class); + + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + private final MRBatchingPublisher dmaapClient; + private final AsyncRestClient agentClient; + + public DmaapMessageHandler(MRBatchingPublisher dmaapClient, ApplicationConfig applicationConfig, + AsyncRestClient agentClient) { + this.agentClient = agentClient; + this.dmaapClient = dmaapClient; + } + + public void handleDmaapMsg(String msg) { + try { + this.createTask(msg) // + .subscribe(x -> logger.debug("handleDmaapMsg: " + x), // + throwable -> logger.warn("handleDmaapMsg failure ", throwable), // + () -> logger.debug("handleDmaapMsg complete")); + } catch (Exception e) { + logger.warn("Received unparsable message from DMAAP: {}", msg); + } + } + + Mono createTask(String msg) { + try { + DmaapRequestMessage dmaapRequestMessage = gson.fromJson(msg, ImmutableDmaapRequestMessage.class); + + return this.invokePolicyAgent(dmaapRequestMessage) // + .onErrorResume(t -> handleAgentCallError(t, dmaapRequestMessage)) // + .flatMap(response -> sendDmaapResponse(response, dmaapRequestMessage, HttpStatus.OK)); + + } catch (Exception e) { + logger.warn("Received unparsable message from DMAAP: {}", msg); + return Mono.error(e); + } + } + + private Mono handleAgentCallError(Throwable t, DmaapRequestMessage dmaapRequestMessage) { + logger.debug("Agent call failed: " + t.getMessage()); + return sendDmaapResponse(t.toString(), dmaapRequestMessage, HttpStatus.NOT_FOUND) // + .flatMap(s -> Mono.empty()); + } + + private Mono invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) { + DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation(); + Mono result = null; + String uri = dmaapRequestMessage.url(); + if (operation == Operation.DELETE) { + result = agentClient.delete(uri); + } else if (operation == Operation.GET) { + result = agentClient.get(uri); + } else if (operation == Operation.PUT) { + result = agentClient.put(uri, dmaapRequestMessage.payload()); + } else if (operation == Operation.POST) { + result = agentClient.post(uri, dmaapRequestMessage.payload()); + } else { + return Mono.error(new Exception("Not implemented operation: " + operation)); + } + return result; + } + + private Mono sendDmaapResponse(String response, DmaapRequestMessage dmaapRequestMessage, + HttpStatus status) { + return getDmaapResponseMessage(dmaapRequestMessage, response, status) // + .flatMap(body -> sendToDmaap(body)) // + .onErrorResume(t -> handleResponseCallError(t, dmaapRequestMessage)); + } + + private Mono sendToDmaap(String body) { + try { + logger.debug("sendToDmaap: {} ", body); + dmaapClient.send(body); + dmaapClient.sendBatchWithResponse(); + return Mono.just("OK"); + } catch (IOException e) { + return Mono.error(e); + } + } + + private Mono handleResponseCallError(Throwable t, DmaapRequestMessage dmaapRequestMessage) { + logger.debug("Failed to respond: " + t.getMessage()); + return Mono.empty(); + } + + private Mono getDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response, + HttpStatus status) { + DmaapResponseMessage dmaapResponseMessage = ImmutableDmaapResponseMessage.builder() // + .status(status.toString()) // + .message(response) // + .type("response") // + .correlationId(dmaapRequestMessage.correlationId()) // + .originatorId(dmaapRequestMessage.originatorId()) // + .requestId(dmaapRequestMessage.requestId()) // + .timestamp(dmaapRequestMessage.timestamp()) // + .build(); + String str = gson.toJson(dmaapResponseMessage); + + return Mono.just(str); + + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapRequestMessage.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapRequestMessage.java new file mode 100644 index 00000000..fe48aec2 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapRequestMessage.java @@ -0,0 +1,53 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.dmaap; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface DmaapRequestMessage { + + public static enum Operation { + PUT, GET, DELETE, POST + } + + String type(); + + String correlationId(); + + String target(); + + String timestamp(); + + String apiVersion(); + + String originatorId(); + + String requestId(); + + Operation operation(); + + String url(); + + String payload(); +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapResponseMessage.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapResponseMessage.java new file mode 100644 index 00000000..7b8bb344 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapResponseMessage.java @@ -0,0 +1,43 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.dmaap; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface DmaapResponseMessage { + + String type(); + + String correlationId(); + + String timestamp(); + + String originatorId(); + + String requestId(); + + String status(); + + String message(); +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/exceptions/EnvironmentLoaderException.java b/policy-agent/src/main/java/org/oransc/policyagent/exceptions/EnvironmentLoaderException.java new file mode 100644 index 00000000..df6f7def --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/exceptions/EnvironmentLoaderException.java @@ -0,0 +1,30 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.exceptions; + +public class EnvironmentLoaderException extends Exception { + + private static final long serialVersionUID = 1L; + + public EnvironmentLoaderException(String message) { + super(message); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/exceptions/ServiceException.java b/policy-agent/src/main/java/org/oransc/policyagent/exceptions/ServiceException.java new file mode 100644 index 00000000..c3443ede --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/exceptions/ServiceException.java @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START====================================================================== + * Copyright (C) 2019 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. + * ============LICENSE_END======================================================================== + */ + +package org.oransc.policyagent.exceptions; + +public class ServiceException extends Exception { + + private static final long serialVersionUID = 1L; + + public ServiceException(String message) { + super(message); + } + + public ServiceException(String message, Exception originalException) { + super(message, originalException); + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Policies.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Policies.java new file mode 100644 index 00000000..a279db54 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Policies.java @@ -0,0 +1,132 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import org.oransc.policyagent.exceptions.ServiceException; + +public class Policies { + private Map policiesId = new HashMap<>(); + private Map> policiesRic = new HashMap<>(); + private Map> policiesService = new HashMap<>(); + private Map> policiesType = new HashMap<>(); + + public Policies() { + } + + public synchronized void put(Policy policy) { + policiesId.put(policy.id(), policy); + multiMapPut(policiesRic, policy.ric().name(), policy); + multiMapPut(policiesService, policy.ownerServiceName(), policy); + multiMapPut(policiesType, policy.type().name(), policy); + } + + private void multiMapPut(Map> multiMap, String key, Policy value) { + Map map = multiMap.get(key); + if (map == null) { + map = new HashMap<>(); + multiMap.put(key, map); + } + map.put(value.id(), value); + } + + private void multiMapRemove(Map> multiMap, String key, Policy value) { + Map map = multiMap.get(key); + if (map != null) { + map.remove(value.id()); + if (map.isEmpty()) { + multiMap.remove(key); + } + } + } + + private Collection multiMapGet(Map> multiMap, String key) { + Map map = multiMap.get(key); + if (map == null) { + return new Vector(); + } + return Collections.unmodifiableCollection(map.values()); + } + + public synchronized boolean containsPolicy(String id) { + return policiesId.containsKey(id); + } + + public synchronized Policy get(String id) { + return policiesId.get(id); + } + + public synchronized Policy getPolicy(String id) throws ServiceException { + Policy p = policiesId.get(id); + if (p == null) { + throw new ServiceException("Could not find policy: " + id); + } + return p; + } + + public synchronized Collection getAll() { + return Collections.unmodifiableCollection(policiesId.values()); + } + + public synchronized Collection getForService(String service) { + return multiMapGet(policiesService, service); + } + + public synchronized Collection getForRic(String ric) { + return multiMapGet(policiesRic, ric); + } + + public synchronized Collection getForType(String type) { + return multiMapGet(policiesType, type); + } + + public synchronized Policy removeId(String id) { + Policy p = policiesId.get(id); + if (p != null) { + remove(p); + } + return p; + } + + public synchronized void remove(Policy policy) { + policiesId.remove(policy.id()); + multiMapRemove(policiesRic, policy.ric().name(), policy); + multiMapRemove(policiesService, policy.ownerServiceName(), policy); + multiMapRemove(policiesType, policy.type().name(), policy); + } + + public synchronized int size() { + return policiesId.size(); + } + + public synchronized void clear() { + while (policiesId.size() > 0) { + Set keys = policiesId.keySet(); + removeId(keys.iterator().next()); + } + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Policy.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Policy.java new file mode 100644 index 00000000..51482262 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Policy.java @@ -0,0 +1,40 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface Policy { + public String id(); + + public String json(); + + public String ownerServiceName(); + + public Ric ric(); + + public PolicyType type(); + + public String lastModified(); +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyType.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyType.java new file mode 100644 index 00000000..41b94985 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyType.java @@ -0,0 +1,32 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import org.immutables.gson.Gson; +import org.immutables.value.Value; + +@Value.Immutable +@Gson.TypeAdapters +public interface PolicyType { + public String name(); + + public String schema(); +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyTypes.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyTypes.java new file mode 100644 index 00000000..77239831 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyTypes.java @@ -0,0 +1,67 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.oransc.policyagent.exceptions.ServiceException; + +public class PolicyTypes { + private Map types = new HashMap(); + + public PolicyTypes() { + } + + public synchronized PolicyType getType(String name) throws ServiceException { + PolicyType t = types.get(name); + if (t == null) { + throw new ServiceException("Could not find type: " + name); + } + return t; + } + + public synchronized PolicyType get(String name) { + return types.get(name); + } + + public synchronized void put(PolicyType type) { + types.put(type.name(), type); + } + + public synchronized boolean contains(String policyType) { + return types.containsKey(policyType); + } + + public synchronized Collection getAll() { + return Collections.unmodifiableCollection(types.values()); + } + + public synchronized int size() { + return types.size(); + } + + public synchronized void clear() { + this.types.clear(); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java new file mode 100644 index 00000000..220477f0 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java @@ -0,0 +1,166 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import lombok.Getter; +import lombok.Setter; + +import org.oransc.policyagent.clients.A1Client.A1ProtocolType; +import org.oransc.policyagent.configuration.RicConfig; + +/** + * Represents the dynamic information about a NearRealtime-RIC. + */ +public class Ric { + private final RicConfig ricConfig; + @Getter + @Setter + private RicState state = RicState.UNDEFINED; + private Map supportedPolicyTypes = new HashMap<>(); + @Getter + @Setter + private A1ProtocolType protocolVersion = A1ProtocolType.UNKNOWN; + + /** + * Creates the Ric. Initial state is {@link RicState.NOT_INITIATED}. + * + * @param ricConfig The {@link RicConfig} for this Ric. + */ + public Ric(RicConfig ricConfig) { + this.ricConfig = ricConfig; + } + + public String name() { + return ricConfig.name(); + } + + public RicConfig getConfig() { + return this.ricConfig; + } + + /** + * Gets the nodes managed by this Ric. + * + * @return a vector containing the nodes managed by this Ric. + */ + public Vector getManagedElementIds() { + return ricConfig.managedElementIds(); + } + + /** + * Determines if the given node is managed by this Ric. + * + * @param managedElementId the node name to check. + * @return true if the given node is managed by this Ric. + */ + public boolean isManaging(String managedElementId) { + return ricConfig.managedElementIds().contains(managedElementId); + } + + /** + * Adds the given node as managed by this Ric. + * + * @param managedElementId the node to add. + */ + public void addManagedElement(String managedElementId) { + if (!ricConfig.managedElementIds().contains(managedElementId)) { + ricConfig.managedElementIds().add(managedElementId); + } + } + + /** + * Removes the given node as managed by this Ric. + * + * @param managedElementId the node to remove. + */ + public void removeManagedElement(String managedElementId) { + ricConfig.managedElementIds().remove(managedElementId); + } + + /** + * Gets the policy types supported by this Ric. + * + * @return the policy types supported by this Ric in an unmodifiable list. + */ + public Collection getSupportedPolicyTypes() { + return supportedPolicyTypes.values(); + } + + public Collection getSupportedPolicyTypeNames() { + return supportedPolicyTypes.keySet(); + } + + /** + * Adds a policy type as supported by this Ric. + * + * @param type the policy type to support. + */ + public void addSupportedPolicyType(PolicyType type) { + supportedPolicyTypes.put(type.name(), type); + } + + /** + * Removes all policy type as supported by this Ric. + */ + public void clearSupportedPolicyTypes() { + supportedPolicyTypes.clear(); + } + + /** + * Checks if a type is supported by this Ric. + * + * @param typeName the name of the type to check if it is supported. + * + * @return true if the given type is supported by this Ric, false otherwise. + */ + public boolean isSupportingType(String typeName) { + return supportedPolicyTypes.containsKey(typeName); + } + + @Override + public String toString() { + return Ric.class.getSimpleName() + ": " + "name: " + name() + ", state: " + state + ", baseUrl: " + + ricConfig.baseUrl() + ", managedNodes: " + ricConfig.managedElementIds(); + } + + /** + * Represents the states possible for a Ric. + */ + public static enum RicState { + /** + * The agent view of the agent may be inconsistent. + */ + UNDEFINED, + /** + * The normal state. Policies can be configured. + */ + IDLE, + /** + * The Ric states are recovered. + */ + RECOVERING + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Rics.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Rics.java new file mode 100644 index 00000000..6f3b3e84 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Rics.java @@ -0,0 +1,75 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.oransc.policyagent.exceptions.ServiceException; + +/** + * Dynamic representation of all Rics in the system. + */ +public class Rics { + Map rics = new HashMap<>(); + + public synchronized void put(Ric ric) { + rics.put(ric.name(), ric); + } + + public synchronized Iterable getRics() { + return rics.values(); + } + + public synchronized Ric getRic(String name) throws ServiceException { + Ric ric = rics.get(name); + if (ric == null) { + throw new ServiceException("Could not find ric: " + name); + } + return ric; + } + + public synchronized Ric get(String name) { + return rics.get(name); + } + + public synchronized void remove(String name) { + rics.remove(name); + } + + public synchronized int size() { + return rics.size(); + } + + public synchronized void clear() { + this.rics.clear(); + } + + public synchronized Optional lookupRicForManagedElement(String managedElementId) { + for (Ric ric : this.rics.values()) { + if (ric.getConfig().managedElementIds().contains(managedElementId)) { + return Optional.of(ric); + } + } + return Optional.empty(); + } +} 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 new file mode 100644 index 00000000..f0863a5e --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java @@ -0,0 +1,62 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import java.time.Duration; +import java.time.Instant; + +import lombok.Getter; + +public class Service { + @Getter + private final String name; + private final Duration keepAliveInterval; + private Instant lastPing; + private final String callbackUrl; + + public Service(String name, Duration keepAliveInterval, String callbackUrl) { + this.name = name; + this.keepAliveInterval = keepAliveInterval; + this.callbackUrl = callbackUrl; + ping(); + } + + public synchronized Duration getKeepAliveInterval() { + return this.keepAliveInterval; + } + + public synchronized void ping() { + this.lastPing = Instant.now(); + } + + public synchronized boolean isExpired() { + return this.keepAliveInterval.getSeconds() > 0 && timeSinceLastPing().compareTo(this.keepAliveInterval) > 0; + } + + public synchronized Duration timeSinceLastPing() { + return Duration.between(this.lastPing, Instant.now()); + } + + public synchronized String getCallbackUrl() { + return this.callbackUrl; + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java new file mode 100644 index 00000000..369b2588 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java @@ -0,0 +1,67 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.repository; + +import java.util.HashMap; +import java.util.Map; + +import org.oransc.policyagent.exceptions.ServiceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Services { + private static final Logger logger = LoggerFactory.getLogger(Services.class); + + private Map services = new HashMap<>(); + + public Services() { + } + + public synchronized Service getService(String name) throws ServiceException { + Service s = services.get(name); + if (s == null) { + throw new ServiceException("Could not find service: " + name); + } + return s; + } + + public synchronized Service get(String name) { + return services.get(name); + } + + public synchronized void put(Service service) { + logger.debug("Put service: " + service.getName()); + services.put(service.getName(), service); + } + + public synchronized Iterable getAll() { + return services.values(); + } + + public synchronized void remove(String name) { + services.remove(name); + } + + public synchronized int size() { + return services.size(); + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/EnvironmentProcessor.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/EnvironmentProcessor.java new file mode 100644 index 00000000..574656f6 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/EnvironmentProcessor.java @@ -0,0 +1,87 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import java.util.Optional; +import java.util.Properties; + +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperties; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties; +import org.oransc.policyagent.exceptions.EnvironmentLoaderException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +class EnvironmentProcessor { + + private static final int DEFAULT_CONSUL_PORT = 8500; + private static final Logger logger = LoggerFactory.getLogger(EnvironmentProcessor.class); + + private EnvironmentProcessor() { + } + + static Mono readEnvironmentVariables(Properties systemEnvironment) { + + EnvProperties envProperties; + try { + envProperties = ImmutableEnvProperties.builder() // + .consulHost(getConsulHost(systemEnvironment)) // + .consulPort(getConsultPort(systemEnvironment)) // + .cbsName(getConfigBindingService(systemEnvironment)) // + .appName(getService(systemEnvironment)) // + .build(); + } catch (EnvironmentLoaderException e) { + return Mono.error(e); + } + logger.trace("Evaluated environment system variables {}", envProperties); + return Mono.just(envProperties); + } + + private static String getConsulHost(Properties systemEnvironments) throws EnvironmentLoaderException { + return Optional.ofNullable(systemEnvironments.getProperty("CONSUL_HOST")) + .orElseThrow(() -> new EnvironmentLoaderException("$CONSUL_HOST environment has not been defined")); + } + + private static Integer getConsultPort(Properties systemEnvironments) { + return Optional.ofNullable(systemEnvironments.getProperty("CONSUL_PORT")) // + .map(Integer::valueOf) // + .orElseGet(EnvironmentProcessor::getDefaultPortOfConsul); + } + + private static String getConfigBindingService(Properties systemEnvironments) throws EnvironmentLoaderException { + return Optional.ofNullable(systemEnvironments.getProperty("CONFIG_BINDING_SERVICE")) // + .orElseThrow( + () -> new EnvironmentLoaderException("$CONFIG_BINDING_SERVICE environment has not been defined")); + } + + private static String getService(Properties systemEnvironments) throws EnvironmentLoaderException { + return Optional + .ofNullable(Optional.ofNullable(systemEnvironments.getProperty("HOSTNAME")) + .orElse(systemEnvironments.getProperty("SERVICE_NAME"))) + .orElseThrow(() -> new EnvironmentLoaderException( + "Neither $HOSTNAME/$SERVICE_NAME have not been defined as system environment")); + } + + private static Integer getDefaultPortOfConsul() { + logger.warn("$CONSUL_PORT variable will be set to default port {}", DEFAULT_CONSUL_PORT); + return DEFAULT_CONSUL_PORT; + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java new file mode 100644 index 00000000..267fc1f1 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java @@ -0,0 +1,170 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapterFactory; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.time.Duration; +import java.util.Properties; +import java.util.ServiceLoader; + +import javax.validation.constraints.NotNull; + +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClientFactory; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsRequests; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.CbsRequest; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperties; +import org.onap.dcaegen2.services.sdk.rest.services.model.logging.RequestDiagnosticContext; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.configuration.ApplicationConfigParser; +import org.oransc.policyagent.exceptions.ServiceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Regularly refreshes the configuration from Consul. + */ +@Component +public class RefreshConfigTask { + + private static final Logger logger = LoggerFactory.getLogger(RefreshConfigTask.class); + + @Value("#{systemEnvironment}") + public Properties systemEnvironment; + + private final ApplicationConfig appConfig; + private Disposable refreshTask = null; + + @Autowired + public RefreshConfigTask(ApplicationConfig appConfig) { + this.appConfig = appConfig; + } + + public void start() { + logger.debug("Starting refreshConfigTask"); + stop(); + loadConfigurationFromFile(); + refreshTask = createRefreshTask() // + .subscribe(notUsed -> logger.info("Refreshed configuration data"), + throwable -> logger.error("Configuration refresh terminated due to exception", throwable), + () -> logger.error("Configuration refresh terminated")); + } + + public void stop() { + if (refreshTask != null) { + refreshTask.dispose(); + refreshTask = null; + } + } + + Flux createRefreshTask() { + return getEnvironment(systemEnvironment) // + .flatMap(this::createCbsClient) // + .flatMapMany(this::periodicConfigurationUpdates) // + .map(this::parseRicConfigurationfromConsul) // + .onErrorResume(this::onErrorResume); + } + + Mono getEnvironment(Properties systemEnvironment) { + return EnvironmentProcessor.readEnvironmentVariables(systemEnvironment); + } + + Mono createCbsClient(EnvProperties env) { + return CbsClientFactory.createCbsClient(env); + } + + private Flux periodicConfigurationUpdates(CbsClient cbsClient) { + final Duration initialDelay = Duration.ZERO; + final Duration refreshPeriod = Duration.ofMinutes(1); + final CbsRequest getConfigRequest = CbsRequests.getAll(RequestDiagnosticContext.create()); + return cbsClient.updates(getConfigRequest, initialDelay, refreshPeriod); + } + + private Mono onErrorResume(Throwable trowable) { + logger.error("Could not refresh application configuration {}", trowable.toString()); + return Mono.empty(); + } + + private ApplicationConfig parseRicConfigurationfromConsul(JsonObject jsonObject) { + try { + ApplicationConfigParser parser = new ApplicationConfigParser(); + parser.parse(jsonObject); + this.appConfig.setConfiguration(parser.getRicConfigs(), parser.getDmaapPublisherConfig(), + parser.getDmaapConsumerConfig()); + } catch (ServiceException e) { + logger.error("Could not parse configuration {}", e.toString(), e); + } + return this.appConfig; + } + + /** + * Reads the configuration from file. + */ + public void loadConfigurationFromFile() { + String filepath = appConfig.getLocalConfigurationFilePath(); + if (filepath == null) { + logger.debug("No localconfiguration file used"); + return; + } + GsonBuilder gsonBuilder = new GsonBuilder(); + ServiceLoader.load(TypeAdapterFactory.class).forEach(gsonBuilder::registerTypeAdapterFactory); + + try (InputStream inputStream = createInputStream(filepath)) { + JsonObject rootObject = getJsonElement(inputStream).getAsJsonObject(); + if (rootObject == null) { + throw new JsonSyntaxException("Root is not a json object"); + } + ApplicationConfigParser appParser = new ApplicationConfigParser(); + appParser.parse(rootObject); + appConfig.setConfiguration(appParser.getRicConfigs(), appParser.getDmaapPublisherConfig(), + appParser.getDmaapConsumerConfig()); + logger.info("Local configuration file loaded: {}", filepath); + } catch (JsonSyntaxException | ServiceException | IOException e) { + logger.trace("Local configuration file not loaded: {}", filepath, e); + } + } + + JsonElement getJsonElement(InputStream inputStream) { + return JsonParser.parseReader(new InputStreamReader(inputStream)); + } + + InputStream createInputStream(@NotNull String filepath) throws IOException { + return new BufferedInputStream(new FileInputStream(filepath)); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java new file mode 100644 index 00000000..d95272b8 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java @@ -0,0 +1,170 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import java.util.Collection; + +import org.oransc.policyagent.clients.A1Client; +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Ric.RicState; +import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.repository.Services; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Regularly checks the exisiting rics towards the local repository to keep it consistent. + */ +@Component +@EnableScheduling +public class RepositorySupervision { + private static final Logger logger = LoggerFactory.getLogger(RepositorySupervision.class); + + private final Rics rics; + private final Policies policies; + private final PolicyTypes policyTypes; + private final A1ClientFactory a1ClientFactory; + private final Services services; + + @Autowired + public RepositorySupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes, + Services services) { + this.rics = rics; + this.policies = policies; + this.a1ClientFactory = a1ClientFactory; + this.policyTypes = policyTypes; + this.services = services; + } + + /** + * Regularly contacts all Rics to check if they are alive. + */ + @Scheduled(fixedRate = 1000 * 60) + public void checkAllRics() { + logger.debug("Checking Rics starting"); + createTask().subscribe(this::onRicChecked, this::onError, this::onComplete); + } + + private Flux createTask() { + synchronized (this.rics) { + return Flux.fromIterable(rics.getRics()) // + .flatMap(ric -> createRicData(ric)) // + .flatMap(ricData -> checkRicState(ricData)) // + .flatMap(ricData -> checkRicPolicies(ricData)) // + .flatMap(ricData -> checkRicPolicyTypes(ricData)); + } + } + + private static class RicData { + RicData(Ric ric, A1Client a1Client) { + this.ric = ric; + this.a1Client = a1Client; + } + + final Ric ric; + final A1Client a1Client; + } + + private Mono createRicData(Ric ric) { + return Mono.just(ric) // + .flatMap(aRic -> this.a1ClientFactory.createA1Client(ric)) // + .flatMap(a1Client -> Mono.just(new RicData(ric, a1Client))); + } + + private Mono checkRicState(RicData ric) { + if (ric.ric.getState() == RicState.UNDEFINED) { + return startRecovery(ric); + } else if (ric.ric.getState() == RicState.RECOVERING) { + return Mono.empty(); + } else { + return Mono.just(ric); + } + } + + private Mono checkRicPolicies(RicData ric) { + return ric.a1Client.getPolicyIdentities() // + .onErrorResume(t -> Mono.empty()) // + .flatMap(ricP -> validateInstances(ricP, ric)); + } + + private Mono validateInstances(Collection ricPolicies, RicData ric) { + synchronized (this.policies) { + if (ricPolicies.size() != policies.getForRic(ric.ric.name()).size()) { + return startRecovery(ric); + } + } + for (String policyId : ricPolicies) { + if (!policies.containsPolicy(policyId)) { + return startRecovery(ric); + } + } + return Mono.just(ric); + } + + private Mono checkRicPolicyTypes(RicData ric) { + return ric.a1Client.getPolicyTypeIdentities() // + .onErrorResume(t -> { + return Mono.empty(); + }) // + .flatMap(ricTypes -> validateTypes(ricTypes, ric)); + } + + private Mono validateTypes(Collection ricTypes, RicData ric) { + if (ricTypes.size() != ric.ric.getSupportedPolicyTypes().size()) { + return startRecovery(ric); + } + for (String typeName : ricTypes) { + if (!ric.ric.isSupportingType(typeName)) { + return startRecovery(ric); + } + } + return Mono.just(ric); + } + + private Mono startRecovery(RicData ric) { + RicRecoveryTask recovery = new RicRecoveryTask(a1ClientFactory, policyTypes, policies, services); + recovery.run(ric.ric); + return Mono.empty(); + } + + private void onRicChecked(RicData ric) { + logger.info("Ric: " + ric.ric.name() + " checked"); + } + + private void onError(Throwable t) { + logger.error("Rics supervision failed", t); + } + + private void onComplete() { + logger.debug("Checking Rics completed"); + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicRecoveryTask.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicRecoveryTask.java new file mode 100644 index 00000000..ab25eab4 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicRecoveryTask.java @@ -0,0 +1,181 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import java.util.Vector; + +import org.oransc.policyagent.clients.A1Client; +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.clients.AsyncRestClient; +import org.oransc.policyagent.exceptions.ServiceException; +import org.oransc.policyagent.repository.ImmutablePolicyType; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Service; +import org.oransc.policyagent.repository.Services; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Recovery handling of RIC. + * This means: + * - load all policy types + * - send all policy instances to the RIC + * --- if that fails remove all policy instances + * - Notify subscribing services + */ +public class RicRecoveryTask { + + private static final Logger logger = LoggerFactory.getLogger(RicRecoveryTask.class); + + private final A1ClientFactory a1ClientFactory; + private final PolicyTypes policyTypes; + private final Policies policies; + private final Services services; + + public RicRecoveryTask(A1ClientFactory a1ClientFactory, PolicyTypes policyTypes, Policies policies, + Services services) { + this.a1ClientFactory = a1ClientFactory; + this.policyTypes = policyTypes; + this.policies = policies; + this.services = services; + } + + public void run(Ric ric) { + logger.debug("Handling ric: {}", ric.getConfig().name()); + + synchronized (ric) { + if (ric.getState() == Ric.RicState.RECOVERING) { + return; // Already running + } + ric.setState(Ric.RicState.RECOVERING); + } + this.a1ClientFactory.createA1Client(ric)// + .flatMapMany(client -> startRecover(ric, client)) // + .subscribe(x -> logger.debug("Recover: " + x), // + throwable -> onRecoveryError(ric, throwable), // + () -> onRecoveryComplete(ric)); + } + + private Flux startRecover(Ric ric, A1Client a1Client) { + Flux recoverTypes = recoverPolicyTypes(ric, a1Client); + Flux deletePoliciesInRic = a1Client.deleteAllPolicies(); + Flux recreatePoliciesInRic = recreateAllPoliciesInRic(ric, a1Client); + + return Flux.concat(recoverTypes, deletePoliciesInRic, recreatePoliciesInRic); + } + + private void onRecoveryComplete(Ric ric) { + logger.debug("Recovery completed for:" + ric.name()); + ric.setState(Ric.RicState.IDLE); + notifyAllServices("Recovery completed for:" + ric.name()); + } + + private void notifyAllServices(String body) { + synchronized (services) { + for (Service service : services.getAll()) { + String url = service.getCallbackUrl(); + if (service.getCallbackUrl().length() > 0) { + createClient(url) // + .put("", body) // + .subscribe(rsp -> logger.debug("Service called"), + throwable -> logger.warn("Service called failed", throwable), + () -> logger.debug("Service called complete")); + } + } + } + } + + private void onRecoveryError(Ric ric, Throwable t) { + logger.warn("Recovery failed for: {}, reason: {}", ric.name(), t.getMessage()); + // If recovery fails, try to remove all instances + deleteAllPolicies(ric); + Flux recoverTypes = this.a1ClientFactory.createA1Client(ric) // + .flatMapMany(a1Client -> recoverPolicyTypes(ric, a1Client)); + Flux deletePoliciesInRic = this.a1ClientFactory.createA1Client(ric) // + .flatMapMany(a1Client -> a1Client.deleteAllPolicies()); + + Flux.merge(recoverTypes, deletePoliciesInRic) // + .subscribe(x -> logger.debug("Brute recover: " + x), // + throwable -> onRemoveAllError(ric, throwable), // + () -> onRecoveryComplete(ric)); + } + + private void onRemoveAllError(Ric ric, Throwable t) { + logger.warn("Remove all failed for: {}, reason: {}", ric.name(), t.getMessage()); + ric.setState(Ric.RicState.UNDEFINED); + } + + private AsyncRestClient createClient(final String url) { + return new AsyncRestClient(url); + } + + private Flux recoverPolicyTypes(Ric ric, A1Client a1Client) { + ric.clearSupportedPolicyTypes(); + return a1Client.getPolicyTypeIdentities() // + .flatMapMany(types -> Flux.fromIterable(types)) // + .doOnNext(typeId -> logger.debug("For ric: {}, handling type: {}", ric.getConfig().name(), typeId)) // + .flatMap((policyTypeId) -> getPolicyType(ric, policyTypeId, a1Client)) // + .doOnNext(policyType -> ric.addSupportedPolicyType(policyType)); // + } + + private Mono getPolicyType(Ric ric, String policyTypeId, A1Client a1Client) { + if (policyTypes.contains(policyTypeId)) { + try { + return Mono.just(policyTypes.getType(policyTypeId)); + } catch (ServiceException e) { + return Mono.error(e); + } + } + return a1Client.getPolicyTypeSchema(policyTypeId) // + .flatMap(schema -> createPolicyType(policyTypeId, schema)); + } + + private Mono createPolicyType(String policyTypeId, String schema) { + PolicyType pt = ImmutablePolicyType.builder().name(policyTypeId).schema(schema).build(); + policyTypes.put(pt); + return Mono.just(pt); + } + + private void deleteAllPolicies(Ric ric) { + synchronized (policies) { + for (Policy policy : policies.getForRic(ric.name())) { + this.policies.remove(policy); + } + } + } + + private Flux recreateAllPoliciesInRic(Ric ric, A1Client a1Client) { + synchronized (policies) { + return Flux.fromIterable(new Vector<>(policies.getForRic(ric.name()))) // + .doOnNext( + policy -> logger.debug("Recreating policy: {}, for ric: {}", policy.id(), ric.getConfig().name())) + .flatMap(policy -> a1Client.putPolicy(policy)); + } + } + +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java new file mode 100644 index 00000000..d4b32e02 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java @@ -0,0 +1,99 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.Service; +import org.oransc.policyagent.repository.Services; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Component +@EnableScheduling +public class ServiceSupervision { + private static final Logger logger = LoggerFactory.getLogger(ServiceSupervision.class); + private final Services services; + private final Policies policies; + private A1ClientFactory a1ClientFactory; + + @Autowired + public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory) { + this.services = services; + this.policies = policies; + this.a1ClientFactory = a1ClientFactory; + } + + @Scheduled(fixedRate = 1000 * 60) + public void checkAllServices() { + logger.debug("Checking services starting"); + createTask().subscribe(this::onPolicyDeleted, this::onError, this::onComplete); + } + + private void onPolicyDeleted(Policy policy) { + logger.info("Policy deleted due to inactivity: " + policy.id() + ", service: " + policy.ownerServiceName()); + } + + private void onError(Throwable t) { + logger.error("Service supervision failed", t); + } + + private void onComplete() { + logger.debug("Checking services completed"); + } + + private Flux createTask() { + synchronized (services) { + return Flux.fromIterable(services.getAll()) // + .filter(service -> service.isExpired()) // + .doOnNext(service -> logger.info("Service is expired:" + service.getName())) // + .flatMap(service -> getAllPolicies(service)) // + .doOnNext(policy -> this.policies.remove(policy)) // + .flatMap(policy -> deletePolicyInRic(policy)); + } + } + + private Flux getAllPolicies(Service service) { + synchronized (policies) { + return Flux.fromIterable(policies.getForService(service.getName())); + } + } + + private Mono deletePolicyInRic(Policy policy) { + return a1ClientFactory.createA1Client(policy.ric()) // + .flatMap(client -> client.deletePolicy(policy) // + .onErrorResume(exception -> handleDeleteFromRicFailure(policy, exception)) // + .map((nothing) -> policy)); + } + + private Mono handleDeleteFromRicFailure(Policy policy, Throwable e) { + logger.warn("Could not delete policy: {} from ric: {}", policy.id(), policy.ric().name(), e); + return Mono.empty(); + } +} diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/StartupService.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/StartupService.java new file mode 100644 index 00000000..54fd79f4 --- /dev/null +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/StartupService.java @@ -0,0 +1,106 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.Policies; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +/** + * Loads information about RealTime-RICs at startup. + */ +@Service("startupService") +@Order(Ordered.HIGHEST_PRECEDENCE) +public class StartupService implements ApplicationConfig.Observer { + + private static final Logger logger = LoggerFactory.getLogger(StartupService.class); + + @Autowired + ApplicationConfig applicationConfig; + + @Autowired + RefreshConfigTask refreshConfigTask; + + @Autowired + private Rics rics; + + @Autowired + PolicyTypes policyTypes; + + @Autowired + private A1ClientFactory a1ClientFactory; + + @Autowired + private Policies policies; + + @Autowired + private Services services; + + // Only for unit testing + StartupService(ApplicationConfig appConfig, RefreshConfigTask refreshTask, Rics rics, PolicyTypes policyTypes, + A1ClientFactory a1ClientFactory, Policies policies, Services services) { + this.applicationConfig = appConfig; + this.refreshConfigTask = refreshTask; + this.rics = rics; + this.policyTypes = policyTypes; + this.a1ClientFactory = a1ClientFactory; + this.policies = policies; + this.services = services; + } + + @Override + public void onRicConfigUpdate(RicConfig ricConfig, ApplicationConfig.RicConfigUpdate event) { + synchronized (this.rics) { + if (event.equals(ApplicationConfig.RicConfigUpdate.ADDED) + || event.equals(ApplicationConfig.RicConfigUpdate.CHANGED)) { + Ric ric = new Ric(ricConfig); + rics.put(ric); + RicRecoveryTask recoveryTask = new RicRecoveryTask(a1ClientFactory, policyTypes, policies, services); + recoveryTask.run(ric); + } else if (event.equals(ApplicationConfig.RicConfigUpdate.REMOVED)) { + rics.remove(ricConfig.name()); + } else { + logger.debug("Unhandled event :" + event); + } + } + } + + /** + * Reads the configured Rics and performs the service discovery. The result is put into the repository. + */ + public void startup() { + logger.debug("Starting up"); + applicationConfig.addObserver(this); + refreshConfigTask.start(); + } + +} diff --git a/policy-agent/src/main/resources/keystore.jks b/policy-agent/src/main/resources/keystore.jks new file mode 100644 index 00000000..574a585b Binary files /dev/null and b/policy-agent/src/main/resources/keystore.jks differ diff --git a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java new file mode 100644 index 00000000..847cde19 --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java @@ -0,0 +1,486 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +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; + +import org.junit.jupiter.api.Test; +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.PolicyInfo; +import org.oransc.policyagent.controllers.ServiceRegistrationInfo; +import org.oransc.policyagent.controllers.ServiceStatus; +import org.oransc.policyagent.exceptions.ServiceException; +import org.oransc.policyagent.repository.ImmutablePolicy; +import org.oransc.policyagent.repository.ImmutablePolicyType; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Ric.RicState; +import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.repository.Services; +import org.oransc.policyagent.tasks.RepositorySupervision; +import org.oransc.policyagent.utils.MockA1Client; +import org.oransc.policyagent.utils.MockA1ClientFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +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; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class ApplicationTest { + @Autowired + ApplicationContext context; + + @Autowired + private Rics rics; + + @Autowired + private Policies policies; + + @Autowired + private PolicyTypes policyTypes; + + @Autowired + MockA1ClientFactory a1ClientFactory; + + @Autowired + RepositorySupervision supervision; + + @Autowired + Services services; + + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + public static class MockApplicationConfig extends ApplicationConfig { + @Override + public String getLocalConfigurationFilePath() { + return ""; // No config file loaded for the test + } + } + + /** + * Overrides the BeanFactory. + */ + @TestConfiguration + static class TestBeanFactory { + private final PolicyTypes policyTypes = new PolicyTypes(); + + @Bean + public ApplicationConfig getApplicationConfig() { + return new MockApplicationConfig(); + } + + @Bean + MockA1ClientFactory getA1ClientFactory() { + return new MockA1ClientFactory(this.policyTypes); + } + + @Bean + public Policies getPolicies() { + return new Policies(); + } + + @Bean + public PolicyTypes getPolicyTypes() { + return this.policyTypes; + } + + @Bean + public Rics getRics() { + return new Rics(); + } + } + + @LocalServerPort + private int port; + + private final RestTemplate restTemplate = new RestTemplate(); + + public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { + + @Override + public boolean hasError(ClientHttpResponse httpResponse) throws IOException { + return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR + || httpResponse.getStatusCode().series() == Series.SERVER_ERROR); + } + + @Override + public void handleError(ClientHttpResponse httpResponse) throws IOException { + System.out.println("Error " + httpResponse.toString()); + } + } + + private void reset() { + rics.clear(); + policies.clear(); + policyTypes.clear(); + assertThat(policies.size()).isEqualTo(0); + restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler()); + } + + @Test + public void testGetRics() throws Exception { + reset(); + addRic("kista_1"); + String url = baseUrl() + "/rics"; + String rsp = this.restTemplate.getForObject(url, String.class); + System.out.println(rsp); + assertThat(rsp).contains("kista_1"); + + url = baseUrl() + "/rics?policyType=STD_PolicyModelUnconstrained_0.2.0"; + rsp = this.restTemplate.getForObject(url, String.class); + assertThat(rsp).isEqualTo("[]"); + } + + @Test + public void testRecovery() throws Exception { + reset(); + Policy policy2 = addPolicy("policyId2", "typeName", "service", "ric"); + + getA1Client("ric").putPolicy(policy2); // put it in the RIC + policies.remove(policy2); // Remove it from the repo -> should be deleted in the RIC + + 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"); + assertThat(ricPolicy.json()).isEqualTo(policy.json()); + } + + MockA1Client getA1Client(String ricName) throws ServiceException { + return a1ClientFactory.getOrCreateA1Client(ricName); + } + + @Test + public void testGetRic() throws Exception { + reset(); + Ric ric = addRic("ric1"); + ric.addManagedElement("kista_1"); + String url = baseUrl() + "/ric?managedElementId=kista_1"; + + 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"; + final String json = jsonString(); + this.rics.getRic("ric1").setState(Ric.RicState.IDLE); + + this.restTemplate.put(url, createJsonHttpEntity(json)); + Policy policy = policies.getPolicy("instance1"); + + assertThat(policy).isNotNull(); + assertThat(policy.id()).isEqualTo("instance1"); + assertThat(policy.ownerServiceName()).isEqualTo("service1"); + + url = baseUrl() + "/policies"; + String rsp = this.restTemplate.getForObject(url, String.class); + System.out.println(rsp); + } + + private PolicyType addPolicyType(String policyTypeName, String ricName) { + PolicyType type = ImmutablePolicyType.builder() // + .name(policyTypeName) // + .schema("{\"title\":\"" + policyTypeName + "\"}") // + .build(); + + policyTypes.put(type); + addRic(ricName).addSupportedPolicyType(type); + return type; + } + + private Ric addRic(String ricName) { + if (rics.get(ricName) != null) { + return rics.get(ricName); + } + Vector mes = new Vector<>(); + RicConfig conf = ImmutableRicConfig.builder() // + .name(ricName) // + .baseUrl(ricName) // + .managedElementIds(mes) // + .build(); + Ric ric = new Ric(conf); + this.rics.put(ric); + 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(jsonString()) // + .ownerServiceName(service) // + .ric(rics.getRic(ric)) // + .type(addPolicyType(typeName, ric)) // + .lastModified("lastModified").build(); + policies.put(p); + return p; + } + + private Policy addPolicy(String id, String typeName, String service) throws ServiceException { + return addPolicy(id, typeName, service, "ric"); + } + + private String baseUrl() { + return "http://localhost:" + port; + } + + @Test + public void testGetPolicy() throws Exception { + String url = baseUrl() + "/policy?instance=id"; + Policy policy = addPolicy("id", "typeName", "service1", "ric1"); + { + String rsp = this.restTemplate.getForObject(url, String.class); + assertThat(rsp).isEqualTo(policy.json()); + } + { + policies.remove(policy); + ResponseEntity rsp = this.restTemplate.getForEntity(url, String.class); + assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value()); + } + } + + @Test + public void testDeletePolicy() throws Exception { + reset(); + String url = baseUrl() + "/policy?instance=id"; + Policy policy = addPolicy("id", "typeName", "service1", "ric1"); + policy.ric().setState(Ric.RicState.IDLE); + assertThat(policies.size()).isEqualTo(1); + + this.restTemplate.delete(url); + + assertThat(policies.size()).isEqualTo(0); + } + + @Test + public void testGetPolicySchemas() throws Exception { + reset(); + addPolicyType("type1", "ric1"); + addPolicyType("type2", "ric2"); + + String url = baseUrl() + "/policy_schemas"; + String rsp = this.restTemplate.getForObject(url, String.class); + System.out.println("*** " + rsp); + assertThat(rsp).contains("type1"); + assertThat(rsp).contains("type2"); + assertThat(rsp).contains("title"); + + List info = parseSchemas(rsp); + assertEquals(2, info.size()); + + url = baseUrl() + "/policy_schemas?ric=ric1"; + rsp = this.restTemplate.getForObject(url, String.class); + assertThat(rsp).contains("type1"); + info = parseSchemas(rsp); + assertEquals(1, info.size()); + } + + @Test + public void testGetPolicySchema() throws Exception { + reset(); + addPolicyType("type1", "ric1"); + addPolicyType("type2", "ric2"); + + String url = baseUrl() + "/policy_schema?id=type1"; + String rsp = this.restTemplate.getForObject(url, String.class); + System.out.println(rsp); + assertThat(rsp).contains("type1"); + assertThat(rsp).contains("title"); + } + + @Test + public void testGetPolicyTypes() throws Exception { + reset(); + addPolicyType("type1", "ric1"); + addPolicyType("type2", "ric2"); + + String url = baseUrl() + "/policy_types"; + String rsp = this.restTemplate.getForObject(url, String.class); + assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]"); + + url = baseUrl() + "/policy_types?ric=ric1"; + rsp = this.restTemplate.getForObject(url, String.class); + assertThat(rsp).isEqualTo("[\"type1\"]"); + } + + @Test + public void testGetPolicies() throws Exception { + String url = baseUrl() + "/policies"; + addPolicy("id1", "type1", "service1"); + + String rsp = this.restTemplate.getForObject(url, String.class); + System.out.println(rsp); + 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 + public void testGetPoliciesFilter() throws Exception { + addPolicy("id1", "type1", "service1"); + addPolicy("id2", "type1", "service2"); + addPolicy("id3", "type2", "service1"); + + String url = baseUrl() + "/policies?type=type1"; + String rsp = this.restTemplate.getForObject(url, String.class); + System.out.println(rsp); + assertThat(rsp).contains("id1"); + assertThat(rsp).contains("id2"); + assertFalse(rsp.contains("id3")); + + url = baseUrl() + "/policies?type=type1&service=service2"; + rsp = this.restTemplate.getForObject(url, String.class); + System.out.println(rsp); + assertFalse(rsp.contains("id1")); + assertThat(rsp).contains("id2"); + assertFalse(rsp.contains("id3")); + } + + @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, ServiceStatus.class); + assertThat(info.size() == 1); + ServiceStatus status = info.iterator().next(); + assertThat(status.keepAliveIntervalSeconds == 1); + assertThat(status.name.equals("name")); + + // GET (all) + url = baseUrl() + "/services"; + rsp = this.restTemplate.getForObject(url, String.class); + 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) { + List result = new ArrayList<>(); + JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray(); + for (JsonElement jsonElement : jsonArr) { + T o = gson.fromJson(jsonElement.toString(), clazz); + result.add(o); + } + return result; + } + + private static List parseSchemas(String jsonString) { + JsonArray arrayOfSchema = JsonParser.parseString(jsonString).getAsJsonArray(); + List result = new ArrayList<>(); + for (JsonElement schemaObject : arrayOfSchema) { + result.add(schemaObject.toString()); + } + return result; + } + +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java b/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java new file mode 100644 index 00000000..1ea677ca --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java @@ -0,0 +1,151 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.repository.ImmutablePolicyType; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.utils.MockA1ClientFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +public class MockPolicyAgent { + + @Autowired + Rics rics; + + static class MockApplicationConfig extends ApplicationConfig { + @Override + public String getLocalConfigurationFilePath() { + URL url = MockApplicationConfig.class.getClassLoader().getResource("test_application_configuration.json"); + return url.getFile(); + } + } + + /** + * Overrides the BeanFactory. + */ + @TestConfiguration + static class TestBeanFactory { + + private final Rics rics = new Rics(); + private final Policies policies = new Policies(); + private final PolicyTypes policyTypes = new PolicyTypes(); + + @Bean + public ApplicationConfig getApplicationConfig() { + return new MockApplicationConfig(); + } + + @Bean + public MockA1ClientFactory getA1ClientFactory() { + PolicyTypes ricTypes = new PolicyTypes(); + loadTypes(ricTypes); + return new MockA1ClientFactory(ricTypes); + } + + @Bean + public Policies getPolicies() { + return this.policies; + } + + @Bean + public PolicyTypes getPolicyTypes() { + return this.policyTypes; + } + + @Bean + public Rics getRics() { + return this.rics; + } + + private static File[] getResourceFolderFiles(String folder) { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + URL url = loader.getResource(folder); + String path = url.getPath(); + return new File(path).listFiles(); + } + + private static String readFile(File file) throws IOException { + return new String(Files.readAllBytes(file.toPath())); + } + + private void loadTypes(PolicyTypes policyTypes) { + File[] files = getResourceFolderFiles("policy_types/"); + for (File file : files) { + try { + String schema = readFile(file); + String typeName = title(schema); + PolicyType type = ImmutablePolicyType.builder().name(typeName).schema(schema).build(); + policyTypes.put(type); + } catch (Exception e) { + System.out.println("Could not load json schema " + e); + } + } + } + } + + @LocalServerPort + private int port; + + private void keepServerAlive() { + System.out.println("Keeping server alive!"); + try { + synchronized (this) { + this.wait(); + } + } catch (Exception ex) { + System.out.println("Unexpected: " + ex.toString()); + } + } + + private static String title(String jsonSchema) { + JsonObject parsedSchema = (JsonObject) JsonParser.parseString(jsonSchema); + String title = parsedSchema.get("title").getAsString(); + return title; + } + + @Test + public void runMock() throws Exception { + keepServerAlive(); + } + +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/clients/StdA1ClientTest.java b/policy-agent/src/test/java/org/oransc/policyagent/clients/StdA1ClientTest.java new file mode 100644 index 00000000..05e5463d --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/clients/StdA1ClientTest.java @@ -0,0 +1,171 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.clients; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Vector; + +import org.json.JSONException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oransc.policyagent.configuration.ImmutableRicConfig; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.ImmutablePolicy; +import org.oransc.policyagent.repository.ImmutablePolicyType; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.Ric; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@ExtendWith(MockitoExtension.class) +public class StdA1ClientTest { + private static final String RIC_URL = "RicUrl"; + private static final String POLICYTYPES_IDENTITIES_URL = "/policytypes"; + private static final String POLICIES_IDENTITIES_URL = "/policies"; + private static final String POLICYTYPES_URL = "/policytypes/"; + private static final String POLICIES_URL = "/policies/"; + + private static final String POLICY_TYPE_1_NAME = "type1"; + private static final String POLICY_TYPE_2_NAME = "type2"; + private static final String POLICY_TYPE_SCHEMA_VALID = "{\"type\":\"type1\"}"; + private static final String POLICY_TYPE_SCHEMA_INVALID = "\"type\":\"type1\"}"; + private static final String POLICY_1_ID = "policy1"; + private static final String POLICY_2_ID = "policy2"; + private static final String POLICY_JSON_VALID = "{\"policyId\":\"policy1\"}"; + private static final String POLICY_JSON_INVALID = "\"policyId\":\"policy1\"}"; + private static final String POLICY_TYPE = "typeName"; + + StdA1Client a1Client; + + AsyncRestClient asyncRestClientMock; + + @BeforeEach + public void init() { + asyncRestClientMock = mock(AsyncRestClient.class); + a1Client = spy(new StdA1Client(createRic(RIC_URL).getConfig(), asyncRestClientMock)); + } + + @Test + public void testGetPolicyTypeIdentities() { + Mono policyTypeIds = Mono.just(Arrays.toString(new String[] {POLICY_TYPE_1_NAME, POLICY_TYPE_2_NAME})); + when(asyncRestClientMock.get(POLICYTYPES_IDENTITIES_URL)).thenReturn(policyTypeIds); + + Mono policyTypeIdsFlux = a1Client.getPolicyTypeIdentities(); + verify(asyncRestClientMock).get(POLICYTYPES_IDENTITIES_URL); + StepVerifier.create(policyTypeIdsFlux).expectNextCount(1).expectComplete().verify(); + } + + @Test + public void testGetPolicyIdentities() { + Mono policyIds = Mono.just(Arrays.toString(new String[] {POLICY_1_ID, POLICY_2_ID})); + when(asyncRestClientMock.get(POLICIES_IDENTITIES_URL)).thenReturn(policyIds); + + Mono policyIdsFlux = a1Client.getPolicyIdentities(); + verify(asyncRestClientMock).get(POLICIES_IDENTITIES_URL); + StepVerifier.create(policyIdsFlux).expectNextCount(1).expectComplete().verify(); + } + + @Test + public void testGetValidPolicyType() { + Mono policyTypeResp = + Mono.just("{\"policySchema\": " + POLICY_TYPE_SCHEMA_VALID + ", \"statusSchema\": {} }"); + + doReturn(policyTypeResp).when(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_NAME); + + Mono policyTypeMono = a1Client.getPolicyTypeSchema(POLICY_TYPE_1_NAME); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_NAME); + StepVerifier.create(policyTypeMono).expectNext(POLICY_TYPE_SCHEMA_VALID).expectComplete().verify(); + } + + @Test + public void testGetInvalidPolicyType() { + when(asyncRestClientMock.get(POLICYTYPES_URL + POLICY_TYPE_1_NAME)) + .thenReturn(Mono.just(POLICY_TYPE_SCHEMA_INVALID)); + + Mono policyTypeMono = a1Client.getPolicyTypeSchema(POLICY_TYPE_1_NAME); + verify(asyncRestClientMock).get(POLICYTYPES_URL + POLICY_TYPE_1_NAME); + StepVerifier.create(policyTypeMono).expectErrorMatches(throwable -> throwable instanceof JSONException) + .verify(); + } + + @Test + public void testPutPolicyValidResponse() { + when(asyncRestClientMock.put(anyString(), anyString())).thenReturn(Mono.just(POLICY_JSON_VALID)); + + Mono policyMono = + a1Client.putPolicy(createPolicy(RIC_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE)); + verify(asyncRestClientMock).put(POLICIES_URL + POLICY_1_ID + "?policyTypeId=" + POLICY_TYPE, POLICY_JSON_VALID); + StepVerifier.create(policyMono).expectNext(POLICY_JSON_VALID).expectComplete().verify(); + } + + @Test + public void testPutPolicyInvalidResponse() { + when(asyncRestClientMock.put(anyString(), anyString())).thenReturn(Mono.just(POLICY_JSON_INVALID)); + + Mono policyMono = + a1Client.putPolicy(createPolicy(RIC_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE)); + StepVerifier.create(policyMono).expectErrorMatches(throwable -> throwable instanceof JSONException).verify(); + } + + private Policy createPolicy(String nearRtRicUrl, String policyId, String json, String type) { + return ImmutablePolicy.builder() // + .id(policyId) // + .json(json) // + .ownerServiceName("service") // + .ric(createRic(nearRtRicUrl)) // + .type(createPolicyType(type)) // + .lastModified("now") // + .build(); + } + + private PolicyType createPolicyType(String name) { + return ImmutablePolicyType.builder().name(name).schema("schema").build(); + } + + private Ric createRic(String url) { + RicConfig cfg = ImmutableRicConfig.builder().name("ric") // + .baseUrl(url) // + .managedElementIds(new Vector(Arrays.asList("kista_1", "kista_2"))) // + .build(); + return new Ric(cfg); + } + + @Test + public void testDeletePolicy() { + when(asyncRestClientMock.delete(POLICIES_URL + POLICY_1_ID)).thenReturn(Mono.empty()); + + Policy policy = createPolicy(RIC_URL, POLICY_1_ID, POLICY_JSON_VALID, POLICY_TYPE); + Mono responseMono = a1Client.deletePolicy(policy); + verify(asyncRestClientMock).delete(POLICIES_URL + POLICY_1_ID); + StepVerifier.create(responseMono).expectComplete().verify(); + } +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigParserTest.java b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigParserTest.java new file mode 100644 index 00000000..3444540b --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigParserTest.java @@ -0,0 +1,95 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.configuration; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.onap.dmaap.mr.test.clients.ProtocolTypeConstants; +import org.springframework.http.MediaType; + +public class ApplicationConfigParserTest { + + @Test + public void whenCorrectDmaapConfig() throws Exception { + JsonObject jsonRootObject = getJsonRootObject(); + + ApplicationConfigParser parserUnderTest = new ApplicationConfigParser(); + + parserUnderTest.parse(jsonRootObject); + + Properties actualPublisherConfig = parserUnderTest.getDmaapPublisherConfig(); + assertAll("publisherConfig", + () -> assertEquals("localhost:6845/events", actualPublisherConfig.get("ServiceName"), "Wrong ServiceName"), + () -> assertEquals("A1-POLICY-AGENT-WRITE", actualPublisherConfig.get("topic"), "Wrong topic"), + () -> assertEquals("localhost:6845", actualPublisherConfig.get("host"), "Wrong host"), + () -> assertEquals(MediaType.APPLICATION_JSON.toString(), actualPublisherConfig.get("contenttype"), + "Wrong contenttype"), + () -> assertEquals("admin", actualPublisherConfig.get("userName"), "Wrong userName"), + () -> assertEquals("admin", actualPublisherConfig.get("password"), "Wrong password"), + () -> assertEquals(ProtocolTypeConstants.HTTPNOAUTH.toString(), actualPublisherConfig.get("TransportType"), + "Wrong TransportType"), + () -> assertEquals(15000, actualPublisherConfig.get("timeout"), "Wrong timeout"), + () -> assertEquals(100, actualPublisherConfig.get("limit"), "Wrong limit")); + + Properties actualConsumerConfig = parserUnderTest.getDmaapConsumerConfig(); + assertAll("consumerConfig", + () -> assertEquals("localhost:6845/events", actualConsumerConfig.get("ServiceName"), "Wrong ServiceName"), + () -> assertEquals("A1-POLICY-AGENT-READ", actualConsumerConfig.get("topic"), "Wrong topic"), + () -> assertEquals("localhost:6845", actualConsumerConfig.get("host"), "Wrong host"), + () -> assertEquals(MediaType.APPLICATION_JSON.toString(), actualConsumerConfig.get("contenttype"), + "Wrong contenttype"), + () -> assertEquals("admin", actualConsumerConfig.get("userName"), "Wrong userName"), + () -> assertEquals("admin", actualConsumerConfig.get("password"), "Wrong password"), + () -> assertEquals("users", actualConsumerConfig.get("group"), "Wrong group"), + () -> assertEquals("policy-agent", actualConsumerConfig.get("id"), "Wrong id"), + () -> assertEquals(ProtocolTypeConstants.HTTPNOAUTH.toString(), actualConsumerConfig.get("TransportType"), + "Wrong TransportType"), + () -> assertEquals(15000, actualConsumerConfig.get("timeout"), "Wrong timeout"), + () -> assertEquals(100, actualConsumerConfig.get("limit"), "Wrong limit")); + } + + private JsonObject getJsonRootObject() throws JsonIOException, JsonSyntaxException, IOException { + JsonObject rootObject = JsonParser.parseReader(new InputStreamReader(getCorrectJson())).getAsJsonObject(); + return rootObject; + } + + private static InputStream getCorrectJson() throws IOException { + URL url = ApplicationConfigParser.class.getClassLoader() + .getResource("test_application_configuration_with_dmaap_config.json"); + String string = Resources.toString(url, Charsets.UTF_8); + return new ByteArrayInputStream((string.getBytes(StandardCharsets.UTF_8))); + } + +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java new file mode 100644 index 00000000..257776db --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java @@ -0,0 +1,126 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.configuration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; + +import java.util.Arrays; +import java.util.Vector; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oransc.policyagent.configuration.ApplicationConfig.Observer; +import org.oransc.policyagent.exceptions.ServiceException; + +@ExtendWith(MockitoExtension.class) +public class ApplicationConfigTest { + @Mock + Observer observerMock1; + + @Mock + Observer observerMock2; + + private static final ImmutableRicConfig RIC_CONFIG_1 = ImmutableRicConfig.builder() // + .name("ric1") // + .baseUrl("ric1_url") // + .managedElementIds(new Vector<>()) // + .build(); + + @Test + public void addRicShouldNotifyAllObserversOfRicAdded() throws Exception { + ApplicationConfig appConfigUnderTest = new ApplicationConfig(); + + appConfigUnderTest.addObserver(observerMock1); + appConfigUnderTest.addObserver(observerMock2); + + appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null); + + verify(observerMock1).onRicConfigUpdate(RIC_CONFIG_1, ApplicationConfig.RicConfigUpdate.ADDED); + verify(observerMock2).onRicConfigUpdate(RIC_CONFIG_1, ApplicationConfig.RicConfigUpdate.ADDED); + + assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configuraions."); + + assertEquals(RIC_CONFIG_1, appConfigUnderTest.getRic(RIC_CONFIG_1.name()), + "Not correct Ric retrieved from configurations."); + } + + @Test + public void changedRicShouldNotifyAllObserversOfRicChanged() throws Exception { + ApplicationConfig appConfigUnderTest = new ApplicationConfig(); + + appConfigUnderTest.addObserver(observerMock1); + + appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null); + + ImmutableRicConfig changedRicConfig = ImmutableRicConfig.builder() // + .name("ric1") // + .baseUrl("changed_ric1_url") // + .managedElementIds(new Vector<>()) // + .build(); + + appConfigUnderTest.setConfiguration(Arrays.asList(changedRicConfig), null, null); + + verify(observerMock1).onRicConfigUpdate(RIC_CONFIG_1, ApplicationConfig.RicConfigUpdate.ADDED); + verify(observerMock1).onRicConfigUpdate(changedRicConfig, ApplicationConfig.RicConfigUpdate.CHANGED); + + assertEquals(changedRicConfig, appConfigUnderTest.getRic(RIC_CONFIG_1.name()), + "Changed Ric not retrieved from configurations."); + } + + @Test + public void removedRicShouldNotifyAllObserversOfRicRemoved() { + ApplicationConfig appConfigUnderTest = new ApplicationConfig(); + + appConfigUnderTest.addObserver(observerMock1); + + ImmutableRicConfig ricConfig2 = ImmutableRicConfig.builder() // + .name("ric2") // + .baseUrl("ric2_url") // + .managedElementIds(new Vector<>()) // + .build(); + + appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1, ricConfig2), null, null); + + appConfigUnderTest.setConfiguration(Arrays.asList(ricConfig2), null, null); + + verify(observerMock1).onRicConfigUpdate(RIC_CONFIG_1, ApplicationConfig.RicConfigUpdate.REMOVED); + + assertEquals(1, appConfigUnderTest.getRicConfigs().size(), "Ric not deleted from configurations."); + } + + @Test + public void gettingNotAddedRicShouldThrowException() { + ApplicationConfig appConfigUnderTest = new ApplicationConfig(); + + appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null); + + Exception exception = assertThrows(ServiceException.class, () -> { + appConfigUnderTest.getRic("name"); + }); + + assertEquals("Could not find ric: name", exception.getMessage()); + } +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java new file mode 100644 index 00000000..5aeb2404 --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java @@ -0,0 +1,115 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 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========================================================= + */ + +package org.oransc.policyagent.dmaap; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onap.dmaap.mr.client.MRBatchingPublisher; +import org.onap.dmaap.mr.client.response.MRPublisherResponse; +import org.oransc.policyagent.clients.AsyncRestClient; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +public class DmaapMessageHandlerTest { + + private ApplicationConfig appConfig = mock(ApplicationConfig.class); + private final MRBatchingPublisher dmaapClient = mock(MRBatchingPublisher.class); + private final AsyncRestClient agentClient = mock(AsyncRestClient.class); + private DmaapMessageHandler testedObject; + private static Gson gson = new GsonBuilder() // + .serializeNulls() // + .create(); // + + @BeforeEach + private void setUp() throws Exception { + testedObject = spy(new DmaapMessageHandler(dmaapClient, appConfig, agentClient)); + } + + ImmutableDmaapRequestMessage dmaapRequestMessage(Operation operation) { + return ImmutableDmaapRequestMessage.builder().apiVersion("apiVersion") // + .correlationId("correlationId") // + .operation(operation) // + .originatorId("originatorId") // + .payload("payload") // + .requestId("requestId") // + .target("target") // + .timestamp("timestamp") // + .type("type") // + .url("url") // + .build(); + } + + private String dmaapInputMessage(Operation operation) { + return gson.toJson(dmaapRequestMessage(operation)); + } + + @Test + public void successfulCase() throws IOException { + doReturn(Mono.just("OK")).when(agentClient).delete("url"); + doReturn(1).when(dmaapClient).send(anyString()); + doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); + + StepVerifier // + .create(testedObject.createTask(dmaapInputMessage(Operation.DELETE))) // + .expectSubscription() // + .expectNext("OK") // + .verifyComplete(); // + + verify(agentClient, times(1)).delete("url"); + verifyNoMoreInteractions(agentClient); + + verify(dmaapClient, times(1)).send(anyString()); + verify(dmaapClient, times(1)).sendBatchWithResponse(); + verifyNoMoreInteractions(dmaapClient); + } + + @Test + public void errorCase() throws IOException { + doReturn(Mono.error(new Exception("Refused"))).when(agentClient).put("url", "payload"); + doReturn(1).when(dmaapClient).send(anyString()); + doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); + StepVerifier // + .create(testedObject.createTask(dmaapInputMessage(Operation.PUT))) // + .expectSubscription() // + .verifyComplete(); // + + verify(agentClient, times(1)).put("url", "payload"); + verifyNoMoreInteractions(agentClient); + + // Error response + verify(dmaapClient, times(1)).send(anyString()); + verify(dmaapClient, times(1)).sendBatchWithResponse(); + verifyNoMoreInteractions(dmaapClient); + } + +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java new file mode 100644 index 00000000..90b38474 --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java @@ -0,0 +1,207 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Properties; +import java.util.Vector; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperties; +import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.configuration.ApplicationConfigParser; +import org.oransc.policyagent.configuration.ImmutableRicConfig; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.exceptions.ServiceException; +import org.oransc.policyagent.utils.LoggingUtils; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +@ExtendWith(MockitoExtension.class) +public class RefreshConfigTaskTest { + + private RefreshConfigTask refreshTaskUnderTest; + + @Spy + ApplicationConfig appConfig; + + @Mock + CbsClient cbsClient; + + public static final ImmutableRicConfig CORRECT_RIC_CONIFG = ImmutableRicConfig.builder() // + .name("ric1") // + .baseUrl("http://localhost:8080/") // + .managedElementIds(new Vector(Arrays.asList("kista_1", "kista_2"))) // + .build(); + + private static EnvProperties properties() { + return ImmutableEnvProperties.builder() // + .consulHost("host") // + .consulPort(123) // + .cbsName("cbsName") // + .appName("appName") // + .build(); + } + + @Test + public void whenTheConfigurationFits() throws IOException, ServiceException { + refreshTaskUnderTest = spy(new RefreshConfigTask(appConfig)); + refreshTaskUnderTest.systemEnvironment = new Properties(); + // When + doReturn(getCorrectJson()).when(refreshTaskUnderTest).createInputStream(any()); + doReturn("fileName").when(appConfig).getLocalConfigurationFilePath(); + refreshTaskUnderTest.start(); + + // Then + verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile(); + + Iterable ricConfigs = appConfig.getRicConfigs(); + RicConfig ricConfig = ricConfigs.iterator().next(); + assertThat(ricConfigs).isNotNull(); + assertThat(ricConfig).isEqualTo(CORRECT_RIC_CONIFG); + } + + @Test + public void whenFileIsExistsButJsonIsIncorrect() throws IOException, ServiceException { + refreshTaskUnderTest = spy(new RefreshConfigTask(appConfig)); + refreshTaskUnderTest.systemEnvironment = new Properties(); + + // When + doReturn(getIncorrectJson()).when(refreshTaskUnderTest).createInputStream(any()); + doReturn("fileName").when(appConfig).getLocalConfigurationFilePath(); + refreshTaskUnderTest.loadConfigurationFromFile(); + + // Then + verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile(); + Assertions.assertEquals(0, appConfig.getRicConfigs().size()); + } + + @Test + public void whenPeriodicConfigRefreshNoEnvironmentVariables() { + refreshTaskUnderTest = spy(new RefreshConfigTask(appConfig)); + refreshTaskUnderTest.systemEnvironment = new Properties(); + + final ListAppender logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class); + Flux task = refreshTaskUnderTest.createRefreshTask(); + + StepVerifier.create(task).expectSubscription().verifyComplete(); + + assertTrue(logAppender.list.toString().contains("$CONSUL_HOST environment has not been defined")); + } + + @Test + public void whenPeriodicConfigRefreshNoConsul() { + refreshTaskUnderTest = spy(new RefreshConfigTask(appConfig)); + refreshTaskUnderTest.systemEnvironment = new Properties(); + + EnvProperties props = properties(); + doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any()); + + doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props); + Flux err = Flux.error(new IOException()); + doReturn(err).when(cbsClient).updates(any(), any(), any()); + + final ListAppender logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class); + Flux task = refreshTaskUnderTest.createRefreshTask(); + + StepVerifier // + .create(task) // + .expectSubscription() // + .verifyComplete(); + + assertTrue( + logAppender.list.toString().contains("Could not refresh application configuration java.io.IOException")); + } + + @Test + public void whenPeriodicConfigRefreshSuccess() throws JsonIOException, JsonSyntaxException, IOException { + refreshTaskUnderTest = spy(new RefreshConfigTask(appConfig)); + refreshTaskUnderTest.systemEnvironment = new Properties(); + + EnvProperties props = properties(); + doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any()); + doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props); + + Flux json = Flux.just(getJsonRootObject()); + doReturn(json).when(cbsClient).updates(any(), any(), any()); + + Flux task = refreshTaskUnderTest.createRefreshTask(); + + StepVerifier // + .create(task) // + .expectSubscription() // + .expectNext(appConfig) // + .verifyComplete(); + + Assertions.assertNotNull(appConfig.getRicConfigs()); + } + + private JsonObject getJsonRootObject() throws JsonIOException, JsonSyntaxException, IOException { + JsonObject rootObject = JsonParser.parseReader(new InputStreamReader(getCorrectJson())).getAsJsonObject(); + return rootObject; + } + + private static InputStream getCorrectJson() throws IOException { + URL url = ApplicationConfigParser.class.getClassLoader().getResource("test_application_configuration.json"); + String string = Resources.toString(url, Charsets.UTF_8); + return new ByteArrayInputStream((string.getBytes(StandardCharsets.UTF_8))); + } + + private static InputStream getIncorrectJson() { + String string = "{" + // + " \"config\": {" + // + " \"ric\": {"; // + return new ByteArrayInputStream((string.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java new file mode 100644 index 00000000..fb8d46fd --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java @@ -0,0 +1,131 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oransc.policyagent.clients.A1Client; +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.configuration.ImmutableRicConfig; +import org.oransc.policyagent.repository.ImmutablePolicy; +import org.oransc.policyagent.repository.ImmutablePolicyType; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Ric.RicState; +import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.repository.Services; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@ExtendWith(MockitoExtension.class) +public class RepositorySupervisionTest { + @Mock + A1Client a1ClientMock; + + @Mock + A1ClientFactory a1ClientFactory; + + @BeforeEach + public void init() { + doReturn(Mono.just(a1ClientMock)).when(a1ClientFactory).createA1Client(any()); + } + + @Test + public void test() { + Ric ric1 = new Ric(ImmutableRicConfig.builder() // + .name("ric1") // + .baseUrl("baseUrl1") // + .managedElementIds(new Vector(Arrays.asList("kista_1", "kista_2"))) // + .build()); + ric1.setState(Ric.RicState.IDLE); + Ric ric2 = new Ric(ImmutableRicConfig.builder() // + .name("ric2") // + .baseUrl("baseUrl2") // + .managedElementIds(new Vector(Arrays.asList("kista_3", "kista_4"))) // + .build()); + ric2.setState(Ric.RicState.UNDEFINED); + Ric ric3 = new Ric(ImmutableRicConfig.builder() // + .name("ric3") // + .baseUrl("baseUrl3") // + .managedElementIds(new Vector(Arrays.asList("kista_5"))) // + .build()); + Rics rics = new Rics(); + rics.put(ric1); + rics.put(ric2); + rics.put(ric3); + + PolicyType policyType = ImmutablePolicyType.builder() // + .name("type") // + .schema("") // + .build(); + Policy policy1 = ImmutablePolicy.builder() // + .id("policyId1") // + .json("") // + .ownerServiceName("service") // + .ric(ric1) // + .type(policyType) // + .lastModified("now") // + .build(); + Policies policies = new Policies(); + policies.put(policy1); + PolicyTypes types = new PolicyTypes(); + Services services = new Services(); + + Mono> policyIds = Mono.just(Arrays.asList("policyId1", "policyId2")); + + doReturn(policyIds).when(a1ClientMock).getPolicyTypeIdentities(); + doReturn(policyIds).when(a1ClientMock).getPolicyIdentities(); + doReturn(Mono.just("schema")).when(a1ClientMock).getPolicyTypeSchema(anyString()); + doReturn(Mono.just("OK")).when(a1ClientMock).putPolicy(any()); + doReturn(Flux.empty()).when(a1ClientMock).deleteAllPolicies(); + + RepositorySupervision supervisorUnderTest = + new RepositorySupervision(rics, policies, a1ClientFactory, types, services); + + supervisorUnderTest.checkAllRics(); + + await().untilAsserted(() -> RicState.IDLE.equals(ric1.getState())); + await().untilAsserted(() -> RicState.IDLE.equals(ric2.getState())); + await().untilAsserted(() -> RicState.IDLE.equals(ric3.getState())); + + verify(a1ClientMock, times(3)).deleteAllPolicies(); + verifyNoMoreInteractions(a1ClientMock); + } +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/tasks/StartupServiceTest.java b/policy-agent/src/test/java/org/oransc/policyagent/tasks/StartupServiceTest.java new file mode 100644 index 00000000..8bf705cd --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/tasks/StartupServiceTest.java @@ -0,0 +1,201 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.tasks; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.oransc.policyagent.repository.Ric.RicState.IDLE; + +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.oransc.policyagent.clients.A1Client; +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.configuration.ImmutableRicConfig; +import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.oransc.policyagent.repository.Ric.RicState; +import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.repository.Services; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@ExtendWith(MockitoExtension.class) +public class StartupServiceTest { + private static final String FIRST_RIC_NAME = "first"; + private static final String FIRST_RIC_URL = "firstUrl"; + private static final String SECOND_RIC_NAME = "second"; + private static final String SECOND_RIC_URL = "secondUrl"; + private static final String MANAGED_NODE_A = "nodeA"; + private static final String MANAGED_NODE_B = "nodeB"; + private static final String MANAGED_NODE_C = "nodeC"; + + private static final String POLICY_TYPE_1_NAME = "type1"; + private static final String POLICY_TYPE_2_NAME = "type2"; + + ApplicationConfig appConfigMock; + RefreshConfigTask refreshTaskMock; + + A1Client a1ClientMock; + A1ClientFactory a1ClientFactory; + + @BeforeEach + public void init() throws Exception { + a1ClientMock = mock(A1Client.class); + a1ClientFactory = mock(A1ClientFactory.class); + appConfigMock = mock(ApplicationConfig.class); + refreshTaskMock = mock(RefreshConfigTask.class); + doReturn(Mono.just(a1ClientMock)).when(a1ClientFactory).createA1Client(any()); + } + + @Test + public void startup_allOk() { + Mono> policyTypes1 = Mono.just(Arrays.asList(POLICY_TYPE_1_NAME)); + Mono> policyTypes2 = Mono.just(Arrays.asList(POLICY_TYPE_1_NAME, POLICY_TYPE_2_NAME)); + doReturn(policyTypes1, policyTypes2).when(a1ClientMock).getPolicyTypeIdentities(); + doReturn(Mono.just("Schema")).when(a1ClientMock).getPolicyTypeSchema(anyString()); + doReturn(Flux.just("OK")).when(a1ClientMock).deleteAllPolicies(); + + Rics rics = new Rics(); + PolicyTypes policyTypes = new PolicyTypes(); + StartupService serviceUnderTest = new StartupService(appConfigMock, refreshTaskMock, rics, policyTypes, + a1ClientFactory, new Policies(), new Services()); + + serviceUnderTest.startup(); + + serviceUnderTest.onRicConfigUpdate(getRicConfig(FIRST_RIC_NAME, FIRST_RIC_URL, MANAGED_NODE_A), + ApplicationConfig.RicConfigUpdate.ADDED); + serviceUnderTest.onRicConfigUpdate( + getRicConfig(SECOND_RIC_NAME, SECOND_RIC_URL, MANAGED_NODE_B, MANAGED_NODE_C), + ApplicationConfig.RicConfigUpdate.ADDED); + + await().untilAsserted(() -> assertThat(policyTypes.size()).isEqualTo(2)); + + verify(a1ClientMock, times(2)).deleteAllPolicies(); + + assertTrue(policyTypes.contains(POLICY_TYPE_1_NAME), POLICY_TYPE_1_NAME + " not added to PolicyTypes."); + assertTrue(policyTypes.contains(POLICY_TYPE_2_NAME), POLICY_TYPE_2_NAME + " not added to PolicyTypes."); + assertEquals(2, rics.size(), "Correct number of Rics not added to Rics"); + + Ric firstRic = rics.get(FIRST_RIC_NAME); + assertNotNull(firstRic, "Ric " + FIRST_RIC_NAME + " not added to repository"); + assertEquals(FIRST_RIC_NAME, firstRic.name(), FIRST_RIC_NAME + " not added to Rics"); + assertEquals(IDLE, firstRic.getState(), "Not correct state for ric " + FIRST_RIC_NAME); + assertEquals(1, firstRic.getSupportedPolicyTypes().size(), + "Not correct no of types supported for ric " + FIRST_RIC_NAME); + assertTrue(firstRic.isSupportingType(POLICY_TYPE_1_NAME), + POLICY_TYPE_1_NAME + " not supported by ric " + FIRST_RIC_NAME); + assertEquals(1, firstRic.getManagedElementIds().size(), + "Not correct no of managed nodes for ric " + FIRST_RIC_NAME); + assertTrue(firstRic.isManaging(MANAGED_NODE_A), MANAGED_NODE_A + " not managed by ric " + FIRST_RIC_NAME); + + Ric secondRic = rics.get(SECOND_RIC_NAME); + assertNotNull(secondRic, "Ric " + SECOND_RIC_NAME + " not added to repository"); + assertEquals(SECOND_RIC_NAME, secondRic.name(), SECOND_RIC_NAME + " not added to Rics"); + assertEquals(IDLE, secondRic.getState(), "Not correct state for " + SECOND_RIC_NAME); + assertEquals(2, secondRic.getSupportedPolicyTypes().size(), + "Not correct no of types supported for ric " + SECOND_RIC_NAME); + assertTrue(secondRic.isSupportingType(POLICY_TYPE_1_NAME), + POLICY_TYPE_1_NAME + " not supported by ric " + SECOND_RIC_NAME); + assertTrue(secondRic.isSupportingType(POLICY_TYPE_2_NAME), + POLICY_TYPE_2_NAME + " not supported by ric " + SECOND_RIC_NAME); + assertEquals(2, secondRic.getManagedElementIds().size(), + "Not correct no of managed nodes for ric " + SECOND_RIC_NAME); + assertTrue(secondRic.isManaging(MANAGED_NODE_B), MANAGED_NODE_B + " not managed by ric " + SECOND_RIC_NAME); + assertTrue(secondRic.isManaging(MANAGED_NODE_C), MANAGED_NODE_C + " not managed by ric " + SECOND_RIC_NAME); + } + + @Test + public void startup_unableToConnectToGetTypes() { + Mono error = Mono.error(new Exception("Unable to contact ric.")); + doReturn(error, error).when(a1ClientMock).getPolicyTypeIdentities(); + + Rics rics = new Rics(); + PolicyTypes policyTypes = new PolicyTypes(); + StartupService serviceUnderTest = new StartupService(appConfigMock, refreshTaskMock, rics, policyTypes, + a1ClientFactory, new Policies(), new Services()); + + serviceUnderTest.startup(); + serviceUnderTest.onRicConfigUpdate(getRicConfig(FIRST_RIC_NAME, FIRST_RIC_URL, MANAGED_NODE_A), + ApplicationConfig.RicConfigUpdate.ADDED); + + assertEquals(RicState.UNDEFINED, rics.get(FIRST_RIC_NAME).getState(), + "Not correct state for " + FIRST_RIC_NAME); + } + + @Test + public void startup_unableToConnectToDeleteAllPolicies() { + + Mono> policyTypes = Mono.just(Arrays.asList(POLICY_TYPE_1_NAME)); + when(a1ClientMock.getPolicyTypeIdentities()).thenReturn(policyTypes); + when(a1ClientMock.getPolicyTypeSchema(anyString())).thenReturn(Mono.just("Schema")); + Flux error = Flux.error(new Exception("Unable to contact ric.")); + doReturn(error).when(a1ClientMock).deleteAllPolicies(); + + Rics rics = new Rics(); + StartupService serviceUnderTest = new StartupService(appConfigMock, refreshTaskMock, rics, new PolicyTypes(), + a1ClientFactory, new Policies(), new Services()); + + serviceUnderTest.startup(); + serviceUnderTest.onRicConfigUpdate(getRicConfig(FIRST_RIC_NAME, FIRST_RIC_URL, MANAGED_NODE_A), + ApplicationConfig.RicConfigUpdate.ADDED); + + assertEquals(RicState.UNDEFINED, rics.get(FIRST_RIC_NAME).getState(), + "Not correct state for " + FIRST_RIC_NAME); + } + + @SafeVarargs + private Vector toVector(T... objs) { + Vector result = new Vector<>(); + for (T o : objs) { + result.add(o); + } + return result; + } + + private RicConfig getRicConfig(String name, String baseUrl, String... managedElementIds) { + ImmutableRicConfig ricConfig = ImmutableRicConfig.builder() // + .name(name) // + .managedElementIds(toVector(managedElementIds)) // + .baseUrl(baseUrl) // + .build(); + return ricConfig; + } +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/utils/LoggingUtils.java b/policy-agent/src/test/java/org/oransc/policyagent/utils/LoggingUtils.java new file mode 100644 index 00000000..a822bb35 --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/utils/LoggingUtils.java @@ -0,0 +1,56 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.utils; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; + +import org.slf4j.LoggerFactory; + +public class LoggingUtils { + + /** + * Returns a ListAppender that contains all logging events. Call this method at the very beginning of the test + */ + public static ListAppender getLogListAppender(Class logClass) { + return getLogListAppender(logClass, false); + } + + /** + * Returns a ListAppender that contains all logging events. Call this method at the very beginning of the test + * + * @param logClass class whose appender is wanted. + * @param allLevels true if all log levels should be activated. + */ + public static ListAppender getLogListAppender(Class logClass, boolean allLevels) { + Logger logger = (Logger) LoggerFactory.getLogger(logClass); + if (allLevels) { + logger.setLevel(Level.ALL); + } + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + logger.addAppender(listAppender); + + return listAppender; + } +} diff --git a/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1Client.java b/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1Client.java new file mode 100644 index 00000000..1151dd20 --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1Client.java @@ -0,0 +1,102 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.utils; + +import java.util.List; +import java.util.Vector; + +import org.oransc.policyagent.clients.A1Client; +import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; +import org.oransc.policyagent.repository.PolicyType; +import org.oransc.policyagent.repository.PolicyTypes; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class MockA1Client implements A1Client { + Policies policies = new Policies(); + private final PolicyTypes policyTypes; + + public MockA1Client(PolicyTypes policyTypes) { + this.policyTypes = policyTypes; + } + + @Override + public Mono> getPolicyTypeIdentities() { + synchronized (this.policyTypes) { + List result = new Vector<>(); + for (PolicyType p : this.policyTypes.getAll()) { + result.add(p.name()); + } + return Mono.just(result); + } + } + + @Override + public Mono> getPolicyIdentities() { + synchronized (this.policies) { + Vector result = new Vector<>(); + for (Policy policy : policies.getAll()) { + result.add(policy.id()); + } + + return Mono.just(result); + } + } + + @Override + public Mono getPolicyTypeSchema(String policyTypeId) { + try { + return Mono.just(this.policyTypes.getType(policyTypeId).schema()); + } catch (Exception e) { + return Mono.error(e); + } + } + + @Override + public Mono putPolicy(Policy p) { + this.policies.put(p); + return Mono.just("OK"); + } + + @Override + public Mono deletePolicy(Policy policy) { + this.policies.remove(policy); + return Mono.just("OK"); + } + + public Policies getPolicies() { + return this.policies; + } + + @Override + public Mono getProtocolVersion() { + return Mono.just(A1ProtocolType.STD_V1); + } + + @Override + public Flux deleteAllPolicies() { + this.policies.clear(); + return Flux.empty(); + } + +} 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 new file mode 100644 index 00000000..ec86a82f --- /dev/null +++ b/policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java @@ -0,0 +1,61 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 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. + * ========================LICENSE_END=================================== + */ + +package org.oransc.policyagent.utils; + +import static org.mockito.Mockito.mock; + +import java.lang.invoke.MethodHandles; +import java.util.HashMap; +import java.util.Map; + +import org.oransc.policyagent.clients.A1Client; +import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MockA1ClientFactory extends A1ClientFactory { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private final Map clients = new HashMap<>(); + private final PolicyTypes policyTypes; + + public MockA1ClientFactory(PolicyTypes policyTypes) { + super(mock(ApplicationConfig.class)); + this.policyTypes = policyTypes; + } + + @Override + protected A1Client createStdA1ClientImpl(Ric ric) { + return getOrCreateA1Client(ric.name()); + } + + public MockA1Client getOrCreateA1Client(String ricName) { + if (!clients.containsKey(ricName)) { + logger.debug("Creating client for RIC: {}", ricName); + MockA1Client client = new MockA1Client(policyTypes); + clients.put(ricName, client); + } + return clients.get(ricName); + } + +} diff --git a/policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json b/policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json new file mode 100644 index 00000000..02bc8645 --- /dev/null +++ b/policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json @@ -0,0 +1,71 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "STD_PolicyModelUnconstrained_0.2.0", + "description": "Standard model of a policy with unconstrained scope id combinations", + "type": "object", + "properties": { + "scope": { + "type": "object", + "properties": { + "ueId": {"type": "string"}, + "groupId": {"type": "string"}, + "sliceId": {"type": "string"}, + "qosId": {"type": "string"}, + "cellId": {"type": "string"} + }, + "minProperties": 1, + "additionalProperties": false + }, + "qosObjectives": { + "type": "object", + "properties": { + "gfbr": {"type": "number"}, + "mfbr": {"type": "number"}, + "priorityLevel": {"type": "number"}, + "pdb": {"type": "number"} + }, + "additionalProperties": false + }, + "qoeObjectives": { + "type": "object", + "properties": { + "qoeScore": {"type": "number"}, + "initialBuffering": {"type": "number"}, + "reBuffFreq": {"type": "number"}, + "stallRatio": {"type": "number"} + }, + "additionalProperties": false + }, + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cellIdList": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "preference": { + "type": "string", + "enum": [ + "SHALL", + "PREFER", + "AVOID", + "FORBID" + ] + }, + "primary": {"type": "boolean"} + }, + "additionalProperties": false, + "required": ["cellIdList", "preference"] + } + } + }, + "minProperties": 2, + "additionalProperties": false, + "required": ["scope"] +} diff --git a/policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json b/policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json new file mode 100644 index 00000000..f3eb28fb --- /dev/null +++ b/policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Example_QoETarget_1.0.0", + "description": "Example QoE Target policy type", + "type": "object", + "properties": { + "scope": { + "type": "object", + "properties": { + "ueId": { + "type": "string" + }, + "sliceId": { + "type": "string" + }, + "qosId": { + "type": "string" + }, + "cellId": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "ueId", + "sliceId" + ] + }, + "statement": { + "type": "object", + "properties": { + "qoeScore": { + "type": "number" + }, + "initialBuffering": { + "type": "number" + }, + "reBuffFreq": { + "type": "number" + }, + "stallRatio": { + "type": "number" + } + }, + "minProperties": 1, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json b/policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json new file mode 100644 index 00000000..a73dd590 --- /dev/null +++ b/policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ERIC_QoSNudging_0.2.0", + "description": "QoS nudging policy type with priorityLevel and ueId and qosId as scope", + "type": "object", + "properties": { + "scope": { + "type": "object", + "properties": { + "ueId": {"type": "string"}, + "qosId": {"type": "string"} + }, + "additionalProperties": false, + "required": ["ueId", "qosId"] + }, + "qosObjectives": { + "type": "object", + "properties": { + "priorityLevel": {"type": "number"} + }, + "additionalProperties": false, + "required": ["priorityLevel"] + } + }, + "additionalProperties": false, + "required": ["scope", "qosObjectives"] +} diff --git a/policy-agent/src/test/resources/test_application_configuration.json b/policy-agent/src/test/resources/test_application_configuration.json new file mode 100644 index 00000000..446c0611 --- /dev/null +++ b/policy-agent/src/test/resources/test_application_configuration.json @@ -0,0 +1,23 @@ +{ + "config": { + "//description": "Application configuration", + "ric": [ + { + "name": "ric1", + "baseUrl": "http://localhost:8080/", + "managedElementIds": [ + "kista_1", + "kista_2" + ] + }, + { + "name": "ric2", + "baseUrl": "http://localhost:8081/", + "managedElementIds": [ + "kista_3", + "kista_4" + ] + } + ] + } +} \ No newline at end of file diff --git a/policy-agent/src/test/resources/test_application_configuration_with_dmaap_config.json b/policy-agent/src/test/resources/test_application_configuration_with_dmaap_config.json new file mode 100644 index 00000000..ec292bf9 --- /dev/null +++ b/policy-agent/src/test/resources/test_application_configuration_with_dmaap_config.json @@ -0,0 +1,38 @@ +{ + "config":{ + "ric":[ + { + "name":"ric1", + "baseUrl":"http://localhost:8083/", + "managedElementIds":[ + "kista_1", + "kista_2" + ] + }, + { + "name":"ric2", + "baseUrl":"http://localhost:8085/", + "managedElementIds":[ + "kista_3", + "kista_4" + ] + } + ], + "streams_publishes":{ + "dmaap_publisher":{ + "type":"message_router", + "dmaap_info":{ + "topic_url":"http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-WRITE" + } + } + }, + "streams_subscribes":{ + "dmaap_subscriber":{ + "type":"message_router", + "dmaap_info":{ + "topic_url":"http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-READ/users/policy-agent" + } + } + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7ac3a79e..49446a8e 100644 --- a/pom.xml +++ b/pom.xml @@ -19,19 +19,20 @@ ============LICENSE_END========================================================= --> - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - org.oransc - nonrtric - 1.0.0-SNAPSHOT - pom + org.o-ran-sc + nonrtric + 1.0.1-SNAPSHOT + pom - nonrtric - - dashboard - near-rt-ric-simulator - sdnc-a1-controller - - + nonrtric + + policy-agent + sdnc-a1-controller + dashboard + near-rt-ric-simulator + + diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/model/src/main/yang/NONRT-RIC-API.yang b/sdnc-a1-controller/northbound/nonrt-ric-api/model/src/main/yang/NONRT-RIC-API.yang index 986e2017..40005d21 100644 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/model/src/main/yang/NONRT-RIC-API.yang +++ b/sdnc-a1-controller/northbound/nonrt-ric-api/model/src/main/yang/NONRT-RIC-API.yang @@ -27,84 +27,38 @@ module A1-ADAPTER-API { import ietf-yang-types { prefix yang; } - revision "2019-10-02" { + revision "2020-01-22" { description "A1 adapter"; } - //Flattend interface using RPC - - //Get an array of near-rt-ric IDs, - //Each item in the returned array will be regarded as one near-rt-ric-id. - rpc getNearRT-RICs { - output { - leaf-list near-rt-ric-id-list { - type string; - } - leaf code { - type string; - } - } - } - - //Get health status for a Near-RT-RIC. true - health ok, false - health is not ok. - rpc getHealthCheck { - input { - leaf near-rt-ric-id { - type string; - } - } - - output { - leaf health-status { - type boolean; - } - leaf code { - type string; - } - } - } - //Get an array of integer policy type ids //Each item in the returned array will be regarded as one policy-type-id. - rpc getPolicyTypes { + rpc getPolicyTypeIdentities { input { - leaf near-rt-ric-id { + leaf near-rt-ric-url { type string; } } output { leaf-list policy-type-id-list { - type uint32; - } - leaf code { type string; } } } - //Create a policy type - rpc createPolicyType { + //Get an array of integer policy ids + //Each item in the returned array will be regarded as one policy-id. + rpc getPolicyIdentities { input { - leaf near-rt-ric-id { - type string; - } - leaf policy-type-id { - type uint32; - } - leaf description { - type string; - } - leaf name { - type string; - } - leaf policy-type { + leaf near-rt-ric-url { type string; } } - output { - leaf code { + + output { + leaf-list policy-id-list { type string; } } @@ -113,152 +67,50 @@ module A1-ADAPTER-API { //Get a policy type rpc getPolicyType { input { - leaf near-rt-ric-id { + leaf near-rt-ric-url { type string; } leaf policy-type-id { - type uint32; - } - } - output { - leaf description { - type string; - } - leaf name { - type string; - } - leaf policy-type { - type string; - } - leaf code { type string; } } - } - - //Delete a policy type - rpc deletePolicyType { - input { - leaf near-rt-ric-id { - type string; - } - leaf policy-type-id { - type uint32; - } - } output { - leaf code { - type string; - } - } - } - - //Get an array of string policy instance ids - //Each item in the returned array will be regarded as one policy-instance-id. - rpc getPolicyInstances { - input { - leaf near-rt-ric-id { - type string; - } - leaf policy-type-id { - type uint32; - } - } - - output { - leaf-list policy-instance-id-list { - type string; - } - leaf code { + leaf policy-type { type string; } } } - //Create a policy instance - rpc createPolicyInstance { + //Create a policy + rpc putPolicy { input { - leaf near-rt-ric-id { + leaf near-rt-ric-url { type string; } - leaf policy-type-id { - type uint32; - } - leaf policy-instance-id { - type string; - } - leaf policy-instance { - type string; - } - } - output { - leaf code { - type string; - } - } - } - - ///Get a policy instance - rpc getPolicyInstance { - input { - leaf near-rt-ric-id { + leaf policy-id { type string; } leaf policy-type-id { - type uint32; - } - leaf policy-instance-id { - type string; - } - } - output { - leaf policy-instance { type string; } - leaf code { - type string; - } - } - } - - //Delete a policy instance - rpc deletePolicyInstance { - input { - leaf near-rt-ric-id { - type string; - } - leaf policy-type-id { - type uint32; - } - leaf policy-instance-id { + leaf policy { type string; } } output { - leaf code { + leaf returned-policy { type string; } } } - //Get the status for a policy instance - rpc getStatus { + //Delete a policy + rpc deletePolicy { input { - leaf near-rt-ric-id { - type string; - } - leaf policy-type-id { - type uint32; - } - leaf policy-instance-id { - type string; - } - } - output { - leaf status { + leaf near-rt-ric-url { type string; } - leaf code { + leaf policy-id { type string; } } diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/exceptions/NearRtRicNotFoundException.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/exceptions/NearRtRicNotFoundException.java deleted file mode 100644 index 1754bfa4..00000000 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/exceptions/NearRtRicNotFoundException.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.onap.sdnc.northbound.exceptions; - -public class NearRtRicNotFoundException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -4503072266424371087L; - - public NearRtRicNotFoundException(String message) { - super(message); - } - -} diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/provider/NonrtRicApiProvider.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/provider/NonrtRicApiProvider.java index b5e3deed..f67508f9 100644 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/provider/NonrtRicApiProvider.java +++ b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/provider/NonrtRicApiProvider.java @@ -23,68 +23,44 @@ package org.onap.sdnc.northbound.provider; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.gson.Gson; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.json.JSONObject; -import org.onap.sdnc.northbound.exceptions.NearRtRicNotFoundException; -import org.onap.sdnc.northbound.restadpter.NearRicUrlProvider; -import org.onap.sdnc.northbound.restadpter.RestAdapter; -import org.onap.sdnc.northbound.restadpter.RestAdapterImpl; +import org.onap.sdnc.northbound.restadapter.NearRicUrlProvider; +import org.onap.sdnc.northbound.restadapter.RestAdapter; +import org.onap.sdnc.northbound.restadapter.RestAdapterImpl; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.A1ADAPTERAPIService; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyInstanceInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyInstanceOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyInstanceOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyTypeInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyTypeOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyTypeOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyInstanceInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyInstanceOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyInstanceOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyTypeInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyTypeOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyTypeOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetHealthCheckInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetHealthCheckOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetHealthCheckOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetNearRTRICsInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetNearRTRICsOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetNearRTRICsOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstanceInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstanceOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstanceOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstancesInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstancesOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstancesOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypeInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypeOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypeOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypesInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypesOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypesOutputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetStatusInput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetStatusOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetStatusOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.A1ADAPTERAPIService; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.DeletePolicyInput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.DeletePolicyOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.DeletePolicyOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyIdentitiesInput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyIdentitiesOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyIdentitiesOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeIdentitiesInput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeIdentitiesOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeIdentitiesOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeInput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.PutPolicyInput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.PutPolicyOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.PutPolicyOutputBuilder; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestClientResponseException; /** * Defines a base implementation for your provider. This class overrides the generated interface @@ -200,189 +176,35 @@ public class NonrtRicApiProvider implements AutoCloseable, A1ADAPTERAPIService { } @Override - public ListenableFuture> createPolicyInstance( - CreatePolicyInstanceInput input) { - log.info("Start of createPolicyInstance"); - CreatePolicyInstanceOutputBuilder responseBuilder = new CreatePolicyInstanceOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyInstanceId(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId()), String.valueOf(input.getPolicyInstanceId())); - log.info("PUT Request input.getPolicyInstance() : {} ", input.getPolicyInstance()); - ResponseEntity response = restAdapter.put(uri, input.getPolicyInstance()); - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); - } - log.info("End of createPolicyInstance"); - RpcResult rpcResult = RpcResultBuilder - .status(true).withResult(responseBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - @Override - public ListenableFuture> createPolicyType( - CreatePolicyTypeInput input) { - log.info("Start of createPolicyType"); - CreatePolicyTypeOutputBuilder responseBuilder = new CreatePolicyTypeOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyTypeId(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId())); - log.info("PUT Request input.getPolicyType() : {} ", input.getPolicyType()); - ResponseEntity response = restAdapter.put(uri, input.getPolicyType()); - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); - } - log.info("End of createPolicyType"); - RpcResult rpcResult = RpcResultBuilder - .status(true).withResult(responseBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - @Override - public ListenableFuture> deletePolicyInstance( - DeletePolicyInstanceInput input) { - log.info("Start of deletePolicyInstance"); - DeletePolicyInstanceOutputBuilder responseBuilder = new DeletePolicyInstanceOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyInstanceId(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId()), String.valueOf(input.getPolicyInstanceId())); - ResponseEntity response = restAdapter.delete(uri); - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); - } - log.info("End of deletePolicyInstance"); - RpcResult rpcResult = RpcResultBuilder - .status(true).withResult(responseBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - @Override - public ListenableFuture> deletePolicyType( - DeletePolicyTypeInput input) { - log.info("Start of deletePolicyType"); - DeletePolicyTypeOutputBuilder responseBuilder = new DeletePolicyTypeOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyTypeId(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId())); - ResponseEntity response = restAdapter.delete(uri); - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); - } - log.info("End of deletePolicyType"); - RpcResult rpcResult = RpcResultBuilder - .status(true).withResult(responseBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - @Override - public ListenableFuture> getHealthCheck( - GetHealthCheckInput input) { - log.info("Start of getHealthCheck"); - GetHealthCheckOutputBuilder responseBuilder = new GetHealthCheckOutputBuilder(); - try { - String uri = nearRicUrlProvider.getHealthCheck(String.valueOf(input.getNearRtRicId())); - ResponseEntity response = restAdapter.get(uri, Object.class); - responseBuilder.setHealthStatus(false); - if (response.getStatusCode().equals(HttpStatus.OK)) { - responseBuilder.setHealthStatus(true); - } - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); + public ListenableFuture> getPolicyTypeIdentities( + GetPolicyTypeIdentitiesInput input) { + log.info("Start of getPolicyTypeIdentities"); + GetPolicyTypeIdentitiesOutputBuilder responseBuilder = new GetPolicyTypeIdentitiesOutputBuilder(); + String uri = nearRicUrlProvider.policyTypesUrl(String.valueOf(input.getNearRtRicUrl())); + ResponseEntity> response = restAdapter.get(uri, List.class); + if (response.hasBody()) { + log.info("Response getPolicyTypeIdentities : {} ", response.getBody()); + responseBuilder.setPolicyTypeIdList(response.getBody()); } - log.info("End of getHealthCheck"); - RpcResult rpcResult = RpcResultBuilder.status(true) + log.info("End of getPolicyTypeIdentities"); + RpcResult rpcResult = RpcResultBuilder.status(true) .withResult(responseBuilder.build()).build(); return Futures.immediateFuture(rpcResult); } @Override - public ListenableFuture> getNearRTRICs(GetNearRTRICsInput input) { - log.info("Start of getNearRTRICs"); - GetNearRTRICsOutputBuilder responseBuilder = new GetNearRTRICsOutputBuilder(); - responseBuilder.setNearRtRicIdList(nearRicUrlProvider.getNearRTRicIdsList()); - responseBuilder.setCode(HttpStatus.OK.toString()); - log.info("End of getNearRTRICs"); - RpcResult rpcResult = RpcResultBuilder.status(true) - .withResult(responseBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - @Override - public ListenableFuture> getPolicyInstance( - GetPolicyInstanceInput input) { - log.info("Start of getPolicyInstance"); - log.info("Policy Type Id : {}, Policy Instance Id : {}", input.getPolicyTypeId(), input.getPolicyInstanceId()); - GetPolicyInstanceOutputBuilder responseBuilder = new GetPolicyInstanceOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyInstanceId(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId()), String.valueOf(input.getPolicyInstanceId())); - ResponseEntity response = restAdapter.get(uri, String.class); - if (response.hasBody()) { - log.info("Response getPolicyInstance : {} ", response.getBody()); - responseBuilder.setPolicyInstance(response.getBody()); - } - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); - } - log.info("End of getPolicyInstance"); - RpcResult rpcResult = RpcResultBuilder - .status(true).withResult(responseBuilder.build()).build(); - return Futures.immediateFuture(rpcResult); - } - - @Override - public ListenableFuture> getPolicyInstances( - GetPolicyInstancesInput input) { - log.info("Start of getPolicyInstances"); - GetPolicyInstancesOutputBuilder responseBuilder = new GetPolicyInstancesOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyInstances(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId())); - ResponseEntity> response = restAdapter.get(uri, List.class); - if (response.hasBody()) { - log.info("Response getPolicyInstances : {} ", response.getBody()); - responseBuilder.setPolicyInstanceIdList(response.getBody()); - } - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); + public ListenableFuture> getPolicyIdentities(GetPolicyIdentitiesInput input) { + log.info("Start of getPolicyIdentities"); + GetPolicyIdentitiesOutputBuilder responseBuilder = new GetPolicyIdentitiesOutputBuilder(); + String uri = nearRicUrlProvider.policiesUrl(String.valueOf(input.getNearRtRicUrl())); + ResponseEntity> response = restAdapter.get(uri, List.class); + if (response.hasBody()) { + log.info("Response getPolicyIdentities : {} ", response.getBody()); + responseBuilder.setPolicyIdList(response.getBody()); } - log.info("End of getPolicyInstances"); - RpcResult rpcResult = RpcResultBuilder - .status(true).withResult(responseBuilder.build()).build(); + log.info("End of getPolicyIdentities"); + RpcResult rpcResult = RpcResultBuilder + .status(true).withResult(responseBuilder.build()).build(); return Futures.immediateFuture(rpcResult); } @@ -391,24 +213,12 @@ public class NonrtRicApiProvider implements AutoCloseable, A1ADAPTERAPIService { log.info("Start of getPolicyType"); log.info("Policy Type Id : {} ", input.getPolicyTypeId()); GetPolicyTypeOutputBuilder responseBuilder = new GetPolicyTypeOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyTypeId(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId())); - ResponseEntity response = restAdapter.get(uri, String.class); - if (response.hasBody()) { - log.info("Response getPolicyType : {} ", response.getBody()); - JSONObject policyTypeObj = new JSONObject(response.getBody()); - responseBuilder.setDescription(policyTypeObj.getString("description")); - responseBuilder.setName(policyTypeObj.getString("name")); - responseBuilder.setPolicyType(policyTypeObj.getJSONObject("create_schema").toString()); - } - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); + String uri = nearRicUrlProvider.getPolicyTypeUrl(String.valueOf(input.getNearRtRicUrl()), + String.valueOf(input.getPolicyTypeId())); + ResponseEntity response = restAdapter.get(uri, String.class); + if (response.hasBody()) { + log.info("Response getPolicyType : {} ", response.getBody()); + responseBuilder.setPolicyType(response.getBody()); } log.info("End of getPolicyType"); RpcResult rpcResult = RpcResultBuilder.status(true) @@ -417,61 +227,33 @@ public class NonrtRicApiProvider implements AutoCloseable, A1ADAPTERAPIService { } @Override - public ListenableFuture> getPolicyTypes( - GetPolicyTypesInput input) { - log.info("Start of getPolicyTypes"); - GetPolicyTypesOutputBuilder responseBuilder = new GetPolicyTypesOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyTypes(String.valueOf(input.getNearRtRicId())); - ResponseEntity> response = restAdapter.get(uri, List.class); - if (response.hasBody()) { - log.info("Response getPolicyTypes : {} ", response.getBody()); - List policyTypesListInteger = response.getBody(); - List policyTypesListLong = new ArrayList<>(); - for(Integer i : policyTypesListInteger){ - policyTypesListLong.add(i.longValue()); - } - responseBuilder.setPolicyTypeIdList(policyTypesListLong); - } - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); + public ListenableFuture> putPolicy(PutPolicyInput input) { + log.info("Start of putPolicy"); + PutPolicyOutputBuilder responseBuilder = new PutPolicyOutputBuilder(); + String uri = nearRicUrlProvider.putPolicyUrl(String.valueOf(input.getNearRtRicUrl()), + String.valueOf(input.getPolicyId()), String.valueOf(input.getPolicyTypeId())); + log.info("PUT Request input.getPolicy() : {} ", input.getPolicy()); + ResponseEntity response = restAdapter.put(uri, input.getPolicy(), String.class); + if (response.hasBody()) { + log.info("Response putPolicy : {} ", response.getBody()); + responseBuilder.setReturnedPolicy(response.getBody()); } - log.info("End of getPolicyTypes"); - RpcResult rpcResult = RpcResultBuilder.status(true) - .withResult(responseBuilder.build()).build(); + log.info("End of putPolicy"); + RpcResult rpcResult = RpcResultBuilder + .status(true).withResult(responseBuilder.build()).build(); return Futures.immediateFuture(rpcResult); } @Override - public ListenableFuture> getStatus(GetStatusInput input) { - log.info("Start of getStatus"); - GetStatusOutputBuilder responseBuilder = new GetStatusOutputBuilder(); - try { - String uri = nearRicUrlProvider.getPolicyInstanceIdStatus(String.valueOf(input.getNearRtRicId()), - String.valueOf(input.getPolicyTypeId()), String.valueOf(input.getPolicyInstanceId())); - ResponseEntity> response = restAdapter.get(uri, List.class); - if (response.hasBody()) { - log.info("Response getStatus : {} ", response.getBody()); - // only return the status of first handler for compliance with current yang model, ignore handler_id - JSONObject statusObj = new JSONObject(new Gson().toJson(response.getBody().get(0))); - responseBuilder.setStatus(statusObj.getString("status")); - } - responseBuilder.setCode(response.getStatusCode().toString()); - } catch (NearRtRicNotFoundException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(HttpStatus.INTERNAL_SERVER_ERROR.toString()); - } catch (RestClientResponseException ex) { - log.error("Caught exception: {}", ex); - responseBuilder.setCode(String.valueOf(ex.getRawStatusCode())); - } - log.info("End of getStatus"); - RpcResult rpcResult = - RpcResultBuilder.status(true).withResult(responseBuilder.build()).build(); + public ListenableFuture> deletePolicy(DeletePolicyInput input) { + log.info("Start of deletePolicy"); + DeletePolicyOutputBuilder responseBuilder = new DeletePolicyOutputBuilder(); + String uri = nearRicUrlProvider.deletePolicyUrl(String.valueOf(input.getNearRtRicUrl()), + String.valueOf(input.getPolicyId())); + ResponseEntity response = restAdapter.delete(uri); + log.info("End of deletePolicy"); + RpcResult rpcResult = RpcResultBuilder + .status(true).withResult(responseBuilder.build()).build(); return Futures.immediateFuture(rpcResult); } } diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/NearRicUrlProvider.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/NearRicUrlProvider.java new file mode 100644 index 00000000..10b16e37 --- /dev/null +++ b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/NearRicUrlProvider.java @@ -0,0 +1,106 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 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========================================================= + */ + +package org.onap.sdnc.northbound.restadapter; + +import org.springframework.web.util.UriComponentsBuilder; + +/** + * This class provides Near-RIC api to invoke the A1 interface + * + * @author lathishbabu.ganesan@est.tech + * + */ + +public class NearRicUrlProvider { + + public NearRicUrlProvider() { + } + + /** + * Retrieve the base url of the Near-RIC + * + * @param nearRtRicUrl the near-rt-ric url + * @return the base url + */ + public String getBaseUrl(final String nearRtRicUrl) { + String baseUrl = nearRtRicUrl + "/A1-P/v1"; + return UriComponentsBuilder.fromUriString(baseUrl).build().toString(); + } + + /** + * Retrieve the policytypes url + * + * @param nearRtRicUrl the near-rt-ric url + * @return the policytypes url + */ + public String policyTypesUrl(final String nearRtRicUrl) { + return UriComponentsBuilder.fromUriString(getBaseUrl(nearRtRicUrl)).pathSegment("policytypes") + .build().toString(); + } + + /** + * Retrieve the policies url + * + * @param nearRtRicUrl the near-rt-ric url + * @return the policies url + */ + public String policiesUrl(final String nearRtRicUrl) { + return UriComponentsBuilder.fromUriString(getBaseUrl(nearRtRicUrl)).pathSegment("policies") + .build().toString(); + } + + /** + * Retrieve the url of policy type + * + * @param nearRtRicUrl the near-rt-ric url + * @param policyTypeId Policy Type Id + * @return the policy type url + */ + public String getPolicyTypeUrl(final String nearRtRicUrl, final String policyTypeId) { + return UriComponentsBuilder.fromUriString(policyTypesUrl(nearRtRicUrl)).pathSegment(policyTypeId) + .build().toString(); + } + + /** + * Retrieve the url of putPolicy + * + * @param nearRtRicUrl the near-rt-ric url + * @param policyId Policy Id + * @param policyTypeId Policy Type Id + * @return the putPolicy url + */ + public String putPolicyUrl(final String nearRtRicUrl, final String policyId, final String policyTypeId) { + return UriComponentsBuilder.fromUriString(policiesUrl(nearRtRicUrl)) + .pathSegment(policyId + "?policyTypeId=" + policyTypeId).build().toString(); + } + + /** + * Retrieve the url of deletePolicy + * + * @param nearRtRicUrl the near-rt-ric url + * @param policyId Policy Id + * @return the deletePolicy url + */ + public String deletePolicyUrl(final String nearRtRicUrl, final String policyId) { + return UriComponentsBuilder.fromUriString(policiesUrl(nearRtRicUrl)).pathSegment(policyId) + .build().toString(); + } +} diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapter.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapter.java similarity index 90% rename from sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapter.java rename to sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapter.java index 8f13f22d..a99edceb 100644 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapter.java +++ b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapter.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.sdnc.northbound.restadpter; +package org.onap.sdnc.northbound.restadapter; import org.springframework.http.ResponseEntity; @@ -46,9 +46,10 @@ public interface RestAdapter { * * @param url the URL * @param request the String to be PUT (may be {@code null}) + * @param clazz responseType the type of the return value * @return the response code */ - ResponseEntity put(final String url, final String body); + ResponseEntity put(final String url, final String body, final Class clazz); /** * Delete resource for the given object to the URI. diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapterImpl.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapterImpl.java similarity index 93% rename from sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapterImpl.java rename to sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapterImpl.java index c2c9522a..afa1d9f3 100644 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapterImpl.java +++ b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapterImpl.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.sdnc.northbound.restadpter; +package org.onap.sdnc.northbound.restadapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,11 +57,11 @@ public class RestAdapterImpl implements RestAdapter { } @Override - public ResponseEntity put(String uri, String body) { + public ResponseEntity put(String uri, String body, Class clazz) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity(body, headers); - return invokeHttpRequest(uri, HttpMethod.PUT, null, entity); + return invokeHttpRequest(uri, HttpMethod.PUT, clazz, entity); } @Override diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/NearRicUrlProvider.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/NearRicUrlProvider.java deleted file mode 100644 index 8c2d6923..00000000 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/NearRicUrlProvider.java +++ /dev/null @@ -1,164 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2019 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========================================================= - */ - -package org.onap.sdnc.northbound.restadpter; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Properties; -import org.onap.sdnc.northbound.exceptions.NearRtRicNotFoundException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * This class provides Near-RIC api to invoke the A1 interface - * - * @author lathishbabu.ganesan@est.tech - * - */ - -public class NearRicUrlProvider { - - // nearRicMap provides mapping from nearRtRicId to domainname:port of nearRTRics - private HashMap nearRicMap = new HashMap<>(); - private static final String NEAR_RIC_LIST_FILE = "NearRtRicList.properties"; - private final Logger log = LoggerFactory.getLogger(NearRicUrlProvider.class); - - public NearRicUrlProvider() { - try { - readNearRtRicConfigFile(); - } catch (IOException ex) { - log.error("Exception while reading nearRtRicConfigFile: {}", ex); - } - } - - private void readNearRtRicConfigFile() throws IOException { - InputStream inputStream = NearRicUrlProvider.class.getClassLoader().getResourceAsStream(NEAR_RIC_LIST_FILE); - if (inputStream == null) { - log.error("The file {} not found in classpath", NEAR_RIC_LIST_FILE); - } else { - Properties properties = new Properties(); - properties.load(inputStream); - Enumeration keys = properties.propertyNames(); - while (keys.hasMoreElements()) { - String key = (String) keys.nextElement(); - nearRicMap.put(key, properties.getProperty(key)); - } - inputStream.close(); - } - } - - /** - * Retrieve the list of Near-RICs - * - * @return the list of Near-RICs - */ - public List getNearRTRicIdsList () { - return new ArrayList<>(nearRicMap.keySet()); - } - - /** - * Retrieve the base url of the Near-RIC - * - * @return the base url - */ - public String getBaseUrl(final String nearRtRicId) { - if (!nearRicMap.containsKey(nearRtRicId)) { - throw new NearRtRicNotFoundException("NearRtRic with this ID is not known by the A1 controller"); - } - String baseUrl = "http://" + nearRicMap.get(nearRtRicId) + "/a1-p/"; - return UriComponentsBuilder.fromUriString(baseUrl).build().toString(); - } - - /** - * Retrieve the url of A1 healthcheck - * - * @return the health check url - */ - public String getHealthCheck(final String nearRtRicId) { - return UriComponentsBuilder.fromUriString(getBaseUrl(nearRtRicId)).pathSegment("healthcheck").build() - .toString(); - } - - /** - * Retrieve the policy type url - * - * @return the base url with the policytypes - */ - public String getPolicyTypes(final String nearRtRicId) { - return UriComponentsBuilder.fromUriString(getBaseUrl(nearRtRicId)).pathSegment("policytypes/").build() - .toString(); - } - - /** - * Retrieve the url of policy type id - * - * @param policyTypeId Policy Type Id - * @return the policy type id url - */ - public String getPolicyTypeId(final String nearRtRicId, - final String policyTypeId) { - return UriComponentsBuilder.fromUriString(getBaseUrl(nearRtRicId)).pathSegment("policytypes") - .pathSegment(policyTypeId).build().toString(); - } - - /** - * Retrieve the url of the policy instances - * - * @param policyTypeId Policy Type Id - * @return the policy instances for the given policy type - */ - public String getPolicyInstances(final String nearRtRicId, - final String policyTypeId) { - return UriComponentsBuilder.fromUriString(getPolicyTypeId(nearRtRicId, policyTypeId)).pathSegment("policies") - .build().toString(); - } - - /** - * Retrieve the url of the policy instance id - * - * @param policyTypeId Policy Type Id - * @param policyInstanceId Policy Instance Id - * @return the policy instance id for the given policy type - */ - public String getPolicyInstanceId(final String nearRtRicId, final String policyTypeId, - final String policyInstanceId) { - return UriComponentsBuilder.fromUriString(getPolicyTypeId(nearRtRicId, policyTypeId)).pathSegment("policies") - .pathSegment(policyInstanceId).build().toString(); - } - - /** - * Retrieve the url of the policy instance id status - * - * @param policyTypeId Policy Type Id - * @param policyInstanceId Policy Instance Id - * @return the policy instance id status for the given policy type - */ - public String getPolicyInstanceIdStatus(final String nearRtRicId, final String policyTypeId, - final String policyInstanceId) { - return UriComponentsBuilder.fromUriString(getPolicyInstanceId(nearRtRicId, policyTypeId, policyInstanceId)) - .pathSegment("status").build().toString(); - } -} diff --git a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/test/java/org/onap/sdnc/northbound/NonrtRicApiProviderTest.java b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/test/java/org/onap/sdnc/northbound/NonrtRicApiProviderTest.java index e9b86be4..3dc1efd1 100644 --- a/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/test/java/org/onap/sdnc/northbound/NonrtRicApiProviderTest.java +++ b/sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/test/java/org/onap/sdnc/northbound/NonrtRicApiProviderTest.java @@ -20,11 +20,9 @@ package org.onap.sdnc.northbound; -import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; import com.google.common.util.concurrent.ListenableFuture; -import com.google.gson.Gson; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; @@ -36,34 +34,20 @@ import org.mockito.Mock; import org.mockito.internal.util.reflection.Whitebox; import org.mockito.runners.MockitoJUnitRunner; import org.onap.sdnc.northbound.provider.NonrtRicApiProvider; -import org.onap.sdnc.northbound.restadpter.NearRicUrlProvider; -import org.onap.sdnc.northbound.restadpter.RestAdapter; +import org.onap.sdnc.northbound.restadapter.NearRicUrlProvider; +import org.onap.sdnc.northbound.restadapter.RestAdapter; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; import org.opendaylight.controller.md.sal.binding.test.AbstractConcurrentDataBrokerTest; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyInstanceInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyInstanceOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyTypeInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.CreatePolicyTypeOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyInstanceInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyInstanceOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyTypeInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.DeletePolicyTypeOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetHealthCheckInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetHealthCheckOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetNearRTRICsInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetNearRTRICsOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstanceInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstanceOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstancesInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyInstancesOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypeInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypeOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypesInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetPolicyTypesOutput; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetStatusInputBuilder; -import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev191002.GetStatusOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyIdentitiesInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyIdentitiesOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeIdentitiesInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeIdentitiesOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.GetPolicyTypeOutput; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.PutPolicyInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.sdnc.northbound.a1.adapter.rev200122.PutPolicyOutput; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,9 +74,9 @@ public class NonrtRicApiProviderTest extends AbstractConcurrentDataBrokerTest { @Mock private RestAdapter restAdapter; private NearRicUrlProvider nearRicUrlProvider; - private static String nearRtRicId = "NearRtRic1"; - private static Long policyTypeId = 11L; - private static String policyTypeInstanceId = "12"; + private static String nearRtRicUrl = "http://ric1:8085"; + private static String policyTypeId = "STD_QoSNudging_0.1.0"; + private static String policyId = "3d2157af-6a8f-4a7c-810f-38c2f824bf12"; @Before @@ -104,178 +88,66 @@ public class NonrtRicApiProviderTest extends AbstractConcurrentDataBrokerTest { } @Test - public void testCreatePolicyInstance() throws InterruptedException, ExecutionException { - CreatePolicyInstanceInputBuilder inputBuilder = new CreatePolicyInstanceInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - inputBuilder.setPolicyTypeId(policyTypeId); - inputBuilder.setPolicyInstanceId(policyTypeInstanceId); + public void testGetPolicyTypeIdentities() throws InterruptedException, ExecutionException { + GetPolicyTypeIdentitiesInputBuilder inputBuilder = new GetPolicyTypeIdentitiesInputBuilder(); + inputBuilder.setNearRtRicUrl(nearRtRicUrl); Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyInstanceId(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId()), inputBuilder.getPolicyInstanceId()); - ResponseEntity createPolicyInstanceResponse = new ResponseEntity<>(HttpStatus.CREATED); - when(restAdapter.put(eq(uri), anyObject())).thenReturn(createPolicyInstanceResponse); - ListenableFuture> result = - nonrtRicApiProvider.createPolicyInstance(inputBuilder.build()); - Assert.assertEquals(String.valueOf(HttpStatus.CREATED.value()), result.get().getResult().getCode()); + String uri = nearRicUrlProvider.policyTypesUrl(inputBuilder.build().getNearRtRicUrl()); + List policyTypeIdentities = new ArrayList<>(); + policyTypeIdentities.add(policyTypeId); + ResponseEntity getPolicyTypeIdentitiesResponse = new ResponseEntity<>(policyTypeIdentities, HttpStatus.OK); + when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyTypeIdentitiesResponse); + ListenableFuture> result = + nonrtRicApiProvider.getPolicyTypeIdentities(inputBuilder.build()); + Assert.assertEquals(policyTypeIdentities, result.get().getResult().getPolicyTypeIdList()); } @Test - public void testDeletePolicyInstance() throws InterruptedException, ExecutionException { - DeletePolicyInstanceInputBuilder inputBuilder = new DeletePolicyInstanceInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - inputBuilder.setPolicyTypeId(policyTypeId); - inputBuilder.setPolicyInstanceId(policyTypeInstanceId); + public void testGetPolicyIdentities() throws InterruptedException, ExecutionException { + GetPolicyIdentitiesInputBuilder inputBuilder = new GetPolicyIdentitiesInputBuilder(); + inputBuilder.setNearRtRicUrl(nearRtRicUrl); Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyInstanceId(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId()), inputBuilder.getPolicyInstanceId()); - ResponseEntity deletePolicyInstanceResponse = new ResponseEntity<>(HttpStatus.NO_CONTENT); - when(restAdapter.delete(eq(uri))).thenReturn(deletePolicyInstanceResponse); - ListenableFuture> result = - nonrtRicApiProvider.deletePolicyInstance(inputBuilder.build()); - Assert.assertEquals(String.valueOf(HttpStatus.NO_CONTENT.value()), result.get().getResult().getCode()); - } - - @Test - public void testGetNearRTRICs() throws InterruptedException, ExecutionException { - GetNearRTRICsInputBuilder inputBuilder = new GetNearRTRICsInputBuilder(); - ListenableFuture> result = - nonrtRicApiProvider.getNearRTRICs(inputBuilder.build()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); - } - - @Test - public void testCreatePolicyType() throws InterruptedException, ExecutionException { - CreatePolicyTypeInputBuilder inputBuilder = new CreatePolicyTypeInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - inputBuilder.setPolicyTypeId(policyTypeId); - Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyTypeId(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId())); - ResponseEntity createPolicyTypeResponse = new ResponseEntity<>(HttpStatus.CREATED); - when(restAdapter.put(eq(uri), anyObject())).thenReturn(createPolicyTypeResponse); - ListenableFuture> result = - nonrtRicApiProvider.createPolicyType(inputBuilder.build()); - Assert.assertEquals(String.valueOf(HttpStatus.CREATED.value()), result.get().getResult().getCode()); - } - - @Test - public void testDeletePolicyType() throws InterruptedException, ExecutionException { - DeletePolicyTypeInputBuilder inputBuilder = new DeletePolicyTypeInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - inputBuilder.setPolicyTypeId(policyTypeId); - Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyTypeId(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId())); - ResponseEntity deletePolicyTypeResponse = new ResponseEntity<>(HttpStatus.NO_CONTENT); - when(restAdapter.delete(eq(uri))).thenReturn(deletePolicyTypeResponse); - ListenableFuture> result = - nonrtRicApiProvider.deletePolicyType(inputBuilder.build()); - Assert.assertEquals(String.valueOf(HttpStatus.NO_CONTENT.value()), result.get().getResult().getCode()); + String uri = nearRicUrlProvider.policiesUrl(inputBuilder.build().getNearRtRicUrl()); + List policyIdentities = new ArrayList<>(); + policyIdentities.add(policyId); + ResponseEntity getPolicyIdentitiesResponse = new ResponseEntity<>(policyIdentities, HttpStatus.OK); + when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyIdentitiesResponse); + ListenableFuture> result = + nonrtRicApiProvider.getPolicyIdentities(inputBuilder.build()); + Assert.assertEquals(policyIdentities, result.get().getResult().getPolicyIdList()); } @Test public void testGetPolicyType() throws InterruptedException, ExecutionException { GetPolicyTypeInputBuilder inputBuilder = new GetPolicyTypeInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); + inputBuilder.setNearRtRicUrl(nearRtRicUrl); inputBuilder.setPolicyTypeId(policyTypeId); Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyTypeId(inputBuilder.build().getNearRtRicId(), + String uri = nearRicUrlProvider.getPolicyTypeUrl(inputBuilder.build().getNearRtRicUrl(), String.valueOf(inputBuilder.build().getPolicyTypeId())); - String policyType = - "{\"name\":\"Policy type 1\",\"description\":\"PT 1\",\"policy_type_id\":1,\"create_schema\":{}}"; - ResponseEntity getPolicyTypeResponse = new ResponseEntity<>(policyType, HttpStatus.OK); + String testPolicyType = "{}"; + ResponseEntity getPolicyTypeResponse = new ResponseEntity<>(testPolicyType, HttpStatus.OK); when(restAdapter.get(eq(uri), eq(String.class))).thenReturn(getPolicyTypeResponse); ListenableFuture> result = nonrtRicApiProvider.getPolicyType(inputBuilder.build()); - Assert.assertEquals("Policy type 1", result.get().getResult().getName()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); - } - - @Test - public void testGetPolicyTypes() throws InterruptedException, ExecutionException { - GetPolicyTypesInputBuilder inputBuilder = new GetPolicyTypesInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyTypes(inputBuilder.build().getNearRtRicId()); - List policyTypesInteger = new ArrayList<>(); - policyTypesInteger.add(20001); - List policyTypesLong = new ArrayList<>(); - policyTypesLong.add(20001L); - ResponseEntity getPolicyTypesResponse = new ResponseEntity<>(policyTypesInteger, HttpStatus.OK); - when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyTypesResponse); - ListenableFuture> result = - nonrtRicApiProvider.getPolicyTypes(inputBuilder.build()); - Assert.assertEquals(policyTypesLong, result.get().getResult().getPolicyTypeIdList()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); + Assert.assertEquals(testPolicyType, result.get().getResult().getPolicyType()); } @Test - public void testGetPolicyInstance() throws InterruptedException, ExecutionException { - GetPolicyInstanceInputBuilder inputBuilder = new GetPolicyInstanceInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); + public void testPutPolicy() throws InterruptedException, ExecutionException { + PutPolicyInputBuilder inputBuilder = new PutPolicyInputBuilder(); + String testPolicy = "{}"; + inputBuilder.setNearRtRicUrl(nearRtRicUrl); + inputBuilder.setPolicyId(policyId); inputBuilder.setPolicyTypeId(policyTypeId); - inputBuilder.setPolicyInstanceId(policyTypeInstanceId); - Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyInstanceId(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId()), inputBuilder.getPolicyInstanceId()); - String policyInstance = - "{\"scope\":{\"ue_id\":\"2\"},\"statement\":{\"priority_level\":\"1\"},\"policy_id\":\"pi12\"}"; - ResponseEntity getPolicyInstanceResponse = new ResponseEntity<>(policyInstance, HttpStatus.OK); - when(restAdapter.get(eq(uri), eq(String.class))).thenReturn(getPolicyInstanceResponse); - ListenableFuture> result = - nonrtRicApiProvider.getPolicyInstance(inputBuilder.build()); - Assert.assertEquals(policyInstance, result.get().getResult().getPolicyInstance()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); - } - - @Test - public void testGetPolicyInstances() throws InterruptedException, ExecutionException { - GetPolicyInstancesInputBuilder inputBuilder = new GetPolicyInstancesInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - inputBuilder.setPolicyTypeId(policyTypeId); - Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyInstances(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId())); - List policyInstances = new ArrayList<>(); - policyInstances.add("3d2157af-6a8f-4a7c-810f-38c2f824bf12"); - ResponseEntity getPolicyInstancesResponse = new ResponseEntity<>(policyInstances, HttpStatus.OK); - when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyInstancesResponse); - ListenableFuture> result = - nonrtRicApiProvider.getPolicyInstances(inputBuilder.build()); - Assert.assertEquals(policyInstances, result.get().getResult().getPolicyInstanceIdList()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); - } - - @Test - public void testGetStatus() throws InterruptedException, ExecutionException { - GetStatusInputBuilder inputBuilder = new GetStatusInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); - inputBuilder.setPolicyTypeId(policyTypeId); - inputBuilder.setPolicyInstanceId(policyTypeInstanceId); - Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getPolicyInstanceIdStatus(inputBuilder.build().getNearRtRicId(), - String.valueOf(inputBuilder.build().getPolicyTypeId()), inputBuilder.getPolicyInstanceId()); - String policyInstanceStatus = "[{\"handler_id\":\"NearRTRIC-1\",\"status\":\"enforced\"}]"; - ResponseEntity getStatusResponse = - new ResponseEntity<>(new Gson().fromJson(policyInstanceStatus, List.class), HttpStatus.OK); - when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getStatusResponse); - ListenableFuture> result = - nonrtRicApiProvider.getStatus(inputBuilder.build()); - Assert.assertEquals("enforced", result.get().getResult().getStatus()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); - } - - @Test - public void testGetHealthCheck() throws InterruptedException, ExecutionException { - GetHealthCheckInputBuilder inputBuilder = new GetHealthCheckInputBuilder(); - inputBuilder.setNearRtRicId(nearRtRicId); + inputBuilder.setPolicy(testPolicy); Whitebox.setInternalState(nonrtRicApiProvider, "restAdapter", restAdapter); - String uri = nearRicUrlProvider.getHealthCheck(inputBuilder.build().getNearRtRicId()); - ResponseEntity getHealthCheckResponse = new ResponseEntity<>(HttpStatus.OK); - when(restAdapter.get(eq(uri), eq(Object.class))).thenReturn(getHealthCheckResponse); - ListenableFuture> result = - nonrtRicApiProvider.getHealthCheck(inputBuilder.build()); - Assert.assertEquals(true, result.get().getResult().isHealthStatus()); - Assert.assertEquals(String.valueOf(HttpStatus.OK.value()), result.get().getResult().getCode()); + String uri = nearRicUrlProvider.putPolicyUrl(inputBuilder.build().getNearRtRicUrl(), + inputBuilder.getPolicyId(), inputBuilder.getPolicyTypeId()); + ResponseEntity putPolicyResponse = new ResponseEntity<>(testPolicy, HttpStatus.CREATED); + when(restAdapter.put(eq(uri), eq(testPolicy), eq(String.class))).thenReturn(putPolicyResponse); + ListenableFuture> result = + nonrtRicApiProvider.putPolicy(inputBuilder.build()); + Assert.assertEquals(testPolicy, result.get().getResult().getReturnedPolicy()); } } diff --git a/sdnc-a1-controller/oam/installation/sdnc/pom.xml b/sdnc-a1-controller/oam/installation/sdnc/pom.xml index 2ede17ed..9e6b65a8 100644 --- a/sdnc-a1-controller/oam/installation/sdnc/pom.xml +++ b/sdnc-a1-controller/oam/installation/sdnc/pom.xml @@ -61,13 +61,6 @@ - - org.onap.sdnc.northbound - generic-resource-api-installer - ${sdnc.northbound.version} - repo - zip - org.onap.sdnc.northbound nonrt-ric-api-installer @@ -75,20 +68,6 @@ repo zip - - org.onap.sdnc.northbound - vnfapi-installer - ${sdnc.northbound.version} - repo - zip - - - org.onap.sdnc.northbound - vnftools-installer - ${sdnc.northbound.version} - repo - zip - org.onap.sdnc.northbound sdnc-northbound-features-installer @@ -96,7 +75,6 @@ repo zip - org.onap.ccsdk.features.sdnr.northbound oofpcipoc-installer @@ -104,7 +82,6 @@ repo zip - org.onap.ccsdk.features.sdnr.northbound sdnr-northbound-features-installer diff --git a/sdnc-a1-controller/pom.xml b/sdnc-a1-controller/pom.xml index b67bcaf7..5770b745 100644 --- a/sdnc-a1-controller/pom.xml +++ b/sdnc-a1-controller/pom.xml @@ -25,7 +25,7 @@ org.onap.sdnc sdnc - 1.7.3-SNAPSHOT + 1.7.4-SNAPSHOT pom sdnc