TVmaze · API Governance Rules

TVmaze API Rules

Spectral linting rules defining API design standards and conventions for TVmaze.

14 Rules error 4 warn 7 info 3
View Rules File View on GitHub

Rule Categories

components info operation path paths premium response

Rules

warn
operation-summary-title-case
Operation summaries should be in Title Case.
$.paths[*][get,post,put,delete,patch].summary
error
operation-id-required
Every operation must declare an operationId.
$.paths[*][get,post,put,delete,patch]
warn
operation-id-lower-camel
operationId should be lowerCamelCase.
$.paths[*][get,post,put,delete,patch].operationId
warn
operation-tags-required
Every operation must declare at least one tag.
$.paths[*][get,post,put,delete,patch]
error
paths-no-extension
Paths should not include a file extension.
$.paths[*]~
error
paths-no-trailing-slash
Paths should not end with a trailing slash.
$.paths[*]~
warn
path-segment-lowercase
Path segments should be lowercase, with no underscores.
$.paths[*]~
warn
path-id-integer
Path parameters named `id` (or `*_id`) should be integers.
$.paths[*].parameters[?(@.in=='path' && (@.name=='id' || @.name=~/_id$/))].schema
warn
response-json-required
2xx responses must offer application/json content.
$.paths[*][get,post,put,delete,patch].responses[?(@property.match(/^2/))]
info
response-rate-limit-documented
GET endpoints should document 429 because TVmaze rate-limits at 20 req / 10 sec / IP.
$.paths[*].get.responses
info
premium-paths-namespace
Premium endpoints should live under /v1/user, /v1/scrobble, or /v1/auth.
$.paths[*]~
error
info-license-required
info.license is required.
$.info
info
info-license-creative-commons
TVmaze data is CC BY-SA 4.0; the license field should reflect that.
$.info.license
warn
components-schemas-required
components.schemas should be defined so payloads are reusable.
$.components

Spectral Ruleset

Raw ↑
# Spectral ruleset enforcing TVmaze API conventions.
#
# Derived from analyzing the public and premium TVmaze OpenAPI specifications.
# These rules codify the patterns the provider already follows so contributors and
# downstream consumers stay aligned with the published surface.
extends:
  - spectral:oas

functions:
  - length

rules:
  # All operations should have descriptive Title Case summaries.
  operation-summary-title-case:
    description: Operation summaries should be in Title Case.
    message: "{{property}} summary should be Title Case"
    given: $.paths[*][get,post,put,delete,patch].summary
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z0-9][A-Za-z0-9 /()_'-]*$"

  # Every operation must have an operationId.
  operation-id-required:
    description: Every operation must declare an operationId.
    given: $.paths[*][get,post,put,delete,patch]
    severity: error
    then:
      field: operationId
      function: truthy

  # operationIds are lowerCamelCase verbs like getShow, listShows, searchPeople.
  operation-id-lower-camel:
    description: operationId should be lowerCamelCase.
    given: $.paths[*][get,post,put,delete,patch].operationId
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^[a-z][A-Za-z0-9]*$"

  # Every operation must declare at least one tag.
  operation-tags-required:
    description: Every operation must declare at least one tag.
    given: $.paths[*][get,post,put,delete,patch]
    severity: warn
    then:
      field: tags
      function: truthy

  # Public TVmaze uses RESTful collection / item paths — disallow .json suffix or trailing slashes.
  paths-no-extension:
    description: Paths should not include a file extension.
    given: $.paths[*]~
    severity: error
    then:
      function: pattern
      functionOptions:
        notMatch: "\\.(json|xml|yaml|yml)$"

  paths-no-trailing-slash:
    description: Paths should not end with a trailing slash.
    given: $.paths[*]~
    severity: error
    then:
      function: pattern
      functionOptions:
        notMatch: "/$"

  # Public TVmaze uses lowercase, hyphen-free path segments (singlesearch, episodebynumber).
  path-segment-lowercase:
    description: Path segments should be lowercase, with no underscores.
    given: $.paths[*]~
    severity: warn
    then:
      function: pattern
      functionOptions:
        notMatch: "[A-Z]"

  # IDs are integers in TVmaze.
  path-id-integer:
    description: "Path parameters named `id` (or `*_id`) should be integers."
    given: "$.paths[*].parameters[?(@.in=='path' && (@.name=='id' || @.name=~/_id$/))].schema"
    severity: warn
    then:
      field: type
      function: enumeration
      functionOptions:
        values: [integer]

  # All success responses should declare an application/json content type.
  response-json-required:
    description: 2xx responses must offer application/json content.
    given: "$.paths[*][get,post,put,delete,patch].responses[?(@property.match(/^2/))]"
    severity: warn
    then:
      field: content.application/json
      function: truthy

  # 429 should be a documented response anywhere — TVmaze applies 20 req / 10 sec / IP.
  response-rate-limit-documented:
    description: GET endpoints should document 429 because TVmaze rate-limits at 20 req / 10 sec / IP.
    given: $.paths[*].get.responses
    severity: info
    then:
      field: "429"
      function: truthy

  # Premium API: every authenticated path lives under /v1/user, /v1/scrobble or /v1/auth.
  premium-paths-namespace:
    description: Premium endpoints should live under /v1/user, /v1/scrobble, or /v1/auth.
    given: $.paths[*]~
    severity: info
    then:
      function: pattern
      functionOptions:
        match: "^/(search|singlesearch|lookup|shows|seasons|episodes|people|schedule|updates|user|scrobble|auth)(/|$)"

  # info.license is required and should be CC BY-SA for TVmaze.
  info-license-required:
    description: info.license is required.
    given: $.info
    severity: error
    then:
      field: license
      function: truthy

  info-license-creative-commons:
    description: TVmaze data is CC BY-SA 4.0; the license field should reflect that.
    given: $.info.license
    severity: info
    then:
      field: name
      function: pattern
      functionOptions:
        match: "CC BY-SA"

  # Components.schemas should be defined and operations should reference them, not inline blobs.
  components-schemas-required:
    description: components.schemas should be defined so payloads are reusable.
    given: $.components
    severity: warn
    then:
      field: schemas
      function: truthy