# Spectral ruleset for the MealMe Food Ordering API # Generated by the API Evangelist pipeline. Enforces the conventions observed # across the MealMe OpenAPI specification (openapi/mealme-openapi.yml): # - snake_case query parameters and schema properties # - camelCase operationIds with verb prefixes # - Title Case tags defined in the global tags array # - API key carried in the Authorization header (not a query parameter) # - https server, monetary values expressed in integer cents rules: # ── INFO / METADATA ─────────────────────────────────────────────── info-title-mealme: description: API title should identify MealMe. severity: warn given: $.info then: field: title function: pattern functionOptions: match: "MealMe" info-description-required: description: Info object must have a non-empty description. severity: warn given: $.info then: field: description function: truthy info-version-required: description: Info object must declare a version. severity: error given: $.info then: field: version function: truthy info-contact-required: description: A contact should be provided. severity: info given: $.info then: field: contact function: truthy # ── OPENAPI VERSION ─────────────────────────────────────────────── openapi-version-3-1: description: Specs should target OpenAPI 3.1.x. severity: warn given: $ then: field: openapi 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[*] then: field: url function: pattern functionOptions: match: "^https://" server-is-mealme: description: The production server should be api.mealme.ai. severity: info given: $.servers[*] then: field: url function: pattern functionOptions: match: "api\\.mealme\\.ai" # ── PATHS — NAMING CONVENTIONS ──────────────────────────────────── paths-no-trailing-slash: description: Paths must not end with a trailing slash. severity: warn given: $.paths[*]~ then: function: pattern functionOptions: notMatch: ".+/$" paths-no-query-string: description: Path keys must not contain query strings. severity: error given: $.paths[*]~ then: function: pattern functionOptions: notMatch: "\\?" paths-lowercase: description: Path segments use lowercase letters, digits, underscores, slashes, and braces. severity: warn given: $.paths[*]~ then: function: pattern functionOptions: match: "^[a-z0-9_/{}]+$" # ── 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-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 have an operationId. severity: error given: $.paths[*][get,post,put,patch,delete] then: field: operationId function: truthy operation-operationId-camelcase: description: operationId should be camelCase. severity: warn given: $.paths[*][get,post,put,patch,delete] then: field: operationId function: pattern functionOptions: match: "^[a-z][a-zA-Z0-9]+$" operation-summary-title-case: description: Summaries should start with a capital letter. severity: info given: $.paths[*][get,post,put,patch,delete] then: field: summary function: pattern functionOptions: match: "^[A-Z]" operation-tags-required: description: Every operation must be tagged. severity: warn given: $.paths[*][get,post,put,patch,delete] then: field: tags function: truthy # ── TAGS ────────────────────────────────────────────────────────── global-tags-defined: description: Specs should declare a global tags array. severity: info given: $ then: field: tags function: truthy tag-has-description: description: Each global tag should have a description. severity: info given: $.tags[*] then: field: description function: truthy tag-title-case: description: Tags should use Title Case (each word capitalized). severity: warn given: $.tags[*] then: field: name function: pattern functionOptions: match: "^[A-Z][A-Za-z]*( [A-Z][A-Za-z]*)*$" # ── PARAMETERS ──────────────────────────────────────────────────── parameter-snake-case: description: Parameter names should be snake_case. severity: warn given: $.paths[*][get,post,put,patch,delete].parameters[*] then: field: name function: pattern functionOptions: match: "^[a-z][a-z0-9_]*$" parameter-has-schema: description: Parameters must declare a schema. severity: warn given: $.paths[*][get,post,put,patch,delete].parameters[*] then: field: schema function: truthy parameter-no-api-key-in-query: description: API keys must travel in the Authorization header, never as a query parameter. severity: error given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in=='query')] then: field: name function: pattern functionOptions: notMatch: "(?i)(api[_-]?key|authorization|token)" # ── REQUEST BODIES ──────────────────────────────────────────────── request-body-json: description: Request bodies should offer an application/json representation. severity: warn given: $.paths[*][post,put,patch].requestBody.content then: field: application/json function: truthy # ── RESPONSES ───────────────────────────────────────────────────── response-2xx-required: description: Every operation must define a success (2xx) response. severity: warn given: $.paths[*][get,post,put,patch,delete].responses then: field: "200" function: truthy response-401-documented: description: Authenticated operations should document a 401 response. severity: info given: $.paths[*][get,post,put,patch,delete].responses then: field: "401" function: truthy response-has-description: description: Each response must have a description. severity: warn given: $.paths[*][get,post,put,patch,delete].responses[*] then: field: description function: truthy # ── SCHEMAS — PROPERTY NAMING ───────────────────────────────────── schema-property-snake-case: description: Schema property names should be snake_case (underscore-prefixed identifiers like _id allowed). severity: warn given: $.components.schemas[*].properties[*]~ then: function: pattern functionOptions: match: "^_?[a-z][a-z0-9_]*$" schema-property-typed: description: Each schema property should declare a type or $ref. severity: info given: $.components.schemas[*].properties[*] then: function: schema functionOptions: schema: anyOf: - required: [type] - required: ["$ref"] # ── SECURITY ────────────────────────────────────────────────────── global-security-defined: description: A global security requirement should be declared. severity: warn given: $ then: field: security function: truthy security-scheme-api-key-header: description: The API key security scheme must be an apiKey carried in the header. severity: warn given: $.components.securitySchemes[*] then: field: type function: pattern functionOptions: match: "apiKey" security-scheme-authorization-header: description: The API key header should be named Authorization. severity: info given: $.components.securitySchemes[?(@.in=='header')] then: field: name function: pattern functionOptions: match: "Authorization" # ── HTTP METHOD CONVENTIONS ─────────────────────────────────────── get-no-request-body: description: GET operations must not declare a request body. severity: error given: $.paths[*].get then: field: requestBody function: falsy # ── GENERAL QUALITY ─────────────────────────────────────────────── examples-encouraged: description: Operations are encouraged to include examples for mocking. severity: info given: $.paths[*][post,put,patch].requestBody.content.application/json then: function: schema functionOptions: schema: anyOf: - required: [example] - required: [examples]