extends: [[spectral:oas, all], [spectral:asyncapi, all]] # https://docs.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules # https://docs.stoplight.io/docs/spectral/1e63ffd0220f3-async-api-rules documentationUrl: https://github.com/adidas/api-guidelines/blob/master/ruleset.md rules: operation-tags: off operation-operationId: off operation-success-response: error # ----------------------------# # Adidas OAS v2.0, v3.0 rules # # ----------------------------# adidas-paths-kebab-case: description: All YAML/JSON paths MUST follow kebab-case severity: warn recommended: true message: "{{property}} is not kebab-case: {{error}}" given: $.paths[*]~ then: function: pattern functionOptions: match: "^\/([a-z0-9]+(-[a-z0-9]+)*)?(\/[a-z0-9]+(-[a-z0-9]+)*|\/{.+})*$" # doesn't allow /asasd{asdas}sadas pattern or not closed braces adidas-path-parameters-camelCase-alphanumeric: description: Path parameters MUST follow camelCase severity: warn recommended: true message: "{{property}} path parameter is not camelCase: {{error}}" given: $..parameters[?(@.in == 'path')].name then: function: pattern functionOptions: match: "^[a-z][a-zA-Z0-9]+$" adidas-definitions-camelCase-alphanumeric: description: All YAML/JSON definitions MUST follow fields-camelCase and be ASCII alphanumeric characters or `_` or `$`. severity: error recommended: true message: "{{property}} MUST follow camelCase and be ASCII alphanumeric characters or `_` or `$`." given: $.definitions[*]~ then: function: pattern functionOptions: match: "/^[a-z$_]{1}[A-Z09$_]*/" adidas-properties-camelCase-alphanumeric: description: All JSON Schema properties MUST follow fields-camelCase and be ASCII alphanumeric characters or `_` or `$`. severity: error recommended: true message: "{{property}} MUST follow camelCase and be ASCII alphanumeric characters or `_` or `$`." given: $.definitions..properties[*]~ then: function: pattern functionOptions: match: "/^[a-z$_]{1}[A-Z09$_]*/" adidas-headers-hyphenated-pascal-case: description: All `HTTP` headers MUST use `Hyphenated-Pascal-Case` notation severity: error given: "$..parameters[?(@.in == 'header')].name" message: "'HTTP' headers MUST follow 'Hyphenated-Pascal-Case' notation" recommended: true type: style then: function: pattern functionOptions: match: "/^([A-Z][a-z0-9]-)*([A-Z][a-z0-9])+/" # ----------------------# # Adidas OAS v2.0 rules # # ----------------------# adidas-oas2-protocol-https-only: description: "ALL requests MUST go through `https` protocol only" formats: - oas2 recommended: true severity: error type: "style" message: "Schemes MUST be https and no other value is allowed." given: $ then: field: schemes function: schema functionOptions: schema: type: array items: type: string enum: ["https"] maxItems: 1 adidas-oas2-request-support-json: description: Every request SHOULD support `application/json` media type formats: - oas2 severity: warn message: "{{description}}: {{error}}" recommended: true given: "$..consumes" then: function: schema functionOptions: schema: type: array contains: type: string enum: - application/json adidas-oas2-example-exists-in-parameters: description: All models MUST have a valid example. severity: error recommended: true formats: - oas2 message: "{{ property }} MUST have a valid example." given: "$..parameters..[?(@.in == 'body' && (@.example || @.schema.$ref))]" then: function: truthy # example-exists-in-definitions covery by oas2-valid-media-example adidas-oas2-response-success-hal: # schemes and/or produces description: "All success responses MUST be of media type `application/hal+json`" severity: error given: $.paths..responses[?( @property >= 200 && @property < 300 && @property != 204)] recommended: true type: "style" formats: - oas2 message: "Response documents MUST follow application/hal+json: {{error}}" then: field: schema function: schema functionOptions: schema: $ref: "./supermodel/adidas/api/HAL.yaml" adidas-oas2-response-error-problem: # schemas and/or produces description: All error responses MUST be of media type `application/problem+json` severity: error formats: - oas2 given: $.paths..responses[?( @property >= 400 && @property < 600 )] recommended: true type: "style" message: "Error response document MUST follow application/problem+json: {{error}}" then: field: schema.example function: schema functionOptions: schema: $ref: "./supermodel/adidas/api/ProblemDetail.yaml" # ----------------------# # Adidas OAS v3.0 rules # # ----------------------# adidas-oas3-request-support-json: description: Every request MUST support `application/json` media type formats: - oas3 recommended: true severity: error message: "{{description}}: {{error}}" given: $.paths.[*].requestBody.content[?(@property.indexOf('json') === -1)]^ then: function: falsy # adidas-oas3-valid-example-in-parameters && adidas-oas3-valid-example-in-definitions covered by oas3-valid-media-example adidas-oas3-protocol-https-only: description: "ALL requests MUST go through `https` protocol only" formats: - oas3 recommended: true severity: error message: "Servers MUST be https and no other protocol is allowed." given: $.servers..url then: function: pattern functionOptions: match: "/^https:/" adidas-oas3-response-success-hal: description: "All success responses MUST be of media type `application/hal+json` " severity: error given: $.paths..responses[?( @property >= 201 && @property < 300 && @property != 204)].content[*]~ recommended: true formats: - oas3 message: "Response documents MUST be of application/hal+json media types: {{error}}" then: function: enumeration functionOptions: values: - application/hal+json # sync and async patterns that can return hal OR problem+json adidas-oas3-response-success-OK: description: "All success responses MUST be of media type `application/hal+json` or `application/problem+json`" severity: error given: $.paths..responses[?( @property == 200 )].content[*]~ recommended: true formats: - oas3 message: "Response documents MUST be of application/hal+json or application/problem+json media types: {{error}}" then: function: enumeration functionOptions: values: - application/hal+json - application/problem+json adidas-oas3-response-success-hal-body: # schemes and/or produces description: "All success responses MUST follow `application/hal+json` schema" severity: error given: $.paths..responses[?( @property == 200 && @property < 300 && @property != 204)].content[?(@property === "application/hal+json")] recommended: true type: "style" formats: - oas3 message: "Response documents MUST follow application/hal+json schema: {{error}}" then: field: schema function: schema functionOptions: schema: $ref: "./supermodel/adidas/api/HAL.yaml" # API Maturity Matrix Rule # 1/1-1/01 adidas-oas3-no-get-request-body: description: "GET requests MUST NOT have a request body." severity: error recommended: true formats: - oas2 - oas3 given: "$.paths[*].get.requestBody" then: function: falsy message: "{{description}}: {{error}}" # 1/1-1/02 adidas-oas3-not-post-to-get-info: description: "POST requests SHOULD NOT be used for retrieving information. Use GET instead." severity: error recommended: true formats: - oas2 - oas3 given: "$.paths[*].post" then: field: "summary" function: pattern functionOptions: notMatch: "(retrieve|fetch|get|read)" message: "{{description}}: {{error}}" # 1/1-1/03 adidas-oas3-put-with-request-body: description: "PUT requests MUST have a request body." severity: error recommended: true formats: - oas2 - oas3 given: "$.paths[*].put" then: field: "requestBody" function: truthy message: "{{description}}: {{error}}" # 1/1-1/04 adidas-oas3-delete-with-request-body: description: "DELETE requests MUST NOT have a request body." severity: error recommended: true formats: - oas2 - oas3 given: "$.paths[*].delete.requestBody" then: function: falsy message: "{{description}}: {{error}}" # 1/1-1/05 adidas-oas3-no-verbs-in-paths: description: "API paths MUST be resource-focused and MUST NOT include verbs like 'get', 'update', 'create', or 'delete'." severity: warn recommended: true formats: - oas2 - oas3 message: "Path '{{path}}' includes a verb (e.g., 'get', 'update', 'create', 'delete'). API paths SHOULD be resource-focused." given: $.paths[*]~ then: function: pattern functionOptions: notMatch: "/\\b(get|update|create|delete|fetch|retrieve)\\b/" # 1/1-1/06 adidas-oas3-real-like-examples: description: "API design SHOULD include real-like examples for request and response definitions." severity: warn recommended: true formats: - oas2 - oas3 message: "The {{property}} SHOULD include a real-like example. Add realistic examples to improve API usability." given: "$..[?(@.example || @.examples)]" then: field: "example" function: truthy # 1/1-1/07 adidas-oas3-stable-semantic-version: description: "The API contract MUST have a stable version and MUST follow semantic versioning (e.g., '1.0.0'). Words like 'SNAPSHOT' or 'RELEASE' are not allowed." severity: error recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$.info.version" then: function: pattern functionOptions: match: "^(?!.*\\b(SNAPSHOT|RELEASE)\\b)(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$" # 1/1-1/08 adidas-oas3-security-section-required: description: "The API contract MUST include a 'security' section at the root level." severity: error recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$" then: field: "security" function: truthy adidas-oas3-components-required: description: "The API contract MUST include a 'components' section." severity: error recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$" then: field: "components" function: truthy adidas-oas3-security-schemes-required: description: "The API contract MUST include a 'securitySchemes' subsection under the 'components' section." severity: error recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$.components" then: field: "securitySchemes" function: truthy # 1/1-1/09 adidas-oas3-x-leanixid-required: description: "The API contract SHOULD include a custom field 'x-leanixid' in the 'info' section." severity: warn recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$.info" then: field: "x-leanixid" function: truthy # 1/1-1/10 adidas-oas3-x-leanixid-uuid: description: "The API contract SHOULD include a custom field 'x-leanixid' containing a valid UUID." severity: warn recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$.info.x-leanixid" then: function: pattern functionOptions: match: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" # 1/1-1/10B # adidas-oas3-x-leanixid-real: # description: "The leanxid must be real in the adidas Leanix inventory." # severity: error # recommended: true # formats: # - oas2 # - oas3 # message: "{{description}}: {{error}}" # given: "$.info.x-leanixid" # then: # function: customFunction # 1/1-1/11 adidas-oas3-x-gateway-required: description: "The API contract SHOULD include a custom field 'x-gateway' in the 'info' section." severity: warn recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$.info" then: field: "x-gateway" function: truthy # 1/1-1/11B adidas-oas3-x-gateway-required-enumeration: description: "The 'x-gateway' property, if present, MUST have a value in the enumeration: kong, nginx, aws, akamai, sap, other." severity: warn recommended: true formats: - oas2 - oas3 given: "$" then: field: "x-gateway" function: enumeration functionOptions: values: - kong - nginx - aws - akamai - sap - other # 1/1-1/12 adidas-oas3-hypermedia-links-required: description: "The API contract MAY include hypermedia links to represent the state of resources and be navigable." severity: hint recommended: true formats: - oas2 - oas3 message: "{{description}}: {{error}}" given: "$.paths[*][*].responses[*]" then: field: "links" function: truthy # --------------------------------------------------------------------------- # Not implemented # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # Other rules which are redundant or not feasible # --------------------------------------------------------------------------- # fields-date-iso8601: # description: Date and time MUST follow [`ISO 8601` standard](https://www.iso.org/iso-8601-date-and-time-format.html) # severity: error # fields-language-iso639: # description: Language codes MUST follow [`ISO 639` standard](https://www.iso.org/iso-639-language-codes.html) # severity: error # fields-country-iso3166: # description: Country codes MUST follow [`ISO 3166 alpha-2` standard](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) # severity: error # fields-currency-iso4217: # description: Currency codes MUST follow [`ISO 4217` standard](https://en.wikipedia.org/wiki/ISO_4217) # severity: error # response-303-async-link-header: # description: A successful and finished async api request returns `303` response code and sends the target resource location in the `Link` header # severity: hint