diff options
author | MatthieuGeerebaert <matthieu.geerebaert@orange.com> | 2018-03-26 17:08:28 +0200 |
---|---|---|
committer | MatthieuGeerebaert <matthieu.geerebaert@orange.com> | 2018-03-27 17:56:13 +0200 |
commit | 25da75eacf48856824dd4e89b7ebd0da68fdef8a (patch) | |
tree | 5812664a19c4630cfc63e323901eddfa59d7f76e /src/main | |
parent | 4b0d6728a146317a091b3e8b831e48bbd3fc9136 (diff) |
Add initial sources
- Springboot application
- Apache license 2.0
- Healthcheck
Change-Id: I0dedfbe3348195f7be00ec8d27fbf25dfcda52b0
Issue-ID: EXTAPI-36
Signed-off-by: MatthieuGeerebaert <matthieu.geerebaert@orange.com>
Diffstat (limited to 'src/main')
15 files changed, 705 insertions, 0 deletions
diff --git a/src/main/java/org/onap/nbi/Application.java b/src/main/java/org/onap/nbi/Application.java new file mode 100644 index 0000000..0609f9c --- /dev/null +++ b/src/main/java/org/onap/nbi/Application.java @@ -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 index 0000000..f4b0bcd --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/status/StatusResource.java @@ -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 index 0000000..2309da4 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/status/StatusService.java @@ -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 index 0000000..7262aea --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/status/StatusServiceImpl.java @@ -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 index 0000000..ecaa89e --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/status/model/ApplicationStatus.java @@ -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 index 0000000..a54dc50 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/status/model/StatusType.java @@ -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 index 0000000..1d14f12 --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/BeanUtils.java @@ -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 index 0000000..bc173c3 --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/JacksonFilter.java @@ -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 index 0000000..0c71262 --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/JsonRepresentation.java @@ -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 index 0000000..8ee453f --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/Query.java @@ -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 index 0000000..69c32df --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/QueryParserUtils.java @@ -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 index 0000000..dad59f2 --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/ReservedKeys.java @@ -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 index 0000000..4b374b0 --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/Resource.java @@ -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 index 0000000..67c0e3e --- /dev/null +++ b/src/main/java/org/onap/nbi/commons/ResourceManagement.java @@ -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 index 0000000..1d8ff27 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,6 @@ +# SERVER +server.contextPath=/nbi/api/v1 +server.port = 8080 + +# LOGGING +logging.level.org.onap.nbi=WARN |