diff --git a/amf-apicontract.versions b/amf-apicontract.versions index 3f40f46054..c8421cea45 100644 --- a/amf-apicontract.versions +++ b/amf-apicontract.versions @@ -2,5 +2,5 @@ amf.apicontract=5.3.0-SNAPSHOT amf.aml=6.3.0-SNAPSHOT amf.model=3.8.1 antlr4Version=0.6.22 -amf.validation.profile.dialect=1.4.0-SNAPSHOT +amf.validation.profile.dialect=1.5.0-SNAPSHOT amf.validation.report.dialect=1.2.0-SNAPSHOT diff --git a/amf-cli/shared/src/test/resources/validations/raml/union-annots-dont-override-member-annots/api.raml b/amf-cli/shared/src/test/resources/validations/raml/union-annots-dont-override-member-annots/api.raml new file mode 100644 index 0000000000..08ad174323 --- /dev/null +++ b/amf-cli/shared/src/test/resources/validations/raml/union-annots-dont-override-member-annots/api.raml @@ -0,0 +1,25 @@ +#%RAML 1.0 +title: Something +annotationTypes: + complex: + allowedTargets: TypeDeclaration + type: string + flag: + type: nil + allowedTargets: TypeDeclaration + +types: + Type1: + (complex): "Type1" + type: object + properties: + type1prop: string + Type2: + (flag): + type: object + properties: + type2prop: string + Union: + (flag): + (complex): "Union" + type: Type1 | Type2 \ No newline at end of file diff --git a/amf-cli/shared/src/test/scala/amf/validation/AMFModelAssertionTest.scala b/amf-cli/shared/src/test/scala/amf/validation/AMFModelAssertionTest.scala index 02d9abc6d7..320af7dccb 100644 --- a/amf-cli/shared/src/test/scala/amf/validation/AMFModelAssertionTest.scala +++ b/amf-cli/shared/src/test/scala/amf/validation/AMFModelAssertionTest.scala @@ -7,17 +7,20 @@ import amf.core.client.common.transform.PipelineId import amf.core.client.scala.config.RenderOptions import amf.core.client.scala.model.document.{BaseUnit, Document} import amf.core.client.scala.model.domain.extensions.PropertyShape -import amf.core.client.scala.model.domain.{AmfArray, Annotation, ExternalSourceElement, Shape} -import amf.core.internal.annotations.{DeclaredElement, Inferred, VirtualElement, VirtualNode} +import amf.core.client.scala.model.domain.{AmfArray, Annotation, ExternalSourceElement, ScalarNode, Shape} +import amf.core.internal.annotations.{DeclaredElement, Inferred, SourceYPart, VirtualElement, VirtualNode} import amf.core.internal.parser.domain.Annotations import amf.graphql.client.scala.GraphQLConfiguration +import amf.shapes.client.scala.config.JsonSchemaConfiguration +import amf.shapes.client.scala.model.document.JsonSchemaDocument import amf.shapes.client.scala.model.domain._ import amf.shapes.internal.annotations.{BaseVirtualNode, TargetName} import amf.shapes.internal.domain.metamodel.AnyShapeModel import amf.testing.BaseUnitUtils._ import amf.testing.ConfigProvider.configFor import org.mulesoft.common.client.lexical.{Position, PositionRange} -import org.scalatest.Assertion +import org.scalatest +import org.scalatest.{Assertion, Checkpoints} import org.scalatest.funsuite.AsyncFunSuite import org.scalatest.matchers.should.Matchers import org.yaml.model.{YNodePlain, YScalar} @@ -479,4 +482,39 @@ class AMFModelAssertionTest extends AsyncFunSuite with Matchers { obtainedAst.trim shouldEqual expectedAst.trim } } + + test("Assert union custom domain properties are not propagated to members") { + + def assertForAllTypes(shapes: Shape*)(assertion: Shape => Unit) = { + shapes.foreach(assertion) + } + + val api = s"$basePath/raml/union-annots-dont-override-member-annots/api.raml" + val cp = new scalatest.Checkpoints.Checkpoint() + modelAssertion(api, pipelineId = PipelineId.Editing) { unit => + val doc = unit.asInstanceOf[Document] + val union = doc.declares.collect { case union: UnionShape => union }.head + val type1 = doc.declares.collect { case node: NodeShape if node.name.value() == "Type1" => node }.head + val type2 = doc.declares.collect { case node: NodeShape if node.name.value() == "Type2" => node }.head + union.customDomainProperties.length shouldBe 2 + assertForAllTypes(type1, union.anyOf.head) { shape => + cp { shape.customDomainProperties.length shouldBe 1 } + cp { shape.customDomainProperties.head.extension.asInstanceOf[ScalarNode].value.value() shouldEqual "Type1" } + } + assertForAllTypes(type2, union.anyOf(1)) { shape => + cp { shape.customDomainProperties.length shouldBe 1 } + cp { shape.customDomainProperties.head.name.value() shouldEqual "flag" } + } + cp.reportAll() + succeed + } + } + + private def sourcePartOf(unit: BaseUnit, extract: JsonSchemaDocument => Shape) = { + val doc = unit.asInstanceOf[JsonSchemaDocument] + val shapeOfInterest = extract(doc) + val sourceYPart = shapeOfInterest.annotations.find(_.isInstanceOf[SourceYPart]).get.asInstanceOf[SourceYPart] + val obtainedAst = sourceYPart.ast.toString + obtainedAst + } } diff --git a/amf-shapes/shared/src/main/scala/amf/shapes/internal/domain/resolution/shape_normalization/MinShapeAlgorithm.scala b/amf-shapes/shared/src/main/scala/amf/shapes/internal/domain/resolution/shape_normalization/MinShapeAlgorithm.scala index 3b601fc8b5..eab375fda3 100644 --- a/amf-shapes/shared/src/main/scala/amf/shapes/internal/domain/resolution/shape_normalization/MinShapeAlgorithm.scala +++ b/amf-shapes/shared/src/main/scala/amf/shapes/internal/domain/resolution/shape_normalization/MinShapeAlgorithm.scala @@ -1,5 +1,6 @@ package amf.shapes.internal.domain.resolution.shape_normalization +import amf.core.client.platform.model.domain.CustomDomainProperty import amf.core.client.scala.errorhandling.AMFErrorHandler import amf.core.client.scala.model.DataType import amf.core.client.scala.model.domain.extensions.PropertyShape @@ -7,7 +8,7 @@ import amf.core.client.scala.model.domain.{AmfArray, AmfScalar, RecursiveShape, import amf.core.client.scala.validation.AMFValidationResult import amf.core.internal.annotations.{Inferred, InheritanceProvenance, LexicalInformation} import amf.core.internal.metamodel.Field -import amf.core.internal.metamodel.domain.ShapeModel +import amf.core.internal.metamodel.domain.{DomainElementModel, ShapeModel} import amf.core.internal.metamodel.domain.extensions.PropertyShapeModel import amf.core.internal.parser.domain.{Annotations, Value} import amf.core.internal.utils.IdCounter @@ -392,7 +393,7 @@ private[resolution] class MinShapeAlgorithm()(implicit val context: Normalizatio baseNode.fields.setWithoutId(NodeShapeModel.Properties, AmfArray(minProps.toSeq), annotations) computeNarrowRestrictions( - NodeShapeModel.fields, + NodeShapeModel.fields :+ DomainElementModel.CustomDomainProperties, // custom domain isn't part of nodeshape model fields baseNode, superNode, filteredFields = Seq(NodeShapeModel.Properties, NodeShapeModel.Examples) @@ -556,7 +557,8 @@ private[resolution] class MinShapeAlgorithm()(implicit val context: Normalizatio AnyShapeModel.DefaultValueString, AnyShapeModel.Default, AnyShapeModel.Examples, - AnyShapeModel.Description + AnyShapeModel.Description, + AnyShapeModel.CustomDomainProperties ) val filteredBase = baseShape.copyShape() filteredBase.fields.filter(f => !filteredFields.contains(f._1))