# Spectral ruleset for the Programming Quotes API # Generated from /Users/kinlane/GitHub/all/programming-quotes/openapi/programming-quotes-openapi.yml # Conventions captured: # - kebab-case paths, no version prefix on canonical surface (`/api` is server-level) # - camelCase query parameters and body property names # - camelCase operationIds with verb prefixes (get/list/create/update/delete/issue/add/vote) # - Title-Case tags (Quotes, Authors, Authentication) # - JWT bearer auth on writes, public on reads # - Summaries prefixed with "Programming Quotes" extends: - spectral:oas rules: # ───────────────────────────────────────────────────────────── # INFO / METADATA # ───────────────────────────────────────────────────────────── info-title-format: description: Info title must start with "Programming Quotes". severity: warn given: $.info.title then: function: pattern functionOptions: match: '^Programming Quotes' info-description-min-length: description: Info description must be at least 50 characters. severity: warn given: $.info.description then: function: length functionOptions: min: 50 info-contact-required: description: Info contact block is required. severity: warn given: $.info then: field: contact function: truthy info-license-required: description: Info license block is required (community license is fine). severity: warn given: $.info then: field: license function: truthy # ───────────────────────────────────────────────────────────── # OPENAPI VERSION # ───────────────────────────────────────────────────────────── openapi-version-3-0-x: description: Must use OpenAPI 3.0.x. severity: error given: $.openapi then: function: pattern functionOptions: match: '^3\.0\.[0-9]+$' # ───────────────────────────────────────────────────────────── # SERVERS # ───────────────────────────────────────────────────────────── servers-defined: description: At least one server must be declared. severity: error given: $ then: field: servers function: truthy servers-https-only: description: All server URLs must be HTTPS. severity: error given: $.servers[*].url then: function: pattern functionOptions: match: '^https://' servers-description-required: description: Each server must have a description. severity: warn given: $.servers[*] then: field: description function: truthy # ───────────────────────────────────────────────────────────── # PATHS — NAMING CONVENTIONS # ───────────────────────────────────────────────────────────── paths-kebab-case: description: Path segments must be kebab-case (lowercase, hyphens). Path params allowed. severity: warn given: $.paths[*]~ then: function: pattern functionOptions: match: '^(/([a-z0-9]+(-[a-z0-9]+)*|\{[a-zA-Z][a-zA-Z0-9]*\}))+$' paths-no-trailing-slash: description: Paths must not end with a trailing slash. severity: error given: $.paths[*]~ then: function: pattern functionOptions: notMatch: '.+/$' paths-no-query-strings: description: Paths must not contain query strings. severity: error given: $.paths[*]~ then: function: pattern functionOptions: notMatch: '\?' # ───────────────────────────────────────────────────────────── # OPERATIONS # ───────────────────────────────────────────────────────────── operation-summary-required: description: Every operation must have a summary. severity: error given: $.paths[*][get,post,put,patch,delete] then: field: summary function: truthy operation-description-required: description: Every operation must have a description. severity: warn given: $.paths[*][get,post,put,patch,delete] then: field: description function: truthy operation-summary-provider-prefix: description: Operation summaries should start with "Programming Quotes". severity: warn given: $.paths[*][get,post,put,patch,delete].summary then: function: pattern functionOptions: match: '^Programming Quotes ' operation-operationid-required: description: Every operation must have an operationId. severity: error given: $.paths[*][get,post,put,patch,delete] then: field: operationId function: truthy operation-operationid-camel-case: description: operationIds must be camelCase. severity: warn given: $.paths[*][get,post,put,patch,delete].operationId then: function: pattern functionOptions: match: '^[a-z][a-zA-Z0-9]*$' operation-operationid-verb-prefix: description: operationIds should start with a known verb (get, list, create, update, delete, add, vote, issue). severity: info given: $.paths[*][get,post,put,patch,delete].operationId then: function: pattern functionOptions: match: '^(get|list|create|update|delete|add|vote|issue)[A-Z]' operation-tags-required: description: Every operation must have at least one tag. severity: error given: $.paths[*][get,post,put,patch,delete] then: field: tags function: truthy # ───────────────────────────────────────────────────────────── # TAGS # ───────────────────────────────────────────────────────────── tags-global-defined: description: Global tags array must be defined. severity: warn given: $ then: field: tags function: truthy tags-title-case: description: Tag names must be Title Case single words (e.g., Quotes, Authentication). severity: warn given: $.tags[*].name then: function: pattern functionOptions: match: '^[A-Z][a-zA-Z]+( [A-Z][a-zA-Z]+)*$' tags-description-required: description: Each global tag must have a description. severity: warn given: $.tags[*] then: field: description function: truthy # ───────────────────────────────────────────────────────────── # PARAMETERS # ───────────────────────────────────────────────────────────── parameter-description-required: description: Every parameter must have a description. severity: warn given: $.paths[*][get,post,put,patch,delete].parameters[*] then: field: description function: truthy parameter-query-camel-case: description: Query parameters must be camelCase (matches `quotesPerPage`, `author`, `page`). severity: warn given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name then: function: pattern functionOptions: match: '^[a-z][a-zA-Z0-9]*$' parameter-schema-required: description: Every parameter must have a schema with a type. severity: error given: $.paths[*][get,post,put,patch,delete].parameters[*] then: field: schema function: truthy # ───────────────────────────────────────────────────────────── # REQUEST BODIES # ───────────────────────────────────────────────────────────── request-body-json-content-type: description: Request bodies must offer application/json. severity: error given: $.paths[*][post,put,patch].requestBody.content then: field: application/json function: truthy request-body-description-required: description: Request bodies should have a description. severity: warn given: $.paths[*][post,put,patch].requestBody then: field: description function: truthy # ───────────────────────────────────────────────────────────── # RESPONSES # ───────────────────────────────────────────────────────────── response-success-required: description: Every operation must define at least one 2xx response. severity: error given: $.paths[*][get,post,put,patch,delete].responses then: function: schema functionOptions: schema: type: object patternProperties: '^2[0-9]{2}$': {} minProperties: 1 response-401-on-writes: description: Write operations should document a 401 response. severity: warn given: $.paths[*][post,put,patch,delete].responses then: field: '401' function: truthy response-404-on-by-id: description: Operations on /quotes/{id} should document a 404. severity: warn given: $.paths[?(@property.match(/\{id\}/))][get,put,patch,delete].responses then: field: '404' function: truthy response-description-required: description: Every response must have a description. severity: error given: $.paths[*][get,post,put,patch,delete].responses[*] then: field: description function: truthy response-json-content-type: description: 2xx and 4xx responses should use application/json (204 may omit content). severity: warn given: $.paths[*][get,post,put,patch,delete].responses[?(@property.match(/^(2[0-35-9][0-9]|4[0-9]{2})$/))].content then: field: application/json function: truthy response-error-schema-has-message: description: Error response schemas should expose a `message` field. severity: warn given: $.components.schemas.ErrorResponse.properties then: field: message function: truthy # ───────────────────────────────────────────────────────────── # SCHEMAS — PROPERTY NAMING # ───────────────────────────────────────────────────────────── schema-property-camel-case: description: Schema properties must be camelCase (allow MongoDB `_id`). severity: warn given: $.components.schemas[*].properties[*]~ then: function: pattern functionOptions: match: '^(_id|[a-z][a-zA-Z0-9]*)$' schema-top-level-description: description: Top-level schemas must have a description. severity: warn given: $.components.schemas[*] then: field: description function: truthy schema-top-level-type: description: Top-level schemas must have a type. severity: error given: $.components.schemas[*] then: field: type function: truthy # ───────────────────────────────────────────────────────────── # SECURITY # ───────────────────────────────────────────────────────────── security-scheme-defined: description: A bearerAuth security scheme must be defined. severity: error given: $.components.securitySchemes then: field: bearerAuth function: truthy security-bearer-jwt: description: bearerAuth must declare bearerFormat=JWT. severity: warn given: $.components.securitySchemes.bearerAuth then: field: bearerFormat function: pattern functionOptions: match: '^JWT$' security-write-operations-protected: description: Write operations (POST/PUT/DELETE on /quotes**) must declare security. severity: warn given: $.paths[?(@property.match(/^\/quotes/))][post,put,delete] then: field: security function: truthy # ───────────────────────────────────────────────────────────── # HTTP METHOD CONVENTIONS # ───────────────────────────────────────────────────────────── http-get-no-request-body: description: GET operations must not declare a request body. severity: error given: $.paths[*].get then: field: requestBody function: falsy http-delete-204: description: DELETE operations should document a 204 No Content response. severity: info given: $.paths[*].delete.responses then: field: '204' function: truthy # ───────────────────────────────────────────────────────────── # GENERAL QUALITY # ───────────────────────────────────────────────────────────── no-empty-descriptions: description: Descriptions must not be empty strings. severity: warn given: $..description then: function: truthy microcks-operation-extension: description: Each operation should declare x-microcks-operation for mock-server compatibility. severity: info given: $.paths[*][get,post,put,patch,delete] then: field: x-microcks-operation function: truthy