Add Xapp Onboarder client to backend 28/4028/5
authorLott, Christopher (cl778h) <cl778h@att.com>
Mon, 8 Jun 2020 17:43:51 +0000 (13:43 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Wed, 17 Jun 2020 13:16:39 +0000 (09:16 -0400)
Add submodule for it/dev repository to access its OpenAPI spec.
Add xapp onboarder controller and basic tests.
Bump version to 2.1.0.

Change-Id: I14975e6765bffc15a9e3738ceb6aaa4c8a35c9f1
Issue-ID: OAM-108
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
25 files changed:
.gitmodules
dashboard/app-mgr-client/pom.xml
dashboard/e2-mgr-client/pom.xml
dashboard/pom.xml
dashboard/webapp-backend/pom.xml
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/config/XappOnboarderApiBuilder.java [new file with mode: 0644]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappOnboarderConfiguration.java [new file with mode: 0644]
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappOnboarderController.java [new file with mode: 0644]
dashboard/webapp-backend/src/main/resources/application.yaml
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/XappOnboarderMockConfiguration.java [new file with mode: 0644]
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/test/XappOnboarderConfigTest.java [new file with mode: 0644]
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/XappOnboarderControllerTest.java [new file with mode: 0644]
dashboard/webapp-backend/src/test/resources/sample-chart-list.json [new file with mode: 0644]
dashboard/webapp-backend/src/test/resources/sample-chart.yaml [new file with mode: 0644]
dashboard/webapp-backend/src/test/resources/sample-values.yaml [new file with mode: 0644]
dashboard/webapp-frontend/package-lock.json
dashboard/webapp-frontend/pom.xml
dashboard/xapp-obrd-client/.gitignore [new file with mode: 0644]
dashboard/xapp-obrd-client/README.md [new file with mode: 0644]
dashboard/xapp-obrd-client/it-dev [new submodule]
dashboard/xapp-obrd-client/pom.xml [new file with mode: 0644]
dashboard/xapp-obrd-client/src/test/java/org/oransc/ric/portal/dashboard/xappobrd/client/test/XappOnboarderClientTest.java [new file with mode: 0644]
docs/config-deploy.rst
docs/release-notes.rst

index 3f35354..1554542 100644 (file)
@@ -4,3 +4,6 @@
 [submodule "dashboard/e2-mgr-client/ric-plt-e2mgr"]
        path = dashboard/e2-mgr-client/ric-plt-e2mgr
        url = ../../ric-plt/e2mgr
+[submodule "dashboard/xapp-obrd-client/it-dev"]
+       path = dashboard/xapp-obrd-client/it-dev
+       url = ../../it/dev
index 80ce2a8..ba6c115 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>2.0.3-SNAPSHOT</version>
+               <version>2.1.0-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric-plt.appmgr.client</groupId>
index bcde475..205bdbf 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>2.0.3-SNAPSHOT</version>
+               <version>2.1.0-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric-plt.e2mgr.client</groupId>
index c8d33d1..387f884 100644 (file)
@@ -32,7 +32,7 @@ limitations under the License.
        <artifactId>ric-dash-parent</artifactId>
        <name>RIC Dashboard Project</name>
        <packaging>pom</packaging>
-       <version>2.0.3-SNAPSHOT</version>
+       <version>2.1.0-SNAPSHOT</version>
        <properties>
                <java.version>11</java.version>
                <!-- Properties for the license-maven-plugin in child POMs -->
@@ -46,6 +46,7 @@ limitations under the License.
        <modules>
                <module>app-mgr-client</module>
                <module>e2-mgr-client</module>
+               <module>xapp-obrd-client</module>
                <module>webapp-frontend</module>
                <module>webapp-backend</module>
        </modules>
index 9c4a317..664747f 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>2.0.3-SNAPSHOT</version>
+               <version>2.1.0-SNAPSHOT</version>
        </parent>
        <!-- reuse parent groupId -->
        <artifactId>ric-dash-be</artifactId>
@@ -54,6 +54,11 @@ limitations under the License.
                        <artifactId>e2-mgr-client</artifactId>
                        <version>4.4.4-SNAPSHOT</version>
                </dependency>
+               <dependency>
+                       <groupId>org.o-ran-sc.it-dev.xapp-onboarder.client</groupId>
+                       <artifactId>xapp-obrd-client</artifactId>
+                       <version>1.0.0-SNAPSHOT</version>
+               </dependency>
                <dependency>
                        <groupId>org.onap.portal.sdk</groupId>
                        <artifactId>epsdk-fw</artifactId>
index f1fcca9..9d9bb11 100644 (file)
@@ -29,6 +29,7 @@ import org.oransc.ric.portal.dashboard.controller.AdminController;
 import org.oransc.ric.portal.dashboard.controller.AppManagerController;
 import org.oransc.ric.portal.dashboard.controller.E2ManagerController;
 import org.oransc.ric.portal.dashboard.controller.SimpleErrorController;
+import org.oransc.ric.portal.dashboard.controller.XappOnboarderController;
 import org.oransc.ric.portal.dashboard.portalapi.PortalAuthManager;
 import org.oransc.ric.portal.dashboard.portalapi.PortalAuthenticationFilter;
 import org.slf4j.Logger;
@@ -102,6 +103,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
                        E2ManagerController.CONTROLLER_PATH + "/" + DashboardConstants.RIC_INSTANCE_KEY + "/*/"
                                        + E2ManagerController.HEALTH_METHOD, //
                        E2ManagerController.CONTROLLER_PATH + "/" + DashboardConstants.VERSION_METHOD, //
+                       XappOnboarderController.CONTROLLER_PATH + "/" + DashboardConstants.VERSION_METHOD, //
+                       XappOnboarderController.CONTROLLER_PATH + "/" + DashboardConstants.RIC_INSTANCE_KEY + "/*/"
+                                       + XappOnboarderController.HEALTH_METHOD, //
                        SimpleErrorController.ERROR_PATH };
 
        @Override
diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappOnboarderApiBuilder.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappOnboarderApiBuilder.java
new file mode 100644 (file)
index 0000000..29ab09a
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.config;
+
+import java.lang.invoke.MethodHandles;
+
+import org.oransc.itdev.xapponboarder.client.api.ChartsApi;
+import org.oransc.itdev.xapponboarder.client.api.HealthApi;
+import org.oransc.itdev.xapponboarder.client.api.OnboardApi;
+import org.oransc.itdev.xapponboarder.client.invoker.ApiClient;
+import org.oransc.ric.portal.dashboard.model.RicInstance;
+import org.oransc.ric.portal.dashboard.model.RicRegionList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.DefaultUriBuilderFactory;
+
+/**
+ * The OpenAPI generated API client code using Spring RestTemplate is not thread
+ * safe according to https://github.com/swagger-api/swagger-codegen/issues/9222
+ *
+ * As a workaround this builder creates a new client at every request. If this
+ * proves to be too slow then clients could be cached for each thread.
+ */
+public class XappOnboarderApiBuilder {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       private final String urlSuffix;
+       private final RicRegionList instanceConfig;
+
+       public XappOnboarderApiBuilder(final RicRegionList instanceConfig, final String urlSuffix) {
+               logger.debug("ctor: suffix {}", urlSuffix);
+               this.instanceConfig = instanceConfig;
+               this.urlSuffix = urlSuffix;
+       }
+
+       private ApiClient apiClient(String instanceKey) {
+               RicInstance instance = instanceConfig.getInstance(instanceKey);
+               String url = new DefaultUriBuilderFactory(instance.getPltUrlPrefix().trim()).builder()
+                               .path(this.urlSuffix.trim()).build().normalize().toString();
+               logger.debug("apiClient URL {}", url);
+               return new ApiClient(new RestTemplate()).setBasePath(url);
+       }
+
+       public HealthApi getHealthApi(String instanceKey) {
+               return new HealthApi(apiClient(instanceKey));
+       }
+
+       public ChartsApi getChartsApi(String instanceKey) {
+               return new ChartsApi(apiClient(instanceKey));
+       }
+
+       public OnboardApi getOnboardApi(String instanceKey) {
+               return new OnboardApi(apiClient(instanceKey));
+       }
+
+}
diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappOnboarderConfiguration.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/XappOnboarderConfiguration.java
new file mode 100644 (file)
index 0000000..671fc4b
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.config;
+
+import java.lang.invoke.MethodHandles;
+
+import org.oransc.ric.portal.dashboard.model.RicRegionList;
+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;
+
+/**
+ * Creates an Xapp onboarder client builder as a bean to be managed by the
+ * Spring container.
+ */
+@Configuration
+@Profile("!test")
+public class XappOnboarderConfiguration {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       // Populated by the autowired constructor
+       private final String urlSuffix;
+       private final RicRegionList instanceConfig;
+
+       @Autowired
+       public XappOnboarderConfiguration(@Value("${xappobrd.url.suffix}") final String urlSuffix,
+                       final RicRegionList instanceConfig) {
+               logger.debug("ctor: URL suffix {}", urlSuffix);
+               this.urlSuffix = urlSuffix;
+               this.instanceConfig = instanceConfig;
+       }
+
+       @Bean
+       // The bean (method) name must be globally unique
+       public XappOnboarderApiBuilder xappOnboarderApiBuilder() {
+               return new XappOnboarderApiBuilder(instanceConfig, urlSuffix);
+       }
+
+}
diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappOnboarderController.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappOnboarderController.java
new file mode 100644 (file)
index 0000000..03a65d6
--- /dev/null
@@ -0,0 +1,154 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.controller;
+
+import java.lang.invoke.MethodHandles;
+
+import org.oransc.itdev.xapponboarder.client.api.HealthApi;
+import org.oransc.itdev.xapponboarder.client.model.Descriptor;
+import org.oransc.itdev.xapponboarder.client.model.DescriptorRemote;
+import org.oransc.itdev.xapponboarder.client.model.Status;
+import org.oransc.ric.portal.dashboard.DashboardApplication;
+import org.oransc.ric.portal.dashboard.DashboardConstants;
+import org.oransc.ric.portal.dashboard.config.XappOnboarderApiBuilder;
+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.context.annotation.Configuration;
+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.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+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 Xapp Onboarder API.
+ * 
+ * If a method throws RestClientResponseException, it is handled by a method in
+ * {@link CustomResponseEntityExceptionHandler} which returns status 502. All
+ * other exceptions are handled by Spring which returns status 500.
+ */
+@Configuration
+@RestController
+@RequestMapping(value = XappOnboarderController.CONTROLLER_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
+public class XappOnboarderController {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       // Publish paths in constants for tests
+       public static final String CONTROLLER_PATH = DashboardConstants.ENDPOINT_PREFIX + "/xappobrd";
+       // Dashboard only
+       public static final String HEALTH_METHOD = "health";
+       public static final String CHARTS_METHOD = "charts";
+       public static final String ONBOARD_METHOD = "onboard";
+       public static final String ONBOARD_DOWNLOAD_METHOD = "onboard/download";
+       // Path component, not a method nor a path parameter
+       public static final String VALUESYAML = "values.yaml";
+       // Path parameters
+       public static final String XAPPNAME_PP = "xapp";
+       public static final String VERSION_PP = "ver";
+
+       // Populated by the autowired constructor
+       private final XappOnboarderApiBuilder xappOnboarderApiBuilder;
+
+       @Autowired
+       public XappOnboarderController(final XappOnboarderApiBuilder xappOnboarderApiBuilder) {
+               Assert.notNull(xappOnboarderApiBuilder, "builder must not be null");
+               this.xappOnboarderApiBuilder = xappOnboarderApiBuilder;
+               if (logger.isDebugEnabled())
+                       logger.debug("ctor: configured with builder type {}", xappOnboarderApiBuilder.getClass().getName());
+       }
+
+       @ApiOperation(value = "Gets the xapp onboarder client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
+       @GetMapping(DashboardConstants.VERSION_METHOD)
+       // No role required
+       public SuccessTransport getClientVersion() {
+               return new SuccessTransport(200, DashboardApplication.getImplementationVersion(HealthApi.class));
+       }
+
+       @ApiOperation(value = "Gets the health from the xapp onboarder, expressed as the response code.")
+       @GetMapping(DashboardConstants.RIC_INSTANCE_KEY + "/{" + DashboardConstants.RIC_INSTANCE_KEY + "}/" + HEALTH_METHOD)
+       // No role required
+       public ResponseEntity<Status> getHealthCheck(
+                       @PathVariable(DashboardConstants.RIC_INSTANCE_KEY) String instanceKey) {
+               logger.debug("getHealthCheck instance {}", instanceKey);
+               HealthApi api = xappOnboarderApiBuilder.getHealthApi(instanceKey);
+               Status status = api.getHealthCheck();
+               return ResponseEntity.status(api.getApiClient().getStatusCode().value()).body(status);
+       }
+
+       @ApiOperation(value = "Gets the helm charts.", response = String.class)
+       @GetMapping(DashboardConstants.RIC_INSTANCE_KEY + "/{" + DashboardConstants.RIC_INSTANCE_KEY + "}/" + CHARTS_METHOD)
+       @Secured({ DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD })
+       public Object getCharts(@PathVariable(DashboardConstants.RIC_INSTANCE_KEY) String instanceKey) {
+               logger.debug("getCharts instance {}", instanceKey);
+               return xappOnboarderApiBuilder.getChartsApi(instanceKey).getChartsList();
+       }
+
+       @ApiOperation(value = "Gets the helm chart for the specified xApp and version.", response = String.class)
+       @GetMapping(DashboardConstants.RIC_INSTANCE_KEY + "/{" + DashboardConstants.RIC_INSTANCE_KEY + "}/" + CHARTS_METHOD
+                       + "/" + XAPPNAME_PP + "/{" + XAPPNAME_PP + "}/" + VERSION_PP + "/{" + VERSION_PP + "}")
+       @Secured({ DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD })
+       public Object getChart(@PathVariable(DashboardConstants.RIC_INSTANCE_KEY) String instanceKey,
+                       @PathVariable(XAPPNAME_PP) String xappName, @PathVariable(VERSION_PP) String version) {
+               logger.debug("getChart instance {} xapp {} ver {}", instanceKey, xappName, version);
+               return xappOnboarderApiBuilder.getChartsApi(instanceKey).getChartsFetcher(xappName, version);
+       }
+
+       @ApiOperation(value = "Gets the values yaml for the specified xApp and version.", response = String.class)
+       @GetMapping(DashboardConstants.RIC_INSTANCE_KEY + "/{" + DashboardConstants.RIC_INSTANCE_KEY + "}/" + CHARTS_METHOD
+                       + "/" + XAPPNAME_PP + "/{" + XAPPNAME_PP + "}/" + VERSION_PP + "/{" + VERSION_PP + "}/" + VALUESYAML)
+       @Secured({ DashboardConstants.ROLE_ADMIN, DashboardConstants.ROLE_STANDARD })
+       public Object getValues(@PathVariable(DashboardConstants.RIC_INSTANCE_KEY) String instanceKey,
+                       @PathVariable(XAPPNAME_PP) String xappName, @PathVariable(VERSION_PP) String version) {
+               logger.debug("getValues instance {} xapp {} ver {}", instanceKey, xappName, version);
+               return xappOnboarderApiBuilder.getChartsApi(instanceKey).getValuesYamlFetcher(xappName, version);
+       }
+
+       @ApiOperation(value = "Onboard xApp using the xApp descriptor and schema in the request body.", response = Status.class)
+       @PostMapping(DashboardConstants.RIC_INSTANCE_KEY + "/{" + DashboardConstants.RIC_INSTANCE_KEY + "}/"
+                       + ONBOARD_METHOD)
+       @Secured({ DashboardConstants.ROLE_ADMIN })
+       public Status onboardXapp(@PathVariable(DashboardConstants.RIC_INSTANCE_KEY) String instanceKey, //
+                       @Validated @RequestBody Descriptor appDescriptor) {
+               logger.debug("onboardxApp instance {} descriptor {}", instanceKey, appDescriptor);
+               return xappOnboarderApiBuilder.getOnboardApi(instanceKey).postOnboardxApps(appDescriptor);
+       }
+
+       @ApiOperation(value = "Onboard xApp after downloading the xApp descriptor and schema from the URLs.", response = Status.class)
+       @PostMapping(DashboardConstants.RIC_INSTANCE_KEY + "/{" + DashboardConstants.RIC_INSTANCE_KEY + "}/"
+                       + ONBOARD_DOWNLOAD_METHOD)
+       @Secured({ DashboardConstants.ROLE_ADMIN })
+       public Status onboardRemoteXapp(@PathVariable(DashboardConstants.RIC_INSTANCE_KEY) String instanceKey, //
+                       @Validated @RequestBody DescriptorRemote appDescriptor) {
+               logger.debug("onboardRemoteXapp instance {} descriptor {}", instanceKey, appDescriptor);
+               return xappOnboarderApiBuilder.getOnboardApi(instanceKey).postOnboardxAppsDownload(appDescriptor);
+       }
+
+}
index a14f7b1..6f0ed23 100644 (file)
@@ -60,6 +60,12 @@ e2mgr:
         # uses pltUrlPrefix
         suffix: /e2mgr/v1
 
+# Xapp Onboarder
+xappobrd:
+    url:
+        # uses pltUrlPrefix
+        suffix: /xappobrd/api/v1
+
 # Kubernetes API via https://github.com/nokia/caas-ingress
 caasingress:
     # Set insecure=true to disable SSL certificate and hostname checking
diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/XappOnboarderMockConfiguration.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/XappOnboarderMockConfiguration.java
new file mode 100644 (file)
index 0000000..5d0bd4d
--- /dev/null
@@ -0,0 +1,151 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.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 java.io.IOException;
+import java.lang.invoke.MethodHandles;
+
+import org.oransc.itdev.xapponboarder.client.api.ChartsApi;
+import org.oransc.itdev.xapponboarder.client.api.HealthApi;
+import org.oransc.itdev.xapponboarder.client.api.OnboardApi;
+import org.oransc.itdev.xapponboarder.client.invoker.ApiClient;
+import org.oransc.itdev.xapponboarder.client.model.Descriptor;
+import org.oransc.itdev.xapponboarder.client.model.DescriptorRemote;
+import org.oransc.itdev.xapponboarder.client.model.Status;
+import org.oransc.ric.portal.dashboard.TestUtils;
+import org.oransc.ric.portal.dashboard.model.RicRegionList;
+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.http.HttpStatus;
+
+/**
+ * Creates a mock implementation of the Xapp Onboarder client API.
+ */
+@Configuration
+@Profile("test")
+public class XappOnboarderMockConfiguration {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       // Simulate remote method delay for UI testing
+       private int delayMs;
+
+       // Autowire all the properties required by the real class
+       // (even tho not used here) as a test of the properties.
+       @Autowired
+       public XappOnboarderMockConfiguration(@Value("${xappobrd.url.suffix}") final String urlSuffix, //
+                       final RicRegionList instanceConfig, //
+                       @Value("${mock.config.delay:0}") int delayMs) {
+               logger.info("ctor: configured with suffix {}, instances {}, delay {}", urlSuffix, instanceConfig, delayMs);
+               this.delayMs = delayMs;
+       }
+
+       private ApiClient apiClient() {
+               ApiClient mockClient = mock(ApiClient.class);
+               when(mockClient.getStatusCode()).thenReturn(HttpStatus.OK);
+               return mockClient;
+       }
+
+       private HealthApi healthApi() {
+               ApiClient apiClient = apiClient();
+               HealthApi mockApi = mock(HealthApi.class);
+               when(mockApi.getApiClient()).thenReturn(apiClient);
+               doAnswer(i -> {
+                       return new Status().status("OK");
+               }).when(mockApi).getHealthCheck();
+               return mockApi;
+       }
+
+       private ChartsApi chartsApi() throws IOException {
+               final String sampleChartListJson = TestUtils.readDataFromPath("sample-chart-list.json");
+               final String sampleChartYaml = TestUtils.readDataFromPath("sample-chart.yaml");
+               final String sampleValuesYaml = TestUtils.readDataFromPath("sample-values.yaml");
+               ApiClient apiClient = apiClient();
+               ChartsApi mockApi = mock(ChartsApi.class);
+               when(mockApi.getApiClient()).thenReturn(apiClient);
+               doAnswer(inv -> {
+                       if (delayMs > 0) {
+                               logger.debug("getChartsList sleeping {}", delayMs);
+                               Thread.sleep(delayMs);
+                       }
+                       return sampleChartListJson;
+               }).when(mockApi).getChartsList();
+               doAnswer(inv -> {
+                       if (delayMs > 0) {
+                               logger.debug("getChartsFetcher sleeping {}", delayMs);
+                               Thread.sleep(delayMs);
+                       }
+                       return sampleChartYaml;
+               }).when(mockApi).getChartsFetcher(any(String.class), any(String.class));
+               doAnswer(inv -> {
+                       if (delayMs > 0) {
+                               logger.debug("getValuesYamlFetcher sleeping {}", delayMs);
+                               Thread.sleep(delayMs);
+                       }
+                       return sampleValuesYaml;
+               }).when(mockApi).getValuesYamlFetcher(any(String.class), any(String.class));
+               return mockApi;
+       }
+
+       private OnboardApi onboardApi() {
+               ApiClient apiClient = apiClient();
+               OnboardApi mockApi = mock(OnboardApi.class);
+               when(mockApi.getApiClient()).thenReturn(apiClient);
+               doAnswer(inv -> {
+                       if (delayMs > 0) {
+                               logger.debug("postOnboardxApps sleeping {}", delayMs);
+                               Thread.sleep(delayMs);
+                       }
+                       return new Status().status("OK");
+               }).when(mockApi).postOnboardxApps(any(Descriptor.class));
+               doAnswer(inv -> {
+                       if (delayMs > 0) {
+                               logger.debug("postOnboardxAppsDownload sleeping {}", delayMs);
+                               Thread.sleep(delayMs);
+                       }
+                       return new Status().status("OK");
+               }).when(mockApi).postOnboardxAppsDownload(any(DescriptorRemote.class));
+               return mockApi;
+       }
+
+       @Bean
+       // Must use the same name as the non-mock configuration
+       public XappOnboarderApiBuilder xappOnboarderApiBuilder() throws IOException {
+               final XappOnboarderApiBuilder mockBuilder = mock(XappOnboarderApiBuilder.class);
+               final HealthApi mockHealthApi = healthApi();
+               when(mockBuilder.getHealthApi(any(String.class))).thenReturn(mockHealthApi);
+               final ChartsApi mockChartsApi = chartsApi();
+               when(mockBuilder.getChartsApi(any(String.class))).thenReturn(mockChartsApi);
+               final OnboardApi mockOnboardApi = onboardApi();
+               when(mockBuilder.getOnboardApi(any(String.class))).thenReturn(mockOnboardApi);
+               return mockBuilder;
+       }
+
+}
diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/test/XappOnboarderConfigTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/test/XappOnboarderConfigTest.java
new file mode 100644 (file)
index 0000000..b21816b
--- /dev/null
@@ -0,0 +1,40 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.config.test;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.oransc.itdev.xapponboarder.client.api.ChartsApi;
+import org.oransc.itdev.xapponboarder.client.api.HealthApi;
+import org.oransc.ric.portal.dashboard.config.RICInstanceMockConfiguration;
+import org.oransc.ric.portal.dashboard.config.XappOnboarderApiBuilder;
+
+public class XappOnboarderConfigTest extends AbstractConfigTest {
+
+       @Test
+       public void builderTest() {
+               XappOnboarderApiBuilder builder = new XappOnboarderApiBuilder(instanceConfig, "suffix");
+               HealthApi healthApi = builder.getHealthApi(RICInstanceMockConfiguration.INSTANCE_KEY_1);
+               Assertions.assertNotNull(healthApi);
+               ChartsApi chartsApi = builder.getChartsApi(RICInstanceMockConfiguration.INSTANCE_KEY_1);
+               Assertions.assertNotNull(chartsApi);
+       }
+
+}
diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/XappOnboarderControllerTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/controller/XappOnboarderControllerTest.java
new file mode 100644 (file)
index 0000000..ff03d94
--- /dev/null
@@ -0,0 +1,120 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.controller;
+
+import java.lang.invoke.MethodHandles;
+import java.net.URI;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.oransc.itdev.xapponboarder.client.model.Config;
+import org.oransc.itdev.xapponboarder.client.model.Descriptor;
+import org.oransc.itdev.xapponboarder.client.model.DescriptorRemote;
+import org.oransc.itdev.xapponboarder.client.model.Status;
+import org.oransc.ric.portal.dashboard.DashboardConstants;
+import org.oransc.ric.portal.dashboard.config.RICInstanceMockConfiguration;
+import org.oransc.ric.portal.dashboard.model.SuccessTransport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+
+public class XappOnboarderControllerTest extends AbstractControllerTest {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       @Test
+       public void versionTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.VERSION_METHOD);
+               logger.info("Invoking {}", uri);
+               SuccessTransport st = restTemplate.getForObject(uri, SuccessTransport.class);
+               Assertions.assertFalse(st.getData().toString().isEmpty());
+       }
+
+       @Test
+       public void healthTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.RIC_INSTANCE_KEY,
+                               RICInstanceMockConfiguration.INSTANCE_KEY_1, XappOnboarderController.HEALTH_METHOD);
+               logger.info("Invoking {}", uri);
+               ResponseEntity<Void> voidResponse = restTemplate.getForEntity(uri, Void.class);
+               Assertions.assertTrue(voidResponse.getStatusCode().is2xxSuccessful());
+       }
+
+       @Test
+       public void getAllChartsTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.RIC_INSTANCE_KEY,
+                               RICInstanceMockConfiguration.INSTANCE_KEY_1, XappOnboarderController.CHARTS_METHOD);
+               logger.info("Invoking {}", uri);
+               ResponseEntity<String> response = testRestTemplateStandardRole().exchange(uri, HttpMethod.GET, null,
+                               new ParameterizedTypeReference<String>() {
+                               });
+               Assertions.assertFalse(response.getBody().isEmpty());
+       }
+
+       @Test
+       public void getOneChartTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.RIC_INSTANCE_KEY,
+                               RICInstanceMockConfiguration.INSTANCE_KEY_1, XappOnboarderController.CHARTS_METHOD,
+                               XappOnboarderController.XAPPNAME_PP, "xapp", XappOnboarderController.VERSION_PP, "v1");
+               logger.info("Invoking {}", uri);
+               ResponseEntity<String> response = testRestTemplateStandardRole().exchange(uri, HttpMethod.GET, null,
+                               new ParameterizedTypeReference<String>() {
+                               });
+               Assertions.assertFalse(response.getBody().isEmpty());
+       }
+
+       @Test
+       public void getOneValuesTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.RIC_INSTANCE_KEY,
+                               RICInstanceMockConfiguration.INSTANCE_KEY_1, XappOnboarderController.CHARTS_METHOD,
+                               XappOnboarderController.XAPPNAME_PP, "xapp", XappOnboarderController.VERSION_PP, "v1",
+                               XappOnboarderController.VALUESYAML);
+               logger.info("Invoking {}", uri);
+               ResponseEntity<String> response = testRestTemplateStandardRole().exchange(uri, HttpMethod.GET, null,
+                               new ParameterizedTypeReference<String>() {
+                               });
+               Assertions.assertFalse(response.getBody().isEmpty());
+       }
+
+       @Test
+       public void onboardXappByBodyTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.RIC_INSTANCE_KEY,
+                               RICInstanceMockConfiguration.INSTANCE_KEY_1, XappOnboarderController.ONBOARD_METHOD);
+               logger.info("Invoking {}", uri);
+               Config configFileJson = new Config().xappName("name").version("1.0.0");
+               Descriptor descr = new Descriptor().configFileJson(configFileJson).controlsSchemaJson("{}");
+               Status status = testRestTemplateAdminRole().postForObject(uri, descr, Status.class);
+               Assertions.assertNotNull(status);
+               Assertions.assertNotNull(status.getStatus());
+       }
+
+       @Test
+       public void onboardXappByUrlTest() {
+               URI uri = buildUri(null, XappOnboarderController.CONTROLLER_PATH, DashboardConstants.RIC_INSTANCE_KEY,
+                               RICInstanceMockConfiguration.INSTANCE_KEY_1, XappOnboarderController.ONBOARD_DOWNLOAD_METHOD);
+               logger.info("Invoking {}", uri);
+               DescriptorRemote descr = new DescriptorRemote().configFileJsonUrl("url1").configFileJsonUrl("url2");
+               Status status = testRestTemplateAdminRole().postForObject(uri, descr, Status.class);
+               Assertions.assertNotNull(status);
+               Assertions.assertNotNull(status.getStatus());
+       }
+
+}
diff --git a/dashboard/webapp-backend/src/test/resources/sample-chart-list.json b/dashboard/webapp-backend/src/test/resources/sample-chart-list.json
new file mode 100644 (file)
index 0000000..e9a7b0a
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "anr": [
+    {
+      "name": "anr",
+      "version": "1.0.0",
+      "description": "Standard xApp Helm Chart",
+      "apiVersion": "v1",
+      "appVersion": "1.0",
+      "urls": [
+        "charts/anr-1.0.0.tgz"
+      ],
+      "created": "2020-06-15T14:57:25.238326423Z",
+      "digest": "d83cf464479e59999917b67305db04480c6c573b1812b8095547705d9e2ce2a1"
+    }
+  ],
+  "mcxapp": [
+    {
+      "name": "mcxapp",
+      "version": "1.0.7",
+      "description": "Standard xApp Helm Chart",
+      "apiVersion": "v1",
+      "appVersion": "1.0",
+      "urls": [
+        "charts/mcxapp-1.0.7.tgz"
+      ],
+      "created": "2020-06-15T14:59:25.734253163Z",
+      "digest": "06a0a194238b505b83e95b2d2637b26335a799f60fe3d04fde0450f98d67a782"
+    }
+  ]
+}
diff --git a/dashboard/webapp-backend/src/test/resources/sample-chart.yaml b/dashboard/webapp-backend/src/test/resources/sample-chart.yaml
new file mode 100644 (file)
index 0000000..2fb8da8
--- /dev/null
@@ -0,0 +1,20 @@
+################################################################################
+#   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.                                             #
+################################################################################
+apiVersion: v1
+appVersion: "1.0"
+description: Standard xApp Helm Chart
+name: xapp-std
+version: 0.0.1
diff --git a/dashboard/webapp-backend/src/test/resources/sample-values.yaml b/dashboard/webapp-backend/src/test/resources/sample-values.yaml
new file mode 100644 (file)
index 0000000..0579c01
--- /dev/null
@@ -0,0 +1,47 @@
+################################################################################
+#   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.                                             #
+################################################################################'
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+
+# Location of the xApp config files injected from the xApp descriptor
+appconfig:
+    path: /opt/ric/config
+
+# Number of replica
+replicaCount: 1
+
+# Image pulling policy
+image_pull_policy: IfNotPresent
+
+# Environment variables that will be injected
+appenv: {}
+
+# Liveness probe definition. If empty, liveness probe will be disabled
+livenessProbe: {}
+
+# Readiness probe definition. If empty, readiness probe will be disabled
+readinessProbe: {}
+
+# Instance name. If empty, chart name will be used
+name: {}
+
+# Full instance name. If empty, full name will be constructed from name
+fullname: {}
+###############    The following are from the xApp descriptor     ###############
+
+
+
index 92d652b..f495df3 100644 (file)
       "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
       "dev": true
     },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
     "blob": {
       "version": "0.0.5",
       "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
         "schema-utils": "^2.6.5"
       }
     },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
     "fileset": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
       "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
       "dev": true
     },
+    "nan": {
+      "version": "2.14.1",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
+      "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
+      "dev": true,
+      "optional": true
+    },
     "nanomatch": {
       "version": "1.2.13",
       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
       "integrity": "sha512-zEKehHdCK8E/k4Y0HepprGdYBHr2AOsaq4QpeqoCyUElOOC5M3qi3ZEHV1VF54I7heBQktswwXe5UyWduJ0Xeg=="
     },
     "ngx-toastr": {
-      "version": "12.0.0",
-      "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-12.0.0.tgz",
-      "integrity": "sha512-J0mvGXH9cLlP3acoLJcuYhsQcL+EOAtkS1Y5tFKzrw2TYqY2HNffMVPvK+KP1LDJHiIgRpYg3ZELw1raAl0R/A=="
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-12.1.0.tgz",
+      "integrity": "sha512-rytCRBhvuudj614Kfj9GoIVQDrFuLvHSMP1YrMwTmR1SNkNJZOpGKmaSDCCBrNDkSrGouzMWBlFbl1UDBBsiqw==",
+      "requires": {
+        "tslib": "^1.10.0"
+      }
     },
     "nice-try": {
       "version": "1.0.5",
           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
           "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
           "dev": true,
-          "optional": true
+          "optional": true,
+          "requires": {
+            "bindings": "^1.5.0",
+            "nan": "^2.12.1"
+          }
         }
       }
     },
           "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
           "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
           "dev": true,
-          "optional": true
+          "optional": true,
+          "requires": {
+            "bindings": "^1.5.0",
+            "nan": "^2.12.1"
+          }
         },
         "is-absolute-url": {
           "version": "3.0.3",
index 3721c2e..0bd61fb 100644 (file)
@@ -25,7 +25,7 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>2.0.3-SNAPSHOT</version>
+               <version>2.1.0-SNAPSHOT</version>
        </parent>
        <!-- reuse parent groupId -->
        <artifactId>ric-dash-fe</artifactId>
diff --git a/dashboard/xapp-obrd-client/.gitignore b/dashboard/xapp-obrd-client/.gitignore
new file mode 100644 (file)
index 0000000..b83d222
--- /dev/null
@@ -0,0 +1 @@
+/target/
diff --git a/dashboard/xapp-obrd-client/README.md b/dashboard/xapp-obrd-client/README.md
new file mode 100644 (file)
index 0000000..edc567f
--- /dev/null
@@ -0,0 +1,31 @@
+# Xapp Onboarder Client Generator
+
+This project generates a REST client library from the OpenAPI specification
+file obtained from the it/dev xapp_onboarder project, available here as a git
+submodule with a pinned version, and packages it in a jar.
+
+## Eclipse and STS Users
+
+The source folder should be generated automatically by the Swagger Codegen maven
+plugin and should also appear on the build path in Eclipse/STS, but if not,
+follow 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) 2020 AT&T Intellectual Property & Nokia. 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/dashboard/xapp-obrd-client/it-dev b/dashboard/xapp-obrd-client/it-dev
new file mode 160000 (submodule)
index 0000000..9945133
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 9945133851dad8f533adcc331b33aca451b0decc
diff --git a/dashboard/xapp-obrd-client/pom.xml b/dashboard/xapp-obrd-client/pom.xml
new file mode 100644 (file)
index 0000000..70ad9e7
--- /dev/null
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--<![CDATA[
+========================LICENSE_START=================================
+O-RAN-SC
+%%
+Copyright (C) 2020 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===================================
+]]>-->
+<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.portal.ric-dashboard</groupId>
+               <artifactId>ric-dash-parent</artifactId>
+               <version>2.1.0-SNAPSHOT</version>
+       </parent>
+       <!-- This groupId will NOT allow deployment in LF -->
+       <groupId>org.o-ran-sc.it-dev.xapp-onboarder.client</groupId>
+       <artifactId>xapp-obrd-client</artifactId>
+       <name>RIC Xapp Onboarder Client</name>
+       <!-- Update to match submodule tag -->
+       <version>1.0.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.itdev.xapponboarder.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>
+       </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}/it-dev/xapp_onboarder/xapp_onboarder/xapp-onboarder-api.yaml</inputSpec>
+                                                       <language>java</language>
+                                                       <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>
+                                                       <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>
+                                               </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>
+                               <!-- version>2.5</version> -->
+                               <configuration>
+                                       <archive>
+                                               <manifest>
+                                                       <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                                               </manifest>
+                                               <manifestEntries>
+                                                       <Implementation-Version>${project.version}-b${build.number}</Implementation-Version>
+                                               </manifestEntries>
+                                       </archive>
+                               </configuration>
+                       </plugin>
+                       <!-- Add generated source folder to Eclipse build path -->
+                       <plugin>
+                               <groupId>org.codehaus.mojo</groupId>
+                               <artifactId>build-helper-maven-plugin</artifactId>
+                               <!-- version>1.7</version> -->
+                               <executions>
+                                       <execution>
+                                               <phase>generate-sources</phase>
+                                               <goals>
+                                                       <goal>add-source</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <sources>
+                                                               <source>target/generated-sources/swagger/src/main/java</source>
+                                                       </sources>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </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</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/xapp-obrd-client/src/test/java/org/oransc/ric/portal/dashboard/xappobrd/client/test/XappOnboarderClientTest.java b/dashboard/xapp-obrd-client/src/test/java/org/oransc/ric/portal/dashboard/xappobrd/client/test/XappOnboarderClientTest.java
new file mode 100644 (file)
index 0000000..35aa82c
--- /dev/null
@@ -0,0 +1,48 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 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.xappobrd.client.test;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.oransc.itdev.xapponboarder.client.api.HealthApi;
+import org.oransc.itdev.xapponboarder.client.invoker.ApiClient;
+import org.springframework.web.client.RestClientException;
+
+/**
+ * Demonstrates use of the generated xapp onboarder client.
+ * 
+ * The get fails because no server is available.
+ */
+public class XappOnboarderClientTest {
+
+       @Test
+       public void demo() {
+               ApiClient apiClient = new ApiClient();
+               apiClient.setBasePath("http://localhost:30099/");
+               HealthApi healthApi = new HealthApi(apiClient);
+               try {
+                       healthApi.getHealthCheck();
+                       System.out.println("getHealthCheck answered: " + apiClient.getStatusCode().toString());
+                       Assertions.assertTrue(apiClient.getStatusCode().is2xxSuccessful());
+               } catch (RestClientException e) {
+                       System.err.println("getHealthCheck failed: " + e.toString());
+               }
+       }
+}
index f22b103..268ee78 100644 (file)
@@ -48,7 +48,7 @@ The application uses the following properties.
 
 ``appmgr.url.suffix``
 
-Application Manager URL suffix. Default is ``/ric/v1``.
+Application Manager URL suffix. Default is ``/appmgr/ric/v1``.
 
 ``caasingress.aux.url.suffix``
 
@@ -66,7 +66,7 @@ CAAS-Ingress application URL suffix for the RIC-PLT cluster. Default is ``api``.
 
 ``e2mgr.url.suffix``
 
-E2 Manager URL suffix. Default is ``/v1``.
+E2 Manager URL suffix. Default is ``/e2mgr/v1``.
 
 ``portalapi.appname``
 
@@ -159,6 +159,9 @@ Path of file that stores application statistic details. Default is
 Path of file that stores user details. Default is
 ``dashboard-users.json``.
 
+``xappobrd.url.suffix``
+
+Xapp Onboarder URL suffix. Default is ``/xapporbd/api/v1``.
 
 Key Properties
 ^^^^^^^^^^^^^^
index e5b8d5e..200ac4d 100644 (file)
@@ -5,6 +5,10 @@
 RIC Dashboard Release Notes
 ===========================
 
+Version 2.1.0, 8 Jun 2020
+--------------------------
+* Add Xapp Onboarder client to backend (`OAM-108 <https://jira.o-ran-sc.org/browse/OAM-108>`_)
+
 Version 2.0.3, 3 Jun 2020
 --------------------------
 * Validate request bodies in backend REST controller methods