# Restaurant365 Spectral Ruleset # Enforces conventions observed across the R365 API and OData Connector OpenAPI specs. # Apply with: spectral lint openapi/*.yml --ruleset rules/restaurant365-rules.yml rules: # ── INFO / METADATA ───────────────────────────────────────────── info-title-required: description: API title must be present. severity: error given: $.info then: field: title function: truthy info-description-required: description: API description must be present and meaningful. severity: warn given: $.info then: field: description function: truthy info-version-required: description: API version must be present. severity: error given: $.info then: field: version function: truthy info-contact-required: description: API should declare a maintainer contact. severity: warn given: $.info then: field: contact function: truthy # ── OPENAPI VERSION ───────────────────────────────────────────── openapi-version-3-1: description: Specs should use OpenAPI 3.1.x. severity: warn given: $.openapi then: function: pattern functionOptions: match: '^3\.1\.' # ── SERVERS ───────────────────────────────────────────────────── servers-defined: description: At least one server must be defined. severity: error given: $ then: field: servers function: truthy servers-https: description: Server URLs must use HTTPS. severity: error given: $.servers[*].url then: function: pattern functionOptions: match: '^https://' # ── OPERATIONS ────────────────────────────────────────────────── operation-summary-required: description: Every operation must have a summary. severity: warn given: $.paths[*][get,post,put,patch,delete] then: field: summary function: truthy operation-summary-title-case: description: Operation summaries must use Title Case (each word capitalized). severity: warn given: $.paths[*][get,post,put,patch,delete].summary then: function: pattern functionOptions: match: '^([A-Z0-9][A-Za-z0-9]*)(\s+([A-Z0-9][A-Za-z0-9]*|And|By|Or|The|Of|To))*$' 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-operationId-required: description: Every operation must declare an operationId. severity: error given: $.paths[*][get,post,put,patch,delete] then: field: operationId function: truthy operation-operationId-camel-case: description: operationId must be camelCase (e.g. listTransactions, createApInvoices). severity: warn given: $.paths[*][get,post,put,patch,delete].operationId then: function: pattern functionOptions: match: '^[a-z][a-zA-Z0-9]*$' operation-tags-required: description: Every operation must be tagged. severity: warn given: $.paths[*][get,post,put,patch,delete] then: field: tags function: truthy # ── TAGS ──────────────────────────────────────────────────────── tag-description-required: description: Global tags should have descriptions. severity: info given: $.tags[*] then: field: description function: truthy tag-title-case: description: Tag names must use Title Case. severity: warn given: $.tags[*].name then: function: pattern functionOptions: match: '^[A-Z][A-Za-z0-9]*(\s+([A-Z][A-Za-z0-9]*|And|By|Or|Of|To))*$' # ── PARAMETERS ────────────────────────────────────────────────── parameter-description-required: description: Parameters should have descriptions. severity: warn given: $.paths[*][get,post,put,patch,delete].parameters[*] then: field: description function: truthy odata-query-options-dollar-prefixed: description: OData system query options must be $-prefixed (e.g. $filter, $top). severity: info given: $.paths[*].get.parameters[?(@.in=='query')].name then: function: pattern functionOptions: match: '^\$' # ── REQUEST BODIES ────────────────────────────────────────────── request-body-json: description: Request bodies should accept application/json. severity: warn given: $.paths[*][post,put,patch].requestBody.content then: field: application/json function: truthy # ── RESPONSES ─────────────────────────────────────────────────── response-success-defined: description: Each operation must define a 200 success response. severity: warn given: $.paths[*][get,post,put,patch,delete].responses then: field: '200' 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-auth-error-defined: description: Authenticated write operations should document a 401 response. severity: info given: $.paths[*].post.responses then: field: '401' function: truthy # ── SCHEMAS ───────────────────────────────────────────────────── schema-property-types-defined: description: Schema properties must declare a type. severity: warn given: $.components.schemas[*].properties[*] then: field: type function: truthy schema-id-uuid-format: description: OData identifier properties ending in Id should be uuid-formatted. severity: info given: $.components.schemas[*].properties[?(@property.match(/Id$/))] then: field: format function: truthy # ── SECURITY ──────────────────────────────────────────────────── security-schemes-defined: description: A securitySchemes block must be defined. severity: warn given: $.components then: field: securitySchemes function: truthy operation-write-secured: description: Record-creation POST operations must declare security. severity: warn given: $.paths[*].post then: field: security function: truthy