# Coveo Spectral Ruleset # # Enforces conventions observed across the 31 Coveo OpenAPI specifications: # Search, Push, Commerce, Usage Analytics (Read/Write), Machine Learning, # Platform, Source, Field, Security Cache, Activity, Extension, Authorization, # Notification, Schema, Search Pages, Customer Service, Migration, Connectivity, # Tailgate, Search Interface, Event, Workspace, Context, Analytics Admin, # Knowledge Generative AI, Catalog Management, Source Logs, Search Usage Metrics, # ML Configuration, and Organization APIs. # # Convention summary (dominant patterns): # OpenAPI version : 3.0.1 # Title prefix : "Coveo ..." # Path casing : lowercase # OperationId : camelCase # Parameter names : camelCase # Schema props : camelCase # Security : OAuth2 (Authorization Code + Client Credentials) # Required errors : 400, 401, 403, 404, 429, 500 extends: - "spectral:oas" formats: - oas3 rules: # ─── INFO / METADATA ───────────────────────────────────────────────────────── info-title-coveo-prefix: description: API title MUST begin with "Coveo " followed by the API name. message: "{{property}} must start with 'Coveo ' (e.g. 'Coveo Search API')." severity: warn given: $.info.title then: function: pattern functionOptions: match: "^Coveo .+" info-description-required: description: API description MUST be present and non-trivial. message: "{{property}} description is required (min 30 chars)." severity: warn given: $.info then: - field: description function: truthy - field: description function: length functionOptions: min: 30 info-version-required: description: API version MUST be declared in info.version. severity: error given: $.info then: field: version function: truthy info-contact-coveo-support: description: Contact MUST be Coveo support. severity: info given: $.info.contact then: field: name function: pattern functionOptions: match: ".*support@coveo\\.com.*|.*Coveo.*" # ─── OPENAPI VERSION ───────────────────────────────────────────────────────── openapi-version-3-0: description: OpenAPI version MUST be 3.0.x (Coveo standard). message: "OpenAPI version should be 3.0.x" severity: error given: $.openapi then: function: pattern functionOptions: match: "^3\\.0\\.[0-9]+$" # ─── SERVERS ───────────────────────────────────────────────────────────────── servers-required: description: At least one server MUST be declared. severity: error given: $ then: field: servers function: truthy servers-https-only: description: Server URLs MUST use HTTPS. severity: error given: $.servers[*].url then: function: pattern functionOptions: match: "^https://" servers-coveo-domain: description: Server URLs SHOULD use a Coveo cloud domain. severity: warn given: $.servers[*].url then: function: pattern functionOptions: match: ".*\\.coveo\\.com(/|$).*" # ─── PATHS — NAMING CONVENTIONS ────────────────────────────────────────────── paths-lowercase: description: Path segments MUST be lowercase (Coveo convention). message: "Path segment '{{value}}' should be lowercase." severity: warn given: $.paths then: function: pattern functionOptions: match: "^(/[a-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-string: description: Paths MUST NOT include a query string. severity: error given: $.paths then: function: pattern functionOptions: notMatch: "\\?" paths-organizations-prefix: description: Tenant-scoped admin endpoints SHOULD include an `/organizations/{organizationId}` prefix. severity: info given: $.paths[?(@property =~ /^\/rest\/(source|field|extension|securityCache|notification|activity)/)] then: function: falsy # ad-hoc marker — surfaces the case for review # ─── OPERATIONS ────────────────────────────────────────────────────────────── operation-operationId-required: description: Every operation MUST declare a unique operationId. severity: error given: $.paths[*][get,post,put,patch,delete,head,options] then: field: operationId function: truthy operation-operationId-camelcase: description: operationId MUST be camelCase. severity: warn given: $.paths[*][get,post,put,patch,delete,head,options].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,head,options] then: field: summary function: truthy operation-summary-title-case: description: Operation summary SHOULD start with a capitalised verb (Title Case). severity: warn given: $.paths[*][get,post,put,patch,delete,head,options].summary then: function: pattern functionOptions: match: "^[A-Z][A-Za-z0-9 ()/_,.'’\"-]+$" operation-description-required: description: Operations SHOULD have a description. severity: info given: $.paths[*][get,post,put,patch,delete,head,options] then: field: description function: truthy operation-tags-required: description: Operations MUST be tagged. severity: warn given: $.paths[*][get,post,put,patch,delete,head,options] then: field: tags function: schema functionOptions: schema: type: array minItems: 1 # ─── TAGS ──────────────────────────────────────────────────────────────────── tags-global-defined: description: A global `tags` array SHOULD be defined. severity: info given: $ then: field: tags function: truthy tags-have-description: description: Global tags SHOULD have descriptions. severity: info given: $.tags[*] then: field: description function: truthy # ─── PARAMETERS ────────────────────────────────────────────────────────────── parameter-name-camelcase: description: Parameter names MUST be camelCase (Coveo convention). severity: warn given: $..parameters[*].name then: function: pattern functionOptions: match: "^[a-z][a-zA-Z0-9]*$" parameter-description-required: description: Parameters SHOULD have a description. severity: info given: $..parameters[*] then: field: description function: truthy parameter-pagination-conventions: description: Pagination SHOULD use `firstResult` (offset) + `numberOfResults` (limit) for search, or `page`/`perPage` for admin lists. severity: info given: $..parameters[?(@.name == 'offset' || @.name == 'limit' || @.name == 'size' || @.name == 'pageSize')] then: function: falsy # ─── REQUEST BODIES ────────────────────────────────────────────────────────── request-body-json-required: description: Request bodies SHOULD support `application/json`. severity: warn given: $..requestBody.content then: field: application/json function: truthy # ─── RESPONSES ─────────────────────────────────────────────────────────────── response-2xx-required: description: Operations MUST declare at least one 2xx response. severity: error given: $.paths[*][get,post,put,patch,delete,head,options].responses then: function: schema functionOptions: schema: type: object patternProperties: "^2[0-9]{2}$": {} minProperties: 1 response-401-on-secured: description: Operations SHOULD document a 401 Unauthorized response. severity: info given: $.paths[*][get,post,put,patch,delete,head,options].responses then: field: "401" function: truthy response-403-on-secured: description: Operations SHOULD document a 403 Forbidden response. severity: info given: $.paths[*][get,post,put,patch,delete,head,options].responses then: field: "403" function: truthy response-404-on-resource: description: Operations on a specific resource SHOULD document a 404 response. severity: info given: $.paths[?(@property =~ /\\{[^}]+\\}/)][get,put,patch,delete].responses then: field: "404" function: truthy response-429-rate-limit: description: Operations SHOULD document a 429 Too Many Requests response (Coveo enforces quotas). severity: info given: $.paths[*][get,post,put,patch,delete,head,options].responses then: field: "429" function: truthy response-500-server-error: description: Operations SHOULD document a 500 response. severity: info given: $.paths[*][get,post,put,patch,delete,head,options].responses then: field: "500" function: truthy response-content-json: description: 2xx responses SHOULD return `application/json`. severity: warn given: $.paths[*][get,post,put,patch,delete,head,options].responses[?(@property =~ /^2[0-9][0-9]$/)].content then: field: application/json function: truthy # ─── SCHEMAS — PROPERTY NAMING ─────────────────────────────────────────────── schema-property-camelcase: description: Schema property names MUST be camelCase. severity: warn given: $.components.schemas[*].properties[*]~ then: function: pattern functionOptions: match: "^[a-z][a-zA-Z0-9]*$" schema-description-required: description: Top-level schemas SHOULD have a description. severity: info given: $.components.schemas[*] then: field: description function: truthy schema-type-required: description: Top-level schemas MUST declare a `type` (unless they use composition). severity: warn given: $.components.schemas[?(!@.allOf && !@.oneOf && !@.anyOf && !@.$ref)] then: field: type function: truthy # ─── SECURITY ──────────────────────────────────────────────────────────────── security-global-defined: description: Global `security` array MUST be defined (Coveo APIs are authenticated). severity: error given: $ then: field: security function: truthy security-schemes-defined: description: components.securitySchemes MUST be present. severity: error given: $.components then: field: securitySchemes function: truthy security-oauth2-required: description: Coveo APIs MUST offer OAuth2 (Authorization Code or Client Credentials). severity: warn given: $.components.securitySchemes then: function: schema functionOptions: schema: type: object additionalProperties: type: object properties: type: type: string enum: ["oauth2", "http", "apiKey", "openIdConnect"] # ─── HTTP METHOD CONVENTIONS ───────────────────────────────────────────────── no-get-request-body: description: GET operations MUST NOT define a request body. severity: error given: $.paths[*].get then: field: requestBody function: falsy no-delete-request-body: description: DELETE operations SHOULD NOT define a request body. severity: warn given: $.paths[*].delete then: field: requestBody function: falsy delete-returns-204: description: DELETE operations SHOULD return 204 No Content on success. 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 examples-encouraged: description: Operations SHOULD include at least one example. severity: info given: $.paths[*][get,post,put,patch,delete].responses[?(@property =~ /^2[0-9][0-9]$/)].content.application/json then: function: schema functionOptions: schema: type: object anyOf: - required: [example] - required: [examples] external-docs-encouraged: description: Provide externalDocs pointing at docs.coveo.com. severity: info given: $ then: field: externalDocs function: truthy