Files
redocly-cli/docs/custom-plugins/visitor.md
2024-05-08 11:15:57 +01:00

108 lines
3.2 KiB
Markdown

# 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.
```js
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](https://redocly.com/docs/openapi-visual-reference/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).
## Nested visitors
The visitor object (if it is not `any` or `ref`) can define [nested visitors](#nested-visitors).
Here is a basic example of a nested visitor:
```js
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**.
{% admonition type="attention" %}
It is executed only for the first level of the Schema Object.
{% /admonition %}
For the example document below:
```yaml
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:
```sh
type string from getOp
type object from getOp
type number from putOp
```