diff options
author | andre.schmid <andre.schmid@est.tech> | 2021-08-31 10:04:26 +0100 |
---|---|---|
committer | andre.schmid <andre.schmid@est.tech> | 2021-08-31 10:20:06 +0100 |
commit | 38c6faa738abe6e0acdd24df2364d725d36fca40 (patch) | |
tree | 3412c0ee222d60bbc98a997390dbae19d55e3fc9 | |
parent | a1cdcda28701f603cf95f591ba447bd723273622 (diff) |
Fix additional types import file generation
The additional_type_definitions.yaml is missing types entries and
header.
Also, the solution is not considering the types entries, i.e.
'data_types:', 'policy_types:', etc., when replacing types and
generating the new file, which renders the solution broken.
This change aims to fix the related problems.
Change-Id: I412683b49966c09dd067ecbf8a1d778155b23fa6
Issue-ID: SDC-3703
Signed-off-by: andre.schmid <andre.schmid@est.tech>
22 files changed, 331 insertions, 86 deletions
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java index c0188d79c3..48b9c58d93 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java @@ -25,10 +25,10 @@ import java.util.Map; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum; import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.model.CapabilityTypeDefinition; import org.openecomp.sdc.be.model.Model; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation; import org.openecomp.sdc.be.model.operations.impl.ModelOperation; @@ -63,7 +63,7 @@ public class CapabilityTypeImportManager { capabilityTypesYml, capabilityTypesFromYml -> createCapabilityTypesFromYml(capabilityTypesYml, modelName), this::upsertCapabilityTypesByDao, ElementTypeEnum.CAPABILITY_TYPE); if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) { - commonImportManager.addTypesToDefaultImports(capabilityTypesYml, modelName); + commonImportManager.addTypesToDefaultImports(ElementTypeEnum.CAPABILITY_TYPE, capabilityTypesYml, modelName); } return elementTypes; } @@ -86,7 +86,7 @@ public class CapabilityTypeImportManager { private Either<List<ImmutablePair<CapabilityTypeDefinition, Boolean>>, ResponseFormat> upsertCapabilityTypesByDao( List<CapabilityTypeDefinition> capabilityTypesToCreate) { return commonImportManager.createElementTypesByDao(capabilityTypesToCreate, capabilityType -> Either.left(ActionStatus.OK), - capabilityType -> new ImmutablePair<>(CommonImportManager.ElementTypeEnum.CAPABILITY_TYPE, + capabilityType -> new ImmutablePair<>(ElementTypeEnum.CAPABILITY_TYPE, UniqueIdBuilder.buildCapabilityTypeUid(capabilityType.getModel(), capabilityType.getType())), capabilityTypeOperation::getCapabilityType, capabilityTypeOperation::addCapabilityType, this::updateCapabilityType); } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java index c0da9eb17b..f1582eecb1 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java @@ -48,6 +48,7 @@ import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.GroupTypeDefinition; import org.openecomp.sdc.be.model.PolicyTypeDefinition; import org.openecomp.sdc.be.model.PropertyDefinition; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.normatives.ToscaTypeMetadata; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.api.TypeOperations; @@ -534,12 +535,8 @@ public class CommonImportManager { } - public void addTypesToDefaultImports(final String typesYaml, final String modelName) { - modelOperation.addTypesToDefaultImports(typesYaml, modelName); - } - - public enum ElementTypeEnum { - POLICY_TYPE, GROUP_TYPE, DATA_TYPE, CAPABILITY_TYPE, INTERFACE_LIFECYCLE_TYPE, RELATIONSHIP_TYPE + public void addTypesToDefaultImports(final ElementTypeEnum elementTypeEnum, final String typesYaml, final String modelName) { + modelOperation.addTypesToDefaultImports(elementTypeEnum, typesYaml, modelName); } public interface ICreateElementType<T1, T2, T3> { diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java index b24d81bcbc..cad0f28477 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java @@ -30,13 +30,13 @@ import java.util.stream.Collectors; import javax.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum; import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.PropertyDefinition; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.ModelOperation; import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; @@ -66,7 +66,7 @@ public class DataTypeImportManager { dataTypeYml, dataTypesFromYml -> createDataTypesFromYml(dataTypeYml, modelName), this::createDataTypesByDao, ElementTypeEnum.DATA_TYPE); if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) { - commonImportManager.addTypesToDefaultImports(dataTypeYml, modelName); + commonImportManager.addTypesToDefaultImports(ElementTypeEnum.DATA_TYPE, dataTypeYml, modelName); } return elementTypes; } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java index e043bc2619..8ff7d994e2 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java @@ -27,7 +27,6 @@ import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum; import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData; import org.openecomp.sdc.be.config.BeEcompErrorManager; import org.openecomp.sdc.be.dao.api.ActionStatus; @@ -39,6 +38,7 @@ import org.openecomp.sdc.be.model.GroupTypeDefinition; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.PropertyDefinition; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation; import org.openecomp.sdc.be.model.operations.impl.ModelOperation; @@ -74,7 +74,7 @@ public class GroupTypeImportManager { final Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat> elementTypes = commonImportManager.createElementTypes( toscaTypeImportData, this::createGroupTypesFromYml, this::upsertGroupTypesByDao, modelName); if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) { - commonImportManager.addTypesToDefaultImports(toscaTypeImportData.getToscaTypesYml(), modelName); + commonImportManager.addTypesToDefaultImports(ElementTypeEnum.GROUP_TYPE, toscaTypeImportData.getToscaTypesYml(), modelName); } return elementTypes; } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java index 5cc8985b90..b757faf491 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java @@ -39,6 +39,7 @@ import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.model.InterfaceDefinition; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.Operation; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.ModelOperation; @@ -72,7 +73,7 @@ public class InterfaceLifecycleTypeImportManager { } final Either<List<InterfaceDefinition>, ResponseFormat> elementTypes = createInterfacesByDao(interfaces.left().value()); if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) { - commonImportManager.addTypesToDefaultImports(interfaceLifecycleTypesYml, modelName); + commonImportManager.addTypesToDefaultImports(ElementTypeEnum.INTERFACE_LIFECYCLE_TYPE, interfaceLifecycleTypesYml, modelName); } return elementTypes; } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java index 54d57800be..be34cce3e4 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java @@ -26,7 +26,6 @@ import java.util.Optional; import java.util.function.Consumer; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum; import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData; import org.openecomp.sdc.be.components.impl.utils.PolicyTypeImportUtils; import org.openecomp.sdc.be.dao.api.ActionStatus; @@ -36,6 +35,7 @@ import org.openecomp.sdc.be.model.GroupTypeDefinition; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.PolicyTypeDefinition; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.IPolicyTypeOperation; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.GroupOperation; @@ -75,7 +75,7 @@ public class PolicyTypeImportManager { final Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat> elementTypes = commonImportManager.createElementTypes( toscaTypeImportData, this::createPolicyTypesFromYml, this::upsertPolicyTypesByDao, modelName); if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) { - commonImportManager.addTypesToDefaultImports(toscaTypeImportData.getToscaTypesYml(), modelName); + commonImportManager.addTypesToDefaultImports(ElementTypeEnum.POLICY_TYPE, toscaTypeImportData.getToscaTypesYml(), modelName); } return elementTypes; } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RelationshipTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RelationshipTypeImportManager.java index 564eb3a794..1bd19143c6 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RelationshipTypeImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RelationshipTypeImportManager.java @@ -21,11 +21,11 @@ import java.util.Map; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; -import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum; import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.RelationshipTypeDefinition; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter; import org.openecomp.sdc.be.model.operations.impl.ModelOperation; import org.openecomp.sdc.be.model.operations.impl.RelationshipTypeOperation; @@ -67,7 +67,7 @@ public class RelationshipTypeImportManager { relationshipTypesToCreate -> createRelationshipTypesByDao(relationshipTypesToCreate, inTransaction), ElementTypeEnum.RELATIONSHIP_TYPE); if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) { - commonImportManager.addTypesToDefaultImports(relationshipTypeYml, modelName); + commonImportManager.addTypesToDefaultImports(ElementTypeEnum.RELATIONSHIP_TYPE, relationshipTypeYml, modelName); } return elementTypes; } diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CommonImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CommonImportManagerTest.java index b360a94862..ce3e12407f 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CommonImportManagerTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CommonImportManagerTest.java @@ -38,11 +38,11 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum; import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.model.CapabilityTypeDefinition; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.ModelOperation; import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java index 5d1501c0b5..c250aecdef 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java @@ -84,7 +84,14 @@ public class ToscaModelImportCassandraDao extends CassandraDao { LOGGER.info("{} successfully initialized", ToscaModelImportCassandraDao.class.getName()); } - public void importAll(final String modelId, final List<ToscaImportByModel> toscaImportByModelList) { + /** + * Completely replaces the previous model imports by the imports on the given list that are from the same model. + * New imports will be added, existing will be replaced and the remaining will be deleted. + * + * @param modelId the model id + * @param toscaImportByModelList the new list of imports + */ + public void replaceImports(final String modelId, final List<ToscaImportByModel> toscaImportByModelList) { final List<ToscaImportByModel> importOfModelList = toscaImportByModelList.stream() .filter(toscaImportByModel -> modelId.equals(toscaImportByModel.getModelId())) .collect(Collectors.toList()); @@ -99,7 +106,13 @@ public class ToscaModelImportCassandraDao extends CassandraDao { ); } - public void importOnly(final String modelId, final List<ToscaImportByModel> toscaImportByModelList) { + /** + * Saves all imports provided on the list that are from the given modelId. + * + * @param modelId the model id + * @param toscaImportByModelList the list of imports to save + */ + public void saveAll(final String modelId, final List<ToscaImportByModel> toscaImportByModelList) { toscaImportByModelList.stream() .filter(toscaImportByModel -> modelId.equals(toscaImportByModel.getModelId())) .forEach(toscaImportByModelMapper::save); diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java index cddf3a2708..a43e153f7b 100644 --- a/catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java +++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java @@ -95,7 +95,7 @@ class ToscaModelImportCassandraDaoTest { when(findAllByModelResult.all()).thenReturn(List.of(toscaImportByModel1, toscaImportByModelDatabase1)); when(toscaImportByModelAccessorMock.findAllByModel(modelId)).thenReturn(findAllByModelResult); - toscaModelImportCassandraDao.importAll(modelId, importModelList); + toscaModelImportCassandraDao.replaceImports(modelId, importModelList); verify(toscaImportByModelMapperMock).save(toscaImportByModel1); verify(toscaImportByModelMapperMock).save(toscaImportByModel2); diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/normatives/ElementTypeEnum.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/normatives/ElementTypeEnum.java new file mode 100644 index 0000000000..57ca559615 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/normatives/ElementTypeEnum.java @@ -0,0 +1,38 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.model.normatives; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum ElementTypeEnum { + POLICY_TYPE("policy_types"), + GROUP_TYPE("group_types"), + DATA_TYPE("data_types"), + CAPABILITY_TYPE("capability_types"), + INTERFACE_LIFECYCLE_TYPE("interface_types"), + RELATIONSHIP_TYPE("relationship_types"); + + private final String toscaEntryName; +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java index 87ab3fc79d..967ffdc711 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java @@ -20,15 +20,15 @@ package org.openecomp.sdc.be.model.operations.impl; import fj.data.Either; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; @@ -51,6 +51,7 @@ import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.resources.data.ModelData; @@ -64,7 +65,7 @@ import org.yaml.snakeyaml.Yaml; public class ModelOperation { private static final Logger log = Logger.getLogger(ModelOperation.class); - private static final String ADDITIONAL_TYPE_DEFINITIONS = "additional_type_definitions.yml"; + static final Path ADDITIONAL_TYPE_DEFINITIONS_PATH = Path.of("additional_type_definitions.yaml"); private final JanusGraphGenericDao janusGraphGenericDao; private final JanusGraphDao janusGraphDao; @@ -173,7 +174,7 @@ public class ModelOperation { toscaImportByModel.setContent(content); return toscaImportByModel; }).collect(Collectors.toList()); - toscaModelImportCassandraDao.importAll(modelId, toscaImportByModelList); + toscaModelImportCassandraDao.replaceImports(modelId, toscaImportByModelList); } /** @@ -257,75 +258,61 @@ public class ModelOperation { } } - public void addTypesToDefaultImports(final String typesYaml, final String modelName) { - final List<ToscaImportByModel> allSchemaImportsByModel = toscaModelImportCassandraDao.findAllByModel(modelName); - final Optional<ToscaImportByModel> additionalTypeDefinitionsOptional = allSchemaImportsByModel.stream() - .filter(t -> ADDITIONAL_TYPE_DEFINITIONS.equals(t.getFullPath())).findAny(); - final ToscaImportByModel toscaImportByModelAdditionalTypeDefinitions; - final List<ToscaImportByModel> schemaImportsByModel; - if (additionalTypeDefinitionsOptional.isPresent()) { - toscaImportByModelAdditionalTypeDefinitions = additionalTypeDefinitionsOptional.get(); - schemaImportsByModel = allSchemaImportsByModel.stream() - .filter(toscaImportByModel -> !ADDITIONAL_TYPE_DEFINITIONS.equals(toscaImportByModel.getFullPath())) + public void addTypesToDefaultImports(final ElementTypeEnum elementTypeEnum, final String typesYaml, final String modelName) { + final List<ToscaImportByModel> modelImportList = toscaModelImportCassandraDao.findAllByModel(modelName); + final Optional<ToscaImportByModel> additionalTypeDefinitionsImportOptional = modelImportList.stream() + .filter(t -> ADDITIONAL_TYPE_DEFINITIONS_PATH.equals(Path.of(t.getFullPath()))).findAny(); + final ToscaImportByModel additionalTypeDefinitionsImport; + final List<ToscaImportByModel> rebuiltModelImportList; + if (additionalTypeDefinitionsImportOptional.isPresent()) { + additionalTypeDefinitionsImport = additionalTypeDefinitionsImportOptional.get(); + rebuiltModelImportList = modelImportList.stream() + .filter(toscaImportByModel -> !ADDITIONAL_TYPE_DEFINITIONS_PATH.equals(Path.of(toscaImportByModel.getFullPath()))) .collect(Collectors.toList()); } else { - toscaImportByModelAdditionalTypeDefinitions = new ToscaImportByModel(); - toscaImportByModelAdditionalTypeDefinitions.setModelId(modelName); - toscaImportByModelAdditionalTypeDefinitions.setFullPath(ADDITIONAL_TYPE_DEFINITIONS); - toscaImportByModelAdditionalTypeDefinitions.setContent(typesYaml); - schemaImportsByModel = new ArrayList<>(allSchemaImportsByModel); + additionalTypeDefinitionsImport = new ToscaImportByModel(); + additionalTypeDefinitionsImport.setModelId(modelName); + additionalTypeDefinitionsImport.setFullPath(ADDITIONAL_TYPE_DEFINITIONS_PATH.toString()); + additionalTypeDefinitionsImport.setContent(createAdditionalTypeDefinitionsHeader()); + rebuiltModelImportList = new ArrayList<>(modelImportList); } - final List<ToscaImportByModel> toscaImportByModels = removeExistingDefaultImports(typesYaml, schemaImportsByModel); + final Map<String, Object> typesYamlMap = new Yaml().load(typesYaml); + removeExistingTypesFromDefaultImports(elementTypeEnum, typesYamlMap, rebuiltModelImportList); - final Map<String, Object> originalContent = (Map<String, Object>) new Yaml().load(toscaImportByModelAdditionalTypeDefinitions.getContent()); - toscaImportByModelAdditionalTypeDefinitions.setContent(buildAdditionalTypeDefinitionsContent(typesYaml, originalContent).toString()); - toscaImportByModels.add(toscaImportByModelAdditionalTypeDefinitions); + final Map<String, Object> originalContent = new Yaml().load(additionalTypeDefinitionsImport.getContent()); + additionalTypeDefinitionsImport.setContent(buildAdditionalTypeDefinitionsContent(elementTypeEnum, typesYamlMap, originalContent)); + rebuiltModelImportList.add(additionalTypeDefinitionsImport); - toscaModelImportCassandraDao.importOnly(modelName, toscaImportByModels); + toscaModelImportCassandraDao.saveAll(modelName, rebuiltModelImportList); } - private List<ToscaImportByModel> removeExistingDefaultImports(final String typesYaml, final List<ToscaImportByModel> schemaImportsByModel) { - final List<ToscaImportByModel> toscaImportByModels = new ArrayList<>(); - schemaImportsByModel.forEach(toscaImportByModel -> { - final ToscaImportByModel toscaImportByModelNew = new ToscaImportByModel(); - toscaImportByModelNew.setModelId(toscaImportByModel.getModelId()); - toscaImportByModelNew.setFullPath(toscaImportByModel.getFullPath()); - - final Map<String, Object> existingImportYamlMap = (Map<String, Object>) new Yaml().load(toscaImportByModel.getContent()); - - ((Map<String, Object>) new Yaml().load(typesYaml)).keySet().forEach(existingImportYamlMap::remove); - - final StringBuilder stringBuilder = new StringBuilder(); - existingImportYamlMap.forEach((key, value) -> { - final Map<Object, Object> hashMap = new HashMap<>(); - hashMap.put(key, value); - stringBuilder.append("\n").append(new YamlUtil().objectToYaml(hashMap)); - }); - - toscaImportByModelNew.setContent(stringBuilder.toString()); - toscaImportByModels.add(toscaImportByModelNew); + private void removeExistingTypesFromDefaultImports(final ElementTypeEnum elementTypeEnum, final Map<String, Object> typesYaml, + final List<ToscaImportByModel> defaultImportList) { + defaultImportList.forEach(toscaImportByModel -> { + final Map<String, Object> existingImportYamlMap = new Yaml().load(toscaImportByModel.getContent()); + final Map<String, Object> currentTypeYamlMap = (Map<String, Object>) existingImportYamlMap.get(elementTypeEnum.getToscaEntryName()); + if (MapUtils.isNotEmpty(currentTypeYamlMap)) { + typesYaml.keySet().forEach(currentTypeYamlMap::remove); + } + toscaImportByModel.setContent(new YamlUtil().objectToYaml(existingImportYamlMap)); }); - return toscaImportByModels; } - private StringBuilder buildAdditionalTypeDefinitionsContent(final String typesYaml, final Map<String, Object> originalContent) { - final var stringBuilder = new StringBuilder(); - - final Map<String, Object> typesYamlMap = (Map<String, Object>) new Yaml().load(typesYaml); - final Set<String> typeYmlKeySet = typesYamlMap.keySet(); + private String buildAdditionalTypeDefinitionsContent(final ElementTypeEnum elementTypeEnum, final Map<String, Object> typesYamlMap, + final Map<String, Object> originalContent) { + final Map<String, Object> originalTypeContent = (Map<String, Object>) originalContent.get(elementTypeEnum.getToscaEntryName()); + if (MapUtils.isEmpty(originalTypeContent)) { + originalContent.put(elementTypeEnum.getToscaEntryName(), new LinkedHashMap<>(typesYamlMap)); + } else { + originalTypeContent.putAll(typesYamlMap); + } + return new YamlUtil().objectToYaml(originalContent); + } - originalContent.forEach((key, value) -> { - final Map<Object, Object> hashMap = new HashMap<>(); - if (typeYmlKeySet.contains(key)) { - hashMap.put(key, typesYamlMap.get(key)); - } else { - hashMap.put(key, value); - } - final String newContent = new YamlUtil().objectToYaml(hashMap); - stringBuilder.append("\n").append(newContent); - }); - return stringBuilder; + private String createAdditionalTypeDefinitionsHeader() { + return "tosca_definitions_version: tosca_simple_yaml_1_3" + "\n" + + "description: Auto-generated file that contains package custom types or types added after system installation." + "\n"; } } diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java index e7c5ddba16..a27177ac21 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java @@ -21,6 +21,7 @@ package org.openecomp.sdc.be.model.operations.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -32,9 +33,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.openecomp.sdc.be.model.operations.impl.ModelOperation.ADDITIONAL_TYPE_DEFINITIONS_PATH; import fj.data.Either; +import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -68,6 +73,7 @@ import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.ModelTestBase; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; +import org.openecomp.sdc.be.model.normatives.ElementTypeEnum; import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation; import org.openecomp.sdc.be.resources.data.ModelData; import org.springframework.test.context.ContextConfiguration; @@ -162,16 +168,16 @@ class ModelOperationTest extends ModelTestBase { toscaImport2.setFullPath(pathEntry2); final List<ToscaImportByModel> toscaImportByModelList = List.of(toscaImport1, toscaImport2); - verify(toscaModelImportCassandraDao).importAll(modelId, toscaImportByModelList); + verify(toscaModelImportCassandraDao).replaceImports(modelId, toscaImportByModelList); } @Test void createModelImportsTest_emptyZipContent() { var modelId = "modelId"; modelOperation.createModelImports(modelId, Collections.emptyMap()); - verify(toscaModelImportCassandraDao, never()).importAll(eq(modelId), anyList()); + verify(toscaModelImportCassandraDao, never()).replaceImports(eq(modelId), anyList()); modelOperation.createModelImports(modelId, null); - verify(toscaModelImportCassandraDao, never()).importAll(eq(null), anyList()); + verify(toscaModelImportCassandraDao, never()).replaceImports(eq(null), anyList()); } @Test @@ -335,6 +341,123 @@ class ModelOperationTest extends ModelTestBase { .forEach(toscaImportByModel -> assertTrue(actualModelImportList.contains(toscaImportByModel))); } + @Test + void addTypesToDefaultImportsTest_nonExistingAdditionalTypesImport() throws IOException { + var modelName = "model"; + final Path testResourcePath = Path.of("src/test/resources/modelOperation"); + + final var dataTypesPath = testResourcePath.resolve(Path.of("input-data_types.yaml")); + final var dataTypes = Files.readString(dataTypesPath); + + final Path import1RelativePath = Path.of("original-import-1.yaml"); + final Path import1Path = testResourcePath.resolve(import1RelativePath); + final Path import2RelativePath = Path.of("original-import-2.yaml"); + final Path import2Path = testResourcePath.resolve(import2RelativePath); + + var toscaImportByModel1 = new ToscaImportByModel(); + toscaImportByModel1.setModelId(modelName); + toscaImportByModel1.setFullPath(import1RelativePath.toString()); + toscaImportByModel1.setContent(Files.readString(import1Path)); + + var toscaImportByModel2 = new ToscaImportByModel(); + toscaImportByModel2.setModelId(modelName); + toscaImportByModel2.setFullPath(import2RelativePath.toString()); + toscaImportByModel2.setContent(Files.readString(import2Path)); + + final List<ToscaImportByModel> modelImports = new ArrayList<>(); + modelImports.add(toscaImportByModel1); + modelImports.add(toscaImportByModel2); + when(toscaModelImportCassandraDao.findAllByModel(modelName)).thenReturn(modelImports); + + modelOperation.addTypesToDefaultImports(ElementTypeEnum.DATA_TYPE, dataTypes, modelName); + ArgumentCaptor<List<ToscaImportByModel>> importListArgumentCaptor = ArgumentCaptor.forClass(List.class); + verify(toscaModelImportCassandraDao).saveAll(eq(modelName), importListArgumentCaptor.capture()); + + final List<ToscaImportByModel> actualImportList = importListArgumentCaptor.getValue(); + assertEquals(3, actualImportList.size()); + assertTrue(actualImportList.contains(toscaImportByModel1)); + assertTrue(actualImportList.contains(toscaImportByModel2)); + + var expectedAdditionalTypesImport = new ToscaImportByModel(); + expectedAdditionalTypesImport.setModelId(modelName); + expectedAdditionalTypesImport.setFullPath(ADDITIONAL_TYPE_DEFINITIONS_PATH.toString()); + expectedAdditionalTypesImport.setContent(Files.readString(testResourcePath.resolve(Path.of("expected-additional_types-1.yaml")))); + final ToscaImportByModel actualAdditionalTypesImport = + actualImportList.stream().filter(expectedAdditionalTypesImport::equals).findFirst().orElse(null); + assertNotNull(actualAdditionalTypesImport); + assertEquals(expectedAdditionalTypesImport.getContent(), actualAdditionalTypesImport.getContent()); + + var expectedImport1 = new ToscaImportByModel(); + expectedImport1.setModelId(modelName); + expectedImport1.setFullPath(import1RelativePath.toString()); + expectedImport1.setContent(Files.readString(testResourcePath.resolve(Path.of("expected-import-1.yaml")))); + final ToscaImportByModel actualImport1 = actualImportList.stream().filter(expectedImport1::equals).findFirst().orElse(null); + assertNotNull(actualImport1); + assertEquals(expectedImport1.getContent(), actualImport1.getContent()); + + var expectedImport2 = new ToscaImportByModel(); + expectedImport2.setModelId(modelName); + expectedImport2.setFullPath(import2RelativePath.toString()); + expectedImport2.setContent(Files.readString(testResourcePath.resolve(Path.of("expected-import-2.yaml")))); + final ToscaImportByModel actualImport2 = actualImportList.stream().filter(expectedImport2::equals).findFirst().orElse(null); + assertNotNull(actualImport2); + assertEquals(expectedImport2.getContent(), actualImport2.getContent()); + } + + @Test + void addTypesToDefaultImportsTest_existingAdditionalTypesImport() throws IOException { + var modelName = "model"; + final Path testResourcePath = Path.of("src/test/resources/modelOperation"); + + final var dataTypesPath = testResourcePath.resolve(Path.of("input-data_types.yaml")); + final var dataTypes = Files.readString(dataTypesPath); + + final Path import1RelativePath = Path.of("original-import-1.yaml"); + final Path import1Path = testResourcePath.resolve(import1RelativePath); + + var toscaImportByModel1 = new ToscaImportByModel(); + toscaImportByModel1.setModelId(modelName); + toscaImportByModel1.setFullPath(import1RelativePath.toString()); + toscaImportByModel1.setContent(Files.readString(import1Path)); + + var originalAdditionalTypesImport = new ToscaImportByModel(); + originalAdditionalTypesImport.setModelId(modelName); + originalAdditionalTypesImport.setFullPath(ADDITIONAL_TYPE_DEFINITIONS_PATH.toString()); + final Path originalAdditionalTypesImportPath = testResourcePath.resolve(Path.of("original-additional_types-1.yaml")); + originalAdditionalTypesImport.setContent(Files.readString(originalAdditionalTypesImportPath)); + + final List<ToscaImportByModel> modelImports = new ArrayList<>(); + modelImports.add(toscaImportByModel1); + modelImports.add(originalAdditionalTypesImport); + when(toscaModelImportCassandraDao.findAllByModel(modelName)).thenReturn(modelImports); + + modelOperation.addTypesToDefaultImports(ElementTypeEnum.DATA_TYPE, dataTypes, modelName); + ArgumentCaptor<List<ToscaImportByModel>> importListArgumentCaptor = ArgumentCaptor.forClass(List.class); + verify(toscaModelImportCassandraDao).saveAll(eq(modelName), importListArgumentCaptor.capture()); + + final List<ToscaImportByModel> actualImportList = importListArgumentCaptor.getValue(); + assertEquals(2, actualImportList.size()); + assertTrue(actualImportList.contains(toscaImportByModel1)); + + var expectedAdditionalTypesImport = new ToscaImportByModel(); + expectedAdditionalTypesImport.setModelId(modelName); + expectedAdditionalTypesImport.setFullPath(ADDITIONAL_TYPE_DEFINITIONS_PATH.toString()); + expectedAdditionalTypesImport.setContent(Files.readString(testResourcePath.resolve(Path.of("expected-additional_types-2.yaml")))); + final ToscaImportByModel actualAdditionalTypesImport = + actualImportList.stream().filter(expectedAdditionalTypesImport::equals).findFirst().orElse(null); + assertNotNull(actualAdditionalTypesImport); + assertEquals(expectedAdditionalTypesImport.getContent(), actualAdditionalTypesImport.getContent()); + + var expectedImport1 = new ToscaImportByModel(); + expectedImport1.setModelId(modelName); + expectedImport1.setFullPath(import1RelativePath.toString()); + expectedImport1.setContent(Files.readString(testResourcePath.resolve(Path.of("expected-import-1.yaml")))); + final ToscaImportByModel actualImport1 = actualImportList.stream().filter(expectedImport1::equals).findFirst().orElse(null); + assertNotNull(actualImport1); + assertEquals(expectedImport1.getContent(), actualImport1.getContent()); + + } + private ToscaImportByModel createModelImport(final String parentModelName, final String importPath) { var toscaImportByModel = new ToscaImportByModel(); toscaImportByModel.setModelId(parentModelName); diff --git a/catalog-model/src/test/resources/modelOperation/expected-additional_types-1.yaml b/catalog-model/src/test/resources/modelOperation/expected-additional_types-1.yaml new file mode 100644 index 0000000000..c9e6741993 --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/expected-additional_types-1.yaml @@ -0,0 +1,13 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 +description: Auto-generated file that contains package custom types or types added + after system installation. +data_types: + tosca.datatypes.nfv.ServiceAvailability: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.L2AddressData: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.Unknown: + derived_from: tosca.datatypes.Root + description: additional type diff --git a/catalog-model/src/test/resources/modelOperation/expected-additional_types-2.yaml b/catalog-model/src/test/resources/modelOperation/expected-additional_types-2.yaml new file mode 100644 index 0000000000..2d2c54206e --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/expected-additional_types-2.yaml @@ -0,0 +1,19 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 +description: Auto-generated file that contains package custom types or types added + after system installation. +data_types: + tosca.datatypes.nfv.PreviouslyExistingType1: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.PreviouslyExistingType2: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.ServiceAvailability: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.L2AddressData: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.Unknown: + derived_from: tosca.datatypes.Root + description: additional type diff --git a/catalog-model/src/test/resources/modelOperation/expected-import-1.yaml b/catalog-model/src/test/resources/modelOperation/expected-import-1.yaml new file mode 100644 index 0000000000..cecb1b49cd --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/expected-import-1.yaml @@ -0,0 +1,5 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +description: ETSI NFV SOL 001 nsd types definitions version 2.5.1 +data_types: + tosca.datatypes.nfv.AddressData: + derived_from: tosca.datatypes.Root diff --git a/catalog-model/src/test/resources/modelOperation/expected-import-2.yaml b/catalog-model/src/test/resources/modelOperation/expected-import-2.yaml new file mode 100644 index 0000000000..cdee947652 --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/expected-import-2.yaml @@ -0,0 +1,7 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +description: ETSI NFV SOL 001 vnfd types definitions version 2.5.1 +data_types: + tosca.datatypes.nfv.L3AddressData: + derived_from: tosca.datatypes.Root + tosca.datatypes.nfv.AddressData: + derived_from: tosca.datatypes.Root diff --git a/catalog-model/src/test/resources/modelOperation/input-data_types.yaml b/catalog-model/src/test/resources/modelOperation/input-data_types.yaml new file mode 100644 index 0000000000..77b7d977b0 --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/input-data_types.yaml @@ -0,0 +1,9 @@ +tosca.datatypes.nfv.ServiceAvailability: + derived_from: tosca.datatypes.Root + description: additional type +tosca.datatypes.nfv.L2AddressData: + derived_from: tosca.datatypes.Root + description: additional type +tosca.datatypes.nfv.Unknown: + derived_from: tosca.datatypes.Root + description: additional type
\ No newline at end of file diff --git a/catalog-model/src/test/resources/modelOperation/original-additional_types-1.yaml b/catalog-model/src/test/resources/modelOperation/original-additional_types-1.yaml new file mode 100644 index 0000000000..c2cc7f13b4 --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/original-additional_types-1.yaml @@ -0,0 +1,10 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 +description: Auto-generated file that contains package custom types or types added + after system installation. +data_types: + tosca.datatypes.nfv.PreviouslyExistingType1: + derived_from: tosca.datatypes.Root + description: additional type + tosca.datatypes.nfv.PreviouslyExistingType2: + derived_from: tosca.datatypes.Root + description: additional type diff --git a/catalog-model/src/test/resources/modelOperation/original-import-1.yaml b/catalog-model/src/test/resources/modelOperation/original-import-1.yaml new file mode 100644 index 0000000000..d54e61e6ce --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/original-import-1.yaml @@ -0,0 +1,9 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +description: ETSI NFV SOL 001 nsd types definitions version 2.5.1 + +data_types: + tosca.datatypes.nfv.ServiceAvailability: + derived_from: tosca.datatypes.Root + description: original type that will be replaced + tosca.datatypes.nfv.AddressData: + derived_from: tosca.datatypes.Root diff --git a/catalog-model/src/test/resources/modelOperation/original-import-2.yaml b/catalog-model/src/test/resources/modelOperation/original-import-2.yaml new file mode 100644 index 0000000000..8ba6b8bfb3 --- /dev/null +++ b/catalog-model/src/test/resources/modelOperation/original-import-2.yaml @@ -0,0 +1,11 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +description: ETSI NFV SOL 001 vnfd types definitions version 2.5.1 + +data_types: + tosca.datatypes.nfv.L2AddressData: + derived_from: tosca.datatypes.Root + description: original type that will be replaced + tosca.datatypes.nfv.L3AddressData: + derived_from: tosca.datatypes.Root + tosca.datatypes.nfv.AddressData: + derived_from: tosca.datatypes.Root
\ No newline at end of file diff --git a/common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java b/common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java index 5e8818b0b6..fabb04f5f5 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java @@ -23,9 +23,11 @@ import com.datastax.driver.mapping.annotations.Column; import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.Table; import lombok.Data; +import lombok.EqualsAndHashCode; @Data @Table(keyspace = "sdcartifact", name = "tosca_import_by_model") +@EqualsAndHashCode public class ToscaImportByModel { @PartitionKey @@ -35,6 +37,7 @@ public class ToscaImportByModel { @Column(name = "full_path") private String fullPath; @Column(name = "content") + @EqualsAndHashCode.Exclude private String content; } |