Responses

A ResponseRule allows you to write assertions around the response status codes in your API Specification.

new ResponseRule({
  name: "prevent response removal",
  rule: (responseAssertions) => {
    responseAssertions.removed("not remove response", () => {
      throw new RuleError({
        message: "cannot remove an response",
      });
    });
  },
});

new ResponseRule(options)

The following table describes the options object.

propertydescriptionrequiredtype
namethe name of the ruleyesstring
matchesA function used to determine when this Rule should be applied. Return true to indicate this Rule should run.no(response: Response, ruleContext: RuleContext) => boolean
docsLinkA link to the documentation for this ruleset. This will be used to show the user on a rule error. If there is a more specific docsLink (e.g. on a nested Rule), the more specific docsLink will be shown)nostring
ruleA function to define assertions for a specification.yes(responseAssertions: ResponseAssertions, ruleContext: RuleContext) => void

matches

matches is invoked with a Response and RuleContext.

Example:

new ResponseRule({
  ...,
  // only matches responses with content type 'application/json' and status code 201 in post operations
  matches: (response, ruleContext) => (
    ruleContext.operation.method === 'post' &&
    response.statusCode === '201'
  ),
  ...
});

responseAssertions

responseAssertions is used to define response rules.

responseAssertions[lifecycle](assertion)

new ResponseRule({
  ...,
  rule: (responseAssertions) => {
    // lifecycle rules that are available are added, changed, addedOrChanged, requirement and removed
    responseAssertions.added('contain description', (response) => {
      if (!response.value.description) {
        throw new RuleError({
          message: 'response must contain a description',
        });
      }
    });
  },
});

responseAssertions also includes a number of common helper functions. These are invoked by defining a lifecycle trigger, and then the helper function. e.g. responseAssertions[lifecycle].helperFunction().

The helper functions that are included are:

  • hasResponseHeaderMatching

All of these helper functions can be inverted by prefixing with .not.

e.g. responseAssertions.added.not.hasResponseHeaderMatching('X-Application', { description: Matchers.string })

hasResponseHeaderMatching

responseAssertions[lifecycle].hasResponseHeaderMatching(headerName, shape)

Expects the operation to match a shape. The default behavior is to do a partial match.

import { Matchers } from '@useoptic/rulesets-base';
 
new ResponseRule({
  ...,
  rule: (responseAssertions) => {
    responseAssertions.added.hasResponseHeaderMatching('X-Application', {
      description: Matchers.string
    });
  },
});

responseAssertions.header

responseAssertions.header is used to define response header rules.

responseAssertions.header[lifecycle](assertion)

new ResponseRule({
  ...,
  rule: (responseAssertions) => {
    // lifecycle rules that are available are added, changed, addedOrChanged, requirement and removed
    responseAssertions.header.added('contains a description', (header) => {
      if (!header.value.description) {
        throw new RuleError({
          message: 'header must contain a description',
        });
      }
    });
  },
});

Response Body

A ResponseBodyRule allows you to write assertions about the shape of the response bodies:

new ResponseBodyRule({
  name: "prevent response body removal",
  rule: (responseBodyAssertions) => {
    responseBodyAssertions.body.removed("not remove response", () => {
      throw new RuleError({
        message: "cannot remove an response",
      });
    });
  },
});

new ResponseBodyRule(options)

The following table describes the options object.

propertydescriptionrequiredtype
namethe name of the ruleyesstring
matchesA function used to determine when this Rule should be applied. Return true to indicate this Rule should run.no(response: ResponseBody, ruleContext: RuleContext) => boolean
docsLinkA link to the documentation for this ruleset. This will be used to show the user on a rule error. If there is a more specific docsLink (e.g. on a nested Rule), the more specific docsLink will be shown)nostring
ruleA function to define assertions for a specification.yes(responseBodyAssertions: ResponseBodyAssertions, ruleContext: RuleContext) => void

matches

matches is invoked with a ResponseBody and RuleContext.

Example:

new ResponseBodyRule({
  ...,
  // only matches responses with content type 'application/json' and status code 201 in post operations
  matches: (response, ruleContext) => (
    ruleContext.operation.method === 'post' &&
    response.contentType === 'application/json' &&
    response.statusCode === '201'
  ),
  ...
});

responseBodyAssertions.body

responseBodyAssertions.body is used to define response rules.

responseBodyAssertions.body[lifecycle](assertion)

new ResponseBodyRule({
  ...,
  rule: (responseBodyAssertions) => {
    // lifecycle rules that are available are added, changed, addedOrChanged, requirement and removed
    responseBodyAssertions.body.added('contain description', (response) => {
      if (!response.value.description) {
        throw new RuleError({
          message: 'response bodies must contain a description',
        });
      }
    });
  },
});

responseBodyAssertions.body also includes a number of common helper functions. These are invoked by defining a lifecycle trigger, and then the helper function. e.g. responseBodyAssertions.body[lifecycle].helperFunction().

The helper functions that are included are:

  • matches
  • matchesOneOf

All of these helper functions can be inverted by prefixing with .not.

e.g. responseBodyAssertions.body.added.not.matches({ schema: { type: 'array' } })

matches

responseBodyAssertions.body[lifecycle].matches(shape)

Expects the operation to match a shape. The default behavior is to do a partial match.

import { Matchers } from '@useoptic/rulesets-base';
 
new ResponseBodyRule({
  ...,
  rule: (responseBodyAssertions) => {
    responseBodyAssertions.body.added.matches({
      description: Matchers.string
    });
  },
});

matchesOneOf

responseBodyAssertions.body[lifecycle].matches(shape)

Expects the operation to match one of an array of shapes. The default behavior is to do a partial match.

import { Matchers } from '@useoptic/rulesets-base';
 
new ResponseBodyRule({
  ...,
  rule: (responseBodyAssertions) => {
    responseBodyAssertions.body.added.matches([
      { description: Matchers.string },
      { summary: Matchers.string },
    ]);
  },
});

responseBodyAssertions.property

responseBodyAssertions.property is used to define response body property rules. You can write a rule about all properties (including request body properties) using the PropertyRule.

responseBodyAssertions.property[lifecycle](assertion)

new ResponseBodyRule({
  ...,
  rule: (responseBodyAssertions) => {
    // lifecycle rules that are available are added, changed, addedOrChanged, requirement and removed
    responseBodyAssertions.property.added('contains a type', (property) => {
      if (!property.value.type) {
        throw new RuleError({
          message: 'properties must contain a type',
        });
      }
    });
  },
});

responseBodyAssertions.schema

responseBodyAssertions.schema is used to define rules on any schema in a response body. Schemas can exist on the root response body, as an object field, array item and on polymorphic types (oneOf, anyOf, allOf).

responseBodyAssertions.schema[lifecycle](assertion)

new ResponseBodyRule({
  ...,
  rule: (responseBodyAssertions) => {
    // lifecycle rules that are available are added, changed, addedOrChanged, requirement and removed
    responseBodyAssertions.schema.added('contains a type', (schema) => {
      if (!schema.value.flatSchema.type) {
        throw new RuleError({
          message: 'schemas must contain a type',
        });
      }
 
      // schemas also contain the location of where the schema is located
      // Other examples include type === 'oneOf', 'array', etc
      if (schema.location.context.type === 'field' && schema.location.context.required) {
        // On field types, the context also includes required and key
        throw new RuleError({
          message: 'cannot add required fields',
        });
      }
    });
  },
});