@aep_dev/aep-openapi-linter 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mike Kistler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # aep-openapi-linter
2
+
3
+ Linter for OpenAPI definitions to check compliance to [AEPs].
4
+
5
+ This repository contains a [Spectral](https://github.com/stoplightio/spectral)
6
+ ruleset to check an [OpenAPI] document for conformance to the [API Enhancement
7
+ Proposals].
8
+
9
+ [AEPs]: https://aep.dev
10
+ [API Enhancement Proposals]: https://aep.dev
11
+ [OpenAPI]: https://www.openapis.org/
12
+
13
+ ## How to use the aep-openapi-linter
14
+
15
+ ### Dependencies
16
+
17
+ The Spectral Ruleset requires Node version 20 or later.
18
+
19
+ ### Install Spectral
20
+
21
+ `npm i @stoplight/spectral-cli -g`
22
+
23
+ ### Usage
24
+
25
+ You can specify the ruleset directly on the command line:
26
+
27
+ `spectral lint -r https://raw.githubusercontent.com/aep-dev/aep-openapi-linter/main/spectral.yaml <api definition file>`
28
+
29
+ Or you can create a Spectral configuration file (`.spectral.yaml`) that
30
+ references the ruleset:
31
+
32
+ ```yaml
33
+ extends:
34
+ - https://raw.githubusercontent.com/aep-dev/aep-openapi-linter/main/spectral.yaml
35
+ ```
36
+
37
+ ### Example
38
+
39
+ ```bash
40
+ spectral lint -r https://raw.githubusercontent.com/aep-dev/aep-openapi-linter/main/spectral.yaml petstore.yaml
41
+ ```
42
+
43
+ ### Using the Spectral VSCode extension
44
+
45
+ There is a
46
+ [Spectral VSCode extension](https://marketplace.visualstudio.com/items?itemName=stoplight.spectral)
47
+ that will run the Spectral linter on an open API definition file and show
48
+ errors right within VSCode. You can use this ruleset with the Spectral VSCode
49
+ extension.
50
+
51
+ 1. Install the Spectral VSCode extension from the extensions tab in VSCode.
52
+ 2. Create a Spectral configuration file (`.spectral.yaml`) in the root
53
+ directory of your project as shown above.
54
+ 3. Set `spectral.rulesetFile` to the name of this configuration file in your
55
+ VSCode settings.
56
+
57
+ Now when you open an OpenAPI document in this project, it should highlight
58
+ lines with errors. You can also get a full list of problems in the file by
59
+ opening the "Problems panel" with "View / Problems". In the Problems panel you
60
+ can filter to show or hide errors, warnings, or infos.
61
+
62
+ ## Contributing
63
+
64
+ See [CONTRIBUTING](./CONTRIBUTING.md) for more details.
package/aep/0004.yaml ADDED
@@ -0,0 +1,38 @@
1
+ rules:
2
+ aep-0004-x-aep-resource-structure:
3
+ description: x-aep-resource must have correct structure with required fields (singular, plural)
4
+ message: The x-aep-resource extension does not conform to AEP-4 requirements
5
+ severity: error
6
+ formats: ['oas2', 'oas3']
7
+ given: $.components.schemas[?(@['x-aep-resource'] && @['x-aep-resource'] != true)]
8
+ then:
9
+ field: x-aep-resource
10
+ function: schema
11
+ functionOptions:
12
+ schema:
13
+ type: object
14
+ required: [singular, plural, patterns, type]
15
+ properties:
16
+ type:
17
+ type: string
18
+ pattern: '^[a-z][a-z0-9./-]*/[a-z][a-z0-9-]*$'
19
+ singular:
20
+ type: string
21
+ pattern: '^[a-z][a-z0-9-]*$'
22
+ plural:
23
+ type: string
24
+ pattern: '^[a-z][a-z0-9-]*$'
25
+ patterns:
26
+ type: array
27
+ minItems: 1
28
+ items:
29
+ type: string
30
+ pattern: '^[a-z][a-z0-9-]*(/([a-z][a-z0-9-]*|\{[a-z][a-z0-9_]*\}))*$'
31
+ parents:
32
+ type: array
33
+ items:
34
+ type: string
35
+ pattern: '^[a-z][a-z0-9-]*$'
36
+ singleton:
37
+ type: boolean
38
+ additionalProperties: false
package/aep/0122.yaml ADDED
@@ -0,0 +1,110 @@
1
+ aliases:
2
+ AepResource:
3
+ description: A schema that represents an AEP resource
4
+ targets:
5
+ - formats: ['oas2', 'oas3']
6
+ given:
7
+ - $.components.schemas[?(@['x-aep-resource'])]
8
+ - $.definitions[?(@['x-aep-resource'])]
9
+
10
+ rules:
11
+ aep-122-resource-path-field:
12
+ description: Resources must have a 'path' field of type string.
13
+ message: Resource schema must include a 'path' property of type 'string'
14
+ severity: error
15
+ formats: ['oas2', 'oas3']
16
+ given: '#AepResource'
17
+ then:
18
+ function: schema
19
+ functionOptions:
20
+ schema:
21
+ type: object
22
+ properties:
23
+ properties:
24
+ type: object
25
+ properties:
26
+ path:
27
+ type: object
28
+ properties:
29
+ type:
30
+ const: string
31
+ required: ['type']
32
+ required: ['path']
33
+ required: ['properties']
34
+
35
+ aep-122-collection-identifier-format:
36
+ description:
37
+ Collection identifiers must begin with a lowercase letter and contain only lowercase letters, numbers, and
38
+ hyphens.
39
+ message: Collection identifiers must match the pattern /[a-z][a-z0-9-]*/
40
+ severity: error
41
+ formats: ['oas2', 'oas3']
42
+ given: $.paths.*~
43
+ then:
44
+ function: pattern
45
+ functionOptions:
46
+ # Ensure collection segments start with lowercase letter
47
+ # and only contain lowercase letters, numbers, and hyphens
48
+ # Allow optional custom method suffix (e.g., :archive) without validating its format
49
+ match: '^(/[a-z][a-z0-9-]*(/\{[^}]+\})?)+(:[^/]*)?$'
50
+
51
+ aep-122-parent-field-type:
52
+ description: Parent fields in request parameters must be of type string.
53
+ message: The 'parent' parameter must be of type 'string'
54
+ severity: error
55
+ formats: ['oas2', 'oas3']
56
+ given:
57
+ - $.paths.*.*.parameters[?(@.name == 'parent')]
58
+ - $.paths.*.parameters[?(@.name == 'parent')]
59
+ - $.components.parameters[?(@.name == 'parent')]
60
+ then:
61
+ function: schema
62
+ functionOptions:
63
+ schema:
64
+ type: object
65
+ properties:
66
+ schema:
67
+ type: object
68
+ properties:
69
+ type:
70
+ const: string
71
+ required: ['type']
72
+
73
+ aep-122-resource-id-type:
74
+ description: All resource ID fields must be of type string.
75
+ message: Resource ID fields (ending in '_id' or named 'id') must be of type 'string'
76
+ severity: error
77
+ formats: ['oas2', 'oas3']
78
+ given:
79
+ - "#AepResource.properties[?(@property.match(/_id$/) || @property == 'id')]"
80
+ then:
81
+ function: schema
82
+ functionOptions:
83
+ schema:
84
+ type: object
85
+ properties:
86
+ type:
87
+ const: string
88
+
89
+ aep-122-no-path-suffix:
90
+ description: Field names should not use the '_path' suffix unless necessary for disambiguation.
91
+ message:
92
+ "Avoid using '_path' suffix in field names; use the field name directly (e.g., 'book' instead of 'book_path')"
93
+ severity: warn
94
+ formats: ['oas2', 'oas3']
95
+ given:
96
+ - '#AepResource.properties.*~'
97
+ then:
98
+ function: pattern
99
+ functionOptions:
100
+ notMatch: '_path$'
101
+
102
+ aep-122-no-self-links:
103
+ description: Resources must not have a 'self_link' field.
104
+ message: Resources must not contain a 'self_link' field; use 'path' instead
105
+ severity: error
106
+ formats: ['oas2', 'oas3']
107
+ given:
108
+ - '#AepResource.properties.self_link'
109
+ then:
110
+ function: falsy
package/aep/0131.yaml ADDED
@@ -0,0 +1,78 @@
1
+ aliases:
2
+ GetOperation:
3
+ description: A Get operation is a get on path that ends in a path parameter
4
+ targets:
5
+ - formats: ['oas2', 'oas3']
6
+ given:
7
+ # first condition excludes custom methods and second condition matches paths ending in a path parameter
8
+ - $.paths[?(!@property.match(/:[^/]*$/) && @property.match(/\}$/))].get
9
+
10
+ rules:
11
+ aep-131-operation-id:
12
+ description: The operation ID should be Get{resource-singular}.
13
+ message: The operation ID does not conform to AEP-131
14
+ severity: error
15
+ formats: ['oas2', 'oas3']
16
+ given: '#GetOperation'
17
+ then:
18
+ function: schema
19
+ functionOptions:
20
+ schema:
21
+ type: object
22
+ properties:
23
+ operationId:
24
+ type: string
25
+ pattern: '^[Gg][Ee][Tt][A-Z].*$'
26
+ required: ['operationId']
27
+
28
+ aep-131-request-body:
29
+ description: A get operation must not accept a request body.
30
+ severity: error
31
+ formats: ['oas3']
32
+ given:
33
+ - '#GetOperation.requestBody'
34
+ then:
35
+ function: falsy
36
+
37
+ aep-131-required-params:
38
+ description: A standard Get method must not have any required parameters other than path parameters.
39
+ severity: error
40
+ formats: ['oas2', 'oas3']
41
+ given:
42
+ - '#GetOperation.parameters[?(@.in != "path")]'
43
+ - '#GetOperation^.parameters[?(@.in != "path")]' # parameters at path item level
44
+ then:
45
+ field: required
46
+ function: falsy
47
+
48
+ aep-131-response-body:
49
+ description: The response must be the AEP resource.
50
+ message: The response body is not an AEP resource.
51
+ severity: error
52
+ formats: ['oas3']
53
+ given: '#GetOperation.responses.200.content[*].schema'
54
+ then:
55
+ function: schema
56
+ functionOptions:
57
+ schema:
58
+ type: object
59
+ required: ['x-aep-resource']
60
+
61
+ aep-131-unknown-optional-params:
62
+ description: A standard Get method should not have unknown optional parameters.
63
+ severity: warn
64
+ formats: ['oas2', 'oas3']
65
+ given:
66
+ - '#GetOperation.parameters[?(@.in == "query")]' # only check query parameters
67
+ - '#GetOperation^.parameters[?(@.in == "query")]' # parameters at path item level
68
+ then:
69
+ # Use schema function on name so that the error points to the name field
70
+ function: schema
71
+ functionOptions:
72
+ schema:
73
+ type: object
74
+ properties:
75
+ name:
76
+ enum:
77
+ - 'read_mask' # AEP-157 Partial responses
78
+ - 'view' # AEP-157 Partial responses
package/aep/0132.yaml ADDED
@@ -0,0 +1,115 @@
1
+ aliases:
2
+ ListOperation:
3
+ description: A list operation is a get on path that does not end in a path parameter
4
+ targets:
5
+ - formats: ['oas2', 'oas3']
6
+ given:
7
+ # first condition excludes custom methods and second condition excludes paths ending in a path parameter
8
+ - $.paths[?(!@property.match(/:[^/]*$/) && !@property.match(/\}$/))].get
9
+
10
+ functionsDir: ../functions
11
+ functions:
12
+ - skipSingletonsSchema
13
+
14
+ rules:
15
+ aep-132-http-body:
16
+ description: A list operation must not accept a request body.
17
+ severity: error
18
+ formats: ['oas3']
19
+ given:
20
+ - '#ListOperation.requestBody'
21
+ then:
22
+ function: falsy
23
+
24
+ aep-132-operation-id:
25
+ description: The operation ID should be List{resource-singular}.
26
+ message: The operation ID does not conform to AEP-132
27
+ severity: error
28
+ formats: ['oas2', 'oas3']
29
+ given: '#ListOperation'
30
+ then:
31
+ function: skipSingletonsSchema
32
+ functionOptions:
33
+ schema:
34
+ type: object
35
+ properties:
36
+ operationId:
37
+ type: string
38
+ pattern: '^[Ll][Ii][Ss][Tt][A-Z].*$'
39
+ required: ['operationId']
40
+
41
+ aep-132-param-types:
42
+ description: List operation must use the correct type for any optional parameters.
43
+ severity: error
44
+ formats: ['oas3']
45
+ given:
46
+ - '#ListOperation.parameters'
47
+ then:
48
+ function: schema
49
+ functionOptions:
50
+ schema:
51
+ type: array
52
+ items:
53
+ oneOf:
54
+ - type: object
55
+ properties:
56
+ name:
57
+ type: string
58
+ enum: ['filter']
59
+ in:
60
+ type: string
61
+ enum: ['query']
62
+ schema:
63
+ type: object
64
+ properties:
65
+ type:
66
+ type: string
67
+ enum: ['string']
68
+ required: ['name']
69
+ - type: object
70
+ properties:
71
+ name:
72
+ type: string
73
+ enum: ['order_by']
74
+ in:
75
+ type: string
76
+ enum: ['query']
77
+ schema:
78
+ type: object
79
+ properties:
80
+ type:
81
+ type: string
82
+ enum: ['string']
83
+ required: ['name']
84
+ - type: object
85
+ properties:
86
+ name:
87
+ type: string
88
+ enum: ['show_deleted']
89
+ in:
90
+ type: string
91
+ enum: ['query']
92
+ schema:
93
+ type: object
94
+ properties:
95
+ type:
96
+ type: string
97
+ enum: ['boolean']
98
+ required: ['name']
99
+ - type: object
100
+ properties:
101
+ name:
102
+ type: string
103
+ not:
104
+ enum: ['filter', 'order_by', 'show_deleted']
105
+ required: ['name']
106
+
107
+ aep-132-required-params:
108
+ description: List operation should have no required parameters (except path parameters)
109
+ severity: error
110
+ formats: ['oas3']
111
+ given:
112
+ - '#ListOperation.parameters[?(@.in != "path")]'
113
+ then:
114
+ field: required
115
+ function: falsy
package/aep/0133.yaml ADDED
@@ -0,0 +1,165 @@
1
+ aliases:
2
+ CreateOperation:
3
+ description: A create operation is a post on path without a ":" in the final segment
4
+ targets:
5
+ - formats: ['oas2', 'oas3']
6
+ given:
7
+ # first condition excludes custom methods and second condition excludes paths ending in a path parameter
8
+ - $.paths[?(!@property.match(/:[^/]*$/) && !@property.match(/\}$/))].post
9
+
10
+ rules:
11
+ aep-133-id-parameter:
12
+ description: A create operation should have an id parameter.
13
+ message: The id parameter is missing.
14
+ severity: warn
15
+ formats: ['oas2', 'oas3']
16
+ given:
17
+ - '#CreateOperation'
18
+ then:
19
+ function: schema
20
+ functionOptions:
21
+ schema:
22
+ type: object
23
+ required: ['parameters']
24
+ properties:
25
+ parameters:
26
+ type: array
27
+ contains:
28
+ type: object
29
+ required: ['name']
30
+ properties:
31
+ name:
32
+ enum: ['id']
33
+
34
+ aep-133-operation-id:
35
+ description: The operation ID should be Create{resource-singular}.
36
+ message: The operation ID does not conform to AEP-133.
37
+ severity: error
38
+ formats: ['oas2', 'oas3']
39
+ given: '#CreateOperation'
40
+ then:
41
+ function: schema
42
+ functionOptions:
43
+ schema:
44
+ type: object
45
+ properties:
46
+ operationId:
47
+ type: string
48
+ pattern: '^[Cc][Rr][Ee][Aa][Tt][Ee][A-Z].*$'
49
+ required: ['operationId']
50
+
51
+ aep-133-param-types:
52
+ description: The id parameter should be a query parameter of type string.
53
+ severity: error
54
+ formats: ['oas2', 'oas3']
55
+ given:
56
+ - '#CreateOperation.parameters[?(@.name == "id")]'
57
+ then:
58
+ function: schema
59
+ functionOptions:
60
+ schema:
61
+ type: object
62
+ required: ['in', 'schema']
63
+ properties:
64
+ in:
65
+ enum: ['query']
66
+ schema:
67
+ type: object
68
+ required: ['type']
69
+ properties:
70
+ type:
71
+ type: string
72
+ enum: ['string']
73
+
74
+ aep-133-request-body:
75
+ description: A standard create method must accept the resource in the request body.
76
+ message: The request body is not an AEP resource.
77
+ severity: error
78
+ formats: ['oas3']
79
+ given: '#CreateOperation'
80
+ then:
81
+ function: schema
82
+ functionOptions:
83
+ schema:
84
+ type: object
85
+ required: ['requestBody']
86
+ properties:
87
+ requestBody:
88
+ type: object
89
+ required: ['content']
90
+ properties:
91
+ content:
92
+ type: object
93
+ additionalProperties:
94
+ type: object
95
+ required: ['schema']
96
+ properties:
97
+ schema:
98
+ type: object
99
+ required: ['x-aep-resource']
100
+
101
+ aep-133-required-params:
102
+ description: A create operation must not have any required parameters other than path parameters.
103
+ severity: error
104
+ formats: ['oas2', 'oas3']
105
+ given:
106
+ - '#CreateOperation.parameters[?(@.in != "path")]'
107
+ - '#CreateOperation^.parameters[?(@.in != "path")]' # parameters at path item level
108
+ then:
109
+ field: required
110
+ function: falsy
111
+
112
+ aep-133-response-body:
113
+ description: The success response for a create operation must be the created AEP resource.
114
+ message: The response body is not an AEP resource.
115
+ severity: error
116
+ formats: ['oas3']
117
+ given: '#CreateOperation'
118
+ then:
119
+ function: schema
120
+ functionOptions:
121
+ schema:
122
+ type: object
123
+ required: ['responses']
124
+ properties:
125
+ responses:
126
+ type: object
127
+ anyOf:
128
+ - required: ['200']
129
+ - required: ['201']
130
+ properties:
131
+ '200':
132
+ $ref: '#/definitions/response'
133
+ '201':
134
+ $ref: '#/definitions/response'
135
+ definitions:
136
+ response:
137
+ type: object
138
+ required: ['content']
139
+ properties:
140
+ content:
141
+ type: object
142
+ additionalProperties:
143
+ type: object
144
+ required: ['schema']
145
+ properties:
146
+ schema:
147
+ type: object
148
+ required: ['x-aep-resource']
149
+
150
+ aep-133-unknown-optional-params:
151
+ description: A create operation should not have unknown optional parameters.
152
+ severity: warn
153
+ formats: ['oas2', 'oas3']
154
+ given:
155
+ - '#CreateOperation.parameters[?(@.in == "query")]' # only check query parameters
156
+ - '#CreateOperation^.parameters[?(@.in == "query")]' # parameters at path item level
157
+ then:
158
+ function: schema
159
+ functionOptions:
160
+ schema:
161
+ type: object
162
+ properties:
163
+ name:
164
+ type: string
165
+ enum: ['id']