diff options
22 files changed, 784 insertions, 18 deletions
diff --git a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml index 47e2851b8e..532ee3ecac 100644 --- a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml +++ b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml @@ -2826,4 +2826,4 @@ errors: DATA_TYPE_NOT_FOUND: code: 404 message: "Data type '%1' was not found." - messageId: "SVC4011" + messageId: "SVC4011"
\ No newline at end of file diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java index d9846a7fb1..e39e0d2c0d 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java @@ -22,16 +22,19 @@ package org.openecomp.sdc.be.servlets; import com.jcabi.aspects.Loggable; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -45,6 +48,8 @@ import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; import org.openecomp.sdc.be.exception.BusinessException; import org.openecomp.sdc.be.impl.ComponentsUtils; +import org.openecomp.sdc.be.model.PropertyDefinition; +import org.openecomp.sdc.be.model.dto.PropertyDefinitionDto; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.operations.impl.DataTypeOperation; import org.openecomp.sdc.common.api.Constants; @@ -99,4 +104,19 @@ public class DataTypeServlet extends BeGenericServlet { return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), dataType); } + @GET + @Path("{id}/properties") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Get a data type properties", method = "GET", summary = "Returns the data type properties", responses = { + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = PropertyDefinition.class)))), + @ApiResponse(responseCode = "200", description = "Data type found, properties may be empty"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "404", description = "Data type not found")}) + @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) + public Response fetchProperties(@PathParam("id") final String id) { + final List<PropertyDefinition> allProperties = dataTypeOperation.findAllProperties(id); + return buildOkResponse(allProperties); + } + } diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/DataTypeServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/DataTypeServletTest.java index d916fffc12..d2380fa96e 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/DataTypeServletTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/DataTypeServletTest.java @@ -25,6 +25,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.List; +import java.util.Map; import java.util.Optional; import javax.servlet.ServletContext; import javax.ws.rs.core.MediaType; @@ -44,6 +46,7 @@ import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.impl.WebAppContextWrapper; +import org.openecomp.sdc.be.model.PropertyDefinition; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.operations.impl.DataTypeOperation; import org.openecomp.sdc.common.api.Constants; @@ -56,6 +59,7 @@ class DataTypeServletTest extends JerseySpringBaseTest { private static final String USER_ID = "cs0008"; private static final String DATA_TYPE_UID = "ETSI SOL001 v2.5.1.tosca.datatypes.nfv.L3AddressData.datatype"; private static final String PATH = "/v1/catalog/data-types/" + DATA_TYPE_UID; + private static final String DATA_TYPE_PROPERTIES_PATH = "/v1/catalog/data-types/%s/properties"; @InjectMocks private DataTypeServlet dataTypeServlet; @@ -153,4 +157,27 @@ class DataTypeServletTest extends JerseySpringBaseTest { assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, response.getStatus()); } + @Test + void fetchDataTypePropertiesTest_Success() { + final DataTypeDataDefinition expectedDataType = new DataTypeDataDefinition(); + final PropertyDefinition expectedProperty1 = new PropertyDefinition(); + expectedProperty1.setName("property1"); + final PropertyDefinition expectedProperty2 = new PropertyDefinition(); + expectedProperty2.setName("property2"); + expectedDataType.setUniqueId(DATA_TYPE_UID); + when(dataTypeOperation.findAllProperties(DATA_TYPE_UID)).thenReturn(List.of(expectedProperty1, expectedProperty2)); + + final Response response = target() + .path(String.format(DATA_TYPE_PROPERTIES_PATH, DATA_TYPE_UID)) + .request(MediaType.APPLICATION_JSON) + .header("USER_ID", USER_ID) + .get(Response.class); + assertNotNull(response); + assertEquals(HttpStatus.SC_OK, response.getStatus()); + final List<Map<String, Object>> actualResponse = response.readEntity(List.class); + assertEquals(2, actualResponse.size()); + assertEquals(expectedProperty1.getName(), actualResponse.get(0).get("name")); + assertEquals(expectedProperty2.getName(), actualResponse.get(1).get("name")); + } + } diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java index 1ca0e4d048..2c7c724af7 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java @@ -75,7 +75,7 @@ public enum ActionStatus { EMPTY_OCCURRENCES_LIST, INVALID_OCCURRENCES, NOT_TOPOLOGY_TOSCA_TEMPLATE, INVALID_NODE_TEMPLATE, // Data type related DATA_TYPE_ALREADY_EXIST, DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, DATA_TYPE_DERIVED_IS_MISSING, DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, DATA_TYPE_DUPLICATE_PROPERTY, DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, DATA_TYPE_CANNOT_HAVE_PROPERTIES, DATA_TYPE_CANNOT_BE_EMPTY, DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST, - DATA_TYPES_NOT_LOADED, DATA_TYPE_NOT_FOUND, + DATA_TYPES_NOT_LOADED, DATA_TYPE_NOT_FOUND, DATA_TYPE_PROPERTIES_NOT_FOUND, // Policy Type related TARGETS_EMPTY, TARGETS_NON_VALID, POLICY_TYPE_ALREADY_EXIST, // Group Type related diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java new file mode 100644 index 0000000000..6a647b1d44 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java @@ -0,0 +1,38 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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.exception; + +public class OperationException extends BusinessException { + + public OperationException(final String message) { + super(message); + } + + public OperationException(final Throwable cause) { + super(cause); + } + + public OperationException(final String message, final Throwable cause) { + super(message, cause); + } + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java new file mode 100644 index 0000000000..75cacdd7a8 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java @@ -0,0 +1,37 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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.exception.supplier; + +import java.util.function.Supplier; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.openecomp.sdc.be.exception.OperationException; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DataTypeOperationExceptionSupplier { + + public static Supplier<OperationException> unexpectedErrorWhileFetchingProperties(final String uniqueId) { + final String errorMessage = String.format("An unexpected error has occurred while retrieving the data type '%s' properties", uniqueId); + return () -> new OperationException(errorMessage); + } + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java new file mode 100644 index 0000000000..bac73d9e1d --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java @@ -0,0 +1,39 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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.dto; + +import java.util.List; +import lombok.Data; +import org.openecomp.sdc.be.model.PropertyConstraint; + +@Data +public class PropertyDefinitionDto { + + private String uniqueId; + private String type; + private String name; + private Boolean required; + private Object value; + private Object defaultValue; + private List<PropertyConstraint> constraints; + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java new file mode 100644 index 0000000000..8566f55339 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java @@ -0,0 +1,67 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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.mapper; + +import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.collections4.CollectionUtils; +import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition; +import org.openecomp.sdc.be.model.PropertyConstraint; +import org.openecomp.sdc.be.model.PropertyDefinition; +import org.openecomp.sdc.be.model.dto.PropertyDefinitionDto; + +public class PropertyDefinitionDtoMapper { + + public static PropertyDefinition mapTo(final PropertyDefinitionDto propertyDefinitionDto) { + final var propertyDefinition = new PropertyDefinition(); + propertyDefinition.setUniqueId(propertyDefinitionDto.getUniqueId()); + propertyDefinition.setType(propertyDefinitionDto.getType()); + propertyDefinition.setRequired(propertyDefinitionDto.getRequired()); + propertyDefinition.setName(propertyDefinitionDto.getName()); + if (CollectionUtils.isNotEmpty(propertyDefinitionDto.getConstraints())) { + final List<PropertyConstraint> propertyConstraints = new ArrayList<>(); + propertyDefinition.setConstraints(propertyConstraints); + propertyConstraints.addAll(propertyDefinitionDto.getConstraints()); + } + propertyDefinition.setValue(new Gson().toJson(propertyDefinitionDto.getValue())); + propertyDefinition.setDefaultValue(new Gson().toJson(propertyDefinitionDto.getDefaultValue())); + return propertyDefinition; + } + + public static PropertyDefinitionDto mapFrom(final PropertyDataDefinition propertyDataDefinition) { + final var propertyDefinition = new PropertyDefinition(propertyDataDefinition); + final var propertyDefinitionDto = new PropertyDefinitionDto(); + propertyDefinitionDto.setUniqueId(propertyDefinition.getUniqueId()); + propertyDefinitionDto.setType(propertyDefinition.getType()); + propertyDefinitionDto.setRequired(propertyDefinition.getRequired()); + propertyDefinitionDto.setName(propertyDefinition.getName()); + if (CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())) { + final List<PropertyConstraint> propertyConstraints = new ArrayList<>(); + propertyDefinitionDto.setConstraints(propertyConstraints); + propertyConstraints.addAll(propertyDefinition.getConstraints()); + } + propertyDefinitionDto.setValue(new Gson().fromJson(propertyDataDefinition.getValue(), Object.class)); + propertyDefinitionDto.setDefaultValue(new Gson().fromJson(propertyDataDefinition.getDefaultValue(), Object.class)); + return propertyDefinitionDto; + } +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java index f75e3cfb17..4194ab70db 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java @@ -21,11 +21,13 @@ package org.openecomp.sdc.be.model.operations.impl; import fj.data.Either; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.janusgraph.core.JanusGraph; @@ -38,6 +40,8 @@ import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels; import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary; import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; +import org.openecomp.sdc.be.exception.supplier.DataTypeOperationExceptionSupplier; +import org.openecomp.sdc.be.model.PropertyDefinition; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.resources.data.DataTypeData; @@ -53,6 +57,7 @@ public class DataTypeOperation extends AbstractOperation { private static final Logger LOGGER = LoggerFactory.getLogger(DataTypeOperation.class); private ModelOperation modelOperation; + private PropertyOperation propertyOperation; @Autowired public DataTypeOperation(final HealingJanusGraphGenericDao janusGraphGenericDao) { @@ -65,6 +70,11 @@ public class DataTypeOperation extends AbstractOperation { this.modelOperation = modelOperation; } + @Autowired + public void setPropertyOperation(PropertyOperation propertyOperation) { + this.propertyOperation = propertyOperation; + } + public List<DataTypeData> getAllDataTypeNodes() { final List<DataTypeData> dataTypesFound = new ArrayList<>(); final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesWithNullModel = @@ -172,4 +182,26 @@ public class DataTypeOperation extends AbstractOperation { } return Optional.of(dataTypeEither.left().value().getDataTypeDataDefinition()); } + + public List<PropertyDefinition> findAllProperties(final String uniqueId) { + final Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> propertiesEither = + propertyOperation.findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId); + if (propertiesEither.isRight()) { + final JanusGraphOperationStatus status = propertiesEither.right().value(); + if (status == JanusGraphOperationStatus.NOT_FOUND) { + return List.of(); + } + LOGGER.error("Could not retrieve data type '{}' properties. JanusGraphOperationStatus: '{}'", uniqueId, status); + + throw DataTypeOperationExceptionSupplier.unexpectedErrorWhileFetchingProperties(uniqueId).get(); + } + final Map<String, PropertyDefinition> propertyMap = propertiesEither.left().value(); + if (MapUtils.isEmpty(propertyMap)) { + return List.of(); + } + final List<PropertyDefinition> propertyDefinitions = new ArrayList<>(propertyMap.values()); + propertyDefinitions.sort(Comparator.comparing(PropertyDefinition::getName)); + return propertyDefinitions; + } + } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java index 6b580527c4..9e5c2e49bc 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java @@ -330,13 +330,9 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe List<PropertyConstraint> constraints = propertyDefinition.getConstraints(); propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(uniqueId, propertyName)); PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints)); - if (log.isDebugEnabled()) { - log.debug(BEFORE_ADDING_PROPERTY_TO_GRAPH, propertyData); - } + log.debug(BEFORE_ADDING_PROPERTY_TO_GRAPH, propertyData); Either<PropertyData, JanusGraphOperationStatus> createNodeResult = janusGraphGenericDao.createNode(propertyData, PropertyData.class); - if (log.isDebugEnabled()) { - log.debug(AFTER_ADDING_PROPERTY_TO_GRAPH, propertyData); - } + log.debug(AFTER_ADDING_PROPERTY_TO_GRAPH, propertyData); if (createNodeResult.isRight()) { JanusGraphOperationStatus operationStatus = createNodeResult.right().value(); log.error("Failed to add property {} to graph. status is {}", propertyName, operationStatus); @@ -346,8 +342,8 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName); UniqueIdData uniqueIdData = new UniqueIdData(nodeType, uniqueId); log.debug("Before associating {} to property {}", uniqueIdData, propertyName); - Either<GraphRelation, JanusGraphOperationStatus> createRelResult = janusGraphGenericDao - .createRelation(uniqueIdData, propertyData, GraphEdgeLabels.PROPERTY, props); + Either<GraphRelation, JanusGraphOperationStatus> createRelResult = + janusGraphGenericDao.createRelation(uniqueIdData, propertyData, GraphEdgeLabels.PROPERTY, props); if (createRelResult.isRight()) { JanusGraphOperationStatus operationStatus = createNodeResult.right().value(); log.error(FAILED_TO_ASSOCIATE_RESOURCE_TO_PROPERTY_IN_GRAPH_STATUS_IS, uniqueId, propertyName, operationStatus); diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java index 0efb751124..1f448fd875 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java @@ -20,6 +20,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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -43,8 +44,11 @@ import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; import org.openecomp.sdc.be.datatypes.enums.ModelTypeEnum; import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; +import org.openecomp.sdc.be.exception.OperationException; +import org.openecomp.sdc.be.exception.supplier.DataTypeOperationExceptionSupplier; 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.resources.data.DataTypeData; import org.springframework.test.context.ContextConfiguration; @@ -57,6 +61,8 @@ class DataTypeOperationTest { private ModelOperation modelOperation; @Mock private HealingJanusGraphGenericDao janusGraphGenericDao; + @Mock + private PropertyOperation propertyOperation; private final String modelName = "ETSI-SDC-MODEL-TEST"; private final List<DataTypeData> dataTypesWithoutModel = new ArrayList<>(); @@ -69,6 +75,7 @@ class DataTypeOperationTest { void beforeEachInit() { MockitoAnnotations.openMocks(this); dataTypeOperation.setModelOperation(modelOperation); + dataTypeOperation.setPropertyOperation(propertyOperation); initTestData(); } @@ -135,6 +142,51 @@ class DataTypeOperationTest { assertTrue(result.isEmpty()); } + @Test + void findAllPropertiesTest_Success() { + final PropertyDefinition property1 = new PropertyDefinition(); + property1.setName("property1"); + final PropertyDefinition property2 = new PropertyDefinition(); + property2.setName("property2"); + final PropertyDefinition property3 = new PropertyDefinition(); + property3.setName("property3"); + + when(propertyOperation.findPropertiesOfNode(NodeTypeEnum.DataType, "uniqueId")) + .thenReturn(Either.left(Map.of(property3.getName(), property3, property1.getName(), property1, property2.getName(), property2))); + final List<PropertyDefinition> dataTypeProperties = dataTypeOperation.findAllProperties("uniqueId"); + assertEquals(3, dataTypeProperties.size()); + assertEquals(property1.getName(), dataTypeProperties.get(0).getName()); + assertEquals(property2.getName(), dataTypeProperties.get(1).getName()); + assertEquals(property3.getName(), dataTypeProperties.get(2).getName()); + } + + @Test + void findAllPropertiesTest_propertiesNotFoundSuccess() { + when(propertyOperation.findPropertiesOfNode(NodeTypeEnum.DataType, "uniqueId")) + .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND)); + final List<PropertyDefinition> dataTypeProperties = dataTypeOperation.findAllProperties("uniqueId"); + assertTrue(dataTypeProperties.isEmpty()); + } + + @Test + void findAllPropertiesTest_emptyPropertiesSuccess() { + when(propertyOperation.findPropertiesOfNode(NodeTypeEnum.DataType, "uniqueId")) + .thenReturn(Either.left(Map.of())); + final List<PropertyDefinition> dataTypeProperties = dataTypeOperation.findAllProperties("uniqueId"); + assertTrue(dataTypeProperties.isEmpty()); + } + + @Test + void findAllPropertiesTest_unknownError() { + final String uniqueId = "uniqueId"; + when(propertyOperation.findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId)) + .thenReturn(Either.right(JanusGraphOperationStatus.GENERAL_ERROR)); + final OperationException actualException = assertThrows(OperationException.class, () -> dataTypeOperation.findAllProperties(uniqueId)); + final OperationException expectedException = + DataTypeOperationExceptionSupplier.unexpectedErrorWhileFetchingProperties(uniqueId).get(); + assertEquals(expectedException.getMessage(), actualException.getMessage()); + } + private void initTestData() { model = new Model(modelName, ModelTypeEnum.NORMATIVE); final String TEST_DATA_TYPE_001 = "test.data.type001"; diff --git a/catalog-ui/configurations/menu.js b/catalog-ui/configurations/menu.js index 4b462b752a..5a7e165a9a 100644 --- a/catalog-ui/configurations/menu.js +++ b/catalog-ui/configurations/menu.js @@ -302,6 +302,7 @@ const SDC_MENU_CONFIG = { ], "DataType": [ {"text": "General", "action": "onMenuItemPressed", "state": "general"}, + {"text": "Properties", "action": "onMenuItemPressed", "state": "properties"}, ] } diff --git a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts index 097bbb2c54..b8cccdd213 100644 --- a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts @@ -197,5 +197,17 @@ export class PropertyBEModel { public isToscaFunction(): boolean { return this.toscaFunction != null; } + + /** + * Gets the schema type, if there is a schema. Otherwise, returns undefined. + * + * @return the schema type. + */ + public getSchemaType(): string { + if (this.schema && this.schema.property) { + return this.schema.property.type; + } + return undefined; + } } diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html new file mode 100644 index 0000000000..61c319eb6f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html @@ -0,0 +1,62 @@ +<!-- + ~ - + ~ ============LICENSE_START======================================================= + ~ Copyright (C) 2022 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========================================================= + --> + +<div class="workspace-properties"> + <div id="left-top-bar"> + <span id="properties-count">{{'PROPERTY_SHOWING_LABEL' | translate}}: {{filteredProperties.length}} of {{properties.length}}</span> + <input id="search-by-name" type="search" [placeholder]="'SEARCH_LABEL' | translate" [ngModel]="tableFilterTerm" (ngModelChange)="this.tableSearchTermUpdate.next($event)"/> + <span class="sprite magnification-glass search-button"></span> + </div> + <div class="table-container-flex"> + <div class="table" [ngClass]="{'view-mode': isViewOnly}"> + <div class="head flex-container"> + <div class="table-header head-row hand flex-item" *ngFor="let header of tableHeadersList" (click)="onUpdateSort(header.property)">{{header.title}} + <span *ngIf="tableSortBy === header.property" class="table-header-sort-arrow" [ngClass]="{'down': tableColumnReverse, 'up': !tableColumnReverse}"></span> + </div> + <div class="table-no-text-header head-row flex-item" *ngIf="!isViewOnly"><span class="delete-col-header"></span></div> + </div> + + <div class="body"> + <div *ngIf="filteredProperties.length === 0" class="no-row-text"> + {{'PROPERTY_LIST_EMPTY_MESSAGE' | translate}} + </div> + <div *ngFor="let property of filteredProperties" [attr.data-tests-id]="'property-row-' + property.name" class="flex-container data-row"> + <div class="table-col-general flex-item text" [title]="property.name"> + <a [attr.data-tests-id]="'property-name-' + property.name" [ngClass]="{'disabled': isViewOnly}">{{property.name}}</a> + </div> + + <div class="table-col-general flex-item text" [title]="property.type"> + <span [attr.data-tests-id]="'property-type-' + property.name">{{property.type}}</span> + </div> + <div class="table-col-general flex-item text" [title]="property.getSchemaType() || ''"> + <span [attr.data-tests-id]="'property-schema-' + property.name">{{property.getSchemaType() || ''}}</span> + </div> + <div class="table-col-general flex-item text" [title]="property.description"> + <span [attr.data-tests-id]="'property-description-' + property.name" [title]="property.description">{{property.description}}</span> + </div> + <div class="table-btn-col flex-item" *ngIf="!isViewOnly"></div> + </div> + </div> + + </div> + </div> + +</div> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less new file mode 100644 index 0000000000..9c101e8ce3 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less @@ -0,0 +1,141 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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========================================================= + */ + +@import "../../../../../assets/styles/sprite-old"; +@import "../../../../../assets/styles/mixins_old"; + +.workspace-properties { + + width: 93%; + display: inline-block; + + #left-top-bar { + float: left; + width: 186px; + + ::-webkit-input-placeholder { + font-style: italic; + } + + :-moz-placeholder { + font-style: italic; + } + + ::-moz-placeholder { + font-style: italic; + } + + :-ms-input-placeholder { + font-style: italic; + } + + #properties-count { + font-weight: bold; + float: left; + } + + #search-by-name { + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + width: 270px; + height: 32px; + line-height: 32px; + border: 1px solid @main_color_o; + text-indent: 10px; + } + + .search-button { + .hand; + cursor: pointer; + float: right; + position: relative; + top: -22px; + right: -80px; + } + } + + .delete-col-header { + .sprite(); + .sprite.e-sdc-small-icon-delete(); + } + + .table { + height: 100%; + min-height: 500px; + margin-bottom: 0; + } + + .data-row { + .table-delete-btn { + display: none !important; + } + + &:hover { + .table-delete-btn { + display: inline-block !important; + } + } + } + + .table-container-flex { + margin-top: 0; + overflow: scroll; + &::-webkit-scrollbar-track { + border: 0; + } + + .text { + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + white-space: nowrap; + } + + .flex-item:nth-child(1) { + flex-grow: 15; + + a { + .hand + } + } + + .flex-item:nth-child(2) { + flex-grow: 6; + } + + .flex-item:nth-child(3) { + flex-grow: 6; + } + + .flex-item:nth-child(4) { + flex-grow: 20; + white-space: normal; + } + + .flex-item:nth-child(5) { + flex-grow: 3; + padding-top: 10px; + } + + } + +} diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts new file mode 100644 index 0000000000..6be572d16a --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts @@ -0,0 +1,111 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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========================================================= + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {TypeWorkspacePropertiesComponent} from './type-workspace-properties.component'; +import {FormsModule} from "@angular/forms"; +import {TranslateModule} from "../../../shared/translator/translate.module"; +import {TranslateService} from "../../../shared/translator/translate.service"; +import {DataTypeService} from "../../../services/data-type.service"; +import {Observable} from "rxjs/Observable"; +import {DataTypeModel} from "../../../../models/data-types"; +import {Component, ViewChild} from "@angular/core"; +import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model"; + +describe('TypeWorkspacePropertiesComponent', () => { + const messages = require("../../../../../assets/languages/en_US.json"); + let testHostComponent: TestHostComponent; + let testHostFixture: ComponentFixture<TestHostComponent>; + let dataTypeServiceMock: Partial<DataTypeService> = { + findAllProperties: jest.fn( (dataTypeId) => { + if (dataTypeId === 'dataTypeId') { + const property1 = new PropertyBEModel(); + property1.name = 'property1' + property1.type = 'string' + return Observable.of([property1]); + } + return Observable.of([]); + }) + }; + + let translateServiceMock: Partial<TranslateService> = { + 'translate': jest.fn( (translateKey: string) => { + return messages[translateKey]; + }) + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TypeWorkspacePropertiesComponent, TestHostComponent], + imports: [ + TranslateModule, + FormsModule + ], + providers: [ + {provide: DataTypeService, useValue: dataTypeServiceMock}, + {provide: TranslateService, useValue: translateServiceMock} + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + testHostFixture = TestBed.createComponent(TestHostComponent); + testHostComponent = testHostFixture.componentInstance; + testHostFixture.detectChanges(); + }); + + it('should create', () => { + expect(testHostComponent).toBeTruthy(); + }); + + it('empty property list', () => { + const element = testHostFixture.nativeElement; + const div: HTMLDivElement = element.querySelector('.no-row-text'); + expect(div.textContent).toContain(messages.PROPERTY_LIST_EMPTY_MESSAGE); + }); + + it('test property list', () => { + testHostFixture = TestBed.createComponent(TestHostComponent); + testHostComponent = testHostFixture.componentInstance; + const dataType = new DataTypeModel(); + dataType.uniqueId = 'dataTypeId'; + testHostComponent.typeWorkspacePropertiesComponent.dataType = dataType; + testHostFixture.detectChanges(); + + const element = testHostFixture.nativeElement; + expect(element.querySelector('.no-row-text')).toBeFalsy(); + const expectedPropertyName = 'property1'; + const propertyNameLink: HTMLAnchorElement = element.querySelector(`a[data-tests-id^="property-name-${expectedPropertyName}"]`); + expect(propertyNameLink.textContent).toContain(expectedPropertyName); + }); + + @Component({ + selector: 'host-component', + template: `<app-type-workspace-properties></app-type-workspace-properties>` + }) + class TestHostComponent { + @ViewChild(TypeWorkspacePropertiesComponent) + public typeWorkspacePropertiesComponent: TypeWorkspacePropertiesComponent; + } + +}); diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts new file mode 100644 index 0000000000..931fd3d97c --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts @@ -0,0 +1,115 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 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========================================================= + */ + +import {Component, Input, OnInit} from '@angular/core'; +import {DataTypeModel} from "../../../../models/data-types"; +import {DataTypeService} from "../../../services/data-type.service"; +import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model"; +import { Subject } from "rxjs"; +import {debounceTime, distinctUntilChanged} from "rxjs/operators"; + +@Component({ + selector: 'app-type-workspace-properties', + templateUrl: './type-workspace-properties.component.html', + styleUrls: ['./type-workspace-properties.component.less'] +}) +export class TypeWorkspacePropertiesComponent implements OnInit { + @Input() isViewOnly = true; + @Input() dataType: DataTypeModel = new DataTypeModel(); + + properties: Array<PropertyBEModel> = []; + filteredProperties: Array<PropertyBEModel> = []; + tableHeadersList: Array<TableHeader> = []; + tableSortBy: string = 'name'; + tableColumnReverse: boolean = false; + tableFilterTerm: string = undefined; + tableSearchTermUpdate = new Subject<string>(); + + constructor(private dataTypeService: DataTypeService) { } + + ngOnInit(): void { + this.initTable(); + this.initProperties(); + this.tableSearchTermUpdate.pipe( + debounceTime(400), + distinctUntilChanged()) + .subscribe(searchTerm => { + this.filter(searchTerm); + }); + } + + private initTable(): void { + this.tableHeadersList = [ + {title: 'Name', property: 'name'}, + {title: 'Type', property: 'type'}, + {title: 'Schema', property: 'schema.property.type'}, + {title: 'Description', property: 'description'}, + ]; + + this.tableSortBy = this.tableHeadersList[0].property; + } + + private initProperties(): void { + this.dataTypeService.findAllProperties(this.dataType.uniqueId).subscribe(properties => { + this.properties = properties.map(value => new PropertyBEModel(value)); + this.filteredProperties = Array.from(this.properties); + this.sort(); + }); + } + + onUpdateSort(property: string): void { + if (this.tableSortBy === property) { + this.tableColumnReverse = !this.tableColumnReverse; + } else { + this.tableColumnReverse = false; + this.tableSortBy = property; + } + this.sort(); + } + + private sort(): void { + const field = this.tableSortBy; + this.filteredProperties = this.filteredProperties.sort((property1, property2) => { + const result = property1[field] > property2[field] ? 1 : property1[field] < property2[field] ? -1 : 0; + return this.tableColumnReverse ? result * -1 : result; + }); + } + + private filter(searchTerm: string): void { + if (searchTerm) { + searchTerm = searchTerm.toLowerCase(); + this.filteredProperties = this.properties.filter(property => + property.name.toLowerCase().includes(searchTerm) + || property.type.toLowerCase().includes(searchTerm) + || (property.getSchemaType() && property.getSchemaType().toLowerCase().includes(searchTerm)) + || (property.description && property.description.toLowerCase().includes(searchTerm)) + ); + } else { + this.filteredProperties = Array.from(this.properties); + } + this.sort(); + } +} + +interface TableHeader { + title: string; + property: string; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html index 8d6ae02598..193c94e6ad 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html @@ -42,9 +42,11 @@ {{currentMenu.text}} </div> </div> - <div class="w-sdc-main-container-body-content"> - <app-type-workspace-general *ngIf="currentMenu.state === 'general' && dataType" [dataType]="dataType"></app-type-workspace-general> + <div class="w-sdc-main-container-body-content" *ngIf="dataType"> + <app-type-workspace-general *ngIf="currentMenu.state === 'general'" [dataType]="dataType"></app-type-workspace-general> + <app-type-workspace-properties *ngIf="currentMenu.state === 'properties'" [dataType]="dataType"></app-type-workspace-properties> </div> + </div> </div> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts index 8450da829b..fcad472890 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts @@ -22,7 +22,7 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {TypeWorkspaceComponent} from './type-workspace.component'; -import {ReactiveFormsModule} from "@angular/forms"; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import {TranslateModule} from "../../shared/translator/translate.module"; import {UiElementsModule} from "../../components/ui/ui-elements.module"; import {DataTypeService} from "../../services/data-type.service"; @@ -37,6 +37,7 @@ import {ISdcConfig, SdcConfigToken} from "../../config/sdc-config.config"; import {States} from "../../../utils/constants"; import {IUserProperties} from "../../../models/user"; import {Observable} from "rxjs/Observable"; +import {TypeWorkspacePropertiesComponent} from "./type-workspace-properties/type-workspace-properties.component"; describe('TypeWorkspaceComponent', () => { let component: TypeWorkspaceComponent; @@ -88,9 +89,10 @@ describe('TypeWorkspaceComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ TypeWorkspaceComponent, WorkspaceMenuComponent, TypeWorkspaceGeneralComponent ], + declarations: [ TypeWorkspaceComponent, WorkspaceMenuComponent, TypeWorkspaceGeneralComponent, TypeWorkspacePropertiesComponent ], imports: [ ReactiveFormsModule, + FormsModule, TranslateModule, UiElementsModule, LayoutModule diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts index d3f391db24..021f84af09 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts @@ -28,9 +28,10 @@ import {UiElementsModule} from "../../components/ui/ui-elements.module"; import {LayoutModule} from "../../components/layout/layout.module"; import {TypeWorkspaceGeneralComponent} from './type-workspace-general/type-workspace-general.component'; import {UpgradeModule} from "@angular/upgrade/static"; -import {ReactiveFormsModule} from "@angular/forms"; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import {TranslateModule} from "../../shared/translator/translate.module"; import {DataTypeService} from "../../services/data-type.service"; +import { TypeWorkspacePropertiesComponent } from './type-workspace-properties/type-workspace-properties.component'; @NgModule({ imports: [ @@ -40,11 +41,13 @@ import {DataTypeService} from "../../services/data-type.service"; UpgradeModule, ReactiveFormsModule, TranslateModule, + FormsModule, ], declarations: [ TypeWorkspaceComponent, WorkspaceMenuComponent, - TypeWorkspaceGeneralComponent + TypeWorkspaceGeneralComponent, + TypeWorkspacePropertiesComponent ], providers: [ CacheService, diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts index bf500ec16e..74d48d35de 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -20,7 +20,7 @@ import * as _ from "lodash"; import {Inject, Injectable} from '@angular/core'; -import { DataTypeModel, DataTypesMap, PropertyFEModel, DerivedFEProperty} from "app/models"; +import {DataTypeModel, DataTypesMap, PropertyFEModel, DerivedFEProperty, PropertyBEModel} from "app/models"; import { DataTypesService } from "app/services/data-types-service"; import { PROPERTY_DATA } from "app/utils"; import {DerivedFEAttribute} from "../../models/attributes-outputs/derived-fe-attribute"; @@ -78,6 +78,11 @@ export class DataTypeService { return this.httpClient.get<DataTypeModel>(url); } + public findAllProperties(id: string): Observable<Array<PropertyBEModel>> { + const url = `${this.dataTypeUrl}/${id}/properties` + return this.httpClient.get<Array<PropertyBEModel>>(url); + } + public getConstraintsByParentTypeAndUniqueID(rootPropertyType, propertyName){ // const property = this.dataTypes[rootPropertyType].properties.filter(property => // property.name == propertyName); diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index 3f5a10726c..c3a6bc9a4c 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -41,6 +41,7 @@ "GENERAL_LABEL_SYSTEM_NAME": "System Name:", "GENERAL_LABEL_SOURCE_SERVICE_NAME": "Source Service Name:", "GENERAL_LABEL_RESOURCE_CUSTOMIZATION_UUID": "Resource Customization UUID:", + "SEARCH_LABEL": "Search", "=========== GENERAL_TAB ===========": "", "GENERAL_TAB_LABEL_RESOURCE_MODEL_NUMBER": "Vendor Model Number", "GENERAL_TAB_LABEL_SERVICE_TYPE": "Service Type", @@ -578,5 +579,8 @@ "MODIFIED_LABEL": "Modified", "UNIQUE_ID_LABEL": "Unique Id", "=========== SERVICE IMPORT ===========": "", - "IMPORT_FAILURE_MESSAGE_TEXT": "Import Failure - error reading CSAR" + "IMPORT_FAILURE_MESSAGE_TEXT": "Import Failure - error reading CSAR", + "=========== PROPERTIES ===========": "", + "PROPERTY_LIST_EMPTY_MESSAGE": "There are no properties to display", + "PROPERTY_SHOWING_LABEL": "Showing Properties" } |