+
+ public Either<Map<String, PropertyData>, JanusGraphOperationStatus> mergePropertiesAssociatedToNode(NodeTypeEnum nodeType, String uniqueId, Map<String, PropertyDefinition> newProperties) {
+ Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> oldPropertiesRes = findPropertiesOfNode(nodeType, uniqueId);
+
+ Map<String, PropertyDefinition> reallyNewProperties;
+ Map<String, PropertyData> unchangedPropsData;
+
+ if (oldPropertiesRes.isRight()) {
+ JanusGraphOperationStatus err = oldPropertiesRes.right().value();
+ if (err == JanusGraphOperationStatus.NOT_FOUND) {
+ reallyNewProperties = newProperties;
+ unchangedPropsData = Collections.emptyMap();
+ }
+ else {
+ return Either.right(err);
+ }
+ }
+ else {
+ Map<String, PropertyDefinition> oldProperties = oldPropertiesRes.left().value();
+ reallyNewProperties = collectReallyNewProperties(newProperties, oldProperties);
+
+ for(Entry<String, PropertyDefinition> oldEntry: oldProperties.entrySet()) {
+ String key = oldEntry.getKey();
+ PropertyDefinition newPropDef = newProperties != null? newProperties.get(key): null;
+ PropertyDefinition oldPropDef = oldEntry.getValue();
+
+ JanusGraphOperationStatus status = updateOldProperty(newPropDef, oldPropDef);
+ if (status != JanusGraphOperationStatus.OK) {
+ return Either.right(status);
+ }
+ }
+ unchangedPropsData = oldProperties.entrySet().stream()
+ .collect(Collectors.toMap(Entry::getKey, e-> new PropertyData(e.getValue(), null)));
+ }
+
+
+ // add other properties
+ return addPropertiesToElementType(nodeType, uniqueId, reallyNewProperties, unchangedPropsData);
+ }
+
+ /**
+ * @param newProperties
+ * @param oldProperties
+ * @return
+ */
+ private Map<String, PropertyDefinition> collectReallyNewProperties(Map<String, PropertyDefinition> newProperties, Map<String, PropertyDefinition> oldProperties) {
+ return newProperties != null? newProperties.entrySet().stream()
+ .filter(entry -> !oldProperties.containsKey(entry.getKey()))
+ .collect(Collectors.toMap(Entry::getKey, Entry::getValue) ): null;
+ }
+
+ /**
+ * @param newPropDef
+ * @param oldPropDef
+ */
+ private JanusGraphOperationStatus updateOldProperty(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
+ if (!isUpdateAllowed(newPropDef, oldPropDef)) {
+ return JanusGraphOperationStatus.MATCH_NOT_FOUND;
+ }
+
+ if (isUpdateRequired(newPropDef, oldPropDef)) {
+ modifyOldPropByNewOne(newPropDef, oldPropDef);
+
+ List<PropertyConstraint> constraints = oldPropDef.getConstraints();
+ PropertyData node = new PropertyData(oldPropDef, convertConstraintsToString(constraints));
+ Either<PropertyData, JanusGraphOperationStatus> updateResult = janusGraphGenericDao
+ .updateNode(node, PropertyData.class);
+
+ if (updateResult.isRight()) {
+ return updateResult.right().value();
+ }
+ }
+
+ return JanusGraphOperationStatus.OK;
+ }
+
+ /**
+ * @param newPropDef
+ * @param oldPropDef
+ */
+ private boolean isUpdateAllowed(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
+ if (newPropDef == null) {
+ log.error("#mergePropertiesAssociatedToNode - Failed due attempt to delete the property with id {}", oldPropDef.getUniqueId());
+ return false;
+ }
+
+ // If the property type is missing it's something that we could want to fix
+ if ( oldPropDef.getType() != null && !oldPropDef.getType().equals(newPropDef.getType())) {
+ log.error("#mergePropertiesAssociatedToNode - Failed due attempt to change type of the property with id {}", oldPropDef.getUniqueId());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Update only fields which modification is permitted.
+ * @param newPropDef
+ * @param oldPropDef
+ */
+ private void modifyOldPropByNewOne(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
+ oldPropDef.setDefaultValue(newPropDef.getDefaultValue());
+ oldPropDef.setDescription(newPropDef.getDescription());
+ oldPropDef.setRequired(newPropDef.isRequired());
+
+ // Type is updated to fix possible null type issue in janusGraph DB
+ oldPropDef.setType(newPropDef.getType());
+ }
+
+
+ private boolean isUpdateRequired(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
+ return !StringUtils.equals(oldPropDef.getDefaultValue(), newPropDef.getDefaultValue()) ||
+ !StringUtils.equals(oldPropDef.getDescription(), newPropDef.getDescription()) ||
+ oldPropDef.isRequired() != newPropDef.isRequired();
+ }
+
+ /**
+ * Adds newProperties and returns in case of success (left part of Either)
+ * map of all properties i. e. added ones and contained in unchangedPropsData
+ * @param nodeType
+ * @param uniqueId
+ * @param newProperties
+ * @param unchangedPropsData
+ * @return
+ */
+ private Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToElementType(NodeTypeEnum nodeType, String uniqueId, Map<String, PropertyDefinition> newProperties, Map<String, PropertyData> unchangedPropsData) {
+ return addPropertiesToElementType(uniqueId, nodeType, newProperties)
+ .left()
+ .map(m -> {
+ m.putAll(unchangedPropsData);
+ return m;
+ });
+ }
+