Uplift Amber for maintenance release 06/2406/1
authorelinuxhenrik <henrik.b.andersson@est.tech>
Mon, 3 Feb 2020 15:02:21 +0000 (16:02 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Mon, 3 Feb 2020 15:02:36 +0000 (16:02 +0100)
Change-Id: Idd92e8193c7fc88c0e00d14f63a8c4e0dcbd012a
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
189 files changed:
.gitignore
dashboard/.gitignore
dashboard/a1-controller-client/.gitignore [deleted file]
dashboard/a1-controller-client/README.md [deleted file]
dashboard/a1-controller-client/pom.xml [deleted file]
dashboard/a1-controller-client/src/main/resources/a1_controller_0.1.0.yaml [deleted file]
dashboard/a1-controller-client/src/test/java/org/oransc/ric/portal/dashboard/a1controller/client/test/A1ControllerClientTest.java [deleted file]
dashboard/pom.xml
dashboard/webapp-backend/eclipse-formatter.xml [new file with mode: 0644]
dashboard/webapp-backend/pom.xml
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/A1ControllerConfiguration.java [deleted file]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AdminConfiguration.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/PortalApiConfiguration.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SwaggerConfiguration.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/WebSecurityConfiguration.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1Controller.java [deleted file]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/Html5PathsController.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/PolicyController.java [new file with mode: 0644]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/k8sapi/SimpleKubernetesClient.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/ErrorTransport.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInfo.java [moved from dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstance.java with 56% similarity]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyInstances.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyType.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/PolicyTypes.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/SuccessTransport.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java [new file with mode: 0644]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java [new file with mode: 0644]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/IPortalSdkDecryptor.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorPkc.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java
dashboard/webapp-backend/src/main/resources/application.properties
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/A1ControllerMockConfiguration.java [deleted file]
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java [new file with mode: 0644]
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PortalApIMockConfiguration.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/WebSecurityMockConfiguration.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/AbstractControllerTest.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/DefaultContextTest.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/PortalRestCentralServiceTest.java
dashboard/webapp-backend/src/test/resources/anr-policy-instance.json [deleted file]
dashboard/webapp-backend/src/test/resources/anr-policy-schema.json [deleted file]
dashboard/webapp-frontend/package-lock.json
dashboard/webapp-frontend/package.json
dashboard/webapp-frontend/pom.xml
dashboard/webapp-frontend/src/app/footer/footer.component.scss
dashboard/webapp-frontend/src/app/interfaces/policy.types.ts
dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.html
dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.scss
dashboard/webapp-frontend/src/app/policy-control/policy-instance-dialog.component.ts
dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.html
dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.scss
dashboard/webapp-frontend/src/app/policy-control/policy-instance.component.ts
dashboard/webapp-frontend/src/app/policy-control/policy-instance.datasource.ts
dashboard/webapp-frontend/src/app/policy-control/policy-type.datasource.ts
dashboard/webapp-frontend/src/app/rd-routing.module.ts
dashboard/webapp-frontend/src/app/rd.component.ts
dashboard/webapp-frontend/src/app/rd.module.ts
dashboard/webapp-frontend/src/app/services/policy/policy.service.ts
docs/api-docs.rst
docs/developer-guide.rst
docs/index.rst
docs/release-notes.rst
near-rt-ric-simulator/.gitignore [changed mode: 0755->0644]
near-rt-ric-simulator/README.md
near-rt-ric-simulator/a1-med-api/.gitignore [deleted file]
near-rt-ric-simulator/a1-med-api/pom.xml [deleted file]
near-rt-ric-simulator/a1-med-api/src/main/resources/a1_mediator_0.11.0.yaml [deleted file]
near-rt-ric-simulator/auto-test/.gitignore [new file with mode: 0644]
near-rt-ric-simulator/auto-test/Dockerize_test.sh [new file with mode: 0755]
near-rt-ric-simulator/auto-test/FTC1.sh [new file with mode: 0755]
near-rt-ric-simulator/auto-test/README.md [new file with mode: 0644]
near-rt-ric-simulator/common/README.md [new file with mode: 0644]
near-rt-ric-simulator/common/test_env.sh [new file with mode: 0755]
near-rt-ric-simulator/common/testcase_common.sh [new file with mode: 0755]
near-rt-ric-simulator/nearric-service/pom.xml
near-rt-ric-simulator/pom.xml
near-rt-ric-simulator/ric-plt/a1/Dockerfile [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/a1-openapi.yaml [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/a1.py [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/commands.sh [new file with mode: 0755]
near-rt-ric-simulator/ric-plt/a1/main.py [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.1.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_instance_1_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.1.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_instance_1_bis_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.1.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_instance_2_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.1.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/policy_type_STD_QoSNudging_0.2.0.json [new file with mode: 0644]
near-rt-ric-simulator/ric-plt/a1/run_me.sh [new file with mode: 0755]
near-rt-ric-simulator/ric-plt/a1/var_declaration.py [new file with mode: 0644]
near-rt-ric-simulator/simulator-group/consul_cbs/config.json [new file with mode: 0644]
near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_config.hcl [new file with mode: 0644]
near-rt-ric-simulator/simulator-group/consul_cbs/consul/cbs_localhost_config.hcl [new file with mode: 0644]
near-rt-ric-simulator/simulator-group/consul_cbs/docker-compose-template.yml [new file with mode: 0644]
near-rt-ric-simulator/simulator-group/consul_cbs/start.sh [new file with mode: 0755]
near-rt-ric-simulator/simulator-group/simulators-start.sh [new file with mode: 0755]
policy-agent/.gitignore [new file with mode: 0644]
policy-agent/Dockerfile [new file with mode: 0644]
policy-agent/README.md [new file with mode: 0644]
policy-agent/config/application.yaml [new file with mode: 0644]
policy-agent/config/application_configuration.json [new file with mode: 0644]
policy-agent/docs/api.doc [new file with mode: 0644]
policy-agent/dpo/blueprints/k8s-policy-agent.yaml [new file with mode: 0644]
policy-agent/eclipse-formatter.xml [new file with mode: 0644]
policy-agent/pom.xml [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/Application.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/A1Client.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/OscA1Client.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/SdnrOnapA1Client.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/clients/StdA1Client.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfig.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/configuration/ApplicationConfigParser.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/configuration/AsyncConfiguration.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/configuration/RicConfig.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerImpl.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapRequestMessage.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapResponseMessage.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/exceptions/EnvironmentLoaderException.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/exceptions/ServiceException.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/Policies.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/Policy.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyType.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/PolicyTypes.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/Rics.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/tasks/EnvironmentProcessor.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/tasks/RicRecoveryTask.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/tasks/StartupService.java [new file with mode: 0644]
policy-agent/src/main/resources/keystore.jks [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/clients/StdA1ClientTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigParserTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/tasks/StartupServiceTest.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/utils/LoggingUtils.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1Client.java [new file with mode: 0644]
policy-agent/src/test/java/org/oransc/policyagent/utils/MockA1ClientFactory.java [new file with mode: 0644]
policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json [new file with mode: 0644]
policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json [new file with mode: 0644]
policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json [new file with mode: 0644]
policy-agent/src/test/resources/test_application_configuration.json [new file with mode: 0644]
policy-agent/src/test/resources/test_application_configuration_with_dmaap_config.json [new file with mode: 0644]
pom.xml
sdnc-a1-controller/northbound/nonrt-ric-api/model/src/main/yang/NONRT-RIC-API.yang
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/exceptions/NearRtRicNotFoundException.java [deleted file]
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/provider/NonrtRicApiProvider.java
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/NearRicUrlProvider.java [new file with mode: 0644]
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapter.java [moved from sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapter.java with 90% similarity]
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadapter/RestAdapterImpl.java [moved from sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/RestAdapterImpl.java with 93% similarity]
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/main/java/org/onap/sdnc/northbound/restadpter/NearRicUrlProvider.java [deleted file]
sdnc-a1-controller/northbound/nonrt-ric-api/provider/src/test/java/org/onap/sdnc/northbound/NonrtRicApiProviderTest.java
sdnc-a1-controller/oam/installation/sdnc/pom.xml
sdnc-a1-controller/pom.xml

index 67d6d0f..90c40a4 100644 (file)
@@ -3,3 +3,7 @@
 .tox
 docs/_build/
 .DS_STORE
+
+# Eclipse
+.checkstyle
+.sts4-cache
\ No newline at end of file
index 675d063..1fe9c87 100644 (file)
@@ -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 (file)
index 212de16..0000000
+++ /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 (file)
index b93a390..0000000
+++ /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 (file)
index 4011bfb..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--<![CDATA[
-========================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===================================
-]]>-->
-<project xmlns="http://maven.apache.org/POM/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/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.o-ran-sc.nonrt.ric-dashboard</groupId>
-               <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.0-SNAPSHOT</version>
-       </parent>
-       <!-- This groupId will NOT allow deployment in LF -->
-       <groupId>org.o-ran-sc.ric.plt.a1controller.client</groupId>
-       <artifactId>a1-controller-client</artifactId>
-       <name>RIC A1 Controller client</name>
-       <version>0.1.0-SNAPSHOT</version>
-       <properties>
-               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-               <!-- Jenkins invokes maven with -Dbuild.number=.. -->
-               <build.number>0</build.number>
-               <!-- same as groupId BUT without hyphens -->
-               <client.base.package.name>org.oransc.ric.a1controller.client</client.base.package.name>
-       </properties>
-       <!-- Successful compilation requires generated code dependencies -->
-       <dependencies>
-               <!-- Required for Java 9 and later -->
-               <dependency>
-                       <groupId>javax.annotation</groupId>
-                       <artifactId>javax.annotation-api</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>io.swagger.core.v3</groupId>
-                       <artifactId>swagger-annotations</artifactId>
-                       <version>2.0.8</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.springframework</groupId>
-                       <artifactId>spring-context</artifactId>
-               </dependency>
-               <!-- HTTP client: Spring RestTemplate -->
-               <dependency>
-                       <groupId>org.springframework</groupId>
-                       <artifactId>spring-web</artifactId>
-               </dependency>
-               <!-- JSON processing: jackson -->
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-core</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-annotations</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-databind</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.jaxrs</groupId>
-                       <artifactId>jackson-jaxrs-json-provider</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.datatype</groupId>
-                       <artifactId>jackson-datatype-jsr310</artifactId>
-               </dependency>
-               <!-- test dependencies -->
-               <dependency>
-                       <groupId>org.junit.jupiter</groupId>
-                       <artifactId>junit-jupiter-api</artifactId>
-                       <scope>test</scope>
-               </dependency>
-                <dependency>
-                        <groupId>org.slf4j</groupId>
-                        <artifactId>slf4j-api</artifactId>
-                </dependency>
-       </dependencies>
-       <build>
-               <plugins>
-                       <plugin>
-                               <!-- This 2019 version is required for OpenAPI 3 -->
-                               <groupId>io.swagger.codegen.v3</groupId>
-                               <artifactId>swagger-codegen-maven-plugin</artifactId>
-                               <version>3.0.8</version>
-                               <executions>
-                                       <execution>
-                                               <goals>
-                                                       <goal>generate</goal>
-                                               </goals>
-                                               <configuration>
-                                                       <inputSpec>${project.basedir}/src/main/resources/a1_controller_0.1.0.yaml</inputSpec>
-                                                       <language>java</language>
-                                                       <packageName>${client.base.package.name}</packageName>
-                                                       <modelPackage>${client.base.package.name}.model</modelPackage>
-                                                       <apiPackage>${client.base.package.name}.api</apiPackage>
-                                                       <invokerPackage>${client.base.package.name}.invoker</invokerPackage>
-                                                       <configOptions>
-                                                               <groupId>${project.groupId}</groupId>
-                                                               <artifactId>${project.artifactId}</artifactId>
-                                                               <artifactVersion>${project.version}</artifactVersion>
-                                                               <library>resttemplate</library>
-                                                               <java8>true</java8>
-                                                               <dateLibrary>java8</dateLibrary>
-                                                               <licenseName>Apache 2.0</licenseName>
-                                                               <licenseUrl>https://www.apache.org/licenses/LICENSE-2.0</licenseUrl>
-                                                       </configOptions>
-                                               </configuration>
-                                       </execution>
-                               </executions>
-                       </plugin>
-                       <!-- add build information to manifest. Java provides access to the implementation 
-                               version for a package, so cram the build number into there. -->
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-jar-plugin</artifactId>
-                               <configuration>
-                                       <archive>
-                                               <manifest>
-                                                       <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
-                                               </manifest>
-                                               <manifestEntries>
-                                                       <Implementation-Version>${project.version}-b${build.number}</Implementation-Version>
-                                               </manifestEntries>
-                                       </archive>
-                               </configuration>
-                       </plugin>
-               </plugins>
-               <pluginManagement>
-                       <plugins>
-                               <!--This plugin's configuration is used to store Eclipse m2e settings 
-                                       only. It has no influence on the Maven build itself. -->
-                               <plugin>
-                                       <groupId>org.eclipse.m2e</groupId>
-                                       <artifactId>lifecycle-mapping</artifactId>
-                                       <version>1.0.0</version>
-                                       <configuration>
-                                               <lifecycleMappingMetadata>
-                                                       <pluginExecutions>
-                                                               <pluginExecution>
-                                                                       <pluginExecutionFilter>
-                                                                               <groupId>io.swagger.codegen.v3</groupId>
-                                                                               <artifactId>swagger-codegen-maven-plugin</artifactId>
-                                                                               <versionRange>[1.0,)</versionRange>
-                                                                               <goals>
-                                                                                       <goal>generate</goal>
-                                                                               </goals>
-                                                                       </pluginExecutionFilter>
-                                                                       <action>
-                                                                               <ignore />
-                                                                       </action>
-                                                               </pluginExecution>
-                                                       </pluginExecutions>
-                                               </lifecycleMappingMetadata>
-                                       </configuration>
-                               </plugin>
-                       </plugins>
-               </pluginManagement>
-       </build>
-</project>
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 (file)
index 86bb823..0000000
+++ /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 (file)
index cbafba4..0000000
+++ /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());
-               }
-       }
-}
index 1473449..ca7673a 100644 (file)
@@ -33,7 +33,7 @@ limitations under the License.
        <artifactId>ric-dash-parent</artifactId>
        <name>NonRT RIC Dashboard project</name>
        <packaging>pom</packaging>
-       <version>1.0.0-SNAPSHOT</version>
+       <version>1.0.1-SNAPSHOT</version>
        <properties>
                <java.version>11</java.version>
                <!-- Properties for the license-maven-plugin in child POMs -->
@@ -44,8 +44,7 @@ limitations under the License.
                <lmp.process.start.tag>========================LICENSE_START=================================</lmp.process.start.tag>
                <lmp.process.end.tag>========================LICENSE_END===================================</lmp.process.end.tag>
        </properties>
-       <modules>
-               <module>a1-controller-client</module>
+       <modules>               
                <module>webapp-frontend</module>
                <module>webapp-backend</module>
        </modules>
diff --git a/dashboard/webapp-backend/eclipse-formatter.xml b/dashboard/webapp-backend/eclipse-formatter.xml
new file mode 100644 (file)
index 0000000..7339434
--- /dev/null
@@ -0,0 +1,315 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ * ========================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===================================
+  -->
+<profiles version="13">
+<profile kind="CodeFormatterProfile" name="java-formatter" version="12">
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+</profile>
+</profiles>
+
index 215ec75..bbbf2a4 100644 (file)
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--<![CDATA[
+<!--
+
+
+<![CDATA[
 ========================LICENSE_START=================================
 O-RAN-SC
 %%
@@ -18,287 +21,321 @@ See the License for the specific language governing permissions and
 limitations under the License.
 ========================LICENSE_END===================================
 ]]>-->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project
+    xmlns="http://maven.apache.org/POM/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">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.o-ran-sc.nonrt.ric-dashboard</groupId>
-               <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.0-SNAPSHOT</version>
-       </parent>
-       <artifactId>ric-dash-be</artifactId>
-       <name>NonRT RIC Dashboard Webapp backend</name>
-       <properties>
-               <springfox.version>2.9.2</springfox.version>
-               <!-- Set by Jenkins -->
-               <build.number>0</build.number>
-       </properties>
-       <repositories>
-               <repository>
-                       <id>onap-releases</id>
-                       <name>ONAP - Release Repository</name>
-                       <url>https://nexus.onap.org/content/repositories/releases</url>
-               </repository>
-       </repositories>
-       <dependencies>
-               <!-- Platform components -->
-               <dependency>
-                       <groupId>org.o-ran-sc.ric.plt.a1controller.client</groupId>
-                       <artifactId>a1-controller-client</artifactId>
-                       <version>0.1.0-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.onap.portal.sdk</groupId>
-                       <artifactId>epsdk-fw</artifactId>
-                       <version>2.6.0</version>
-                       <exclusions>
-                               <exclusion>
-                                       <groupId>commons-logging</groupId>
-                                       <artifactId>commons-logging</artifactId>
-                               </exclusion>
-                               <exclusion>
-                                       <groupId>log4j</groupId>
-                                       <artifactId>log4j</artifactId>
-                               </exclusion>
-                               <exclusion>
-                                       <groupId>log4j</groupId>
-                                       <artifactId>apache-log4j-extras</artifactId>
-                               </exclusion>
-                               <exclusion>
-                                       <groupId>org.slf4j</groupId>
-                                       <artifactId>slf4j-log4j12</artifactId>
-                               </exclusion>
-                               <exclusion>
-                                       <groupId>junit</groupId>
-                                       <artifactId>junit</artifactId>
-                               </exclusion>
-                               <exclusion>
-                                       <groupId>commons-fileupload</groupId>
-                                       <artifactId>commons-fileupload</artifactId>
-                               </exclusion>
-                               <exclusion>
-                                       <groupId>commons-beanutils</groupId>
-                                       <artifactId>commons-beanutils</artifactId>
-                               </exclusion>
-                               <!-- EELF omits "test" scope on this dependency -->
-                               <exclusion>
-                                       <groupId>org.powermock</groupId>
-                                       <artifactId>powermock-module-junit4</artifactId>
-                               </exclusion>
-                               <!-- EELF omits "test" scope on this dependency -->
-                               <exclusion>
-                                       <groupId>org.powermock</groupId>
-                                       <artifactId>powermock-api-mockito</artifactId>
-                               </exclusion>
-                       </exclusions>
-               </dependency>
-               <dependency>
-                       <groupId>org.springframework.boot</groupId>
-                       <artifactId>spring-boot-starter-security</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>org.springframework.boot</groupId>
-                       <artifactId>spring-boot-starter-web</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>org.slf4j</groupId>
-                       <artifactId>slf4j-api</artifactId>
-               </dependency>
-               <!-- Bridge uses of Apache commons logging, like EPSDK-FW -->
-               <dependency>
-                       <groupId>org.slf4j</groupId>
-                       <artifactId>jcl-over-slf4j</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>ch.qos.logback</groupId>
-                       <artifactId>logback-classic</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>ch.qos.logback</groupId>
-                       <artifactId>logback-core</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>io.springfox</groupId>
-                       <artifactId>springfox-swagger2</artifactId>
-                       <version>${springfox.version}</version>
-               </dependency>
-               <dependency>
-                       <groupId>io.springfox</groupId>
-                       <artifactId>springfox-swagger-ui</artifactId>
-                       <version>${springfox.version}</version>
-               </dependency>
-               <!-- Test dependencies -->
-               <!-- Mockito supports development, not just testing -->
-               <dependency>
-                       <groupId>org.mockito</groupId>
-                       <artifactId>mockito-core</artifactId>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.springframework.boot</groupId>
-                       <artifactId>spring-boot-starter-test</artifactId>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.junit.jupiter</groupId>
-                       <artifactId>junit-jupiter-api</artifactId>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.junit.jupiter</groupId>
-                       <artifactId>junit-jupiter-engine</artifactId>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.junit.platform</groupId>
-                       <artifactId>junit-platform-launcher</artifactId>
-                       <!-- Override Spring-Boot choice for Eclipse -->
-                       <version>1.4.2</version>
-                       <scope>test</scope>
-               </dependency>
-       </dependencies>
-       <build>
-               <plugins>
-                       <plugin>
-                               <groupId>org.springframework.boot</groupId>
-                               <artifactId>spring-boot-maven-plugin</artifactId>
-                       </plugin>
-                       <plugin>
-                               <!-- Most configuration and all execution is inherited -->
-                               <groupId>org.codehaus.mojo</groupId>
-                               <artifactId>license-maven-plugin</artifactId>
-                               <configuration>
-                                       <roots>
-                                               <root>src</root>
-                                       </roots>
-                                       <excludes>
-                                               <exclude>**/*.json</exclude>
-                                       </excludes>
-                               </configuration>
-                       </plugin>
-                       <!-- Add the build number to the jar manifest. Spring-Boot uses a complex 
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.o-ran-sc.nonrt.ric-dashboard</groupId>
+        <artifactId>ric-dash-parent</artifactId>
+        <version>1.0.1-SNAPSHOT</version>
+    </parent>
+    <artifactId>ric-dash-be</artifactId>
+    <name>NonRT RIC Dashboard Webapp backend</name>
+    <properties>
+        <springfox.version>2.9.2</springfox.version>
+        <immutable.version>2.7.1</immutable.version>
+        <formatter-maven-plugin.version>2.8.1</formatter-maven-plugin.version>
+        <spotless-maven-plugin.version>1.18.0</spotless-maven-plugin.version>
+        <!-- Set by Jenkins -->
+        <build.number>0</build.number>
+    </properties>
+    <repositories>
+        <repository>
+            <id>onap-releases</id>
+            <name>ONAP - Release Repository</name>
+            <url>https://nexus.onap.org/content/repositories/releases</url>
+        </repository>
+    </repositories>
+    <dependencies>
+        <!-- Platform components -->
+        <dependency>
+            <groupId>org.onap.portal.sdk</groupId>
+            <artifactId>epsdk-fw</artifactId>
+            <version>2.6.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>apache-log4j-extras</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>junit</groupId>
+                    <artifactId>junit</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-fileupload</groupId>
+                    <artifactId>commons-fileupload</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-beanutils</groupId>
+                    <artifactId>commons-beanutils</artifactId>
+                </exclusion>
+                <!-- EELF omits "test" scope on this dependency -->
+                <exclusion>
+                    <groupId>org.powermock</groupId>
+                    <artifactId>powermock-module-junit4</artifactId>
+                </exclusion>
+                <!-- EELF omits "test" scope on this dependency -->
+                <exclusion>
+                    <groupId>org.powermock</groupId>
+                    <artifactId>powermock-api-mockito</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <!-- Bridge uses of Apache commons logging, like EPSDK-FW -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.immutables</groupId>
+            <artifactId>value</artifactId>
+            <version>${immutable.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.immutables</groupId>
+            <artifactId>gson</artifactId>
+            <version>${immutable.version}</version>
+        </dependency>
+        <!-- Test dependencies -->
+        <!-- Mockito supports development, not just testing -->
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <version>${formatter-maven-plugin.version}</version>
+                <configuration>
+                    <configFile>${project.basedir}/eclipse-formatter.xml</configFile>
+                </configuration>
+                <!-- https://code.revelc.net/formatter-maven-plugin/ use
+                    mvn formatter:format spotless:apply process-sources -->
+            </plugin>
+            <plugin>
+                <groupId>com.diffplug.spotless</groupId>
+                <artifactId>spotless-maven-plugin</artifactId>
+                <version>${spotless-maven-plugin.version}</version>
+                <configuration>
+                    <java>
+                        <removeUnusedImports />
+                        <importOrder>
+                            <order>com,java,javax,org</order>
+                        </importOrder>
+                    </java>
+                </configuration>
+                <!-- https://github.com/diffplug/spotless/tree/master/plugin-maven
+                    use mvn spotless:apply to rewrite source files use mvn spotless:check to
+                    validate source files -->
+            </plugin>
+            <plugin>
+                <!-- Most configuration and all execution is inherited -->
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <configuration>
+                    <roots>
+                        <root>src</root>
+                    </roots>
+                    <excludes>
+                        <exclude>**/*.json</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <!-- Add the build number to the jar manifest. Spring-Boot uses a complex 
                                packaging process that makes access to the original Manifest.MF very difficult. 
                                However, Java provides access to the implementation version for a package, 
                                so cram the build number into there. -->
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-jar-plugin</artifactId>
-                               <configuration>
-                                       <archive>
-                                               <manifest>
-                                                       <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
-                                               </manifest>
-                                               <manifestEntries>
-                                                       <Implementation-Version>${project.version}-b${build.number}</Implementation-Version>
-                                               </manifestEntries>
-                                       </archive>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <artifactId>maven-resources-plugin</artifactId>
-                               <executions>
-                                       <execution>
-                                               <id>copy-resources</id>
-                                               <phase>validate</phase>
-                                               <goals>
-                                                       <goal>copy-resources</goal>
-                                               </goals>
-                                               <configuration>
-                                                       <outputDirectory>${project.build.directory}/classes/resources/</outputDirectory>
-                                                       <resources>
-                                                               <resource>
-                                                                       <directory>${project.parent.basedir}/webapp-frontend/dist/dashApp/</directory>
-                                                               </resource>
-                                                       </resources>
-                                               </configuration>
-                                       </execution>
-                               </executions>
-                       </plugin>
-                       <!-- do not deploy a jar or pom file -->
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-deploy-plugin</artifactId>
-                               <configuration>
-                                       <skip>true</skip>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.jacoco</groupId>
-                               <artifactId>jacoco-maven-plugin</artifactId>
-                               <version>0.8.4</version>
-                               <executions>
-                                       <execution>
-                                               <id>default-prepare-agent</id>
-                                               <goals>
-                                                       <goal>prepare-agent</goal>
-                                               </goals>
-                                       </execution>
-                                       <execution>
-                                               <id>default-report</id>
-                                               <phase>prepare-package</phase>
-                                               <goals>
-                                                       <goal>report</goal>
-                                               </goals>
-                                       </execution>
-                               </executions>
-                       </plugin>
-                       <!-- https://stackoverflow.com/questions/39126226/fabric8-springboot-full-example -->
-                       <plugin>
-                               <groupId>io.fabric8</groupId>
-                               <artifactId>docker-maven-plugin</artifactId>
-                               <version>0.30.0</version>
-                               <configuration>
-                                       <verbose>true</verbose>
-                                       <!-- environment variables supplied by Jenkins -->
-                                       <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
-                                       <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
-                                       <images>
-                                               <image>
-                                                       <!-- Specify a tag to avoid default tag "latest" -->
-                                                       <!-- Avoid maven artifact name here -->
-                                                       <name>o-ran-sc/nonrtric-dashboard:${project.version}</name>
-                                                       <build>
-                                                               <from>openjdk:11-jre-slim</from>
-                                                               <tags>
-                                                                       <!-- Add tag with build number -->
-                                                                       <tag>${project.version}</tag>
-                                                               </tags>
-                                                               <assembly>
-                                                                       <descriptorRef>artifact</descriptorRef>
-                                                               </assembly>
-                                                               <runCmds>
-                                                                       <!-- Ensure logs dir exists and is world writable -->
-                                                                       <runCmd>mkdir /logs</runCmd>
-                                                                       <runCmd>chmod -R 777 /logs</runCmd>
-                                                               </runCmds>
-                                                               <cmd>
-                                                                       <!-- Include maven dir on classpath for prop files -->
-                                                                       <exec>
-                                                                               <arg>java</arg>
-                                                                               <arg>-Xms128m</arg>
-                                                                               <arg>-Xmx256m</arg>
-                                                                               <arg>-cp</arg>
-                                                                               <arg>maven:maven/${project.artifactId}-${project.version}.${project.packaging}</arg>
-                                                                               <arg>-Dloader.main=org.oransc.ric.portal.dashboard.DashboardApplication</arg>
-                                                                               <arg>-Djava.security.egd=file:/dev/./urandom</arg>
-                                                                               <arg>org.springframework.boot.loader.PropertiesLauncher</arg>
-                                                                       </exec>
-                                                               </cmd>
-                                                       </build>
-                                               </image>
-                                       </images>
-                               </configuration>
-                               <!-- build Docker images in install phase, push in deploy phase -->
-                               <executions>
-                                       <execution>
-                                               <goals>
-                                                       <goal>build</goal>
-                                                       <goal>push</goal>
-                                               </goals>
-                                       </execution>
-                               </executions>
-                       </plugin>
-               </plugins>
-       </build>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                        </manifest>
+                        <manifestEntries>
+                            <Implementation-Version>${project.version}-b${build.number}</Implementation-Version>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-resources</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/classes/resources/</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>${project.parent.basedir}/webapp-frontend/dist/dashApp/</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- do not deploy a jar or pom file -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>0.8.4</version>
+                <executions>
+                    <execution>
+                        <id>default-prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>default-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- https://stackoverflow.com/questions/39126226/fabric8-springboot-full-example -->
+            <plugin>
+                <groupId>io.fabric8</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>0.30.0</version>
+                <configuration>
+                    <verbose>true</verbose>
+                    <!-- environment variables supplied by Jenkins -->
+                    <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                    <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
+                    <images>
+                        <image>
+                            <!-- Specify a tag to avoid default tag "latest" -->
+                            <!-- Avoid maven artifact name here -->
+                            <name>o-ran-sc/nonrtric-dashboard:${project.version}</name>
+                            <build>
+                                <from>openjdk:11-jre-slim</from>
+                                <tags>
+                                    <!-- Add tag with build number -->
+                                    <tag>${project.version}</tag>
+                                </tags>
+                                <assembly>
+                                    <descriptorRef>artifact</descriptorRef>
+                                </assembly>
+                                <runCmds>
+                                    <!-- Ensure logs dir exists and is world writable -->
+                                    <runCmd>mkdir /logs</runCmd>
+                                    <runCmd>chmod -R 777 /logs</runCmd>
+                                </runCmds>
+                                <cmd>
+                                    <!-- Include maven dir on classpath for prop files -->
+                                    <exec>
+                                        <arg>java</arg>
+                                        <arg>-Xms128m</arg>
+                                        <arg>-Xmx256m</arg>
+                                        <arg>-cp</arg>
+                                        <arg>maven:maven/${project.artifactId}-${project.version}.${project.packaging}</arg>
+                                        <arg>-Dloader.main=org.oransc.ric.portal.dashboard.DashboardApplication</arg>
+                                        <arg>-Djava.security.egd=file:/dev/./urandom</arg>
+                                        <arg>org.springframework.boot.loader.PropertiesLauncher</arg>
+                                    </exec>
+                                </cmd>
+                            </build>
+                        </image>
+                    </images>
+                </configuration>
+                <!-- build Docker images in install phase, push in deploy phase -->
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build</goal>
+                            <goal>push</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
index 333c532..2550b8e 100644 (file)
@@ -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";
+    }
 
 }
index d61ce1d..4b5d9a4 100644 (file)
@@ -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";
 
 }
index c5fd101..ffb7396 100644 (file)
@@ -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.
  */
 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<EcompUser> 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<List<EcompUser>>() {
-                       });
-               } else {
-                       users = new ArrayList<>();
-               }
-       }
-
-       /**
-        * Gets the current users.
-        * 
-        * @return List of EcompUser objects, possibly empty
-        */
-       public List<EcompUser> 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<EcompRole> 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<EcompUser> 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<List<EcompUser>>() {});
+        } else {
+            users = new ArrayList<>();
+        }
+    }
+
+    /**
+     * Gets the current users.
+     *
+     * @return List of EcompUser objects, possibly empty
+     */
+    public List<EcompUser> 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<EcompRole> 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 (file)
index ffdcacd..0000000
+++ /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());
-       }
-
-}
index 696d74f..8c646fe 100644 (file)
@@ -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);
+    }
 
 }
index f318cad..520f27f 100644 (file)
@@ -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<PortalRestAPIProxy> portalApiProxyServletBean() {
-               logger.debug("portalApiProxyServletBean");
-               PortalRestAPIProxy servlet = new PortalRestAPIProxy();
-               final ServletRegistrationBean<PortalRestAPIProxy> 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<PortalRestAPIProxy> portalApiProxyServletBean() {
+        logger.debug("portalApiProxyServletBean");
+        PortalRestAPIProxy servlet = new PortalRestAPIProxy();
+        final ServletRegistrationBean<PortalRestAPIProxy> servletBean =
+            new ServletRegistrationBean<>(servlet, PortalApiConstants.API_PREFIX + "/*");
+        servletBean.setName("PortalRestApiProxyServlet");
+        return servletBean;
+    }
 
 }
index 3a87782..a2f3d8f 100644 (file)
@@ -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;
+    }
 
 }
index b4bb0a3..435414a 100644 (file)
@@ -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();
+    }
 }
index 0a7f02c..b43f8a9 100644 (file)
@@ -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 (file)
index 08b485e..0000000
+++ /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<Integer> 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<String> 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");
-           }
-       }
-}
index f5ecd10..52a51fb 100644 (file)
@@ -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:<br>
  * 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: <blockquote>HTTP server received an invalid response from a
-        * server it consulted when acting as a proxy or gateway.</blockquote>
-        * 
-        * @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<ErrorTransport> 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: <blockquote>HTTP server received an invalid response from a
+     * server it consulted when acting as a proxy or gateway.</blockquote>
+     *
+     * @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<ErrorTransport> 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);
+        }
+    }
 
 }
index 7fb6e67..5d80538 100644 (file)
@@ -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 (file)
index 0000000..c01c7c6
--- /dev/null
@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> getRicsSupportingType(
+        @RequestParam(name = "policyType", required = true) String supportingPolicyType) {
+        logger.debug("getRicsSupportingType {}", supportingPolicyType);
+
+        return this.policyAgentApi.getRicsSupportingType(supportingPolicyType);
+    }
+}
index e2248e6..63b78ec 100644 (file)
@@ -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<String, Object> 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<String, Object> 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";
+    }
 
 }
index bcfae62..d3add60 100644 (file)
@@ -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<String> 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<String> podsResponse = rt.getForEntity(podsUrl, String.class);
+        return podsResponse.getBody();
+    }
 
 }
index 7bc9f00..3a08206 100644 (file)
@@ -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<? extends GrantedAuthority> getAuthorities() {
-               List<GrantedAuthority> roleList = new ArrayList<>();
-               Iterator<EcompRole> 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<? extends GrantedAuthority> getAuthorities() {
+        List<GrantedAuthority> roleList = new ArrayList<>();
+        Iterator<EcompRole> 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();
+    }
 
 }
index 516e3c8..f1250c1 100644 (file)
@@ -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;
+    }
 
 }
  */
 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();
+
 }
index c750487..c6faf9b 100644 (file)
@@ -21,8 +21,8 @@ package org.oransc.ric.portal.dashboard.model;
 
 import java.util.ArrayList;
 
-public class PolicyInstances extends ArrayList<PolicyInstance> {
+public class PolicyInstances extends ArrayList<PolicyInfo> {
 
-       private static final long serialVersionUID = -928428052502491021L;
+    private static final long serialVersionUID = -928428052502491021L;
 
 }
index efe4010..f0ca285 100644 (file)
@@ -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 + "]";
+    }
 }
index 3165d1b..43a6383 100644 (file)
@@ -23,6 +23,6 @@ import java.util.ArrayList;
 
 public class PolicyTypes extends ArrayList<PolicyType> {
 
-       private static final long serialVersionUID = -928428052502491021L;
+    private static final long serialVersionUID = -928428052502491021L;
 
 }
index 9e13789..65b39eb 100644 (file)
@@ -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 (file)
index 0000000..ff254d2
--- /dev/null
@@ -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<String> getAllPolicyTypes();
+
+    public ResponseEntity<String> getPolicyInstancesForType(String type);
+
+    public ResponseEntity<String> getPolicyInstance(String id);
+
+    public ResponseEntity<String> putPolicy(String policyTypeIdString, String policyInstanceId, String json,
+        String ric);
+
+    public ResponseEntity<String> deletePolicy(String policyInstanceId);
+
+    public ResponseEntity<String> 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 (file)
index 0000000..c0dde9b
--- /dev/null
@@ -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<String> getAllPolicyTypes() {
+        try {
+            String url = baseUrl() + "/policy_schemas";
+            ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
+            if (!rsp.getStatusCode().is2xxSuccessful()) {
+                return rsp;
+            }
+
+            PolicyTypes result = new PolicyTypes();
+            JsonParser jsonParser = new JsonParser();
+
+            JsonArray schemas = jsonParser.parse(rsp.getBody()).getAsJsonArray();
+            for (JsonElement schema : schemas) {
+                JsonObject schemaObj = schema.getAsJsonObject();
+                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<String> getPolicyInstancesForType(String type) {
+        String url = baseUrl() + "/policies?type={type}";
+        Map<String, ?> uriVariables = Map.of("type", type);
+        ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class, uriVariables);
+        if (!rsp.getStatusCode().is2xxSuccessful()) {
+            return rsp;
+        }
+
+        try {
+            Type listType = new TypeToken<List<ImmutablePolicyInfo>>() {}.getType();
+            List<PolicyInfo> 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<String> getPolicyInstance(String id) {
+        String url = baseUrl() + "/policy?instance={id}";
+        Map<String, ?> uriVariables = Map.of("id", id);
+
+        return this.restTemplate.getForEntity(url, String.class, uriVariables);
+    }
+
+    @Override
+    public ResponseEntity<String> putPolicy(String policyTypeIdString, String policyInstanceId, String json,
+        String ric) {
+        String url = baseUrl() + "/policy?type={type}&instance={instance}&ric={ric}&service={service}";
+        Map<String, ?> uriVariables = Map.of( //
+            "type", policyTypeIdString, //
+            "instance", policyInstanceId, //
+            "ric", ric, //
+            "service", "dashboard");
+
+        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<String> deletePolicy(String policyInstanceId) {
+        String url = baseUrl() + "/policy?instance={instance}";
+        Map<String, ?> 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<String> nodeNames();
+
+        public Collection<String> policyTypes();
+    }
+
+    @Override
+    public ResponseEntity<String> getRicsSupportingType(String typeName) {
+        String url = baseUrl() + "/rics?policyType={typeName}";
+        Map<String, ?> uriVariables = Map.of("typeName", typeName);
+        String rsp = this.restTemplate.getForObject(url, String.class, uriVariables);
+
+        try {
+            Type listType = new TypeToken<List<ImmutableRicInfo>>() {}.getType();
+            List<RicInfo> rspParsed = gson.fromJson(rsp, listType);
+            Collection<String> result = new Vector<>(rspParsed.size());
+            for (RicInfo ric : rspParsed) {
+                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<String> createJsonHttpEntity(String content) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return new HttpEntity<String>(content, headers);
+    }
+
+}
index 34d80c9..552d783 100644 (file)
@@ -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;
 
 }
index dc70f7e..f4daa5c 100644 (file)
@@ -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<String, String> credentialsMap;
-       private final IPortalSdkDecryptor portalSdkDecryptor;
-       private final String userIdCookieName;
+    final Map<String, String> 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<String, String> getAppCredentials() {
-               return credentialsMap;
-       }
+    /**
+     * @return A map of key-value pairs with application name, user name and
+     *         password.
+     */
+    public Map<String, String> 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;
+    }
 
 }
index 4b6de91..711761a 100644 (file)
@@ -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.
  * </UL>
- * 
+ *
  * 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<EcompRole> 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<EcompRole> 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 = "<a href=\"" + redirectUrl + "\">";
-               // If only Java had "here" documents.
-               String body = String.join(//
-                               System.getProperty("line.separator"), //
-                               "<html>", //
-                               "<head>", //
-                               "<title>RIC Dashboard</title>", //
-                               "<style>", //
-                               "html, body { ", //
-                               "  font-family: Helvetica, Arial, sans-serif;", //
-                               "}", //
-                               "</style>", //
-                               "</head>", //
-                               "<body>", //
-                               "<h2>RIC Dashboard</h2>", //
-                               "<h4>Please log in.</h4>", //
-                               "<p>", //
-                               aHref, "Click here to authenticate at the ONAP Portal</a>", //
-                               "</p>", //
-                               "</body>", //
-                               "</html>");
-               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 = "<a href=\"" + redirectUrl + "\">";
+        // If only Java had "here" documents.
+        String body = String.join(//
+            System.getProperty("line.separator"), //
+            "<html>", //
+            "<head>", //
+            "<title>RIC Dashboard</title>", //
+            "<style>", //
+            "html, body { ", //
+            "  font-family: Helvetica, Arial, sans-serif;", //
+            "}", //
+            "</style>", //
+            "</head>", //
+            "<body>", //
+            "<h2>RIC Dashboard</h2>", //
+            "<h4>Please log in.</h4>", //
+            "<p>", //
+            aHref, "Click here to authenticate at the ONAP Portal</a>", //
+            "</p>", //
+            "</body>", //
+            "</html>");
+        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();
+    }
 
 }
index 581ca25..a2fae9f 100644 (file)
@@ -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<String, String> getAppCredentials() throws PortalAPIException {
-               logger.debug("getAppCredentials");
-               return authManager.getAppCredentials();
-       }
+    /*
+     * Answers the Portal API credentials.
+     */
+    @Override
+    public Map<String, String> 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);
+    }
 
 }
index 279f1dd..f62ea5f 100644 (file)
@@ -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);
+    }
 
 }
index 0127267..1020527 100644 (file)
@@ -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);
+    }
 
 }
index a97ed7b..d5f2cbf 100644 (file)
@@ -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.");
+    }
 }
index 3aa2406..cfb1f8c 100644 (file)
@@ -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
index df1a51c..e19890b 100644 (file)
  */
 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 (file)
index 5706a91..0000000
+++ /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<Integer> 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.<InputNRRidPTidSchema>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.<InputNRRidPTidSchema>getArgument(0);
-                       List<String> 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.<InputNRRidPTidPIidSchema>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.<InputNRRidPTidPIidPISchema>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.<InputNRRidPTidPIidSchema>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<String> getInstances() {
-                               return new ArrayList<>(instances.keySet());
-                       }
-
-                       private final PolicyType policyType;
-                       private Map<String, String> 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<Integer> getTypes() {
-                       return new ArrayList<>(types.keySet());
-               }
-
-               List<String> getInstances(Optional<Integer> typeId) throws PolicyException {
-                       if (typeId.isPresent()) {
-                               return getTypeHolder(typeId.get()).getInstances();
-                       } else {
-                               Set<String> res = new HashSet<String>();
-                               for (Iterator<PolicyTypeHolder> 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<Integer, PolicyTypeHolder> 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 (file)
index 0000000..2268fe0
--- /dev/null
@@ -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<String> getPolicyInstance(String id) {
+            return new ResponseEntity<>(database.getInstance(id), HttpStatus.OK);
+        }
+
+        @Override
+        public ResponseEntity<String> 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<String> deletePolicy(String policyInstanceId) {
+            database.deleteInstance(policyInstanceId);
+            return new ResponseEntity<>("Policy was deleted successfully", HttpStatus.NO_CONTENT);
+        }
+
+        @Override
+        public ResponseEntity<String> getAllPolicyTypes() {
+            PolicyTypes result = new PolicyTypes();
+            result.addAll(database.getTypes());
+            return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+        }
+
+        @Override
+        public ResponseEntity<String> getPolicyInstancesForType(String type) {
+            PolicyInstances result = new PolicyInstances();
+            List<PolicyInfo> inst = database.getInstances(Optional.of(type));
+            result.addAll(inst);
+            return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+        }
+
+        @Override
+        public ResponseEntity<String> getRicsSupportingType(String typeName) {
+            Vector<String> 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<PolicyType> getTypes() {
+            return types.values();
+        }
+
+        public List<PolicyInfo> getInstances(Optional<String> typeId) {
+            ArrayList<PolicyInfo> 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<String, PolicyType> types = new HashMap<>();
+        private Map<String, PolicyInfo> instances = new HashMap<>();
+
+    }
+
+}
index a3d05be..01265d9 100644 (file)
@@ -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<PortalRestAPIProxy> portalApiProxyServlet() {
-               PortalRestAPIProxy servlet = new PortalRestAPIProxy();
-               final ServletRegistrationBean<PortalRestAPIProxy> servletBean = new ServletRegistrationBean<>(servlet,
-                               PortalApiConstants.API_PREFIX + "/*");
-               servletBean.setName("PortalRestApiProxyServlet");
-               return servletBean;
-       }
+    @Bean
+    public ServletRegistrationBean<PortalRestAPIProxy> portalApiProxyServlet() {
+        PortalRestAPIProxy servlet = new PortalRestAPIProxy();
+        final ServletRegistrationBean<PortalRestAPIProxy> 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<String, String> 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<String, String> 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;
+    }
 
 }
index 80cde66..3e75606 100644 (file)
@@ -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
+    }
 
 }
index d4163f0..09d3a86 100644 (file)
@@ -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<String, String> 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<String, String> 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<String, String> 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<String, String> 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);
+    }
 
 }
index 48c5931..643f15e 100644 (file)
@@ -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");
+    }
 
 }
index dc80968..ecb9640 100644 (file)
@@ -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<String> 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<String> 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<String> 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<String> response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class);
+        Assertions.assertTrue(response.getStatusCode().is4xxClientError());
+        Assertions.assertTrue(response.getBody().contains("Static error page"));
+    }
 
-       private HttpEntity<Object> 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<Object> 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<EcompRole> 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<Object> requestEntity = getEntityWithHeaders(createEcompUser(loginId));
-               ResponseEntity<String> 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<Object> requestEntity = getEntityWithHeaders(user);
-               // Create
-               ResponseEntity<String> 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<Object> 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<Object> 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<EcompRole> 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<Object> requestEntity = getEntityWithHeaders(createEcompUser(loginId));
+     * ResponseEntity<String> 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<Object> requestEntity = getEntityWithHeaders(user);
+     * // Create
+     * ResponseEntity<String> 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 (file)
index 0d7315e..0000000
+++ /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 (file)
index 6e0263d..0000000
+++ /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
index b82a67c..75dfadd 100644 (file)
@@ -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": {
       }
     },
     "@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",
         "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",
       }
     },
     "@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",
       }
     },
     "@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": {
       }
     },
     "@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",
       }
     },
     "@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": {
       }
     },
     "@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"
       }
       }
     },
     "@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",
         "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",
           "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",
       }
     },
     "@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",
             "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",
       }
     },
     "@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": {
       }
     },
     "@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",
         "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",
             "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",
           "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"
       }
     },
     "@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",
       }
     },
     "@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",
             "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": {
           "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",
             "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",
       }
     },
     "@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": {
             "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",
       }
     },
     "@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",
       }
     },
     "@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": {
         }
       }
     },
+    "@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",
       "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",
       "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": "*"
       }
     },
     "@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": {
       "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": "*",
       }
     },
     "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": {
       "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"
       "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": {
       "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": {
         }
       }
     },
-    "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",
         "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",
       }
     },
     "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",
       }
     },
     "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",
       },
       "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",
       "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": {
       "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"
       }
     },
     "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": {
         "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",
       "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",
       "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": {
       "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
         }
       }
       "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",
       "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"
       "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",
         "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",
             "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",
           }
         },
         "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",
       }
     },
     "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
         }
       }
         "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",
         "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": {
       "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",
       }
     },
     "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",
       "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": {
             }
           }
         },
-        "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",
       "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",
       "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",
       "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",
       }
     },
     "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",
       }
     },
     "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": {
       "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": {
           }
         },
         "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": {
       "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"
       },
       "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",
       "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",
       "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": {
       "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": {
           }
         },
         "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
         }
       }
       },
       "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": {
           "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": {
           "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
+            }
           }
         }
       }
       "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": {
       }
     },
     "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": {
       "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",
       "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": {
       "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": {
       "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": {
         "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",
             "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": {
       }
     },
     "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": {
           "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
         }
       }
     },
           "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",
       }
     },
     "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": {
         "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
         }
       }
     },
       }
     },
     "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"
       }
     },
       }
     },
     "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"
       }
     },
     "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",
       "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",
       }
     },
     "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": {
       "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",
       "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",
       }
     },
     "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",
       "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"
       }
       }
     },
     "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"
       },
       "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"
       }
     },
     "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": {
       }
     },
     "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": {
       "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": {
       }
     },
     "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": {
       "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"
       }
     },
     "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": {
       "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",
       }
     },
     "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
         }
       }
       }
     },
     "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",
             "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",
           "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",
       },
       "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",
       }
     },
     "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": {
       }
     },
     "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"
       "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",
       }
     },
     "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": {
       "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",
       "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": {
       }
     },
     "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",
       }
     },
     "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": {
       "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": {
       "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": {
           "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",
       }
     },
     "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": {
       }
     },
     "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",
       }
     },
     "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",
         "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",
       },
       "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",
       }
     },
     "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",
       },
       "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": {
           "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",
       }
     },
     "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",
       }
     },
     "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": {
       }
     },
     "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": {
       "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": {
       "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
         },
       "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": {
       }
     },
     "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": {
       }
     },
     "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": {
         "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": {
       },
       "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
         }
       }
     },
       }
     },
     "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"
       },
       }
     },
     "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"
           "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",
       }
     },
     "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": {
       }
     },
     "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": {
index 9f2cc86..840ef04 100644 (file)
   },
   "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"
index 2888f73..e316a6c 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.nonrt.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.0-SNAPSHOT</version>
+               <version>1.0.1-SNAPSHOT</version>
        </parent>
        <artifactId>ric-dash-fe</artifactId>
        <name>NonRT RIC Dashboard Webapp frontend</name>
index c06dc01..fb87ba5 100644 (file)
@@ -21,6 +21,7 @@
   color: gray;
   letter-spacing: 0.1rem;
   font-size: 10px;
+  margin-left: 10px;
 }
 
 .copyright__text-dark {
index e694bb6..b6ecce6 100644 (file)
 // 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 {
index 776d503..d447c70 100644 (file)
 <div class="text-muted" *ngIf="jsonSchemaObject.description">{{jsonSchemaObject.description}}</div>
 
 <div fxLayout="row" fxLayoutAlign="space-around start" fxLayout.lt-sm="column" fxLayoutAlign.lt-sm="flex-start center">
+
+
     <mat-card class="card" [ngClass]="{'card-dark': darkMode}">
+
+        <mat-form-field *ngIf="!this.policyInstanceId">
+            <mat-label [class.text-danger]="!this.ric">Select RIC</mat-label>
+            <mat-select [(value)]="this.ric">
+                <mat-option *ngFor="let ric of this.allRics" [value]="ric">
+                    {{ric}}
+                </mat-option>
+            </mat-select>
+        </mat-form-field>
+
         <h4 class="default-cursor" (click)="toggleVisible('form')">
             <mat-icon matTooltip="Properties">{{isVisible.form ? 'expand_less' : 'expand_more'}}</mat-icon>
             Properties
@@ -51,8 +63,8 @@
             </json-schema-form>
         </div>
         <hr />
-        <button mat-raised-button (click)="this.onSubmit()" [disabled]="!this.formIsValid" class="submitBtn"
-            style="margin-right:10px">Submit</button>
+        <button mat-raised-button (click)="this.onSubmit()" [disabled]="!this.formIsValid || !this.ric"
+            class="submitBtn">Submit</button>
         <button mat-raised-button (click)="this.onClose()">Close</button>
         <hr />
         <h4 [class.text-danger]="!formIsValid && !isVisible.json" [class.default-cursor]="formIsValid || isVisible.json"
index d69400d..b4ad194 100644 (file)
@@ -74,14 +74,29 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
     formValidationErrors: any;
     formIsValid = false;
 
-
     @ViewChild(MatMenuTrigger, { static: true }) menuTrigger: MatMenuTrigger;
 
-    public policyInstanceId: string;
-    public policyTypeName: string;
+    policyInstanceId: string; // null if not yet created
+    policyTypeName: string;
     darkMode: boolean;
-    private policyTypeId: number;
+    ric: string;
+    allRics: string[];
 
+    private fetchRics() {
+        console.log('fetchRics ' + this.policyTypeName);
+        const self: PolicyInstanceDialogComponent = this;
+        this.dataService.getRics(this.policyTypeName).subscribe(
+            {
+                next(value) {
+                    self.allRics = value;
+                    console.log(value);
+                },
+                error(error) {
+                    self.errorService.displayError('Fetching of rics failed: ' + error.message);
+                },
+                complete() { }
+            });
+    }
 
     constructor(
         private dataService: PolicyService,
@@ -93,9 +108,9 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
         this.formActive = false;
         this.policyInstanceId = data.instanceId;
         this.policyTypeName = data.name;
-        this.policyTypeId = data.policyTypeId;
         this.jsonSchemaObject = data.createSchema;
         this.jsonObject = this.parseJson(data.instanceJson);
+        this.ric = data.ric;
     }
 
     ngOnInit() {
@@ -104,6 +119,9 @@ export class PolicyInstanceDialogComponent implements OnInit, AfterViewInit {
         this.ui.darkModeState.subscribe((isDark) => {
             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
         }
     };
 }
index e1a67dd..8c305e4 100644 (file)
 
     <ng-container matColumnDef="instanceId">
         <mat-header-cell mat-sort-header *matHeaderCellDef>Instance</mat-header-cell>
-        <mat-cell *matCellDef="let element" (click)="modifyInstance(element)">{{element.instanceId}}
+        <mat-cell *matCellDef="let element" (click)="modifyInstance(element)">{{element.id}}
+        </mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="ric">
+        <mat-header-cell mat-sort-header *matHeaderCellDef>Ric</mat-header-cell>
+        <mat-cell *matCellDef="let element" (click)="modifyInstance(element)">{{element.ric}}
+        </mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="service">
+        <mat-header-cell mat-sort-header *matHeaderCellDef>Owner</mat-header-cell>
+        <mat-cell *matCellDef="let element" (click)="modifyInstance(element)">{{element.service}}
+        </mat-cell>
+    </ng-container>
+
+    <ng-container matColumnDef="lastModified">
+        <mat-header-cell mat-sort-header *matHeaderCellDef>Last modified</mat-header-cell>
+        <mat-cell *matCellDef="let element" (click)="modifyInstance(element)">{{toLocalTime(element.lastModified)}}
         </mat-cell>
     </ng-container>
 
         <mat-footer-cell *matFooterCellDef>No records found.</mat-footer-cell>
     </ng-container>
 
-    <mat-header-row *matHeaderRowDef="['instanceId',  'action']" [ngClass]="{'display-none': !this.hasInstances()}">
+    <mat-header-row *matHeaderRowDef="['instanceId', 'ric', 'service', 'lastModified', 'action']"
+        [ngClass]="{'display-none': !this.hasInstances()}">
     </mat-header-row>
-    <mat-row *matRowDef="let instance; columns: ['instanceId', 'action'];"></mat-row>
+    <mat-row *matRowDef="let instance; columns: ['instanceId', 'ric', 'service', 'lastModified', 'action'];"></mat-row>
 
     <mat-footer-row *matFooterRowDef="['noRecordsFound']" [ngClass]="{'display-none': this.hasInstances()}">
     </mat-footer-row>
index b6a29a4..6a18c34 100644 (file)
  */
 
 .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;
 }
index b62deb4..ecf6f36 100644 (file)
@@ -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<Object>) => {
                                     switch (response.status) {
index 53a6239..b82ee75 100644 (file)
@@ -51,7 +51,7 @@ export class PolicyInstanceDataSource extends DataSource<PolicyInstance> {
 
     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<PolicyInstance> {
         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;
             }
         });
index 8d9dec7..97d792e 100644 (file)
@@ -60,7 +60,12 @@ export class PolicyTypeDataSource extends DataSource<PolicyType> {
                 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);
             });
index 520f9a5..57b9d57 100644 (file)
@@ -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: []
 })
index f7acff8..a6078a0 100644 (file)
@@ -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');
   }
 
 }
index 3ba8a40..d44ffe5 100644 (file)
  */
 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
index 1c87081..6af517f 100644 (file)
@@ -66,7 +66,7 @@ export class PolicyService {
         return this.httpClient.get<PolicyType[]>(url);
     }
 
-    getPolicyInstances(policyTypeId: number): Observable<PolicyInstance[]> {
+    getPolicyInstances(policyTypeId: string): Observable<PolicyInstance[]> {
         const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath);
         return this.httpClient.get<PolicyInstance[]>(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<any> {
+    getPolicy(policyTypeId: string, policyInstanceId: string): Observable<any> {
         const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId);
         return this.httpClient.get<any>(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<any> {
-        const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId);
+    putPolicy(policyTypeId: string, policyInstanceId: string, policyJson: string, ric: string): Observable<any> {
+        const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId) + "?ric=" + ric;
         return this.httpClient.put<PolicyInstanceAck>(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<any> {
+    deletePolicy(policyTypeId: string, policyInstanceId: string): Observable<any> {
         const url = this.buildPath(this.policyTypePath, policyTypeId, this.policyPath, policyInstanceId);
         return this.httpClient.delete(url, { observe: 'response' });
     }
+
+
+    getRics(policyTypeId: string): Observable<string[]> {
+        const url = this.buildPath('rics') + '?policyType=' + policyTypeId;
+        return this.httpClient.get<any>(url);
+    }
 }
index 193a828..5177088 100644 (file)
@@ -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.
+
index aea9f07..0b98cc5 100644 (file)
@@ -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
index 3f421d3..d33d2f9 100644 (file)
@@ -20,4 +20,4 @@ Non-RT RIC
 
 * :ref:`genindex`
 * :ref:`modindex`
-* :ref:`search`
\ No newline at end of file
+* :ref:`search`
index 14a2490..e4cbc8b 100644 (file)
@@ -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           |
++--------------------------------------+--------------------------------------+
 
old mode 100755 (executable)
new mode 100644 (file)
index 264db96..ba53c6e
@@ -29,3 +29,4 @@ classes
 out/
 .DS_STORE
 .metadata
+__pycache__
index 298b988..03d9a56 100644 (file)
@@ -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 (file)
index b83d222..0000000
+++ /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 (file)
index 4030c1c..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ============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=========================================================
--->
-<project xmlns="http://maven.apache.org/POM/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/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.oran.nearric</groupId>
-               <artifactId>nearric-simulator</artifactId>
-               <version>1.0.0-SNAPSHOT</version>
-               
-       </parent>
-
-       <artifactId>a1-med-api</artifactId>
-       <name>${project.artifactId}</name>
-
-       <properties>
-               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-               <gson-fire-version>1.8.2</gson-fire-version>
-               <threetenbp-version>1.3.6</threetenbp-version>
-               <generated.package.api>org.oransc.ric.a1med.api</generated.package.api>
-       </properties>
-       <dependencies>
-               <dependency>
-                       <groupId>javax.annotation</groupId>
-                       <artifactId>javax.annotation-api</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>io.swagger.core.v3</groupId>
-                       <artifactId>swagger-annotations</artifactId>
-                       <version>2.0.8</version>
-               </dependency>
-               <dependency>
-                       <groupId>io.swagger</groupId>
-                       <artifactId>swagger-codegen-maven-plugin</artifactId>
-                       <version>2.4.8</version>
-               </dependency>
-               <!-- <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> 
-                       <version>2.9.2</version> </dependency> -->
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-core</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-annotations</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-databind</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.jaxrs</groupId>
-                       <artifactId>jackson-jaxrs-json-provider</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.datatype</groupId>
-                       <artifactId>jackson-datatype-jsr310</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>com.squareup.okhttp</groupId>
-                       <artifactId>okhttp</artifactId>
-                       <version>2.7.5</version>
-               </dependency>
-               <dependency>
-                       <groupId>com.squareup.okhttp</groupId>
-                       <artifactId>logging-interceptor</artifactId>
-                       <version>2.7.5</version>
-               </dependency>
-               <dependency>
-                       <groupId>io.gsonfire</groupId>
-                       <artifactId>gson-fire</artifactId>
-                       <version>${gson-fire-version}</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.threeten</groupId>
-                       <artifactId>threetenbp</artifactId>
-                       <version>${threetenbp-version}</version>
-               </dependency>
-       </dependencies>
-       <build>
-               <plugins>
-                       <plugin>
-                               <groupId>io.swagger.codegen.v3</groupId>
-                               <artifactId>swagger-codegen-maven-plugin</artifactId>
-                               <version>3.0.11</version>
-                               <executions>
-                                       <execution>
-                                               <id>generate-sources-server</id>
-                                               <phase>generate-sources</phase>
-                                               <goals>
-                                                       <goal>generate</goal>
-                                               </goals>
-                                               <configuration>
-                                                       <inputSpec>${project.basedir}/src/main/resources/a1_mediator_0.11.0.yaml</inputSpec>
-                                                       <language>java</language>
-                                                       <output>${project.basedir}/target/generated-sources/a1med</output>
-                                                       <configOptions>
-                                                               <apiPackage>${generated.package.api}</apiPackage>
-                                                               <modelPackage>${generated.package.api}.model</modelPackage>
-                                                               <addCompileSourceRoot>true</addCompileSourceRoot>
-                                                               <generateApiTests>true</generateApiTests>
-                                                               <generateModels>true</generateModels>
-                                                               <generateApis>true</generateApis>
-                                                               <generateModelTests>true</generateModelTests>
-                                                               <sourceFolder>src/gen/java/main</sourceFolder>
-                                                               <jackson>true</jackson>
-                                                       </configOptions>
-                                               </configuration>
-                                       </execution>
-                               </executions>
-                       </plugin>
-               </plugins>
-               <pluginManagement>
-                       <plugins>
-                               <plugin>
-                                       <groupId>org.springframework.boot</groupId>
-                                       <artifactId>spring-boot-maven-plugin</artifactId>
-                                       <configuration>
-                                               <skip>true</skip>
-                                       </configuration>
-                               </plugin>
-                               <!--This plugin's configuration is used to store Eclipse m2e settings 
-                                       only. It has no influence on the Maven build itself. -->
-                               <plugin>
-                                       <groupId>org.eclipse.m2e</groupId>
-                                       <artifactId>lifecycle-mapping</artifactId>
-                                       <version>1.0.0</version>
-                                       <configuration>
-                                               <lifecycleMappingMetadata>
-                                                       <pluginExecutions>
-                                                               <pluginExecution>
-                                                                       <pluginExecutionFilter>
-                                                                               <groupId>io.swagger.codegen.v3</groupId>
-                                                                               <artifactId>swagger-codegen-maven-plugin</artifactId>
-                                                                               <versionRange>[1.0,)</versionRange>
-                                                                               <goals>
-                                                                                       <goal>generate</goal>
-                                                                               </goals>
-                                                                       </pluginExecutionFilter>
-                                                                       <action>
-                                                                               <ignore />
-                                                                       </action>
-                                                               </pluginExecution>
-                                                       </pluginExecutions>
-                                               </lifecycleMappingMetadata>
-                                       </configuration>
-                               </plugin>
-                       </plugins>
-               </pluginManagement>
-       </build>
-</project>
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 (file)
index b8b8f91..0000000
+++ /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 (file)
index 0000000..1c5ae4e
--- /dev/null
@@ -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 (executable)
index 0000000..611523e
--- /dev/null
@@ -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 (executable)
index 0000000..80d1219
--- /dev/null
@@ -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 (file)
index 0000000..319137a
--- /dev/null
@@ -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/<testcase id>`. \
+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:
+```
+./<testcase-id>.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="<test case description>"
+
+. ../common/testcase_common.sh $1 
+
+#### TEST BEGIN ####
+
+
+<tests here>
+
+
+#### 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 (file)
index 0000000..37d81ec
--- /dev/null
@@ -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 (executable)
index 0000000..b307a69
--- /dev/null
@@ -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 (executable)
index 0000000..ae6563d
--- /dev/null
@@ -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="<no-description>"
+       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, <file-prefix>"
+               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 "<no-response-from-server>"
+               return 1
+       else
+               if [ $http_code -lt 200 ] && [ $http_code -gt 299]; then
+                       echo "<not found, resp:${http_code}>"
+                       return 1
+               fi
+               if [ $# -eq 2 ]; then
+                       echo "${res:0:${#res}-3}" | xargs
+               else
+                       echo "${res:0:${#res}-3}"
+               fi
+
+               return 0
+       fi
+}
+
index d142bed..dd21d57 100644 (file)
                         <image>
                             <!-- Specify a tag to avoid default tag "latest" -->
                             <!-- Avoid maven artifact name here -->
-                            <name>o-ran-sc/nearric-simulator:${project.version}</name>
+                            <name>o-ran-sc/nonrtric-nearric-simulator:${project.version}</name>
                             <build>
                                 <from>openjdk:11-jre-slim</from>
                                 <tags>
index ce73b7a..4b66634 100644 (file)
   SPDX-License-Identifier: Apache-2.0
   ============LICENSE_END=========================================================
 -->
-<project xmlns="http://maven.apache.org/POM/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">
-       <modelVersion>4.0.0</modelVersion>
-       <groupId>org.oran.nearric</groupId>
-       <artifactId>nearric-simulator</artifactId>
-       <version>1.0.0-SNAPSHOT</version>
-       <packaging>pom</packaging>
-       <name>${project.artifactId}</name>
 
-       <parent>
-               <groupId>org.springframework.boot</groupId>
-               <artifactId>spring-boot-starter-parent</artifactId>
-               <version>2.1.9.RELEASE</version>
-               <relativePath />
-       </parent>
+<project xmlns="http://maven.apache.org/POM/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">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.oran.nearric</groupId>
+  <artifactId>nearric-simulator</artifactId>
+  <version>1.0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <name>${project.artifactId}</name>
 
-       <properties>
-               <spring.boot.version>2.1.9.RELEASE</spring.boot.version>
-        <java.version>11</java.version>
-       </properties>
+  <parent>
+    <groupId>org.springframework.boot</groupId>
+    <artifactId>spring-boot-starter-parent</artifactId>
+    <version>2.1.9.RELEASE</version>
+    <relativePath />
+  </parent>
 
-       <dependencyManagement>
-               <dependencies>
-                       <dependency>
-                               <groupId>org.springframework.boot</groupId>
-                               <artifactId>spring-boot-dependencies</artifactId>
-                               <version>${spring.boot.version}</version>
-                               <type>pom</type>
-                               <scope>import</scope>
-                       </dependency>
-               </dependencies>
-       </dependencyManagement>
+  <properties>
+    <spring.boot.version>2.1.9.RELEASE</spring.boot.version>
+    <java.version>11</java.version>
+    <docker-maven-plugin.version>0.30.0</docker-maven-plugin.version>
+  </properties>
 
-       <dependencies>
-               <dependency>
-                       <groupId>org.springframework</groupId>
-                       <artifactId>spring-context</artifactId>
-               </dependency>
-               <dependency>
-                       <groupId>org.springframework</groupId>
-                       <artifactId>spring-web</artifactId>
-               </dependency>
-       </dependencies>
-       <build>
-               <plugins>
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.1</version>
-                <configuration>
-                    <release>${java.version}</release>
-                </configuration>
-                       </plugin>
-               </plugins>
-       </build>
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-dependencies</artifactId>
+        <version>${spring.boot.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
 
-       <modules>
-               <module>a1-med-api</module>
-               <module>nearric-service</module>
-       </modules>
-</project>
\ No newline at end of file
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <release>${java.version}</release>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>io.fabric8</groupId>
+        <artifactId>docker-maven-plugin</artifactId>
+        <version>${docker-maven-plugin.version}</version>
+        <inherited>false</inherited>
+        <executions>
+          <execution>
+            <id>push-near-rt-ric-simulator-image</id>
+            <goals>
+              <goal>build</goal>
+              <goal>push</goal>
+            </goals>
+            <configuration>
+              <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+              <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
+              <images>
+                <image>
+                  <name>o-ran-sc/near-rt-ric-simulator:${project.version}</name>
+                    <build>
+                      <contextDir>${basedir}/ric-plt/a1</contextDir>
+                      <dockerFile>Dockerfile</dockerFile>
+                      <tags>
+                        <tag>${project.version}</tag>
+                      </tags>
+                    </build>
+                  </image>
+                </images>
+              </configuration>
+            </execution>
+         </executions>
+       </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/near-rt-ric-simulator/ric-plt/a1/Dockerfile b/near-rt-ric-simulator/ric-plt/a1/Dockerfile
new file mode 100644 (file)
index 0000000..db06a21
--- /dev/null
@@ -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 (file)
index 0000000..223d1bd
--- /dev/null
@@ -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 (file)
index 0000000..76f51b8
--- /dev/null
@@ -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 (executable)
index 0000000..4e873db
--- /dev/null
@@ -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 (file)
index 0000000..48b7f2e
--- /dev/null
@@ -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/<string:policyTypeId>', 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('/<string:policyId>/<string:enforceStatus>', 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('/<string:policyId>/<string:enforceStatus>/<string:enforceReason>', 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 (file)
index 0000000..8d1cdd6
--- /dev/null
@@ -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 (file)
index 0000000..74f22c7
--- /dev/null
@@ -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 (file)
index 0000000..f43ca1a
--- /dev/null
@@ -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 (file)
index 0000000..8d2e985
--- /dev/null
@@ -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 (file)
index 0000000..6890d05
--- /dev/null
@@ -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 (file)
index 0000000..257298c
--- /dev/null
@@ -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 (file)
index 0000000..8be17bb
--- /dev/null
@@ -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 (file)
index 0000000..baf1bbe
--- /dev/null
@@ -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 (executable)
index 0000000..9052ecf
--- /dev/null
@@ -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 (file)
index 0000000..03ee527
--- /dev/null
@@ -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 (file)
index 0000000..4dc0f6c
--- /dev/null
@@ -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 (file)
index 0000000..9a9c0c4
--- /dev/null
@@ -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 (file)
index 0000000..f6f3dac
--- /dev/null
@@ -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 (file)
index 0000000..2f7ac3e
--- /dev/null
@@ -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 (executable)
index 0000000..ed4f9e6
--- /dev/null
@@ -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 (executable)
index 0000000..7f83397
--- /dev/null
@@ -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 (file)
index 0000000..ca6a2f3
--- /dev/null
@@ -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 (file)
index 0000000..26ce531
--- /dev/null
@@ -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 (file)
index 0000000..bbd6fcc
--- /dev/null
@@ -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 <path to test_application_configuration.json> 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 <path to test_application_configuration_with_dmaap_config.json> 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 (file)
index 0000000..90b73a4
--- /dev/null
@@ -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 (file)
index 0000000..8f3f80b
--- /dev/null
@@ -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 (file)
index 0000000..e3f44ed
--- /dev/null
@@ -0,0 +1,516 @@
+swagger: '2.0'
+info:
+  description: Api Documentation
+  version: '1.0'
+  title: Api Documentation
+  termsOfService: 'urn:tos'
+  contact: {}
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0'
+host: 'localhost:8081'
+basePath: /
+tags:
+  - name: policy-controller
+    description: Policy Controller
+  - name: ric-repository-controller
+    description: Ric Repository Controller
+  - name: service-controller
+    description: Service Controller
+  - name: status-controller
+    description: Status Controller
+paths:
+  /policies:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns the policies
+      operationId: getPoliciesUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: type
+          in: query
+          description: type
+          required: false
+          type: string
+        - name: ric
+          in: query
+          description: ric
+          required: false
+          type: string
+        - name: service
+          in: query
+          description: service
+          required: false
+          type: string
+      responses:
+        '200':
+          description: Policies
+          schema:
+            type: array
+            items:
+              $ref: '#/definitions/PolicyInfo'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /policy:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns a policy configuration
+      operationId: getPolicyUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: instance
+          in: query
+          description: instance
+          required: true
+          type: string
+      responses:
+        '200':
+          description: Policy found
+          schema:
+            type: object
+        '204':
+          description: Policy is not found
+          schema:
+            type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+    put:
+      tags:
+        - policy-controller
+      summary: Put a policy
+      operationId: putPolicyUsingPUT
+      consumes:
+        - application/json
+      produces:
+        - '*/*'
+      parameters:
+        - name: type
+          in: query
+          description: type
+          required: true
+          type: string
+        - name: instance
+          in: query
+          description: instance
+          required: true
+          type: string
+        - name: ric
+          in: query
+          description: ric
+          required: true
+          type: string
+        - name: service
+          in: query
+          description: service
+          required: true
+          type: string
+        - in: body
+          name: jsonBody
+          description: jsonBody
+          required: true
+          schema:
+            type: object
+      responses:
+        '200':
+          description: Policy created or updated
+          schema:
+            type: string
+        '201':
+          description: Created
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+    delete:
+      tags:
+        - policy-controller
+      summary: Deletes the policy
+      operationId: deletePolicyUsingDELETE
+      produces:
+        - '*/*'
+      parameters:
+        - name: instance
+          in: query
+          description: instance
+          required: true
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            $ref: '#/definitions/Mono«ResponseEntity«Void»»'
+        '204':
+          description: Policy deleted
+          schema:
+            $ref: '#/definitions/Mono«ResponseEntity«Void»»'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+  /policy_schema:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns one policy type schema definition
+      operationId: getPolicySchemaUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: id
+          in: query
+          description: id
+          required: true
+          type: string
+      responses:
+        '200':
+          description: Policy schema
+          schema:
+            type: object
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /policy_schemas:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns policy type schema definitions
+      operationId: getPolicySchemasUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: ric
+          in: query
+          description: ric
+          required: false
+          type: string
+      responses:
+        '200':
+          description: Policy schemas
+          schema:
+            type: array
+            items:
+              type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /policy_types:
+    get:
+      tags:
+        - policy-controller
+      summary: Returns policy types
+      operationId: getPolicyTypesUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: ric
+          in: query
+          description: ric
+          required: false
+          type: string
+      responses:
+        '200':
+          description: Policy type names
+          schema:
+            type: array
+            items:
+              type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /ric:
+    get:
+      tags:
+        - ric-repository-controller
+      summary: Returns the name of a RIC managing one Mananged Element
+      operationId: getRicUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: managedElementId
+          in: query
+          description: managedElementId
+          required: false
+          type: string
+      responses:
+        '200':
+          description: RIC is fond
+          schema:
+            type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: RIC is not fond
+          schema:
+            type: string
+  /rics:
+    get:
+      tags:
+        - ric-repository-controller
+      summary: Returns NearRT RIC information
+      operationId: getRicsUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: policyType
+          in: query
+          description: policyType
+          required: false
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: array
+            items:
+              $ref: '#/definitions/RicInfo'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /service:
+    put:
+      tags:
+        - service-controller
+      summary: Register a service
+      operationId: putServiceUsingPUT
+      consumes:
+        - application/json
+      produces:
+        - '*/*'
+      parameters:
+        - in: body
+          name: registrationInfo
+          description: registrationInfo
+          required: true
+          schema:
+            $ref: '#/definitions/ServiceRegistrationInfo'
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: string
+        '201':
+          description: Created
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+  /services:
+    get:
+      tags:
+        - service-controller
+      summary: Returns service information
+      operationId: getServicesUsingGET
+      produces:
+        - '*/*'
+      parameters:
+        - name: name
+          in: query
+          description: name
+          required: false
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: array
+            items:
+              $ref: '#/definitions/ServiceStatus'
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+    delete:
+      tags:
+        - service-controller
+      summary: Delete a service
+      operationId: deleteServiceUsingDELETE
+      produces:
+        - '*/*'
+      parameters:
+        - name: name
+          in: query
+          description: name
+          required: true
+          type: string
+      responses:
+        '200':
+          description: OK
+          schema:
+            type: string
+        '204':
+          description: No Content
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+  /services/keepalive:
+    post:
+      tags:
+        - service-controller
+      summary: Keep the poilicies alive for a service
+      operationId: keepAliveServiceUsingPOST
+      consumes:
+        - application/json
+      produces:
+        - '*/*'
+      parameters:
+        - name: name
+          in: query
+          description: name
+          required: true
+          type: string
+      responses:
+        '200':
+          description: Policies timeout supervision refreshed
+          schema:
+            type: string
+        '201':
+          description: Created
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: 'The service is not found, needs re-registration'
+  /status:
+    get:
+      tags:
+        - status-controller
+      summary: Returns status and statistics of the service
+      operationId: getStatusUsingGET
+      produces:
+        - '*/*'
+      responses:
+        '200':
+          description: Service is living
+          schema:
+            type: string
+        '401':
+          description: Unauthorized
+        '403':
+          description: Forbidden
+        '404':
+          description: Not Found
+definitions:
+  Mono«ResponseEntity«Void»»:
+    type: object
+    title: Mono«ResponseEntity«Void»»
+  Mono«ResponseEntity«string»»:
+    type: object
+    title: Mono«ResponseEntity«string»»
+  PolicyInfo:
+    type: object
+    properties:
+      id:
+        type: string
+        description: identity of the policy
+        allowEmptyValue: false
+      json:
+        type: string
+        description: the configuration of the policy
+        allowEmptyValue: false
+      lastModified:
+        type: string
+        description: 'timestamp, last modification time'
+        allowEmptyValue: false
+      ric:
+        type: string
+        description: identity the target NearRT RIC
+        allowEmptyValue: false
+      service:
+        type: string
+        description: the name of the service owning the policy
+        allowEmptyValue: false
+      type:
+        type: string
+        description: name of the policy type
+        allowEmptyValue: false
+    title: PolicyInfo
+  RicInfo:
+    type: object
+    properties:
+      managedElementIds:
+        type: array
+        description: O1 identities for managed entities
+        allowEmptyValue: false
+        items:
+          type: string
+      name:
+        type: string
+        description: identity of the ric
+        allowEmptyValue: false
+      policyTypes:
+        type: array
+        description: supported policy types
+        allowEmptyValue: false
+        items:
+          type: string
+    title: RicInfo
+  ServiceRegistrationInfo:
+    type: object
+    properties:
+      callbackUrl:
+        type: string
+        description: callback for notifying of RIC recovery
+        allowEmptyValue: false
+      keepAliveIntervalSeconds:
+        type: integer
+        format: int64
+        description: keep alive interval for policies owned by the service. 0 means no timeout supervision. Polcies that are not refreshed within this time are removed
+        allowEmptyValue: false
+      name:
+        type: string
+        description: identity of the service
+        allowEmptyValue: false
+    title: ServiceRegistrationInfo
+  ServiceStatus:
+    type: object
+    properties:
+      keepAliveIntervalSeconds:
+        type: integer
+        format: int64
+        description: policy keep alive timeout
+        allowEmptyValue: false
+      name:
+        type: string
+        description: identity of the service
+        allowEmptyValue: false
+      timeSincePingSeconds:
+        type: integer
+        format: int64
+        description: time since last invocation by the service
+        allowEmptyValue: false
+    title: ServiceStatus
+
diff --git a/policy-agent/dpo/blueprints/k8s-policy-agent.yaml b/policy-agent/dpo/blueprints/k8s-policy-agent.yaml
new file mode 100644 (file)
index 0000000..0121d27
--- /dev/null
@@ -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 (file)
index 0000000..7339434
--- /dev/null
@@ -0,0 +1,315 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ * ========================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===================================
+  -->
+<profiles version="13">
+<profile kind="CodeFormatterProfile" name="java-formatter" version="12">
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+</profile>
+</profiles>
+
diff --git a/policy-agent/pom.xml b/policy-agent/pom.xml
new file mode 100644 (file)
index 0000000..311229f
--- /dev/null
@@ -0,0 +1,325 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ========================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===================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.springframework.boot</groupId>
+               <artifactId>spring-boot-starter-parent</artifactId>
+               <version>2.2.4.RELEASE</version>
+               <relativePath />
+       </parent>
+       <groupId>org.o-ran-sc.nonrtric</groupId>
+       <artifactId>policy-agent</artifactId>
+       <version>1.0.0-SNAPSHOT</version>
+       <licenses>
+               <license>
+                       <name>The Apache Software License, Version 2.0</name>
+                       <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+               </license>
+       </licenses>
+       <repositories>
+               <repository>
+                       <id>onap-releases</id>
+                       <name>onap-releases</name>
+                       <url>https://nexus.onap.org/content/repositories/releases/</url>
+               </repository>
+       </repositories>
+       <properties>
+               <java.version>11</java.version>
+               <springfox.version>2.8.0</springfox.version>
+               <immutable.version>2.7.1</immutable.version>
+               <sdk.version>1.1.6</sdk.version>
+               <swagger.version>2.0.0</swagger.version>
+               <json.version>20180130</json.version>
+        <commons-net.version>3.3</commons-net.version>
+               <awaitility.version>4.0.1</awaitility.version>
+               <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+               <formatter-maven-plugin.version>2.8.1</formatter-maven-plugin.version>
+               <spotless-maven-plugin.version>1.18.0</spotless-maven-plugin.version>
+               <docker-maven-plugin>0.30.0</docker-maven-plugin>
+               <version.dmaap>1.1.9</version.dmaap>
+        <javax.ws.rs-api.version>2.1.1</javax.ws.rs-api.version>
+       </properties>
+       <dependencies>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-web</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-thymeleaf</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-webflux</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-devtools</artifactId>
+                       <optional>true</optional>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework</groupId>
+                       <artifactId>spring-webflux</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>io.swagger.core.v3</groupId>
+                       <artifactId>swagger-jaxrs2</artifactId>
+                       <version>${swagger.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>io.swagger.core.v3</groupId>
+                       <artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
+                       <version>${swagger.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>javax.xml.bind</groupId>
+                       <artifactId>jaxb-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.immutables</groupId>
+                       <artifactId>value</artifactId>
+                       <version>${immutable.version}</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.immutables</groupId>
+                       <artifactId>gson</artifactId>
+                       <version>${immutable.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.json</groupId>
+                       <artifactId>json</artifactId>
+                       <version>${json.version}</version>
+               </dependency>
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+            <version>${commons-net.version}</version>
+        </dependency>
+               <!--TEST -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-test</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-configuration-processor</artifactId>
+                       <optional>true</optional>
+               </dependency>
+               <dependency>
+                       <groupId>org.onap.dcaegen2.services.sdk.rest.services</groupId>
+                       <artifactId>cbs-client</artifactId>
+            <version>${sdk.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.dcaegen2.services.sdk.rest.services</groupId>
+            <artifactId>dmaap-client</artifactId>
+                       <version>${sdk.version}</version>
+               </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+               <!--REQUIRED TO GENERATE DOCUMENTATION -->
+               <dependency>
+                       <groupId>io.springfox</groupId>
+                       <artifactId>springfox-swagger2</artifactId>
+                       <version>${springfox.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>io.springfox</groupId>
+                       <artifactId>springfox-swagger-ui</artifactId>
+                       <version>${springfox.version}</version>
+               </dependency>
+               <!-- TEST -->
+               <dependency>
+                       <groupId>org.awaitility</groupId>
+                       <artifactId>awaitility</artifactId>
+                       <version>${awaitility.version}</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>io.projectreactor</groupId>
+                       <artifactId>reactor-test</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.junit.jupiter</groupId>
+                       <artifactId>junit-jupiter-engine</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.mockito</groupId>
+                       <artifactId>mockito-junit-jupiter</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.mockito</groupId>
+                       <artifactId>mockito-core</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.onap.dmaap.messagerouter.dmaapclient</groupId>
+                       <artifactId>dmaapClient</artifactId>
+                       <version>${version.dmaap}</version>
+               </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <version>${javax.ws.rs-api.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+       </dependencies>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-maven-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>net.revelc.code.formatter</groupId>
+                               <artifactId>formatter-maven-plugin</artifactId>
+                               <version>${formatter-maven-plugin.version}</version>
+                               <configuration>
+                                       <configFile>${project.basedir}/eclipse-formatter.xml</configFile>
+                               </configuration>
+                               <!-- https://code.revelc.net/formatter-maven-plugin/ use mvn formatter:format 
+                                       spotless:apply process-sources -->
+                       </plugin>
+                       <plugin>
+                               <groupId>com.diffplug.spotless</groupId>
+                               <artifactId>spotless-maven-plugin</artifactId>
+                               <version>${spotless-maven-plugin.version}</version>
+                               <configuration>
+                                       <java>
+                                               <removeUnusedImports />
+                                               <importOrder>
+                                                       <order>com,java,javax,org</order>
+                                               </importOrder>
+                                       </java>
+                               </configuration>
+                               <!-- https://github.com/diffplug/spotless/tree/master/plugin-maven use 
+                                       mvn spotless:apply to rewrite source files use mvn spotless:check to validate 
+                                       source files -->
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-surefire-plugin</artifactId>
+                               <configuration>
+                                       <skipTests>false</skipTests>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-failsafe-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.codehaus.mojo</groupId>
+                               <artifactId>build-helper-maven-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>add-source</id>
+                                               <phase>generate-sources</phase>
+                                               <goals>
+                                                       <goal>add-source</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <sources>
+                                                               <source>${project.build.directory}/generated-sources/annotations/</source>
+                                                       </sources>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>io.fabric8</groupId>
+                               <artifactId>docker-maven-plugin</artifactId>
+                               <version>${docker-maven-plugin}</version>
+                               <inherited>false</inherited>
+                               <executions>
+                                       <execution>
+                                               <id>generate-policy-agent-image</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>build</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                                                       <images>
+                                                               <image>
+                                                                       <name>o-ran-sc/nonrtric-policy-agent:${project.version}</name>
+                                                                       <build>
+                                                                               <cleanup>try</cleanup>
+                                                                               <contextDir>${basedir}</contextDir>
+                                                                               <dockerFile>Dockerfile</dockerFile>
+                                                                               <args>
+                                                                                       <JAR>${project.build.finalName}.jar</JAR>
+                                                                               </args>
+                                                                               <tags>
+                                                                                       <tag>${project.version}</tag>
+                                                                               </tags>
+                                                                       </build>
+                                                               </image>
+                                                       </images>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
+                                               <id>push-policy-agent-image</id>
+                                               <goals>
+                                                       <goal>build</goal>
+                                                       <goal>push</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                                                       <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
+                                                       <images>
+                                                               <image>
+                                                                       <name>o-ran-sc/nonrtric-policy-agent:${project.version}</name>
+                                                                       <build>
+                                                                               <contextDir>${basedir}</contextDir>
+                                                                               <dockerFile>Dockerfile</dockerFile>
+                                                                               <args>
+                                                                                       <JAR>${project.build.finalName}.jar</JAR>
+                                                                               </args>
+                                                                               <tags>
+                                                                                       <tag>${project.version}</tag>
+                                                                               </tags>
+                                                                       </build>
+                                                               </image>
+                                                       </images>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <issueManagement>
+               <system>JIRA</system>
+               <url>https://jira.o-ran-sc.org/</url>
+       </issueManagement>
+</project>
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 (file)
index 0000000..6d8cd99
--- /dev/null
@@ -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 (file)
index 0000000..8297b22
--- /dev/null
@@ -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 (file)
index 0000000..f3b4a5a
--- /dev/null
@@ -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 (file)
index 0000000..0a2bc8e
--- /dev/null
@@ -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<A1ProtocolType> getProtocolVersion();
+
+    public Mono<List<String>> getPolicyTypeIdentities();
+
+    public Mono<List<String>> getPolicyIdentities();
+
+    public Mono<String> getPolicyTypeSchema(String policyTypeId);
+
+    public Mono<String> putPolicy(Policy policy);
+
+    public Mono<String> deletePolicy(Policy policy);
+
+    public Flux<String> 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 (file)
index 0000000..c150c08
--- /dev/null
@@ -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<A1Client> createA1Client(Ric ric) {
+        return getProtocolVersion(ric) //
+            .flatMap(version -> createA1Client(ric, version));
+    }
+
+    private Mono<A1Client> 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<A1Client.A1ProtocolType> 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<A1Client.A1ProtocolType> 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 (file)
index 0000000..b10cf27
--- /dev/null
@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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 (file)
index 0000000..dd77504
--- /dev/null
@@ -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<List<String>> getPolicyTypeIdentities() {
+        return restClient.get("/policytypes") //
+            .flatMap(this::parseJsonArrayOfString);
+    }
+
+    @Override
+    public Mono<List<String>> getPolicyIdentities() {
+        return getPolicyTypeIdentities() //
+            .flatMapMany(types -> Flux.fromIterable(types)) //
+            .flatMap(type -> getPolicyIdentities(type)) //
+            .flatMap(policyIds -> Flux.fromIterable(policyIds)) //
+            .collectList();
+    }
+
+    private Mono<List<String>> getPolicyIdentities(String typeId) {
+        return restClient.get("/policytypes/" + typeId + "/policies") //
+            .flatMap(this::parseJsonArrayOfString);
+    }
+
+    @Override
+    public Mono<String> getPolicyTypeSchema(String policyTypeId) {
+        return restClient.get("/policytypes/" + policyTypeId) //
+            .flatMap(response -> getCreateSchema(response, policyTypeId));
+    }
+
+    private Mono<String> 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<String> putPolicy(Policy policy) {
+        return restClient.put("/policytypes/" + policy.type().name() + "/policies/" + policy.id(), policy.json());
+    }
+
+    @Override
+    public Mono<String> deletePolicy(Policy policy) {
+        return deletePolicy(policy.type().name(), policy.id());
+    }
+
+    private Mono<String> deletePolicy(String typeId, String policyId) {
+        return restClient.delete("/policytypes/" + typeId + "/policies/" + policyId);
+    }
+
+    @Override
+    public Mono<A1ProtocolType> getProtocolVersion() {
+        return restClient.get("/healthcheck") //
+            .flatMap(resp -> Mono.just(A1ProtocolType.OSC_V1));
+    }
+
+    @Override
+    public Flux<String> deleteAllPolicies() {
+        return getPolicyTypeIdentities() //
+            .flatMapMany(types -> Flux.fromIterable(types)) //
+            .flatMap(typeId -> deletePoliciesForType(typeId)); //
+    }
+
+    private Flux<String> deletePoliciesForType(String typeId) {
+        return getPolicyIdentities(typeId) //
+            .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) //
+            .flatMap(policyId -> deletePolicy(typeId, policyId)); //
+    }
+
+    private Mono<List<String>> parseJsonArrayOfString(String inputString) {
+        try {
+            List<String> 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 (file)
index 0000000..2f8fab4
--- /dev/null
@@ -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<List<String>> 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<List<String>> 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<String> 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<String> 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<String> deletePolicy(Policy policy) {
+        return deletePolicy(policy.id());
+    }
+
+    @Override
+    public Flux<String> deleteAllPolicies() {
+        return getPolicyIdentities() //
+            .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) // )
+            .flatMap(policyId -> deletePolicy(policyId)); //
+    }
+
+    public Mono<String> 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<A1ProtocolType> 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<String> 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<List<String>> parseJsonArrayOfString(String inputString) {
+        try {
+            List<String> 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<String> 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<String> 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 (file)
index 0000000..1993be7
--- /dev/null
@@ -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<List<String>> 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<List<String>> getPolicyIdentities() {
+        return getPolicyTypeIdentities() //
+            .flatMapMany(types -> Flux.fromIterable(types)) //
+            .flatMap(type -> getPolicyIdentities(type)) //
+            .flatMap(policyIds -> Flux.fromIterable(policyIds)) //
+            .collectList();
+    }
+
+    public Mono<List<String>> 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<String> 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<String> 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<String> 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<String> deletePolicy(Policy policy) {
+        return deletePolicy(policy.type().name(), policy.id());
+    }
+
+    @Override
+    public Flux<String> deleteAllPolicies() {
+        return getPolicyTypeIdentities() //
+            .flatMapMany(types -> Flux.fromIterable(types)) //
+            .flatMap(typeId -> deletePoliciesForType(typeId)); //
+    }
+
+    private Flux<String> deletePoliciesForType(String typeId) {
+        return getPolicyIdentities(typeId) //
+            .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) //
+            .flatMap(policyId -> deletePolicy(typeId, policyId)); //
+    }
+
+    @Override
+    public Mono<A1ProtocolType> 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<String> 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<List<String>> parseJsonArrayOfString(String inputString) {
+        try {
+            List<String> 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<String> 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 (file)
index 0000000..1c40308
--- /dev/null
@@ -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<List<String>> getPolicyIdentities() {
+        return restClient.get("/policies") //
+            .flatMap(this::parseJsonArrayOfString);
+    }
+
+    @Override
+    public Mono<String> putPolicy(Policy policy) {
+        String url = "/policies/" + policy.id() + "?policyTypeId=" + policy.type().name();
+        return restClient.put(url, policy.json()) //
+            .flatMap(this::validateJson);
+    }
+
+    @Override
+    public Mono<List<String>> getPolicyTypeIdentities() {
+        return restClient.get("/policytypes") //
+            .flatMap(this::parseJsonArrayOfString);
+    }
+
+    @Override
+    public Mono<String> getPolicyTypeSchema(String policyTypeId) {
+        return restClient.get("/policytypes/" + policyTypeId) //
+            .flatMap(this::extractPolicySchema);
+    }
+
+    @Override
+    public Mono<String> deletePolicy(Policy policy) {
+        return deletePolicy(policy.id());
+    }
+
+    @Override
+    public Flux<String> deleteAllPolicies() {
+        return getPolicyIdentities() //
+            .flatMapMany(policyIds -> Flux.fromIterable(policyIds)) // )
+            .flatMap(policyId -> deletePolicy(policyId)); //
+    }
+
+    @Override
+    public Mono<A1ProtocolType> getProtocolVersion() {
+        return getPolicyTypeIdentities() //
+            .flatMap(x -> Mono.just(A1ProtocolType.STD_V1));
+    }
+
+    private Mono<String> deletePolicy(String policyId) {
+        return restClient.delete("/policies/" + policyId);
+    }
+
+    private Mono<List<String>> parseJsonArrayOfString(String inputString) {
+        try {
+            List<String> 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<String> 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<String> 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 (file)
index 0000000..1e8c6d4
--- /dev/null
@@ -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<Observer> observers = new Vector<>();
+    private Map<String, RicConfig> 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<RicConfig> 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<RicConfig> ricConfigs, Properties dmaapPublisherConfig,
+        Properties dmaapConsumerConfig) {
+        Collection<Notification> notifications = new Vector<>();
+        synchronized (this) {
+            Map<String, RicConfig> 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<Notification> 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 (file)
index 0000000..1ffe9c8
--- /dev/null
@@ -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<RicConfig> 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<RicConfig> parseRics(JsonObject config) throws ServiceException {
+        Vector<RicConfig> result = new Vector<RicConfig>();
+        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<Entry<String, JsonElement>> 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 (file)
index 0000000..882f30c
--- /dev/null
@@ -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 (file)
index 0000000..d446f94
--- /dev/null
@@ -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<String> 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 (file)
index 0000000..12deb81
--- /dev/null
@@ -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<String> getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) {
+        synchronized (this.policyTypes) {
+            if (ricName == null) {
+                Collection<PolicyType> types = this.policyTypes.getAll();
+                return new ResponseEntity<String>(toPolicyTypeSchemasJson(types), HttpStatus.OK);
+            } else {
+                try {
+                    Collection<PolicyType> types = rics.getRic(ricName).getSupportedPolicyTypes();
+                    return new ResponseEntity<String>(toPolicyTypeSchemasJson(types), HttpStatus.OK);
+                } catch (ServiceException e) {
+                    return new ResponseEntity<String>(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<String> getPolicySchema(@RequestParam(name = "id", required = true) String id) {
+        try {
+            PolicyType type = policyTypes.getType(id);
+            return new ResponseEntity<String>(type.schema(), HttpStatus.OK);
+        } catch (ServiceException e) {
+            return new ResponseEntity<String>(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<String> getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) {
+        synchronized (this.policyTypes) {
+            if (ricName == null) {
+                Collection<PolicyType> types = this.policyTypes.getAll();
+                return new ResponseEntity<String>(toPolicyTypeIdsJson(types), HttpStatus.OK);
+            } else {
+                try {
+                    Collection<PolicyType> types = rics.getRic(ricName).getSupportedPolicyTypes();
+                    return new ResponseEntity<String>(toPolicyTypeIdsJson(types), HttpStatus.OK);
+                } catch (ServiceException e) {
+                    return new ResponseEntity<String>(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<String> getPolicy( //
+        @RequestParam(name = "instance", required = true) String instance) {
+        try {
+            Policy p = policies.getPolicy(instance);
+            return new ResponseEntity<String>(p.json(), HttpStatus.OK);
+        } catch (ServiceException e) {
+            return new ResponseEntity<String>(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<ResponseEntity<Void>> 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<ResponseEntity<String>> putPolicy( //
+        @RequestParam(name = "type", required = true) String typeName, //
+        @RequestParam(name = "instance", required = true) String instanceId, //
+        @RequestParam(name = "ric", required = true) String ricName, //
+        @RequestParam(name = "service", required = true) String service, //
+        @RequestBody 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<String> 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<Policy> 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<String>(policiesToJson(result), HttpStatus.OK);
+        }
+    }
+
+    private boolean include(String filter, String value) {
+        return filter == null || value.equals(filter);
+    }
+
+    private Collection<Policy> filter(Collection<Policy> collection, String type, String ric, String service) {
+        if (type == null && ric == null && service == null) {
+            return collection;
+        }
+        Vector<Policy> 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<Policy> policies) {
+        Vector<PolicyInfo> 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<PolicyType> 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<PolicyType> types) {
+        Vector<String> 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 (file)
index 0000000..bd13062
--- /dev/null
@@ -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 (file)
index 0000000..73103b8
--- /dev/null
@@ -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<String> managedElementIds;
+
+    @ApiModelProperty(value = "supported policy types")
+    public final Collection<String> policyTypes;
+
+    RicInfo(String name, Collection<String> managedElementIds, Collection<String> 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 (file)
index 0000000..a566781
--- /dev/null
@@ -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<String> getRic(
+        @RequestParam(name = "managedElementId", required = false, defaultValue = "") String managedElementId) {
+
+        Optional<Ric> 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<String> getRics(
+        @RequestParam(name = "policyType", required = false) String supportingPolicyType) {
+
+        Vector<RicInfo> 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 (file)
index 0000000..012d40a
--- /dev/null
@@ -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<String> getServices( //
+        @RequestParam(name = "name", required = false) String name) {
+
+        Collection<ServiceStatus> 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<String>(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<String> putService( //
+        @RequestBody ServiceRegistrationInfo registrationInfo) {
+        try {
+            this.services.put(toService(registrationInfo));
+            return new ResponseEntity<String>("OK", HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
+        }
+    }
+
+    @ApiOperation(value = "Delete a service")
+    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")})
+    @DeleteMapping("/services")
+    public ResponseEntity<String> deleteService( //
+        @RequestParam(name = "name", required = true) String name) {
+        try {
+            Service service = removeService(name);
+            // Remove the policies from the repo and let the consistency monitoring
+            // do the rest.
+            removePolicies(service);
+            return new ResponseEntity<String>("OK", HttpStatus.NO_CONTENT);
+        } catch (Exception e) {
+            return new ResponseEntity<String>(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<String> keepAliveService( //
+        @RequestParam(name = "name", required = true) String name) {
+        try {
+            services.getService(name).ping();
+            return new ResponseEntity<String>("OK", HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<String>(e.getMessage(), HttpStatus.NOT_FOUND);
+        }
+    }
+
+    private Service removeService(String name) throws ServiceException {
+        synchronized (this.services) {
+            Service service = this.services.getService(name);
+            this.services.remove(service.getName());
+            return service;
+        }
+    }
+
+    private void removePolicies(Service service) {
+        synchronized (this.policies) {
+            Vector<Policy> 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 (file)
index 0000000..145fdb0
--- /dev/null
@@ -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 (file)
index 0000000..c30cce1
--- /dev/null
@@ -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 (file)
index 0000000..1219a0d
--- /dev/null
@@ -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<ResponseEntity<String>> getStatus() {
+        Mono<ResponseEntity<String>> 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 (file)
index 0000000..c132fa2
--- /dev/null
@@ -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 (file)
index 0000000..db4956a
--- /dev/null
@@ -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<String> 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<String> 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 (file)
index 0000000..2d963c3
--- /dev/null
@@ -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<String> 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<String> 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<String> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
+        DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation();
+        Mono<String> 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<String> sendDmaapResponse(String response, DmaapRequestMessage dmaapRequestMessage,
+            HttpStatus status) {
+        return getDmaapResponseMessage(dmaapRequestMessage, response, status) //
+                .flatMap(body -> sendToDmaap(body)) //
+                .onErrorResume(t -> handleResponseCallError(t, dmaapRequestMessage));
+    }
+
+    private Mono<String> 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<String> handleResponseCallError(Throwable t, DmaapRequestMessage dmaapRequestMessage) {
+        logger.debug("Failed to respond: " + t.getMessage());
+        return Mono.empty();
+    }
+
+    private Mono<String> 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 (file)
index 0000000..fe48aec
--- /dev/null
@@ -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 (file)
index 0000000..7b8bb34
--- /dev/null
@@ -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 (file)
index 0000000..df6f7de
--- /dev/null
@@ -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 (file)
index 0000000..c3443ed
--- /dev/null
@@ -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 (file)
index 0000000..a279db5
--- /dev/null
@@ -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<String, Policy> policiesId = new HashMap<>();
+    private Map<String, Map<String, Policy>> policiesRic = new HashMap<>();
+    private Map<String, Map<String, Policy>> policiesService = new HashMap<>();
+    private Map<String, Map<String, Policy>> 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<String, Map<String, Policy>> multiMap, String key, Policy value) {
+        Map<String, Policy> map = multiMap.get(key);
+        if (map == null) {
+            map = new HashMap<>();
+            multiMap.put(key, map);
+        }
+        map.put(value.id(), value);
+    }
+
+    private void multiMapRemove(Map<String, Map<String, Policy>> multiMap, String key, Policy value) {
+        Map<String, Policy> map = multiMap.get(key);
+        if (map != null) {
+            map.remove(value.id());
+            if (map.isEmpty()) {
+                multiMap.remove(key);
+            }
+        }
+    }
+
+    private Collection<Policy> multiMapGet(Map<String, Map<String, Policy>> multiMap, String key) {
+        Map<String, Policy> map = multiMap.get(key);
+        if (map == null) {
+            return new Vector<Policy>();
+        }
+        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<Policy> getAll() {
+        return Collections.unmodifiableCollection(policiesId.values());
+    }
+
+    public synchronized Collection<Policy> getForService(String service) {
+        return multiMapGet(policiesService, service);
+    }
+
+    public synchronized Collection<Policy> getForRic(String ric) {
+        return multiMapGet(policiesRic, ric);
+    }
+
+    public synchronized Collection<Policy> 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<String> 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 (file)
index 0000000..5148226
--- /dev/null
@@ -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 (file)
index 0000000..41b9498
--- /dev/null
@@ -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 (file)
index 0000000..7723983
--- /dev/null
@@ -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<String, PolicyType> types = new HashMap<String, PolicyType>();
+
+    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<PolicyType> 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 (file)
index 0000000..220477f
--- /dev/null
@@ -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<String, PolicyType> 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<String> 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<PolicyType> getSupportedPolicyTypes() {
+        return supportedPolicyTypes.values();
+    }
+
+    public Collection<String> 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 (file)
index 0000000..6f3b3e8
--- /dev/null
@@ -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<String, Ric> rics = new HashMap<>();
+
+    public synchronized void put(Ric ric) {
+        rics.put(ric.name(), ric);
+    }
+
+    public synchronized Iterable<Ric> 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<Ric> 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 (file)
index 0000000..f0863a5
--- /dev/null
@@ -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 (file)
index 0000000..369b258
--- /dev/null
@@ -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<String, Service> 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<Service> 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 (file)
index 0000000..574656f
--- /dev/null
@@ -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<EnvProperties> 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 (file)
index 0000000..267fc1f
--- /dev/null
@@ -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<ApplicationConfig> createRefreshTask() {
+        return getEnvironment(systemEnvironment) //
+            .flatMap(this::createCbsClient) //
+            .flatMapMany(this::periodicConfigurationUpdates) //
+            .map(this::parseRicConfigurationfromConsul) //
+            .onErrorResume(this::onErrorResume);
+    }
+
+    Mono<EnvProperties> getEnvironment(Properties systemEnvironment) {
+        return EnvironmentProcessor.readEnvironmentVariables(systemEnvironment);
+    }
+
+    Mono<CbsClient> createCbsClient(EnvProperties env) {
+        return CbsClientFactory.createCbsClient(env);
+    }
+
+    private Flux<JsonObject> 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 <R> Mono<R> 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 (file)
index 0000000..d95272b
--- /dev/null
@@ -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<RicData> 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<RicData> createRicData(Ric ric) {
+        return Mono.just(ric) //
+            .flatMap(aRic -> this.a1ClientFactory.createA1Client(ric)) //
+            .flatMap(a1Client -> Mono.just(new RicData(ric, a1Client)));
+    }
+
+    private Mono<RicData> 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<RicData> checkRicPolicies(RicData ric) {
+        return ric.a1Client.getPolicyIdentities() //
+            .onErrorResume(t -> Mono.empty()) //
+            .flatMap(ricP -> validateInstances(ricP, ric));
+    }
+
+    private Mono<RicData> validateInstances(Collection<String> 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<RicData> checkRicPolicyTypes(RicData ric) {
+        return ric.a1Client.getPolicyTypeIdentities() //
+            .onErrorResume(t -> {
+                return Mono.empty();
+            }) //
+            .flatMap(ricTypes -> validateTypes(ricTypes, ric));
+    }
+
+    private Mono<RicData> validateTypes(Collection<String> 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<RicData> 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 (file)
index 0000000..ab25eab
--- /dev/null
@@ -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<Object> startRecover(Ric ric, A1Client a1Client) {
+        Flux<PolicyType> 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<PolicyType> 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<PolicyType> 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<PolicyType> 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<PolicyType> 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<String> 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 (file)
index 0000000..d4b32e0
--- /dev/null
@@ -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<Policy> 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<Policy> getAllPolicies(Service service) {
+        synchronized (policies) {
+            return Flux.fromIterable(policies.getForService(service.getName()));
+        }
+    }
+
+    private Mono<Policy> deletePolicyInRic(Policy policy) {
+        return a1ClientFactory.createA1Client(policy.ric()) //
+            .flatMap(client -> client.deletePolicy(policy) //
+                .onErrorResume(exception -> handleDeleteFromRicFailure(policy, exception)) //
+                .map((nothing) -> policy));
+    }
+
+    private Mono<String> 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 (file)
index 0000000..54fd79f
--- /dev/null
@@ -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 (file)
index 0000000..574a585
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 (file)
index 0000000..847cde1
--- /dev/null
@@ -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<String> 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<String> createJsonHttpEntity(String content) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return new HttpEntity<String>(content, headers);
+    }
+
+    private void putService(String name) {
+        String url = baseUrl() + "/service";
+        HttpEntity<String> entity = createJsonHttpEntity(createServiceJson(name));
+        this.restTemplate.put(url, entity);
+    }
+
+    private String jsonString() {
+        return "{\n  \"servingCellNrcgi\": \"1\"\n }";
+    }
+
+    private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
+        addRic(ric);
+        Policy p = ImmutablePolicy.builder().id(id) //
+            .json(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<String> 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<String> 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<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
+        assertThat(info).size().isEqualTo(1);
+        PolicyInfo policyInfo = info.get(0);
+        assert (policyInfo.validate());
+        assertThat(policyInfo.id).isEqualTo("id1");
+        assertThat(policyInfo.type).isEqualTo("type1");
+        assertThat(policyInfo.service).isEqualTo("service1");
+    }
+
+    @Test
+    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<ServiceStatus> 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<String> entity = this.restTemplate.postForEntity(url, null, String.class);
+        assertThat(entity.getStatusCode().equals(HttpStatus.NOT_FOUND));
+    }
+
+    private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
+        List<T> 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<String> parseSchemas(String jsonString) {
+        JsonArray arrayOfSchema = JsonParser.parseString(jsonString).getAsJsonArray();
+        List<String> 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 (file)
index 0000000..1ea677c
--- /dev/null
@@ -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 (file)
index 0000000..05e5463
--- /dev/null
@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String>(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 (file)
index 0000000..3444540
--- /dev/null
@@ -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 (file)
index 0000000..257776d
--- /dev/null
@@ -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 (file)
index 0000000..5aeb240
--- /dev/null
@@ -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 (file)
index 0000000..90b3847
--- /dev/null
@@ -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<String>(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<RicConfig> 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<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class);
+        Flux<ApplicationConfig> 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<JsonObject> err = Flux.error(new IOException());
+        doReturn(err).when(cbsClient).updates(any(), any(), any());
+
+        final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class);
+        Flux<ApplicationConfig> 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<JsonObject> json = Flux.just(getJsonRootObject());
+        doReturn(json).when(cbsClient).updates(any(), any(), any());
+
+        Flux<ApplicationConfig> 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 (file)
index 0000000..fb8d46f
--- /dev/null
@@ -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<String>(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<String>(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<String>(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<List<String>> 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 (file)
index 0000000..8bf705c
--- /dev/null
@@ -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<List<String>> policyTypes1 = Mono.just(Arrays.asList(POLICY_TYPE_1_NAME));
+        Mono<List<String>> 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<List<String>> 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 <T> Vector<T> toVector(T... objs) {
+        Vector<T> 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 (file)
index 0000000..a822bb3
--- /dev/null
@@ -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<ILoggingEvent> 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<ILoggingEvent> getLogListAppender(Class<?> logClass, boolean allLevels) {
+        Logger logger = (Logger) LoggerFactory.getLogger(logClass);
+        if (allLevels) {
+            logger.setLevel(Level.ALL);
+        }
+        ListAppender<ILoggingEvent> 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 (file)
index 0000000..1151dd2
--- /dev/null
@@ -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<List<String>> getPolicyTypeIdentities() {
+        synchronized (this.policyTypes) {
+            List<String> result = new Vector<>();
+            for (PolicyType p : this.policyTypes.getAll()) {
+                result.add(p.name());
+            }
+            return Mono.just(result);
+        }
+    }
+
+    @Override
+    public Mono<List<String>> getPolicyIdentities() {
+        synchronized (this.policies) {
+            Vector<String> result = new Vector<>();
+            for (Policy policy : policies.getAll()) {
+                result.add(policy.id());
+            }
+
+            return Mono.just(result);
+        }
+    }
+
+    @Override
+    public Mono<String> getPolicyTypeSchema(String policyTypeId) {
+        try {
+            return Mono.just(this.policyTypes.getType(policyTypeId).schema());
+        } catch (Exception e) {
+            return Mono.error(e);
+        }
+    }
+
+    @Override
+    public Mono<String> putPolicy(Policy p) {
+        this.policies.put(p);
+        return Mono.just("OK");
+    }
+
+    @Override
+    public Mono<String> deletePolicy(Policy policy) {
+        this.policies.remove(policy);
+        return Mono.just("OK");
+    }
+
+    public Policies getPolicies() {
+        return this.policies;
+    }
+
+    @Override
+    public Mono<A1ProtocolType> getProtocolVersion() {
+        return Mono.just(A1ProtocolType.STD_V1);
+    }
+
+    @Override
+    public Flux<String> 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 (file)
index 0000000..ec86a82
--- /dev/null
@@ -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<String, MockA1Client> 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 (file)
index 0000000..02bc864
--- /dev/null
@@ -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 (file)
index 0000000..f3eb28f
--- /dev/null
@@ -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 (file)
index 0000000..a73dd59
--- /dev/null
@@ -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 (file)
index 0000000..446c061
--- /dev/null
@@ -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 (file)
index 0000000..ec292bf
--- /dev/null
@@ -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 7ac3a79..49446a8 100644 (file)
--- a/pom.xml
+++ b/pom.xml
   ============LICENSE_END=========================================================
 -->
 <project xmlns="http://maven.apache.org/POM/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">
-       <modelVersion>4.0.0</modelVersion>
+    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">
+    <modelVersion>4.0.0</modelVersion>
 
-       <groupId>org.oransc</groupId>
-       <artifactId>nonrtric</artifactId>
-       <version>1.0.0-SNAPSHOT</version>
-       <packaging>pom</packaging>
+    <groupId>org.o-ran-sc</groupId>
+    <artifactId>nonrtric</artifactId>
+    <version>1.0.1-SNAPSHOT</version>
+    <packaging>pom</packaging>
 
-       <name>nonrtric</name>
-       <modules>
-               <module>dashboard</module>
-               <module>near-rt-ric-simulator</module>
-               <module>sdnc-a1-controller</module>
-       </modules>
-</project>
+    <name>nonrtric</name>
+    <modules>
+        <module>policy-agent</module>
+       <module>sdnc-a1-controller</module>
+        <module>dashboard</module>
+        <module>near-rt-ric-simulator</module>
+    </modules>
+ </project>
index 986e201..40005d2 100644 (file)
@@ -27,84 +27,38 @@ module A1-ADAPTER-API {
 \r
     import ietf-yang-types { prefix yang; }\r
 \r
-    revision "2019-10-02" {\r
+    revision "2020-01-22" {\r
         description\r
         "A1 adapter";\r
     }\r
 \r
-    //Flattend interface using RPC\r
-\r
-    //Get an array of near-rt-ric IDs,\r
-    //Each item in the returned array will be regarded as one near-rt-ric-id.\r
-    rpc getNearRT-RICs {\r
-        output {\r
-            leaf-list near-rt-ric-id-list {\r
-                type string;\r
-            }\r
-            leaf code {\r
-                type string;\r
-            }\r
-        }\r
-    }\r
-\r
-    //Get health status for a Near-RT-RIC. true - health ok, false - health is not ok.\r
-    rpc getHealthCheck {\r
-         input {\r
-            leaf near-rt-ric-id {\r
-                type string;\r
-            }\r
-        }\r
-\r
-        output {\r
-            leaf health-status {\r
-                type boolean;\r
-            }\r
-            leaf code {\r
-                type string;\r
-            }\r
-        }\r
-    }\r
-\r
     //Get an array of integer policy type ids\r
     //Each item in the returned array will be regarded as one policy-type-id.\r
-    rpc getPolicyTypes {\r
+    rpc getPolicyTypeIdentities {\r
         input {\r
-            leaf near-rt-ric-id {\r
+            leaf near-rt-ric-url {\r
                 type string;\r
             }\r
         }\r
 \r
         output {\r
             leaf-list policy-type-id-list {\r
-                type uint32;\r
-            }\r
-            leaf code {\r
                 type string;\r
             }\r
         }\r
     }\r
 \r
-    //Create a policy type\r
-    rpc createPolicyType {\r
+    //Get an array of integer policy ids\r
+    //Each item in the returned array will be regarded as one policy-id.\r
+    rpc getPolicyIdentities {\r
         input {\r
-            leaf near-rt-ric-id {\r
-                type string;\r
-            }\r
-            leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-            leaf description {\r
-                type string;\r
-            }\r
-            leaf name {\r
-                type string;\r
-            }\r
-            leaf policy-type {\r
+            leaf near-rt-ric-url {\r
                 type string;\r
             }\r
         }\r
-       output {\r
-            leaf code {\r
+\r
+        output {\r
+            leaf-list policy-id-list {\r
                 type string;\r
             }\r
         }\r
@@ -113,152 +67,50 @@ module A1-ADAPTER-API {
     //Get a policy type\r
     rpc getPolicyType {\r
         input {\r
-            leaf near-rt-ric-id {\r
+            leaf near-rt-ric-url {\r
                 type string;\r
             }\r
             leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-        }\r
-        output {\r
-            leaf description {\r
-                type string;\r
-            }\r
-            leaf name {\r
-                type string;\r
-            }\r
-            leaf policy-type {\r
-                type string;\r
-            }\r
-            leaf code {\r
                 type string;\r
             }\r
         }\r
-    }\r
-\r
-    //Delete a policy type\r
-    rpc deletePolicyType {\r
-        input {\r
-            leaf near-rt-ric-id {\r
-                type string;\r
-            }\r
-            leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-        }\r
         output {\r
-            leaf code {\r
-                type string;\r
-            }\r
-        }\r
-    }\r
-\r
-    //Get an array of string policy instance ids\r
-    //Each item in the returned array will be regarded as one policy-instance-id.\r
-    rpc getPolicyInstances {\r
-        input {\r
-            leaf near-rt-ric-id {\r
-                type string;\r
-            }\r
-            leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-        }\r
-\r
-        output {\r
-            leaf-list policy-instance-id-list {\r
-                type string;\r
-            }\r
-            leaf code {\r
+            leaf policy-type {\r
                 type string;\r
             }\r
         }\r
     }\r
 \r
-    //Create a policy instance\r
-    rpc createPolicyInstance {\r
+    //Create a policy\r
+    rpc putPolicy {\r
         input {\r
-            leaf near-rt-ric-id {\r
+            leaf near-rt-ric-url {\r
                 type string;\r
             }\r
-            leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-            leaf policy-instance-id {\r
-                type string;\r
-            }\r
-            leaf policy-instance {\r
-                type string;\r
-            }\r
-        }\r
-        output {\r
-            leaf code {\r
-                type string;\r
-            }\r
-        }\r
-    }\r
-\r
-    ///Get a policy instance\r
-    rpc getPolicyInstance {\r
-        input {\r
-            leaf near-rt-ric-id {\r
+            leaf policy-id {\r
                 type string;\r
             }\r
             leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-            leaf policy-instance-id {\r
-                type string;\r
-            }\r
-        }\r
-        output {\r
-            leaf policy-instance {\r
                 type string;\r
             }\r
-            leaf code {\r
-                type string;\r
-            }\r
-        }\r
-    }\r
-\r
-    //Delete a policy instance\r
-    rpc deletePolicyInstance {\r
-        input {\r
-            leaf near-rt-ric-id {\r
-                type string;\r
-            }\r
-            leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-            leaf policy-instance-id {\r
+            leaf policy {\r
                 type string;\r
             }\r
         }\r
         output {\r
-            leaf code {\r
+            leaf returned-policy {\r
                 type string;\r
             }\r
         }\r
     }\r
 \r
-    //Get the status for a policy instance\r
-    rpc getStatus {\r
+    //Delete a policy\r
+    rpc deletePolicy {\r
         input {\r
-            leaf near-rt-ric-id {\r
-                type string;\r
-            }\r
-            leaf policy-type-id {\r
-                type uint32;\r
-            }\r
-            leaf policy-instance-id {\r
-                type string;\r
-            }\r
-        }\r
-        output {\r
-            leaf status {\r
+            leaf near-rt-ric-url {\r
                 type string;\r
             }\r
-            leaf code {\r
+            leaf policy-id {\r
                 type string;\r
             }\r
         }\r
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 (file)
index 1754bfa..0000000
+++ /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);
-    }
-
-}
index b5e3dee..f67508f 100644 (file)
@@ -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<RpcResult<CreatePolicyInstanceOutput>> 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<Void> 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<CreatePolicyInstanceOutput> rpcResult = RpcResultBuilder
-        .<CreatePolicyInstanceOutput>status(true).withResult(responseBuilder.build()).build();
-    return Futures.immediateFuture(rpcResult);
-  }
-
-  @Override
-  public ListenableFuture<RpcResult<CreatePolicyTypeOutput>> 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<Void> 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<CreatePolicyTypeOutput> rpcResult = RpcResultBuilder
-        .<CreatePolicyTypeOutput>status(true).withResult(responseBuilder.build()).build();
-    return Futures.immediateFuture(rpcResult);
-  }
-
-  @Override
-  public ListenableFuture<RpcResult<DeletePolicyInstanceOutput>> 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<Void> 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<DeletePolicyInstanceOutput> rpcResult = RpcResultBuilder
-        .<DeletePolicyInstanceOutput>status(true).withResult(responseBuilder.build()).build();
-    return Futures.immediateFuture(rpcResult);
-  }
-
-  @Override
-  public ListenableFuture<RpcResult<DeletePolicyTypeOutput>> 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<Void> 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<DeletePolicyTypeOutput> rpcResult = RpcResultBuilder
-        .<DeletePolicyTypeOutput>status(true).withResult(responseBuilder.build()).build();
-    return Futures.immediateFuture(rpcResult);
-  }
-
-  @Override
-  public ListenableFuture<RpcResult<GetHealthCheckOutput>> getHealthCheck(
-      GetHealthCheckInput input) {
-    log.info("Start of getHealthCheck");
-    GetHealthCheckOutputBuilder responseBuilder = new GetHealthCheckOutputBuilder();
-    try {
-        String uri = nearRicUrlProvider.getHealthCheck(String.valueOf(input.getNearRtRicId()));
-        ResponseEntity<Object> 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<RpcResult<GetPolicyTypeIdentitiesOutput>> getPolicyTypeIdentities(
+          GetPolicyTypeIdentitiesInput input) {
+    log.info("Start of getPolicyTypeIdentities");
+    GetPolicyTypeIdentitiesOutputBuilder responseBuilder = new GetPolicyTypeIdentitiesOutputBuilder();
+    String uri = nearRicUrlProvider.policyTypesUrl(String.valueOf(input.getNearRtRicUrl()));
+    ResponseEntity<List<String>> 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<GetHealthCheckOutput> rpcResult = RpcResultBuilder.<GetHealthCheckOutput>status(true)
+    log.info("End of getPolicyTypeIdentities");
+    RpcResult<GetPolicyTypeIdentitiesOutput> rpcResult = RpcResultBuilder.<GetPolicyTypeIdentitiesOutput>status(true)
         .withResult(responseBuilder.build()).build();
     return Futures.immediateFuture(rpcResult);
   }
 
   @Override
-  public ListenableFuture<RpcResult<GetNearRTRICsOutput>> 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<GetNearRTRICsOutput> rpcResult = RpcResultBuilder.<GetNearRTRICsOutput>status(true)
-          .withResult(responseBuilder.build()).build();
-      return Futures.immediateFuture(rpcResult);
-  }
-
-  @Override
-  public ListenableFuture<RpcResult<GetPolicyInstanceOutput>> 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<String> 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<GetPolicyInstanceOutput> rpcResult = RpcResultBuilder
-        .<GetPolicyInstanceOutput>status(true).withResult(responseBuilder.build()).build();
-    return Futures.immediateFuture(rpcResult);
-  }
-
-  @Override
-  public ListenableFuture<RpcResult<GetPolicyInstancesOutput>> 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<List<String>> 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<RpcResult<GetPolicyIdentitiesOutput>> getPolicyIdentities(GetPolicyIdentitiesInput input) {
+    log.info("Start of getPolicyIdentities");
+    GetPolicyIdentitiesOutputBuilder responseBuilder = new GetPolicyIdentitiesOutputBuilder();
+    String uri = nearRicUrlProvider.policiesUrl(String.valueOf(input.getNearRtRicUrl()));
+    ResponseEntity<List<String>> 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<GetPolicyInstancesOutput> rpcResult = RpcResultBuilder
-        .<GetPolicyInstancesOutput>status(true).withResult(responseBuilder.build()).build();
+    log.info("End of getPolicyIdentities");
+    RpcResult<GetPolicyIdentitiesOutput> rpcResult = RpcResultBuilder
+        .<GetPolicyIdentitiesOutput>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<String> 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<String> 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<GetPolicyTypeOutput> rpcResult = RpcResultBuilder.<GetPolicyTypeOutput>status(true)
@@ -417,61 +227,33 @@ public class NonrtRicApiProvider implements AutoCloseable, A1ADAPTERAPIService {
   }
 
   @Override
-  public ListenableFuture<RpcResult<GetPolicyTypesOutput>> getPolicyTypes(
-      GetPolicyTypesInput input) {
-    log.info("Start of getPolicyTypes");
-    GetPolicyTypesOutputBuilder responseBuilder = new GetPolicyTypesOutputBuilder();
-    try {
-        String uri = nearRicUrlProvider.getPolicyTypes(String.valueOf(input.getNearRtRicId()));
-        ResponseEntity<List<Integer>> response = restAdapter.get(uri, List.class);
-        if (response.hasBody()) {
-            log.info("Response getPolicyTypes : {} ", response.getBody());
-            List<Integer> policyTypesListInteger = response.getBody();
-            List<Long> 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<RpcResult<PutPolicyOutput>> 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<String> 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<GetPolicyTypesOutput> rpcResult = RpcResultBuilder.<GetPolicyTypesOutput>status(true)
-        .withResult(responseBuilder.build()).build();
+    log.info("End of putPolicy");
+    RpcResult<PutPolicyOutput> rpcResult = RpcResultBuilder
+        .<PutPolicyOutput>status(true).withResult(responseBuilder.build()).build();
     return Futures.immediateFuture(rpcResult);
   }
 
   @Override
-  public ListenableFuture<RpcResult<GetStatusOutput>> 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<List<Object>> 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<GetStatusOutput> rpcResult =
-        RpcResultBuilder.<GetStatusOutput>status(true).withResult(responseBuilder.build()).build();
+  public ListenableFuture<RpcResult<DeletePolicyOutput>> 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<Void> response = restAdapter.delete(uri);
+    log.info("End of deletePolicy");
+    RpcResult<DeletePolicyOutput> rpcResult = RpcResultBuilder
+        .<DeletePolicyOutput>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 (file)
index 0000000..10b16e3
--- /dev/null
@@ -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();
+  }
+}
@@ -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
    */
-  <T> ResponseEntity<T> put(final String url, final String body);
+  <T> ResponseEntity<T> put(final String url, final String body, final Class<T> clazz);
 
   /**
    * Delete resource for the given object to the URI.
@@ -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 <T> ResponseEntity<T> put(String uri, String body) {
+  public <T> ResponseEntity<T> put(String uri, String body, Class<T> clazz) {
     HttpHeaders headers = new HttpHeaders();
     headers.setContentType(MediaType.APPLICATION_JSON);
     HttpEntity<String> entity = new HttpEntity<String>(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 (file)
index 8c2d692..0000000
+++ /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<String, String> 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<String> 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();
-  }
-}
index e9b86be..3dc1efd 100644 (file)
 
 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<Object> createPolicyInstanceResponse = new ResponseEntity<>(HttpStatus.CREATED);
-    when(restAdapter.put(eq(uri), anyObject())).thenReturn(createPolicyInstanceResponse);
-    ListenableFuture<RpcResult<CreatePolicyInstanceOutput>> result =
-        nonrtRicApiProvider.createPolicyInstance(inputBuilder.build());
-    Assert.assertEquals(String.valueOf(HttpStatus.CREATED.value()), result.get().getResult().getCode());
+    String uri = nearRicUrlProvider.policyTypesUrl(inputBuilder.build().getNearRtRicUrl());
+    List<String> policyTypeIdentities = new ArrayList<>();
+    policyTypeIdentities.add(policyTypeId);
+    ResponseEntity<Object> getPolicyTypeIdentitiesResponse = new ResponseEntity<>(policyTypeIdentities, HttpStatus.OK);
+    when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyTypeIdentitiesResponse);
+    ListenableFuture<RpcResult<GetPolicyTypeIdentitiesOutput>> 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<Object> deletePolicyInstanceResponse = new ResponseEntity<>(HttpStatus.NO_CONTENT);
-    when(restAdapter.delete(eq(uri))).thenReturn(deletePolicyInstanceResponse);
-    ListenableFuture<RpcResult<DeletePolicyInstanceOutput>> 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<RpcResult<GetNearRTRICsOutput>> 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<Object> createPolicyTypeResponse = new ResponseEntity<>(HttpStatus.CREATED);
-    when(restAdapter.put(eq(uri), anyObject())).thenReturn(createPolicyTypeResponse);
-    ListenableFuture<RpcResult<CreatePolicyTypeOutput>> 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<Object> deletePolicyTypeResponse = new ResponseEntity<>(HttpStatus.NO_CONTENT);
-    when(restAdapter.delete(eq(uri))).thenReturn(deletePolicyTypeResponse);
-    ListenableFuture<RpcResult<DeletePolicyTypeOutput>> 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<String> policyIdentities = new ArrayList<>();
+    policyIdentities.add(policyId);
+    ResponseEntity<Object> getPolicyIdentitiesResponse = new ResponseEntity<>(policyIdentities, HttpStatus.OK);
+    when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyIdentitiesResponse);
+    ListenableFuture<RpcResult<GetPolicyIdentitiesOutput>> 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<Object> getPolicyTypeResponse = new ResponseEntity<>(policyType, HttpStatus.OK);
+    String testPolicyType = "{}";
+    ResponseEntity<Object> getPolicyTypeResponse = new ResponseEntity<>(testPolicyType, HttpStatus.OK);
     when(restAdapter.get(eq(uri), eq(String.class))).thenReturn(getPolicyTypeResponse);
     ListenableFuture<RpcResult<GetPolicyTypeOutput>> 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<Integer> policyTypesInteger = new ArrayList<>();
-    policyTypesInteger.add(20001);
-    List<Long> policyTypesLong = new ArrayList<>();
-    policyTypesLong.add(20001L);
-    ResponseEntity<Object> getPolicyTypesResponse = new ResponseEntity<>(policyTypesInteger, HttpStatus.OK);
-    when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyTypesResponse);
-    ListenableFuture<RpcResult<GetPolicyTypesOutput>> 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<Object> getPolicyInstanceResponse = new ResponseEntity<>(policyInstance, HttpStatus.OK);
-    when(restAdapter.get(eq(uri), eq(String.class))).thenReturn(getPolicyInstanceResponse);
-    ListenableFuture<RpcResult<GetPolicyInstanceOutput>> 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<String> policyInstances = new ArrayList<>();
-    policyInstances.add("3d2157af-6a8f-4a7c-810f-38c2f824bf12");
-    ResponseEntity<Object> getPolicyInstancesResponse = new ResponseEntity<>(policyInstances, HttpStatus.OK);
-    when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getPolicyInstancesResponse);
-    ListenableFuture<RpcResult<GetPolicyInstancesOutput>> 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<Object> getStatusResponse =
-            new ResponseEntity<>(new Gson().fromJson(policyInstanceStatus, List.class), HttpStatus.OK);
-    when(restAdapter.get(eq(uri), eq(List.class))).thenReturn(getStatusResponse);
-    ListenableFuture<RpcResult<GetStatusOutput>> 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<Object> getHealthCheckResponse = new ResponseEntity<>(HttpStatus.OK);
-    when(restAdapter.get(eq(uri), eq(Object.class))).thenReturn(getHealthCheckResponse);
-    ListenableFuture<RpcResult<GetHealthCheckOutput>> 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<String> putPolicyResponse = new ResponseEntity<>(testPolicy, HttpStatus.CREATED);
+    when(restAdapter.put(eq(uri), eq(testPolicy), eq(String.class))).thenReturn(putPolicyResponse);
+    ListenableFuture<RpcResult<PutPolicyOutput>> result =
+        nonrtRicApiProvider.putPolicy(inputBuilder.build());
+    Assert.assertEquals(testPolicy, result.get().getResult().getReturnedPolicy());
   }
 }
index 2ede17e..9e6b65a 100644 (file)
     </properties>
 
     <dependencies>
-        <dependency>
-            <groupId>org.onap.sdnc.northbound</groupId>
-            <artifactId>generic-resource-api-installer</artifactId>
-            <version>${sdnc.northbound.version}</version>
-            <classifier>repo</classifier>
-            <type>zip</type>
-        </dependency>
         <dependency>
             <groupId>org.onap.sdnc.northbound</groupId>
             <artifactId>nonrt-ric-api-installer</artifactId>
             <classifier>repo</classifier>
             <type>zip</type>
         </dependency>
-        <dependency>
-            <groupId>org.onap.sdnc.northbound</groupId>
-            <artifactId>vnfapi-installer</artifactId>
-            <version>${sdnc.northbound.version}</version>
-            <classifier>repo</classifier>
-            <type>zip</type>
-        </dependency>
-        <dependency>
-            <groupId>org.onap.sdnc.northbound</groupId>
-            <artifactId>vnftools-installer</artifactId>
-            <version>${sdnc.northbound.version}</version>
-            <classifier>repo</classifier>
-            <type>zip</type>
-        </dependency>
         <dependency>
             <groupId>org.onap.sdnc.northbound</groupId>
             <artifactId>sdnc-northbound-features-installer</artifactId>
@@ -96,7 +75,6 @@
             <classifier>repo</classifier>
             <type>zip</type>
         </dependency>
-
         <dependency>
             <groupId>org.onap.ccsdk.features.sdnr.northbound</groupId>
             <artifactId>oofpcipoc-installer</artifactId>
             <classifier>repo</classifier>
             <type>zip</type>
         </dependency>
-
         <dependency>
             <groupId>org.onap.ccsdk.features.sdnr.northbound</groupId>
             <artifactId>sdnr-northbound-features-installer</artifactId>
index b67bcaf..5770b74 100644 (file)
@@ -25,7 +25,7 @@
 
        <groupId>org.onap.sdnc</groupId>
        <artifactId>sdnc</artifactId>
-       <version>1.7.3-SNAPSHOT</version>
+       <version>1.7.4-SNAPSHOT</version>
        <packaging>pom</packaging>
 
        <name>sdnc</name>