# Tomorrow.io Weather API — Spectral Ruleset # # Opinionated rules enforcing the conventions observed in the Tomorrow.io v4 # API: kebab-case paths, camelCase parameters/properties, camelCase # operationIds, summary prefix "Tomorrow.io ", apikey-in-query auth, and # realistic response/error coverage. formats: - oas3 rules: # ───────────────────────── INFO / METADATA ───────────────────────── info-title-tomorrow-prefix: description: API title must start with "Tomorrow.io". message: '{{property}} should start with "Tomorrow.io"' severity: error given: $.info.title then: function: pattern functionOptions: match: '^Tomorrow\.io' info-description-required: description: API info.description is required and substantive. message: 'info.description must be at least 40 characters' severity: error given: $.info then: field: description function: length functionOptions: min: 40 info-version-required: description: API info.version is required. severity: error given: $.info then: field: version function: truthy info-contact-required: description: API info.contact should be populated. severity: warn given: $.info then: field: contact function: truthy info-license-required: description: API info.license should be populated. severity: warn given: $.info then: field: license function: truthy # ───────────────────────── OPENAPI VERSION ───────────────────────── openapi-version-3: description: OpenAPI version must be 3.0.x. severity: error given: $.openapi then: function: pattern functionOptions: match: '^3\.0\.' # ───────────────────────────── SERVERS ───────────────────────────── server-https-required: description: All server URLs must be HTTPS. severity: error given: $.servers[*].url then: function: pattern functionOptions: match: '^https://' server-tomorrow-host: description: Servers should point at api.tomorrow.io. severity: warn given: $.servers[*].url then: function: pattern functionOptions: match: '^https://api\.tomorrow\.io/v4' # ────────────────────── PATHS — NAMING CONVENTIONS ───────────────── path-no-trailing-slash: description: Paths must not end with a trailing slash. severity: error given: $.paths.*~ then: function: pattern functionOptions: notMatch: '.+/$' path-no-query-string: description: Paths must not contain a query string. severity: error given: $.paths.*~ then: function: pattern functionOptions: notMatch: '\?' path-kebab-case-segments: description: Path segments should be kebab-case (lowercase + hyphens). Path params are exempt. severity: warn given: $.paths.*~ then: function: pattern functionOptions: match: '^(/(\{[A-Za-z][A-Za-z0-9]*\}|[a-z0-9]+(-[a-z0-9]+)*))+$' # ────────────────────────────── OPERATIONS ───────────────────────── 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 must be camelCase. severity: error given: '$.paths.*[get,post,put,patch,delete].operationId' then: function: pattern functionOptions: match: '^[a-z][a-zA-Z0-9]*$' 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-summary-tomorrow-prefix: description: Operation summaries must start with "Tomorrow.io ". severity: warn given: '$.paths.*[get,post,put,patch,delete].summary' then: function: pattern functionOptions: match: '^Tomorrow\.io ' operation-description-required: description: Every operation should have a description. severity: warn given: '$.paths.*[get,post,put,patch,delete]' then: field: description function: truthy operation-tags-required: description: Every operation must declare at least one tag. severity: error given: '$.paths.*[get,post,put,patch,delete]' then: field: tags function: truthy operation-microcks-extension: description: Each operation should declare x-microcks-operation for mock compatibility. severity: info given: '$.paths.*[get,post,put,patch,delete]' then: field: x-microcks-operation function: truthy # ────────────────────────────── TAGS ─────────────────────────────── global-tags-defined: description: A global tags array must be defined. severity: warn given: $ then: field: tags function: truthy tag-description-required: description: Each tag should carry a description. severity: warn given: $.tags[*] then: field: description function: truthy tag-title-case: description: Tag names should be Title Case. severity: warn given: $.tags[*].name then: function: pattern functionOptions: match: '^[A-Z][A-Za-z0-9]*( [A-Z][A-Za-z0-9]*)*$' # ────────────────────────── PARAMETERS ───────────────────────────── parameter-description-required: description: Every parameter should have a description. severity: warn given: '$.paths.*[get,post,put,patch,delete].parameters[*]' then: field: description function: truthy parameter-schema-required: description: Every parameter must declare a schema with a type. severity: error given: '$.paths.*[get,post,put,patch,delete].parameters[*]' then: field: schema function: truthy parameter-camelcase: description: Parameter names should be camelCase (matches Tomorrow.io v4 convention). severity: warn given: '$.paths.*[get,post,put,patch,delete].parameters[*].name' then: function: pattern functionOptions: match: '^[a-z][a-zA-Z0-9]*$' parameter-api-key-not-in-query-public: description: Avoid taking API keys as ordinary parameters — declare them as a securityScheme. severity: warn given: '$.paths.*[get,post,put,patch,delete].parameters[?(@.name=="apikey" || @.name=="api_key")]' then: function: falsy # ────────────────────────── REQUEST BODIES ───────────────────────── request-body-json-content: description: Request bodies must declare application/json content. severity: error given: '$.paths.*[get,post,put,patch,delete].requestBody.content' then: field: application/json function: truthy # ─────────────────────────── RESPONSES ───────────────────────────── response-success-required: description: Each operation must declare at least one 2xx response. severity: error given: '$.paths.*[get,post,put,patch,delete].responses' then: function: schema functionOptions: schema: anyOf: - required: ['200'] - required: ['201'] - required: ['202'] - required: ['204'] response-401-on-writes: description: Write operations should document a 401 Unauthorized response. severity: warn given: '$.paths.*[post,put,patch,delete].responses' then: field: '401' function: truthy response-429-rate-limit: description: Operations should document a 429 Too Many Requests response. severity: warn given: '$.paths.*[get,post,put,patch,delete].responses' then: field: '429' 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 # ─────────────────────── SCHEMAS — PROPERTY NAMING ───────────────── schema-property-camelcase: description: Schema properties should be camelCase. severity: warn given: '$.components.schemas.*.properties.*~' then: function: pattern functionOptions: match: '^[a-z][a-zA-Z0-9]*$' schema-property-type-required: description: Schema properties must declare a type or $ref. severity: error given: '$.components.schemas.*.properties.*' then: function: schema functionOptions: schema: anyOf: - required: ['type'] - required: ['$ref'] - required: ['oneOf'] - required: ['anyOf'] - required: ['allOf'] schema-object-description: description: Top-level component schemas should carry a description. severity: warn given: '$.components.schemas.*' then: field: description function: truthy schema-error-shape: description: Error schema should expose code, type, and message fields (Tomorrow.io error envelope). severity: warn given: '$.components.schemas.Error' then: function: schema functionOptions: schema: required: ['properties'] properties: properties: required: ['code', 'type', 'message'] # ──────────────────────────── SECURITY ───────────────────────────── security-defined: description: A global security requirement must be defined. severity: error given: $ then: field: security function: truthy security-scheme-defined: description: securitySchemes must include the Tomorrow.io apikey query scheme. severity: error given: $.components.securitySchemes then: field: apiKeyQuery function: truthy security-apikey-query-name: description: API key parameter name should be `apikey` to match Tomorrow.io conventions. severity: warn given: '$.components.securitySchemes.apiKeyQuery' then: function: schema functionOptions: schema: required: ['type', 'in', 'name'] properties: type: const: apiKey in: const: query name: const: apikey # ────────────────────── HTTP METHOD CONVENTIONS ──────────────────── no-get-request-body: description: GET operations must not declare a requestBody. severity: error given: '$.paths.*.get' then: field: requestBody function: falsy no-delete-request-body: description: DELETE operations should not declare a requestBody. severity: warn given: '$.paths.*.delete' then: field: requestBody function: falsy post-put-request-body: description: POST/PUT/PATCH operations must declare a requestBody. severity: warn given: '$.paths.*[post,put,patch]' then: field: requestBody function: truthy delete-returns-204: description: DELETE operations should return 204 No Content on success. severity: warn given: '$.paths.*.delete.responses' then: field: '204' function: truthy # ──────────────────────────── GENERAL ────────────────────────────── no-empty-descriptions: description: Descriptions should not be empty strings. severity: warn given: '$..description' then: function: truthy