-
Notifications
You must be signed in to change notification settings - Fork 18
/
normalize-servers.ts
150 lines (133 loc) · 6 KB
/
normalize-servers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { Element } from '@swagger-api/apidom-core';
import {
PathItemServersElement,
OperationServersElement,
ServersElement,
} from '@swagger-api/apidom-ns-openapi-3-0';
import type OpenApi3_1Element from '../../elements/OpenApi3-1';
import type PathItemElement from '../../elements/PathItem';
import type ServerElement from '../../elements/Server';
import type OperationElement from '../../elements/Operation';
import type { Toolbox } from '../toolbox';
import NormalizeStorage from './normalize-header-examples/NormalizeStorage';
/**
* Override of Server Objects.
*
* List of Server Objects can be defined in OpenAPI 3.1 on multiple levels:
*
* - OpenAPI.servers
* - PathItem.servers
* - Operation.servers
*
* If an alternative server object is specified at the Path Item Object level, it will override OpenAPI.servers.
* If an alternative server object is specified at the Operation Object level, it will override PathItem.servers and OpenAPI.servers respectively.
*/
interface PluginOptions {
storageField?: string;
}
/* eslint-disable no-param-reassign */
const plugin =
({ storageField = 'x-normalized' }: PluginOptions = {}) =>
(toolbox: Toolbox) => {
const { namespace, ancestorLineageToJSONPointer, predicates } = toolbox;
let storage: NormalizeStorage | undefined;
return {
visitor: {
OpenApi3_1Element: {
enter(openapiElement: OpenApi3_1Element) {
const isServersUndefined = typeof openapiElement.servers === 'undefined';
const isServersArrayElement = predicates.isArrayElement(openapiElement.servers);
const isServersEmpty = isServersArrayElement && openapiElement.servers!.length === 0;
// @ts-ignore
const defaultServer = namespace.elements.Server.refract({ url: '/' });
if (isServersUndefined || !isServersArrayElement) {
openapiElement.servers = new ServersElement([defaultServer]);
} else if (isServersArrayElement && isServersEmpty) {
openapiElement.servers!.push(defaultServer);
}
storage = new NormalizeStorage(openapiElement, storageField, 'servers');
},
leave() {
storage = undefined;
},
},
PathItemElement(
pathItemElement: PathItemElement,
key: string | number,
parent: Element | undefined,
path: (string | number)[],
ancestors: [Element | Element[]],
) {
// skip visiting this Path Item
if (ancestors.some(predicates.isComponentsElement)) return;
if (!ancestors.some(predicates.isOpenApi3_1Element)) return;
const pathItemJSONPointer = ancestorLineageToJSONPointer([
...ancestors,
parent!,
pathItemElement,
]);
// skip visiting this Path Item Object if it's already normalized
if (storage!.includes(pathItemJSONPointer)) {
return;
}
const parentOpenapiElement = ancestors.find(predicates.isOpenApi3_1Element);
const isServersUndefined = typeof pathItemElement.servers === 'undefined';
const isServersArrayElement = predicates.isArrayElement(pathItemElement.servers);
const isServersEmpty = isServersArrayElement && pathItemElement.servers!.length === 0;
// duplicate OpenAPI.servers into this Path Item object
if (predicates.isOpenApi3_1Element(parentOpenapiElement)) {
const openapiServersContent = parentOpenapiElement.servers?.content;
const openapiServers = (openapiServersContent ?? []) as ServerElement[];
if (isServersUndefined || !isServersArrayElement) {
pathItemElement.servers = new PathItemServersElement(openapiServers);
} else if (isServersArrayElement && isServersEmpty) {
openapiServers.forEach((server) => {
pathItemElement.servers!.push(server);
});
}
storage!.append(pathItemJSONPointer);
}
},
OperationElement(
operationElement: OperationElement,
key: string | number,
parent: Element | undefined,
path: (string | number)[],
ancestors: [Element | Element[]],
) {
// skip visiting this Operation
if (ancestors.some(predicates.isComponentsElement)) return;
if (!ancestors.some(predicates.isOpenApi3_1Element)) return;
const operationJSONPointer = ancestorLineageToJSONPointer([
...ancestors,
parent!,
operationElement,
]);
// skip visiting this Operation Object if it's already normalized
if (storage!.includes(operationJSONPointer)) {
return;
}
// @TODO([email protected]): can be replaced by Array.prototype.findLast in future
const parentPathItemElement = [...ancestors].reverse().find(predicates.isPathItemElement);
const isServersUndefined = typeof operationElement.servers === 'undefined';
const isServersArrayElement = predicates.isArrayElement(operationElement.servers);
const isServersEmpty = isServersArrayElement && operationElement.servers!.length === 0;
if (predicates.isPathItemElement(parentPathItemElement)) {
const pathItemServersContent = parentPathItemElement.servers?.content;
const pathItemServers = (pathItemServersContent ?? []) as ServerElement[];
if (isServersUndefined || !isServersArrayElement) {
// duplicate parent PathItem.servers into this Operation object
operationElement.servers = new OperationServersElement(pathItemServers);
} else if (isServersArrayElement && isServersEmpty) {
pathItemServers.forEach((server) => {
operationElement.servers!.push(server);
});
}
storage!.append(operationJSONPointer);
}
},
},
};
};
/* eslint-enable */
export default plugin;