Add initial sources 85/38585/2
authorMatthieuGeerebaert <matthieu.geerebaert@orange.com>
Mon, 26 Mar 2018 15:08:28 +0000 (17:08 +0200)
committerMatthieuGeerebaert <matthieu.geerebaert@orange.com>
Tue, 27 Mar 2018 15:56:13 +0000 (17:56 +0200)
- Springboot application
- Apache license 2.0
- Healthcheck

Change-Id: I0dedfbe3348195f7be00ec8d27fbf25dfcda52b0
Issue-ID: EXTAPI-36
Signed-off-by: MatthieuGeerebaert <matthieu.geerebaert@orange.com>
20 files changed:
.gitignore [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/java/org/onap/nbi/Application.java [new file with mode: 0644]
src/main/java/org/onap/nbi/apis/status/StatusResource.java [new file with mode: 0644]
src/main/java/org/onap/nbi/apis/status/StatusService.java [new file with mode: 0644]
src/main/java/org/onap/nbi/apis/status/StatusServiceImpl.java [new file with mode: 0644]
src/main/java/org/onap/nbi/apis/status/model/ApplicationStatus.java [new file with mode: 0644]
src/main/java/org/onap/nbi/apis/status/model/StatusType.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/BeanUtils.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/JacksonFilter.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/JsonRepresentation.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/Query.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/QueryParserUtils.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/ReservedKeys.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/Resource.java [new file with mode: 0644]
src/main/java/org/onap/nbi/commons/ResourceManagement.java [new file with mode: 0644]
src/main/resources/application.properties [new file with mode: 0644]
src/test/java/org/onap/nbi/apis/status/StatusTest.java [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a05f4a2
--- /dev/null
@@ -0,0 +1,127 @@
+#### OS related ####
+### Linux related ###
+*~
+
+# KDE directory preferences
+.directory
+
+### OSX related ###
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+### Windows related ###
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+#### IDE related ####
+### Eclipse related ###
+*.pydevproject
+.metadata
+.gradle
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.factorypath
+.loadpath
+.classpath
+.project
+.springBeans
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+# TeXlipse plugin
+.texlipse
+
+### Idea related ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+## Directory-based project format
+.idea/
+
+## File-based project format
+*.ipr
+*.iws
+*.iml
+
+## Additional for IntelliJ
+out/
+
+# generated by JIRA plugin
+atlassian-ide-plugin.xml
+
+### Netbeans related ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+nbactions.xml
+nb-configuration.xml
+
+### Notepad++ backups ###
+*.bak
+
+### Shell ###
+*.sh
+
+#### Tools & Languages related ####
+### Java related ###
+*.class
+
+# Package Files #
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+### Maven related ###
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+
+### Git related ###
+*.orig
+/.apt_generated/
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..e8cdcf3
--- /dev/null
@@ -0,0 +1,7 @@
+Copyright (c) 2017 Orange.  All rights reserved.
+===================================================================
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..6e46f00
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+nbi
+===============
+NBI stands for NorthBound Interface. It brings to ONAP a set of API - at service level - that can be used by external systems as BSS for example. These API are based on TMForum API 
+
+## License
+The Microservice Bus is released under version 2.0 of the [Apache License][].
+[Apache License]: http://www.apache.org/licenses/LICENSE-2.0
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..3c5bbbd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+
+       <groupId>org.onap.nbi</groupId>
+       <artifactId>nbi-rest-services</artifactId>
+       <version>1.0.0-SNAPSHOT</version>
+       <packaging>jar</packaging>
+
+       <name>nbi-rest-services</name>
+
+       <parent>
+               <groupId>org.springframework.boot</groupId>
+               <artifactId>spring-boot-starter-parent</artifactId>
+               <version>1.5.10.RELEASE</version>
+               <relativePath /> <!-- lookup parent from repository -->
+       </parent>
+
+       <properties>
+               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+               <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+               <java.version>1.8</java.version>
+       </properties>
+       
+       <licenses>
+               <license>
+                       <name>Apache2</name>
+                       <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+               </license>
+       </licenses>     
+
+       <dependencies>
+
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-web</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.apache.commons</groupId>
+                       <artifactId>commons-io</artifactId>
+                       <version>1.3.2</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>commons-beanutils</groupId>
+                       <artifactId>commons-beanutils</artifactId>
+                       <version>1.9.3</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.apache.commons</groupId>
+                       <artifactId>commons-lang3</artifactId>
+                       <version>3.4</version>
+               </dependency>
+               
+               <!-- test -->           
+               
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-test</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               
+               <!-- runtime dev -->
+                               
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-devtools</artifactId>
+                       <scope>runtime</scope>
+               </dependency>
+
+       </dependencies>
+
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-maven-plugin</artifactId>
+                       </plugin>
+               </plugins>
+
+       </build>
+
+</project>
diff --git a/src/main/java/org/onap/nbi/Application.java b/src/main/java/org/onap/nbi/Application.java
new file mode 100644 (file)
index 0000000..0609f9c
--- /dev/null
@@ -0,0 +1,13 @@
+package org.onap.nbi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/status/StatusResource.java b/src/main/java/org/onap/nbi/apis/status/StatusResource.java
new file mode 100644 (file)
index 0000000..f4b0bcd
--- /dev/null
@@ -0,0 +1,55 @@
+package org.onap.nbi.apis.status;
+
+import org.onap.nbi.apis.status.model.ApplicationStatus;
+import org.onap.nbi.apis.status.model.StatusType;
+import org.onap.nbi.commons.JsonRepresentation;
+import org.onap.nbi.commons.ResourceManagement;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import javax.servlet.http.HttpServletRequest;
+
+
+@RestController
+@RequestMapping("/status")
+public class StatusResource extends ResourceManagement<ApplicationStatus> {
+
+    @Autowired
+    private StatusService statusService;
+
+    private JsonRepresentation fullRepresentation = new JsonRepresentation().add("name").add("status").add("version")
+            .add("components.name").add("components.status");
+
+    @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> status(HttpServletRequest request) {
+
+        ResponseEntity<Object> responseEntity = null;
+
+        final String[] splitPath = request.getRequestURI().split("/");
+
+        final String applicationName = splitPath[1];
+        final String applicationVersion = splitPath[3];
+
+        final ApplicationStatus applicationStatus = this.statusService.get(applicationName, applicationVersion);
+
+        final boolean isServiceFullyFunctional =
+                StatusType.OK.equals(applicationStatus.getStatus()) ? applicationStatus.getComponents().stream()
+                        .allMatch(componentStatus -> StatusType.OK.equals(componentStatus.getStatus())) : false;
+
+        // filter object
+        Object response = this.getEntity(applicationStatus, fullRepresentation);
+
+        if (isServiceFullyFunctional) {
+            responseEntity = ResponseEntity.ok(response);
+        } else {
+            responseEntity = ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(response);
+        }
+
+        return responseEntity;
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/status/StatusService.java b/src/main/java/org/onap/nbi/apis/status/StatusService.java
new file mode 100644 (file)
index 0000000..2309da4
--- /dev/null
@@ -0,0 +1,9 @@
+package org.onap.nbi.apis.status;
+
+import org.onap.nbi.apis.status.model.ApplicationStatus;
+
+public interface StatusService {
+
+    ApplicationStatus get(String serviceName, String serviceVersion);
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/status/StatusServiceImpl.java b/src/main/java/org/onap/nbi/apis/status/StatusServiceImpl.java
new file mode 100644 (file)
index 0000000..7262aea
--- /dev/null
@@ -0,0 +1,28 @@
+package org.onap.nbi.apis.status;
+
+import org.onap.nbi.apis.status.model.ApplicationStatus;
+import org.onap.nbi.apis.status.model.StatusType;
+import org.springframework.stereotype.Service;
+
+@Service("statusService")
+public class StatusServiceImpl implements StatusService {
+
+    @Override
+    public ApplicationStatus get(final String serviceName, final String serviceVersion) {
+
+        final boolean applicationIsUp = true;
+
+
+        final ApplicationStatus applicationStatus =
+                new ApplicationStatus(serviceName, (applicationIsUp ? StatusType.OK : StatusType.KO), serviceVersion);
+
+
+        return applicationStatus;
+    }
+
+
+    public boolean serviceIsUp() {
+        return true;
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/status/model/ApplicationStatus.java b/src/main/java/org/onap/nbi/apis/status/model/ApplicationStatus.java
new file mode 100644 (file)
index 0000000..ecaa89e
--- /dev/null
@@ -0,0 +1,55 @@
+package org.onap.nbi.apis.status.model;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.onap.nbi.commons.Resource;
+
+public class ApplicationStatus implements Resource {
+
+    private String name;
+
+    private StatusType status;
+
+    private String version;
+
+    private Set<ApplicationStatus> components = new HashSet<>();
+
+    /**
+     * Builds a new {@code ApplicationStatus} with the following attributes :
+     *
+     * @param name name of the service
+     * @param state state of the service ({@code OK} | {@code KO})
+     * @param version version of the service ({@code x.y.z})
+     */
+    public ApplicationStatus(final String name, final StatusType status, final String version) {
+        this.name = name;
+        this.status = status;
+        this.version = version;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public StatusType getStatus() {
+        return this.status;
+    }
+
+    public String getVersion() {
+        return this.version;
+    }
+
+    public Set<ApplicationStatus> getComponents() {
+        return this.components;
+    }
+
+    public ApplicationStatus component(final ApplicationStatus componentStatus) {
+        this.components.add(componentStatus);
+        return this;
+    }
+
+    @Override
+    public String getId() {
+        return null;
+    }
+}
diff --git a/src/main/java/org/onap/nbi/apis/status/model/StatusType.java b/src/main/java/org/onap/nbi/apis/status/model/StatusType.java
new file mode 100644 (file)
index 0000000..a54dc50
--- /dev/null
@@ -0,0 +1,30 @@
+package org.onap.nbi.apis.status.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum StatusType {
+
+    OK("ok"), KO("ko");
+    private final String value;
+
+    StatusType(String v) {
+        this.value = v;
+    }
+
+    @JsonValue
+    public String getValue() {
+        return this.value;
+    }
+
+    @JsonCreator
+    public static StatusType fromValue(String v) {
+        for (StatusType c : StatusType.values()) {
+            if (c.value.equals(v)) {
+                return c;
+            }
+        }
+        throw new IllegalArgumentException(v);
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/BeanUtils.java b/src/main/java/org/onap/nbi/commons/BeanUtils.java
new file mode 100644 (file)
index 0000000..1d14f12
--- /dev/null
@@ -0,0 +1,39 @@
+package org.onap.nbi.commons;
+
+import org.apache.commons.beanutils.PropertyUtilsBean;
+
+/**
+ *
+ */
+public class BeanUtils {
+
+    private static final PropertyUtilsBean PUB = new PropertyUtilsBean();
+
+    /**
+     *
+     * @param bean
+     * @param name
+     * @return
+     */
+    public static Object getNestedProperty(Object bean, String name) {
+        try {
+            return BeanUtils.PUB.getNestedProperty(bean, name);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     *
+     * @param bean
+     * @param name
+     * @param value
+     */
+    public static void setNestedProperty(Object bean, String name, Object value) {
+        try {
+            BeanUtils.PUB.setNestedProperty(bean, name, value);
+        } catch (Exception ex) {
+        }
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/JacksonFilter.java b/src/main/java/org/onap/nbi/commons/JacksonFilter.java
new file mode 100644 (file)
index 0000000..bc173c3
--- /dev/null
@@ -0,0 +1,169 @@
+package org.onap.nbi.commons;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class JacksonFilter {
+
+    private final static List<String> SKIPPED_FIELDS = Arrays.asList("internalId");
+
+
+    public static <R> List<ObjectNode> createNodes(List<R> list, JsonRepresentation jsonRepresentation) {
+
+        Set<R> set;
+        if (list == null) {
+            set = new LinkedHashSet<>();
+        } else {
+            set = new LinkedHashSet<>(list);
+        }
+        return createNodes(set, jsonRepresentation);
+    }
+
+    public static <R> List<ObjectNode> createNodes(Collection<R> collection, JsonRepresentation jsonRepresentation) {
+        List<ObjectNode> nodeList = new ArrayList<>();
+        for (R element : collection) {
+            ObjectNode node = createNode(element, jsonRepresentation);
+            nodeList.add(node);
+        }
+        return nodeList;
+    }
+
+    public static <R> ObjectNode createNode(R bean, JsonRepresentation jsonRepresentation) {
+        ObjectMapper mapper = new ObjectMapper();
+        return JacksonFilter.createNode(mapper, bean, jsonRepresentation.getAttributes());
+    }
+
+    private static <R> ObjectNode createNode(ObjectMapper mapper, R bean, Set<String> names) {
+        // split fieldNames in 2 categories :
+        // simpleFields for simple property names with no '.'
+        // nestedFields for nested property names with a '.'
+        Set<String> simpleFields = new LinkedHashSet<String>();
+        MultiValueMap nestedFields = new LinkedMultiValueMap();
+        buildFields(names, simpleFields, nestedFields);
+
+        // create a simple node with only one level containing all simples
+        // properties
+        ObjectNode rootNode = JacksonFilter.createNodeWithSimpleFields(mapper, bean, simpleFields);
+
+        // create nested nodes with deeper levels
+        Set<Map.Entry<String, List<String>>> entrySet = nestedFields.entrySet();
+        // for each nested value, create recursively a node
+        for (Map.Entry<String, List<String>> entry : entrySet) {
+            String rootFieldName = entry.getKey();
+            // add in current node only if full value is not already present in
+            // 1st level
+            if (!simpleFields.contains(rootFieldName)) {
+                Object nestedBean = BeanUtils.getNestedProperty(bean, rootFieldName);
+                // add only non null fields
+                if (nestedBean == null) {
+                    continue;
+                }
+                Set<String> nestedFieldNames = new LinkedHashSet<String>(entry.getValue());
+                // current node is an array or a list
+                if ((nestedBean.getClass().isArray()) || (Collection.class.isAssignableFrom(nestedBean.getClass()))) {
+                    handleListNode(mapper, rootNode, rootFieldName, nestedBean, nestedFieldNames);
+                } else {
+                    // create recursively a node and add it in current root node
+                    createNodeRecursively(mapper, rootNode, rootFieldName, nestedBean, nestedFieldNames);
+                }
+            }
+        }
+        return rootNode;
+    }
+
+    private static void createNodeRecursively(ObjectMapper mapper, ObjectNode rootNode, String rootFieldName,
+            Object nestedBean, Set<String> nestedFieldNames) {
+        ObjectNode nestedNode = JacksonFilter.createNode(mapper, nestedBean, nestedFieldNames);
+        if ((nestedNode != null) && (nestedNode.size() > 0)) {
+            rootNode.set(rootFieldName, nestedNode);
+        }
+    }
+
+    private static void buildFields(Set<String> names, Set<String> simpleFields, MultiValueMap nestedFields) {
+        for (String name : names) {
+            int index = name.indexOf('.');
+            boolean isNestedField = (index > 0) && (index < name.length());
+            if (isNestedField) {
+                String rootFieldName = name.substring(0, index);
+                String subFieldName = name.substring(index + 1);
+                nestedFields.add(rootFieldName, subFieldName);
+            } else {
+                simpleFields.add(name);
+            }
+        }
+    }
+
+    private static void handleListNode(ObjectMapper mapper, ObjectNode rootNode, String rootFieldName,
+            Object nestedBean, Set<String> nestedFieldNames) {
+        Object[] array = null;
+        if ((nestedBean.getClass().isArray())) {
+            array = (Object[]) nestedBean;
+        } else {
+            Collection<?> collection = (Collection<?>) nestedBean;
+            array = collection.toArray();
+        }
+        if (array.length > 0) {
+            // create a node for each element in array
+            // and add created node in an arrayNode
+            Collection<JsonNode> nodes = new LinkedList<JsonNode>();
+            for (Object object : array) {
+                ObjectNode nestedNode = JacksonFilter.createNode(mapper, object, nestedFieldNames);
+                if ((nestedNode != null) && (nestedNode.size() > 0)) {
+                    nodes.add(nestedNode);
+                }
+            }
+            ArrayNode arrayNode = mapper.createArrayNode();
+            arrayNode.addAll(nodes);
+            if (arrayNode.size() > 0) {
+                rootNode.set(rootFieldName, arrayNode);
+            }
+        }
+    }
+
+    private static <R> ObjectNode createNodeWithSimpleFields(ObjectMapper mapper, R bean, Set<String> names) {
+        ObjectNode node = mapper.createObjectNode();
+        for (String name : names) {
+            // Prevent getting value of some fields
+            if (SKIPPED_FIELDS.contains(name)) {
+                continue;
+            }
+
+            JacksonFilter.nodePut(node, name, BeanUtils.getNestedProperty(bean, name));
+        }
+        return node;
+    }
+
+    private static void nodePut(ObjectNode node, String name, Object value) {
+        if (value instanceof Boolean) {
+            node.put(name, (Boolean) value);
+        } else if (value instanceof Integer) {
+            node.put(name, (Integer) value);
+        } else if (value instanceof Long) {
+            node.put(name, (Long) value);
+        } else if (value instanceof Float) {
+            node.put(name, (Float) value);
+        } else if (value instanceof Double) {
+            node.put(name, (Double) value);
+        } else if (value instanceof BigDecimal) {
+            node.put(name, (BigDecimal) value);
+        } else if (value instanceof String) {
+            node.put(name, (String) value);
+        } else {
+            node.putPOJO(name, value);
+        }
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/JsonRepresentation.java b/src/main/java/org/onap/nbi/commons/JsonRepresentation.java
new file mode 100644 (file)
index 0000000..0c71262
--- /dev/null
@@ -0,0 +1,35 @@
+package org.onap.nbi.commons;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.springframework.util.MultiValueMap;
+
+public class JsonRepresentation {
+
+    private Set<String> attributes = new LinkedHashSet<>();
+
+    public JsonRepresentation() {}
+
+    public JsonRepresentation(MultiValueMap<String, String> queryParameters) {
+        this.attributes = QueryParserUtils.getFields(queryParameters);
+    }
+
+    public JsonRepresentation(Set<String> attributes) {
+        this.attributes.addAll(attributes);
+    }
+
+    public JsonRepresentation add(String attributePath) {
+        this.attributes.add(attributePath);
+        return this;
+    }
+
+    public JsonRepresentation add(JsonRepresentation jsonRepresentation) {
+        this.attributes.addAll(jsonRepresentation.getAttributes());
+        return this;
+    }
+
+    public Set<String> getAttributes() {
+        return attributes;
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/Query.java b/src/main/java/org/onap/nbi/commons/Query.java
new file mode 100644 (file)
index 0000000..8ee453f
--- /dev/null
@@ -0,0 +1,26 @@
+package org.onap.nbi.commons;
+
+import org.springframework.util.MultiValueMap;
+
+
+
+public class Query {
+
+    private MultiValueMap<String, String> parameters;
+
+    private JsonRepresentation jsonRepresentation;
+
+    public Query(MultiValueMap<String, String> queryParameters) {
+        this.parameters = queryParameters;
+        this.jsonRepresentation = new JsonRepresentation(queryParameters);
+    }
+
+    public MultiValueMap<String, String> getParameters() {
+        return parameters;
+    }
+
+    public JsonRepresentation getRepresentation() {
+        return jsonRepresentation;
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/QueryParserUtils.java b/src/main/java/org/onap/nbi/commons/QueryParserUtils.java
new file mode 100644 (file)
index 0000000..69c32df
--- /dev/null
@@ -0,0 +1,65 @@
+package org.onap.nbi.commons;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+/**
+ *
+ */
+public class QueryParserUtils {
+
+    private QueryParserUtils() {}
+
+    /**
+     *
+     * @param queryParameters
+     * @return
+     */
+    public static Set<String> getFields(MultiValueMap<String, String> queryParameters) {
+
+        Set<String> fieldSet = new HashSet<>();
+        if (queryParameters != null) {
+            // search for "all" parameter
+            if (queryParameters.containsKey(ReservedKeys.ALL_FIELDS)) {
+                queryParameters.remove(ReservedKeys.ALL_FIELDS);
+                fieldSet.add(ReservedKeys.ALL_FIELDS);
+            }
+            // search for "fields" parameters
+            List<String> queryParameterField =
+                    queryParameters.remove(ReservedKeys.QUERY_KEY_FIELD_ESCAPE + ReservedKeys.QUERY_KEY_FIELD);
+            if (queryParameterField == null) {
+                queryParameterField = queryParameters.remove(ReservedKeys.QUERY_KEY_FIELD);
+            }
+            if (queryParameterField != null && !queryParameterField.isEmpty()) {
+                String queryParameterValue = queryParameterField.get(0);
+                if (queryParameterValue != null) {
+                    String[] tokenArray = queryParameterValue.split(",");
+                    fieldSet.addAll(Arrays.asList(tokenArray));
+                }
+            }
+        }
+        return fieldSet;
+    }
+
+    public static MultiValueMap<String, String> popCriteria(MultiValueMap<String, String> queryParameters) {
+
+        Set<Entry<String, List<String>>> entrySet = queryParameters.entrySet();
+
+        MultiValueMap<String, String> criterias = new LinkedMultiValueMap<String, String>();
+
+        entrySet.stream().forEach(entry -> {
+            final List<String> tempValues = new ArrayList<String>();
+            entry.getValue().stream().forEach(value -> tempValues.addAll(Arrays.asList(value.split(","))));
+            criterias.put(entry.getKey(), tempValues);
+        });
+
+        return criterias;
+    }
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/ReservedKeys.java b/src/main/java/org/onap/nbi/commons/ReservedKeys.java
new file mode 100644 (file)
index 0000000..dad59f2
--- /dev/null
@@ -0,0 +1,18 @@
+package org.onap.nbi.commons;
+
+/**
+ *
+ */
+public class ReservedKeys {
+
+    public static final String ALL_FIELDS = "all";
+
+    public static final String ID_FIELD = "id";
+
+    public static final String QUERY_KEY_FIELD = "fields";
+
+    public static final String QUERY_KEY_FIELD_ESCAPE = ":";
+
+    private ReservedKeys() {}
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/Resource.java b/src/main/java/org/onap/nbi/commons/Resource.java
new file mode 100644 (file)
index 0000000..4b374b0
--- /dev/null
@@ -0,0 +1,8 @@
+package org.onap.nbi.commons;
+
+
+public interface Resource {
+
+    public String getId();
+
+}
diff --git a/src/main/java/org/onap/nbi/commons/ResourceManagement.java b/src/main/java/org/onap/nbi/commons/ResourceManagement.java
new file mode 100644 (file)
index 0000000..67c0e3e
--- /dev/null
@@ -0,0 +1,149 @@
+package org.onap.nbi.commons;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+public class ResourceManagement<T extends Resource> {
+
+    /**
+     * Build default 201 filtered response for resource
+     *
+     * @param resource
+     * @param jsonRepresentation
+     * @return
+     */
+    protected ResponseEntity<Object> createResponse(final Resource resource,
+            final JsonRepresentation jsonRepresentation) {
+
+        URI location = null;
+        if (RequestContextHolder.getRequestAttributes() != null) {
+            location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(resource.getId())
+                    .toUri();
+        } else {
+            location = URI.create("/");
+        }
+
+
+        // Get entity representation
+        final Object entity = this.getEntity(resource, jsonRepresentation);
+
+        return ResponseEntity.created(location).body(entity);
+
+    }
+
+    /**
+     * Build default 200 filtered response for resource
+     *
+     * @param resource
+     * @param jsonRepresentation
+     * @return
+     */
+    protected ResponseEntity<Object> getResponse(final Object resource, final JsonRepresentation jsonRepresentation) {
+
+        // Get entity representation
+        final Object entity = this.getEntity(resource, jsonRepresentation);
+
+        return ResponseEntity.ok(entity);
+
+    }
+
+
+
+    /**
+     * Build default 206 filtered partial response for resource
+     *
+     * @param resource
+     * @param jsonRepresentation
+     * @return
+     */
+    protected ResponseEntity<Object> getPartialResponse(final Object resource,
+            final JsonRepresentation jsonRepresentation) {
+
+        // Get entity representation
+        final Object entity = this.getEntity(resource, jsonRepresentation);
+
+        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(entity);
+
+    }
+
+
+    /**
+     * Build default 200 filtered response for resource collection
+     *
+     * @param resources
+     * @param jsonRepresentation
+     * @param headers
+     * @return
+     */
+    protected ResponseEntity<Object> findResponse(final List<?> resources, final JsonRepresentation jsonRepresentation,
+            HttpHeaders headers) {
+
+        // Get entities representation
+        final Object entities = this.getEntities(resources, jsonRepresentation);
+
+        return ResponseEntity.ok().headers(headers).body(entities);
+
+    }
+
+
+    /**
+     * Build 204 Empty response
+     *
+     * @return
+     */
+    protected ResponseEntity<Object> deleteResponse() {
+
+        return ResponseEntity.noContent().build();
+    }
+
+    /**
+     * Get entity, as resource or jacksonNode depending fields value
+     *
+     * @param resource
+     * @param jsonRepresentation
+     * @return
+     */
+    protected Object getEntity(final Object resource, JsonRepresentation jsonRepresentation) {
+
+        Object entity;
+
+        Set<String> attributes = jsonRepresentation.getAttributes();
+
+        if (attributes == null || attributes.isEmpty() || attributes.contains(ReservedKeys.ALL_FIELDS)) {
+            entity = resource;
+        } else {
+            entity = JacksonFilter.createNode(resource, jsonRepresentation);
+        }
+
+        return entity;
+    }
+
+    /**
+     * Get entities, as resource list or jacksonNode depending fields value
+     *
+     * @param resources
+     * @param jsonRepresentation
+     * @return
+     */
+    protected Object getEntities(final List<?> resources, JsonRepresentation jsonRepresentation) {
+
+        Object entities;
+
+        Set<String> attributes = jsonRepresentation.getAttributes();
+
+        if (attributes == null || attributes.isEmpty() || attributes.contains(ReservedKeys.ALL_FIELDS)) {
+            entities = resources;
+        } else {
+            entities = JacksonFilter.createNodes(resources, jsonRepresentation);
+        }
+
+        return entities;
+    }
+
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644 (file)
index 0000000..1d8ff27
--- /dev/null
@@ -0,0 +1,6 @@
+# SERVER
+server.contextPath=/nbi/api/v1
+server.port = 8080
+
+# LOGGING
+logging.level.org.onap.nbi=WARN
diff --git a/src/test/java/org/onap/nbi/apis/status/StatusTest.java b/src/test/java/org/onap/nbi/apis/status/StatusTest.java
new file mode 100644 (file)
index 0000000..d262e15
--- /dev/null
@@ -0,0 +1,41 @@
+package org.onap.nbi.apis.status;
+
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.test.context.junit4.SpringRunner;
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class StatusTest {
+
+    @Autowired
+    StatusResource statusResource;
+
+    private MockHttpServletRequest request;
+
+    @Before
+    public void setup() {
+        request = new MockHttpServletRequest();
+        request.setRequestURI("/nbi/api/v1/status");
+    }
+
+    @Test
+    public void testHealthCheck() {
+        ResponseEntity<Object> response = statusResource.status(request);
+        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+        ObjectNode status = (ObjectNode) response.getBody();
+        assertThat(status.get("name").textValue()).isEqualTo("nbi");
+        assertThat(status.get("status").toString()).isEqualTo("OK");
+        assertThat(status.get("version").textValue()).isEqualTo("v1");
+    }
+}