api-quality-spectral-ruleset 1.5.0-beta.2 → 1.5.0-beta.4
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/apq-spectral.yaml +104 -171
- package/functions/apq-allowed-http-verbs.js +31 -0
- package/functions/apq-alternate-paths.js +8 -4
- package/functions/apq-check-ambiguous-path.js +24 -38
- package/functions/apq-example-schema-types.js +116 -0
- package/functions/apq-forbidden-query-format.js +29 -0
- package/functions/apq-mandatory-response-codes.js +36 -0
- package/functions/apq-numeric-invalid-format.js +64 -0
- package/functions/apq-numeric-missing-format.js +58 -0
- package/functions/apq-numeric-well-defined-format.js +64 -0
- package/functions/apq-rate-limit-response.js +21 -0
- package/functions/apq-require-response-on-path-params.js +12 -23
- package/functions/apq-required-fields-exist.js +30 -5
- package/functions/apq-response-media-type.js +28 -3
- package/functions/apq-schema-format.js +17 -0
- package/functions/apq-standard-response-schema.js +318 -0
- package/functions/apq-string-parameter-integrity.js +35 -0
- package/functions/apq-url-naming-convention.js +59 -0
- package/package.json +1 -1
package/apq-spectral.yaml
CHANGED
|
@@ -34,6 +34,17 @@ functions:
|
|
|
34
34
|
- apq-query-params-optional
|
|
35
35
|
- apq-wso2-scopes-valid
|
|
36
36
|
- apq-numeric-path-param
|
|
37
|
+
- apq-numeric-invalid-format
|
|
38
|
+
- apq-numeric-missing-format
|
|
39
|
+
- apq-numeric-well-defined-format
|
|
40
|
+
- apq-string-parameter-integrity
|
|
41
|
+
- apq-example-schema-types
|
|
42
|
+
- apq-standard-response-schema
|
|
43
|
+
- apq-allowed-http-verbs
|
|
44
|
+
- apq-url-naming-convention
|
|
45
|
+
- apq-mandatory-response-codes
|
|
46
|
+
- apq-rate-limit-response
|
|
47
|
+
- apq-forbidden-query-format
|
|
37
48
|
extends:
|
|
38
49
|
- spectral:asyncapi
|
|
39
50
|
- spectral:oas
|
|
@@ -111,12 +122,14 @@ rules:
|
|
|
111
122
|
function: truthy
|
|
112
123
|
apiq:OAR008:
|
|
113
124
|
description: "HTTP verbs not encouraged."
|
|
114
|
-
message: "
|
|
125
|
+
message: "{{error}}"
|
|
115
126
|
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR008.md"
|
|
116
127
|
severity: error
|
|
117
|
-
given: "$.paths[*]
|
|
128
|
+
given: "$.paths[*]"
|
|
118
129
|
then:
|
|
119
|
-
function:
|
|
130
|
+
function: apq-allowed-http-verbs
|
|
131
|
+
functionOptions:
|
|
132
|
+
allowed-verbs: "get,post,put,delete,patch"
|
|
120
133
|
apiq:OAR009:
|
|
121
134
|
description: "Default request media type should be defined for operations."
|
|
122
135
|
message: "OAR009: Default request media type is mandatory."
|
|
@@ -132,18 +145,21 @@ rules:
|
|
|
132
145
|
given: "$.paths[*][get,post,put,patch,delete]"
|
|
133
146
|
then:
|
|
134
147
|
function: apq-response-media-type
|
|
148
|
+
functionOptions:
|
|
149
|
+
default-media-type: "application/json"
|
|
150
|
+
media-type-exceptions: "-"
|
|
135
151
|
apiq:OAR011:
|
|
136
|
-
description: "URLs should follow the
|
|
137
|
-
message: "
|
|
152
|
+
description: "URLs should follow the configured naming convention (default kebab-case): all literal path segments must comply."
|
|
153
|
+
message: "{{error}}"
|
|
138
154
|
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR011.md"
|
|
139
155
|
severity: error
|
|
140
156
|
given:
|
|
141
157
|
- "$.paths[*]~"
|
|
142
158
|
- "$.servers[*].url"
|
|
143
159
|
then:
|
|
144
|
-
function:
|
|
160
|
+
function: apq-url-naming-convention
|
|
145
161
|
functionOptions:
|
|
146
|
-
|
|
162
|
+
naming-convention: "kebab-case"
|
|
147
163
|
apiq:OAR012:
|
|
148
164
|
description: "Path params, query params, object names and property names should follow the configured naming convention. You can configure snake_case (default), kebab-case, camelCase or UpperCamelCase"
|
|
149
165
|
message: "OAR012: Path params, query params, object names and property names must follow the configured naming convention."
|
|
@@ -196,29 +212,10 @@ rules:
|
|
|
196
212
|
message: "OAR016: Numeric types must use a valid format for their type."
|
|
197
213
|
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR016.md"
|
|
198
214
|
severity: error
|
|
199
|
-
|
|
215
|
+
resolved: false
|
|
216
|
+
given: "$..[?(@ && @.type)]"
|
|
200
217
|
then:
|
|
201
|
-
function:
|
|
202
|
-
functionOptions:
|
|
203
|
-
schema:
|
|
204
|
-
required: [format]
|
|
205
|
-
allOf:
|
|
206
|
-
- if:
|
|
207
|
-
properties:
|
|
208
|
-
type:
|
|
209
|
-
const: integer
|
|
210
|
-
then:
|
|
211
|
-
properties:
|
|
212
|
-
format:
|
|
213
|
-
enum: [int32, int64, uint32, uint64]
|
|
214
|
-
- if:
|
|
215
|
-
properties:
|
|
216
|
-
type:
|
|
217
|
-
const: number
|
|
218
|
-
then:
|
|
219
|
-
properties:
|
|
220
|
-
format:
|
|
221
|
-
enum: [float, double, decimal]
|
|
218
|
+
function: apq-numeric-invalid-format
|
|
222
219
|
apiq:OAR017:
|
|
223
220
|
description: "Resource path should alternate static and parametrized parts."
|
|
224
221
|
message: "OAR017: Resource path should alternate static and parametrized parts"
|
|
@@ -228,7 +225,7 @@ rules:
|
|
|
228
225
|
then:
|
|
229
226
|
function: apq-alternate-paths
|
|
230
227
|
functionOptions:
|
|
231
|
-
|
|
228
|
+
exclude_patterns: "get,me,search,delete"
|
|
232
229
|
apiq:OAR018:
|
|
233
230
|
description: Operation not recommended for resource path depending on HTTP verb
|
|
234
231
|
message: "OAR018: Operation not recommended for resource path: {{path}} ({{verb}})"
|
|
@@ -368,46 +365,26 @@ rules:
|
|
|
368
365
|
field: "headers.Location.schema.type"
|
|
369
366
|
function: truthy
|
|
370
367
|
apiq:OAR028:
|
|
371
|
-
description: "$filter must be defined as a query parameter in
|
|
372
|
-
message: "
|
|
368
|
+
description: "$filter must be defined as a query parameter in the collection GET operations selected by the configured paths."
|
|
369
|
+
message: "{{error}}"
|
|
373
370
|
severity: warn
|
|
374
|
-
given: "$.paths
|
|
371
|
+
given: "$.paths"
|
|
375
372
|
then:
|
|
376
|
-
function: apq-
|
|
373
|
+
function: apq-collection-query-param-required
|
|
374
|
+
functionOptions:
|
|
375
|
+
parameterName: "$filter"
|
|
376
|
+
paths: "/examples"
|
|
377
|
+
pathValidationStrategy: "/include"
|
|
377
378
|
apiq:OAR029:
|
|
378
379
|
description: "A response not compliant with the standard may cause application issues."
|
|
379
|
-
message: "
|
|
380
|
+
message: "{{error}}"
|
|
380
381
|
severity: error
|
|
381
|
-
given: "$.
|
|
382
|
+
given: "$.paths"
|
|
382
383
|
then:
|
|
383
|
-
function: schema
|
|
384
|
+
function: apq-standard-response-schema
|
|
384
385
|
functionOptions:
|
|
385
|
-
schema:
|
|
386
|
-
|
|
387
|
-
properties:
|
|
388
|
-
properties:
|
|
389
|
-
type: object
|
|
390
|
-
properties:
|
|
391
|
-
error:
|
|
392
|
-
type: object
|
|
393
|
-
properties:
|
|
394
|
-
properties:
|
|
395
|
-
type: object
|
|
396
|
-
properties:
|
|
397
|
-
code:
|
|
398
|
-
type: string
|
|
399
|
-
message:
|
|
400
|
-
type: string
|
|
401
|
-
details:
|
|
402
|
-
type: array
|
|
403
|
-
httpStatus:
|
|
404
|
-
type: integer
|
|
405
|
-
required:
|
|
406
|
-
- code
|
|
407
|
-
- message
|
|
408
|
-
- httpStatus
|
|
409
|
-
required:
|
|
410
|
-
- properties
|
|
386
|
+
response-schema: '{"type":"object","properties":{"status":{"type":"object","properties":{"code":{"type":"integer"},"description":{"type":"string"},"internal_code":{"type":"string"},"errors":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}}}},"required":["code"]},"payload":{"type":"any"}},"required":["status","payload"]}'
|
|
387
|
+
path-exclusions: "/status"
|
|
411
388
|
apiq:OAR030:
|
|
412
389
|
description: "The configured status endpoint must be declared with the configured HTTP method."
|
|
413
390
|
message: "OAR030: The required status endpoint is not declared or does not have the required method."
|
|
@@ -435,13 +412,13 @@ rules:
|
|
|
435
412
|
validateProperty: true
|
|
436
413
|
apiq:OAR032:
|
|
437
414
|
description: "Ambiguous path parts not encouraged."
|
|
438
|
-
message: "
|
|
415
|
+
message: "{{error}}"
|
|
439
416
|
severity: error
|
|
440
417
|
given: "$.paths.*~"
|
|
441
418
|
then:
|
|
442
419
|
function: apq-check-ambiguous-path
|
|
443
420
|
functionOptions:
|
|
444
|
-
ambiguous-
|
|
421
|
+
ambiguous-names: "elementos,instancias,recursos,valores,terminos,objetos,articulos,elements,instances,resources,values,terms,objects,items"
|
|
445
422
|
apiq:OAR033:
|
|
446
423
|
description: "Request operation parameters must not include forbidden headers (Accept, Content-Type, Authorization). Note: This validates REQUEST headers, not response headers (see OAR053/114 for response header validation)."
|
|
447
424
|
message: "OAR033: The request header parameter '{{value}}' is not allowed in operations."
|
|
@@ -688,10 +665,10 @@ rules:
|
|
|
688
665
|
message: "OAR052: Numeric types requires a format"
|
|
689
666
|
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR052.md"
|
|
690
667
|
severity: warn
|
|
691
|
-
|
|
668
|
+
resolved: false
|
|
669
|
+
given: "$..[?(@ && @.type)]"
|
|
692
670
|
then:
|
|
693
|
-
|
|
694
|
-
function: truthy
|
|
671
|
+
function: apq-numeric-missing-format
|
|
695
672
|
apiq:OAR053:
|
|
696
673
|
description: "Response headers for API observability and tracing must be defined (excluding 204 responses and health endpoints)."
|
|
697
674
|
message: "OAR053: Response must include mandatory headers and exclude forbidden headers."
|
|
@@ -732,75 +709,59 @@ rules:
|
|
|
732
709
|
path-exclusions: "/status"
|
|
733
710
|
apiq:OAR061:
|
|
734
711
|
description: "Ensure get have mandatory response codes"
|
|
735
|
-
message: "
|
|
712
|
+
message: "{{error}}"
|
|
736
713
|
severity: error
|
|
737
|
-
given: "$.paths[
|
|
714
|
+
given: "$.paths[*][get]"
|
|
738
715
|
then:
|
|
739
|
-
function:
|
|
716
|
+
function: apq-mandatory-response-codes
|
|
740
717
|
functionOptions:
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
- required: ['202']
|
|
745
|
-
- required: ['206']
|
|
746
|
-
not:
|
|
747
|
-
required: ['201', '204']
|
|
718
|
+
mandatory-response-codes: "200, 202, 206"
|
|
719
|
+
paths: "/status, /another"
|
|
720
|
+
pathValidationStrategy: "/exclude"
|
|
748
721
|
apiq:OAR062:
|
|
749
722
|
description: "Ensure post have mandatory response codes"
|
|
750
|
-
message: "
|
|
723
|
+
message: "{{error}}"
|
|
751
724
|
severity: error
|
|
752
|
-
given: "$.paths[
|
|
725
|
+
given: "$.paths[*][post]"
|
|
753
726
|
then:
|
|
754
|
-
function:
|
|
727
|
+
function: apq-mandatory-response-codes
|
|
755
728
|
functionOptions:
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
- required: ['201']
|
|
760
|
-
- required: ['202']
|
|
761
|
-
- required: ['204']
|
|
762
|
-
- required: ['206']
|
|
729
|
+
mandatory-response-codes: "200, 201, 202, 204, 206"
|
|
730
|
+
paths: "/status, /another"
|
|
731
|
+
pathValidationStrategy: "/exclude"
|
|
763
732
|
apiq:OAR063:
|
|
764
733
|
description: "Ensure put have mandatory response codes"
|
|
765
|
-
message: "
|
|
734
|
+
message: "{{error}}"
|
|
766
735
|
severity: error
|
|
767
|
-
given: "$.paths[
|
|
736
|
+
given: "$.paths[*][put]"
|
|
768
737
|
then:
|
|
769
|
-
function:
|
|
738
|
+
function: apq-mandatory-response-codes
|
|
770
739
|
functionOptions:
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
- required: ['202']
|
|
775
|
-
- required: ['204']
|
|
776
|
-
- required: ['206']
|
|
740
|
+
mandatory-response-codes: "200, 202, 204, 206"
|
|
741
|
+
paths: "/status, /another"
|
|
742
|
+
pathValidationStrategy: "/exclude"
|
|
777
743
|
apiq:OAR064:
|
|
778
744
|
description: "Ensure patch have mandatory response codes"
|
|
779
|
-
message: "
|
|
745
|
+
message: "{{error}}"
|
|
780
746
|
severity: error
|
|
781
|
-
given: "$.paths[
|
|
747
|
+
given: "$.paths[*][patch]"
|
|
782
748
|
then:
|
|
783
|
-
function:
|
|
749
|
+
function: apq-mandatory-response-codes
|
|
784
750
|
functionOptions:
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
- required: ['202']
|
|
789
|
-
- required: ['204']
|
|
790
|
-
- required: ['206']
|
|
751
|
+
mandatory-response-codes: "200, 202, 204, 206"
|
|
752
|
+
paths: "/status, /another"
|
|
753
|
+
pathValidationStrategy: "/exclude"
|
|
791
754
|
apiq:OAR065:
|
|
792
755
|
description: "Ensure delete have mandatory response codes"
|
|
793
|
-
message: "
|
|
756
|
+
message: "{{error}}"
|
|
794
757
|
severity: error
|
|
795
|
-
given: "$.paths[
|
|
758
|
+
given: "$.paths[*][delete]"
|
|
796
759
|
then:
|
|
797
|
-
function:
|
|
760
|
+
function: apq-mandatory-response-codes
|
|
798
761
|
functionOptions:
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
- required: ['202']
|
|
803
|
-
- required: ['204']
|
|
762
|
+
mandatory-response-codes: "200, 202, 204"
|
|
763
|
+
paths: "/status, /another"
|
|
764
|
+
pathValidationStrategy: "/exclude"
|
|
804
765
|
apiq:OAR066:
|
|
805
766
|
description: "RequestBody and Responses schema property names must be compliant with the snake_case naming convention."
|
|
806
767
|
message: "OAR066: RequestBody and Responses schema property names must be compliant with the snake_case naming convention."
|
|
@@ -871,10 +832,12 @@ rules:
|
|
|
871
832
|
description: "API should include a 429 response to indicate rate limiting, except for health check paths like /status, /health, /health-check, /ping, /liveness, /readiness."
|
|
872
833
|
message: "OAR073: API should include a 429 response to indicate rate limiting."
|
|
873
834
|
severity: error
|
|
874
|
-
given: "$.paths[
|
|
835
|
+
given: "$.paths[*][get,post,put,patch,delete]"
|
|
875
836
|
then:
|
|
876
|
-
|
|
877
|
-
|
|
837
|
+
function: apq-rate-limit-response
|
|
838
|
+
functionOptions:
|
|
839
|
+
paths: "/status, /health, /health-check, /ping, /liveness, /readiness"
|
|
840
|
+
pathValidationStrategy: "/exclude"
|
|
878
841
|
apiq:OAR074:
|
|
879
842
|
description: "Numeric parameters should define minimum and maximum, or a format restriction."
|
|
880
843
|
message: "OAR074: Numeric parameter should define both 'minimum' and 'maximum', or a 'format' restriction."
|
|
@@ -889,47 +852,25 @@ rules:
|
|
|
889
852
|
description: "String parameters should have minLength, maxLength, pattern (regular expression), or enum restriction."
|
|
890
853
|
message: "OAR075: String parameters should have minLength, maxLength, pattern, or enum restriction."
|
|
891
854
|
severity: error
|
|
892
|
-
|
|
855
|
+
resolved: false
|
|
856
|
+
given:
|
|
857
|
+
- "$.paths[*][*].parameters[*]"
|
|
858
|
+
- "$.paths[*].parameters[*]"
|
|
859
|
+
- "$.components.parameters[*]"
|
|
860
|
+
- "$.parameters[*]"
|
|
893
861
|
then:
|
|
894
|
-
function:
|
|
862
|
+
function: apq-string-parameter-integrity
|
|
895
863
|
functionOptions:
|
|
896
|
-
|
|
897
|
-
type: object
|
|
898
|
-
properties:
|
|
899
|
-
schema:
|
|
900
|
-
anyOf:
|
|
901
|
-
- required: ["minLength"]
|
|
902
|
-
- required: ["maxLength"]
|
|
903
|
-
- required: ["pattern"]
|
|
904
|
-
- required: ["enum"]
|
|
864
|
+
parameter_integrity: "minLength,maxLength,pattern,enum"
|
|
905
865
|
apiq:OAR076:
|
|
906
866
|
description: "Schema should use well-defined type and format."
|
|
907
867
|
message: "OAR076: Schema should use well-defined type and format."
|
|
908
868
|
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR076.md"
|
|
909
869
|
severity: error
|
|
910
|
-
|
|
870
|
+
resolved: false
|
|
871
|
+
given: "$..[?(@ && @.type)]"
|
|
911
872
|
then:
|
|
912
|
-
function:
|
|
913
|
-
functionOptions:
|
|
914
|
-
schema:
|
|
915
|
-
required: [format]
|
|
916
|
-
allOf:
|
|
917
|
-
- if:
|
|
918
|
-
properties:
|
|
919
|
-
type:
|
|
920
|
-
const: integer
|
|
921
|
-
then:
|
|
922
|
-
properties:
|
|
923
|
-
format:
|
|
924
|
-
enum: [int32, int64]
|
|
925
|
-
- if:
|
|
926
|
-
properties:
|
|
927
|
-
type:
|
|
928
|
-
const: number
|
|
929
|
-
then:
|
|
930
|
-
properties:
|
|
931
|
-
format:
|
|
932
|
-
enum: [float, double]
|
|
873
|
+
function: apq-numeric-well-defined-format
|
|
933
874
|
apiq:OAR077:
|
|
934
875
|
description: "All parameters in query must be snake_case."
|
|
935
876
|
message: "OAR077: All parameters in query must be snake_case."
|
|
@@ -957,7 +898,8 @@ rules:
|
|
|
957
898
|
then:
|
|
958
899
|
function: apq-require-response-on-path-params
|
|
959
900
|
functionOptions:
|
|
960
|
-
|
|
901
|
+
paths: "/status"
|
|
902
|
+
pathValidationStrategy: "/exclude"
|
|
961
903
|
apiq:OAR080:
|
|
962
904
|
description: "The security scheme must be among those allowed by the organization and must be complete."
|
|
963
905
|
message: "OAR080: The security scheme '{{property}}' must be among those allowed by the organization and must be complete."
|
|
@@ -995,15 +937,16 @@ rules:
|
|
|
995
937
|
functionOptions:
|
|
996
938
|
notMatch: "^(email|password)$"
|
|
997
939
|
apiq:OAR084:
|
|
998
|
-
description: "
|
|
999
|
-
message: "
|
|
940
|
+
description: "Some formats should not pass through this querystring."
|
|
941
|
+
message: "{{error}}"
|
|
1000
942
|
severity: error
|
|
1001
943
|
given: "$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')]"
|
|
1002
944
|
then:
|
|
1003
|
-
|
|
1004
|
-
function: pattern
|
|
945
|
+
function: apq-forbidden-query-format
|
|
1005
946
|
functionOptions:
|
|
1006
|
-
|
|
947
|
+
forbidden-query-formats: "password"
|
|
948
|
+
paths: "/examples"
|
|
949
|
+
pathValidationStrategy: "/include"
|
|
1007
950
|
apiq:OAR085:
|
|
1008
951
|
description: "The OpenAPI version must be one of: 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.1.0, 3.1.1, 3.1.2, 3.2.0."
|
|
1009
952
|
message: "{{error}}"
|
|
@@ -1199,19 +1142,10 @@ rules:
|
|
|
1199
1142
|
description: "The schemas should match the provided examples."
|
|
1200
1143
|
message: "OAR108: Schema does not match the provided example."
|
|
1201
1144
|
severity: "error"
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
- "$.paths['/item'].get.responses['200'].examples['application/json']"
|
|
1145
|
+
resolved: false
|
|
1146
|
+
given: "$.paths[*][*].responses[*]"
|
|
1205
1147
|
then:
|
|
1206
|
-
|
|
1207
|
-
functionOptions:
|
|
1208
|
-
schema:
|
|
1209
|
-
type: "object"
|
|
1210
|
-
properties:
|
|
1211
|
-
id:
|
|
1212
|
-
type: "integer"
|
|
1213
|
-
nombre:
|
|
1214
|
-
type: "string"
|
|
1148
|
+
function: apq-example-schema-types
|
|
1215
1149
|
apiq:OAR109:
|
|
1216
1150
|
description: "Use default response instead of directly specifying 5XX codes."
|
|
1217
1151
|
message: "OAR109: Use default response instead of specifying 5XX codes directly."
|
|
@@ -1265,9 +1199,8 @@ rules:
|
|
|
1265
1199
|
message: "OAR115: All fields in the required array must be defined in schema properties."
|
|
1266
1200
|
documentationUrl: "https://github.com/apiaddicts/apquality-spectral/blob/main/docs/resources/OAR115.md"
|
|
1267
1201
|
severity: warn
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
- "$.definitions[*]"
|
|
1202
|
+
resolved: false
|
|
1203
|
+
given: "$..[?(@ && @.required)]"
|
|
1271
1204
|
then:
|
|
1272
1205
|
function: apq-required-fields-exist
|
|
1273
1206
|
apiq:OAR116:
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const HTTP_VERBS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {object} given
|
|
5
|
+
* @param {object} options
|
|
6
|
+
* @param {import('@stoplight/spectral-core').RulesetFunctionContext} context
|
|
7
|
+
*/
|
|
8
|
+
module.exports = (given, options, context) => {
|
|
9
|
+
if (!given || typeof given !== 'object') {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const allowedVerbs = ((options && options['allowed-verbs']) || '')
|
|
14
|
+
.split(',')
|
|
15
|
+
.map((verb) => verb.trim().toLowerCase())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
|
|
18
|
+
const basePath = context.path || [];
|
|
19
|
+
const errors = [];
|
|
20
|
+
|
|
21
|
+
HTTP_VERBS.forEach((verb) => {
|
|
22
|
+
if (Object.prototype.hasOwnProperty.call(given, verb) && !allowedVerbs.includes(verb)) {
|
|
23
|
+
errors.push({
|
|
24
|
+
message: `HTTP verb '${verb}' is not encouraged. Only the following verbs are allowed: ${allowedVerbs.join(', ')}.`,
|
|
25
|
+
path: [...basePath, verb]
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return errors;
|
|
31
|
+
};
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
2
1
|
const isVariable = (part) => {
|
|
3
2
|
return (part.startsWith('{') && part.endsWith('}'));
|
|
4
3
|
}
|
|
5
4
|
|
|
6
|
-
module.exports = (given,
|
|
5
|
+
module.exports = (given, options, context) => {
|
|
7
6
|
const result = [];
|
|
8
7
|
const paths = given || [];
|
|
9
8
|
if (paths.length === 0) return result;
|
|
10
9
|
|
|
10
|
+
const excludePatterns = ((options && options['exclude_patterns']) || '')
|
|
11
|
+
.split(',')
|
|
12
|
+
.map((pattern) => pattern.trim())
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
|
|
11
15
|
const parts = paths.substr(1).split('/').filter(p => p.length > 0);
|
|
12
16
|
if (parts.length === 0) return result;
|
|
13
17
|
const firstPart = parts.shift();
|
|
14
18
|
let previousIsVar;
|
|
15
|
-
if (
|
|
19
|
+
if (excludePatterns.includes(firstPart)) {
|
|
16
20
|
previousIsVar = true;
|
|
17
21
|
} else if (isVariable(firstPart)) {
|
|
18
22
|
return [{ message: context.rule.message }];
|
|
@@ -21,7 +25,7 @@ module.exports = (given, { except }, context) => {
|
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
for (const part of parts) {
|
|
24
|
-
if (
|
|
28
|
+
if (excludePatterns.includes(part)) {
|
|
25
29
|
previousIsVar = true;
|
|
26
30
|
continue;
|
|
27
31
|
}
|
|
@@ -1,52 +1,38 @@
|
|
|
1
|
+
const PARAM_REGEX = /\{[^}{]*}/g;
|
|
2
|
+
|
|
3
|
+
const DEFAULT_AMBIGUOUS_NAMES =
|
|
4
|
+
'elementos,instancias,recursos,valores,terminos,objetos,articulos,elements,instances,resources,values,terms,objects,items';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
|
-
* Validates that URL path segments use clear, unambiguous names
|
|
3
7
|
* @param {string} given - The path key (e.g. "/users/{id}/items")
|
|
4
|
-
* @param {object} options
|
|
5
|
-
* @param {string} options
|
|
8
|
+
* @param {object} options
|
|
9
|
+
* @param {string} options['ambiguous-names']
|
|
6
10
|
* @param {import('@stoplight/spectral-core').RulesetFunctionContext} context
|
|
7
11
|
*/
|
|
8
12
|
module.exports = (given, options, context) => {
|
|
9
|
-
const results = [];
|
|
10
|
-
|
|
11
13
|
if (typeof given !== 'string') {
|
|
12
|
-
return
|
|
14
|
+
return [];
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'elements', 'instances', 'resources', 'values', 'terms', 'objects', 'items'
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// Parse custom ambiguous words from options if provided
|
|
21
|
-
let ambiguousWords = defaultAmbiguousWords;
|
|
22
|
-
if (options && options['ambiguous-words']) {
|
|
23
|
-
ambiguousWords = options['ambiguous-words']
|
|
17
|
+
const forbiddenValues = new Set(
|
|
18
|
+
((options && options['ambiguous-names']) || DEFAULT_AMBIGUOUS_NAMES)
|
|
24
19
|
.split(',')
|
|
25
|
-
.map(
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
|
|
20
|
+
.map((value) => value.trim())
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
);
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
.filter(segment => {
|
|
33
|
-
// Keep only static segments (not empty, not {param})
|
|
34
|
-
return segment.length > 0 && !segment.startsWith('{');
|
|
35
|
-
});
|
|
24
|
+
const pathWithoutParams = given.replace(PARAM_REGEX, '');
|
|
25
|
+
const pathParts = pathWithoutParams.split('/').filter(Boolean);
|
|
26
|
+
const forbidden = pathParts.filter((part) => forbiddenValues.has(part));
|
|
36
27
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const lowerSegment = segment.toLowerCase();
|
|
40
|
-
for (const word of ambiguousWords) {
|
|
41
|
-
if (lowerSegment.includes(word)) {
|
|
42
|
-
results.push({
|
|
43
|
-
message: `${context.rule.message || 'OAR032'}: Path segment '${segment}' is ambiguous. Avoid using words like '${word}' in resource names.`,
|
|
44
|
-
path: context.path
|
|
45
|
-
});
|
|
46
|
-
break; // Report only once per segment
|
|
47
|
-
}
|
|
48
|
-
}
|
|
28
|
+
if (forbidden.length === 0) {
|
|
29
|
+
return [];
|
|
49
30
|
}
|
|
50
31
|
|
|
51
|
-
return
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
message: `Ambiguous name(s) found in path: ${forbidden.join(', ')}.`,
|
|
35
|
+
path: context.path
|
|
36
|
+
}
|
|
37
|
+
];
|
|
52
38
|
};
|