Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

W 11046755, W-11046765 & W-11046733 validations #1483

Merged
merged 3 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,24 @@ object AMFRawValidations {
owlClass = shape("UnionShape"),
owlProperty = shape("anyOf"),
constraint = shape("unionInvalidMembers")
),
AMFValidation(
uri = amfParser("duplicated-union-members"),
owlClass = shape("UnionShape"),
owlProperty = shape("anyOf"),
constraint = shape("duplicatedUnionMembers")
),
AMFValidation(
uri = amfParser("duplicated-interface-implementations"),
owlClass = sh("NodeShape"),
owlProperty = shape("inherits"),
constraint = shape("duplicatedInterfaceImplementations")
),
AMFValidation(
uri = amfParser("duplicated-enum-values"),
owlClass = shape("ScalarShape"),
owlProperty = shape("values"),
constraint = shape("duplicatedEnumValues")
)
)
override def validations(): Seq[AMFValidation] = result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import amf.apicontract.internal.validation.runtimeexpression.{AsyncExpressionVal
import amf.core.client.scala.model.domain._
import amf.core.client.scala.model.domain.extensions.{CustomDomainProperty, DomainExtension, PropertyShape}
import amf.core.internal.annotations.SynthesizedField
import amf.core.internal.metamodel.Field
import amf.core.internal.metamodel.domain.common.NameFieldSchema
import amf.core.internal.metamodel.domain.extensions.{CustomDomainPropertyModel, PropertyShapeModel}
import amf.core.internal.utils.RegexConverter
Expand All @@ -23,7 +24,6 @@ import amf.validation.internal.shacl.custom.CustomShaclValidator.{
CustomShaclFunctions,
ValidationInfo
}

import java.util.regex.Pattern

object CustomShaclFunctions {
Expand Down Expand Up @@ -404,6 +404,55 @@ object CustomShaclFunctions {
case _ => // ignore
}
}
},
new CustomShaclFunction {
override val name: String = "duplicatedUnionMembers"
override def run(element: AmfObject, validate: Option[ValidationInfo] => Unit): Unit = {
element match {
case union: UnionShape =>
val members = union.anyOf
checkDuplicates(
members,
validate,
UnionShapeModel.AnyOf,
{ name: String => s"Union must have at most one member with name '$name'" }
)
case _ => // ignore
}
}
},
new CustomShaclFunction {
override val name: String = "duplicatedInterfaceImplementations"
override def run(element: AmfObject, validate: Option[ValidationInfo] => Unit): Unit = {
element match {
case shape: NodeShape =>
val typeName = shape.name.option().getOrElse("unnamedType")
val interfaces = shape.inherits
checkDuplicates(
interfaces,
validate,
NodeShapeModel.Inherits,
{ name: String => s"$typeName cannot implement interface '$name' more than once" }
)
case _ => // ignore
}
}
},
new CustomShaclFunction {
override val name: String = "duplicatedEnumValues"
override def run(element: AmfObject, validate: Option[ValidationInfo] => Unit): Unit = {
element match {
case s: ScalarShape =>
val enumValues = s.values
checkDuplicates(
enumValues,
validate,
ScalarShapeModel.Values,
{ name: String => s"Each enum value must be unique, '$name' it's not" }
)
case _ => // ignore
}
}
}
)

Expand Down Expand Up @@ -449,4 +498,18 @@ object CustomShaclFunctions {

private def hasIntrospectionName(element: NamedDomainElement): Boolean =
element.name.nonNull && element.name.value().startsWith("__")

def checkDuplicates(
s: Seq[NamedDomainElement],
validate: Option[ValidationInfo] => Unit,
field: Field,
message: String => String
): Unit = {
s.foreach({ elem =>
val elemName = elem.name.value()
if (elemName != null && isDuplicated(elemName, s))
validate(Some(ValidationInfo(field, Some(message(elemName)), Some(elem.annotations))))
})
}
private def isDuplicated(elemName: String, s: Seq[NamedDomainElement]) = s.count(_.name.value() == elemName) > 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ModelId: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-interfaces-object.api.graphql
Profile: GraphQL
Conforms: false
Number of results: 1

Level: Violation

- Constraint: http://a.ml/vocabularies/amf/parser#duplicated-interface-implementations
Message: Person cannot implement interface 'HasName' more than once
Severity: Violation
Target: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-interfaces-object.api.graphql#/declares/shape/Person
Property: http://a.ml/vocabularies/shapes#inherits
Range: [(14,0)-(16,0)]
Location:
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ModelId: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-members-union-extension.api.graphql
Profile: GraphQL
Conforms: false
Number of results: 1

Level: Violation

- Constraint: http://a.ml/vocabularies/amf/parser#duplicated-union-members
Message: Union must have at most one member with name 'Dog'
Severity: Violation
Target: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-members-union-extension.api.graphql#/declares/union/SearchResult/or/union/SearchResult_1
Property: http://a.ml/vocabularies/shapes#anyOf
Range: [(14,0)-(17,0)]
Location:
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ModelId: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-members-union.api.graphql
Profile: GraphQL
Conforms: false
Number of results: 1

Level: Violation

- Constraint: http://a.ml/vocabularies/amf/parser#duplicated-union-members
Message: Union must have at most one member with name 'Dog'
Severity: Violation
Target: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-members-union.api.graphql#/declares/union/SearchResult
Property: http://a.ml/vocabularies/shapes#anyOf
Range: [(9,0)-(12,0)]
Location:
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ModelId: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-values-enum-extension.api.graphql
Profile: GraphQL
Conforms: false
Number of results: 1

Level: Violation

- Constraint: http://a.ml/vocabularies/amf/parser#duplicated-enum-values
Message: Each enum value must be unique, 'PLUTO' it's not
Severity: Violation
Target: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-values-enum-extension.api.graphql#/declares/scalar/Planet/or/scalar/Planet_1
Property: http://www.w3.org/ns/shacl#in
Range: [(1,0)-(1,0)]
Location:
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ModelId: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-values-enum.api.graphql
Profile: GraphQL
Conforms: false
Number of results: 1

Level: Violation

- Constraint: http://a.ml/vocabularies/amf/parser#duplicated-enum-values
Message: Each enum value must be unique, 'URANUS' it's not
Severity: Violation
Target: file://amf-cli/shared/src/test/resources/graphql/tck/apis/invalid/duplicate-values-enum.api.graphql#/declares/scalar/Planet
Property: http://www.w3.org/ns/shacl#in
Range: [(1,0)-(1,0)]
Location: