Last updated

Visitor pattern

Visitor is a design pattern that allows operations to be performed on individual elements in a more complex structure. Redocly uses it as the basis for custom plugins where every node in a document must be evaluated, for example when applying rules or decorators.

To understand how this applies to your API description, think of the document as a tree structure. The top level elements are entries like info and components. To examine the description field in the info section, the visitor goes to the info node first, then on to the description node. This pattern is repeated all over the document as the visitor pattern works its way around the document tree.

Structure of the visitor object

In your plugin, create a JavaScript visitor object, and describe the functionality required for each type of node.

Redocly CLI calls enter() while going down the tree and leave() going up the tree after processing the node and its children. If the skip() predicate is defined and returns true the node is ignored for this visitor.

function ExampleRule() {
  const seen = {};
  return {
    Root: {
      leave() {
        // check something and report
      }
    }
    Operation: {
      enter(operation, ctx) {
        seen[operation.operationId] = true;
      },
    }
  };
}

Keys of the object are one of the following:

  • document-specific node types, such as the OpenAPI node types.
  • any - visitor is called on every node.
  • ref - visitor is called on $ref nodes.

Visitors execution and $ref

Top level visitor functions run only once for each node. If the same node is referenced by the $ref multiple times, top-level visitor functions are executed only once for this node.

This works fine for most context-free rules. If you need contextual info you should use nested visitors.

Nested visitors

The visitor object (if it is not any or ref) can define nested visitors.

Here is a basic example of a nested visitor:

function ExampleRule() {
  return {
    Operation: {
      Schema: {
        enter(schema, ctx, parents) {
          console.log(`type ${schema.type} from ${parents.Operation.operationId}`)
        }
      }
    }
  };
}

The Schema visitor function is called by Redocly CLI only if the Schema Object is encountered while traversing a tree while the Operation Object is entered.

As the third argument, enter() in a nested visitor object accepts the parents object with corresponding parent nodes as defined in the visitor object.

It is executed only for the first level of the Schema Object.

For the example document below:

get:
  operationId: getOp
  parameters:
    - name: a
      in: path
      schema:
        type: string
  requestBody:
    content:
      application/json:
        schema:
          type: object
          properties:
            a:
              type: boolean
put:
  operationId: putOp
  parameters:
    - name: a
      in: path
      schema:
        type: number

The visitor above logs the following:

type string from getOp
type object from getOp
type number from putOp