Skip to content

Commit a7963dd

Browse files
authored
Merge pull request #742 from rpocklin/feat-spectral-linter
Introduce spectral OpenAPI spec linting
2 parents 1625fdf + b2837dd commit a7963dd

File tree

4 files changed

+236
-0
lines changed

4 files changed

+236
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: Spectral Lint
2+
on: [push, pull_request]
3+
jobs:
4+
lint:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v3
8+
- uses: actions/setup-node@v3
9+
- name: spectral-lint
10+
run: ./validator/scripts/validate-all.sh

validator/scripts/validate-all.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/bin/bash
2+
#
3+
# Run this script from root folder in the repository
4+
#
5+
# To validate a single file: npx spectral lint <filename>.yaml --ruleset <ruleset>
6+
#
7+
8+
# Ruleset
9+
ruleset="./validator/spectral.yaml"
10+
11+
# Array of all YAML files
12+
files=(
13+
"xero-identity.yaml"
14+
"xero-projects.yaml"
15+
"xero-app-store.yaml"
16+
"xero-payroll-uk.yaml"
17+
"xero_files.yaml"
18+
"xero_accounting.yaml"
19+
"xero-payroll-nz.yaml"
20+
"xero_assets.yaml"
21+
"xero_bankfeeds.yaml"
22+
"xero-payroll-au.yaml"
23+
)
24+
25+
total_files=${#files[@]}
26+
passed=0
27+
failed=0
28+
29+
30+
# Validate all Xero OpenAPI YAML files with the custom Spectral ruleset
31+
echo "🔍 Validating all Xero OpenAPI specifications..."
32+
echo "================================================"
33+
34+
echo "Testing $total_files OpenAPI specification files..."
35+
echo ""
36+
37+
for file in "${files[@]}"; do
38+
if [ -f "$file" ]; then
39+
echo "📄 Validating $file..."
40+
41+
# Run spectral and capture the exit code
42+
if npx @stoplight/spectral-cli lint "$file" --ruleset "$ruleset" --quiet > /dev/null 2>&1; then
43+
echo "$file - PASSED"
44+
((passed++))
45+
else
46+
echo "$file - FAILED"
47+
echo " Running detailed check:"
48+
npx @stoplight/spectral-cli lint "$file" --ruleset "$ruleset" --format=stylish | head -10
49+
echo ""
50+
((failed++))
51+
fi
52+
else
53+
echo "⚠️ $file - FILE NOT FOUND"
54+
((failed++))
55+
fi
56+
done
57+
58+
echo ""
59+
echo "================================================"
60+
echo "📊 SUMMARY:"
61+
echo " Total files: $total_files"
62+
echo " Passed: $passed"
63+
echo " Failed: $failed"
64+
65+
if [ $failed -eq 0 ]; then
66+
echo ""
67+
echo "🎉 ALL FILES PASSED! The Spectral ruleset is working correctly."
68+
echo " This ruleset can be used as a baseline for validating future OpenAPI specs."
69+
exit 0
70+
else
71+
echo ""
72+
echo "⚠️ Some files failed validation. Check the output above for details."
73+
exit 1
74+
fi

validator/spectral.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
extends: ["spectral:oas", "./xero-spectral.yaml"]
2+
3+
rules:
4+
# Override default rules to be more lenient for existing Xero API specs
5+
6+
# Re-enabled: operation-description (produces warnings only - acceptable for documentation improvement)
7+
# operation-description: off
8+
9+
# Disabled: info-description (xero_accounting.yaml missing description)
10+
info-description: off
11+
12+
# Disabled: operation-tag-defined (many APIs use undeclared tags)
13+
operation-tag-defined: off
14+
15+
# Disabled: no-$ref-siblings (xero_accounting.yaml uses this pattern with type field)
16+
no-$ref-siblings: off
17+
18+
# Disabled: example validation rules (legacy string examples would cause many errors)
19+
oas3-valid-media-example: off
20+
oas3-valid-schema-example: off
21+
22+
# Re-enabled: oas3-unused-component (produces warnings only - helps identify unused schemas)
23+
# oas3-unused-component: off
24+
25+
# Re-enabled: oas3-server-trailing-slash (produces warnings only - helps clean up URLs)
26+
# oas3-server-trailing-slash: off
27+
28+
# Disabled: path-params (FileId/FolderId path conflicts in xero_files.yaml)
29+
path-params: off
30+
31+
# Re-enabled: oas3-operation-security-defined (produces warnings only - helps identify security gaps)
32+
# oas3-operation-security-defined: off

validator/xero-spectral.yaml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
rules:
2+
# Custom rules specific to Xero APIs
3+
xero-info-required-fields:
4+
description: "Ensure required info fields are present"
5+
given: "$.info"
6+
severity: error
7+
then:
8+
- field: "title"
9+
function: truthy
10+
- field: "version"
11+
function: truthy
12+
- field: "termsOfService"
13+
function: truthy
14+
- field: "contact"
15+
function: truthy
16+
17+
xero-contact-required-fields:
18+
description: "Ensure contact has required fields"
19+
given: "$.info.contact"
20+
severity: error
21+
then:
22+
- field: "name"
23+
function: truthy
24+
- field: "email"
25+
function: truthy
26+
- field: "url"
27+
function: truthy
28+
29+
xero-servers-required:
30+
description: "Ensure servers are defined"
31+
given: "$"
32+
severity: error
33+
then:
34+
field: "servers"
35+
function: truthy
36+
37+
xero-server-description:
38+
description: "Each server should have a description"
39+
given: "$.servers[*]"
40+
severity: warn
41+
then:
42+
field: "description"
43+
function: truthy
44+
45+
xero-operation-summary:
46+
description: "Operations should have summaries"
47+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace]"
48+
severity: warn
49+
then:
50+
field: "summary"
51+
function: truthy
52+
53+
xero-operation-id:
54+
description: "Operations must have operationId"
55+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace]"
56+
severity: error
57+
then:
58+
field: "operationId"
59+
function: truthy
60+
61+
xero-operation-tags:
62+
description: "Operations should have tags"
63+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace]"
64+
severity: warn
65+
then:
66+
field: "tags"
67+
function: truthy
68+
69+
xero-operation-security:
70+
description: "Operations should have security defined"
71+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace]"
72+
severity: info
73+
then:
74+
field: "security"
75+
function: truthy
76+
77+
xero-response-200-description:
78+
description: "200 responses should have descriptions"
79+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace].responses.200"
80+
severity: warn
81+
then:
82+
field: "description"
83+
function: truthy
84+
85+
xero-schema-properties-description:
86+
description: "Schema properties should have descriptions for better documentation"
87+
given: "$.components.schemas[*].properties[*]"
88+
severity: info
89+
then:
90+
field: "description"
91+
function: truthy
92+
93+
xero-openapi-version:
94+
description: "Should use OpenAPI 3.0.0 or higher"
95+
given: "$.openapi"
96+
severity: error
97+
then:
98+
function: pattern
99+
functionOptions:
100+
match: "^3\\.[0-9]+\\.[0-9]+$"
101+
102+
xero-path-parameters:
103+
description: "Path parameters should be properly defined"
104+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace].parameters[?(@.in === 'path')]"
105+
severity: error
106+
then:
107+
- field: "name"
108+
function: truthy
109+
- field: "required"
110+
function: truthy
111+
- field: "schema"
112+
function: truthy
113+
114+
xero-consistent-error-responses:
115+
description: "Should have consistent error response structure"
116+
given: "$.paths[*][get,post,put,patch,delete,head,options,trace].responses[?(@property >= '400')]"
117+
severity: info
118+
then:
119+
field: "description"
120+
function: truthy

0 commit comments

Comments
 (0)