Skip to content

Commit e625c88

Browse files
authored
feat(spec): eslint with custom rules APIC-387 (#304)
1 parent a73c2c7 commit e625c88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+404
-107
lines changed

.eslintrc.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = {
1313
{
1414
files: ['*.yml'],
1515
parser: 'yaml-eslint-parser',
16+
plugins: ["automation-custom"],
1617
rules: {
1718
'@typescript-eslint/naming-convention': 0,
1819
'yml/quotes': [
@@ -31,10 +32,14 @@ module.exports = {
3132
},
3233
],
3334
'yml/require-string-key': 2,
34-
35-
// Should be removed once the specs are finished
36-
'yml/no-empty-document': 0,
3735
},
36+
overrides: [{
37+
files: ['specs/**/*.yml'],
38+
rules: {
39+
"automation-custom/description-dot": "error",
40+
}
41+
}
42+
]
3843
},
3944
],
4045

.github/workflows/check.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ jobs:
6666
- name: Test scripts
6767
run: yarn scripts:test
6868

69+
- name: Test custom eslint plugin
70+
run: yarn workspace eslint-plugin-automation-custom test
71+
6972
specs:
7073
runs-on: ubuntu-20.04
7174
timeout-minutes: 10

eslint/jest.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { Config } from '@jest/types';
2+
3+
const config: Config.InitialOptions = {
4+
preset: 'ts-jest',
5+
testEnvironment: 'node',
6+
};
7+
8+
export default config;

eslint/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "eslint-plugin-automation-custom",
3+
"version": "1.0.0",
4+
"description": "Custom rules for eslint",
5+
"packageManager": "[email protected]",
6+
"main": "dist/index.js",
7+
"files": [
8+
"src/**.ts"
9+
],
10+
"scripts": {
11+
"build": "tsc",
12+
"test": "jest"
13+
},
14+
"devDependencies": {
15+
"@types/jest": "27.4.1",
16+
"eslint": "8.11.0",
17+
"jest": "27.5.1",
18+
"ts-jest": "27.1.3",
19+
"ts-node": "10.7.0",
20+
"typescript": "4.5.4"
21+
}
22+
}

eslint/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { descriptionDot } from './rules/descriptionDot';
2+
3+
const rules = {
4+
'description-dot': descriptionDot,
5+
};
6+
7+
export { rules };

eslint/src/rules/descriptionDot.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* eslint-disable no-console */
2+
import type { Rule } from 'eslint';
3+
4+
import { isBLockScalar, isPairWithKey, isScalar } from '../utils';
5+
6+
export const descriptionDot: Rule.RuleModule = {
7+
meta: {
8+
docs: {
9+
description: 'description must end with a dot',
10+
},
11+
messages: {
12+
descriptionNoDot: 'description does not end with a dot',
13+
},
14+
fixable: 'code',
15+
},
16+
create(context) {
17+
if (!context.parserServices.isYAML) {
18+
return {};
19+
}
20+
21+
return {
22+
YAMLPair(node): void {
23+
if (!isPairWithKey(node, 'description')) {
24+
return;
25+
}
26+
if (!isScalar(node.value)) {
27+
return;
28+
}
29+
const value = node.value;
30+
if (
31+
typeof value.value !== 'string' ||
32+
value.value.trim().endsWith('.') ||
33+
!value.value.trim().includes(' ')
34+
) {
35+
// The rule is respected if:
36+
// the description is not a string
37+
// or it ends with a dot
38+
// or it's a single word (like 'OK' or 'Success', it's not a sentence)
39+
return;
40+
}
41+
42+
// trim the whitespaces at the end before adding the dot. This assume the indent is 2
43+
let toTrim = value.value.length - value.value.trimEnd().length;
44+
if (isBLockScalar(value)) {
45+
toTrim += node.key!.loc.start.column + 2;
46+
}
47+
context.report({
48+
node: node as any,
49+
messageId: 'descriptionNoDot',
50+
fix(fixer) {
51+
return fixer.insertTextAfterRange(
52+
[0, value.range[1] - toTrim],
53+
'.'
54+
);
55+
},
56+
});
57+
},
58+
};
59+
},
60+
};

eslint/src/utils.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { AST } from 'yaml-eslint-parser';
2+
3+
export function isScalar(node: AST.YAMLNode | null): node is AST.YAMLScalar {
4+
return node !== null && node.type === 'YAMLScalar';
5+
}
6+
7+
export function isBLockScalar(
8+
node: AST.YAMLNode | null
9+
): node is AST.YAMLBlockFoldedScalar | AST.YAMLBlockLiteralScalar {
10+
return isScalar(node) && 'chomping' in node;
11+
}
12+
13+
export function isPairWithKey(
14+
node: AST.YAMLNode | null,
15+
key: string
16+
): node is AST.YAMLPair {
17+
if (node === null || node.type !== 'YAMLPair' || node.key === null)
18+
return false;
19+
return isScalar(node.key) && node.key.value === key;
20+
}

eslint/tests/descriptionDot.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { RuleTester } from 'eslint';
2+
3+
import { descriptionDot } from '../src/rules/descriptionDot';
4+
5+
const ruleTester = new RuleTester({
6+
parser: require.resolve('yaml-eslint-parser'),
7+
});
8+
9+
ruleTester.run('description-dot', descriptionDot, {
10+
valid: [
11+
`
12+
simple:
13+
type: number
14+
description: a number.
15+
`,
16+
`
17+
multi:
18+
description: >
19+
Creates a new A/B test with provided configuration.
20+
21+
You can set an A/B test on two different indices with different settings, or on the same index with different search parameters by providing a customSearchParameters setting on one of the variants.
22+
`,
23+
`
24+
multiStrip:
25+
description: >-
26+
Multiline comment
27+
on description.
28+
`,
29+
`
30+
responses:
31+
'200':
32+
description: OK
33+
`,
34+
],
35+
invalid: [
36+
{
37+
code: `
38+
simple:
39+
description: a number
40+
`,
41+
errors: [{ messageId: 'descriptionNoDot' }],
42+
output: `
43+
simple:
44+
description: a number.
45+
`,
46+
},
47+
{
48+
code: `
49+
multi:
50+
description: >
51+
Multiline comment
52+
on description
53+
`,
54+
errors: [{ messageId: 'descriptionNoDot' }],
55+
output: `
56+
multi:
57+
description: >
58+
Multiline comment
59+
on description.
60+
`,
61+
},
62+
],
63+
});

eslint/tsconfig.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "../config/base.tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
},
6+
"include": [
7+
"src/**/*.ts",
8+
],
9+
"exclude": [
10+
"node_modules",
11+
"dist"
12+
]
13+
}

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"playground/javascript/browser/",
88
"scripts/",
99
"tests/output/javascript",
10-
"website/"
10+
"website/",
11+
"eslint"
1112
],
1213
"scripts": {
1314
"cli": "yarn workspace scripts ts-node --transpile-only ./index.ts",
@@ -19,6 +20,8 @@
1920
"docker": "docker exec -it dev yarn cli $*",
2021
"github-actions:lint": "eslint --ext=yml .github/",
2122
"playground:browser": "yarn workspace javascript-browser-playground start",
23+
"postinstall": "yarn workspace eslint-plugin-automation-custom build",
24+
"build:eslint": "yarn workspace eslint-plugin-automation-custom build && yarn install",
2225
"release": "yarn workspace scripts createReleaseIssue",
2326
"scripts:lint": "eslint --ext=ts scripts/",
2427
"scripts:test": "yarn workspace scripts test",
@@ -37,6 +40,7 @@
3740
"eslint-config-algolia": "20.0.0",
3841
"eslint-config-prettier": "8.5.0",
3942
"eslint-plugin-algolia": "2.0.0",
43+
"eslint-plugin-automation-custom": "1.0.0",
4044
"eslint-plugin-eslint-comments": "3.2.0",
4145
"eslint-plugin-import": "2.25.4",
4246
"eslint-plugin-jsdoc": "37.9.7",

specs/abtesting/spec.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ security:
2323
apiKey: []
2424
tags:
2525
- name: abtesting
26-
description: abtesting API reference
26+
description: abtesting API reference.
2727
paths:
2828
# ######################
2929
# ### Custom request ###

specs/analytics/spec.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ security:
2323
apiKey: []
2424
tags:
2525
- name: analytics
26-
description: Analytics API reference
26+
description: Analytics API reference.
2727
paths:
2828
# ######################
2929
# ### Custom request ###

specs/bundled/abtesting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ security:
273273
apiKey: []
274274
tags:
275275
- name: abtesting
276-
description: abtesting API reference
276+
description: abtesting API reference.
277277
paths:
278278
/1{path}:
279279
get:

specs/bundled/analytics.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ security:
442442
apiKey: []
443443
tags:
444444
- name: analytics
445-
description: Analytics API reference
445+
description: Analytics API reference.
446446
paths:
447447
/1{path}:
448448
get:

specs/bundled/insights.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ security:
8181
apiKey: []
8282
tags:
8383
- name: insights
84-
description: Insights API reference
84+
description: Insights API reference.
8585
paths:
8686
/1{path}:
8787
get:

specs/bundled/personalization.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ security:
149149
apiKey: []
150150
tags:
151151
- name: personalization
152-
description: Personalization API reference
152+
description: Personalization API reference.
153153
paths:
154154
/1{path}:
155155
get:
@@ -297,7 +297,7 @@ paths:
297297
$ref: '#/components/schemas/userToken'
298298
lastEventAt:
299299
type: string
300-
description: Date of last event update. (ISO-8601 format)
300+
description: Date of last event update. (ISO-8601 format).
301301
scores:
302302
type: object
303303
description: The userToken scores.

0 commit comments

Comments
 (0)