WSO2 Rule Validator¶
- WSO2 Rule Validator is a JSON/YAML linter that is a Java implementation of Stoplight Spectral. https://github.com/stoplightio/spectral
- This validator is automatically called by the APIM Governance feature to validate different artifacts. (Open API Specifications, API Metadata files, etc.)
- Any valid JSON or YAML file can be validated by providing a valid Spectral ruleset.
Features and Limitations¶
WSO2 Rule validator supports most features that Spectral itself does. With the exception of a few listed below.
- given path is a JSON Path. But currently WSO2 Rule Validator does not support JSON Path Plus features, even though Spectral does.
- Object access should always be done inside single quotes (
['paths']['order']\
) - Comma separated object access is not supported. (
$['paths'][*]['get', 'put', 'post']
)
- Object access should always be done inside single quotes (
- All core functions except UnreferencedReusableObject and typedEnum are supported.
- Custom functions are not supported.
- References ($ref) are not supported.
- The core function "pattern" allows you to define regex patterns and check whether a certain lint target matches it or not.
- In YAML rulesets, always define the regex pattern inside single quotes.
- All regex should be valid Java regex, which is different from JavaScript regex used in Stoplight Spectral.
- Extends and overrides are currently not supported
- Parser options are not supported
- Only Async API and Open API are supported (all versions)
Terms¶
- Ruleset - Contains all the rules, aliases, and formats.
- Document - The target document that should be validated against the ruleset. Mostly API specifications.
- Target - The part of the document which is identified by the
given
andfield
properties of a rule. The target can be of any type, an object, array, or a primitive.
Usage¶
1. Writing rulesets¶
Following is a simple ruleset.
aliases:
API_Server_URL:
- "$.servers[*].url"
formats:
- oas3_0
- oas3_1
rules:
server-lowercase:
given:
- "#API_Server_URL"
severity: error
then:
function: pattern
field: "url"
functionOptions:
match: '^[^A-Z]*$'
formats:
- oas3
aliases
- This is a way to reuse pre-defined given paths instead of redefining them for each rule. More information in the Aliases section.formats
- The formats of documents to which this ruleset should be applied. This ruleset will not be applied to any documents that are not in the listed formats. These ruleset level formats can be overridden by rule level formats.rules
- Definitions of rules. More information in the Rules section.
2. Writing rules¶
Following is a simple rule. These rules have been extracted from the wso2_rest_api_design_guidelines.yaml
ruleset provided as a default ruleset with WSO2 API Manager 4.5.0 onwards.
server-lowercase:
given:
- "$.servers[*]"
severity: error
then:
function: pattern
field: "url"
functionOptions:
match: '^[^A-Z]*$'
description: |-
Server URLs must be lowercase. This standard helps meet industry best practices.
**Invalid Example**
The `url` property uses uppercase letters.
```json
{
"servers": [
{
"url": "https://ACME.com/api"
}
]
}
```
**Valid Example**
The `url` property is fully lowercase.
```json
{
"servers": [
{
"url": "https://acme.com/api"
}
]
}
```
message: Server URL must be lowercase.
formats:
- oas3
Following table contains a summary of the fields in a rule.
Field name | Required | Type |
---|---|---|
given | Yes | Json path string, or an array of json paths. |
then | Yes | Object with the properties: function , field , and functionOptions . |
function | Yes | String. Name of a valid core function |
field | No | String. Sub target. |
functionOptions | No | Object or values required for the function |
description | No | String |
message | No | String with optional valid placeholders |
severity | No | String with a valid severity |
formats | No | Array of strings of valid formats |
Following is an explanation on the attributes of a rule.
given
(required) - The part of the document (the target) the rule should be applied to. Written using Json Path syntax. Can be an array of strings or a single stringthen
(required) - Describes which function and how it should be applied to the target. Can be an array of objects or a single object containing the following fields.function
(required) - which of the core functions should be applied. More information in the Core Functions section.field
- which sub part of the target should the function be applied to. If omitted, the function is applied to the entire target. The field can be,@key
- The function will be applied to the key of the target.- A property - A property of the target.
- A Json Path - This Json Path will further traverse the target.
functionOptions
- Additional information that is required by the function.
description
- Description of the rulemessage
- The message that is passed if the rule is violated. Can be customized using the following placeholders.{{ error }}
{{ description }}
{{ path }}
{{ property }}
{{ value }}
severity
- The severity of the rule. The default value iswarn
. Can be one of the following. If the severity isoff
, the rule will not be applied. Other values do not affect the execution of the rule.error
warn
info
hint
off
formats
- The rule will only apply if the target document is of one of the following defined formats.aas2
(AsyncAPI v2.x)aas2_0
(AsyncAPI v2.0.0)aas2_1
(AsyncAPI v2.1.0)aas2_2
(AsyncAPI v2.2.0)aas2_3
(AsyncAPI v2.3.0)aas2_4
(AsyncAPI v2.4.0)aas2_5
(AsyncAPI v2.5.0)aas2_6
(AsyncAPI v2.6.0)aas3
(AsyncAPI v3.x)aas3_0
(AsyncAPI v3.0.0)oas2
(OpenAPI v2.0)oas3
(OpenAPI v3.x)oas3_0
(OpenAPI v3.0.x)oas3_1
(OpenAPI v3.1.x)
The combination of given
and then
should be constructed carefully. If the provided paths are not present in the document it will not give out an error, but simply the rule will not run.
For example, in the following document, separate rules should be written for each level. If we need to write a rule to validate the existence of the description
, it will run only if the parent is present. The validation of the parent should be a separate rule.
paths:
/order:
post:
operationId: orderPizza
description: Create a new Order
requestBody:
$ref: "#/components/requestBodies/Order"
3. Core functions¶
Below is a list of core functions that are supported by the rule validator, with a summary of each of its options.
Function Name | Function Description | Function Options | Type | Required | Option Description |
---|---|---|---|---|---|
alphabetical | Make sure an object or an array is sorted. If keyedBy is provided the target should contain an array of objects, and the order is deduced using the key. | keyedBy | String | No | The key by which the objects are sorted. |
enumeration | Does the field value exist in this set of possible values? | values | List of strings or numbers | Yes | The list of possible values to search the membership of the target. |
falsy | Is the value a boolean false, 0, null, undefined, or an empty string ("")? | ||||
length | Count the length of a string or an array, the number of properties in an object, or a numeric value, and define minimum and/or maximum values. | min | number | No | At least one of the min and max values has to be provided. Both can be provided as well. If both are provided, the target should be smaller than the min value and larger than the max value. |
max | number | No | |||
pattern | Check if part of the target string matches with a regex pattern. | match | String (valid regex) | No | Either match or notMatch has to be provided. Both can also be provided. If both are provided, the function checks whether the target matches the match regex and it doesn't match the notMatch regex. |
notMatch | String (valid regex) | No | |||
casing | Text must match a certain predefined case. | type | String (valid case) | Yes | Can be one of flat (verylongname), camel (veryLongName), pascal (VeryLongName), kebab (very-long-name), cobol (VERY-LONG-NAME), snake (very_long_name), macro (VERY_LONG_NAME). |
disallowDigits | boolean | No | Can the target not contain numbers? | ||
separator | Object which contains the following values | No | Contains the following sub options. This object (the next 2 options) can be leveraged to accomplish custom cases. | ||
separator.char | String (single character) | No | Custom character to separate groups of words. | ||
separator.allowLeading | boolean | No | Can the separator character be used as the first character? | ||
schema | Check if the target is defined according to the given schema. | schema | A valid schema | Yes | A valid json or yaml schema object. |
truthy | Is the value not a boolean false, 0, null, undefined, or an empty string ("")? | ||||
defined | The `field` should be defined in the `then` object when using this function. Checks if the target value object contains the property defined by the `field`. | ||||
undefined | The `field` should be defined in the `then` object when using this function. Checks if the target value object doesn't contain the property defined by the `field`. | ||||
xor | One of the properties should be defined in the target object. But not more than one is allowed to be defined. |
Examples¶
- Alphabetical If the above ruleset is used to validate the below document, it will fail because the tag objects are not sorted according to their names' alphabetical order.
This function can be used without keyedBy
for simple arrays or objects.
-
Enumeration
The above rule will check all tags whether they are one of the values defined inwhitelisted-tags: description: Pick from a very restrictive set of tags. given: "$.paths.*.tags.*" then: function: enumeration functionOptions: values: - users - articles - categories
values
. -
falsy
Simply checks whether the targets selected by the given path are falsy. -
Length
Checks whether operation tags contain between 1 to 999 tags. - If the target is a string, it will consider the length of the string.
- If the target is an array, it will consider the number of elements.
- If the target is an object, it will consider the number of properties.
-
If the target is a number, it will simply consider its value.
-
Pattern
In this example, the function will check whether the target does not match the given regex. Ifpath-declarations-must-exist: given: - "#Path_Object" severity: error then: function: pattern functionOptions: notMatch: '\{\}' field: "@key"
match
is provided, it will also check whether the target matches with that particular regex. -
Casing
The above will create a custom casing function where it allows strings likepath-kebab-casing: given: - "$.paths[*]" then: function: casing functionOptions: type: kebab disallowDigits: false separator: char: "*" allowLeading: true
*very*lo3ng*word
. -
Schema
The above rule will check whether the tags object is an array, and it contains at least 1 element.
The schema function is the most powerful and expressive one of the core functions.
-
Truthy
Simply checks whether the targets selected by the given path is truthy. -
Defined
Simply checks if the document root object has the propertytags
defined. -
Undefined
Simply checks if the document root object does not have the propertytags
defined. -
Xor
Checks if all the examples contains one of the 3 properties provided.
4. Aliases¶
- Aliases are defined for commonly used JSONPath expressions on a global level and then reused across the ruleset.
- It's similar to
given
but with more capabilities. - Aliases can be recursive. That is one alias can use another alias. But the references should never be circular.
- When using the aliases either in other aliases or rulesets, the alias can be extended by thinking of it as a Json Path, which it will resolve to during runtime.
2 types of aliases can be defined. 1. Simple aliases - Each alias is a list of Json Paths. They will simply replace the given clauses where the alias is called.
aliases:
Path_Item:
- "$.paths[*]"
- "$.hidden.paths[*]"
Operation_Object:
- "#Path_Item['get']"
- "#Path_Item['put']"
- "#Path_Item['post']"
- "#Path_Item['delete']"
- "#Path_Item['options']"
- "#Path_Item['head']"
- "#Path_Item['patch']"
- "#Path_Item['trace']"
rules:
operation-description:
given:
- "#Operation_Object.description"
severity: warn
then:
function: truthy
message: Operation "description" should be present and non-empty string.
- Complex aliases - Complex aliases or scoped aliases allow you to define formats and given paths for each set of formats.
Building¶
- Build using
mvn clean install
. - Use the
.jar
file generated incomponent/target
. - Use the following maven dependency
xml <dependency> <groupId>org.wso2.carbon</groupId> <artifactId>org.wso2.rule.validator</artifactId> <version>${rule.validator.version}</version> </dependency>
- Read the ruleset file into a string and call the
validateRuleset
method to validate the ruleset. - Read the target document file and the ruleset into strings and call the
validateDocument
method to validate the document against the ruleset.