2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017-2018 Amdocs
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
21 package org.onap.aai.datarouter.policy;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.security.NoSuchAlgorithmException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
33 import java.util.Map.Entry;
35 import org.apache.camel.Exchange;
36 import org.apache.camel.Processor;
37 import org.eclipse.persistence.dynamic.DynamicType;
38 import org.eclipse.persistence.internal.helper.DatabaseField;
39 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
40 import org.eclipse.persistence.oxm.MediaType;
41 import org.json.JSONException;
42 import org.json.JSONObject;
43 import org.onap.aai.cl.api.Logger;
44 import org.onap.aai.cl.eelf.LoggerFactory;
45 import org.onap.aai.cl.mdc.MdcContext;
46 import org.onap.aai.datarouter.entity.DocumentStoreDataEntity;
47 import org.onap.aai.datarouter.entity.OxmEntityDescriptor;
48 import org.onap.aai.datarouter.entity.SpikeEventEntity;
49 import org.onap.aai.datarouter.entity.SpikeEventVertex;
50 import org.onap.aai.datarouter.logging.EntityEventPolicyMsgs;
51 import org.onap.aai.datarouter.util.EntityOxmReferenceHelper;
52 import org.onap.aai.datarouter.util.ExternalOxmModelProcessor;
53 import org.onap.aai.datarouter.schema.OxmModelLoader;
54 import org.onap.aai.datarouter.util.RouterServiceUtil;
55 import org.onap.aai.datarouter.util.SearchServiceAgent;
56 import org.onap.aai.restclient.client.Headers;
57 import org.onap.aai.restclient.client.OperationResult;
58 import org.onap.aai.restclient.rest.HttpUtil;
61 import com.fasterxml.jackson.core.JsonProcessingException;
62 import com.fasterxml.jackson.databind.JsonNode;
63 import com.fasterxml.jackson.databind.ObjectMapper;
64 import com.fasterxml.jackson.databind.ObjectWriter;
66 public class SpikeEntityEventPolicy implements Processor {
68 public static final String additionalInfo = "Response of SpikeEntityEventPolicy";
69 private static final String ENTITY_SEARCH_SCHEMA = "entitysearch_schema.json";
71 private Collection<ExternalOxmModelProcessor> externalOxmModelProcessors;
76 * At present we don't need to support every event-type that could be present in the spike-events.
77 * The only one we want is a SPIKE "update-notification". In the future perhaps we need to add some
78 * configurability to the camel-route itself with a json camel filtering component so that routing
79 * logic can be modified as part of the camel route spring-xml instead of hard-coding the
80 * event filtering in here.
83 private static final String PROCESS_SPIKE_EVENT = "Process Spike Event";
85 private static final String UPDATE_NOTIFICATION = "update-notification";
86 private static final String SPIKE = "SPIKE";
88 private static final String HEADER_KEY = "header";
89 private static final String EVENT_TYPE_KEY = "event-type";
90 private static final String SOURCE_NAME_KEY = "source-name";
91 private static final String BODY_KEY = "body";
92 private static final String OPERATION_KEY = "operation";
94 private static final String VERTEX_KEY = "vertex";
95 private static final String VERTEX_KEY_KEY = "key";
96 private static final String VERTEX_TYPE_KEY = "type";
97 private static final String VERTEX_SCHEMA_VERSION_KEY = "schema-version";
99 private static final String CREATE = "create";
100 private static final String DELETE = "delete";
101 private static final String UPDATE = "update";
103 private static final List<String> SUPPORTED_ACTIONS =
104 Arrays.asList(CREATE, UPDATE, DELETE);
106 Map<String, DynamicJAXBContext> oxmVersionContextMap = new HashMap<>();
107 private String oxmVersion = null;
109 /** Agent for communicating with the Search Service. */
110 private SearchServiceAgent searchAgent = null;
111 private String entitySearchIndex;
113 private Logger logger;
114 private Logger metricsLogger;
116 public enum ResponseType {
117 SUCCESS, PARTIAL_SUCCESS, FAILURE;
120 public SpikeEntityEventPolicy(SpikeEntityEventPolicyConfig config) throws FileNotFoundException {
121 LoggerFactory loggerFactoryInstance = LoggerFactory.getInstance();
122 logger = loggerFactoryInstance.getLogger(SpikeEntityEventPolicy.class.getName());
123 metricsLogger = loggerFactoryInstance.getMetricsLogger(SpikeEntityEventPolicy.class.getName());
126 //srcDomain = config.getSourceDomain();
128 // Populate the index names.
129 entitySearchIndex = config.getSearchEntitySearchIndex();
131 // Instantiate the agent that we will use for interacting with the Search Service.
132 searchAgent = new SearchServiceAgent(config.getSearchCertName(), config.getSearchKeystore(),
133 config.getSearchKeystorePwd(),
134 EntityEventPolicy.concatSubUri(config.getSearchBaseUrl(), config.getSearchEndpoint()),
135 config.getSearchEndpointDocuments(), logger);
137 this.externalOxmModelProcessors = new ArrayList<>();
138 this.externalOxmModelProcessors.add(EntityOxmReferenceHelper.getInstance());
139 OxmModelLoader.registerExternalOxmModelProcessors(externalOxmModelProcessors);
140 OxmModelLoader.loadModels();
141 oxmVersionContextMap = OxmModelLoader.getVersionContextMap();
142 parseLatestOxmVersion();
145 private void parseLatestOxmVersion() {
146 int latestVersion = -1;
147 if (oxmVersionContextMap != null) {
148 Iterator<Entry<String, DynamicJAXBContext>> it = oxmVersionContextMap.entrySet().iterator();
149 while (it.hasNext()) {
150 Map.Entry pair = (Map.Entry) it.next();
152 String version = pair.getKey().toString();
153 int versionNum = Integer.parseInt(version.substring(1, version.length()));
155 if (versionNum > latestVersion) {
156 latestVersion = versionNum;
157 oxmVersion = pair.getKey().toString();
160 logger.info(EntityEventPolicyMsgs.PROCESS_OXM_MODEL_FOUND, pair.getKey().toString());
163 logger.error(EntityEventPolicyMsgs.PROCESS_OXM_MODEL_MISSING, "");
167 public void startup() {
169 // Create the indexes in the search service if they do not already exist.
170 searchAgent.createSearchIndex(entitySearchIndex, ENTITY_SEARCH_SCHEMA);
171 logger.info(EntityEventPolicyMsgs.ENTITY_EVENT_POLICY_REGISTERED);
177 * Convert object to json.
179 * @param object the object
180 * @param pretty the pretty
182 * @throws JsonProcessingException the json processing exception
184 public static String convertObjectToJson(Object object, boolean pretty)
185 throws JsonProcessingException {
189 ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
192 ow = new ObjectMapper().writer();
195 return ow.writeValueAsString(object);
198 public void returnWithError(Exchange exchange, String payload, String errorMsg) {
199 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE, errorMsg);
200 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, errorMsg, payload);
201 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
205 public void process(Exchange exchange) /*throws Exception*/ {
207 long startTime = System.currentTimeMillis();
209 final String eventPayload = exchange.getIn().getBody().toString();
210 JSONObject mainJson = null;
215 * It is expected that mainJson will have multiple top level objects:
221 mainJson = new JSONObject(eventPayload);
222 } catch (JSONException exc) {
223 returnWithError(exchange, eventPayload, "Invalid Payload");
227 JSONObject eventHeader = mainJson.getJSONObject(HEADER_KEY);
229 if (eventHeader == null) {
230 returnWithError(exchange, eventPayload, "Payload is missing " + HEADER_KEY);
235 * Only process SPIKE update-notification events
238 final String sourceName = eventHeader.getString(SOURCE_NAME_KEY);
239 final String eventType = eventHeader.getString(EVENT_TYPE_KEY);
241 if (!(SPIKE.equals(sourceName) && UPDATE_NOTIFICATION.equals(eventType))) {
243 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, "Ignoring event with sourceName='"
244 + sourceName + "' and eventType='" + eventType + "'. Payload=" + eventPayload);
245 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
246 "Ignoring event with sourceName='" + sourceName + "' and eventType='" + eventType + "'.");
249 * I don't think ignoring a non-applicable event constitutes a failure.
252 setResponse(exchange, ResponseType.SUCCESS, additionalInfo);
256 JSONObject eventBody = mainJson.getJSONObject(BODY_KEY);
258 if (eventBody == null) {
259 returnWithError(exchange, eventPayload, "Payload is missing " + BODY_KEY);
263 String action = eventBody.getString(OPERATION_KEY);
264 if (action == null || !SUPPORTED_ACTIONS.contains(action.toLowerCase())) {
265 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
266 "Unrecognized action '" + action + "'", eventPayload);
267 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
268 "Unrecognized action '" + action + "'");
269 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
273 // Load the event body data, any errors will result in a failure and discard
275 JSONObject spikeVertex = eventBody.getJSONObject(VERTEX_KEY);
276 if (spikeVertex == null) {
277 returnWithError(exchange, eventPayload, "Payload is missing " + VERTEX_KEY);
281 SpikeEventVertex eventVertex = null;
283 eventVertex = initializeSpikeEventVertex(spikeVertex);
284 } catch (JSONException exc) {
285 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, "Error initializating spike event. Error: " + exc.getMessage(),
287 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
288 "Error initializating spike event. Error: " + exc.getMessage());
290 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
294 DynamicJAXBContext oxmJaxbContext = loadOxmContext(oxmVersion);
295 if (oxmJaxbContext == null) {
296 logger.error(EntityEventPolicyMsgs.OXM_VERSION_NOT_SUPPORTED, oxmVersion);
297 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, "OXM version mismatch", eventPayload);
299 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
303 String entityType = eventVertex.getType();
304 if (entityType == null || entityType.isEmpty()) {
305 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
306 "Payload header missing entity type", eventPayload);
307 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
308 "Payload header missing entity type");
310 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
314 String entityKey = eventVertex.getKey();
315 if (entityKey == null || entityKey.isEmpty()) {
316 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, "Payload vertex missing entity key",
318 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
319 "Payload vertex missing entity key");
321 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
324 String entityLink = eventVertex.getEntityLink();
325 if (entityLink == null || entityLink.isEmpty()) {
326 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
327 "Payload header missing entity link", eventPayload);
328 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
329 "Payload header missing entity link");
331 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
335 // log the fact that all data are in good shape
336 logger.info(EntityEventPolicyMsgs.PROCESS_ENTITY_EVENT_POLICY_NONVERBOSE, action, entityType);
337 logger.debug(EntityEventPolicyMsgs.PROCESS_ENTITY_EVENT_POLICY_VERBOSE, action, entityType,
340 // Process for building SpikeEventEntity object
341 String[] entityTypeArr = entityType.split("-");
342 String oxmEntityType = "";
343 for (String entityWord : entityTypeArr) {
344 oxmEntityType += entityWord.substring(0, 1).toUpperCase() + entityWord.substring(1);
347 List<String> searchableAttr =
348 getOxmAttributes(oxmJaxbContext, oxmEntityType, entityType, "searchable");
349 if (searchableAttr == null) {
350 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
351 "Searchable attribute not found for payload entity type '" + entityType + "'");
352 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
353 "Searchable attribute not found for payload entity type '" + entityType + "'",
356 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
360 String entityPrimaryKeyFieldName =
361 getEntityPrimaryKeyFieldName(oxmJaxbContext, eventPayload, oxmEntityType, entityType);
362 if (entityPrimaryKeyFieldName == null) {
363 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
364 "Payload missing primary key attribute");
365 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
366 "Payload missing primary key attribute", eventPayload);
367 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
370 String entityPrimaryKeyFieldValue = lookupValueUsingKey(eventPayload, entityPrimaryKeyFieldName);
371 if (entityPrimaryKeyFieldValue == null || entityPrimaryKeyFieldValue.isEmpty()) {
372 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
373 "Payload missing primary value attribute");
374 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
375 "Payload missing primary value attribute", eventPayload);
377 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
381 SpikeEventEntity spikeEventEntity = new SpikeEventEntity();
384 * Use the OXM Model to determine the primary key field name based on the entity-type
387 spikeEventEntity.setEntityPrimaryKeyName(entityPrimaryKeyFieldName);
388 spikeEventEntity.setEntityPrimaryKeyValue(entityPrimaryKeyFieldValue);
389 spikeEventEntity.setEntityType(entityType);
390 spikeEventEntity.setLink(entityLink);
392 System.out.println(spikeEventEntity);
394 if (!getSearchTags(spikeEventEntity, searchableAttr, eventPayload, action)) {
395 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_NONVERBOSE,
396 "Payload missing searchable attribute for entity type '" + entityType + "'");
397 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE,
398 "Payload missing searchable attribute for entity type '" + entityType + "'", eventPayload);
400 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
405 spikeEventEntity.deriveFields();
407 } catch (NoSuchAlgorithmException e) {
408 logger.error(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, "Cannot create unique SHA digest");
409 logger.debug(EntityEventPolicyMsgs.DISCARD_EVENT_VERBOSE, "Cannot create unique SHA digest",
412 setResponse(exchange, ResponseType.FAILURE, additionalInfo);
417 handleSearchServiceOperation(spikeEventEntity, action, entitySearchIndex);
419 long stopTime = System.currentTimeMillis();
420 metricsLogger.info(EntityEventPolicyMsgs.OPERATION_RESULT_NO_ERRORS, PROCESS_SPIKE_EVENT,
421 String.valueOf(stopTime - startTime));
423 setResponse(exchange, ResponseType.SUCCESS, additionalInfo);
429 private void setResponse(Exchange exchange, ResponseType responseType, String additionalInfo) {
431 exchange.getOut().setHeader("ResponseType", responseType.toString());
432 exchange.getOut().setBody(additionalInfo);
435 private SpikeEventVertex initializeSpikeEventVertex(JSONObject vertexObject) throws JSONException {
438 * These are all critical keys
441 final String vertexType = vertexObject.getString(VERTEX_TYPE_KEY);
442 final String vertexKey = vertexObject.getString(VERTEX_KEY_KEY);
443 final String vertexSchemaVersion = vertexObject.getString(VERTEX_SCHEMA_VERSION_KEY);
445 SpikeEventVertex eventVertex = new SpikeEventVertex(vertexType, vertexKey);
446 eventVertex.setSchemaVersion(vertexSchemaVersion);
447 logger.debug(EntityEventPolicyMsgs.UEB_EVENT_HEADER_PARSED, eventVertex.toString());
454 private String getEntityPrimaryKeyFieldName(DynamicJAXBContext oxmJaxbContext, String payload,
455 String oxmEntityType, String entityType) {
457 DynamicType entity = oxmJaxbContext.getDynamicType(oxmEntityType);
458 if (entity == null) {
462 List<DatabaseField> list = entity.getDescriptor().getPrimaryKeyFields();
463 if (list != null && !list.isEmpty()) {
464 String keyName = list.get(0).getName();
465 return keyName.substring(0, keyName.indexOf('/'));
471 private String lookupValueUsingKey(String payload, String key) {
472 JsonNode jsonNode = convertToJsonNode(payload);
473 return RouterServiceUtil.recursivelyLookupJsonPayload(jsonNode, key);
477 private JsonNode convertToJsonNode(String payload) {
479 ObjectMapper mapper = new ObjectMapper();
480 JsonNode jsonNode = null;
482 jsonNode = mapper.readTree(mapper.getJsonFactory().createJsonParser(payload));
483 } catch (IOException e) {
484 logger.debug(EntityEventPolicyMsgs.FAILED_TO_PARSE_UEB_PAYLOAD, VERTEX_KEY + " missing",
486 logger.error(EntityEventPolicyMsgs.FAILED_TO_PARSE_UEB_PAYLOAD, VERTEX_KEY + " missing",
494 private boolean getSearchTags(SpikeEventEntity spikeEventEntity, List<String> searchableAttr,
495 String payload, String action) {
497 boolean hasSearchableAttr = false;
498 for (String searchTagField : searchableAttr) {
499 String searchTagValue;
500 if (searchTagField.equalsIgnoreCase(spikeEventEntity.getEntityPrimaryKeyName())) {
501 searchTagValue = spikeEventEntity.getEntityPrimaryKeyValue();
503 searchTagValue = this.lookupValueUsingKey(payload, searchTagField);
506 if (searchTagValue != null && !searchTagValue.isEmpty()) {
507 hasSearchableAttr = true;
508 spikeEventEntity.addSearchTagWithKey(searchTagValue, searchTagField);
511 return hasSearchableAttr;
515 * Check if OXM version is available. If available, load it.
517 private DynamicJAXBContext loadOxmContext(String version) {
518 if (version == null) {
519 logger.error(EntityEventPolicyMsgs.FAILED_TO_FIND_OXM_VERSION, version);
523 return oxmVersionContextMap.get(version);
526 private List<String> getOxmAttributes(DynamicJAXBContext oxmJaxbContext, String oxmEntityType,
527 String entityType, String fieldName) {
529 DynamicType entity = oxmJaxbContext.getDynamicType(oxmEntityType);
530 if (entity == null) {
535 * Check for searchable XML tag
537 List<String> fieldValues = null;
538 Map<String, String> properties = entity.getDescriptor().getProperties();
539 for (Map.Entry<String, String> entry : properties.entrySet()) {
540 if (entry.getKey().equalsIgnoreCase(fieldName)) {
541 fieldValues = Arrays.asList(entry.getValue().split(","));
551 protected SpikeEventEntity getPopulatedEntity(JsonNode entityNode,
552 OxmEntityDescriptor resultDescriptor) {
553 SpikeEventEntity d = new SpikeEventEntity();
555 d.setEntityType(resultDescriptor.getEntityName());
557 List<String> primaryKeyValues = new ArrayList<>();
558 List<String> primaryKeyNames = new ArrayList<>();
561 for (String keyName : resultDescriptor.getPrimaryKeyAttributeName()) {
562 pkeyValue = RouterServiceUtil.getNodeFieldAsText(entityNode, keyName);
563 if (pkeyValue != null) {
564 primaryKeyValues.add(pkeyValue);
565 primaryKeyNames.add(keyName);
567 logger.error(EntityEventPolicyMsgs.PRIMARY_KEY_NULL_FOR_ENTITY_TYPE,
568 resultDescriptor.getEntityName());
572 final String primaryCompositeKeyValue = RouterServiceUtil.concatArray(primaryKeyValues, "/");
573 d.setEntityPrimaryKeyValue(primaryCompositeKeyValue);
574 final String primaryCompositeKeyName = RouterServiceUtil.concatArray(primaryKeyNames, "/");
575 d.setEntityPrimaryKeyName(primaryCompositeKeyName);
577 final List<String> searchTagFields = resultDescriptor.getSearchableAttributes();
580 * Based on configuration, use the configured field names for this entity-Type to build a
581 * multi-value collection of search tags for elastic search entity search criteria.
585 for (String searchTagField : searchTagFields) {
586 String searchTagValue = RouterServiceUtil.getNodeFieldAsText(entityNode, searchTagField);
587 if (searchTagValue != null && !searchTagValue.isEmpty()) {
588 d.addSearchTagWithKey(searchTagValue, searchTagField);
597 * Perform create, read, update or delete (CRUD) operation on search engine's suggestive search
600 * @param eventEntity Entity/data to use in operation
601 * @param action The operation to perform
602 * @param target Resource to perform the operation on
603 * @param allowDeleteEvent Allow delete operation to be performed on resource
605 protected void handleSearchServiceOperation(DocumentStoreDataEntity eventEntity, String action,
609 Map<String, List<String>> headers = new HashMap<>();
610 headers.put(Headers.FROM_APP_ID, Arrays.asList("DataLayer"));
611 headers.put(Headers.TRANSACTION_ID, Arrays.asList(MDC.get(MdcContext.MDC_REQUEST_ID)));
613 String entityId = eventEntity.getId();
615 if ((action.equalsIgnoreCase(CREATE) && entityId != null)
616 || action.equalsIgnoreCase(UPDATE)) {
618 // Run the GET to retrieve the ETAG from the search service
619 OperationResult storedEntity = searchAgent.getDocument(index, entityId);
621 if (HttpUtil.isHttpResponseClassSuccess(storedEntity.getResultCode())) {
622 List<String> etag = storedEntity.getHeaders().get(Headers.ETAG);
624 if (etag != null && !etag.isEmpty()) {
625 headers.put(Headers.IF_MATCH, etag);
627 logger.error(EntityEventPolicyMsgs.NO_ETAG_AVAILABLE_FAILURE, index, entityId);
631 // Write the entity to the search service.
633 searchAgent.putDocument(index, entityId, eventEntity.getAsJson(), headers);
634 } else if (action.equalsIgnoreCase(CREATE)) {
635 // Write the entry to the search service.
636 searchAgent.postDocument(index, eventEntity.getAsJson(), headers);
638 } else if (action.equalsIgnoreCase(DELETE)) {
639 // Run the GET to retrieve the ETAG from the search service
640 OperationResult storedEntity = searchAgent.getDocument(index, entityId);
642 if (HttpUtil.isHttpResponseClassSuccess(storedEntity.getResultCode())) {
643 List<String> etag = storedEntity.getHeaders().get(Headers.ETAG);
645 if (etag != null && !etag.isEmpty()) {
646 headers.put(Headers.IF_MATCH, etag);
648 logger.error(EntityEventPolicyMsgs.NO_ETAG_AVAILABLE_FAILURE, index, entityId);
652 * The Spring-Boot version of the search-data-service rejects the DELETE operation unless
653 * we specify a Content-Type.
656 headers.put("Content-Type", Arrays.asList(MediaType.APPLICATION_JSON.getMediaType()));
658 searchAgent.deleteDocument(index, eventEntity.getId(), headers);
660 logger.error(EntityEventPolicyMsgs.NO_ETAG_AVAILABLE_FAILURE, index, entityId);
663 logger.error(EntityEventPolicyMsgs.ENTITY_OPERATION_NOT_SUPPORTED, action);
665 } catch (IOException e) {
666 logger.error(EntityEventPolicyMsgs.FAILED_TO_UPDATE_ENTITY_IN_DOCSTORE, eventEntity.getId(),
673 // put this here until we find a better spot
675 * Helper utility to concatenate substrings of a URI together to form a proper URI.
677 * @param suburis the list of substrings to concatenate together
678 * @return the concatenated list of substrings
680 public static String concatSubUri(String... suburis) {
681 String finalUri = "";
683 for (String suburi : suburis) {
685 if (suburi != null) {
686 // Remove any leading / since we only want to append /
687 suburi = suburi.replaceFirst("^/*", "");
689 // Add a trailing / if one isn't already there
690 finalUri += suburi.endsWith("/") ? suburi : suburi + "/";