From 467338ad23721809b1f95cb9d2920740b83acb5f Mon Sep 17 00:00:00 2001 From: franciscovila Date: Wed, 21 Sep 2022 15:39:47 +0100 Subject: [PATCH] Service import - Import unknown capability types When an unknown capability is found in the csar used to import a service, this capability will be added to the model to which the service is being imported Issue-ID: SDC-4187 Signed-off-by: franciscovila Change-Id: If43b14008e18195bff237c35c5fc1154e57239ff --- .../openecomp/sdc/be/components/csar/CsarInfo.java | 2 ++ .../sdc/be/components/csar/OnboardedCsarInfo.java | 5 ++++ .../sdc/be/components/csar/ServiceCsarInfo.java | 5 ++++ .../impl/ServiceImportBusinessLogic.java | 33 ++++++++++++++++++++- .../impl/ServiceImportBusinessLogicTest.java | 24 ++++++++++++--- .../resources/csars/service-Ser09080002-csar.csar | Bin 63901 -> 63922 bytes .../java/org/openecomp/sdc/be/utils/TypeUtils.java | 2 +- 7 files changed, 65 insertions(+), 6 deletions(-) diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/CsarInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/CsarInfo.java index 5f49bd7bf2..f92c1d10d2 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/CsarInfo.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/CsarInfo.java @@ -179,6 +179,8 @@ public abstract class CsarInfo { public abstract Map getGroupTypes(); + public abstract Map getCapabilityTypes(); + public abstract Map getArtifactTypes(); public Map getPolicyTypes() { diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/OnboardedCsarInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/OnboardedCsarInfo.java index 68a51e3ed3..9ab767876d 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/OnboardedCsarInfo.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/OnboardedCsarInfo.java @@ -216,6 +216,11 @@ public class OnboardedCsarInfo extends CsarInfo { return getTypes(GROUP_TYPES); } + @Override + public Map getCapabilityTypes() { + return getTypes(ToscaTagNamesEnum.CAPABILITY_TYPES); + } + private Map getTypes(ToscaTagNamesEnum toscaTag) { final Map types = new HashMap<>(); for (Map.Entry entry : globalSubstitutes) { diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/ServiceCsarInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/ServiceCsarInfo.java index aeef3acd64..54cd29868b 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/ServiceCsarInfo.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/csar/ServiceCsarInfo.java @@ -154,6 +154,11 @@ public class ServiceCsarInfo extends CsarInfo { return getTypes(ToscaTagNamesEnum.GROUP_TYPES); } + @Override + public Map getCapabilityTypes() { + return getTypes(ToscaTagNamesEnum.CAPABILITY_TYPES); + } + private Map getTypes(ToscaTagNamesEnum toscaTag) { final Map types = new HashMap<>(); mainTemplateImports.entrySet().stream().forEach(entry -> types.putAll(getTypesFromTemplate(entry.getValue(), toscaTag))); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogic.java index a5e20f30eb..151717f9cd 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogic.java @@ -96,6 +96,7 @@ import org.openecomp.sdc.be.model.ArtifactTypeDefinition; import org.openecomp.sdc.be.model.AttributeDefinition; import org.openecomp.sdc.be.model.CapabilityDefinition; import org.openecomp.sdc.be.model.CapabilityRequirementRelationship; +import org.openecomp.sdc.be.model.CapabilityTypeDefinition; import org.openecomp.sdc.be.model.Component; import org.openecomp.sdc.be.model.ComponentInstance; import org.openecomp.sdc.be.model.ComponentInstanceInput; @@ -141,6 +142,7 @@ import org.openecomp.sdc.be.model.operations.StorageException; import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.ArtifactTypeOperation; +import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation; import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation; import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder; import org.openecomp.sdc.be.model.tosca.ToscaPropertyType; @@ -202,6 +204,9 @@ public class ServiceImportBusinessLogic { private final GroupTypeImportManager groupTypeImportManager; private final GroupTypeOperation groupTypeOperation; + private final CapabilityTypeImportManager capabilityTypeImportManager; + private final CapabilityTypeOperation capabilityTypeOperation; + public ServiceImportBusinessLogic(final GroupBusinessLogic groupBusinessLogic, final ArtifactsBusinessLogic artifactsBusinessLogic, final ComponentsUtils componentsUtils, final ToscaOperationFacade toscaOperationFacade, final ServiceBusinessLogic serviceBusinessLogic, final CsarBusinessLogic csarBusinessLogic, @@ -213,7 +218,9 @@ public class ServiceImportBusinessLogic { final IGraphLockOperation graphLockOperation, final ToscaFunctionService toscaFunctionService, final DataTypeBusinessLogic dataTypeBusinessLogic, final ArtifactTypeOperation artifactTypeOperation, final ArtifactTypeImportManager artifactTypeImportManager, final GroupTypeImportManager groupTypeImportManager, - final GroupTypeOperation groupTypeOperation) { + final GroupTypeOperation groupTypeOperation, + final CapabilityTypeImportManager capabilityTypeImportManager, + final CapabilityTypeOperation capabilityTypeOperation) { this.componentsUtils = componentsUtils; this.toscaOperationFacade = toscaOperationFacade; this.serviceBusinessLogic = serviceBusinessLogic; @@ -235,6 +242,8 @@ public class ServiceImportBusinessLogic { this.artifactTypeImportManager = artifactTypeImportManager; this.groupTypeImportManager = groupTypeImportManager; this.groupTypeOperation = groupTypeOperation; + this.capabilityTypeImportManager = capabilityTypeImportManager; + this.capabilityTypeOperation = capabilityTypeOperation; } @Autowired @@ -301,6 +310,12 @@ public class ServiceImportBusinessLogic { groupTypeImportManager.createGroupTypes(toscaTypeImportData, service.getModel(), true); } + final Map capabilityTypesToCreate = getCapabilityTypesToCreate(service.getModel(), csarInfo); + + if (MapUtils.isNotEmpty(capabilityTypesToCreate)) { + capabilityTypeImportManager.createCapabilityTypes(new Yaml().dump(capabilityTypesToCreate), service.getModel(), true); + } + Map nodeTypesInfo = csarInfo.extractTypesInfo(); Either>>, ResponseFormat> findNodeTypesArtifactsToHandleRes = serviceImportParseLogic .findNodeTypesArtifactsToHandle(nodeTypesInfo, csarInfo, service); @@ -360,6 +375,22 @@ public class ServiceImportBusinessLogic { return groupTypesToCreate; } + private Map getCapabilityTypesToCreate(final String model, final CsarInfo csarInfo) { + final Map capabilityTypesToCreate = new HashMap<>(); + final Map capabilityTypes = csarInfo.getCapabilityTypes(); + if (MapUtils.isNotEmpty(capabilityTypes)) { + for (final Entry entry : capabilityTypes.entrySet()) { + final Either result + = capabilityTypeOperation.getCapabilityType(UniqueIdBuilder.buildCapabilityTypeUid(model, entry.getKey())); + if (result.isRight() && result.right().value().equals(StorageOperationStatus.NOT_FOUND)) { + capabilityTypesToCreate.put(entry.getKey(), entry.getValue()); + log.info("Deploying new capability type {} to model {} from package {}", entry.getKey(), model, csarInfo.getCsarUUID()); + } + } + } + return capabilityTypesToCreate; + } + private Map getDatatypesToCreate(final String model, final CsarInfo csarInfo) { final Map dataTypesToCreate = new HashMap<>(); diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogicTest.java index e4b6dd4021..944f51c244 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogicTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogicTest.java @@ -80,9 +80,9 @@ import org.openecomp.sdc.be.externalapi.servlet.ArtifactExternalServlet; import org.openecomp.sdc.be.impl.ServletUtils; import org.openecomp.sdc.be.info.NodeTypeInfoToUpdateArtifacts; import org.openecomp.sdc.be.model.ArtifactDefinition; -import org.openecomp.sdc.be.model.ArtifactTypeDefinition; import org.openecomp.sdc.be.model.AttributeDefinition; import org.openecomp.sdc.be.model.CapabilityDefinition; +import org.openecomp.sdc.be.model.CapabilityTypeDefinition; import org.openecomp.sdc.be.model.Component; import org.openecomp.sdc.be.model.ComponentInstance; import org.openecomp.sdc.be.model.ComponentInstanceInput; @@ -114,8 +114,9 @@ import org.openecomp.sdc.be.model.User; import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation; 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.ArtifactTypeOperation; +import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation; +import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation; import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum; import org.openecomp.sdc.be.servlets.AbstractValidationsServlet; import org.openecomp.sdc.be.tosca.CsarUtils; @@ -130,8 +131,6 @@ import org.yaml.snakeyaml.Yaml; class ServiceImportBusinessLogicTest extends ServiceImportBussinessLogicBaseTestSetup { - private static final String DEFAULT_ICON = "defaulticon"; - private final ArtifactDefinition artifactDefinition = mock(ArtifactDefinition.class); private final ResourceImportManager resourceImportManager = mock(ResourceImportManager.class); private final ServletUtils servletUtils = mock(ServletUtils.class); @@ -142,6 +141,8 @@ class ServiceImportBusinessLogicTest extends ServiceImportBussinessLogicBaseTest private final DataTypeBusinessLogic dataTypeBusinessLogic = mock(DataTypeBusinessLogic.class); private final ArtifactTypeImportManager artifactTypeImportManager = mock(ArtifactTypeImportManager.class); private final GroupTypeOperation groupTypeOperation = mock(GroupTypeOperation.class); + private final CapabilityTypeOperation capabilityTypeOperation = mock(CapabilityTypeOperation.class); + private final CapabilityTypeImportManager capabilityTypeImportManager = mock(CapabilityTypeImportManager.class); @InjectMocks private ServiceImportBusinessLogic sIBL; @@ -254,6 +255,12 @@ class ServiceImportBusinessLogicTest extends ServiceImportBussinessLogicBaseTest when(artifactTypeOperation.getArtifactTypeByUid(contains("tosca.testartifacts.Name"))).thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); when(artifactTypeOperation.getArtifactTypeByUid(contains("tosca.artifacts"))).thenReturn(Either.left(null)); + + when(capabilityTypeOperation.getCapabilityType(anyString())) + .thenReturn(Either.left(new CapabilityTypeDefinition())); + when(capabilityTypeOperation.getCapabilityType(contains("tosca.testcapabilitytypes.Name"))) + .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); + when(toscaOperationFacade.getLatestByToscaResourceName(contains("org.openecomp.resource"), isNull())) .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); when(toscaOperationFacade.getLatestByToscaResourceName(contains("tosca.nodes."), isNull())) @@ -291,6 +298,15 @@ class ServiceImportBusinessLogicTest extends ServiceImportBussinessLogicBaseTest assertEquals(1, artifactTypesMap.size()); assertNotNull(artifactTypesMap.get("tosca.testartifacts.Name")); + ArgumentCaptor capabilityTypes = ArgumentCaptor.forClass(String.class); + verify(capabilityTypeImportManager).createCapabilityTypes( + capabilityTypes.capture(), + isNull(), + anyBoolean()); + Map capabilityTypesMap = new Yaml().load(capabilityTypes.getValue()); + assertEquals(1, capabilityTypesMap.size()); + assertNotNull(capabilityTypesMap.get("tosca.testcapabilitytypes.Name")); + ArgumentCaptor> nodeTypes = ArgumentCaptor.forClass(Map.class); verify(resourceImportManager).importAllNormativeResource(nodeTypes.capture(), any(), any(), any(), anyBoolean(), anyBoolean()); diff --git a/catalog-be/src/test/resources/csars/service-Ser09080002-csar.csar b/catalog-be/src/test/resources/csars/service-Ser09080002-csar.csar index 815b798ec3b13aeaa3ea0803e0f802fcb44fe1e5..c780e3484cf322b1831099f5aef673b1b7e90f78 100644 GIT binary patch delta 2851 zcmZ8jc{tR27oQnsEMrTICSM9KAa!e8 zQMSmIt-+*(Fk_vu*89_Y@AE$Q{XNh3e9rRuea?B#Ux%{9Q?+pqFu0%8Y*A7bx%$hqKYy~%5S2*hX~z(y#M&xVZG ziHA>~zS{Bn1K!inU{Y)Hp0QW#5rGr%Bp z`7HZVdYHz>ZMG4kGD6VS%*xzKGEF%q+y=IJL5~POfcH^>Z#Hkhj}8j3t8%Z){Yntm zI~W<~Z*0YLkE$BwlcsGKNr&r@_5R}JJs7XAsE||PH`9;sqc9Txv|mq^xm8| zaS_+I1n#S_nbc!w+cJmu<$2TGyOV00!{l2(9v0?(7BDnyV7eR1*jkEuY_3SIe;s3z z^A_d>Rfh#Xb-g&_vdN$9V$jpIJHXUO_%AnV#8n#}y~|J*PF=hzry^;8F>fFrp;&=B z=i_@v>ZwGhf>&IyeW+fHFZtH-9}%na6zv9v4moALQ8(XgnDF#xtU2S?IC=VuebIe7 z>vmMquJHhcQRMt%ulw4xg$@T$72==7P;z@tXh-!AX5^EKzA_O*f&m4W@Wxntrg%={c4b0|7bG z*gS{ox`Ef-bgikH9g@_{W%YPY|SC+Q$IlE9AXBuwUhz2)XgVsMIQF%aY}0Q%^X&nX}Qm2)zA<*l zoR|q;j7w)`t0_yqf4fGHX>mvp#vV&m7tJP?TvO89$nSQG)>6G==!$MU=$q;B_0%0n z&&j`<_Z~+iv$K*a>*DXG`JH$xY%Mw-rW|;Ylrqwn)F~=cNYycU8X87WQ7$x}Eq<

`e#4O;QoqFQ^17z6>}DqRBrb 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 RO$;;%$l8>GQ