Add AC controller with get/put policy methods 62/162/7
authorLott, Christopher (cl778h) <cl778h@att.com>
Tue, 14 May 2019 23:43:50 +0000 (19:43 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Wed, 15 May 2019 19:29:50 +0000 (15:29 -0400)
Upgrade ANR spec to version 0.0.4.
Drop the A1 Mediation Controller for pendulum demo.
Add footer with version info.
Add get-version endpoints for clients.

Change-Id: Ie45d412b8f126853fa59d74bb50e74fdecd94688
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
41 files changed:
a1-med-client/pom.xml
anr-xapp-client/pom.xml
anr-xapp-client/src/main/resources/anr_swagger_0.0.4.yaml [moved from anr-xapp-client/src/main/resources/anr_swagger_0.0.3.yaml with 84% similarity]
docs/release-notes.rst
e2-mgr-client/pom.xml
pom.xml
webapp-backend/pom.xml
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardConstants.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/AnrXappMockConfiguration.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1MediationController.java [deleted file]
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java [new file with mode: 0644]
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AnrXappController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/E2ManagerController.java
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/HealthcheckController.java [new file with mode: 0644]
webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/XappManagerController.java
webapp-frontend/package-lock.json
webapp-frontend/package.json
webapp-frontend/pom.xml
webapp-frontend/src/app/app.component.css
webapp-frontend/src/app/app.component.html
webapp-frontend/src/app/app.module.ts
webapp-frontend/src/app/footer/footer.component.html [new file with mode: 0644]
webapp-frontend/src/app/footer/footer.component.scss [new file with mode: 0644]
webapp-frontend/src/app/footer/footer.component.spec.ts [new file with mode: 0644]
webapp-frontend/src/app/footer/footer.component.ts [new file with mode: 0644]
webapp-frontend/src/app/interfaces/ac-xapp.types.ts [new file with mode: 0644]
webapp-frontend/src/app/interfaces/anr-xapp.types.ts [new file with mode: 0644]
webapp-frontend/src/app/interfaces/dashboard.types.ts [new file with mode: 0644]
webapp-frontend/src/app/interfaces/e2-mgr.types.ts [new file with mode: 0644]
webapp-frontend/src/app/interfaces/xapp-mgr.types.ts [moved from webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/UrlTransport.java with 59% similarity]
webapp-frontend/src/app/services/catalog/catalog.service.ts
webapp-frontend/src/app/services/signal/signal.service.ts
webapp-frontend/src/app/services/version/version.service.spec.ts [new file with mode: 0644]
webapp-frontend/src/app/services/version/version.service.ts [new file with mode: 0644]
webapp-frontend/src/app/signal/signal.component.ranconnect-dialog.html
webapp-frontend/src/app/signal/signal.component.ts
webapp-frontend/src/app/ui/catalog-card/catalog-card.component.html
webapp-frontend/src/app/ui/control-card/control-card.component.html
webapp-frontend/src/index.html
xapp-mgr-client/pom.xml

index c586b66..8c106b0 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>1.0.2-SNAPSHOT</version>
+               <version>1.0.3-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.a1med.client</groupId>
@@ -35,6 +35,9 @@ limitations under the License.
        <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.a1med.client</client.base.package.name>
        </properties>
        <!-- Successful compilation requires generated code dependencies -->
@@ -145,6 +148,23 @@ limitations under the License.
                                        </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>
                        <!-- Skip the deploy-jar-to-nexus step -->
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
index 08689f9..e450c1d 100644 (file)
@@ -25,16 +25,18 @@ limitations under the License.
        <parent>
                <groupId>org.o-ran-sc.portal.ric-dashboard</groupId>
                <artifactId>ric-dash-parent</artifactId>
-               <version>1.0.2-SNAPSHOT</version>
+               <version>1.0.3-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.anrxapp.client</groupId>
        <artifactId>anr-xapp-client</artifactId>
        <name>RIC ANR xApp client</name>
-       <version>0.0.3-SNAPSHOT</version>
+       <version>0.0.4-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.anrxapp.client</client.base.package.name>
        </properties>
@@ -102,7 +104,7 @@ limitations under the License.
                                                        <goal>generate</goal>
                                                </goals>
                                                <configuration>
-                                                       <inputSpec>${project.basedir}/src/main/resources/anr_swagger_0.0.3.yaml</inputSpec>
+                                                       <inputSpec>${project.basedir}/src/main/resources/anr_swagger_0.0.4.yaml</inputSpec>
                                                        <language>java</language>
                                                        <configOptions>
                                                                <groupId>${project.groupId}</groupId>
@@ -148,6 +150,23 @@ limitations under the License.
                                        </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>
                        <!-- Skip the deploy-jar-to-nexus step -->
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
@@ -179,7 +198,7 @@ limitations under the License.
                                                                                </goals>
                                                                        </pluginExecutionFilter>
                                                                        <action>
-                                                                               <ignore/>
+                                                                               <ignore />
                                                                        </action>
                                                                </pluginExecution>
                                                        </pluginExecutions>
@@ -18,7 +18,7 @@
 swagger : '2.0'
 info :
   description : REST API specification for RIC ANR closed xAPP
-  version     : 0.0.3
+  version     : 0.0.4
   title       : RIC ANR
   license:
     name : Nokia Closed App
@@ -31,7 +31,7 @@ paths :
   /health/alive :
     get :
       summary     : Health check of ANR - Liveness probe
-      tags        : 
+      tags        :
         - health
       operationId : getHealthAlive
       responses   :
@@ -40,7 +40,7 @@ paths :
   /health/ready :
     get :
       summary     : Health check of ANR - Readiness probe
-      tags        : 
+      tags        :
         - health
       operationId : getHealthReady
       responses   :
@@ -53,7 +53,7 @@ paths :
       - $ref : '#/parameters/limit'
     get :
       summary     : Returns neighbor cell relation table for all gnodeBs or based on query string
-      tags        : 
+      tags        :
         - ncrt
       operationId : getNcrtInfo
       produces  :
@@ -68,35 +68,36 @@ paths :
   /ncrt/cell/{cellIdentifier} :
     parameters :
       - $ref : '#/parameters/cellIdentifier'
-      - $ref : '#/parameters/startIndex'
-      - $ref : '#/parameters/limit'
-      - $ref : '#/parameters/neighborCellIdentifierNrpci'
-      - $ref : '#/parameters/neighborCellIdentifierNrcgi'
+      - $ref : '#/parameters/neighborCellNrpci'
+      - $ref : '#/parameters/neighborCellNrcgi'
     get :
       summary     : Returns neighbor cell relation table for a Source Cell (NR CGI) and optionally a neighbor cell based on query string
-      tags        : 
+      tags        :
         - ncrt
       operationId : getCellNcrtInfo
       produces  :
         - application/json
+      parameters :
+        - $ref : '#/parameters/startIndex'
+        - $ref : '#/parameters/limit'
       responses :
         '200' :
           description : successful operation
           schema      :
             $ref : '#/definitions/neighborCellRelationTable'
         '400' :
-          description : Non-existant identifier {cellIdentifier | neighborCellIdentifierNrpci | neighborCellIdentifierNrcgi} in request
+          description : Non-existant identifier {cellIdentifier | neighborCellNrpci | neighborCellNrcgi} in request
     put :
       summary     : Modify neighbor cell relation based on Source Cell NR CGI and Target Cell NR PCI / NR CGI
-      tags        : 
+      tags        :
         - ncrt
-      operationId : modifyNCRT
+      operationId : modifyNcrt
       consumes   :
         - application/json
       produces   :
         - application/json
       parameters :
-        - name        : NCRTModificationParameters
+        - name        : NcrtModificationParameters
           in          : body
           description : Parameters to modify neighbor cell relation
           required    : true
@@ -107,29 +108,29 @@ paths :
           description : Successfully modified neighbor cell relation
         '400' :
           description : Modification failed.
-                        { Identifier neighborCellIdentifierNrpci / neighborCellIdentifierNrcgi not provided |
-                          Non-existant identifier {cellIdentifier | neighborCellIdentifierNrpci | neighborCellIdentifierNrcgi} in request |
+                        { Identifier neighborCellNrpci / neighborCellNrcgi not provided |
+                          Non-existant identifier {cellIdentifier | neighborCellNrpci | neighborCellNrcgi} in request |
                           Invalid data in body
                         }
     delete :
       summary     : Delete neighbor cell relation based on Source Cell NR CGI and Target Cell NR PCI / NR CGI
-      tags        : 
+      tags        :
         - ncrt
       operationId : deleteNcrt
       parameters :
-        - name        : NCRTDeletionParameters
+        - name        : NcrtDeletionParameters
           in          : body
           description : Parameters to delete neighbor cell relation
           required    : true
           schema      :
-            $ref : '#/definitions/neighborCellRelationDelTable'      
+            $ref : '#/definitions/neighborCellRelationDelTable'
       responses :
         '204' :
           description : Successfully deleted neighbor cell relation
         '400' :
           description : Deletion failed.
-                        { Identifier neighborCellIdentifierNrpci / neighborCellIdentifierNrcgi not provided |
-                          Non-existant identifier {cellIdentifier | neighborCellIdentifierNrpci | neighborCellIdentifierNrcgi} in request
+                        { Identifier neighborCellNrpci / neighborCellNrcgi not provided |
+                          Non-existant identifier {cellIdentifier | neighborCellNrpci | neighborCellNrcgi} in request
                         }
 parameters :
   ggnbId :
@@ -153,21 +154,21 @@ parameters :
     description : Source Cell Identifier (NR CGI)
     in          : path
     required    : true
-  neighborCellIdentifierNrpci :
+  neighborCellNrpci :
     type        : string
-    name        : neighborCellIdentifierNrpci
+    name        : neighborCellNrpci
     description : Neighbor Cell Identifier (NR PCI)
     in          : query
-  neighborCellIdentifierNrcgi :
+  neighborCellNrcgi :
     type        : string
-    name        : neighborCellIdentifierNrcgi
+    name        : neighborCellNrcgi
     description : Neigbhor Cell Identifier (NR CGI)
     in          : query
 definitions :
   neighborCellRelationTable :
     type : object
-    properties : 
-      ncrtRelations : 
+    properties :
+      ncrtRelations :
         type   : array
         items  :
           $ref : '#/definitions/neighborCellRelation'
@@ -181,14 +182,14 @@ definitions :
       $ref : '#/definitions/neighborCellRelationMod'
   neighborCellRelationDelTable :
     type   : array
-    items  : 
+    items  :
       $ref : '#/definitions/neighborCellRelationDel'
   neighborCellRelation :
     type  : object
-    required : 
+    required :
       - cellIdentifierNrcgi
-      - neighborCellIdentifierNrpci
-      - neighborCellIdentifierNrcgi
+      - neighborCellNrpci
+      - neighborCellNrcgi
       - flagNoHo
       - flagNoXn
       - flagNoRemove
@@ -197,11 +198,11 @@ definitions :
         type        : string
         description : Source / Serving Cell Identifier (NR CGI)
         example     : E12345
-      neighborCellIdentifierNrpci :
+      neighborCellNrpci :
         type        : string
         description : Neighbor Cell Identifier (NR PCI)
         example     : E12345
-      neighborCellIdentifierNrcgi :
+      neighborCellNrcgi :
         type        : string
         description : Neighbor Cell Identifier (NR CGI)
         example     : E12345
@@ -236,11 +237,11 @@ definitions :
           - nrpciToNrcgiMapping
           - flagUpdates
           - nrcgiMappingAndFlagUpdates
-      neighborCellIdentifierNrpci :
+      neighborCellNrpci :
         type        : string
         description : Neighbor Cell Identifier (NR PCI)
         example     : E12345
-      neighborCellIdentifierNrcgi :
+      neighborCellNrcgi :
         type        : string
         description : Neighbor Cell Identifier (NR CGI)
         example     : E12345
@@ -267,11 +268,11 @@ definitions :
         enum :
           - nrpci
           - nrcgi
-      neighborCellIdentifierNrpci :
+      neighborCellNrpci :
         type        : string
         description : Neighbor Cell Identifier (NR PCI)
         example     : E12345
-      neighborCellIdentifierNrcgi :
+      neighborCellNrcgi :
         type        : string
         description : Neighbor Cell Identifier (NR PCI)
         example     : E12345
index 19f3b57..7f75207 100644 (file)
 RIC Dashboard Release Notes
 ===========================
 
+Version 1.0.3, 15 May 2019
+--------------------------
+* Add AC xapp controller
+* Add RAN type radio selector to connection setup
+* Update ANR xApp client to spec version 0.0.4
+* Add get-version methods to all controllers
+* Add simple page footer with copyright and version
+
 Version 1.0.2, 13 May 2019
 --------------------------
 * Update A1 mediator client to version 0.4.0
index 46f86d9..48caf8e 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>1.0.2-SNAPSHOT</version>
+               <version>1.0.3-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.e2mgr.client</groupId>
@@ -35,6 +35,9 @@ limitations under the License.
        <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.e2mgr.client</client.base.package.name>
        </properties>
        <!-- Successful compilation requires generated code dependencies -->
@@ -145,6 +148,23 @@ limitations under the License.
                                        </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>
                        <!-- Skip the deploy-jar-to-nexus step -->
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
diff --git a/pom.xml b/pom.xml
index c9b2878..1946823 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@ limitations under the License.
        <artifactId>ric-dash-parent</artifactId>
        <name>RIC Dashboard project</name>
        <packaging>pom</packaging>
-       <version>1.0.2-SNAPSHOT</version>
+       <version>1.0.3-SNAPSHOT</version>
        <!-- Properties for the license-maven-plugin in child POMs -->
        <properties>
                <lmp.organization.name>AT&amp;T Intellectual Property and Nokia</lmp.organization.name>
index 426e7ce..f14db0c 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>1.0.2-SNAPSHOT</version>
+               <version>1.0.3-SNAPSHOT</version>
        </parent>
        <artifactId>ric-dash-be</artifactId>
        <name>RIC Dashboard Webapp backend</name>
@@ -42,7 +42,7 @@ limitations under the License.
                <dependency>
                        <groupId>org.o-ran-sc.ric.anrxapp.client</groupId>
                        <artifactId>anr-xapp-client</artifactId>
-                       <version>0.0.3-SNAPSHOT</version>
+                       <version>0.0.4-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.o-ran-sc.ric.e2mgr.client</groupId>
index 8c86a60..c9a98e4 100644 (file)
@@ -38,19 +38,21 @@ public class DashboardApplication {
        public static void main(String[] args) {
                SpringApplication.run(DashboardApplication.class, args);
                // Force this onto the console by using level WARN
-               logger.warn("main: version '{}' successful start", getVersion());
+               logger.warn("main: version '{}' successful start", getImplementationVersion(MethodHandles.lookup().lookupClass()));
        }
 
        /**
-        * Gets version details.
+        * 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.
         */
-       private static String getVersion() {
-               Class<?> clazz = MethodHandles.lookup().lookupClass();
+       public static String getImplementationVersion(Class<?> clazz) {
                String classPath = clazz.getResource(clazz.getSimpleName() + ".class").toString();
-               return classPath.startsWith("jar") ? clazz.getPackage().getImplementationVersion() : "unknown";
+               return classPath.startsWith("jar") ? clazz.getPackage().getImplementationVersion() : "unknown-not-jar";
        }
 
 }
index e73f697..c913efb 100644 (file)
@@ -26,4 +26,7 @@ public abstract class DashboardConstants {
        }
 
        public static final String ENDPOINT_PREFIX = "/api/";
+       public static final String HEALTHCHECK_PATH = "health";
+       public static final String VERSION_PATH = "version";
+
 }
index 32b507f..275f047 100644 (file)
@@ -80,28 +80,26 @@ public class AnrXappMockConfiguration {
                NcrtApi mockApi = mock(NcrtApi.class);
                when(mockApi.getApiClient()).thenReturn(apiClient);
 
-               NeighborCellRelation a = new NeighborCellRelation().cellIdentifierNrcgi("A12345")
-                               .neighborCellIdentifierNrpci("A123456").neighborCellIdentifierNrcgi("A12347").flagNoHo(true)
-                               .flagNoXn(true).flagNoRemove(true);
-               NeighborCellRelation e = new NeighborCellRelation().cellIdentifierNrcgi("E12345")
-                               .neighborCellIdentifierNrpci("E123456").neighborCellIdentifierNrcgi("E12347").flagNoHo(true)
-                               .flagNoXn(true).flagNoRemove(true);
+               NeighborCellRelation a = new NeighborCellRelation().cellIdentifierNrcgi("A12345").neighborCellNrpci("A123456")
+                               .neighborCellNrcgi("A12347").flagNoHo(true).flagNoXn(true).flagNoRemove(true);
+               NeighborCellRelation e = new NeighborCellRelation().cellIdentifierNrcgi("E12345").neighborCellNrpci("E123456")
+                               .neighborCellNrcgi("E12347").flagNoHo(true).flagNoXn(true).flagNoRemove(true);
                NeighborCellRelationTable ncrt = new NeighborCellRelationTable().addNcrtRelationsItem(a)
                                .addNcrtRelationsItem(e);
 
                when(mockApi.getNcrtInfo(any(String.class), any(String.class), any(Integer.class))).thenReturn(ncrt);
-               when(mockApi.getCellNcrtInfo(any(String.class), any(String.class), any(Integer.class), any(String.class),
-                               any(String.class))).thenReturn(ncrt);
+               when(mockApi.getCellNcrtInfo(any(String.class), any(String.class), any(String.class), any(String.class),
+                               any(Integer.class))).thenReturn(ncrt);
 
                doAnswer(i -> {
                        return null;
                }).when(mockApi).deleteNcrt(any(String.class), any(NeighborCellRelationDelTable.class), any(String.class),
-                               any(Integer.class), any(String.class), any(String.class));
+                               any(String.class));
 
                doAnswer(i -> {
                        return null;
-               }).when(mockApi).modifyNCRT(any(String.class), any(NeighborCellRelationModTable.class), any(String.class),
-                               any(Integer.class), any(String.class), any(String.class));
+               }).when(mockApi).modifyNcrt(any(String.class), any(NeighborCellRelationModTable.class), any(String.class),
+                               any(String.class));
 
                return mockApi;
        }
diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1MediationController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/A1MediationController.java
deleted file mode 100644 (file)
index 1cbda95..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*-
- * ========================LICENSE_START=================================
- * O-RAN-SC
- * %%
- * Copyright (C) 2019 AT&T Intellectual Property and Nokia
- * %%
- * 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.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-
-import javax.servlet.http.HttpServletResponse;
-
-import org.oransc.ric.portal.dashboard.DashboardConstants;
-import org.oransc.ric.portal.dashboard.model.DelayTransport;
-import org.oransc.ric.portal.dashboard.model.ErrorTransport;
-import org.oransc.ric.portal.dashboard.model.IDashboardResponse;
-import org.oransc.ric.portal.dashboard.model.LoadTransport;
-import org.oransc.ric.portal.dashboard.model.MetricsTransport;
-import org.oransc.ric.portal.dashboard.model.PathTransport;
-import org.oransc.ric.portal.dashboard.model.SuccessTransport;
-import org.oransc.ric.portal.dashboard.model.UrlTransport;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.client.RestTemplate;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import io.swagger.annotations.ApiOperation;
-
-/**
- * Provides endpoints to get/set paths, AND to access the REST resources at
- * those paths. This allows very late binding of deployment details.
- */
-@Configuration
-@RestController
-@RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/a1med", produces = MediaType.APPLICATION_JSON_VALUE)
-public class A1MediationController {
-
-       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-       private static final String A1_MEDIATION_URL = "url";
-       private static final String A1_MEDIATION_DELAY = "delay";
-       private static final String A1_MEDIATION_DELAY_PATH = A1_MEDIATION_DELAY + "path";
-       private static final String A1_MEDIATION_LOAD = "load";
-       private static final String A1_MEDIATION_LOAD_PATH = A1_MEDIATION_LOAD + "path";
-       private static final String A1_MEDIATION_METRICS = "metrics";
-       private static final String A1_MEDIATION_METRICS_PATH = A1_MEDIATION_METRICS + "path";
-
-       @Value("${a1med.basepath}")
-       private String a1MediationUrl;
-       @Value("${a1med.delaypath}")
-       private String a1MediationDelayPath;
-       @Value("${a1med.loadpath}")
-       private String a1MediationLoadPath;
-       @Value("${a1med.metricspath}")
-       private String a1MediationMetricsPath;
-
-       // For demo purposes
-       private final boolean mockData = true;
-       private final DelayTransport mockDelay = new DelayTransport(10);
-       private final LoadTransport mockLoad = new LoadTransport(1);
-       private final MetricsTransport mockMetrics = new MetricsTransport(11, 100, 123);
-
-       private final RestTemplate restTemplate = new RestTemplate();
-
-       private URI buildUri(final String baseUrl, final String[] paths) {
-               UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl);
-               for (int p = 0; p < paths.length; ++p) {
-                       if (paths[p] == null)
-                               throw new IllegalArgumentException("Unexpected null at index " + Integer.toString(p));
-                       // this allows slashes
-                       builder.path(paths[p]);
-               }
-               return builder.build().encode().toUri();
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation URL.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_URL, method = RequestMethod.GET)
-       public IDashboardResponse getA1MediationUrl() {
-               return new UrlTransport(a1MediationUrl);
-       }
-
-       @ApiOperation(value = "Sets the A1 Mediation URL.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_URL, method = RequestMethod.PUT)
-       public IDashboardResponse setA1MediationUrl(@RequestBody UrlTransport st, HttpServletResponse response) {
-               try {
-                       this.a1MediationUrl = new URL(st.getUrl()).toString();
-                       return new SuccessTransport(HttpServletResponse.SC_OK, null);
-               } catch (MalformedURLException ex) {
-                       logger.error("Failed to parse url " + st.getUrl(), ex);
-                       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-                       return new ErrorTransport(400, "Bad URL", ex);
-               }
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation delay path.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_DELAY_PATH, method = RequestMethod.GET)
-       public IDashboardResponse getA1MediationDelayPath() {
-               return new PathTransport(a1MediationDelayPath);
-       }
-
-       @ApiOperation(value = "Sets the A1 Mediation delay path.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_DELAY_PATH, method = RequestMethod.PUT)
-       public IDashboardResponse setA1MediationDelayPath(@RequestBody PathTransport st) {
-               this.a1MediationDelayPath = st.getPath();
-               return new SuccessTransport(HttpServletResponse.SC_OK, null);
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation load path.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_LOAD_PATH, method = RequestMethod.GET)
-       public IDashboardResponse getA1MediationLoadPath() {
-               return new PathTransport(a1MediationLoadPath);
-       }
-
-       @ApiOperation(value = "Sets the A1 Mediation load path.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_LOAD_PATH, method = RequestMethod.PUT)
-       public IDashboardResponse setA1MediationLoadPath(@RequestBody PathTransport st) {
-               this.a1MediationLoadPath = st.getPath();
-               return new SuccessTransport(HttpServletResponse.SC_OK, null);
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation metrics path.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_METRICS_PATH, method = RequestMethod.GET)
-       public IDashboardResponse getA1MediationMetricsPath() {
-               return new PathTransport(a1MediationMetricsPath);
-       }
-
-       @ApiOperation(value = "Sets the A1 Mediation metrics path.", response = IDashboardResponse.class)
-       @RequestMapping(value = A1_MEDIATION_METRICS_PATH, method = RequestMethod.PUT)
-       public IDashboardResponse setA1MediationMetricsPath(@RequestBody PathTransport st) {
-               this.a1MediationMetricsPath = st.getPath();
-               return new SuccessTransport(HttpServletResponse.SC_OK, null);
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation delay value.", response = DelayTransport.class)
-       @RequestMapping(value = A1_MEDIATION_DELAY, method = RequestMethod.GET)
-       public DelayTransport getA1MediationDelay() {
-               if (mockData) {
-                       return mockDelay;
-               } else {
-                       URI uri = buildUri(a1MediationUrl, new String[] { a1MediationDelayPath });
-                       logger.debug("getA1MediationDelay: uri {}", uri);
-                       ResponseEntity<DelayTransport> response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<DelayTransport>() {
-                                       });
-                       return response.getBody();
-               }
-       }
-
-       @ApiOperation(value = "Sets the A1 Mediation delay value.")
-       @RequestMapping(value = A1_MEDIATION_DELAY, method = RequestMethod.PUT)
-       public void putA1MediationDelay(DelayTransport value) {
-               if (mockData) {
-                       mockDelay.setDelay(value.getDelay());
-               } else {
-                       URI uri = buildUri(a1MediationUrl, new String[] { a1MediationDelayPath });
-                       logger.debug("putA1MediationDelay: uri {}", uri);
-                       restTemplate.put(uri, value);
-               }
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation load value.", response = LoadTransport.class)
-       @RequestMapping(value = A1_MEDIATION_LOAD, method = RequestMethod.GET)
-       public LoadTransport getA1MediationLoad() {
-               if (mockData) {
-                       return mockLoad;
-               } else {
-                       URI uri = buildUri(a1MediationUrl, new String[] { a1MediationLoadPath });
-                       logger.debug("getA1MediationLoad: uri {}", uri);
-                       ResponseEntity<LoadTransport> response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<LoadTransport>() {
-                                       });
-                       return response.getBody();
-               }
-       }
-
-       @ApiOperation(value = "Sets the A1 Mediation delay value.")
-       @RequestMapping(value = A1_MEDIATION_LOAD, method = RequestMethod.PUT)
-       public void putA1MediationLoad(LoadTransport value) {
-               if (mockData) {
-                       mockLoad.setLoad(value.getLoad());
-               } else {
-                       URI uri = buildUri(a1MediationUrl, new String[] { a1MediationDelayPath });
-                       logger.debug("putA1MediationLoad: uri {}", uri);
-                       restTemplate.put(uri, value);
-               }
-       }
-
-       @ApiOperation(value = "Gets the A1 Mediation metrics object.", response = MetricsTransport.class)
-       @RequestMapping(value = A1_MEDIATION_METRICS, method = RequestMethod.GET)
-       public MetricsTransport getA1MediationMetrics() {
-               if (mockData) {
-                       return mockMetrics;
-               } else {
-                       URI uri = buildUri(a1MediationUrl, new String[] { a1MediationLoadPath });
-                       logger.debug("getA1MediationMetrics: uri {}", uri);
-                       ResponseEntity<MetricsTransport> response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<MetricsTransport>() {
-                                       });
-                       return response.getBody();
-               }
-       }
-
-}
diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/AcXappController.java
new file mode 100644 (file)
index 0000000..859efb8
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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 javax.servlet.http.HttpServletResponse;
+
+import org.oransc.ric.a1med.client.api.A1MediatorApi;
+import org.oransc.ric.portal.dashboard.DashboardApplication;
+import org.oransc.ric.portal.dashboard.DashboardConstants;
+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.MediaType;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+/**
+ * Provides methods to manage policies of the Admission Control xApp. All
+ * messages go via the A1 Mediatior.
+ */
+@RestController
+@RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/xapp/ac", produces = MediaType.APPLICATION_JSON_VALUE)
+public class AcXappController {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       private static final String POLICY_NAME = "policyname";
+
+       // Populated by the autowired constructor
+       private final A1MediatorApi a1MediatorApi;
+
+       @Autowired
+       public AcXappController(final A1MediatorApi a1MediatorApi) {
+               Assert.notNull(a1MediatorApi, "API must not be null");
+               this.a1MediatorApi = a1MediatorApi;
+       }
+
+       @ApiOperation(value = "Gets the A1 client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
+       @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
+       public SuccessTransport getVersion() {
+               logger.debug("getVersion enter");
+               return new SuccessTransport(200, DashboardApplication.getImplementationVersion(A1MediatorApi.class));
+       }
+
+       @ApiOperation(value = "Gets the named policy for AC xApp via the A1 Mediator")
+       @RequestMapping(value = "policy/{" + POLICY_NAME + "}", method = RequestMethod.GET)
+       public Object getPolicy(@PathVariable(POLICY_NAME) String policyName) {
+               logger.debug("getPolicy: policy {}", policyName);
+               a1MediatorApi.a1ControllerGetHandler(policyName);
+               return null;
+       }
+
+       @ApiOperation(value = "Sets the named policy for AC xApp via the A1 Mediator")
+       @RequestMapping(value = "policy/{" + POLICY_NAME + "}", method = RequestMethod.PUT)
+       public void putPolicy(@PathVariable(POLICY_NAME) String policyName, //
+                       @ApiParam(value = "JSON formatted policy") @RequestBody JsonNode policy, //
+                       HttpServletResponse response) {
+               logger.debug("putPolicy: policy {}", policyName);
+               a1MediatorApi.a1ControllerPutHandler(policyName, policy);
+               response.setStatus(a1MediatorApi.getApiClient().getStatusCode().value());
+       }
+
+}
index 3654356..5692c5e 100644 (file)
@@ -28,7 +28,9 @@ import org.oransc.ric.anrxapp.client.api.NcrtApi;
 import org.oransc.ric.anrxapp.client.model.NeighborCellRelationDelTable;
 import org.oransc.ric.anrxapp.client.model.NeighborCellRelationModTable;
 import org.oransc.ric.anrxapp.client.model.NeighborCellRelationTable;
+import org.oransc.ric.portal.dashboard.DashboardApplication;
 import org.oransc.ric.portal.dashboard.DashboardConstants;
+import org.oransc.ric.portal.dashboard.model.SuccessTransport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -50,7 +52,7 @@ import io.swagger.annotations.ApiOperation;
  */
 @Configuration
 @RestController
-@RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/ncrt", produces = MediaType.APPLICATION_JSON_VALUE)
+@RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/xapp/anr", produces = MediaType.APPLICATION_JSON_VALUE)
 public class AnrXappController {
 
        private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -74,6 +76,13 @@ public class AnrXappController {
                this.ncrtApi = ncrtApi;
        }
 
+       @ApiOperation(value = "Gets the ANR client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
+       @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
+       public SuccessTransport getVersion() {
+               logger.debug("getVersion enter");
+               return new SuccessTransport(200, DashboardApplication.getImplementationVersion(HealthApi.class));
+       }
+
        @ApiOperation(value = "Performs a liveness probe on the ANR xApp, result expressed as the response code.")
        @RequestMapping(value = "/health/alive", method = RequestMethod.GET)
        public void getHealthAlive(HttpServletResponse response) {
@@ -109,7 +118,7 @@ public class AnrXappController {
                        @RequestParam(name = NRCGI, required = false) String nrcgi) {
                logger.debug("queryNcrtAllCells: cellIdentifier {}, startIndex {} limit {} nrpci {} nrcgi {}", cellIdentifier,
                                startIndex, limit, nrpci, nrcgi);
-               return ncrtApi.getCellNcrtInfo(cellIdentifier, startIndex, limit, nrpci, nrcgi);
+               return ncrtApi.getCellNcrtInfo(cellIdentifier,  nrpci, nrcgi, startIndex, limit);
        }
 
        @ApiOperation(value = "Modify neighbor cell relation based on Source Cell NR CGI and Target Cell NR PCI / NR CGI")
@@ -118,7 +127,7 @@ public class AnrXappController {
                        @RequestBody NeighborCellRelationModTable ncrtModTable, //
                        HttpServletResponse response) {
                logger.debug("modifyNcrt: cellIdentifier {} modTable {}", cellIdentifier, ncrtModTable);
-               ncrtApi.modifyNCRT(cellIdentifier, ncrtModTable, null, null, null, null);
+               ncrtApi.modifyNcrt(cellIdentifier, ncrtModTable, null, null);
                response.setStatus(healthApi.getApiClient().getStatusCode().value());
        }
 
@@ -128,7 +137,7 @@ public class AnrXappController {
                        @RequestBody NeighborCellRelationDelTable ncrtDelTable, //
                        HttpServletResponse response) {
                logger.debug("modifyNcrt: cellIdentifier {} delTable {}", cellIdentifier, ncrtDelTable);
-               ncrtApi.deleteNcrt(cellIdentifier, ncrtDelTable, null, null, null, null);
+               ncrtApi.deleteNcrt(cellIdentifier, ncrtDelTable, null, null);
                response.setStatus(healthApi.getApiClient().getStatusCode().value());
        }
 }
index 26b404a..8b1fe72 100644 (file)
@@ -27,10 +27,12 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.oransc.ric.e2mgr.client.api.E2ManagerApi;
 import org.oransc.ric.e2mgr.client.model.SetupRequest;
+import org.oransc.ric.portal.dashboard.DashboardApplication;
 import org.oransc.ric.portal.dashboard.DashboardConstants;
 import org.oransc.ric.portal.dashboard.model.E2SetupRequestType;
 import org.oransc.ric.portal.dashboard.model.E2SetupResponse;
 import org.oransc.ric.portal.dashboard.model.IDashboardResponse;
+import org.oransc.ric.portal.dashboard.model.SuccessTransport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -82,6 +84,13 @@ public class E2ManagerController {
                        throw new IllegalArgumentException("Empty not permitted");
        }
 
+       @ApiOperation(value = "Gets the E2 manager client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
+       @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
+       public SuccessTransport getVersion() {
+               logger.debug("getVersion enter");
+               return new SuccessTransport(200, DashboardApplication.getImplementationVersion(E2ManagerApi.class));
+       }
+
        @ApiOperation(value = "Gets the health from the E2 manager, expressed as the response code.")
        @RequestMapping(value = "/health", method = RequestMethod.GET)
        public void getHealth(HttpServletResponse response) {
diff --git a/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/HealthcheckController.java b/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/HealthcheckController.java
new file mode 100644 (file)
index 0000000..9509c71
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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.ric.portal.dashboard.DashboardApplication;
+import org.oransc.ric.portal.dashboard.DashboardConstants;
+import org.oransc.ric.portal.dashboard.model.SuccessTransport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * Answers REST requests for the API service health etc.
+ */
+@RestController
+@RequestMapping(value = DashboardConstants.ENDPOINT_PREFIX + "/dashboard", produces = MediaType.APPLICATION_JSON_VALUE)
+public class HealthcheckController {
+
+       private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+       @ApiOperation(value = "Checks the health of the application by (TBD).", response = SuccessTransport.class)
+       @RequestMapping(value = DashboardConstants.HEALTHCHECK_PATH, method = RequestMethod.GET)
+       public SuccessTransport getHealth() {
+               logger.debug("getHealth enter");
+               long count = 0;
+               return new SuccessTransport(200, "(TBD) reports count is " + count);
+       }
+
+       @ApiOperation(value = "Gets the Dashboard MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
+       @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
+       public SuccessTransport getVersion() {
+               logger.debug("getVersion enter");
+               return new SuccessTransport(200, DashboardApplication.getImplementationVersion(MethodHandles.lookup().lookupClass()));
+       }
+
+}
index ef1a918..002bf0e 100644 (file)
@@ -23,8 +23,10 @@ import java.lang.invoke.MethodHandles;
 
 import javax.servlet.http.HttpServletResponse;
 
+import org.oransc.ric.portal.dashboard.DashboardApplication;
 import org.oransc.ric.portal.dashboard.DashboardConstants;
 import org.oransc.ric.portal.dashboard.model.ErrorTransport;
+import org.oransc.ric.portal.dashboard.model.SuccessTransport;
 import org.oransc.ric.xappmgr.client.api.HealthApi;
 import org.oransc.ric.xappmgr.client.api.XappApi;
 import org.oransc.ric.xappmgr.client.model.AllXapps;
@@ -71,6 +73,13 @@ public class XappManagerController {
                this.xappApi = xappApi;
        }
 
+       @ApiOperation(value = "Gets the XApp manager client library MANIFEST.MF property Implementation-Version.", response = SuccessTransport.class)
+       @RequestMapping(value = DashboardConstants.VERSION_PATH, method = RequestMethod.GET)
+       public SuccessTransport getVersion() {
+               logger.debug("getVersion enter");
+               return new SuccessTransport(200, DashboardApplication.getImplementationVersion(HealthApi.class));
+       }
+
        @ApiOperation(value = "Calls the xApp Manager health check.")
        @RequestMapping(value = "/health", method = RequestMethod.GET)
        public void getHealth(HttpServletResponse response) {
index add9d36..e7ccfa1 100644 (file)
       "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.7.2.tgz",
       "integrity": "sha512-Ha4HshKdCVKgu4TVCtG8XyPPYdzTzNW4/fvPnn+LT7AosRABryhlRv4cc4+o84dgpvVJN9reN7jo/c+nYujFug=="
     },
+    "@material/animation": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@material/animation/-/animation-1.0.0.tgz",
+      "integrity": "sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw==",
+      "requires": {
+        "tslib": "^1.9.3"
+      }
+    },
+    "@material/base": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@material/base/-/base-1.0.0.tgz",
+      "integrity": "sha512-5dxFp46x5FA+Epg6YHLzN+5zRt9S2wR84UdvVAEJ1egea94m9UHUg7y9tAnNSN16aexRSywmzyLwPr+i8PGEYA==",
+      "requires": {
+        "tslib": "^1.9.3"
+      }
+    },
+    "@material/dom": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@material/dom/-/dom-1.1.0.tgz",
+      "integrity": "sha512-+HWW38ZaM2UBPu4+7QCusLDSf4tFT31rsEXHkTkxYSg/QpDivfPx6YDz4OmYtafmhPR1d1YjqB3MYysUHdodyw==",
+      "requires": {
+        "tslib": "^1.9.3"
+      }
+    },
+    "@material/feature-targeting": {
+      "version": "0.44.1",
+      "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-0.44.1.tgz",
+      "integrity": "sha512-90cc7njn4aHbH9UxY8qgZth1W5JgOgcEdWdubH1t7sFkwqFxS5g3zgxSBt46TygFBVIXNZNq35Xmg80wgqO7Pg=="
+    },
+    "@material/radio": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@material/radio/-/radio-2.1.1.tgz",
+      "integrity": "sha512-srbafm+JP4HhfvqLKOsLaIH1L67Y2EDfmeHbkI5Ag2zLsLLqGT8BI3AtytrOeg9xSvChqex/Bg8So8+i9FcalA==",
+      "requires": {
+        "@material/animation": "^1.0.0",
+        "@material/base": "^1.0.0",
+        "@material/feature-targeting": "^0.44.1",
+        "@material/ripple": "^2.1.1",
+        "@material/theme": "^1.1.0",
+        "tslib": "^1.9.3"
+      }
+    },
+    "@material/ripple": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-2.1.1.tgz",
+      "integrity": "sha512-/WIOBen3OLasJrr2ZXo9wpVJo6Z+1qFxnOvC61iXWqnDctq1fgTAQQbiMaRtsbfU68jDfhgLFJlwjgzvqAvFWQ==",
+      "requires": {
+        "@material/animation": "^1.0.0",
+        "@material/base": "^1.0.0",
+        "@material/dom": "^1.1.0",
+        "@material/feature-targeting": "^0.44.1",
+        "@material/theme": "^1.1.0",
+        "tslib": "^1.9.3"
+      }
+    },
+    "@material/theme": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@material/theme/-/theme-1.1.0.tgz",
+      "integrity": "sha512-YYUV9Rhbx4r/EMb/zoOYJUWjhXChNaLlH1rqt3vpNVyxRcxGqoVMGp5u1XALBCFiD9dACPKLIkKyRYa928nmPQ==",
+      "requires": {
+        "@material/feature-targeting": "^0.44.1"
+      }
+    },
     "@ngtools/webpack": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.2.3.tgz",
index 9b4810e..5877327 100644 (file)
@@ -22,6 +22,7 @@
     "@angular/platform-browser-dynamic": "~7.2.0",
     "@angular/router": "~7.2.0",
     "@fortawesome/fontawesome-free": "^5.7.2",
+    "@material/radio": "^2.1.1",
     "@types/chart.js": "^2.7.46",
     "angular-bootstrap-md": "^7.4.2",
     "bootstrap": "^4.3.1",
index 2ac083e..9a8e84e 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>1.0.2-SNAPSHOT</version>
+               <version>1.0.3-SNAPSHOT</version>
        </parent>
        <artifactId>ric-dash-fe</artifactId>
        <name>RIC Dashboard Webapp frontend</name>
index 19ac23e..43e88c1 100644 (file)
@@ -329,15 +329,22 @@ main {
 */
 .main__footer {
   background: transparent;
-  position: absolute;
+  /* position: absolute;
   bottom: 1rem;
-  left: 1.5rem;
+  left: 1.5rem; */
+  margin: 10px;
   z-index: 100;
 }
 
-.copyright__text {
-  letter-spacing: 0.1rem;
-  color: gray;
+.main__footer-dark {
+  background-color: #2B244D;
+  color: white;
+}
+
+.main-footer__bg-dark {
+  opacity: 1;
+  background: linear-gradient(to bottom, #B290FF, #2E1D65);
+  transition: opacity 300ms linear;
 }
 
 @media only screen and (max-width: 300px) {
index ef2405a..2d4834f 100644 (file)
@@ -29,7 +29,7 @@
       </div>
       <div class="account-details">
         <span class="name__text">Demo</span>
-        <span class="email__text">demo@onap.org</span>
+        <span class="email__text">demo@o-ran-sc.org</span>
       </div>
     </section>
     <section #sidenav class="menu-body">
@@ -60,9 +60,6 @@
       </svg>
       <img src="../../assets/oran-logo.png" width="180%" height="100%" style="position: relative; z-index: 50"/>
       <svg class="logo__icon" viewBox="150.3 22.2 313.7 42.8">
-        <!--<path fill="#00ff9b" d="M150.3 65V22.2L193 65z" data-name="Path 1"/>
-        <path fill="#003eff" d="M193.1 65h-42.8L193 22.2z" data-name="Path 2"/>-->
-        
         <text [ngClass]="{'logo__text-dark': darkModeActive}" class="logo__text" fill="#432c85"
               font-size="30" font-weight="600"
               letter-spacing=".1em" transform="translate(149 56)">
 
     <h3 class="date__text"></h3>
 
-
     <div class="mode-toggle__container">
       <span class="mode-toggle__text">Light</span>
-
       <label class="toggle-button__container">
         <input (click)="modeToggleSwitch()" type="checkbox" class="mode-toggle__input"/>
         <span [ngClass]="{'mode-toggle__bg-checked': darkModeActive}" class="mode-toggle__bg"></span>
         <span [ngClass]="{'mode-toggle__circle-checked': darkModeActive}" class="mode-toggle__circle"></span>
       </label>
-
-
       <span class="mode-toggle__text">Dark</span>
     </div>
 
   </header>
 
   <!-- Main Content -->
-
-  <!--<router-outlet></router-outlet>-->
   <main class="main__container">
     <div class="main-container__bg" [ngClass]="{'main-container__bg-dark': darkModeActive}"></div>
     <router-outlet></router-outlet>
   </main>
 
   <!-- Footer -->
+  <footer class="main__footer">
+       <div class="main-footer__bg" [ngClass]="{'main-footer__bg-dark': darkModeActive}"></div>
+    <app-footer></app-footer>
+  </footer>
 
-<!--   <footer class="main__footer">
-    <small class="copyright__text">© 2019 AT&T and Nokia. All rights reserved.</small>
-  </footer> -->
 </div>
index 184ad46..fabd258 100644 (file)
 import { BrowserModule } from '@angular/platform-browser';
 // tslint:disable-next-line:max-line-length
 import { MatIconModule, MatCardModule, MatListModule, MatSidenavModule,
-    MatButtonToggleModule, MatSliderModule, MatGridListModule, MatSlideToggleModule,
-  MatExpansionModule, MatTabsModule, MatDialogModule, MatFormFieldModule, MatButtonModule, MatInputModule, MatSnackBarModule} from '@angular/material';
+    MatButtonToggleModule, MatSliderModule, MatGridListModule, MatSlideToggleModule, 
+    MatExpansionModule, MatTabsModule, MatDialogModule, MatFormFieldModule, 
+    MatButtonModule, MatInputModule, MatSnackBarModule} from '@angular/material';
 import { BrowserAnimationsModule} from '@angular/platform-browser/animations';
 import { NgModule } from '@angular/core';
 import { Ng2SmartTableModule } from 'ng2-smart-table';
+import { MatRadioModule } from '@angular/material/radio'; 
 import { ChartsModule } from 'ng2-charts';
 import { MDBBootstrapModule } from 'angular-bootstrap-md';
-import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
@@ -50,6 +52,7 @@ import { ModalEventComponent } from './ui/modal-event/modal-event.component';
 import { XappComponent } from './xapp/xapp.component';
 import { ConfigEventComponent } from './ui/config-event/config-event.component';
 import { ConfirmDialogComponent } from './ui/confirm-dialog/confirm-dialog.component';
+import { FooterComponent } from './footer/footer.component';
 
 
 @NgModule({
@@ -70,6 +73,7 @@ import { ConfirmDialogComponent } from './ui/confirm-dialog/confirm-dialog.compo
     ConfigEventComponent,
     AppRANConnectDialog,
     ConfirmDialogComponent,
+    FooterComponent,
   ],
     imports: [
     BrowserModule,
@@ -81,6 +85,7 @@ import { ConfirmDialogComponent } from './ui/confirm-dialog/confirm-dialog.compo
     ReactiveFormsModule,
     MatButtonToggleModule,
     MatExpansionModule,
+    MatRadioModule,
     MatSliderModule,
     MatCardModule,
     MatIconModule,
diff --git a/webapp-frontend/src/app/footer/footer.component.html b/webapp-frontend/src/app/footer/footer.component.html
new file mode 100644 (file)
index 0000000..07c5d8c
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+  ========================LICENSE_START=================================
+  O-RAN-SC
+  %%
+  Copyright (C) 2019 AT&T Intellectual Property and Nokia
+  %%
+  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===================================
+  -->
+<div class="copyright__text">
+  Copyright (C) 2019 AT&T Intellectual Property and Nokia. Licensed under the Apache License, Version 2.0.
+  <br/>
+  Version {{dashboardVersion}}
+</div>
diff --git a/webapp-frontend/src/app/footer/footer.component.scss b/webapp-frontend/src/app/footer/footer.component.scss
new file mode 100644 (file)
index 0000000..e589f60
--- /dev/null
@@ -0,0 +1,24 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+.copyright__text {
+  color: gray;
+  letter-spacing: 0.1rem;
+  font-size: 12px;
+}
diff --git a/webapp-frontend/src/app/footer/footer.component.spec.ts b/webapp-frontend/src/app/footer/footer.component.spec.ts
new file mode 100644 (file)
index 0000000..7f64be6
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FooterComponent } from './footer.component';
+
+describe('FooterComponent', () => {
+  let component: FooterComponent;
+  let fixture: ComponentFixture<FooterComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ FooterComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(FooterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/webapp-frontend/src/app/footer/footer.component.ts b/webapp-frontend/src/app/footer/footer.component.ts
new file mode 100644 (file)
index 0000000..f01b6b0
--- /dev/null
@@ -0,0 +1,45 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+import { Component, OnInit } from '@angular/core'
+import { Observable } from 'rxjs/Rx'
+import { VersionService } from '../services/version/version.service'
+import { DashboardSuccessTransport } from '../interfaces/dashboard.types'
+
+@Component({
+  selector: 'app-footer',
+  templateUrl: './footer.component.html',
+  styleUrls: ['./footer.component.scss']
+})
+
+/**
+ * Fetches the version on load for display in the footer
+ */
+export class FooterComponent implements OnInit {
+
+  public dashboardVersion : string
+  // Inject the service
+  constructor(public versionService: VersionService) { }
+
+  ngOnInit() {
+    this.versionService.getDashboardVersion().subscribe((res : DashboardSuccessTransport) => this.dashboardVersion = res.data)
+  }
+
+}
diff --git a/webapp-frontend/src/app/interfaces/ac-xapp.types.ts b/webapp-frontend/src/app/interfaces/ac-xapp.types.ts
new file mode 100644 (file)
index 0000000..5c3cf3f
--- /dev/null
@@ -0,0 +1,31 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+
+// Models of data used by the AC xApp
+
+export interface ACAdmissionIntervalControl {
+  dc_admission_start_time: string;
+  dc_admission_end_time: string;
+}
+
+export interface ACAdmissionIntervalControlAck {
+  status: string;
+  message: string;
+}
diff --git a/webapp-frontend/src/app/interfaces/anr-xapp.types.ts b/webapp-frontend/src/app/interfaces/anr-xapp.types.ts
new file mode 100644 (file)
index 0000000..0e07ceb
--- /dev/null
@@ -0,0 +1,46 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+
+// Models of data used by the ANR xApp
+
+export interface ANRNeighborCellRelation {
+  cellIdentifierNrcgi: string;
+  neighborCellNrpci: string;
+  neighborCellNrcgi: string;
+  flagNoHo: boolean;
+  flagNoXn: boolean;
+  flagNoRemove: boolean;
+}
+
+export interface ANRNeighborCellRelationDel {
+  idType: string;
+  neighborCellNrpci: string;
+  neighborCellNrcgi: string;
+}
+
+export interface ANRNeighborCellRelationMod {
+  neighbourCellIdentifierType: string;
+  action: string;
+  neighborCellNrpci: string;
+  neighborCellNrcgi: string;
+  flagNoHo: boolean;
+  flagNoXn: boolean;
+  flagNoRemove: boolean;
+}
diff --git a/webapp-frontend/src/app/interfaces/dashboard.types.ts b/webapp-frontend/src/app/interfaces/dashboard.types.ts
new file mode 100644 (file)
index 0000000..6bb7534
--- /dev/null
@@ -0,0 +1,26 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+
+// Models of data used by the Dashboard
+
+export interface DashboardSuccessTransport {
+  status: number;
+  data: string;
+}
diff --git a/webapp-frontend/src/app/interfaces/e2-mgr.types.ts b/webapp-frontend/src/app/interfaces/e2-mgr.types.ts
new file mode 100644 (file)
index 0000000..1790bf9
--- /dev/null
@@ -0,0 +1,32 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+
+// Models of data used by the E2 Manager
+
+export interface E2SetupRequest {
+  ranName: string;
+  ranIp: string;
+  ranPort: number;
+}
+
+export interface E2ErrorResponse {
+  errorCode: string;
+  errorMessage: string;
+}
  * ========================LICENSE_END===================================
  */
 
-package org.oransc.ric.portal.dashboard.model;
-
-/**
- * Trivial model to transport a URL, to be serialized as JSON.
- */
-public class UrlTransport implements IDashboardResponse {
-
-       private String url;
-
-       /**
-        * Builds an empty object.
-        */
-       public UrlTransport() {
-               // no-arg constructor
-       }
-
-       /**
-        * Builds an object with the specified value.
-        * 
-        * @param s
-        *              value to transport.
-        */
-       public UrlTransport(String s) {
-               this.url = s;
-       }
+// Models of data used by the xApp Manager
+
+export interface XMSubscription {
+  eventType: string;
+  id: string;
+  maxRetries: number;
+  retryTimer: number;
+  targetUrl: string;
+}
 
-       public String getUrl() {
-               return url;
-       }
+export interface XMXappInfo {
+  xAppName: string;
+}
 
-       public void setUrl(String s) {
-               this.url = s;
-       }
+export interface XMXappInstance {
+  ip: string;
+  name: string;
+  port: number;
+  status: string;
+  rxMessages: Array<string>;
+  txMessages: Array<string>;
+}
 
+export interface XMXapp {
+  name: string;
+  status: string;
+  version: string;
+  instances: Array<XMXappInstance>;
 }
index f412a4e..586df92 100644 (file)
 import { Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
+import { XMXappInfo } from '../../interfaces/xapp-mgr.types';
 
 @Injectable()
 export class CatalogService {
 
-  constructor(private http: HttpClient) {
+  constructor(private httpClient: HttpClient) {
+    // injects to variable httpClient
   }
 
   getAll() {
-    return this.http.get('api/xappmgr/xapps');
+    return this.httpClient.get('api/xappmgr/xapps');
   }
 
   deployXapp(name) {
-    return this.http.post('api/xappmgr/xapps',
-      {
-        "xAppName": name
-      }
-      , { observe: 'response' });
+    let xappInfo: XMXappInfo = {
+      xAppName: name
+    };
+    return this.httpClient.post('api/xappmgr/xapps', xappInfo, { observe: 'response' });
   }
 
 }
index 4b1024a..45a4dc8 100644 (file)
  */
 import { Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
-import { Observable } from 'rxjs';
 
 @Injectable()
 export class SignalService {
   
-  constructor(private http: HttpClient) {
+  constructor(private httpClient: HttpClient) {
+    // injects to variable httpClient
   }
 
   getAll() {
-    return this.http.get('api/e2mgr/setup');
+    return this.httpClient.get('api/e2mgr/setup');
   }
 
   x2Setup(req) {
-    return this.http.post('api/e2mgr/x2Setup', req);
+    return this.httpClient.post('api/e2mgr/x2Setup', req);
   }
 
   endcSetup(req) {
-    return this.http.post('api/e2mgr/endcSetup', req);
+    return this.httpClient.post('api/e2mgr/endcSetup', req);
   }
 
 }
diff --git a/webapp-frontend/src/app/services/version/version.service.spec.ts b/webapp-frontend/src/app/services/version/version.service.spec.ts
new file mode 100644 (file)
index 0000000..184a0f2
--- /dev/null
@@ -0,0 +1,31 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+import { TestBed } from '@angular/core/testing';
+
+import { VersionService } from './version.service';
+
+describe('VersionService', () => {
+  beforeEach(() => TestBed.configureTestingModule({}));
+
+  it('should be created', () => {
+    const service: VersionService = TestBed.get(VersionService);
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/webapp-frontend/src/app/services/version/version.service.ts b/webapp-frontend/src/app/services/version/version.service.ts
new file mode 100644 (file)
index 0000000..2a3ec5e
--- /dev/null
@@ -0,0 +1,73 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property and Nokia
+ * %%
+ * 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===================================
+ */
+import { Injectable } from '@angular/core'
+import { HttpClient } from '@angular/common/http'
+
+@Injectable({
+  providedIn: 'root'
+})
+export class VersionService {
+
+  constructor(private httpClient : HttpClient) {
+    // injects to variable httpClient
+  }
+
+  /**
+   * Gets an Observable with version details
+   * @returns {Observable<SuccessTransport>} with Dashboard version
+   */
+  getDashboardVersion() {
+    return this.httpClient.get('api/dashboard/version')
+  }
+
+  /**
+   * Gets an Observable with version details
+   * @returns {Observable<SuccessTransport>} with A1 Mediator version
+   */
+  getA1MediatorVersion() {
+    // Remember that AC traffic goes via A1
+    return this.httpClient.get('api/xapp/ac/version')
+  }
+
+  /**
+   * Gets an Observable with version details
+   * @returns {Observable<SuccessTransport>} with ANR xApp version
+   */
+  getAnrXappVersion() {
+    return this.httpClient.get('api/xapp/anr/version')
+  }
+
+  /**
+   * Gets an Observable with version details
+   * @returns {Observable<SuccessTransport>} with E2 Manager version
+   */
+  getE2ManagerVersion() {
+    return this.httpClient.get('api/e2mgr/version')
+  }
+
+  /**
+   * Gets an Observable with version details
+   * @returns {Observable<SuccessTransport>} with XApp Manager version
+   */
+  getXappManagerVersion(){
+    return this.httpClient.get('api/xappmgr/version')
+  }
+
+}
index 25b1a28..c0b0d86 100644 (file)
     Setup RAN Connection
 </div>
 
-<form [formGroup]="ranDialogForm" novalidate autocomplete="off">
+<form [formGroup]="ranDialogForm" novalidate autocomplete="off" (ngSubmit)="setupConnection(ranDialogForm.value)">
   <div mat-dialog-content>
+    <div name="rantype">
+      <label id="request-type-radio-group-label">Select the RAN type</label>
+      <mat-radio-group aria-label="Select the RAN type" formControlName="ranType">
+        <mat-radio-button value="endc">EN-DC</mat-radio-button>
+        <mat-radio-button value="x2">X2</mat-radio-button>
+      </mat-radio-group>
+    </div>
     <mat-form-field class="input-display-block">
-      <input matInput type="text" placeholder="eNB/gNB Name" [(ngModel)]="data.ranName" formControlName="ranName">
-      <mat-hint align="end">Not more then 50 characters long.</mat-hint>
+      <input matInput type="text" placeholder="eNB/gNB Name" formControlName="ranName">
+      <mat-hint align="end">Example: ABCD123456</mat-hint>
       <mat-error *ngIf="validateControl('ranName') && hasError('ranName', 'required')">Name is required</mat-error>
       <mat-error *ngIf="hasError('ranName', 'length')">Valid name is required</mat-error>
     </mat-form-field>
     <mat-form-field class="input-display-block">
-      <input matInput type="text" placeholder="IP" [(ngModel)]="data.ranIp" formControlName="ranIp">
+      <input matInput type="text" placeholder="IP" formControlName="ranIp">
       <mat-error *ngIf="validateControl('ranIp') && hasError('ranIp', 'required')">IP is required</mat-error>
       <mat-error *ngIf="hasError('ranIp', 'pattern')">Valid IP is required</mat-error>
     </mat-form-field>
     <mat-form-field class="input-display-block">
-      <input matInput type="text" placeholder="Port" [(ngModel)]="data.ranPort" formControlName="ranPort">
+      <input matInput type="text" placeholder="Port" formControlName="ranPort">
       <mat-error *ngIf="validateControl('ranPort') && hasError('ranPort', 'required')">Port is required</mat-error>
       <mat-error *ngIf="hasError('ranPort', 'pattern')">Valid port number is required</mat-error>
     </mat-form-field>
   </div>
   <div mat-dialog-actions class="modal-footer justify-content-center">
-    <button class="mat-raised-button" (click)="close()">Cancel</button>
-    <button class="mat-raised-button mat-primary"
-      [disabled]="(validateControl('ranName') && hasError('ranName', 'required'))
-      || (validateControl('ranIp') && hasError('ranIp', 'required'))
-      || (validateControl('ranPort') && hasError('ranPort', 'required'))
-      || hasError('ranName', 'maxlength')
-      || hasError('ranIp', 'pattern')
-      || hasError('ranPort', 'pattern')
-      || (!data.ranName || !data.ranIp || !data.ranPort)"
-       (click)="connectRAN()">Connect</button>
+    <button class="mat-raised-button" (click)="onCancel()">Cancel</button>
+    <button class="mat-raised-button mat-primary" [disabled]="!ranDialogForm.valid">Connect</button>
   </div>
 </form>
index 3af4ca8..5a9b925 100644 (file)
  */
 import { Component, OnInit, Inject } from '@angular/core';
 import { LocalDataSource } from 'ng2-smart-table';
-import { SignalService } from '../services/signal/signal.service';
 import { Router } from '@angular/router';
-import { MatDialog, MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
+import { MatDialog, MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { MatFormFieldModule } from '@angular/material';
-import { FormGroup, FormControl, FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
+import { MatRadioModule } from '@angular/material/radio'; 
+import { FormGroup, FormControl, FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
 import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs/Rx';
-
-export interface DialogData {
-    ranName: string;
-    ranIp: number;
-    ranPort: number;
-}
+import { SignalService } from '../services/signal/signal.service';
+import { E2SetupRequest } from '../interfaces/e2-mgr.types';
 
 @Component({
-    selector: 'app-signal',
-    templateUrl: 'signal.component.html',
-    styleUrls: ['signal.component.css']
+  selector: 'app-signal',
+  templateUrl: 'signal.component.html',
+  styleUrls: ['signal.component.css']
 })
 
 export class SignalComponent {
-    settings = {
-        hideSubHeader: true,
-        actions: {
-            columnTitle: 'Actions',
-            add: false,
-            edit: false,
-            delete: false,
-            position: 'right'
-        },
-        columns: {
-            requestType: {
-                title: 'Request Type',
-                type: 'string',
-            },
-            ranName: {
-                title: 'eNodeB/gNodeB Name',
-                type: 'string',
-            },
-            ranIp: {
-                title: 'IP',
-                type: 'number',
-            },
-            ranPort: {
-                title: 'Port',
-                type: 'number',
-            },
-            responseCode: {
-                title: 'Response',
-                type: 'number',
-            },
-            timeStamp: {
-                title: 'Time Stamp',
-                type: 'string',
-            }
-        }
-    };
-
-    source: LocalDataSource = new LocalDataSource();
-
-    ranName: string;
-
-    ranIp: number;
-
-    ranPort: number;
-
-    constructor(private service: SignalService, public dialog: MatDialog, private http: HttpClient) {
-        this.service.getAll().subscribe((val: any[]) => this.source.load(val));
+  settings = {
+    hideSubHeader: true,
+    actions: {
+      columnTitle: 'Actions',
+      add: false,
+      edit: false,
+      delete: false,
+      position: 'right'
+    },
+    columns: {
+      requestType: {
+        title: 'RAN Type',
+        type: 'string',
+      },
+      ranName: {
+        title: 'eNodeB/gNodeB Name',
+        type: 'string',
+      },
+      ranIp: {
+        title: 'IP',
+        type: 'number',
+      },
+      ranPort: {
+        title: 'Port',
+        type: 'number',
+      },
+      responseCode: {
+        title: 'Response',
+        type: 'number',
+      },
+      timeStamp: {
+        title: 'Time Stamp',
+        type: 'string',
+      }
     }
+  };
 
-    openRanConnectDialog() {
+  source: LocalDataSource = new LocalDataSource();
 
-        const dialogRef = this.dialog.open(AppRANConnectDialog,  {
-            width: '450px',
-            data: {ranName: this.ranName, ranIp: this.ranIp, ranPort: this.ranPort}
-    })
+  constructor(private service: SignalService, public dialog: MatDialog, private http: HttpClient) {
+    this.service.getAll().subscribe((val: any[]) => this.source.load(val));
+  }
 
-        dialogRef.afterClosed().subscribe(result => {
-            this.service.getAll().subscribe((val: any[]) => this.source.load(val));
-        });
+  openRanConnectDialog() {
+    const dialogRef = this.dialog.open(AppRANConnectDialog, {
+      width: '450px',
+      data: { }
+    })
+    dialogRef.afterClosed().subscribe(result => {
+      this.service.getAll().subscribe((val: any[]) => this.source.load(val));
+    });
+  }
 
-    }
-}
+}// class SignalComponent
 
 @Component({
-    selector: 'app-signal-ranconnect-dialog',
-    templateUrl: 'signal.component.ranconnect-dialog.html',
-    styleUrls: ['signal.component.css']
+  selector: 'app-signal-ranconnect-dialog',
+  templateUrl: 'signal.component.ranconnect-dialog.html',
+  styleUrls: ['signal.component.css']
 })
 
 export class AppRANConnectDialog implements OnInit {
 
-    constructor(public dialogRef: MatDialogRef<AppRANConnectDialog>,
-          private service: SignalService,
-          @Inject(MAT_DIALOG_DATA) public data: DialogData) {
-    }
-
-    public ranDialogForm: FormGroup;
+  public ranDialogForm: FormGroup;
 
-        public ranName: string;
-
-        public ranIp: number;
-
-        public ranPort: number;
-
-    ngOnInit() {
-        const ipPattern = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/;
-        const portPattern = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
-        this.ranDialogForm = new FormGroup({
-                ranName: new FormControl('', [Validators.required, Validators.maxLength(50)]),
-                ranIp: new FormControl('', [Validators.required, Validators.pattern(ipPattern)]),
-                ranPort: new FormControl('', [Validators.required, Validators.pattern(portPattern)])
-            });
+  constructor(
+      public dialogRef: MatDialogRef<AppRANConnectDialog>,
+      private service: SignalService, 
+      @Inject(MAT_DIALOG_DATA) public data: E2SetupRequest) { 
     }
 
-    close() {
-        this.dialogRef.close();
+  ngOnInit() {
+    const namePattern = /^([A-Z]){4}([0-9]){6}$/;
+    const ipPattern = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/;
+    const portPattern = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
+    this.ranDialogForm = new FormGroup({
+      ranType: new FormControl('endc'),
+      ranName: new FormControl('', [Validators.required, Validators.pattern(namePattern)]),
+      ranIp: new FormControl('', [Validators.required, Validators.pattern(ipPattern)]),
+      ranPort: new FormControl('', [Validators.required, Validators.pattern(portPattern)])
+    });
+  }
+
+  onCancel() {
+    this.dialogRef.close();
+  }
+
+  public setupConnection = (ranFormValue) => {
+    if (this.ranDialogForm.valid) {
+      this.executeSetupConnection(ranFormValue);
     }
+  }
 
-    connectRAN(): void {
-        this.service.x2Setup(this.data).subscribe((val: any[]) => {});
-        this.dialogRef.close();
+  private executeSetupConnection = (ranFormValue) => {
+    let setupRequest: E2SetupRequest = {
+      ranName: ranFormValue.ranName,
+      ranIp:   ranFormValue.ranIp,
+      ranPort: ranFormValue.ranPort
     }
-
-    public hasError(controlName: string, errorName: string) {
-        if (this.ranDialogForm.controls[controlName].hasError(errorName))
-            return true;
-        return false;
+    if (ranFormValue.ranType === 'endc') {
+      this.service.endcSetup(setupRequest).subscribe((val: any[]) => {});
     }
+    else {
+      this.service.x2Setup(setupRequest).subscribe((val: any[]) => {});
+    }
+    this.dialogRef.close();
+  }
+
+  public hasError(controlName: string, errorName: string) {
+    if (this.ranDialogForm.controls[controlName].hasError(errorName))
+      return true;
+    return false;
+  }
 
   public validateControl(controlName: string) {
-        if (this.ranDialogForm.controls[controlName].invalid && this.ranDialogForm.controls[controlName].touched)
-            return true;
-        return false;
-    }
-}
+    if (this.ranDialogForm.controls[controlName].invalid && this.ranDialogForm.controls[controlName].touched)
+      return true;
+    return false;
+  }
+
+} // class AppRANConnectDialog
index 51c8d77..3c8f59c 100644 (file)
     <div style="color: black; size: 1em; text-align: center">
         <mat-icon>settings_input_antenna</mat-icon> ANR</div>
     <div style="color: black; size: 1em; text-align: center">
-        <mat-icon>call_split</mat-icon> IFLB</div>
-
+        <mat-icon>open_in_new</mat-icon> Deploy</div>
   </div>
   <div class="body__container">
-    <img src="../../../assets/intelligence.png" width="100%" height="100%"/>
+    <img src="../../../assets/intelligence.png" width="250px"/>
   </div>
 </div>
index 0f071dd..fff42e8 100644 (file)
@@ -20,8 +20,6 @@
 <div class="add__card" routerLink="/control" [ngClass]="{'add__card-dark': darkMode}">
   <div class="header__container">
     <span class="card__title">xApp Control</span><br><br><br>
-    <div style="color: black; size: 1em; text-align: center">
-        <mat-icon>open_in_new</mat-icon> Deploy</div>
     <div style="color: black; size: 1em; text-align: center">
         <mat-icon>play_circle_outline</mat-icon> Start</div>
     <div style="color: black; size: 1em; text-align: center">
@@ -32,6 +30,6 @@
 
   </div>
   <div class="body__container">
-    <img src="../../../assets/xAppControl.png" width="100%" height="100%"/>
+    <img src="../../../assets/xAppControl.png" width="250px"/>
   </div>
 </div>
index 14d03e3..4cb3ec2 100644 (file)
   -->
 <!doctype html>
 <html lang="en">
-<head>
-  <meta charset="utf-8">
-  <title>RIC Dashboard</title>
-  <base href="/">
-
-  <meta name="viewport" content="width=device-width, initial-scale=1">
-  <link rel="icon" type="image/x-icon" href="assets/oran-logo.png">
-</head>
-<body>
-  <app-root></app-root>
-</body>
+  <head>
+    <meta charset="utf-8">
+    <title>RIC Dashboard</title>
+    <base href="/">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="icon" type="image/x-icon" href="assets/oran-logo.png">
+  </head>
+  <body>
+    <app-root></app-root>
+  </body>
 </html>
index 9146e19..a8c3611 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>1.0.2-SNAPSHOT</version>
+               <version>1.0.3-SNAPSHOT</version>
        </parent>
        <!-- This groupId will NOT allow deployment in LF -->
        <groupId>org.o-ran-sc.ric.xappmgr.client</groupId>
@@ -35,6 +35,9 @@ limitations under the License.
        <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.xappmgr.client</client.base.package.name>
        </properties>
        <!-- Successful compilation requires generated code dependencies -->
@@ -147,6 +150,23 @@ limitations under the License.
                                        </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>
                        <!-- Skip the deploy-jar-to-nexus step -->
                        <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
@@ -178,7 +198,7 @@ limitations under the License.
                                                                                </goals>
                                                                        </pluginExecutionFilter>
                                                                        <action>
-                                                                               <ignore/>
+                                                                               <ignore />
                                                                        </action>
                                                                </pluginExecution>
                                                        </pluginExecutions>