-
Notifications
You must be signed in to change notification settings - Fork 2
/
isp-rules.yaml
179 lines (179 loc) · 5.74 KB
/
isp-rules.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
formats:
- "oas3"
extends: spectral:oas
functionsDir: isp-functions
functions:
- contains
- english
- iso8601
- noun
rules:
# English recommendations for description fields
english:
severity: warn
given: $..description
then:
function: english
# Use JSON as much as possible
json-responses:
severity: error
message: "{{description}}: {{error}}"
given: $..responses.[?(@property == '200' || @ == '201' || @ == '202' || @ == '400' || @ == '401' || @ == '403' || @ == '404' || @ == '422' || @ == '500')].content
then:
function: contains
functionOptions:
match: "json"
patch-request-content-type:
severity: error
description: "`PATCH` requests cannot use `application/json`"
given: $.paths.*.[?(@property == 'patch')].requestBody.content[?(@property == 'application/json')]^
then:
function: falsy
patch-prefer-merge:
severity: warn
description: Prefer `application/merge-patch+json` for `PATCH` requests
given: $.paths.*.[?(@property == 'patch')].requestBody.content
then:
function: contains
functionOptions:
match: 'application/merge-patch\+json'
# Reduce duplication in data structures
# TODO... maybe detect similar strings in JSON paths?
# Version the API
server-version:
severity: error
description: Server URL must include version in the path (except localhost)
given: $.servers.[*].url
then:
function: pattern
functionOptions:
match: "localhost|/v[0-9]+$"
# Use ISO 8601 for dates
iso8601:
severity: warn
given: $..parameters.[*]
then:
function: iso8601
# Auth? TODO
# Resource plural nouns
resource-nouns:
severity: error
given: $.paths.*~
then:
function: noun
# DNS & URL friendliness
dns-friendly:
severity: error
description: Identifier parameter missing DNS-friendly pattern, e.g. ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
given: $..parameters[?((@.name || '').match(/^id[ -_A-Z]|[ -_]id$|[a-z0-9]Id$/))]
then:
# Currently this just checks for existence of any pattern, but it should
# both catch small mistakes and not be too limited if params have
# additional constraints (e.g. not limited to a specific pattern).
field: schema.pattern
function: truthy
# Resources should have schemas with descriptions
resource-schemas:
severity: error
given: $..['application/json']
then:
field: schema
function: truthy
schema-descriptions:
severity: warn
given: $..properties.*
then:
field: description
function: truthy
# Resource field casing
properties-lower-snake-case:
severity: error
given: $..properties[?([email protected]().startsWith("$"))]
then:
function: casing
functionOptions:
type: snake
# Don't POST with identifier
post-with-id:
severity: error
description: Use PUT/PATCH rather than POSTing with a user-supplied identifier
given: $.paths.[?(@property.toString().match(/[ -_]id}$|[a-z]Id}$/))]
then:
field: post
function: falsy
# Listing should return a list & include pagination
listing-returns-list:
severity: warn
message: Type "{{value}}" should be "array" when returning a list of resources
given: $.paths.[?([email protected]().includes("}"))].get.responses.[?(@property.toString().startsWith("2"))].content.*.schema
then:
field: type
function: enumeration
functionOptions:
values:
- array
# Create/update should either repond 204 or with a JSON body
disallow-body:
severity: error
description: 204 response should have no body. Use e.g. 200 otherwise.
given: $..responses.204
then:
field: content
function: falsy
# requiring a body fails for image/json and cannot because because of a spectral
# bug that prevents image/jpeg with type:"string" from parsing.
# TODO: Readd this if spectral ever fixes that bug.
# require-body:
# severity: error
# description: 200 response must have a body. Use 201/204 otherwise.
# given: $..responses.200
# then:
# field: content
# function: truthy
delete-response:
severity: error
description: Delete should return an HTTP 204
given: $.paths.*[?(@property === 'delete')].responses
then:
field: "204"
function: truthy
# Errors must include a `detail` field
error-detail:
severity: error
# 429 returns from the API Gateway, so we exclude it
description: Errors must be problem+JSON or text/plain and include a "detail" field
given: $..responses.[?((@property.toString().startsWith("4") || @property.toString() === "500") && @property.toString() != "429")]
then:
- field: content
function: truthy
- field: content.application/problem+json.schema
function: truthy
- field: content.application/problem+json.schema.properties.detail
function: truthy
# Header & parameter casing
headers-hyphenated-pascal-case:
severity: error
given: "$..parameters[?(@.in == 'header')].name"
description: "'HTTP' headers MUST follow 'Hyphenated-Pascal-Case' notation"
then:
function: pattern
functionOptions:
match: "/^([A-Z][a-z0-9]-)*([A-Z][a-z0-9])+/"
params-lower-snake-case:
severity: error
message: "`{{value}}` must follow `snake` notation"
given: "$..parameters[?(@.in == 'query' || @ == 'path')].name"
then:
function: casing
functionOptions:
type: snake
# Parameter location constraints
params-location:
severity: error
description: Required parameters must be in the URL path or header
given: $..parameters[?(@.in != 'path' && @.in != 'header')]
then:
field: required
function: falsy
# Date/time ranges
# TODO