mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-06 04:21:09 +00:00
docs: add more dev docs
This commit is contained in:
@@ -230,10 +230,10 @@ Ignore file can be autogenerated to add all messages to it by running `openapi l
|
||||
### Advanced
|
||||
|
||||
[Plugins](docs/plugins.md)
|
||||
[Type extensions](docs/type-extensions.md)
|
||||
[Custom rules](docs/custom-rules.md)
|
||||
[Decorators](docs/decorators.md)
|
||||
[Preprocessors](docs/preprocessors.md)
|
||||
[Type extensions](docs/type-extensions.md)
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
@@ -1,11 +1,178 @@
|
||||
# Custom rules
|
||||
|
||||
Each rule is a function that accepts rule config and returns an object with methods that openapi-cli calls to "visit" nodes while traversing the defintion document.
|
||||
|
||||
#### Rules (visitors)
|
||||
Here is the basic example of a rule:
|
||||
|
||||
TBD
|
||||
```js
|
||||
function OperationIdNotTest() {
|
||||
return {
|
||||
Operation(operation, ctx) {
|
||||
if (operation.operationId === 'test') {
|
||||
ctx.report({
|
||||
message: `operationId must be not "test"`,
|
||||
location: ctx.location.child('operationId'),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Adding custom rules
|
||||
## Format of visitor
|
||||
|
||||
Keys of the object can be any of the following:
|
||||
|
||||
- node type - visitor will be called on specific node type. List of available node types for speicific OAS3 version:
|
||||
TODO: update the link below once merged
|
||||
- OAS3: https://github.com/Redocly/openapi-cli/blob/full-rewrite/src/types/oas3.ts#L518-L560
|
||||
- OAS2: not supported yet
|
||||
- `any` - visitor will be called on every node
|
||||
- `ref` - visitor will be called on $ref nodes
|
||||
|
||||
The value of each node can be either **visitor function** (runs while going down the tree) or **visitor object** (see below).
|
||||
|
||||
## Visitor Object
|
||||
Visitor object can contain `enter` and/or `leave` visitor functions and `skip` predicate method.
|
||||
|
||||
openapi-cli calls `enter` **visitor function** while going down the tree and `leave` going up the tree.
|
||||
`skip` predidate is called and if it returns `true` the node is is ignored from this visitor.
|
||||
|
||||
|
||||
TBD
|
||||
```js
|
||||
function ExampleRule() {
|
||||
const seen = {};
|
||||
return {
|
||||
DefinitionRoot: {
|
||||
leave() {
|
||||
// check something and report
|
||||
}
|
||||
}
|
||||
Operation: {
|
||||
enter(operation, ctx) {
|
||||
seen[operation.operationId] = true;
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Also, visitor object (if it is not `any` or `ref`) can define [nested visitors](#nested-visitors).
|
||||
|
||||
## Visitors execution and $ref
|
||||
|
||||
Top level **visitor functions** run only once for each node. If the same node is referenced via $ref multiple times top-level **visitor functions** will be executed only once for this node.
|
||||
|
||||
This works fine for most context-free rules which check basic things. If you need contextual info you should use [nested visitors](#nested-visitors).
|
||||
|
||||
## Nested Visitors
|
||||
|
||||
Here is basic example of nested visitor:
|
||||
|
||||
```js
|
||||
function ExampleRule() {
|
||||
const seen = {};
|
||||
return {
|
||||
Operation: {
|
||||
// skip: (value, key) => ... // if needed
|
||||
// enter(operation) {} // if needed
|
||||
Schema(schema, ctx, parents) {
|
||||
console.log(`type ${schema.type} from ${parents.Operation.operationId}`)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The `Schema` **visit function** will be called by openapi-cli if only Schema Object is encountered while traversing a tree ahile Operation Object is **entered**.
|
||||
|
||||
As the third argument the **visitor function** accpets the `parents` object with corresponding parent nodes as defined in the **visitor object**.
|
||||
|
||||
|
||||
> Note: it will be executed only for the first level of Schema Object.
|
||||
|
||||
For the example document below:
|
||||
|
||||
```yaml
|
||||
get:
|
||||
operationId: get
|
||||
parameters:
|
||||
- name: a
|
||||
in: path
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
a:
|
||||
type: boolean
|
||||
put:
|
||||
operationId: put
|
||||
parameters:
|
||||
- name: a
|
||||
in: path
|
||||
schema:
|
||||
type: number
|
||||
```
|
||||
|
||||
The visitor above will log the following:
|
||||
|
||||
```
|
||||
type string from get
|
||||
tpye object from get
|
||||
type number from put
|
||||
```
|
||||
|
||||
## The Context Object
|
||||
|
||||
The context object contains additional functionality that is helpful for rules to do their jobs. As the name implies, the context object contains information that is relevant to the context of the rule. The context object has the following properties:
|
||||
|
||||
- `location` - current location in the source document. See [Location Object](#location-object)
|
||||
- `parentLocations` - mapping of parent node to its location (only for nested visitors)
|
||||
- `type` - information about current type from type tree
|
||||
- `parent` - parent object or array
|
||||
- `key` - key in parent object or array
|
||||
- `oasVersion` specific OAS minor version of current document (can be `oas2`, `oas3` or `oas3_1`).
|
||||
|
||||
|
||||
Additionally, the context object has the following methods:
|
||||
|
||||
- `report(descriptor)` - reports a problem in the definition (see the dedicated section).
|
||||
- `resolve(node)` - synchornosly dereferences $ref node to its value. Works only with $refs from the original document.
|
||||
|
||||
|
||||
## Location Object
|
||||
|
||||
Location is the class with the folowing fields:
|
||||
|
||||
`source` - current document source
|
||||
`pointer` - pointer within the document to the node
|
||||
`abolutePointer` - absolute pointer to the node (including source document absolute ref)
|
||||
|
||||
and the following methods:
|
||||
|
||||
`key()` - returns new Location pointing to the current node key instead of value (used to highlight the key in codeframes)
|
||||
`child(propName)` - returns new Location pointing to the `propName` of the current node. `propName` can be array of strings to point deep.
|
||||
|
||||
|
||||
## context.report()
|
||||
|
||||
The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:
|
||||
|
||||
`message` - {string} the problem message.
|
||||
`location` - {Location} (optional) an object specifying the location of the problem. Can be constructed using location object methods.
|
||||
`suggest` - {string[]} (optional) - "did you mean" suggestion
|
||||
`from` - {Location} (optional) - referenced by location
|
||||
|
||||
The simplest example is to use just message:
|
||||
|
||||
```js
|
||||
context.report({
|
||||
message: "Unexpected identifier"
|
||||
});
|
||||
```
|
||||
|
||||
By default, the message is reported at the current node location.
|
||||
125
docs/plugins.md
125
docs/plugins.md
@@ -0,0 +1,125 @@
|
||||
# Working with Plugins
|
||||
|
||||
Plugins can be used to extend behaviour of `@redocly/openapi-cli`. Each plugin is an javascript module which can export custom rules, preprocessors, decorators or type tree extenstions.
|
||||
|
||||
## Plugin structure
|
||||
|
||||
The minimal plugin should export `id` string:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
id: 'my-local-plugin'
|
||||
}
|
||||
```
|
||||
|
||||
## OAS Major versions
|
||||
|
||||
Everything that is exported from plugin can be related to one of supported OAS3 major versions. It is done by exporting object containing key-value mapping from major OAS version (`oas2` or `oas3` are supported) to the extension object (rules, prerpocessors, etc.).
|
||||
|
||||
Before processing the definition document openapi-cli detects the OAS version and applies corresponding set of extensions.
|
||||
|
||||
## Rules in Plugins
|
||||
|
||||
Plugins can expose additional rules for use in openapi-cli. To do so, the plugin must export a `rules` object containing a key-value mapping of rule ID to rule. The rule ID does not have to follow any naming convention (so it can just be `tag-name`, for instance). The rules defined in
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
id: 'my-local-plugin',
|
||||
rules: {
|
||||
oas3: {
|
||||
'tag-name': () => {
|
||||
//...
|
||||
},
|
||||
}
|
||||
oas2: {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To use the rule in openapi-cli, you would use the plugin name, followed by a slash, followed by the rule name. So if this plugin id is `my-local-plugin`, then in your configuration you'd refer to the rule by the name `my-local-plugin/tag-name`. Example: `"rules": {"my-local-plugin/tag-name": "error"}`.
|
||||
|
||||
See [rules documentation](./custom-rules.md)
|
||||
|
||||
## Preprocessors and Decorators in Plugins
|
||||
|
||||
In order to create a preprocessor or decorators, the object that is exported from your module has to conform to the following interface:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
id: 'my-local-plugin`,
|
||||
preprocessors: {
|
||||
oas3: {
|
||||
"processor-id": () => {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
},
|
||||
decorators: {
|
||||
oas3: {
|
||||
"decorator-id": () => {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [preprocessors and decorators documentation](./preprocessors-and-decorators.md)
|
||||
|
||||
## Configs in Plugins
|
||||
|
||||
You can bundle configurations inside a plugin by specifying them under the `configs` key. Multiple configurations are supported per plugin. Note that it is not possible to specify a default configuration for a given plugin and that users must specify in their configuration file when they want to use one.
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
id: 'my-local-plugin'
|
||||
configs: {
|
||||
all: {
|
||||
rules: {
|
||||
'operation-id-not-test': 'error',
|
||||
'boolean-parameter-prefixes': 'error',
|
||||
},
|
||||
},
|
||||
minimal: {
|
||||
rules: {
|
||||
'operation-id-not-test': 'off',
|
||||
'boolean-parameter-prefixes': 'error',
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If the example plugin above id was `my-local-plugin`, the `all` and `minimal` configurations would then be usable by extending off of `"my-local-plugin/all"` and `"my-local-plugin/minimal"`, respectively.
|
||||
|
||||
```yaml
|
||||
extends:
|
||||
- my-local-plugin/all
|
||||
```
|
||||
|
||||
|
||||
## Type Extensions in plugins
|
||||
|
||||
See [type extensions](./type-extensions.md)
|
||||
|
||||
Plugin can define certain type extension. To do so it must export `typeExtension` property:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
id: 'my-local-plugin',
|
||||
typeExtension: {
|
||||
oas3(types) {
|
||||
// modify types here
|
||||
return {
|
||||
...types,
|
||||
// add new or modify existing
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Share Plugins
|
||||
|
||||
Community plugins are not supported yet.
|
||||
|
||||
|
||||
14
docs/preprocessors-and-decorators.md
Normal file
14
docs/preprocessors-and-decorators.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Preprocessors and Decorators
|
||||
|
||||
|
||||
## Rationale
|
||||
|
||||
There might appear such situation, when the input data should be mutated before or after validation and/or bundling. For this case, you might want to run custom preprocessors before or decorators after bundling.
|
||||
|
||||
## Preprocessors
|
||||
|
||||
> NOTE: this feature is experimental. Mutating OpenAPI definition may break codeframes in errors.
|
||||
|
||||
## Decorators
|
||||
|
||||
TBD
|
||||
@@ -1,13 +0,0 @@
|
||||
# Preprocessors
|
||||
|
||||
> NOTE: this feature is experimental. Modifying your data may break codeframes in errors.
|
||||
|
||||
## Rationale
|
||||
|
||||
There might appear such situation, when the input data should be mutated before applying default rules and/or bundling. For this case, you might want to run certain custom preprocessors before rules.
|
||||
|
||||
You can do it with `preprocessors`.
|
||||
|
||||
## Usage
|
||||
|
||||
TBD
|
||||
@@ -8,11 +8,11 @@ Type tree is built from top level `Types` which can link to child types.
|
||||
|
||||
TBD
|
||||
|
||||
This tree can be modified.
|
||||
This tree can be extended or even modified.
|
||||
|
||||
## Extending type definitions
|
||||
|
||||
Type tree can be extended using by [plugin](./plugin.md) It should follow the following pattern (similar to reducers):
|
||||
Type tree can be extended by exporting `typeExtension` function from [plugin](./plugins.md). It should follow the following pattern (similar to reducers):
|
||||
|
||||
```js
|
||||
exports.typeExtension = {
|
||||
|
||||
Reference in New Issue
Block a user