Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@zapier/spectral-api-ruleset

Package Overview
Dependencies
Maintainers
0
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zapier/spectral-api-ruleset - npm Package Compare versions

Comparing version 0.0.3 to 0.0.4

dist/declarations/src/functions/doNotUseValue.d.ts

6

CHANGELOG.md
# @zapier/spectral-api-ruleset
## 0.0.4
### Patch Changes
- c85b6fa: Add description, documentationUrl, unit tests and integration tests
## 0.0.3

@@ -4,0 +10,0 @@

1

dist/declarations/src/types.d.ts
import type { RuleDefinition } from '@stoplight/spectral-core';
export declare type Function = Extract<RuleDefinition['then'], Array<any>>[any]['function'];
export declare type Message = Extract<ReturnType<Function>, Array<any>>[any];

105

dist/zapier-spectral-api-ruleset.cjs.dev.js

@@ -11,30 +11,48 @@ 'use strict';

/**
* Checks: Each path has a version that follows the pattern 'v<MAJOR_VERSION_NUMBER' and the number is
* the same for all paths.
*
* Versioning API Guidelines: https://engineering.zapier.com/guides/api-design-guidelines/versioning/
*/
const checkMajorVersion = (paths, _options, {
document
const doNotUseValue = (targetVal, {
values
}) => {
if (paths === null) {
return undefined;
if (values.includes(targetVal)) {
return [{
message: `Do not use: ${targetVal}`
}];
}
};
const base_url = ___default["default"].get(document, 'data.servers[0].url', '');
const singleMajorVersionInPaths = (paths, _options, {
document,
path
}) => {
// Find URL of first non-local server
let baseURL = '';
const servers = ___default["default"].get(document, 'data.servers', '');
if (Array.isArray(servers)) {
for (const server of servers) {
const url = ___default["default"].get(server, 'url');
if (typeof url === 'string' && url.startsWith('https://')) {
baseURL = url;
break;
}
}
}
const messages = [];
const foundVersions = new Set();
Object.keys(paths).forEach(path => {
const url = new URL(`${base_url}${path}`, 'https://example.com');
const pathname_segments = url.pathname.split('/');
const version = pathname_segments.find(segment => segment.match(/v[0-9]+/));
Object.keys(paths).forEach(endpointPath => {
// Prepend server URL to the possibly relative path
const url = new URL(`${baseURL}${endpointPath}`, 'https://example.com');
const fullPath = url.pathname;
const match = fullPath.match(/\/(v[0-9])\//);
if (version !== undefined) {
foundVersions.add(version);
if (match) {
foundVersions.add(match[1]);
} else {
messages.push({
severity: 'error',
message: `Major version missing in path ${path}`
message: `Major version missing in path: ${fullPath}`,
// When returning multiple messages, each must have a unique and correct path
// https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTkw-custom-functions#returning-multiple-results
path: [...path, endpointPath]
});

@@ -46,4 +64,4 @@ }

messages.push({
severity: 'error',
message: `Found multiple major versions: ${[...foundVersions].join(', ')}`
message: `Found multiple major versions: ${[...foundVersions].join(', ')}`,
path
});

@@ -55,40 +73,27 @@ }

/**
* Checks: The sorting parameter should be `order_by`
*
* Sorting API Guidelines: https://engineering.zapier.com/guides/api-design-guidelines/searching-filtering-sorting/#sorting
*/
const checkSortingParameterName = parameters => {
if (parameters === null) {
return;
}
const messages = [];
for (const parameter of parameters) {
if (['sort_by', 'ordering'].includes(parameter.name)) {
messages.push({
severity: 'error',
message: `Found sorting parameter name which doesn't conform to the API Guidelines: ${parameter.name}, should use: order_by.`
});
}
}
return messages;
};
const UNWANTED_SORTING_PARAMETERS = ['sort_by', 'ordering'];
const ruleset = {
rules: {
'major-version': {
given: ['$.paths'],
'single-major-version-in-paths': {
description: 'All paths need to contain the same major version as /v[0-9]+/',
documentationUrl: 'https://engineering.zapier.com/guides/api-design-guidelines/versioning/',
given: '$.paths',
message: '{{error}}',
severity: 'error',
then: {
function: checkMajorVersion
function: singleMajorVersionInPaths
}
},
'sorting-parameter': {
given: '$.paths[*][*].parameters',
description: `For sorting order_by should be used and not: ${UNWANTED_SORTING_PARAMETERS.join(', ')}`,
documentationUrl: 'https://engineering.zapier.com/guides/api-design-guidelines/searching-filtering-sorting/#sorting',
given: '$..parameters[*]',
message: '{{error}}',
severity: 'error',
then: {
function: checkSortingParameterName
field: 'name',
function: doNotUseValue,
functionOptions: {
values: UNWANTED_SORTING_PARAMETERS
}
}

@@ -95,0 +100,0 @@ }

@@ -11,30 +11,48 @@ 'use strict';

/**
* Checks: Each path has a version that follows the pattern 'v<MAJOR_VERSION_NUMBER' and the number is
* the same for all paths.
*
* Versioning API Guidelines: https://engineering.zapier.com/guides/api-design-guidelines/versioning/
*/
const checkMajorVersion = (paths, _options, {
document
const doNotUseValue = (targetVal, {
values
}) => {
if (paths === null) {
return undefined;
if (values.includes(targetVal)) {
return [{
message: `Do not use: ${targetVal}`
}];
}
};
const base_url = ___default["default"].get(document, 'data.servers[0].url', '');
const singleMajorVersionInPaths = (paths, _options, {
document,
path
}) => {
// Find URL of first non-local server
let baseURL = '';
const servers = ___default["default"].get(document, 'data.servers', '');
if (Array.isArray(servers)) {
for (const server of servers) {
const url = ___default["default"].get(server, 'url');
if (typeof url === 'string' && url.startsWith('https://')) {
baseURL = url;
break;
}
}
}
const messages = [];
const foundVersions = new Set();
Object.keys(paths).forEach(path => {
const url = new URL(`${base_url}${path}`, 'https://example.com');
const pathname_segments = url.pathname.split('/');
const version = pathname_segments.find(segment => segment.match(/v[0-9]+/));
Object.keys(paths).forEach(endpointPath => {
// Prepend server URL to the possibly relative path
const url = new URL(`${baseURL}${endpointPath}`, 'https://example.com');
const fullPath = url.pathname;
const match = fullPath.match(/\/(v[0-9])\//);
if (version !== undefined) {
foundVersions.add(version);
if (match) {
foundVersions.add(match[1]);
} else {
messages.push({
severity: 'error',
message: `Major version missing in path ${path}`
message: `Major version missing in path: ${fullPath}`,
// When returning multiple messages, each must have a unique and correct path
// https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTkw-custom-functions#returning-multiple-results
path: [...path, endpointPath]
});

@@ -46,4 +64,4 @@ }

messages.push({
severity: 'error',
message: `Found multiple major versions: ${[...foundVersions].join(', ')}`
message: `Found multiple major versions: ${[...foundVersions].join(', ')}`,
path
});

@@ -55,40 +73,27 @@ }

/**
* Checks: The sorting parameter should be `order_by`
*
* Sorting API Guidelines: https://engineering.zapier.com/guides/api-design-guidelines/searching-filtering-sorting/#sorting
*/
const checkSortingParameterName = parameters => {
if (parameters === null) {
return;
}
const messages = [];
for (const parameter of parameters) {
if (['sort_by', 'ordering'].includes(parameter.name)) {
messages.push({
severity: 'error',
message: `Found sorting parameter name which doesn't conform to the API Guidelines: ${parameter.name}, should use: order_by.`
});
}
}
return messages;
};
const UNWANTED_SORTING_PARAMETERS = ['sort_by', 'ordering'];
const ruleset = {
rules: {
'major-version': {
given: ['$.paths'],
'single-major-version-in-paths': {
description: 'All paths need to contain the same major version as /v[0-9]+/',
documentationUrl: 'https://engineering.zapier.com/guides/api-design-guidelines/versioning/',
given: '$.paths',
message: '{{error}}',
severity: 'error',
then: {
function: checkMajorVersion
function: singleMajorVersionInPaths
}
},
'sorting-parameter': {
given: '$.paths[*][*].parameters',
description: `For sorting order_by should be used and not: ${UNWANTED_SORTING_PARAMETERS.join(', ')}`,
documentationUrl: 'https://engineering.zapier.com/guides/api-design-guidelines/searching-filtering-sorting/#sorting',
given: '$..parameters[*]',
message: '{{error}}',
severity: 'error',
then: {
function: checkSortingParameterName
field: 'name',
function: doNotUseValue,
functionOptions: {
values: UNWANTED_SORTING_PARAMETERS
}
}

@@ -95,0 +100,0 @@ }

import _ from 'lodash';
/**
* Checks: Each path has a version that follows the pattern 'v<MAJOR_VERSION_NUMBER' and the number is
* the same for all paths.
*
* Versioning API Guidelines: https://engineering.zapier.com/guides/api-design-guidelines/versioning/
*/
const checkMajorVersion = (paths, _options, {
document
const doNotUseValue = (targetVal, {
values
}) => {
if (paths === null) {
return undefined;
if (values.includes(targetVal)) {
return [{
message: `Do not use: ${targetVal}`
}];
}
};
const base_url = _.get(document, 'data.servers[0].url', '');
const singleMajorVersionInPaths = (paths, _options, {
document,
path
}) => {
// Find URL of first non-local server
let baseURL = '';
const servers = _.get(document, 'data.servers', '');
if (Array.isArray(servers)) {
for (const server of servers) {
const url = _.get(server, 'url');
if (typeof url === 'string' && url.startsWith('https://')) {
baseURL = url;
break;
}
}
}
const messages = [];
const foundVersions = new Set();
Object.keys(paths).forEach(path => {
const url = new URL(`${base_url}${path}`, 'https://example.com');
const pathname_segments = url.pathname.split('/');
const version = pathname_segments.find(segment => segment.match(/v[0-9]+/));
Object.keys(paths).forEach(endpointPath => {
// Prepend server URL to the possibly relative path
const url = new URL(`${baseURL}${endpointPath}`, 'https://example.com');
const fullPath = url.pathname;
const match = fullPath.match(/\/(v[0-9])\//);
if (version !== undefined) {
foundVersions.add(version);
if (match) {
foundVersions.add(match[1]);
} else {
messages.push({
severity: 'error',
message: `Major version missing in path ${path}`
message: `Major version missing in path: ${fullPath}`,
// When returning multiple messages, each must have a unique and correct path
// https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTkw-custom-functions#returning-multiple-results
path: [...path, endpointPath]
});

@@ -37,4 +55,4 @@ }

messages.push({
severity: 'error',
message: `Found multiple major versions: ${[...foundVersions].join(', ')}`
message: `Found multiple major versions: ${[...foundVersions].join(', ')}`,
path
});

@@ -46,40 +64,27 @@ }

/**
* Checks: The sorting parameter should be `order_by`
*
* Sorting API Guidelines: https://engineering.zapier.com/guides/api-design-guidelines/searching-filtering-sorting/#sorting
*/
const checkSortingParameterName = parameters => {
if (parameters === null) {
return;
}
const messages = [];
for (const parameter of parameters) {
if (['sort_by', 'ordering'].includes(parameter.name)) {
messages.push({
severity: 'error',
message: `Found sorting parameter name which doesn't conform to the API Guidelines: ${parameter.name}, should use: order_by.`
});
}
}
return messages;
};
const UNWANTED_SORTING_PARAMETERS = ['sort_by', 'ordering'];
const ruleset = {
rules: {
'major-version': {
given: ['$.paths'],
'single-major-version-in-paths': {
description: 'All paths need to contain the same major version as /v[0-9]+/',
documentationUrl: 'https://engineering.zapier.com/guides/api-design-guidelines/versioning/',
given: '$.paths',
message: '{{error}}',
severity: 'error',
then: {
function: checkMajorVersion
function: singleMajorVersionInPaths
}
},
'sorting-parameter': {
given: '$.paths[*][*].parameters',
description: `For sorting order_by should be used and not: ${UNWANTED_SORTING_PARAMETERS.join(', ')}`,
documentationUrl: 'https://engineering.zapier.com/guides/api-design-guidelines/searching-filtering-sorting/#sorting',
given: '$..parameters[*]',
message: '{{error}}',
severity: 'error',
then: {
function: checkSortingParameterName
field: 'name',
function: doNotUseValue,
functionOptions: {
values: UNWANTED_SORTING_PARAMETERS
}
}

@@ -86,0 +91,0 @@ }

{
"name": "@zapier/spectral-api-ruleset",
"version": "0.0.3",
"version": "0.0.4",
"description": "Node package with Spectral ruleset for Zapier API Guidelines.",

@@ -23,3 +23,5 @@ "repository": "https://gitlab.com/zapier/spectral-api-ruleset/-/tree/main",

"lint": "eslint \"{src/**/*,*}.{js,ts,tsx}\"",
"test:integration": "yarn spectral lint test-schema.yaml",
"pretest:spectral": "yarn build",
"test:spectral": "yarn spectral lint tests/schema.yaml --ruleset tests/spectral.yaml --fail-severity hint --verbose",
"pretest": "yarn build",
"test": "jest",

@@ -31,17 +33,16 @@ "prettier-check": "prettier --check \"{,**/}*.{js,ts,tsx}\"",

"release": "yarn changeset publish",
"validate": "yarn typecheck && yarn lint && yarn test && yarn test:integrate"
"validate": "yarn typecheck && yarn lint && yarn test && yarn test:spectral"
},
"devDependencies": {
"@babel/core": "^7.15.8",
"@babel/preset-env": "^7.15.8",
"@babel/preset-typescript": "^7.15.0",
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.16.0",
"@babel/preset-typescript": "^7.16.0",
"@changesets/cli": "^2.17.0",
"@preconstruct/cli": "^2.1.5",
"@stoplight/spectral-cli": "^6.1.0",
"@stoplight/spectral-core": "^1.6.0",
"@types/jest": "^27.0.2",
"@types/lodash": "^4.14.175",
"@typescript-eslint/parser": "^5.1.0",
"eslint": "^8.0.1",
"eslint-plugin-jest": "^25.2.2",
"@types/lodash": "^4.14.176",
"@typescript-eslint/parser": "^5.3.0",
"eslint": "^8.1.0",
"eslint-plugin-jest": "^25.2.3",
"husky": "^4.3.8",

@@ -63,3 +64,4 @@ "jest": "^27.3.1",

"lodash": "^4.17.21"
}
}
},
"peerDependencies": {}
}

@@ -82,8 +82,12 @@ <h1 align="center">

You can add rules to `spectral.js`. See the [Alternative JS Ruleset Format docs](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg5-custom-rulesets#alternative-js-ruleset-format) for details.
You can add rules to `src/index.ts`. See the [Alternative JS Ruleset Format docs](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg5-custom-rulesets#alternative-js-ruleset-format) for details.
> We're using the JS format so that the package ruleset can also be [used in JavaScript](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg3-spectral-in-java-script).
- Try to use the [Core Functions](https://meta.stoplight.io/docs/spectral/ZG9jOjExNg-core-functions) to avoid custom functions.
- Include a unit test for any custom functions you add to `functions/`.
- Update `test-schema.yaml` to meet all rules (run `yarn test:integrate` to verify).
- Provide the correct [`severity`](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg5-custom-rulesets#severity) (`error` for **musts** and `warn` for **shoulds**).
- Provide a clear `description`, as well as a [`documentationUrl`](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg5-custom-rulesets#documentation-url) that points to the relevant guide.
- Provide a [`message`](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTg5-custom-rulesets#message), which in most cases should just be `{{error}}`.
- Prefer [Core Functions](https://meta.stoplight.io/docs/spectral/ZG9jOjExNg-core-functions) over [Custom Functions](https://meta.stoplight.io/docs/spectral/ZG9jOjI1MTkw-custom-functions).
- Include a unit test for any custom functions you add to [`src/functions`](src/functions).
- Update `tests/schema.yaml` to meet all rules (run `yarn test:spectral` to verify).
- Add failing schemas to `tests/__fixtures__` and run `yarn test integrations --updateSnapshot` to update `test/__snapshots__/integration.test.ts.snap` and verify the found issues.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc