Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DPE-35): Created Collections rules #86

Merged
merged 11 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rulesets/src/.spectral.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extends:
- serialization.ruleset.yml
- url-structure.ruleset.yml
- webhooks.ruleset.yml
- collections.ruleset.yml

rules:
# not keeping this, just off for testing in this repo
Expand Down
136 changes: 136 additions & 0 deletions rulesets/src/collections.ruleset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
rules:
##### General #####
sps-no-collection-paging-capability:
description: Response bodies from collection endpoints must offer paging capability.
sps-benjacobs marked this conversation as resolved.
Show resolved Hide resolved
severity: error
given: $.paths[?([email protected](/.*\/\{[^}]+\}$/))].get.responses['200'].content.application/json.schema.properties
then:
- field: "paging"
function: truthy
- field: "paging.type"
function: pattern
functionOptions:
match: "object"

##### Root Element #####
sps-collection-missing-results-array:
description: Response bodies must have a root element called results and is an array of objects.
severity: error
given: $.paths[?([email protected](/.*\/\{[^}]+\}$/))].get.responses['200'].content.application/json.schema.properties.results
then:
- field: type
function: pattern
functionOptions:
match: "array"
- field: items.type
function: pattern
functionOptions:
match: "object"

##### Pagination #####
sps-missing-pagination-query-parameters:
description: 'Collection GET endpoints must support pagination using query parameters: limit, offset, cursor.'
severity: error
given: $.paths[?([email protected](/.*\/\{[^}]+\}$/))].get
then:
- field: parameters
function: schema
functionOptions:
schema:
type: array
items:
type: object
properties:
name:
type: string
required:
- name
minItems: 3
sps-benjacobs marked this conversation as resolved.
Show resolved Hide resolved
contains:
anyOf:
- properties:
name:
const: offset
- properties:
name:
const: limit
- properties:
name:
const: cursor
travisgosselin marked this conversation as resolved.
Show resolved Hide resolved

sps-post-request-body-missing-paging-object:
description: "POST collection endpoints MUST have a request body schema that includes paging parameters."
severity: error
given: $.paths[?([email protected](/.*\/\{[^}]+\}$/))].post.requestBody.content.application/json.schema.properties.paging
then:
field: "type"
function: pattern
functionOptions:
match: "object"
travisgosselin marked this conversation as resolved.
Show resolved Hide resolved

##### FILTERING #####
sps-disallow-resource-identifier-filtering:
description: "Resource identifier filtering is not allowed. Use the resource identifier in the URL path."
sps-benjacobs marked this conversation as resolved.
Show resolved Hide resolved
severity: warn
given: $.paths..get.parameters.[?(@.in=='query' && @.name=='id')]
then:
field: "name"
function: pattern
functionOptions:
notMatch: "^id$"

sps-unreasonable-query-parameters-limit:
description: "Filtering query parameters SHOULD have a reasonable limit, no more than 12."
severity: warn
given: $.paths..get
then:
function: schema
functionOptions:
dialect: "draft2020-12"
brandonsahadeo marked this conversation as resolved.
Show resolved Hide resolved
schema:
type: object
properties:
parameters:
type: array
minContains: 0
maxContains: 12
contains:
type: object
properties:
in:
const: query

sps-multiple-filter-parameters:
description: "Ensures the 'filter' query parameter is specified only once."
sps-benjacobs marked this conversation as resolved.
Show resolved Hide resolved
severity: error
given: $.paths..get
then:
function: schema
functionOptions:
dialect: "draft2020-12"
schema:
type: object
properties:
parameters:
type: array
minContains: 0
maxContains: 1
contains:
type: object
properties:
in:
const: query
name:
pattern: "(filter|Filter)"
brandonsahadeo marked this conversation as resolved.
Show resolved Hide resolved

##### SORTING #####
sps-sorting-parameters-only-get-requests:
description: "Sorting query parameters SHOULD only be used with GET endpoints."
sps-benjacobs marked this conversation as resolved.
Show resolved Hide resolved
severity: error
given: $.paths.*[?(@property!='get')].parameters.[?(@.in=='query')]
then:
field: "name"
function: pattern
functionOptions:
notMatch: "^sort|sorting|sort_by|order|ordering|order_by$"

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const { SpectralTestHarness } = require("../harness/spectral-test-harness.js");

describe("sps-collection-missing-results-array", () => {
let spectral = null;
const ruleName = "sps-collection-missing-results-array";
const ruleset = "src/collections.ruleset.yml";

beforeEach(async () => {
spectral = new SpectralTestHarness(ruleset);
});

test("valid - collection response with results array of objects", async () => {
const spec = `
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/users:
get:
summary: Get a list of users
responses:
'200':
description: A list of users
content:
application/json:
schema:
properties:
results:
type: array
items:
type: object
paging:
type: object
`;

await spectral.validateSuccess(spec, ruleName);
});

test("invalid - collection response - results is not an array", async () => {
const spec = `
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/users:
get:
summary: Get a list of users
responses:
'200':
description: A list of users
content:
application/json:
schema:
properties:
results:
type: object
paging:
type: object
`;

await spectral.validateFailure(spec, ruleName, "Error", 1);
});

test("invalid - collection response - results is not an array of objects", async () => {
const spec = `
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/users:
get:
summary: Get a list of users
responses:
'200':
description: A list of users
content:
application/json:
schema:
properties:
results:
type: array
items:
type: string
paging:
type: object
`;

await spectral.validateFailure(spec, ruleName, "Error", 1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { SpectralTestHarness } = require("../harness/spectral-test-harness.js");

describe("sps-disallow-resource-identifier-filtering", () => {
let spectral = null;
const ruleName = "sps-disallow-resource-identifier-filtering";
const ruleset = "src/collections.ruleset.yml";

beforeEach(async () => {
spectral = new SpectralTestHarness(ruleset);
});

test("valid - resource identifier is within the path of the endpoint", async () => {
const spec = `
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/users/{id}:
get:
summary: Get a list of users
parameters:
- name: id
in: path
required: true
responses:
'200':
description: A list of users
`;

await spectral.validateSuccess(spec, ruleName);
});

test("invalid - resource identifier is defined as a query parameter", async () => {
const spec = `
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/users:
get:
summary: Get a list of users
parameters:
- name: id
in: query
required: false
`;

await spectral.validateFailure(spec, ruleName, "Warning", 1);
});
});
Loading
Loading