Skip to content

Commit c5cbc14

Browse files
authored
feat: support for reusable pathItems definitions in components for OAS 3.1 (#30)
1 parent cf6de89 commit c5cbc14

File tree

4 files changed

+162
-15
lines changed

4 files changed

+162
-15
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
"update-lock-file": "update-lock-file @netcracker"
2828
},
2929
"dependencies": {
30-
"@netcracker/qubership-apihub-api-unifier": "2.3.0",
30+
"@netcracker/qubership-apihub-api-unifier": "dev",
3131
"@netcracker/qubership-apihub-json-crawl": "1.0.4",
3232
"fast-equals": "4.0.3"
3333
},
3434
"devDependencies": {
35-
"@netcracker/qubership-apihub-compatibility-suites": "2.2.0",
35+
"@netcracker/qubership-apihub-compatibility-suites": "dev",
3636
"@netcracker/qubership-apihub-graphapi": "1.0.8",
3737
"@netcracker/qubership-apihub-npm-gitflow": "3.1.0",
3838
"@types/jest": "29.5.11",

src/openapi/openapi3.rules.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,20 @@ export const openApi3Rules = (options: OpenApi3RulesOptions): CompareRules => {
388388
'/*': operationAnnotationRule,
389389
}
390390

391+
const pathItemObjectRules = (options: OpenApi3RulesOptions): CompareRules => ({
392+
$: pathChangeClassifyRule,
393+
mapping: options.mode === COMPARE_MODE_OPERATION ? singleOperationPathMappingResolver : pathMappingResolver,
394+
'/description': { $: allAnnotation },
395+
'/parameters': {
396+
$: [nonBreaking, breaking, breaking],
397+
mapping: paramMappingResolver(1),
398+
...parametersRules,
399+
},
400+
'/servers': serversRules,
401+
'/summary': { $: allAnnotation },
402+
'/*': operationRule,
403+
})
404+
391405
const componentsRule: CompareRules = {
392406
$: allNonBreaking,
393407
[START_NEW_COMPARE_SCOPE_RULE]: COMPARE_SCOPE_COMPONENTS,
@@ -412,6 +426,10 @@ export const openApi3Rules = (options: OpenApi3RulesOptions): CompareRules => {
412426
...requestSchemaRules,
413427
}),
414428
},
429+
'/pathItems': {
430+
$: [nonBreaking, breaking, breaking],
431+
'/*': pathItemObjectRules(options),
432+
},
415433
'/securitySchemes': {
416434
$: [breaking, nonBreaking, breaking],
417435
'/*': {
@@ -438,19 +456,7 @@ export const openApi3Rules = (options: OpenApi3RulesOptions): CompareRules => {
438456
'/paths': {
439457
$: allUnclassified,
440458
mapping: options.mode === COMPARE_MODE_OPERATION ? singleOperationPathMappingResolver : pathMappingResolver,
441-
'/*': {
442-
$: pathChangeClassifyRule,
443-
mapping: options.mode === COMPARE_MODE_OPERATION ? singleOperationPathMappingResolver : pathMappingResolver,
444-
'/description': { $: allAnnotation },
445-
'/parameters': {
446-
$: [nonBreaking, breaking, breaking],
447-
mapping: paramMappingResolver(1),
448-
...parametersRules,
449-
},
450-
'/servers': serversRules,
451-
'/summary': { $: allAnnotation },
452-
'/*': operationRule,
453-
},
459+
'/*': pathItemObjectRules(options),
454460
},
455461
'/components': componentsRule,
456462
'/security': {

test/compatibility-suites/openapi/general-operation-parameters.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,3 +767,59 @@ describe('Openapi3 General Operation Parameters', () => {
767767
]))
768768
})
769769
})
770+
771+
const PATH_ITEM_PATH = [
772+
'components',
773+
'pathItems',
774+
'UserOps',
775+
]
776+
777+
describe('Openapi3.1 PathItems', () => {
778+
test('Add method in path item', async () => {
779+
const testId = 'add-method-in-path-item'
780+
const result = await compareFiles(SUITE_ID, testId)
781+
expect(result).toEqual(diffsMatcher([
782+
expect.objectContaining({
783+
action: DiffAction.add,
784+
afterDeclarationPaths: [[...PATH_ITEM_PATH, 'post']],
785+
type: nonBreaking,
786+
}),
787+
]))
788+
})
789+
790+
test('Remove unused method in path item', async () => {
791+
const testId = 'remove-unused-method-in-path-item'
792+
const result = await compareFiles(SUITE_ID, testId)
793+
expect(result).toEqual([])
794+
})
795+
796+
test('Add unused method in path item', async () => {
797+
const testId = 'add-unused-method-in-path-item'
798+
const result = await compareFiles(SUITE_ID, testId)
799+
expect(result).toEqual([])
800+
})
801+
802+
test('Remove method in path item', async () => {
803+
const testId = 'remove-method-in-path-item'
804+
const result = await compareFiles(SUITE_ID, testId)
805+
expect(result).toEqual(diffsMatcher([
806+
expect.objectContaining({
807+
action: DiffAction.remove,
808+
beforeDeclarationPaths: [[...PATH_ITEM_PATH, 'post']],
809+
type: breaking,
810+
}),
811+
]))
812+
})
813+
814+
test('Replace inline path item to ref', async () => {
815+
const testId = 'replace-inline-path-item-to-ref'
816+
const result = await compareFiles(SUITE_ID, testId)
817+
expect(result).toEqual([])
818+
})
819+
820+
test('Replace ref path item to inline', async () => {
821+
const testId = 'replace-ref-path-item-to-inline'
822+
const result = await compareFiles(SUITE_ID, testId)
823+
expect(result).toEqual([])
824+
})
825+
})

test/pathItems.rules.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { apiDiff, ClassifierType, DiffAction } from '../src'
2+
import { diffsMatcher } from './helper/matchers'
3+
4+
const COMPONENTS_RESPONSE_PATH = [
5+
'components',
6+
'pathItems',
7+
'componentsPathItem',
8+
'post',
9+
'responses',
10+
'200',
11+
]
12+
13+
describe('Openapi3.1 Components PathItems Rules', () => {
14+
test('reports changes to path items in components', async () => {
15+
const before = {
16+
'openapi': '3.1.0',
17+
'paths': {
18+
'/path1': {
19+
'post': {
20+
'responses': {
21+
'200': {},
22+
},
23+
},
24+
},
25+
},
26+
'components': {
27+
'pathItems': {
28+
'componentsPathItem': {
29+
'post': {
30+
'responses': {
31+
'200': {
32+
'description': 'Pet successfully added',
33+
},
34+
},
35+
},
36+
},
37+
},
38+
},
39+
}
40+
41+
const after = {
42+
'openapi': '3.1.0',
43+
'paths': {
44+
'/path1': {
45+
'post': {
46+
'responses': {
47+
'200': {},
48+
},
49+
},
50+
},
51+
},
52+
'components': {
53+
'pathItems': {
54+
'componentsPathItem': {
55+
'post': {
56+
'responses': {
57+
'200': {
58+
'description': 'new value',
59+
},
60+
},
61+
},
62+
},
63+
},
64+
},
65+
}
66+
67+
const result = apiDiff(before, after)
68+
expect(result.diffs).toEqual(diffsMatcher([
69+
expect.objectContaining({
70+
action: DiffAction.replace,
71+
beforeValue: 'Pet successfully added',
72+
afterValue: 'new value',
73+
beforeDeclarationPaths: [[
74+
...COMPONENTS_RESPONSE_PATH,
75+
'description',
76+
]],
77+
afterDeclarationPaths: [[
78+
...COMPONENTS_RESPONSE_PATH,
79+
'description',
80+
]],
81+
type: ClassifierType.annotation,
82+
}),
83+
]))
84+
})
85+
})

0 commit comments

Comments
 (0)