@redocly/openapi-core 1.0.0-beta.89 → 1.0.0-beta.92

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.
Files changed (50) hide show
  1. package/__tests__/__snapshots__/bundle.test.ts.snap +15 -13
  2. package/__tests__/ref-utils.test.ts +23 -1
  3. package/lib/config/all.js +1 -0
  4. package/lib/config/config.js +6 -11
  5. package/lib/config/minimal.js +1 -0
  6. package/lib/config/recommended.js +1 -0
  7. package/lib/config/rules.d.ts +1 -1
  8. package/lib/config/rules.js +10 -2
  9. package/lib/redocly/index.d.ts +4 -1
  10. package/lib/redocly/index.js +28 -19
  11. package/lib/redocly/registry-api.d.ts +4 -1
  12. package/lib/redocly/registry-api.js +3 -3
  13. package/lib/ref-utils.js +1 -1
  14. package/lib/rules/common/assertions/asserts.d.ts +5 -0
  15. package/lib/rules/common/assertions/asserts.js +143 -0
  16. package/lib/rules/common/assertions/index.d.ts +2 -0
  17. package/lib/rules/common/assertions/index.js +52 -0
  18. package/lib/rules/common/assertions/utils.d.ts +20 -0
  19. package/lib/rules/common/assertions/utils.js +123 -0
  20. package/lib/rules/oas2/index.d.ts +1 -0
  21. package/lib/rules/oas2/index.js +2 -0
  22. package/lib/rules/oas3/index.js +2 -0
  23. package/lib/types/redocly-yaml.js +24 -11
  24. package/lib/visitors.d.ts +2 -2
  25. package/lib/walk.d.ts +1 -0
  26. package/lib/walk.js +1 -1
  27. package/package.json +1 -1
  28. package/src/__tests__/lint.test.ts +40 -6
  29. package/src/bundle.ts +1 -1
  30. package/src/config/all.ts +1 -0
  31. package/src/config/config.ts +15 -12
  32. package/src/config/minimal.ts +1 -0
  33. package/src/config/recommended.ts +1 -0
  34. package/src/config/rules.ts +11 -2
  35. package/src/lint.ts +3 -3
  36. package/src/redocly/index.ts +49 -30
  37. package/src/redocly/registry-api.ts +38 -21
  38. package/src/ref-utils.ts +1 -1
  39. package/src/rules/common/assertions/__tests__/asserts.test.ts +231 -0
  40. package/src/rules/common/assertions/__tests__/index.test.ts +65 -0
  41. package/src/rules/common/assertions/__tests__/utils.test.ts +89 -0
  42. package/src/rules/common/assertions/asserts.ts +137 -0
  43. package/src/rules/common/assertions/index.ts +75 -0
  44. package/src/rules/common/assertions/utils.ts +164 -0
  45. package/src/rules/oas2/index.ts +2 -0
  46. package/src/rules/oas3/index.ts +2 -0
  47. package/src/types/redocly-yaml.ts +30 -11
  48. package/src/visitors.ts +2 -2
  49. package/src/walk.ts +2 -1
  50. package/tsconfig.tsbuildinfo +1 -1
@@ -22,28 +22,43 @@ export class RegistryApi {
22
22
 
23
23
  private async request(path = '', options: RequestInit = {}, region?: Region) {
24
24
  const headers = Object.assign({}, options.headers || {}, { 'x-redocly-cli-version': version });
25
- if (!headers.hasOwnProperty('authorization')) { throw new Error('Unauthorized'); }
25
+
26
+ if (!headers.hasOwnProperty('authorization')) {
27
+ throw new Error('Unauthorized');
28
+ }
29
+
26
30
  const response = await fetch(
27
31
  `${this.getBaseUrl(region)}${path}`,
28
32
  Object.assign({}, options, { headers }),
29
33
  );
30
- if (response.status === 401) { throw new Error('Unauthorized'); }
34
+
35
+ if (response.status === 401) {
36
+ throw new Error('Unauthorized');
37
+ }
38
+
31
39
  if (response.status === 404) {
32
40
  const body: RegistryApiTypes.NotFoundProblemResponse = await response.json();
33
41
  throw new Error(body.code);
34
42
  }
43
+
35
44
  return response;
36
45
  }
37
46
 
38
- async authStatus(accessToken: string, region: Region, verbose = false) {
47
+ async authStatus(
48
+ accessToken: string,
49
+ region: Region,
50
+ verbose = false,
51
+ ): Promise<{ viewerId: string; organizations: string[] }> {
39
52
  try {
40
- const response = await this.request('', { headers: { authorization: accessToken }}, region);
41
- return response.ok;
53
+ const response = await this.request('', { headers: { authorization: accessToken } }, region);
54
+
55
+ return await response.json();
42
56
  } catch (error) {
43
57
  if (verbose) {
44
58
  console.log(error);
45
59
  }
46
- return false;
60
+
61
+ throw error;
47
62
  }
48
63
  }
49
64
 
@@ -69,7 +84,7 @@ export class RegistryApi {
69
84
  isUpsert,
70
85
  }),
71
86
  },
72
- this.region
87
+ this.region,
73
88
  );
74
89
 
75
90
  if (response.ok) {
@@ -88,20 +103,22 @@ export class RegistryApi {
88
103
  branch,
89
104
  isUpsert,
90
105
  }: RegistryApiTypes.PushApiParams) {
91
- const response = await this.request(`/${organizationId}/${name}/${version}`, {
92
- method: 'PUT',
93
- headers: {
94
- 'content-type': 'application/json',
95
- authorization: this.accessToken
96
- } as HeadersInit,
97
- body: JSON.stringify({
98
- rootFilePath,
99
- filePaths,
100
- branch,
101
- isUpsert,
102
- }),
103
- },
104
- this.region
106
+ const response = await this.request(
107
+ `/${organizationId}/${name}/${version}`,
108
+ {
109
+ method: 'PUT',
110
+ headers: {
111
+ 'content-type': 'application/json',
112
+ authorization: this.accessToken,
113
+ } as HeadersInit,
114
+ body: JSON.stringify({
115
+ rootFilePath,
116
+ filePaths,
117
+ branch,
118
+ isUpsert,
119
+ }),
120
+ },
121
+ this.region,
105
122
  );
106
123
 
107
124
  if (response.ok) {
package/src/ref-utils.ts CHANGED
@@ -60,7 +60,7 @@ export function pointerBaseName(pointer: string) {
60
60
 
61
61
  export function refBaseName(ref: string) {
62
62
  const parts = ref.split(/[\/\\]/); // split by '\' and '/'
63
- return parts[parts.length - 1].split('.')[0];
63
+ return parts[parts.length - 1].replace(/\.[^.]+$/, ''); // replace extension with empty string
64
64
  }
65
65
 
66
66
  export function isAbsoluteUrl(ref: string) {
@@ -0,0 +1,231 @@
1
+ import { asserts } from '../asserts';
2
+
3
+ describe('oas3 assertions', () => {
4
+ describe('generic rules', () => {
5
+ const fakeNode = {
6
+ foo: '',
7
+ bar: '',
8
+ baz: '',
9
+ };
10
+
11
+ describe('pattern', () => {
12
+ it('value should match regex pattern', () => {
13
+ expect(asserts.pattern('test string', '/test/')).toBeTruthy();
14
+ expect(asserts.pattern('test string', '/test me/')).toBeFalsy();
15
+ expect(asserts.pattern(['test string', 'test me'], '/test/')).toBeTruthy();
16
+ expect(asserts.pattern(['test string', 'test me'], '/test me/')).toBeFalsy();
17
+ });
18
+ });
19
+
20
+ describe('enum', () => {
21
+ it('value should be among predefined keys', () => {
22
+ expect(asserts.enum('test', ['test', 'example'])).toBeTruthy();
23
+ expect(asserts.enum(['test'], ['test', 'example'])).toBeTruthy();
24
+ expect(asserts.enum(['test', 'example'], ['test', 'example'])).toBeTruthy();
25
+ expect(asserts.enum(['test', 'example', 'foo'], ['test', 'example'])).toBeFalsy();
26
+ expect(asserts.enum('test', ['foo', 'example'])).toBeFalsy();
27
+ expect(asserts.enum(['test', 'foo'], ['test', 'example'])).toBeFalsy();
28
+ });
29
+ });
30
+
31
+ describe('defined', () => {
32
+ it('value should be defined', () => {
33
+ expect(asserts.defined('test', true)).toBeTruthy();
34
+ expect(asserts.defined(undefined, true)).toBeFalsy();
35
+ });
36
+ it('value should be undefined', () => {
37
+ expect(asserts.defined(undefined, false)).toBeTruthy();
38
+ expect(asserts.defined('test', false)).toBeFalsy();
39
+ });
40
+ });
41
+
42
+ describe('undefined', () => {
43
+ it('value should be undefined', () => {
44
+ expect(asserts.undefined(undefined, true)).toBeTruthy();
45
+ expect(asserts.undefined('test', true)).toBeFalsy();
46
+ });
47
+ it('value should be defined', () => {
48
+ expect(asserts.undefined('test', false)).toBeTruthy();
49
+ expect(asserts.undefined(undefined, false)).toBeFalsy();
50
+ });
51
+ });
52
+
53
+ describe('required', () => {
54
+ it('values should be required', () => {
55
+ expect(asserts.required(['one', 'two', 'three'], ['one', 'two'])).toBeTruthy();
56
+ expect(asserts.required(['one', 'two'], ['one', 'two', 'three'])).toBeFalsy();
57
+ });
58
+ });
59
+
60
+ describe('nonEmpty', () => {
61
+ it('value should not be empty', () => {
62
+ expect(asserts.nonEmpty('test', true)).toBeTruthy();
63
+ expect(asserts.nonEmpty('', true)).toBeFalsy();
64
+ expect(asserts.nonEmpty(null, true)).toBeFalsy();
65
+ expect(asserts.nonEmpty(undefined, true)).toBeFalsy();
66
+ });
67
+ it('value should be empty', () => {
68
+ expect(asserts.nonEmpty('', false)).toBeTruthy();
69
+ expect(asserts.nonEmpty(null, false)).toBeTruthy();
70
+ expect(asserts.nonEmpty(undefined, false)).toBeTruthy();
71
+ expect(asserts.nonEmpty('test', false)).toBeFalsy();
72
+ });
73
+ });
74
+
75
+ describe('minLength', () => {
76
+ it('value should have less or equal than 5 symbols length', () => {
77
+ expect(asserts.minLength('test', 5)).toBeFalsy();
78
+ expect(asserts.minLength([1, 2, 3, 4], 5)).toBeFalsy();
79
+ expect(asserts.minLength([1, 2, 3, 4, 5], 5)).toBeTruthy();
80
+ expect(asserts.minLength([1, 2, 3, 4, 5, 6], 5)).toBeTruthy();
81
+ expect(asserts.minLength('example', 5)).toBeTruthy();
82
+ expect(asserts.minLength([], 5)).toBeFalsy();
83
+ expect(asserts.minLength('', 5)).toBeFalsy();
84
+ });
85
+ });
86
+
87
+ describe('maxLength', () => {
88
+ it('value should have more or equal than 5 symbols length', () => {
89
+ expect(asserts.maxLength('test', 5)).toBeTruthy();
90
+ expect(asserts.maxLength([1, 2, 3, 4], 5)).toBeTruthy();
91
+ expect(asserts.maxLength([1, 2, 3, 4, 5], 5)).toBeTruthy();
92
+ expect(asserts.maxLength([1, 2, 3, 4, 5, 6], 5)).toBeFalsy();
93
+ expect(asserts.maxLength('example', 5)).toBeFalsy();
94
+ expect(asserts.maxLength([], 5)).toBeTruthy();
95
+ expect(asserts.maxLength('', 5)).toBeTruthy();
96
+ });
97
+ });
98
+
99
+ describe('casing', () => {
100
+ it('value should be camelCase', () => {
101
+ expect(asserts.casing(['testExample', 'fooBar'], 'camelCase')).toBeTruthy();
102
+ expect(asserts.casing(['testExample', 'FooBar'], 'camelCase')).toBeFalsy();
103
+ expect(asserts.casing('testExample', 'camelCase')).toBeTruthy();
104
+ expect(asserts.casing('TestExample', 'camelCase')).toBeFalsy();
105
+ expect(asserts.casing('test-example', 'camelCase')).toBeFalsy();
106
+ expect(asserts.casing('test_example', 'camelCase')).toBeFalsy();
107
+ });
108
+ it('value should be PascalCase', () => {
109
+ expect(asserts.casing('TestExample', 'PascalCase')).toBeTruthy();
110
+ expect(asserts.casing(['TestExample', 'FooBar'], 'PascalCase')).toBeTruthy();
111
+ expect(asserts.casing(['TestExample', 'fooBar'], 'PascalCase')).toBeFalsy();
112
+ expect(asserts.casing('testExample', 'PascalCase')).toBeFalsy();
113
+ expect(asserts.casing('test-example', 'PascalCase')).toBeFalsy();
114
+ expect(asserts.casing('test_example', 'PascalCase')).toBeFalsy();
115
+ });
116
+ it('value should be kebab-case', () => {
117
+ expect(asserts.casing('test-example', 'kebab-case')).toBeTruthy();
118
+ expect(asserts.casing(['test-example', 'foo-bar'], 'kebab-case')).toBeTruthy();
119
+ expect(asserts.casing(['test-example', 'foo_bar'], 'kebab-case')).toBeFalsy();
120
+ expect(asserts.casing('testExample', 'kebab-case')).toBeFalsy();
121
+ expect(asserts.casing('TestExample', 'kebab-case')).toBeFalsy();
122
+ expect(asserts.casing('test_example', 'kebab-case')).toBeFalsy();
123
+ });
124
+ it('value should be snake_case', () => {
125
+ expect(asserts.casing('test_example', 'snake_case')).toBeTruthy();
126
+ expect(asserts.casing(['test_example', 'foo_bar'], 'snake_case')).toBeTruthy();
127
+ expect(asserts.casing(['test_example', 'foo-bar'], 'snake_case')).toBeFalsy();
128
+ expect(asserts.casing('testExample', 'snake_case')).toBeFalsy();
129
+ expect(asserts.casing('TestExample', 'snake_case')).toBeFalsy();
130
+ expect(asserts.casing('test-example', 'snake_case')).toBeFalsy();
131
+ });
132
+ it('value should be MACRO_CASE', () => {
133
+ expect(asserts.casing('TEST_EXAMPLE', 'MACRO_CASE')).toBeTruthy();
134
+ expect(asserts.casing(['TEST_EXAMPLE', 'FOO_BAR'], 'MACRO_CASE')).toBeTruthy();
135
+ expect(asserts.casing(['TEST_EXAMPLE', 'FOO-BAR'], 'MACRO_CASE')).toBeFalsy();
136
+ expect(asserts.casing('TEST_EXAMPLE_', 'MACRO_CASE')).toBeFalsy();
137
+ expect(asserts.casing('_TEST_EXAMPLE', 'MACRO_CASE')).toBeFalsy();
138
+ expect(asserts.casing('TEST__EXAMPLE', 'MACRO_CASE')).toBeFalsy();
139
+ expect(asserts.casing('TEST-EXAMPLE', 'MACRO_CASE')).toBeFalsy();
140
+ expect(asserts.casing('testExample', 'MACRO_CASE')).toBeFalsy();
141
+ expect(asserts.casing('TestExample', 'MACRO_CASE')).toBeFalsy();
142
+ expect(asserts.casing('test-example', 'MACRO_CASE')).toBeFalsy();
143
+ });
144
+ it('value should be COBOL-CASE', () => {
145
+ expect(asserts.casing('TEST-EXAMPLE', 'COBOL-CASE')).toBeTruthy();
146
+ expect(asserts.casing(['TEST-EXAMPLE', 'FOO-BAR'], 'COBOL-CASE')).toBeTruthy();
147
+ expect(asserts.casing(['TEST-EXAMPLE', 'FOO_BAR'], 'COBOL-CASE')).toBeFalsy();
148
+ expect(asserts.casing('TEST-EXAMPLE-', 'COBOL-CASE')).toBeFalsy();
149
+ expect(asserts.casing('0TEST-EXAMPLE', 'COBOL-CASE')).toBeFalsy();
150
+ expect(asserts.casing('-TEST-EXAMPLE', 'COBOL-CASE')).toBeFalsy();
151
+ expect(asserts.casing('TEST--EXAMPLE', 'COBOL-CASE')).toBeFalsy();
152
+ expect(asserts.casing('TEST_EXAMPLE', 'COBOL-CASE')).toBeFalsy();
153
+ expect(asserts.casing('testExample', 'COBOL-CASE')).toBeFalsy();
154
+ expect(asserts.casing('TestExample', 'COBOL-CASE')).toBeFalsy();
155
+ expect(asserts.casing('test-example', 'COBOL-CASE')).toBeFalsy();
156
+ });
157
+ it('value should be flatcase', () => {
158
+ expect(asserts.casing('testexample', 'flatcase')).toBeTruthy();
159
+ expect(asserts.casing(['testexample', 'foobar'], 'flatcase')).toBeTruthy();
160
+ expect(asserts.casing(['testexample', 'foo_bar'], 'flatcase')).toBeFalsy();
161
+ expect(asserts.casing('testexample_', 'flatcase')).toBeFalsy();
162
+ expect(asserts.casing('0testexample', 'flatcase')).toBeFalsy();
163
+ expect(asserts.casing('testExample', 'flatcase')).toBeFalsy();
164
+ expect(asserts.casing('TestExample', 'flatcase')).toBeFalsy();
165
+ expect(asserts.casing('test-example', 'flatcase')).toBeFalsy();
166
+ });
167
+ });
168
+
169
+ describe.skip('sortOrder', () => {
170
+ it('value should be ordered in ASC direction', () => {
171
+ expect(asserts.sortOrder(['example', 'foo', 'test'], 'asc')).toBeTruthy();
172
+ expect(asserts.sortOrder(['example', 'foo', 'test'], { direction: 'asc' })).toBeTruthy();
173
+ expect(asserts.sortOrder(['example'], 'asc')).toBeTruthy();
174
+ expect(asserts.sortOrder(['example', 'test', 'foo'], 'asc')).toBeFalsy();
175
+ expect(asserts.sortOrder(['example', 'foo', 'test'], 'desc')).toBeFalsy();
176
+ expect(
177
+ asserts.sortOrder([{ name: 'bar' }, { name: 'baz' }, { name: 'foo' }], {
178
+ direction: 'asc',
179
+ property: 'name',
180
+ }),
181
+ ).toBeTruthy();
182
+ expect(
183
+ asserts.sortOrder([{ name: 'bar' }, { name: 'baz' }, { name: 'foo' }], {
184
+ direction: 'desc',
185
+ property: 'name',
186
+ }),
187
+ ).toBeFalsy();
188
+ });
189
+ it('value should be ordered in DESC direction', () => {
190
+ expect(asserts.sortOrder(['test', 'foo', 'example'], 'desc')).toBeTruthy();
191
+ expect(asserts.sortOrder(['test', 'foo', 'example'], { direction: 'desc' })).toBeTruthy();
192
+ expect(asserts.sortOrder(['example'], 'desc')).toBeTruthy();
193
+ expect(asserts.sortOrder(['example', 'test', 'foo'], 'desc')).toBeFalsy();
194
+ expect(asserts.sortOrder(['test', 'foo', 'example'], 'asc')).toBeFalsy();
195
+ expect(
196
+ asserts.sortOrder([{ name: 'foo' }, { name: 'baz' }, { name: 'bar' }], {
197
+ direction: 'desc',
198
+ property: 'name',
199
+ }),
200
+ ).toBeTruthy();
201
+ expect(
202
+ asserts.sortOrder([{ name: 'foo' }, { name: 'baz' }, { name: 'bar' }], {
203
+ direction: 'asc',
204
+ property: 'name',
205
+ }),
206
+ ).toBeFalsy();
207
+ });
208
+ });
209
+
210
+ describe('mutuallyExclusive', () => {
211
+ it('node should not have more than one property from predefined list', () => {
212
+ expect(asserts.mutuallyExclusive(Object.keys(fakeNode), ['foo', 'test'])).toBeTruthy();
213
+ expect(asserts.mutuallyExclusive(Object.keys(fakeNode), [])).toBeTruthy();
214
+ expect(asserts.mutuallyExclusive(Object.keys(fakeNode), ['foo', 'bar'])).toBeFalsy();
215
+ expect(
216
+ asserts.mutuallyExclusive(Object.keys(fakeNode), ['foo', 'bar', 'test']),
217
+ ).toBeFalsy();
218
+ });
219
+ });
220
+
221
+ describe('mutuallyRequired', () => {
222
+ it('node should have all the properties from predefined list', () => {
223
+ expect(asserts.mutuallyRequired(Object.keys(fakeNode), ['foo', 'bar'])).toBeTruthy();
224
+ expect(asserts.mutuallyRequired(Object.keys(fakeNode), ['foo', 'bar', 'baz'])).toBeTruthy();
225
+ expect(asserts.mutuallyRequired(Object.keys(fakeNode), [])).toBeTruthy();
226
+ expect(asserts.mutuallyRequired(Object.keys(fakeNode), ['foo', 'test'])).toBeFalsy();
227
+ expect(asserts.mutuallyRequired(Object.keys(fakeNode), ['foo', 'bar', 'test'])).toBeFalsy();
228
+ });
229
+ });
230
+ });
231
+ });
@@ -0,0 +1,65 @@
1
+ import { Assertions } from '../.';
2
+
3
+ const opts = {
4
+ '0': {
5
+ subject: 'Operation',
6
+ property: 'summary',
7
+ description: 'example warn text',
8
+ severity: 'warn',
9
+ pattern: '/example/',
10
+ },
11
+ '1': {
12
+ subject: 'PathItem',
13
+ context: [{ type: 'Operation', matchParentKeys: ['post'] }],
14
+ description: 'example warn text',
15
+ severity: 'warn',
16
+ mutuallyExclusive: ['summary', 'security'],
17
+ },
18
+ '2': {
19
+ subject: ['PathItem'],
20
+ context: [{ type: 'Operation' }],
21
+ property: 'tags',
22
+ description: 'example warn text',
23
+ severity: 'warn',
24
+ sortOrder: 'desc',
25
+ },
26
+ '3': {
27
+ subject: ['Foo'],
28
+ context: [{ type: 'Bar' }, { type: 'Baz' }],
29
+ property: 'test',
30
+ description: 'example warn text',
31
+ severity: 'warn',
32
+ sortOrder: 'desc',
33
+ },
34
+ };
35
+
36
+ describe('Oas3 assertions', () => {
37
+ it('should return the right visitor structure', () => {
38
+ const visitors = Assertions(opts) as any;
39
+ expect(visitors).toMatchInlineSnapshot(`
40
+ Array [
41
+ Object {
42
+ "Operation": [Function],
43
+ },
44
+ Object {
45
+ "Operation": Object {
46
+ "PathItem": [Function],
47
+ "skip": [Function],
48
+ },
49
+ },
50
+ Object {
51
+ "Operation": Object {
52
+ "PathItem": [Function],
53
+ },
54
+ },
55
+ Object {
56
+ "Bar": Object {
57
+ "Baz": Object {
58
+ "Foo": [Function],
59
+ },
60
+ },
61
+ },
62
+ ]
63
+ `);
64
+ });
65
+ });
@@ -0,0 +1,89 @@
1
+ import { isOrdered, buildVisitorObject, getIntersectionLength } from '../utils';
2
+
3
+ describe('Oas3 assertions', () => {
4
+ describe('Utils', () => {
5
+ describe('getCounts', () => {
6
+ it('should return the right counts', () => {
7
+ const arr = ['foo', 'bar', 'baz'];
8
+ expect(getIntersectionLength(arr, ['foo'])).toBe(1);
9
+ expect(getIntersectionLength(arr, ['foo', 'bar', 'baz'])).toBe(3);
10
+ expect(getIntersectionLength(arr, ['foo', 'test', 'baz'])).toBe(2);
11
+ expect(getIntersectionLength(arr, ['example', 'test'])).toBe(0);
12
+ });
13
+ });
14
+
15
+ describe('isOrdered', () => {
16
+ it('should say if array is ordered or not in specific direction', () => {
17
+ expect(isOrdered(['example', 'foo', 'test'], 'asc')).toBeTruthy();
18
+ expect(isOrdered(['example'], 'asc')).toBeTruthy();
19
+ expect(isOrdered(['test', 'foo', 'example'], 'desc')).toBeTruthy();
20
+ expect(isOrdered(['example'], 'desc')).toBeTruthy();
21
+ expect(isOrdered(['example', 'test', 'foo'], 'asc')).toBeFalsy();
22
+ expect(isOrdered(['example', 'foo', 'test'], 'desc')).toBeFalsy();
23
+ });
24
+ });
25
+
26
+ describe('buildVisitorObject', () => {
27
+ it('should return a consistent visitor structure', () => {
28
+ const context = [
29
+ {
30
+ type: 'Foo',
31
+ matchParentKeys: ['test'],
32
+ },
33
+ {
34
+ type: 'Bar',
35
+ matchParentKeys: ['test'],
36
+ },
37
+ {
38
+ type: 'Roof',
39
+ matchParentKeys: ['test'],
40
+ },
41
+ ];
42
+
43
+ const visitors = buildVisitorObject('Bar', context, () => {}) as any;
44
+
45
+ expect(visitors).toMatchInlineSnapshot(`
46
+ Object {
47
+ "Foo": Object {
48
+ "Bar": Object {
49
+ "Roof": Object {
50
+ "Bar": [Function],
51
+ "skip": [Function],
52
+ },
53
+ "skip": [Function],
54
+ },
55
+ "skip": [Function],
56
+ },
57
+ }
58
+ `);
59
+ });
60
+
61
+ it('should return the right visitor structure', () => {
62
+ const context = [
63
+ {
64
+ type: 'Operation',
65
+ matchParentKeys: ['put'],
66
+ },
67
+ {
68
+ type: 'ResponsesMap',
69
+ matchParentKeys: [201, 200],
70
+ },
71
+ ];
72
+
73
+ const visitors = buildVisitorObject('MediaTypeMap', context, () => {}) as any;
74
+
75
+ expect(visitors).toMatchInlineSnapshot(`
76
+ Object {
77
+ "Operation": Object {
78
+ "ResponsesMap": Object {
79
+ "MediaTypeMap": [Function],
80
+ "skip": [Function],
81
+ },
82
+ "skip": [Function],
83
+ },
84
+ }
85
+ `);
86
+ });
87
+ });
88
+ });
89
+ });
@@ -0,0 +1,137 @@
1
+ import { OrderOptions, OrderDirection, isOrdered, getIntersectionLength } from './utils';
2
+
3
+ type Asserts = Record<string, (value: any, condition: any) => boolean>;
4
+
5
+ export const runOnKeysSet = new Set([
6
+ 'mutuallyExclusive',
7
+ 'mutuallyRequired',
8
+ 'enum',
9
+ 'pattern',
10
+ 'minLength',
11
+ 'maxLength',
12
+ 'casing',
13
+ 'sortOrder',
14
+ 'disallowed',
15
+ 'required',
16
+ ]);
17
+ export const runOnValuesSet = new Set([
18
+ 'pattern',
19
+ 'enum',
20
+ 'defined',
21
+ 'undefined',
22
+ 'nonEmpty',
23
+ 'minLength',
24
+ 'maxLength',
25
+ 'casing',
26
+ 'sortOrder',
27
+ ]);
28
+
29
+ export const asserts: Asserts = {
30
+ pattern: (value: string | string[], condition: string): boolean => {
31
+ if (typeof value === 'undefined') return true; // property doesn't exist, no need to lint it with this assert
32
+ const values = typeof value === 'string' ? [value] : value;
33
+ const regexOptions = condition.match(/(\b\/\b)(.+)/g) || ['/'];
34
+ condition = condition.slice(1).replace(regexOptions[0], '');
35
+ const regx = new RegExp(condition, regexOptions[0].slice(1));
36
+ for (let _val of values) {
37
+ if (!_val.match(regx)) {
38
+ return false;
39
+ }
40
+ }
41
+ return true;
42
+ },
43
+ enum: (value: string | string[], condition: string[]): boolean => {
44
+ if (typeof value === 'undefined') return true; // property doesn't exist, no need to lint it with this assert
45
+ const values = typeof value === 'string' ? [value] : value;
46
+ for (let _val of values) {
47
+ if (!condition.includes(_val)) {
48
+ return false;
49
+ }
50
+ }
51
+ return true;
52
+ },
53
+ defined: (value: string | undefined, condition: boolean = true): boolean => {
54
+ const isDefined = typeof value !== 'undefined';
55
+ return condition ? isDefined : !isDefined;
56
+ },
57
+ required: (value: string[], keys: string[]): boolean => {
58
+ for (const requiredKey of keys) {
59
+ if (!value.includes(requiredKey)) {
60
+ return false;
61
+ }
62
+ }
63
+ return true;
64
+ },
65
+ disallowed: (value: string | string[], condition: string[]): boolean => {
66
+ if (typeof value === 'undefined') return true; // property doesn't exist, no need to lint it with this assert
67
+ const values = typeof value === 'string' ? [value] : value;
68
+ for (let _val of values) {
69
+ if (condition.includes(_val)) {
70
+ return false;
71
+ }
72
+ }
73
+ return true;
74
+ },
75
+ undefined: (value: any, condition: boolean = true): boolean => {
76
+ const isUndefined = typeof value === 'undefined';
77
+ return condition ? isUndefined : !isUndefined;
78
+ },
79
+ nonEmpty: (value: string | undefined | null, condition: boolean = true): boolean => {
80
+ const isEmpty = typeof value === 'undefined' || value === null || value === '';
81
+ return condition ? !isEmpty : isEmpty;
82
+ },
83
+ minLength: (value: string | any[], condition: number): boolean => {
84
+ if (typeof value === 'undefined') return true; // property doesn't exist, no need to lint it with this assert
85
+ return value.length >= condition;
86
+ },
87
+ maxLength: (value: string | any[], condition: number): boolean => {
88
+ if (typeof value === 'undefined') return true; // property doesn't exist, no need to lint it with this assert
89
+ return value.length <= condition;
90
+ },
91
+ casing: (value: string | string[], condition: string): boolean => {
92
+ if (typeof value === 'undefined') return true; // property doesn't exist, no need to lint it with this assert
93
+ const values = typeof value === 'string' ? [value] : value;
94
+ for (let _val of values) {
95
+ let matchCase = false;
96
+ switch (condition) {
97
+ case 'camelCase':
98
+ matchCase = !!_val.match(/^[a-z][a-zA-Z0-9]+$/g);
99
+ break;
100
+ case 'kebab-case':
101
+ matchCase = !!_val.match(/^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/g);
102
+ break;
103
+ case 'snake_case':
104
+ matchCase = !!_val.match(/^([a-z][a-z0-9]*)(_[a-z0-9]+)*$/g);
105
+ break;
106
+ case 'PascalCase':
107
+ matchCase = !!_val.match(/^[A-Z][a-zA-Z0-9]+$/g);
108
+ break;
109
+ case 'MACRO_CASE':
110
+ matchCase = !!_val.match(/^([A-Z][A-Z0-9]*)(_[A-Z0-9]+)*$/g);
111
+ break;
112
+ case 'COBOL-CASE':
113
+ matchCase = !!_val.match(/^([A-Z][A-Z0-9]*)(-[A-Z0-9]+)*$/g);
114
+ break;
115
+ case 'flatcase':
116
+ matchCase = !!_val.match(/^[a-z][a-z0-9]+$/g);
117
+ break;
118
+ }
119
+ if (!matchCase) {
120
+ return false;
121
+ }
122
+ }
123
+ return true;
124
+ },
125
+ sortOrder: (value: any[], condition: OrderOptions | OrderDirection): boolean => {
126
+ if (typeof value === 'undefined') return true;
127
+ return isOrdered(value, condition);
128
+ },
129
+ mutuallyExclusive: (value: string[], condition: string[]): boolean => {
130
+ return getIntersectionLength(value, condition) < 2;
131
+ },
132
+ mutuallyRequired: (value: string[], condition: string[]): boolean => {
133
+ return getIntersectionLength(value, condition) > 0
134
+ ? getIntersectionLength(value, condition) === condition.length
135
+ : true;
136
+ },
137
+ };