2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.aai.serialization.queryformats;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import com.google.gson.JsonArray;
26 import com.google.gson.JsonObject;
27 import org.apache.tinkerpop.gremlin.structure.Edge;
28 import org.apache.tinkerpop.gremlin.structure.Vertex;
29 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
30 import org.onap.aai.db.props.AAIProperties;
31 import org.onap.aai.logging.LogFormatTools;
32 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
33 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
34 import org.springframework.util.LinkedMultiValueMap;
35 import org.springframework.util.MultiValueMap;
38 import java.util.stream.Collectors;
39 import java.util.stream.Stream;
42 public class LifecycleFormat extends HistoryFormat {
44 private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleFormat.class);
46 protected LifecycleFormat(Builder builder) {
50 protected JsonArray createPropertiesObject(Vertex v) {
51 JsonArray jsonArray = new JsonArray();
52 Iterator<VertexProperty<Object>> iter = v.properties();
53 List<JsonObject> jsonList = new ArrayList<>();
55 Map<String, Set<Long>> propStartTimes = new HashMap<>(); //vertex end
56 while (iter.hasNext()) {
57 JsonObject json = new JsonObject();
58 VertexProperty<Object> prop = iter.next();
59 if(prop.key() != null && ignoredKeys.contains(prop.key())){
62 if (!propStartTimes.containsKey(prop.key())) {
63 propStartTimes.put(prop.key(), new HashSet<>());
64 if (v.property(AAIProperties.END_TS).isPresent()) {
65 propStartTimes.get(prop.key()).add(v.<Long>value(AAIProperties.END_TS));
69 json.addProperty(KEY, prop.key());
70 json = mapPropertyValues(json, VALUE, prop.value());
71 JsonObject metaProperties = createMetaPropertiesObject(prop);
72 if (isTsInRange(metaProperties.get(AAIProperties.START_TS).getAsLong())) {
73 JsonObject jo = new JsonObject();
74 jo.add(KEY, json.get(KEY));
75 jo.add(VALUE, json.get(VALUE));
76 jo.add(TIMESTAMP, metaProperties.get(AAIProperties.START_TS));
77 jo.add(SOT, metaProperties.get(AAIProperties.SOURCE_OF_TRUTH));
78 jo.add(TX_ID, metaProperties.get(AAIProperties.START_TX_ID));
80 propStartTimes.get(prop.key()).add(metaProperties.get(AAIProperties.START_TS).getAsLong());
82 if (!AAIProperties.RESOURCE_VERSION.equals(prop.key())
83 && metaProperties.has(AAIProperties.END_TS)
84 && isTsInRange(metaProperties.get(AAIProperties.END_TS).getAsLong())
85 && !propStartTimes.get(prop.key()).contains(metaProperties.get(AAIProperties.END_TS).getAsLong())) {
86 JsonObject jo = new JsonObject();
87 jo.add(KEY, json.get(KEY));
89 jo.add(TIMESTAMP, metaProperties.get(AAIProperties.END_TS));
90 jo.add(SOT, metaProperties.get(AAIProperties.END_SOT));
91 jo.add(TX_ID, metaProperties.get(AAIProperties.END_TX_ID));
97 // remove all the null values that is the start time for another value
98 .filter(jo -> !jo.get(VALUE).isJsonNull() || !propStartTimes.get(jo.get(KEY).getAsString()).contains(jo.get(TIMESTAMP).getAsLong()))
99 // sort by ts in decreasing order
100 .sorted((o1, o2) -> {
101 if (o1.get(TIMESTAMP).getAsLong() == o2.get(TIMESTAMP).getAsLong()) {
102 return o1.get(KEY).getAsString().compareTo(o2.get(KEY).getAsString());
104 return Long.compare(o2.get(TIMESTAMP).getAsLong(), o1.get(TIMESTAMP).getAsLong());
106 }).forEach(jsonArray::add);
111 private boolean isTsInRange(long ts) {
112 return ts >= startTs && ts <= endTs;
117 protected boolean isValidEdge(Edge e) {
118 if (e.property(AAIProperties.END_TS).isPresent()) {
119 long edgeStartTs = e.<Long>value(AAIProperties.START_TS);
120 long edgeEndTs = e.<Long>value(AAIProperties.END_TS);
121 return isTsInRange(edgeStartTs) || isTsInRange(edgeEndTs);
123 long edgeStartTs = e.<Long>value(AAIProperties.START_TS);
124 return isTsInRange(edgeStartTs);
129 protected JsonObject getRelatedObject(Edge e, Vertex related) throws AAIFormatVertexException {
131 JsonObject json = new JsonObject();
132 json.addProperty("relationship-label", e.label());
133 json.addProperty("node-type", related.<String>value(AAIProperties.NODE_TYPE));
134 json.addProperty("url", this.urlBuilder.pathed(related));
135 if (related.property(AAIProperties.AAI_URI).isPresent()) {
136 json.addProperty("uri", related.<String>value(AAIProperties.AAI_URI));
138 LOGGER.warn("Vertex {} is missing aai-uri", related.id());
139 json.addProperty("uri", "NA");
142 if(e.property(AAIProperties.START_TS).isPresent()) {
143 long edgeStartTimestamp = e.<Long>value(AAIProperties.START_TS);
144 if (isTsInRange(edgeStartTimestamp)) {
145 json.addProperty(TIMESTAMP, e.property(AAIProperties.START_TS).isPresent()? e.<Long>value(AAIProperties.START_TS) : 0);
146 json.addProperty(SOT, e.property(AAIProperties.SOURCE_OF_TRUTH).isPresent()? e.value(AAIProperties.SOURCE_OF_TRUTH) : "");
147 json.addProperty(TX_ID, e.property(AAIProperties.START_TX_ID).isPresent()? e.value(AAIProperties.START_TX_ID) : "N/A");
151 if(e.property(AAIProperties.END_TS).isPresent()) {
152 long edgeEndTimestamp = e.<Long>value(AAIProperties.END_TS);
153 if (isTsInRange(edgeEndTimestamp)) {
154 json.addProperty(END_TIMESTAMP, edgeEndTimestamp);
155 json.addProperty(END_SOT, e.property(AAIProperties.END_SOT).isPresent() ? e.value(AAIProperties.END_SOT) : "");
156 json.addProperty(END_TX_ID, e.property(AAIProperties.END_TX_ID).isPresent() ? e.value(AAIProperties.END_TX_ID) : "N/A");
164 protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException {
165 JsonObject json = new JsonObject();
166 json.addProperty(NODE_TYPE, v.<String>value(AAIProperties.NODE_TYPE));
167 json.addProperty("url", this.urlBuilder.pathed(v));
168 if (v.property(AAIProperties.AAI_URI).isPresent()) {
169 json.addProperty("uri", v.<String>value(AAIProperties.AAI_URI));
171 LOGGER.warn("Vertex {} is missing aai-uri", v.id());
172 json.addProperty("uri", "NA");
174 json.addProperty(TIMESTAMP, v.<Long>value(AAIProperties.START_TS));
176 json.add(PROPERTIES, this.createPropertiesObject(v));
179 json.add(RELATED_TO, this.createRelationshipObject(v));
182 json.add(NODE_ACTIONS, getNodeActions(v, json));
184 if (json.getAsJsonObject().get(PROPERTIES).getAsJsonArray().size() == 0
185 && json.getAsJsonObject().get(RELATED_TO).getAsJsonArray().size() == 0
186 && json.getAsJsonObject().get(NODE_ACTIONS).getAsJsonArray().size() == 0) {
187 return Optional.empty();
188 } else if (json.getAsJsonObject().get(PROPERTIES).getAsJsonArray().size() == 1
189 && (json.getAsJsonObject().get(RELATED_TO).getAsJsonArray().size() > 0
190 || json.getAsJsonObject().get(NODE_ACTIONS).getAsJsonArray().size() > 0)) {
191 if (json.getAsJsonObject().get(PROPERTIES).getAsJsonArray()
192 .get(0).getAsJsonObject().get("key").getAsString().equals(AAIProperties.END_TS)) {
193 json.getAsJsonObject().add(PROPERTIES, new JsonArray());
197 return Optional.of(json);
201 protected Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException {
202 return Optional.empty();
205 private JsonArray getNodeActions(Vertex v, JsonObject json) {
206 JsonArray nodeActions = new JsonArray();
208 if (v.property(AAIProperties.END_TS).isPresent()) {
209 long deletedTs = (Long) v.property(AAIProperties.END_TS).value();
210 if (isTsInRange(deletedTs)) {
211 action = new JsonObject();
212 action.addProperty("action", "DELETED");
213 action.addProperty(TIMESTAMP, deletedTs);
214 if (v.property(AAIProperties.END_TS).property(AAIProperties.SOURCE_OF_TRUTH).isPresent()) {
215 action.addProperty(SOT, v.property(AAIProperties.END_TS).<String>value(AAIProperties.SOURCE_OF_TRUTH));
217 if (v.property(AAIProperties.END_TS).property(AAIProperties.END_TX_ID).isPresent()) {
218 action.addProperty(TX_ID, v.property(AAIProperties.END_TS).<String>value(AAIProperties.END_TX_ID));
220 action.addProperty(TX_ID, "N/A");
222 nodeActions.add(action);
225 long createdTs = json.get(TIMESTAMP).getAsLong();
226 if (isTsInRange(createdTs)) {
227 action = new JsonObject();
228 action.addProperty("action", "CREATED");
229 action.addProperty(TIMESTAMP, createdTs);
230 action.addProperty(SOT, v.<String>value(AAIProperties.SOURCE_OF_TRUTH));
231 if (v.property(AAIProperties.SOURCE_OF_TRUTH).property(AAIProperties.START_TX_ID).isPresent()) {
232 action.addProperty(TX_ID, v.property(AAIProperties.SOURCE_OF_TRUTH).<String>value(AAIProperties.START_TX_ID));
234 action.addProperty(TX_ID, "N/A");
236 nodeActions.add(action);
241 public JsonArray process(List<Object> queryResults) {
242 JsonArray body = new JsonArray();
243 Stream<Object> stream;
244 if (queryResults.size() >= this.parallelThreshold()) {
245 stream = queryResults.parallelStream();
247 stream = queryResults.stream();
250 final boolean isParallel = stream.isParallel();
254 return this.formatObject(o);
255 } catch (AAIFormatVertexException e) {
256 LOGGER.warn("Failed to format vertex, returning a partial list " + LogFormatTools.getStackTop(e));
257 } catch (AAIFormatQueryResultFormatNotSupported e) {
258 LOGGER.warn("Failed to format result type of the query " + LogFormatTools.getStackTop(e));
261 return Optional.<JsonObject>empty();
262 }).filter(Optional::isPresent)
266 synchronized (body) {
273 JsonArray result = organizeBody(body);
274 result.forEach(jsonElement -> jsonElement.getAsJsonObject().remove(TIMESTAMP));
278 private JsonArray organizeBody(JsonArray body) {
280 final MultiValueMap<String, Integer> toBeMerged = new LinkedMultiValueMap<>();
281 for (int i = 0; i < body.size(); i++) {
282 toBeMerged.add(body.get(i).getAsJsonObject().get("uri").getAsString(), i);
285 final List<List<Integer>> dupes = toBeMerged.values().stream().filter(l -> l.size() > 1).collect(Collectors.toList());
286 if (dupes.isEmpty()) {
289 Set<Integer> remove = new HashSet<>();
290 for (List<Integer> dupe : dupes) {
291 dupe.sort((a,b) -> Long.compare(body.get(b).getAsJsonObject().get(TIMESTAMP).getAsLong(), body.get(a).getAsJsonObject().get(TIMESTAMP).getAsLong()));
292 int keep = dupe.remove(0);
293 for (Integer idx : dupe) {
294 body.get(keep).getAsJsonObject().getAsJsonArray(NODE_ACTIONS)
295 .addAll(body.get(idx).getAsJsonObject().getAsJsonArray(NODE_ACTIONS));
296 body.get(keep).getAsJsonObject().getAsJsonArray(PROPERTIES)
297 .addAll(body.get(idx).getAsJsonObject().getAsJsonArray(PROPERTIES));
298 body.get(keep).getAsJsonObject().getAsJsonArray(RELATED_TO)
299 .addAll(body.get(idx).getAsJsonObject().getAsJsonArray(RELATED_TO));
303 final JsonArray newBody = new JsonArray();
304 for (int i = 0; i < body.size(); i++) {
305 if (!remove.contains(i)) {
306 newBody.add(body.get(i));