diff --git a/README.md b/README.md index fc2b5e20..7818d97a 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ Allows to validate RAML files and generate code from them Commands: generate Generate source code from a RAML specification. verify Allows to verify if a raml spec is valid. + validate Validate the raml spec against defined rules and generate a validation report. + ``` Generating Client SDKs or normalized RAML for documentation: @@ -83,7 +85,7 @@ Generate source code from a RAML specification. ``` -Validating a RAML API: +Verifying a RAML API: ``` Usage: rmf-codegen verify [-hw] @@ -93,6 +95,31 @@ Allows to verify if a RAML spec is valid. -w, --watch Watches the files for changes ``` +Validating a RAML API and generating a validity report: + +``` +Usage:
validate [-hvw] [--list-rules] [-f=] +[-l=] [-lf=] +[-o=] [-r=] +[-s=] [-t=] + + +Allows to verify if a raml spec is valid according to CT guidelines and generates a validation report + + Api file location + -f, --format= Specifies the output format. Valid values: CLI, JSON, MARKDOWN + -h, --help display this help message + -l, --link-base= + -lf, --link-format= Specifies the link format. Valid values: CLI, GITHUB + --list-rules Show all rules + -o, --outputTarget= + -r, --ruleset= Ruleset configuration + -s, --severity= Diagnostic severity. Valid values: info, warn, error + -t, --temp= Temporary folder + -v, --verbose Verbose + -w, --watch Watches the files for changes +``` + ## Development ### Build the "fat jar" diff --git a/ctp-validators/src/main/README.md b/ctp-validators/src/main/README.md index 808f0fcf..f95a2a53 100644 --- a/ctp-validators/src/main/README.md +++ b/ctp-validators/src/main/README.md @@ -17,4 +17,6 @@ This rule states that all resources should be plural. But some Platform APIs don't follow this and they are explicitly excluded. -* Add the new validator rule in this [folder](https://github.com/commercetools/rmf-codegen/tree/main/ctp-validators/src/main/kotlin/com/commercetools/rmf/validators). A new `case` method should be added to the rule class which validates the test examples added above. \ No newline at end of file +* Add the new validator rule in this [folder](https://github.com/commercetools/rmf-codegen/tree/main/ctp-validators/src/main/kotlin/com/commercetools/rmf/validators). A new `case` method should be added to the rule class which validates the test examples added above. + +* Add a description of the rule in this [page](https://github.com/commercetools/commercetools-docs/tree/main/api-specs#validator-rules) \ No newline at end of file diff --git a/ctp-validators/src/main/kotlin/com/commercetools/rmf/validators/BooleanPropertyNameRule.kt b/ctp-validators/src/main/kotlin/com/commercetools/rmf/validators/BooleanPropertyNameRule.kt new file mode 100644 index 00000000..a92e92e6 --- /dev/null +++ b/ctp-validators/src/main/kotlin/com/commercetools/rmf/validators/BooleanPropertyNameRule.kt @@ -0,0 +1,46 @@ +package com.commercetools.rmf.validators + +import io.vrap.rmf.raml.model.types.* +import org.eclipse.emf.common.util.Diagnostic +import java.util.* + +@ValidatorSet +class BooleanPropertyNameRule(severity: RuleSeverity, options: List? = null) : TypesRule(severity, options) { + + private val exclude: List = + (options?.filter { ruleOption -> ruleOption.type.lowercase(Locale.getDefault()) == RuleOptionType.EXCLUDE.toString() }?.map { ruleOption -> ruleOption.value }?.plus("") ?: defaultExcludes) + + override fun caseObjectType(type: ObjectType): List { + val validationResults: MutableList = ArrayList() + + type.properties.filter { property -> property.type.isBoolean() }.forEach { property -> + if (exclude.contains(property.name).not()) { + if (property.name.matches(Regex("^is[A-Z].*$"))) { + validationResults.add(error(type, "Property \"{0}\" of type \"{1}\" must not have \"is\" as a prefix", property.name, type.name)) + } + } + } + return validationResults + } + + private fun AnyType.isBoolean(): Boolean { + return when(this) { + is BooleanType -> true + else -> false + } + } + + companion object : ValidatorFactory { + private val defaultExcludes by lazy { listOf("") } + + @JvmStatic + override fun create(options: List?): BooleanPropertyNameRule { + return BooleanPropertyNameRule(RuleSeverity.ERROR, options) + } + + @JvmStatic + override fun create(severity: RuleSeverity, options: List?): BooleanPropertyNameRule { + return BooleanPropertyNameRule(severity, options) + } + } +} diff --git a/ctp-validators/src/test/groovy/com/commercetools/rmf/validators/ValidatorRulesTest.groovy b/ctp-validators/src/test/groovy/com/commercetools/rmf/validators/ValidatorRulesTest.groovy index 03bd5422..5a5bdc76 100644 --- a/ctp-validators/src/test/groovy/com/commercetools/rmf/validators/ValidatorRulesTest.groovy +++ b/ctp-validators/src/test/groovy/com/commercetools/rmf/validators/ValidatorRulesTest.groovy @@ -37,6 +37,17 @@ class ValidatorRulesTest extends Specification implements ValidatorFixtures { result.validationResults[11].message == "Property \"fooTimeUntil\" of type \"InvalidTimeRangeUntil\" indicates a range, property ending with \"From\" is missing" } + def "boolean property name rule"() { + when: + def validators = Arrays.asList(new TypesValidator(Arrays.asList(BooleanPropertyNameRule.create(emptyList())))) + def uri = uriFromClasspath("/boolean-property-name-rule.raml") + def result = new RamlModelBuilder(validators).buildApi(uri) + then: + result.validationResults.size == 2 + result.validationResults[0].message == "Property \"isBad\" of type \"InvalidBoolean\" must not have \"is\" as a prefix" + result.validationResults[1].message == "Property \"isNotGood\" of type \"InvalidBoolean\" must not have \"is\" as a prefix" + } + def "discriminator name rule"() { when: def validators = Arrays.asList(new TypesValidator(Arrays.asList(DiscriminatorNameRule.create(emptyList())))) diff --git a/ctp-validators/src/test/resources/boolean-property-name-rule.raml b/ctp-validators/src/test/resources/boolean-property-name-rule.raml new file mode 100644 index 00000000..05682615 --- /dev/null +++ b/ctp-validators/src/test/resources/boolean-property-name-rule.raml @@ -0,0 +1,49 @@ +#%RAML 1.0 +title: boolean property name rule + +annotationTypes: + package: string + sdkBaseUri: string + +baseUri: https://api.europe-west1.commercetools.com + +types: + InvalidBoolean: + (package): Common + type: object + properties: # invalid because it is a boolean that is prefixed with "is" + isBad: + description: xyz + type: boolean + isNotGood: boolean + ValidBoolean: + (package): Common + type: object + properties: + /a-z/: # rule can't apply on dynamic properties + description: xyz + type: boolean + good: # name is not prefixed with "is" + description: xyz + type: boolean + alsoGood: boolean + isolated: # name starts with "is" but not as a prefix + description: xyz + type: boolean + ValidNonBoolean: + (package): Common + type: object + properties: # type is not a boolean + isFine: + description: xyz + type: string + isAlsoFine: number + ValidBooleanArray: + (package): Common + type: object + properties: + isGoods: + description: xyz + type: array + items: boolean +