From 2e4af1e0c0611851f450b2e215485064f6795958 Mon Sep 17 00:00:00 2001 From: vasraz Date: Wed, 28 Jun 2023 15:34:47 +0100 Subject: [PATCH] Implement YAML Validator Signed-off-by: Vasyl Razinkov Change-Id: I0365d4160984e4d68906959fb801ec7da5449b77 Issue-ID: SDC-4537 --- .../main/resources/config/error-configuration.yaml | 16 +- catalog-be/pom.xml | 20 +- .../files/default/error-configuration.yaml | 10 +- .../sdc/be/servlets/TypesUploadServlet.java | 207 ++++++++++++--------- .../main/resources/config/error-configuration.yaml | 10 +- .../src/main/resources/validateYaml/schema.json | 121 ++++++++++++ .../org/openecomp/sdc/be/tosca/SchemaFiles.java | 76 -------- .../openecomp/sdc/be/tosca/SchemaFilesTest.java | 131 +++++++++++++ .../config/catalog-be/error-configuration.yaml | 10 +- .../noValid/data_types_no-valid-001.yaml | 1 + .../noValid/data_types_no-valid-002.yaml | 1 + .../noValid/data_types_no-valid-003.yaml | 2 + .../noValid/data_types_no-valid-004.yaml | 3 + .../noValid/data_types_no-valid-005.yaml | 4 + .../noValid/data_types_no-valid-006.yaml | 4 + .../noValid/data_types_no-valid-007.yaml | 5 + .../noValid/data_types_no-valid-008.yaml | 6 + .../noValid/data_types_no-valid-009.yaml | 7 + .../noValid/data_types_no-valid-010.yaml | 7 + .../noValid/data_types_no-valid-011.yaml | 9 + .../noValid/data_types_no-valid-012.yaml | 14 ++ .../noValid/data_types_no-valid-013.yaml | 20 ++ .../noValid/data_types_valid-01.yaml | 85 +++++++++ .../noValid/data_types_valid-02.yaml | 84 +++++++++ .../noValid/data_types_valid-03.yaml | 83 +++++++++ .../resources/yamlValidation/noValid/schema.json | 121 ++++++++++++ .../org/openecomp/sdc/be/dao/api/ActionStatus.java | 2 +- .../src/app/utils/service-data-type-reader.ts | 11 +- 28 files changed, 888 insertions(+), 182 deletions(-) create mode 100644 catalog-be/src/main/resources/validateYaml/schema.json delete mode 100644 catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFiles.java create mode 100644 catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFilesTest.java create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-001.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-002.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-003.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-004.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-005.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-006.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-007.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-008.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-009.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-010.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-011.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-012.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-013.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-01.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-02.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-03.yaml create mode 100644 catalog-be/src/test/resources/yamlValidation/noValid/schema.json diff --git a/asdctool/src/main/resources/config/error-configuration.yaml b/asdctool/src/main/resources/config/error-configuration.yaml index e8f100da3f..0ac4369aa2 100644 --- a/asdctool/src/main/resources/config/error-configuration.yaml +++ b/asdctool/src/main/resources/config/error-configuration.yaml @@ -2874,7 +2874,15 @@ errors: # %1 - componentType # %2 - component id CANNOT_ARCHIVE_SYSTEM_DEPLOYED_RESOURCES: { - code: 409, - message: "System deployed %1 cannot be archived. Component: '%2'", - messageId: "SVC4018" - } \ No newline at end of file + code: 409, + message: "System deployed %1 cannot be archived. Component: '%2'", + messageId: "SVC4018" + } + + #---------SVC4010----------------------------- + # %1 - error's list + YAML_IS_INVALID: { + code: 402, + message: "Error: Uploaded YAML file is invalid.\n%1", + messageId: "SVC4010" + } diff --git a/catalog-be/pom.xml b/catalog-be/pom.xml index 5dc9423def..54d6ce2f6a 100644 --- a/catalog-be/pom.xml +++ b/catalog-be/pom.xml @@ -22,6 +22,7 @@ + org.openecomp.sdc togglz-rest-services @@ -41,6 +42,11 @@ + + com.networknt + json-schema-validator + 1.0.84 + com.fasterxml.jackson.core jackson-core @@ -57,7 +63,6 @@ - com.fasterxml.jackson.core jackson-databind @@ -220,11 +225,11 @@ - - io.micrometer - micrometer-registry-prometheus - ${micrometer.version} - + + io.micrometer + micrometer-registry-prometheus + ${micrometer.version} + ch.qos.logback @@ -1183,6 +1188,9 @@ src/test/resources/artifacts/pnfSoftwareInformation/** + + src/test/resources/yamlValidation/noValid/** + 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 0cdcd01a00..006752ee0c 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 @@ -2894,4 +2894,12 @@ errors: code: 409, message: "System deployed %1 cannot be archived. Component: '%2'", messageId: "SVC4018" - } \ No newline at end of file + } + + #---------SVC4010----------------------------- + # %1 - error's list + YAML_IS_INVALID: { + code: 402, + message: "Error: Uploaded YAML file is invalid.\n%1", + messageId: "SVC4010" + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java index f7ea9bb407..309bff28c3 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java @@ -19,8 +19,13 @@ */ package org.openecomp.sdc.be.servlets; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.google.gson.reflect.TypeToken; import com.jcabi.aspects.Loggable; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; import fj.data.Either; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -30,25 +35,6 @@ 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.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import org.apache.commons.lang3.tuple.ImmutablePair; import org.glassfish.jersey.media.multipart.FormDataParam; import org.jetbrains.annotations.NotNull; @@ -64,6 +50,7 @@ import org.openecomp.sdc.be.components.impl.RelationshipTypeImportManager; import org.openecomp.sdc.be.components.impl.ResourceImportManager; import org.openecomp.sdc.be.components.impl.aaf.AafPermission; import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed; +import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException; import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData; import org.openecomp.sdc.be.config.BeEcompErrorManager; import org.openecomp.sdc.be.dao.api.ActionStatus; @@ -85,6 +72,28 @@ import org.openecomp.sdc.common.log.wrappers.Logger; import org.openecomp.sdc.exception.ResponseFormat; import org.springframework.stereotype.Controller; +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + @Loggable(prepend = true, value = Loggable.DEBUG, trim = false) @Path("/v1/catalog/uploadType") @Consumes(MediaType.MULTIPART_FORM_DATA) @@ -129,67 +138,67 @@ public class TypesUploadServlet extends AbstractValidationsServlet { @POST @Path("/capability") @Operation(description = "Create Capability Type from yaml", method = "POST", summary = "Returns created Capability Type", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Capability Type created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Capability Type already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Capability Type created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Capability Type already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadCapabilityType(@Parameter(description = "FileInputStream") @FormDataParam("capabilityTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator, @Parameter(description = "model name") @FormDataParam("model") String modelName, @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { ConsumerFourParam, String, String, Boolean> createElementsMethod = (responseWrapper, ymlPayload, model, includeToModelImport) -> - createElementsType(responseWrapper, () -> capabilityTypeImportManager.createCapabilityTypes(ymlPayload, modelName, - includeToModelDefaultImports)); + createElementsType(responseWrapper, () -> capabilityTypeImportManager.createCapabilityTypes(ymlPayload, modelName, + includeToModelDefaultImports)); return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.CapabilityType.name(), modelName, - includeToModelDefaultImports); + includeToModelDefaultImports); } @POST @Path("/relationship") @Operation(description = "Create Relationship Type from yaml", method = "POST", summary = "Returns created Relationship Type", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Relationship Type created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Relationship Type already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Relationship Type created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Relationship Type already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadRelationshipType(@Parameter(description = "FileInputStream") @FormDataParam("relationshipTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator, @Parameter(description = "model name") @FormDataParam("model") String modelName, @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { return uploadElementTypeServletLogic(this::createRelationshipTypes, file, request, creator, NodeTypeEnum.RelationshipType.getName(), - modelName, includeToModelDefaultImports); + modelName, includeToModelDefaultImports); } @POST @Path("/interfaceLifecycle") @Operation(description = "Create Interface Lyfecycle Type from yaml", method = "POST", summary = "Returns created Interface Lifecycle Type", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Interface Lifecycle Type created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Interface Lifecycle Type already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Interface Lifecycle Type created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Interface Lifecycle Type already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadInterfaceLifecycleType(@Parameter(description = "FileInputStream") @FormDataParam("interfaceLifecycleTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator, @Parameter(description = "model name") @FormDataParam("model") String modelName, @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { ConsumerTwoParam, String> createElementsMethod = (responseWrapper, ymlPayload) -> - createElementsType(responseWrapper, () -> interfaceLifecycleTypeImportManager.createLifecycleTypes(ymlPayload, modelName, - includeToModelDefaultImports)); + createElementsType(responseWrapper, () -> interfaceLifecycleTypeImportManager.createLifecycleTypes(ymlPayload, modelName, + includeToModelDefaultImports)); return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "Interface Types"); } @POST @Path("/artifactTypes") @Operation(description = "Create Tosca Artifact types from yaml", method = "POST", summary = "Returns created Tosca artifact types", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Tosca Artifact types created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Tosca Artifact Type already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Tosca Artifact types created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Tosca Artifact Type already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadArtifactTypes(@Parameter(description = "Zip file containing a yaml with the TOSCA artifact types definition") @FormDataParam("artifactsZip") File file, @@ -198,69 +207,69 @@ public class TypesUploadServlet extends AbstractValidationsServlet { @Parameter(description = "A flag to add types to the default imports") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { final ConsumerTwoParam, String> createElementsMethod = (responseWrapper, ymlPayload) -> - createElementsType(responseWrapper, - () -> artifactTypeImportManager.createArtifactTypes(ymlPayload, modelName, includeToModelDefaultImports)); + createElementsType(responseWrapper, + () -> artifactTypeImportManager.createArtifactTypes(ymlPayload, modelName, includeToModelDefaultImports)); return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.ArtifactType.getName()); } @POST @Path("/categories") @Operation(description = "Create Categories from yaml", method = "POST", summary = "Returns created categories", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Categories created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Category already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Categories created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Category already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadCategories(@Parameter(description = "FileInputStream") @FormDataParam("categoriesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) { ConsumerTwoParam, String> createElementsMethod = (responseWrapper, ymlPayload) -> - createElementsType(responseWrapper, () -> categoriesImportManager.createCategories(ymlPayload)); + createElementsType(responseWrapper, () -> categoriesImportManager.createCategories(ymlPayload)); return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "categories"); } @POST @Path("/datatypes") @Operation(description = "Create Data Types from zip", method = "POST", summary = "Returns created data types", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Data types created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Data types already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Data types created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Data types already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadDataTypes(@Parameter(description = "FileInputStream") @FormDataParam("dataTypesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator, @Parameter(description = "model name") @FormDataParam("model") String modelName, @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { return uploadElementTypeServletLogic(this::createDataTypes, file, request, creator, NodeTypeEnum.DataType.getName(), modelName, - includeToModelDefaultImports); + includeToModelDefaultImports); } @POST @Path("/datatypesyaml") @Operation(description = "Create Data Types from yaml", method = "POST", summary = "Returns created data types", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "Data types created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "Data types already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Data types created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Data types already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadDataTypesYaml(@Parameter(description = "FileInputStream") @FormDataParam("dataTypesYaml") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator, @Parameter(description = "model name") @FormDataParam("model") String modelName, @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { return uploadElementTypeServletLogicYaml(this::createDataTypes, file, request, creator, NodeTypeEnum.DataType.getName(), modelName, - includeToModelDefaultImports); + includeToModelDefaultImports); } @POST @Path("/grouptypes") @Operation(description = "Create GroupTypes from yaml", method = "POST", summary = "Returns created group types", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "group types created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "group types already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "group types created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "group types already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadGroupTypes(@Parameter(description = "toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData, @Parameter(description = "model name") @FormDataParam("model") String modelName, @@ -269,17 +278,17 @@ public class TypesUploadServlet extends AbstractValidationsServlet { @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { Map typesMetadata = getTypesMetadata(toscaTypesMetaData); return uploadTypesWithMetaData(this::createGroupTypes, typesMetadata, file, request, creator, NodeTypeEnum.GroupType.getName(), modelName, - includeToModelDefaultImports); + includeToModelDefaultImports); } @POST @Path("/policytypes") @Operation(description = "Create PolicyTypes from yaml", method = "POST", summary = "Returns created policy types", responses = { - @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), - @ApiResponse(responseCode = "201", description = "policy types created"), - @ApiResponse(responseCode = "403", description = "Restricted operation"), - @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), - @ApiResponse(responseCode = "409", description = "policy types already exist")}) + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "policy types created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "policy types already exist")}) @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) public Response uploadPolicyTypes(@Parameter(description = "toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData, @Parameter(description = "model name") @FormDataParam("model") String modelName, @@ -288,7 +297,7 @@ public class TypesUploadServlet extends AbstractValidationsServlet { @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { Map typesMetadata = getTypesMetadata(toscaTypesMetaData); return uploadTypesWithMetaData(this::createPolicyTypes, typesMetadata, file, request, creator, NodeTypeEnum.PolicyType.getName(), modelName, - includeToModelDefaultImports); + includeToModelDefaultImports); } private Map getTypesMetadata(String toscaTypesMetaData) { @@ -346,6 +355,12 @@ public class TypesUploadServlet extends AbstractValidationsServlet { final String elementTypeName, final String modelName, final boolean includeToModelDefaultImports) { init(); + final Set validationMessages = validateYaml(file); + if (!validationMessages.isEmpty()) { + throw new ByActionStatusComponentException(ActionStatus.YAML_IS_INVALID, validationMessages.stream().map(validationMessage -> { + return validationMessage.getMessage(); + }).collect(Collectors.joining("\n"))); + } final String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER); try { final String url = request.getMethod() + " " + request.getRequestURI(); @@ -365,6 +380,24 @@ public class TypesUploadServlet extends AbstractValidationsServlet { } } + private Set validateYaml(final File file) { + final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + final JsonSchemaFactory factory = + JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012)).objectMapper(mapper).build(); + + final Set validationMessages = new HashSet<>(); + try (final InputStream yamlFile = new FileInputStream(file); + final InputStream schemaFile = Thread.currentThread().getContextClassLoader().getResourceAsStream("validateYaml/schema.json")) { + validationMessages.addAll(factory.getSchema(schemaFile).validate(mapper.readTree(yamlFile))); + } catch (final IOException e) { + ValidationMessage.Builder builder = new ValidationMessage.Builder(); + builder.customMessage(e.getMessage()); + validationMessages.add(builder.build()); + } + + return validationMessages; + } + @NotNull private String getFileAsString(File file) throws IOException { try (final FileInputStream fl = new FileInputStream(file)) { @@ -423,7 +456,7 @@ public class TypesUploadServlet extends AbstractValidationsServlet { } else { try { Response response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), - RepresentationUtils.toRepresentation(eitherResult.left().value())); + RepresentationUtils.toRepresentation(eitherResult.left().value())); responseWrapper.setInnerElement(response); } catch (Exception e) { responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR))); @@ -436,27 +469,27 @@ public class TypesUploadServlet extends AbstractValidationsServlet { private void createDataTypes(Wrapper responseWrapper, String dataTypesYml, final String modelName, final boolean includeToModelDefaultImports) { final Supplier>, ResponseFormat>> generateElementTypeFromYml = () -> - dataTypeImportManager.createDataTypes(dataTypesYml, modelName, includeToModelDefaultImports); + dataTypeImportManager.createDataTypes(dataTypesYml, modelName, includeToModelDefaultImports); buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.DATA_TYPE_ALREADY_EXIST, - NodeTypeEnum.DataType.name()); + NodeTypeEnum.DataType.name()); } // group types private void createGroupTypes(Wrapper responseWrapper, ToscaTypeImportData toscaTypeImportData, String modelName, final boolean includeToModelDefaultImports) { final Supplier>, ResponseFormat>> generateElementTypeFromYml = () -> - groupTypeImportManager.createGroupTypes(toscaTypeImportData, modelName, includeToModelDefaultImports); + groupTypeImportManager.createGroupTypes(toscaTypeImportData, modelName, includeToModelDefaultImports); buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.GROUP_TYPE_ALREADY_EXIST, - NodeTypeEnum.GroupType.name()); + NodeTypeEnum.GroupType.name()); } // policy types private void createPolicyTypes(Wrapper responseWrapper, ToscaTypeImportData toscaTypeImportData, String modelName, final boolean includeToModelDefaultImports) { final Supplier>, ResponseFormat>> generateElementTypeFromYml = () -> - policyTypeImportManager.createPolicyTypes(toscaTypeImportData, modelName, includeToModelDefaultImports); + policyTypeImportManager.createPolicyTypes(toscaTypeImportData, modelName, includeToModelDefaultImports); buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.POLICY_TYPE_ALREADY_EXIST, - NodeTypeEnum.PolicyType.name()); + NodeTypeEnum.PolicyType.name()); } // data types @@ -475,7 +508,7 @@ public class TypesUploadServlet extends AbstractValidationsServlet { // Group result by the right value - true or false. // I.e., get the number of data types which are new and which are old. final Map>> collect = - list.stream().collect(Collectors.groupingBy(ImmutablePair::getRight)); + list.stream().collect(Collectors.groupingBy(ImmutablePair::getRight)); if (collect != null) { Set keySet = collect.keySet(); if (keySet.size() == 1) { @@ -506,8 +539,8 @@ public class TypesUploadServlet extends AbstractValidationsServlet { final String modelName, final boolean includeToModelDefaultImports) { final Supplier>, ResponseFormat>> generateElementTypeFromYml = () -> relationshipTypeImportManager - .createRelationshipTypes(relationshipTypesYml, modelName, includeToModelDefaultImports); + .createRelationshipTypes(relationshipTypesYml, modelName, includeToModelDefaultImports); buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.RELATIONSHIP_TYPE_ALREADY_EXIST, - NodeTypeEnum.RelationshipType.name()); + NodeTypeEnum.RelationshipType.name()); } } diff --git a/catalog-be/src/main/resources/config/error-configuration.yaml b/catalog-be/src/main/resources/config/error-configuration.yaml index 7589511f54..d77f98ed28 100644 --- a/catalog-be/src/main/resources/config/error-configuration.yaml +++ b/catalog-be/src/main/resources/config/error-configuration.yaml @@ -2886,4 +2886,12 @@ errors: code: 409, message: "System deployed %1 cannot be archived. Component: '%2'", messageId: "SVC4018" - } \ No newline at end of file + } + + #---------SVC4010----------------------------- + # %1 - error's list + YAML_IS_INVALID: { + code: 402, + message: "Error: Uploaded YAML file is invalid.\n%1", + messageId: "SVC4010" + } diff --git a/catalog-be/src/main/resources/validateYaml/schema.json b/catalog-be/src/main/resources/validateYaml/schema.json new file mode 100644 index 0000000000..df43dc4fe3 --- /dev/null +++ b/catalog-be/src/main/resources/validateYaml/schema.json @@ -0,0 +1,121 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "http://example.com/example.json", + "title": "Root Schema", + "type": "object", + "default": {}, + "properties": { + "tosca_definitions_version": { + "title": "The tosca_definitions_version Schema", + "type": "string", + "default": "" + }, + "data_types": { + "title": "The data_types Schema", + "type": "object", + "default": {}, + "required": [], + "additionalProperties": { + "title": "The additionalProperties Schema", + "type": "object", + "default": {}, + "required": [ + "derived_from", + "properties" + ], + "properties": { + "derived_from": { + "title": "The derived_from Schema", + "type": "string", + "default": "" + }, + "properties": { + "title": "The properties Schema", + "type": "object", + "default": {}, + "required": [], + "additionalProperties": { + "title": "The additionalProperties Schema", + "type": "object", + "default": {}, + "required": [ + "type" + ], + "properties": { + "type": { + "title": "The type Schema", + "type": "string", + "default": "" + }, + "description": { + "title": "The description Schema", + "type": "string", + "default": "" + }, + "default": { + "title": "The default Schema", + "type": [ + "string", + "integer", + "boolean", + "number" + ], + "default": "" + }, + "required": { + "title": "The required Schema", + "type": "boolean", + "default": false + }, + "status": { + "title": "The status Schema", + "type": "string", + "default": "" + }, + "constraints": { + "title": "The constraints Schema", + "type": [ + "array" + ], + "default": {} + }, + "entry_schema": { + "title": "The entry_schema Schema", + "type": "object", + "default": {}, + "properties": { + "type": { + "title": "The type Schema", + "type": [ + "string", + "integer", + "boolean", + "number" + ], + "default": "" + } + } + } + }, + "if": { + "properties": { + "type": { + "enum": [ + "map", + "list" + ] + } + } + }, + "then": { + "required": [ + "entry_schema" + ] + } + } + } + } + } + } + } +} diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFiles.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFiles.java deleted file mode 100644 index 839095f1ca..0000000000 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFiles.java +++ /dev/null @@ -1,76 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -package org.openecomp.sdc.be.tosca; - -import org.junit.Test; -import org.yaml.snakeyaml.Yaml; - -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Collectors; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class SchemaFiles { - - @Test - public void testValidateYamlNormativeFiles(){ - String importToscaPath = "src/main/resources/import/tosca"; - assertTrue(checkValidYamlInFileTree(importToscaPath)); - } - - @Test - public void testRainyYamlNormativeFiles(){ - String importToscaPathTest = "src/test/resources/yamlValidation"; - assertFalse(checkValidYamlInFileTree(importToscaPathTest)); - } - - private boolean checkValidYamlInFileTree(String fileTree) { - - try { - List fileTreeYamlList = Files.walk(Paths.get(fileTree)) - .filter(path -> path.getFileName().toString().toLowerCase().endsWith(".yml")) - .collect(Collectors.toList()); - - for (Path yamlFile : fileTreeYamlList) { - try { - FileInputStream inputStream = new FileInputStream(yamlFile.toAbsolutePath().toString()); - Yaml yaml = new Yaml(); - Object content = yaml.load(inputStream); - } catch (Exception e) { - System.out.println("Not valid yaml in file creation : " + yamlFile.toAbsolutePath().toString()); - return false; - } - } - } catch (IOException e) { - System.out.println("Error in reading file from folder : " + fileTree); - return false; - } - return true; - } - - -} diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFilesTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFilesTest.java new file mode 100644 index 0000000000..ddcf649c22 --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/SchemaFilesTest.java @@ -0,0 +1,131 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.tosca; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.openecomp.sdc.be.test.util.TestResourcesHandler; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.parser.ParserException; +import org.yaml.snakeyaml.scanner.ScannerException; + +class SchemaFilesTest { + + @Test + void testValidateYamlNormativeFiles() { + String importToscaPath = "src/main/resources/import/tosca"; + assertTrue(checkValidYamlInFileTree(importToscaPath)); + } + + @Test + void testRainyYamlNormativeFiles() { + String importToscaPathTest = "src/test/resources/yamlValidation"; + assertFalse(checkValidYamlInFileTree(importToscaPathTest)); + } + + private boolean checkValidYamlInFileTree(final String fileTree) { + AtomicBoolean ret = new AtomicBoolean(true); + try (final Stream pathStream = Files.walk(Paths.get(fileTree))) { + pathStream + .filter(path -> path.getFileName().toString().toLowerCase().endsWith(".yml")) + .forEach(yamlFile -> { + try { + new Yaml().load(new FileInputStream(yamlFile.toAbsolutePath().toString())); + } catch (final Exception e) { + System.out.println("Not valid yaml in file creation : " + yamlFile.toAbsolutePath()); + ret.set(false); + } + }); + } catch (final IOException e) { + System.out.println("Error in reading file from folder : " + fileTree); + return false; + } + return ret.get(); + } + + @Test + void yamlValidation_test_no_valid() { + final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + final JsonSchemaFactory factory = + JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012)).objectMapper(mapper).build(); + + try (final Stream pathStream = Files.walk(Paths.get("src/test/resources/yamlValidation"))) { + pathStream + .filter(path -> path.getFileName().toString().toLowerCase().startsWith("data_types_no-valid-")) + .forEach(path -> { + try (final InputStream schemaFile = TestResourcesHandler.getResourceAsStream("yamlValidation/noValid/" + "schema.json"); + final InputStream yamlFile = TestResourcesHandler.getResourceAsStream("yamlValidation/noValid/" + path.getFileName())) { + final Set validationMessages = factory.getSchema(schemaFile).validate(mapper.readTree(yamlFile)); + validationMessages.forEach(System.out::println); + assertFalse(validationMessages.isEmpty()); + } catch (JsonParseException e) { + assertTrue(e.getCause() instanceof ParserException || e.getCause() instanceof ScannerException); + } catch (IOException e) { + fail(e.getMessage()); + } + }); + } catch (final IOException e) { + fail(e.getMessage()); + } + } + @Test + void yamlValidation_test_valid() { + final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + final JsonSchemaFactory factory = + JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012)).objectMapper(mapper).build(); + + try (final Stream pathStream = Files.walk(Paths.get("src/test/resources/yamlValidation"))) { + pathStream + .filter(path -> path.getFileName().toString().toLowerCase().startsWith("data_types_valid-")) + .forEach(path -> { + try (final InputStream schemaFile = TestResourcesHandler.getResourceAsStream("yamlValidation/noValid/" + "schema.json"); + final InputStream yamlFile = TestResourcesHandler.getResourceAsStream("yamlValidation/noValid/" + path.getFileName())) { + final Set validationMessages = factory.getSchema(schemaFile).validate(mapper.readTree(yamlFile)); + validationMessages.forEach(System.out::println); + assertTrue(validationMessages.isEmpty()); + } catch (IOException e) { + fail(e.getMessage()); + } + }); + } catch (final IOException e) { + fail(e.getMessage()); + } + } + +} diff --git a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml index 6911570d16..0937c1d58e 100644 --- a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml +++ b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml @@ -2872,4 +2872,12 @@ errors: code: 409, message: "System deployed %1 cannot be archived. Component: '%2'", messageId: "SVC4018" - } \ No newline at end of file + } + + #---------SVC4010----------------------------- + # %1 - error's list + YAML_IS_INVALID: { + code: 402, + message: "Error: Uploaded YAML file is invalid.\n%1", + messageId: "SVC4010" + } diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-001.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-001.yaml new file mode 100644 index 0000000000..ca05128ddd --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-001.yaml @@ -0,0 +1 @@ +tosca_definitions_version: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-002.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-002.yaml new file mode 100644 index 0000000000..ca05128ddd --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-002.yaml @@ -0,0 +1 @@ +tosca_definitions_version: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-003.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-003.yaml new file mode 100644 index 0000000000..85683a0e63 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-003.yaml @@ -0,0 +1,2 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-004.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-004.yaml new file mode 100644 index 0000000000..ae209d17da --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-004.yaml @@ -0,0 +1,3 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-005.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-005.yaml new file mode 100644 index 0000000000..00d452c440 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-005.yaml @@ -0,0 +1,4 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-006.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-006.yaml new file mode 100644 index 0000000000..74e2c8a114 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-006.yaml @@ -0,0 +1,4 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-007.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-007.yaml new file mode 100644 index 0000000000..6eb68d05d8 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-007.yaml @@ -0,0 +1,5 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-008.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-008.yaml new file mode 100644 index 0000000000..21b54453b2 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-008.yaml @@ -0,0 +1,6 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_2: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-009.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-009.yaml new file mode 100644 index 0000000000..00073a3cac --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-009.yaml @@ -0,0 +1,7 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_2: + type: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-010.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-010.yaml new file mode 100644 index 0000000000..2ccb8dd3fa --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-010.yaml @@ -0,0 +1,7 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_2: + type: map diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-011.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-011.yaml new file mode 100644 index 0000000000..d7e219726f --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-011.yaml @@ -0,0 +1,9 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_2: + type: map + entry_schema: + type: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-012.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-012.yaml new file mode 100644 index 0000000000..8e9ade8473 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-012.yaml @@ -0,0 +1,14 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_2: + type: map + entry_schema: + type: boolean + descriptor_id: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-013.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-013.yaml new file mode 100644 index 0000000000..bfc5f2a48f --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_no-valid-013.yaml @@ -0,0 +1,20 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_1: + type: list + entry_schema: + type: boolean + default: [ + pr_2: + type: map + entry_schema: + type: boolean + default: } + descriptor_id: + type: string + description: Globally unique identifier of the VNFD + default: 'Missing quote + required: true diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-01.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-01.yaml new file mode 100644 index 0000000000..991477ccbd --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-01.yaml @@ -0,0 +1,85 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_1: + type: map + entry_schema: + type: boolean + pr_2: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - equal: fgh + pr_3: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - greater_than: a + pr_4: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - greater_or_equal: b + pr_5: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - less_than: z + pr_6: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - less_or_equal: y + pr_7: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - in_range: + - bb + - yy + pr_8: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - valid_values: + - dd + - rr + pr_9: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - length: 3 + pr_10: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - min_length: 3 + pr_11: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - min_length: 3 + pr_12: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - max_length: 3 + pr_13: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - pattern: '[a-z]' diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-02.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-02.yaml new file mode 100644 index 0000000000..d4ba271134 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-02.yaml @@ -0,0 +1,84 @@ +data_types: + datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_1: + type: map + entry_schema: + type: boolean + pr_2: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - equal: fgh + pr_3: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - greater_than: a + pr_4: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - greater_or_equal: b + pr_5: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - less_than: z + pr_6: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - less_or_equal: y + pr_7: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - in_range: + - bb + - yy + pr_8: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - valid_values: + - dd + - rr + pr_9: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - length: 3 + pr_10: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - min_length: 3 + pr_11: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - min_length: 3 + pr_12: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - max_length: 3 + pr_13: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - pattern: '[a-z]' diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-03.yaml b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-03.yaml new file mode 100644 index 0000000000..a15f36899f --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/data_types_valid-03.yaml @@ -0,0 +1,83 @@ +datatype_test: + derived_from: tosca.datatypes.Root + properties: + pr_1: + type: map + entry_schema: + type: boolean + pr_2: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - equal: fgh + pr_3: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - greater_than: a + pr_4: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - greater_or_equal: b + pr_5: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - less_than: z + pr_6: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - less_or_equal: y + pr_7: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - in_range: + - bb + - yy + pr_8: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - valid_values: + - dd + - rr + pr_9: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - length: 3 + pr_10: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - min_length: 3 + pr_11: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - min_length: 3 + pr_12: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - max_length: 3 + pr_13: + type: string + description: Globally unique identifier of the VNFD + required: true + constraints: + - pattern: '[a-z]' diff --git a/catalog-be/src/test/resources/yamlValidation/noValid/schema.json b/catalog-be/src/test/resources/yamlValidation/noValid/schema.json new file mode 100644 index 0000000000..df43dc4fe3 --- /dev/null +++ b/catalog-be/src/test/resources/yamlValidation/noValid/schema.json @@ -0,0 +1,121 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "http://example.com/example.json", + "title": "Root Schema", + "type": "object", + "default": {}, + "properties": { + "tosca_definitions_version": { + "title": "The tosca_definitions_version Schema", + "type": "string", + "default": "" + }, + "data_types": { + "title": "The data_types Schema", + "type": "object", + "default": {}, + "required": [], + "additionalProperties": { + "title": "The additionalProperties Schema", + "type": "object", + "default": {}, + "required": [ + "derived_from", + "properties" + ], + "properties": { + "derived_from": { + "title": "The derived_from Schema", + "type": "string", + "default": "" + }, + "properties": { + "title": "The properties Schema", + "type": "object", + "default": {}, + "required": [], + "additionalProperties": { + "title": "The additionalProperties Schema", + "type": "object", + "default": {}, + "required": [ + "type" + ], + "properties": { + "type": { + "title": "The type Schema", + "type": "string", + "default": "" + }, + "description": { + "title": "The description Schema", + "type": "string", + "default": "" + }, + "default": { + "title": "The default Schema", + "type": [ + "string", + "integer", + "boolean", + "number" + ], + "default": "" + }, + "required": { + "title": "The required Schema", + "type": "boolean", + "default": false + }, + "status": { + "title": "The status Schema", + "type": "string", + "default": "" + }, + "constraints": { + "title": "The constraints Schema", + "type": [ + "array" + ], + "default": {} + }, + "entry_schema": { + "title": "The entry_schema Schema", + "type": "object", + "default": {}, + "properties": { + "type": { + "title": "The type Schema", + "type": [ + "string", + "integer", + "boolean", + "number" + ], + "default": "" + } + } + } + }, + "if": { + "properties": { + "type": { + "enum": [ + "map", + "list" + ] + } + } + }, + "then": { + "required": [ + "entry_schema" + ] + } + } + } + } + } + } + } +} 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 352f09e138..5434f1ffb7 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_TYPE_PROPERTIES_NOT_FOUND, + DATA_TYPES_NOT_LOADED, DATA_TYPE_NOT_FOUND, DATA_TYPE_PROPERTIES_NOT_FOUND, YAML_IS_INVALID, // Policy Type related TARGETS_EMPTY, TARGETS_NON_VALID, POLICY_TYPE_ALREADY_EXIST, // Group Type related diff --git a/catalog-ui/src/app/utils/service-data-type-reader.ts b/catalog-ui/src/app/utils/service-data-type-reader.ts index 9686f3d40e..dbb550894a 100644 --- a/catalog-ui/src/app/utils/service-data-type-reader.ts +++ b/catalog-ui/src/app/utils/service-data-type-reader.ts @@ -35,10 +35,11 @@ export class ServiceDataTypeReader { const result = reader.result; const loadedContent = load(result); console.log("Readed content: " + loadedContent); - this.readName(this.getDataType(loadedContent)); - this.readDerivedFrom(this.getDataType(loadedContent)); - this.readDescription(this.getDataType(loadedContent)); - this.readProperties(this.getDataType(loadedContent)); + const dataType = this.getDataType(loadedContent); + this.readName(dataType); + this.readDerivedFrom(dataType); + this.readDescription(dataType); + this.readProperties(dataType); resolve(this.serviceDataType); } catch (error) { reject(error); @@ -201,4 +202,4 @@ export class ServiceDataTypeReader { value:constraintValue } } -} \ No newline at end of file +} -- 2.16.6