From 7eb81d52479e247050bee5968b2702030bf2c8c4 Mon Sep 17 00:00:00 2001 From: Michael Arrastia Date: Wed, 6 Jun 2018 14:55:59 +0100 Subject: [PATCH] Return ETag in response header This update generates a unique hash for the payload and returns it as an etag header. Change-Id: I471dc1e74e8096d4fdb4f4db7f22e08ef4842ace Issue-ID: AAI-1208 Signed-off-by: Michael Arrastia --- .../src/main/java/org/onap/champ/ChampRESTAPI.java | 54 ++-- .../java/org/onap/champ/util/HashGenerator.java | 63 ++++ .../org/onap/champ/util/etag/EtagGenerator.java | 107 +++++++ .../onap/champ/util/etag/TestEtagGenerator.java | 352 +++++++++++++++++++++ 4 files changed, 554 insertions(+), 22 deletions(-) create mode 100644 champ-service/src/main/java/org/onap/champ/util/HashGenerator.java create mode 100644 champ-service/src/main/java/org/onap/champ/util/etag/EtagGenerator.java create mode 100644 champ-service/src/test/java/org/onap/champ/util/etag/TestEtagGenerator.java diff --git a/champ-service/src/main/java/org/onap/champ/ChampRESTAPI.java b/champ-service/src/main/java/org/onap/champ/ChampRESTAPI.java index 4b7c9a7..726944b 100644 --- a/champ-service/src/main/java/org/onap/champ/ChampRESTAPI.java +++ b/champ-service/src/main/java/org/onap/champ/ChampRESTAPI.java @@ -21,6 +21,7 @@ package org.onap.champ; import java.io.IOException; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -40,6 +41,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -67,6 +69,7 @@ import org.onap.champ.service.logging.ChampMsgs; import org.onap.champ.service.logging.LoggingUtil; import org.onap.champ.util.ChampProperties; import org.onap.champ.util.ChampServiceConstants; +import org.onap.champ.util.etag.EtagGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -77,6 +80,7 @@ public class ChampRESTAPI { private ObjectMapper mapper; private ChampDataService champDataService; + private EtagGenerator etagGenerator; private String TRANSACTION_METHOD = "method"; private Timer timer; @@ -85,8 +89,7 @@ public class ChampRESTAPI { private static Logger metricsLogger = LoggerFactory.getInstance().getMetricsLogger(ChampRESTAPI.class.getName()); private static final Pattern QUERY_OBJECT_ID_URL_MATCH = Pattern.compile("_reserved_(.*)"); - - public ChampRESTAPI(ChampDataService champDataService, ChampAsyncRequestProcessor champAsyncRequestProcessor) { + public ChampRESTAPI(ChampDataService champDataService, ChampAsyncRequestProcessor champAsyncRequestProcessor) throws NoSuchAlgorithmException { this.champDataService = champDataService; // Async request handling is optional. @@ -103,6 +106,8 @@ public class ChampRESTAPI { module.addSerializer(ChampRelationship.class, new ChampRelationshipSerializer()); module.addDeserializer(ChampRelationship.class, new ChampRelationshipDeserializer()); mapper.registerModule(module); + + etagGenerator = new EtagGenerator(); } @GET @@ -134,13 +139,17 @@ public class ChampRESTAPI { if (retrieved == null) { response = Response.status(Status.NOT_FOUND).entity(objectId + " not found").build(); } else { - response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).build(); + EntityTag etag = new EntityTag(etagGenerator.computeHashForChampObject(retrieved)); + response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).tag(etag).build(); } } catch (JsonProcessingException e) { response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build(); } catch (ChampServiceException ce) { response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + LoggingUtil.logInternalError(logger, e); } finally { logger.debug(response.getEntity().toString()); LoggingUtil.logRestRequest(logger, auditLogger, req, response); @@ -157,7 +166,6 @@ public class ChampRESTAPI { LoggingUtil.initMdcContext(req, headers); long startTimeInMs = System.currentTimeMillis(); logger.info(ChampMsgs.INCOMING_REQUEST, tId, objectId); - ChampObject retrieved; Response response = null; try { ChampTransaction transaction = champDataService.getTransaction(tId); @@ -200,7 +208,8 @@ public class ChampRESTAPI { ChampObject champObject = mapper.readValue(champObj, ChampObject.class); ChampObject created = champDataService.storeObject(champObject, Optional.ofNullable(transaction)); - response = Response.status(Status.CREATED).entity(mapper.writeValueAsString(created)).build(); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampObject(created)); + response = Response.status(Status.CREATED).entity(mapper.writeValueAsString(created)).tag(eTag).build(); } catch (IOException e) { response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build(); } catch (ChampServiceException ce) { @@ -239,8 +248,8 @@ public class ChampRESTAPI { ChampObject co = mapper.readValue(champObj, ChampObject.class); // check if key is present or if it equals the key that is in the URI ChampObject updated = champDataService.replaceObject(co, objectId, Optional.ofNullable(transaction)); - - response = Response.status(Status.OK).entity(mapper.writeValueAsString(updated)).build(); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampObject(updated)); + response = Response.status(Status.OK).entity(mapper.writeValueAsString(updated)).tag(eTag).build(); } catch (IOException e) { response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build(); } catch (ChampServiceException ce) { @@ -265,13 +274,12 @@ public class ChampRESTAPI { LoggingUtil.initMdcContext(req, headers); long startTimeInMs = System.currentTimeMillis(); List retrieved; - Optional rObject; Response response = null; ChampTransaction transaction = null; try { - retrieved = champDataService.getRelationshipsByObject(oId, Optional.ofNullable(transaction)); - response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).build(); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationships(retrieved)); + response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).tag(eTag).build(); } catch (JsonProcessingException e) { response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build(); } catch (ChampServiceException ce) { @@ -294,7 +302,7 @@ public class ChampRESTAPI { LoggingUtil.initMdcContext(req, headers); long startTimeInMs = System.currentTimeMillis(); String propertiesKey = ChampProperties.get(ChampServiceConstants.CHAMP_COLLECTION_PROPERTIES_KEY); - List objects; + List champObjects; Map filter = new HashMap<>(); for (Map.Entry> e : uriInfo.getQueryParameters().entrySet()) { @@ -312,8 +320,9 @@ public class ChampRESTAPI { Response response = null; try { - objects = champDataService.queryObjects(filter, properties); - response = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).entity(mapper.writeValueAsString(objects)) + champObjects = champDataService.queryObjects(filter, properties); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampObjects(champObjects)); + response = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).tag(eTag).entity(mapper.writeValueAsString(champObjects)) .build(); } catch (JsonProcessingException e) { e.printStackTrace(); @@ -351,7 +360,8 @@ public class ChampRESTAPI { response = Response.status(Status.NOT_FOUND).entity(rId + " not found").build(); return response; } - response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).build(); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationship(retrieved)); + response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).tag(eTag).build(); } catch (IOException e) { response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build(); @@ -385,8 +395,8 @@ public class ChampRESTAPI { ChampRelationship r = mapper.readValue(relationship, ChampRelationship.class); ChampRelationship created = champDataService.storeRelationship(r, Optional.ofNullable(transaction)); - - response = Response.status(Status.CREATED).entity(mapper.writeValueAsString(created)).build(); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationship(created)); + response = Response.status(Status.CREATED).entity(mapper.writeValueAsString(created)).tag(eTag).build(); } catch (IOException e) { response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build(); } catch (ChampServiceException ce) { @@ -423,8 +433,8 @@ public class ChampRESTAPI { } ChampRelationship r = mapper.readValue(relationship, ChampRelationship.class); ChampRelationship updated = champDataService.updateRelationship(r, rId, Optional.ofNullable(transaction)); - - response = Response.status(Status.OK).entity(mapper.writeValueAsString(updated)).build(); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationship(updated)); + response = Response.status(Status.OK).entity(mapper.writeValueAsString(updated)).tag(eTag).build(); } catch (IOException e) { response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build(); } catch (ChampServiceException ce) { @@ -480,7 +490,7 @@ public class ChampRESTAPI { @Context HttpServletRequest req) { LoggingUtil.initMdcContext(req, headers); long startTimeInMs = System.currentTimeMillis(); - List list; + List champRelationshipList; Map filter = new HashMap<>(); for (Map.Entry> e : uriInfo.getQueryParameters().entrySet()) { if (!reservedKeyMatcher ( QUERY_OBJECT_ID_URL_MATCH, e.getKey () )) { @@ -489,8 +499,9 @@ public class ChampRESTAPI { } Response response = null; try { - list = champDataService.queryRelationships(filter); - response = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).entity(mapper.writeValueAsString(list)) + champRelationshipList = champDataService.queryRelationships(filter); + EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationships(champRelationshipList)); + response = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).tag(eTag).entity(mapper.writeValueAsString(champRelationshipList)) .build(); } catch (JsonProcessingException e) { e.printStackTrace(); @@ -595,7 +606,6 @@ public class ChampRESTAPI { } return response; } - private boolean reservedKeyMatcher(Pattern p, String key) { Matcher m = p.matcher ( key ); if (m.matches()) { diff --git a/champ-service/src/main/java/org/onap/champ/util/HashGenerator.java b/champ-service/src/main/java/org/onap/champ/util/HashGenerator.java new file mode 100644 index 0000000..d2d154b --- /dev/null +++ b/champ-service/src/main/java/org/onap/champ/util/HashGenerator.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + */ +package org.onap.champ.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +/** + * Generates a sha 256 hash + */ +public class HashGenerator { + private MessageDigest messageDigest; + + public HashGenerator() throws NoSuchAlgorithmException { + this.messageDigest = MessageDigest.getInstance("SHA-256"); + } + + /** + * Generates a SHA 256 hash as a hexadecimal string for the inputs. + * Calls toString on the input objects to convert into a byte stream. + * @param values + * @return SHA 256 hash of the inputs as a hexadecimal string. + * @throws IOException + */ + public String generateSHA256AsHex(Object... values) throws IOException { + byte[] bytes = convertToBytes(values); + byte[] digest = messageDigest.digest(bytes); + StringBuilder result = new StringBuilder(); + for (byte byt : digest) result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1)); + return result.toString(); + } + + private byte[] convertToBytes(Object... values) throws IOException { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = new ObjectOutputStream(bos)) { + for (Object object : values) { + out.writeObject(object.toString()); + } + return bos.toByteArray(); + } + } +} \ No newline at end of file diff --git a/champ-service/src/main/java/org/onap/champ/util/etag/EtagGenerator.java b/champ-service/src/main/java/org/onap/champ/util/etag/EtagGenerator.java new file mode 100644 index 0000000..ac5474e --- /dev/null +++ b/champ-service/src/main/java/org/onap/champ/util/etag/EtagGenerator.java @@ -0,0 +1,107 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + */ +package org.onap.champ.util.etag; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.onap.aai.champcore.model.ChampObject; +import org.onap.aai.champcore.model.ChampRelationship; +import org.onap.champ.util.HashGenerator; +/** + * Computes etag for ChampObjects and ChampRelationships + * + */ +public class EtagGenerator { + + private static final String AAI_LAST_MOD_TS = "aai-last-mod-ts"; + private final HashGenerator hashGenerator; + + public EtagGenerator() throws NoSuchAlgorithmException { + this.hashGenerator = new HashGenerator(); + } + + /** + * Takes in the ChampObject for which the hash is to be computed. + * @param champObject + * @return hash for the ChampObject + * @throws IOException + */ + public String computeHashForChampObject(ChampObject champObject) throws IOException { + return hashGenerator.generateSHA256AsHex(champObject.getKey().orElse(""), champObject.getType(), filterAndSortProperties(champObject.getProperties())); + } + + /** + * Takes in the ChampRelationship for which the hash is to be computed. + * @param ChampRelationship + * @return hash for the ChampRelationship + * @throws IOException + */ + public String computeHashForChampRelationship(ChampRelationship champRelationship) throws IOException { + return hashGenerator.generateSHA256AsHex(champRelationship.getKey().orElse(""), champRelationship.getType(), filterAndSortProperties(champRelationship.getProperties()), computeHashForChampObject(champRelationship.getSource()), computeHashForChampObject(champRelationship.getTarget())); + } + + /** + * Takes in the list of ChampObjects for which the hash is to be computed.
+ * Computes the individual hash, adds them to a List.
+ * Note that the order of items in the list affects the hash. + * @param champObjects + * @return hash for the list of ChampObjects + * @throws IOException + */ + public String computeHashForChampObjects(List champObjects) throws IOException { + List champObjectHashList = new ArrayList<>(); + for(ChampObject champObject : champObjects) { + champObjectHashList.add(computeHashForChampObject(champObject)); + } + return hashGenerator.generateSHA256AsHex(champObjectHashList); + } + + /** + * Takes in the list of ChampRelationships for which the hash is to be computed.
+ * Computes the individual hash, adds them to a List.
+ * Note that the order of items in the list affects the hash. + * @param champRelationships + * @return hash for the list of ChampRelationships + * @throws IOException + */ + public String computeHashForChampRelationships(List champRelationships) throws IOException { + List champRelationshipHashList = new ArrayList<>(); + for(ChampRelationship champRelationship : champRelationships) { + champRelationshipHashList.add(computeHashForChampRelationship(champRelationship)); + } + return hashGenerator.generateSHA256AsHex(champRelationshipHashList); + } + + private Map filterAndSortProperties(Map properties) { + return properties + .entrySet() + .stream() + .filter(x -> !x.getKey().equals(AAI_LAST_MOD_TS)) + .sorted((x, y) -> x.getKey().compareTo(y.getKey())) + .collect(LinkedHashMap::new, + (m, e) -> m.put(e.getKey(), e.getValue()), + Map::putAll); + } +} diff --git a/champ-service/src/test/java/org/onap/champ/util/etag/TestEtagGenerator.java b/champ-service/src/test/java/org/onap/champ/util/etag/TestEtagGenerator.java new file mode 100644 index 0000000..aaa8cb0 --- /dev/null +++ b/champ-service/src/test/java/org/onap/champ/util/etag/TestEtagGenerator.java @@ -0,0 +1,352 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2018 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + */ +package org.onap.champ.util.etag; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.onap.aai.champcore.model.ChampObject; +import org.onap.aai.champcore.model.ChampRelationship; + +public class TestEtagGenerator { + + private EtagGenerator etagGenerator; + + @Before + public void init() throws NoSuchAlgorithmException { + etagGenerator = new EtagGenerator(); + } + + @Test + public void computeHashForIdenticalChampRelationshipObjects() throws Exception { + // everything is same + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship11), is(etagGenerator.computeHashForChampRelationship(champRelationship12))); + } + + @Test + public void computeHashForIdenticalChampRelationshipObjects1() throws Exception { + // everything is same + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship11), is(etagGenerator.computeHashForChampRelationship(champRelationship12))); + } + + @Test + public void computeHashForIdenticalChampRelationshipObjects2() throws Exception { + // everything is same + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship11), is(etagGenerator.computeHashForChampRelationship(champRelationship12))); + } + + + @Test + public void computeHashForChampRelationshipObjectsWithDifferentKey() throws Exception { + // key is different + ChampObject sourceChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship21 = new ChampRelationship.Builder(sourceChampObject21, targetChampObject21, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship22 = new ChampRelationship.Builder(sourceChampObject22, targetChampObject22, "tosca.relationships.HostedOn").key("rel1234").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship21), not(etagGenerator.computeHashForChampRelationship(champRelationship22))); + } + + @Test + public void computeHashForChampRelationshipObjectsWithDifferentRelationShip() throws Exception { + // relationship is different + ChampObject sourceChampObject31 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject31 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship31 = new ChampRelationship.Builder(sourceChampObject31, targetChampObject31, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject32 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject32 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship32 = new ChampRelationship.Builder(sourceChampObject32, targetChampObject32, "tosca.relationships.RelatedTo").key("rel123").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship31), not(etagGenerator.computeHashForChampRelationship(champRelationship32))); + } + + @Test + public void computeHashForChampRelationshipObjectsWithDifferentChampObjects() throws Exception { + // source/target different + ChampObject sourceChampObject41 = new ChampObject.Builder("pserver").key("a123456").property("prop1", "value1").build(); + ChampObject targetChampObject41 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship41 = new ChampRelationship.Builder(sourceChampObject41, targetChampObject41, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject42 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject42 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship42 = new ChampRelationship.Builder(sourceChampObject42, targetChampObject42, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship41), not(etagGenerator.computeHashForChampRelationship(champRelationship42))); + } + + @Test + public void computeHashForChampRelationshipObjectsWithDifferentProperties() throws Exception { + // property different + ChampObject sourceChampObject51 = new ChampObject.Builder("pserver").key("a123456").property("prop1", "value1").build(); + ChampObject targetChampObject51 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship51 = new ChampRelationship.Builder(sourceChampObject51, targetChampObject51, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject52 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject52 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship52 = new ChampRelationship.Builder(sourceChampObject52, targetChampObject52, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampRelationship(champRelationship51), not(etagGenerator.computeHashForChampRelationship(champRelationship52))); + } + + @Test + public void testComputeHashForIdenticalChampObjects() throws Exception { + ChampObject champObject1 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject2 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampObject(champObject1), is(etagGenerator.computeHashForChampObject(champObject2))); + } + + @Test + public void testComputeHashForEquivalentChampObjects() throws Exception { + Map properties1 = new HashMap<>(); + properties1.put("prop3", "value3"); + properties1.put("prop1", "value1"); + properties1.put("prop2", "value2"); + properties1.put("aai-last-mod-ts", "1234"); + // note aai-last-mod-ts value is different + Map properties2 = new HashMap<>(); + properties2.put("prop3", "value3"); + properties2.put("prop1", "value1"); + properties2.put("prop2", "value2"); + properties2.put("aai-last-mod-ts", "12345"); + + + ChampObject champObject1 = new ChampObject.Builder("pserver").key("a1234").properties(properties1).build(); + ChampObject champObject2 = new ChampObject.Builder("pserver").key("a1234").properties(properties2).build(); + assertThat(etagGenerator.computeHashForChampObject(champObject1), is(etagGenerator.computeHashForChampObject(champObject2))); + } + + @Test + public void testComputeHashForChampObjectsWithDifferentProperties() throws Exception { + ChampObject champObject3 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject champObject4= new ChampObject.Builder("pserver").key("a12345").property("prop2", "value1").build(); + assertThat(etagGenerator.computeHashForChampObject(champObject3), not(etagGenerator.computeHashForChampObject(champObject4))); + } + + @Test + public void testComputeHashForChampObjectsWithDifferentKey() throws Exception { + ChampObject champObject5 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject champObject6= new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + assertThat(etagGenerator.computeHashForChampObject(champObject5), not(etagGenerator.computeHashForChampObject(champObject6))); + } + + @Test + public void testComputeHashForIdenticalListOfChampObjects() throws Exception { + //List 1 + ChampObject champObject11 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + List champObjects1 = new ArrayList<>(); + champObjects1.add(champObject11); + champObjects1.add(champObject12); + // List 2 + ChampObject champObject21 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + List champObjects2 = new ArrayList<>(); + champObjects2.add(champObject21); + champObjects2.add(champObject22); + + assertThat(etagGenerator.computeHashForChampObjects(champObjects1), is(etagGenerator.computeHashForChampObjects(champObjects2))); + } + + @Test + public void testComputeHashForIdenticalListOfChampObjectsWithDifferentOrder() throws Exception { + //List 1 + ChampObject champObject11 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + List champObjects1 = new ArrayList<>(); + champObjects1.add(champObject11); + champObjects1.add(champObject12); + // List 2 + ChampObject champObject21 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + List champObjects2 = new ArrayList<>(); + // Different order of the items added. + champObjects2.add(champObject22); + champObjects2.add(champObject21); + + assertThat(etagGenerator.computeHashForChampObjects(champObjects1), not(etagGenerator.computeHashForChampObjects(champObjects2))); + } + + @Test + public void testComputeHashForDifferentListOfChampObjects() throws Exception { + //List 1 + ChampObject champObject11 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + List champObjects1 = new ArrayList<>(); + champObjects1.add(champObject11); + champObjects1.add(champObject12); + // List 2 + ChampObject champObject21 = new ChampObject.Builder("pserver").key("a1234").property("prop1", "value1").build(); + ChampObject champObject22 = new ChampObject.Builder("pserver").key("a123456").property("prop2", "value2").build(); + List champObjects2 = new ArrayList<>(); + champObjects2.add(champObject21); + champObjects2.add(champObject22); + + assertThat(etagGenerator.computeHashForChampObjects(champObjects1), not(etagGenerator.computeHashForChampObjects(champObjects2))); + } + + @Test + public void testComputeHashForIdenticalListOfChampRelationships() throws Exception { + //List 1 + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + List champRelationships1 = new ArrayList<>(); + champRelationships1.add(champRelationship11); + champRelationships1.add(champRelationship12); + // List 2 + ChampObject sourceChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship21 = new ChampRelationship.Builder(sourceChampObject21, targetChampObject21, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship22 = new ChampRelationship.Builder(sourceChampObject22, targetChampObject22, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + List champRelationships2 = new ArrayList<>(); + champRelationships2.add(champRelationship21); + champRelationships2.add(champRelationship22); + + assertThat(etagGenerator.computeHashForChampRelationships(champRelationships1), is(etagGenerator.computeHashForChampRelationships(champRelationships2))); + } + + @Test + public void testComputeHashForIdenticalListOfChampRelationships1() throws Exception { + //List 1 + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").build(); + List champRelationships1 = new ArrayList<>(); + champRelationships1.add(champRelationship11); + champRelationships1.add(champRelationship12); + // List 2 + ChampObject sourceChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship21 = new ChampRelationship.Builder(sourceChampObject21, targetChampObject21, "tosca.relationships.HostedOn").key("rel123").build(); + ChampObject sourceChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship22 = new ChampRelationship.Builder(sourceChampObject22, targetChampObject22, "tosca.relationships.HostedOn").key("rel123").build(); + List champRelationships2 = new ArrayList<>(); + champRelationships2.add(champRelationship21); + champRelationships2.add(champRelationship22); + + assertThat(etagGenerator.computeHashForChampRelationships(champRelationships1), is(etagGenerator.computeHashForChampRelationships(champRelationships2))); + } + + @Test + public void testComputeHashForIdenticalListOfChampRelationships2() throws Exception { + //List 1 + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + List champRelationships1 = new ArrayList<>(); + champRelationships1.add(champRelationship11); + champRelationships1.add(champRelationship12); + // List 2 + ChampObject sourceChampObject21 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampObject targetChampObject21 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampRelationship champRelationship21 = new ChampRelationship.Builder(sourceChampObject21, targetChampObject21, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject22 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampObject targetChampObject22 = new ChampObject.Builder("pserver").key("a12345").build(); + ChampRelationship champRelationship22 = new ChampRelationship.Builder(sourceChampObject22, targetChampObject22, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + List champRelationships2 = new ArrayList<>(); + champRelationships2.add(champRelationship21); + champRelationships2.add(champRelationship22); + + assertThat(etagGenerator.computeHashForChampRelationships(champRelationships1), is(etagGenerator.computeHashForChampRelationships(champRelationships2))); + } + + @Test + public void testComputeHashForIdenticalListOfChampRelationshipsWithDifferentOrder() throws Exception { + + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a123456").property("prop1", "value1").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a123456").property("prop2", "value2").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").property("prop2", "value2").build(); + List champRelationships1 = new ArrayList<>(); + // List 1 + champRelationships1.add(champRelationship11); + champRelationships1.add(champRelationship12); + // List 2, elements added in different order + List champRelationships2 = new ArrayList<>(); + champRelationships2.add(champRelationship12); + champRelationships2.add(champRelationship11); + + assertThat(etagGenerator.computeHashForChampRelationships(champRelationships1), not(etagGenerator.computeHashForChampRelationships(champRelationships2))); + } + + @Test + public void testComputeHashForDifferntListOfChampRelationships() throws Exception { + //List 1 + ChampObject sourceChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject11 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship11 = new ChampRelationship.Builder(sourceChampObject11, targetChampObject11, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop1", "value1").build(); + ChampObject targetChampObject12 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship12 = new ChampRelationship.Builder(sourceChampObject12, targetChampObject12, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + List champRelationships1 = new ArrayList<>(); + champRelationships1.add(champRelationship11); + champRelationships1.add(champRelationship12); + // List 2 + ChampObject sourceChampObject21 = new ChampObject.Builder("pserver").key("a123456").property("prop1", "value1").build(); + ChampObject targetChampObject21 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship21 = new ChampRelationship.Builder(sourceChampObject21, targetChampObject21, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + ChampObject sourceChampObject22 = new ChampObject.Builder("pserver").key("a123456").property("prop1", "value1").build(); + ChampObject targetChampObject22 = new ChampObject.Builder("pserver").key("a12345").property("prop2", "value2").build(); + ChampRelationship champRelationship22 = new ChampRelationship.Builder(sourceChampObject22, targetChampObject22, "tosca.relationships.HostedOn").key("rel123").property("prop1", "value1").build(); + List champRelationships2 = new ArrayList<>(); + champRelationships2.add(champRelationship21); + champRelationships2.add(champRelationship22); + + assertThat(etagGenerator.computeHashForChampRelationships(champRelationships1), not(etagGenerator.computeHashForChampRelationships(champRelationships2))); + } + + +} + -- 2.16.6