extends: - [spectral:oas, recommended] # Spectral ruleset for API Snap (https://api-snap.com/) # Enforces the conventions observed across the api-snap-openapi.yml spec: # - OpenAPI 3.0.3 # - Single base server: https://api-snap.com # - All paths under /api with kebab-case (jwt-decode) # - camelCase operationIds with verb prefixes (generate*, capture*, decode*, convert*, extract*, hash*, resize*) # - PascalCase tags (Images, Browser, Documents, Security, Utilities) # - snake_case query parameters (full_page) and camelCase JSON body properties (urlSafe, expiresAt) # - Bearer auth via Authorization header (snp_ prefix) with api_key query alias # - Standard error response set: 400, 401, 429 rules: # ---------------------------------------------------------------- # INFO / METADATA # ---------------------------------------------------------------- info-title-required: description: Spec must declare an info.title. given: $.info severity: error then: field: title function: truthy info-description-required: description: Spec must declare an info.description with at least 60 characters. given: $.info severity: warn then: field: description function: length functionOptions: min: 60 info-version-required: description: Spec must declare info.version. given: $.info severity: error then: field: version function: truthy info-contact-required: description: Spec must declare info.contact with name and url. given: $.info.contact severity: warn then: - field: name function: truthy - field: url function: truthy # ---------------------------------------------------------------- # OPENAPI VERSION # ---------------------------------------------------------------- openapi-version-3-0-3: description: API Snap uses OpenAPI 3.0.3. given: $.openapi severity: warn then: function: pattern functionOptions: match: ^3\.0\.\d+$ # ---------------------------------------------------------------- # SERVERS # ---------------------------------------------------------------- servers-defined: description: At least one server must be defined. given: $ severity: error then: field: servers function: truthy servers-https: description: All servers must use HTTPS. given: $.servers[*].url severity: error then: function: pattern functionOptions: match: ^https:// servers-api-snap: description: Production server should be https://api-snap.com. given: $.servers[*].url severity: info then: function: pattern functionOptions: match: ^https://api-snap\.com # ---------------------------------------------------------------- # PATHS - NAMING CONVENTIONS # ---------------------------------------------------------------- paths-api-prefix: description: All paths must be prefixed with /api/. given: $.paths severity: error then: field: '@key' function: pattern functionOptions: match: ^/api/ paths-kebab-case: description: Path segments must be lowercase kebab-case (e.g. /api/jwt-decode). given: $.paths severity: warn then: field: '@key' function: pattern functionOptions: match: ^/[a-z0-9/-]+$ paths-no-trailing-slash: description: Paths must not end with a trailing slash. given: $.paths severity: warn then: field: '@key' function: pattern functionOptions: notMatch: .+/$ # ---------------------------------------------------------------- # OPERATIONS # ---------------------------------------------------------------- operation-summary-required: description: Every operation must have a summary in Title Case. given: $.paths[*][get,post,put,patch,delete] severity: error then: field: summary function: truthy operation-description-required: description: Every operation should have a description. given: $.paths[*][get,post,put,patch,delete] severity: warn then: field: description function: truthy operation-id-required: description: Every operation must have an operationId. given: $.paths[*][get,post,put,patch,delete] severity: error then: field: operationId function: truthy operation-id-camel-case: description: operationId must be camelCase (e.g. generateQrCode, captureScreenshot). given: $.paths[*][get,post,put,patch,delete].operationId severity: warn then: function: pattern functionOptions: match: ^[a-z][a-zA-Z0-9]+$ operation-id-verb-prefix: description: operationId should start with a recognized verb (generate, capture, convert, hash, resize, decode, extract, base64). given: $.paths[*][get,post,put,patch,delete].operationId severity: info then: function: pattern functionOptions: match: ^(generate|capture|convert|hash|resize|decode|extract|base64|html|markdown) operation-tags-required: description: Every operation must declare at least one tag. given: $.paths[*][get,post,put,patch,delete] severity: warn then: field: tags function: length functionOptions: min: 1 # ---------------------------------------------------------------- # TAGS # ---------------------------------------------------------------- tags-pascal-case: description: Tags must use PascalCase (e.g. Images, Browser, Documents, Security, Utilities). given: $.paths[*][get,post,put,patch,delete].tags[*] severity: warn then: function: pattern functionOptions: match: ^[A-Z][a-zA-Z]+$ tags-from-allowed-set: description: Tags should be one of the documented categories. given: $.paths[*][get,post,put,patch,delete].tags[*] severity: info then: function: enumeration functionOptions: values: - Images - Browser - Documents - Security - Utilities # ---------------------------------------------------------------- # PARAMETERS # ---------------------------------------------------------------- parameter-description-required: description: Every parameter must have a description. given: $.paths[*][get,post,put,patch,delete].parameters[*] severity: warn then: field: description function: truthy parameter-snake-case: description: Query parameters use snake_case (full_page) when multi-word. given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in=='query')].name severity: info then: function: pattern functionOptions: match: ^[a-z][a-z0-9_]*$ parameter-api-key-allowed: description: api_key query parameter is permitted as an Authorization header alias. given: $.paths[*][get,post,put,patch,delete].parameters[?(@.name=='api_key')] severity: info then: function: truthy # ---------------------------------------------------------------- # REQUEST BODIES # ---------------------------------------------------------------- request-body-content-required: description: Request bodies must declare a content map. given: $.paths[*][post,put,patch].requestBody severity: error then: field: content function: truthy request-body-json-or-multipart: description: Request bodies should use application/json or multipart/form-data. given: $.paths[*][post,put,patch].requestBody.content severity: warn then: field: '@key' function: pattern functionOptions: match: ^(application/json|multipart/form-data)$ # ---------------------------------------------------------------- # RESPONSES # ---------------------------------------------------------------- response-200-required: description: Every operation must declare a 200 success response. given: $.paths[*][get,post,put,patch,delete].responses severity: error then: field: '200' function: truthy response-401-required: description: Every operation must declare a 401 response (missing/invalid API key). given: $.paths[*][get,post,put,patch,delete].responses severity: warn then: field: '401' function: truthy response-429-required: description: Every operation must declare a 429 response (rate limit exceeded). given: $.paths[*][get,post,put,patch,delete].responses severity: warn then: field: '429' function: truthy response-description-required: description: Each response must have a description. given: $.paths[*][get,post,put,patch,delete].responses[*] severity: warn then: field: description function: truthy # ---------------------------------------------------------------- # SCHEMAS - PROPERTY NAMING # ---------------------------------------------------------------- schema-camel-case-properties: description: JSON body and response properties use camelCase (urlSafe, expiresAt). given: $.components.schemas[*].properties severity: info then: field: '@key' function: pattern functionOptions: match: ^[a-z][a-zA-Z0-9]*$ schema-type-required: description: Schema properties must declare a type or $ref. given: $.components.schemas[*].properties[*] severity: warn then: function: schema functionOptions: schema: anyOf: - required: [type] - required: [$ref] - required: [oneOf] - required: [anyOf] - required: [allOf] # ---------------------------------------------------------------- # SECURITY # ---------------------------------------------------------------- security-schemes-defined: description: components.securitySchemes must define at least one scheme. given: $.components severity: error then: field: securitySchemes function: truthy security-bearer-auth: description: BearerAuth security scheme must be defined for the snp_ API key. given: $.components.securitySchemes severity: warn then: field: BearerAuth function: truthy security-global-required: description: Top-level security must be declared and reference BearerAuth. given: $ severity: warn then: field: security function: truthy # ---------------------------------------------------------------- # HTTP METHOD CONVENTIONS # ---------------------------------------------------------------- get-no-request-body: description: GET operations must not declare a requestBody. given: $.paths[*].get severity: error then: field: requestBody function: falsy post-has-request-body: description: POST operations should declare a requestBody. given: $.paths[*].post severity: warn then: field: requestBody function: truthy # ---------------------------------------------------------------- # GENERAL QUALITY # ---------------------------------------------------------------- examples-encouraged: description: Operations should include at least one example for response or request. given: $.paths[*][get,post,put,patch,delete].responses[*].content[*] severity: info then: function: schema functionOptions: schema: anyOf: - required: [example] - required: [examples] microcks-extension-encouraged: description: Operations should include x-microcks-operation for mock-server compatibility. given: $.paths[*][get,post,put,patch,delete] severity: info then: field: x-microcks-operation function: truthy