apib2swagger 1.10.0 → 1.14.0

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 (118) hide show
  1. package/.github/workflows/codeql-analysis.yml +71 -0
  2. package/.github/workflows/docker-publish.yml +70 -0
  3. package/.github/workflows/nodejs.yml +26 -0
  4. package/.travis.yml +2 -3
  5. package/Dockerfile +5 -0
  6. package/LICENSE +0 -0
  7. package/README.md +31 -10
  8. package/bin/apib2swagger.js +44 -18
  9. package/index.js +82 -366
  10. package/package.json +6 -4
  11. package/src/escape_json_pointer.js +0 -0
  12. package/src/mson_to_json_schema.js +13 -8
  13. package/src/parameters.js +111 -0
  14. package/src/requests.js +344 -0
  15. package/src/responses.js +181 -0
  16. package/src/util.js +127 -0
  17. package/test/input/Attributes.md +0 -0
  18. package/test/input/Issue-#15.md +0 -0
  19. package/test/input/Issue-#22.md +0 -0
  20. package/test/input/Issue-#23.md +0 -0
  21. package/test/input/Issue-#26.md +0 -0
  22. package/test/input/Issue-#29.md +0 -0
  23. package/test/input/Issue-#33.md +0 -0
  24. package/test/input/Issue-#35.md +0 -0
  25. package/test/input/Issue-#36.md +0 -0
  26. package/test/input/Issue-#38.md +0 -0
  27. package/test/input/Issue-#49.md +24 -0
  28. package/test/input/OpenAPI3_attributes.md +36 -0
  29. package/test/input/OpenAPI3_headers.md +74 -0
  30. package/test/input/OpenAPI3_includes.md +22 -0
  31. package/test/input/OpenAPI3_requests.md +162 -0
  32. package/test/input/OpenAPI3_responses.md +177 -0
  33. package/test/input/Schema.md +0 -0
  34. package/test/input/apiblueprint_uber.md +0 -0
  35. package/test/input/apiblueprint_valid_simple.md +0 -0
  36. package/test/input/includes/resource-request-body-2.json +3 -0
  37. package/test/input/includes/resource-request-body.json +3 -0
  38. package/test/input/includes/resource-request-schema.json +9 -0
  39. package/test/input/includes/resource-response-body-2.json +3 -0
  40. package/test/input/includes/resource-response-body.json +3 -0
  41. package/test/input/includes/resource-response-schema.json +9 -0
  42. package/test/input/schema_without_body.md +17 -0
  43. package/test/meta_schema.json +0 -0
  44. package/test/openAPI_30_schema.json +1662 -0
  45. package/test/output/01. Simplest API.json +1 -0
  46. package/test/output/01. Simplest API.ref.json +1 -0
  47. package/test/output/02. Resource and Actions.json +2 -0
  48. package/test/output/02. Resource and Actions.ref.json +2 -0
  49. package/test/output/03. Named Resource and Actions.json +2 -0
  50. package/test/output/03. Named Resource and Actions.ref.json +2 -0
  51. package/test/output/04. Grouping Resources.json +2 -0
  52. package/test/output/04. Grouping Resources.ref.json +2 -0
  53. package/test/output/05. Responses.json +2 -0
  54. package/test/output/05. Responses.ref.json +2 -0
  55. package/test/output/06. Requests.json +2 -0
  56. package/test/output/06. Requests.ref.json +2 -0
  57. package/test/output/07. Parameters.json +3 -0
  58. package/test/output/07. Parameters.ref.json +3 -0
  59. package/test/output/08. Attributes.json +1 -0
  60. package/test/output/08. Attributes.ref.json +1 -0
  61. package/test/output/09. Advanced Attributes.json +3 -0
  62. package/test/output/09. Advanced Attributes.ref.json +3 -0
  63. package/test/output/10. Data Structures.json +3 -0
  64. package/test/output/10. Data Structures.ref.json +3 -0
  65. package/test/output/11. Resource Model.json +2 -0
  66. package/test/output/11. Resource Model.ref.json +2 -0
  67. package/test/output/12. Advanced Action.json +3 -0
  68. package/test/output/12. Advanced Action.ref.json +3 -0
  69. package/test/output/13. Named Endpoints.json +2 -0
  70. package/test/output/13. Named Endpoints.ref.json +2 -0
  71. package/test/output/Attributes.json +1 -0
  72. package/test/output/Attributes.ref.json +1 -0
  73. package/test/output/Gist Fox API + Auth.json +12 -0
  74. package/test/output/Gist Fox API + Auth.ref.json +12 -0
  75. package/test/output/Issue-#15.json +3 -0
  76. package/test/output/Issue-#15.ref.json +3 -0
  77. package/test/output/Issue-#22.json +0 -0
  78. package/test/output/Issue-#22.ref.json +0 -0
  79. package/test/output/Issue-#23.json +1 -0
  80. package/test/output/Issue-#23.ref.json +1 -0
  81. package/test/output/Issue-#26.json +2 -0
  82. package/test/output/Issue-#26.ref.json +2 -0
  83. package/test/output/Issue-#29.json +2 -0
  84. package/test/output/Issue-#29.ref.json +2 -0
  85. package/test/output/Issue-#33.json +9 -8
  86. package/test/output/Issue-#33.ref.json +9 -8
  87. package/test/output/Issue-#35.json +0 -0
  88. package/test/output/Issue-#35.ref.json +0 -0
  89. package/test/output/Issue-#36.json +1 -0
  90. package/test/output/Issue-#36.ref.json +1 -0
  91. package/test/output/Issue-#38.bearer.json +53 -52
  92. package/test/output/Issue-#38.json +1 -0
  93. package/test/output/Issue-#38.ref.json +1 -0
  94. package/test/output/Issue-#49.json +141 -0
  95. package/test/output/Issue-#49.ref.json +126 -0
  96. package/test/output/OpenAPI3_attributes.json +199 -0
  97. package/test/output/OpenAPI3_attributes.ref.json +173 -0
  98. package/test/output/OpenAPI3_headers.json +169 -0
  99. package/test/output/OpenAPI3_headers.ref.json +169 -0
  100. package/test/output/OpenAPI3_includes.json +66 -0
  101. package/test/output/OpenAPI3_includes.ref.json +66 -0
  102. package/test/output/OpenAPI3_requests.json +326 -0
  103. package/test/output/OpenAPI3_requests.ref.json +321 -0
  104. package/test/output/OpenAPI3_responses.json +255 -0
  105. package/test/output/OpenAPI3_responses.ref.json +245 -0
  106. package/test/output/Schema.json +3 -0
  107. package/test/output/Schema.ref.json +3 -0
  108. package/test/output/apiblueprint_uber.json +5 -0
  109. package/test/output/apiblueprint_uber.ref.json +5 -0
  110. package/test/output/apiblueprint_valid_simple.json +12 -0
  111. package/test/output/apiblueprint_valid_simple.ref.json +12 -0
  112. package/test/output/schema_without_body.json +52 -0
  113. package/test/output/schema_without_body.ref.json +52 -0
  114. package/test/swagger_20_schema.json +0 -0
  115. package/test/test.js +31 -15
  116. package/test/util.test.js +151 -0
  117. package/.npmignore +0 -35
  118. package/package-lock.json +0 -2320
@@ -0,0 +1,111 @@
1
+
2
+ const getParamType = (name, uriTemplate, options) => {
3
+ const defaultType = options.openApi3 ? 'query' : 'body'
4
+ if (!uriTemplate) return defaultType;
5
+ for (var i = 0; i < uriTemplate.expressions.length; i++) {
6
+ var exp = uriTemplate.expressions[i];
7
+ if (!exp.varspecs) continue;
8
+ for (var j = 0; j < exp.varspecs.length; j++) {
9
+ var spec = exp.varspecs[j];
10
+ if (spec.varname === name) {
11
+ return exp.operator.symbol === '?' ? 'query' : 'path';
12
+ }
13
+ }
14
+ }
15
+ return defaultType;
16
+ }
17
+
18
+ module.exports.processParameters = (parameters, uriTemplate, options) => {
19
+ const { openApi3 } = options
20
+ var PARAM_TYPES = {
21
+ 'string': 'string',
22
+ 'number': 'number',
23
+ 'integer': 'integer',
24
+ 'boolean': 'boolean', 'bool': 'boolean',
25
+ 'array': 'array',
26
+ 'file': 'file'
27
+ }
28
+ var params = [];
29
+ //console.log(parameters);
30
+ for (var l = 0; l < parameters.length; l++) {
31
+ var parameter = parameters[l];
32
+ //console.log(parameter);
33
+ // in = ["query", "header", "path", "formData", "body"]
34
+ var param = {
35
+ 'name': parameter.name,
36
+ 'in': getParamType(parameter.name, uriTemplate, options),
37
+ 'description': parameter.description
38
+ };
39
+ if (openApi3) {
40
+ if (parameter.required) {
41
+ // Only add required if the value is true. False is not an accepted value.
42
+ param.required = true
43
+
44
+ }
45
+ } else {
46
+ param.required = parameter.required
47
+ }
48
+ if (parameter.example) {
49
+ if (openApi3) {
50
+ param.example = parameter.example
51
+ } else {
52
+ param['x-example'] = parameter.example
53
+ }
54
+ }
55
+
56
+ var paramType = undefined;
57
+ if (PARAM_TYPES.hasOwnProperty(parameter.type)) {
58
+ paramType = PARAM_TYPES[parameter.type];
59
+ } else {
60
+ paramType = 'string';
61
+ }
62
+
63
+ var allowedValues = []
64
+ for (var j = 0; j < parameter.values.length; j++) {
65
+ allowedValues.push(parameter.values[j].value);
66
+ }
67
+
68
+ var parameterDefault = undefined;
69
+ if (parameter.default) {
70
+ if (paramType === 'number' || paramType === 'integer') {
71
+ parameterDefault = Number(parameter.default);
72
+ } else {
73
+ parameterDefault = parameter.default;
74
+ }
75
+ }
76
+
77
+ // Body parameters has all info in schema
78
+ if (param.in === 'body') {
79
+ param.schema = {
80
+ 'type': paramType
81
+ }
82
+ if (allowedValues.length > 0) {
83
+ param.schema.enum = allowedValues;
84
+ }
85
+ if (parameterDefault) {
86
+ param.schema.default = parameterDefault;
87
+ }
88
+ } else {
89
+ if (openApi3) {
90
+ param.schema = { type: paramType }
91
+ if (parameterDefault) {
92
+ param.schema.default = parameterDefault;
93
+ }
94
+ if (allowedValues.length > 0) {
95
+ param.schema.enum = allowedValues;
96
+ }
97
+ } else {
98
+ param.type = paramType;
99
+ if (parameterDefault) {
100
+ param.default = parameterDefault;
101
+ }
102
+ if (allowedValues.length > 0) {
103
+ param.enum = allowedValues;
104
+ }
105
+ }
106
+ }
107
+ params.push(param);
108
+ }
109
+ return params;
110
+ }
111
+
@@ -0,0 +1,344 @@
1
+ const { hasFileRef, getRefFromInclude, searchDataStructure, generateSchemaFromExample } = require('./util')
2
+ const isEqual = require('lodash.isequal')
3
+ const escapeJSONPointer = require('./escape_json_pointer')
4
+
5
+ const swaggerHeaders = function (options, headers) {
6
+ var params = [];
7
+ const skipParams = options.openApi3 ? ['content-type'] : ['content-type', 'authorization']; // handled in another way
8
+ for (let i = 0; i < headers.length; i++) {
9
+ const element = headers[i];
10
+ if (skipParams.includes(element.name.toLowerCase())) continue;
11
+ var param = {
12
+ 'name': element.name,
13
+ 'in': 'header',
14
+ 'description': `e.g. ${element.value}`,
15
+ 'required': false
16
+ };
17
+ if (options.openApi3) {
18
+ param.schema = { type: 'string' }
19
+ param.example = element.value
20
+ } else {
21
+ param['x-example'] = element.value
22
+ param.type = 'string' // TODO: string, number, boolean, integer, array
23
+ }
24
+ if (!params.find(p => isEqual(p, param)))
25
+ params.push(param);
26
+ }
27
+ return params;
28
+ };
29
+
30
+ // generate security and securityDefinitions from authorization headers
31
+ function swaggerSecurity(context, headers) {
32
+ var security = null;
33
+ headers.filter(function (header) {
34
+ return header.name.toLowerCase() === 'authorization';
35
+ }).forEach(function (header) {
36
+ if (header.value.match(/^Basic /)) {
37
+ if (!security) security = {};
38
+ security['basic'] = [];
39
+ context.swagger.securityDefinitions['basic'] = { type: 'basic' };
40
+ } else if (header.value.match(/^Bearer /)) {
41
+ if (!security) security = {};
42
+ if (context.options.bearerAsApikey) {
43
+ security['bearer'] = [];
44
+ context.swagger.securityDefinitions['bearer'] = {
45
+ type: 'apiKey', in: 'header', name: 'Authorization'
46
+ };
47
+ } else {
48
+ security['oauth2'] = [];
49
+ context.swagger.securityDefinitions['oauth2'] = {
50
+ type: 'oauth2', flow: 'accessCode',
51
+ authorizationUrl: '', tokenUrl: '', scopes: {}
52
+ };
53
+ }
54
+ }
55
+ });
56
+ return security;
57
+ }
58
+
59
+ const setSecurity = (context, request, operation) => {
60
+ if (context.options.openApi3){
61
+ return operation
62
+ }
63
+ var security = swaggerSecurity(context, request.headers);
64
+ if (security) {
65
+ if (!operation.security) {
66
+ operation.security = [security];
67
+ } else {
68
+ if (!operation.security.find(s => isEqual(s, security))){
69
+ operation.security.push(security);
70
+ }
71
+ }
72
+ }
73
+ return operation
74
+ }
75
+
76
+ const setSwaggerRequestSchema = (operation, schema) => {
77
+ if (!schema.length) {
78
+ return operation
79
+ }
80
+ if (schema.length === 1) {
81
+ operation.parameters.push({ name: 'body', in: 'body', schema: schema[0].scheme })
82
+ return operation
83
+ }
84
+ operation.parameters.push({
85
+ name: 'body',
86
+ in: 'body',
87
+ schema: {
88
+ anyOf: schema.map((s) => s.scheme)
89
+ }
90
+ });
91
+ return operation
92
+ }
93
+
94
+ const mergeHeaders = (headers, parameters, options) => {
95
+ const existingHeaders = parameters
96
+ .filter(param => param.in === 'header')
97
+ .map(param => param.name.toLowerCase());
98
+
99
+ let nonDuplicateHeaders
100
+ if (options.openApi3) {
101
+ // We are comparing the whole object instead of just the name to allow duplicate names.
102
+ // In doing so, we allow multiple types of Authorization headers to be specified.
103
+ nonDuplicateHeaders = headers.filter(header =>
104
+ !parameters.find(p => isEqual(p, header))
105
+ )
106
+ } else {
107
+ nonDuplicateHeaders = headers.filter(header =>
108
+ !existingHeaders.includes(header.name.toLowerCase())
109
+ );
110
+ }
111
+ return parameters.concat(nonDuplicateHeaders);
112
+ }
113
+
114
+ const processRequestSchema = (request, options) => {
115
+ let schema
116
+ const { openApi3 } = options
117
+ if (openApi3 && hasFileRef(request.schema)) {
118
+ return getRefFromInclude(request.schema)
119
+ }
120
+
121
+ // referencing Model's Schema is also here (no need to reference definitions)
122
+ try {
123
+ schema = JSON.parse(request.schema);
124
+ delete schema['$schema'];
125
+ } catch (e){
126
+ // If we can't parse the request schema we have nothing left to do here.
127
+ return
128
+ }
129
+ try {
130
+ // if we have example values in the body then insert them into the json schema
131
+ if (!openApi3) {
132
+ var body = JSON.parse(request.body);
133
+
134
+ if (schema['type'] === 'object') {
135
+ schema.example = body;
136
+ } else if (schema['type'] === 'array') {
137
+ schema.items.example = body;
138
+ }
139
+ }
140
+ // Catch any error from parsing the request body. However, if there is an error
141
+ // (ex. no request body given), we still want to keep the schema.
142
+ } catch (e) {}
143
+ return schema
144
+ }
145
+
146
+ const processRequestAttributes = (request, options, contentType) => {
147
+ const schema = []
148
+ const scheme = searchDataStructure(request.content, options); // Attributes 4
149
+ if (scheme) schema.push({ scheme, contentType });
150
+ if (request.reference) {
151
+ const componentsPath = options.openApi3 ? '#/components/schemas' : '#/definitions/'
152
+ schema.push({
153
+ scheme: {
154
+ '$ref': componentsPath + escapeJSONPointer(request.reference.id + 'Model')
155
+ },
156
+ contentType
157
+ });
158
+ }
159
+ return schema
160
+ }
161
+ const buildBodyExamples = (requestBody, bodyExamples, contentType) => {
162
+ let body
163
+ if (typeof requestBody === 'string') {
164
+ if (hasFileRef(requestBody)){
165
+ body = getRefFromInclude(requestBody)
166
+ } else {
167
+ try {
168
+ body = JSON.parse(requestBody);
169
+ } catch (e) {
170
+ if (contentType === 'text/plain') {
171
+ body = requestBody
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ if (typeof body !== 'object' && typeof body !== 'array' && typeof body !== 'string') {
178
+ return bodyExamples
179
+ }
180
+
181
+ if (!bodyExamples[contentType]) {
182
+ bodyExamples[contentType] = { example: body }
183
+ } else if (bodyExamples[contentType].examples) {
184
+ const count = Object.keys(bodyExamples[contentType].examples).length + 1
185
+ const name = 'example' + count
186
+ bodyExamples[contentType].examples[name] = body.$ref ? body : { value: body }
187
+ } else {
188
+ const existingExample = bodyExamples[contentType].example
189
+ bodyExamples[contentType].examples = {
190
+ example1: existingExample.$ref ? existingExample : { value: existingExample },
191
+ example2: body.$ref ? body : { value: body }
192
+ }
193
+ delete bodyExamples[contentType].example
194
+ }
195
+ return bodyExamples
196
+ }
197
+
198
+ const getOpenApiRequestSchema = (request, schema, contentType, options) => {
199
+ /*
200
+ With OpenAPI3 we can make full use of Attributes because examples no longer live
201
+ on the schema object and therefore, we don't lose our examples when we use
202
+ Attributes as the schema definition.
203
+ */
204
+ const contentTypeScheme = {}
205
+ if (!options.preferReference) {
206
+ if (request.schema) { // Schema section in Request section
207
+ contentTypeScheme[contentType] = processRequestSchema(request, options)
208
+ }
209
+ if (contentTypeScheme[contentType]) {
210
+ schema.push({ scheme: contentTypeScheme[contentType], contentType });
211
+ } else {
212
+ const attributes = processRequestAttributes(request, options, contentType)
213
+ schema.push(...attributes)
214
+ }
215
+ } else {
216
+ const attributes = processRequestAttributes(request, options, contentType)
217
+ if (attributes.length) {
218
+ schema.push(...attributes)
219
+ } else {
220
+ contentTypeScheme[contentType] = processRequestSchema(request, options)
221
+ if (contentTypeScheme[contentType]) {
222
+ schema.push({ scheme: contentTypeScheme[contentType], contentType });
223
+ }
224
+ }
225
+ }
226
+ // If there are no schema but we have a body example, try to generate a schema from it.
227
+ // We stop at 1 auto-generated schema.
228
+ if (request.body && schema.length === 0) {
229
+ contentTypeScheme[contentType] = generateSchemaFromExample(request.headers, request.body, options);
230
+ if (contentTypeScheme[contentType]){
231
+ schema.push({ scheme: contentTypeScheme[contentType], contentType });
232
+ }
233
+ }
234
+ return schema
235
+ }
236
+
237
+ const setOpenApiRequestSchema = (operation, schema) => {
238
+ if (!schema.length){
239
+ return operation
240
+ }
241
+
242
+ // Make sure all content types exist to make setting the schemes easier.
243
+ schema.forEach(s => {
244
+ if (!operation.requestBody.content[s.contentType]) {
245
+ operation.requestBody.content[s.contentType] = {}
246
+ }
247
+ })
248
+
249
+ if (schema.length === 1) {
250
+ if (!operation.requestBody.content[schema[0].contentType]){
251
+ operation.requestBody.content[schema[0].contentType] = schema[0].scheme
252
+ } else {
253
+ operation.requestBody.content[schema[0].contentType].schema = schema[0].scheme
254
+ }
255
+ return operation
256
+ }
257
+
258
+ schema.forEach(s => {
259
+ if (!operation.requestBody.content[s.contentType].schema){
260
+ operation.requestBody.content[s.contentType].schema = s.scheme
261
+ return
262
+ }
263
+ // If a schema exist for this content type,
264
+ // we need to use oneOf because we have an additional schema to add.
265
+ const { oneOf } = operation.requestBody.content[s.contentType].schema
266
+ if (oneOf){
267
+ // If oneOf already exists we can just add the new schema if its not a duplicate.
268
+ if (!oneOf.find(sc => isEqual(sc, s.scheme))){
269
+ operation.requestBody.content[s.contentType].schema.oneOf.push(s.scheme)
270
+ }
271
+ return
272
+ }
273
+ const existing = operation.requestBody.content[s.contentType].schema
274
+ if (isEqual(existing, s.scheme)){
275
+ return
276
+ }
277
+ // If oneOf does not exist and its not a duplicate, we need to create it,
278
+ // making sure to include the existing schema.
279
+ operation.requestBody.content[s.contentType].schema = {
280
+ oneOf: [
281
+ operation.requestBody.content[s.contentType].schema, // existing
282
+ s.scheme // additional
283
+ ]
284
+ }
285
+ })
286
+ return operation
287
+ }
288
+
289
+ module.exports.processRequests = (operation, action, context) => {
290
+ const { options } = context
291
+ const { openApi3 } = options
292
+ var schema = [],
293
+ scheme = searchDataStructure(action.content, options); // Attributes 3
294
+ if (scheme) schema.push({ contentType: 'application/json', scheme });
295
+
296
+ let bodyExamples = {}
297
+
298
+ for (var j = 0; j < action.examples.length; j++) {
299
+ var example = action.examples[j];
300
+ for (var l = 0; l < example.requests.length; l++) {
301
+ var request = example.requests[l];
302
+
303
+ operation = setSecurity(context, request, operation)
304
+
305
+ var headers = swaggerHeaders(options, request.headers);
306
+ if (headers) {
307
+ operation.parameters = mergeHeaders(headers, operation.parameters, options)
308
+ }
309
+
310
+ const contentTypeHeader = request.headers.find((h) => h.name === 'Content-Type')
311
+ const contentType = contentTypeHeader ? contentTypeHeader.value : 'application/json'
312
+
313
+ if (request.body && openApi3) {
314
+ bodyExamples = buildBodyExamples(request.body, bodyExamples, contentType)
315
+ }
316
+
317
+ if (!openApi3) {
318
+ // Build schemas and examples for swagger 2.0
319
+ if (request.schema) {
320
+ scheme = processRequestSchema(request, options)
321
+ if (scheme) schema.push({ scheme, contentType });
322
+ } else {
323
+ const attributes = processRequestAttributes(request, options, contentType)
324
+ schema.push(...attributes)
325
+ // fall back to body
326
+ if (request.body && schema.length === 0) {
327
+ scheme = generateSchemaFromExample(request.headers, request.body, options);
328
+ if (scheme) schema.push({ scheme, contentType });
329
+ }
330
+ }
331
+ } else {
332
+ schema = getOpenApiRequestSchema(request, schema, contentType, options)
333
+ }
334
+ }
335
+ }
336
+ if (openApi3 && (schema.length || Object.keys(bodyExamples).length)){
337
+ operation.requestBody = { content: {} }
338
+ if (Object.keys(bodyExamples).length) {
339
+ operation.requestBody.content = bodyExamples
340
+ }
341
+ }
342
+
343
+ return openApi3 ? setOpenApiRequestSchema(operation, schema) : setSwaggerRequestSchema(operation, schema)
344
+ }
@@ -0,0 +1,181 @@
1
+ const { fixArraySchema, hasFileRef, getRefFromInclude, searchDataStructure } = require('./util')
2
+ const escapeJSONPointer = require('./escape_json_pointer')
3
+ const isEqual = require('lodash.isequal')
4
+ const http = require('http')
5
+
6
+ const parseResponseSchema = (schema, options) => {
7
+ if (!schema) return
8
+ if (options.openApi3 && hasFileRef(schema)){
9
+ return getRefFromInclude(schema)
10
+ }
11
+ try {
12
+ const result = JSON.parse(schema);
13
+ delete result['$schema'];
14
+ fixArraySchema(result); // work around for Swagger UI / Editor
15
+ return result
16
+ } catch (e) { }
17
+ }
18
+
19
+ const parseResponseBody = (body, header, options) => {
20
+ if (options.openApi3 && hasFileRef(body)){
21
+ return getRefFromInclude(body)
22
+ }
23
+ if (!header.value.match(/application\/.*json/)) {
24
+ return body
25
+ }
26
+ try {
27
+ return JSON.parse(body);
28
+ } catch (e) { }
29
+ }
30
+
31
+ const getResponseSchema = (response, options) => {
32
+ const componentsPath = options.openApi3 ? '#/components/schemas' : '#/definitions/'
33
+ if (options.preferReference) { // MSON then schema
34
+ const inputSchema = searchDataStructure(response.content, options); // Attributes in response
35
+ if (inputSchema) {
36
+ return inputSchema
37
+ } else if (response.reference) {
38
+ return {
39
+ '$ref': componentsPath + escapeJSONPointer(response.reference.id + 'Model')
40
+ };
41
+ } else if (response.schema) {
42
+ return parseResponseSchema(response.schema, options)
43
+ }
44
+ } else { // schema then MSON
45
+ if (response.schema) {
46
+ const schema = parseResponseSchema(response.schema, options)
47
+ if (schema) {
48
+ return schema
49
+ }
50
+ }
51
+
52
+ const inputSchema = searchDataStructure(response.content, options); // Attributes in response
53
+ if (inputSchema) return inputSchema;
54
+ if (response.reference) {
55
+ return {
56
+ '$ref': componentsPath + escapeJSONPointer(response.reference.id + 'Model')
57
+ };
58
+ }
59
+ }
60
+ }
61
+
62
+ const setResponseSchema = (responses, response, schema, options) => {
63
+ if (!options.openApi3){
64
+ responses[response.name].schema = schema
65
+ return responses
66
+ }
67
+ // In openAPI 3 the schema lives under the content type
68
+ const contentTypeHeader = response.headers.find((h) => h.name.toLowerCase() === 'content-type')
69
+ if (!contentTypeHeader.value) {
70
+ return responses
71
+ }
72
+ if (!responses[response.name].content[contentTypeHeader.value]){
73
+ responses[response.name].content[contentTypeHeader.value] = {}
74
+ }
75
+
76
+ if (!responses[response.name].content[contentTypeHeader.value].schema){
77
+ responses[response.name].content[contentTypeHeader.value].schema = schema
78
+ return responses
79
+ }
80
+
81
+ // If a schema already exists, we need to use oneOf for additional unique schemas.
82
+ let existingSchema = { ...responses[response.name].content[contentTypeHeader.value].schema }
83
+ // It is possible that the given schema is a duplicate. If that's the case, we don't add it.
84
+ if (isEqual(existingSchema, schema)){
85
+ return responses
86
+ }
87
+
88
+ if (existingSchema.oneOf){
89
+ if (!existingSchema.oneOf.find((s) => isEqual(s, schema))){
90
+ responses[response.name].content[contentTypeHeader.value].schema.oneOf.push(schema)
91
+ }
92
+ } else {
93
+ responses[response.name].content[contentTypeHeader.value].schema = { oneOf: [existingSchema, schema] }
94
+ }
95
+ return responses
96
+ }
97
+
98
+ const setResponseExample = (responses, response, header, body, options) => {
99
+ if (!options.openApi3) {
100
+ // Sample path to Swagger 2.0 example:
101
+ // responses -> 200 -> examples -> application/json -> { }
102
+ responses[response.name].examples[header.value] = body;
103
+ return responses
104
+ }
105
+ if (!body) {
106
+ return responses
107
+ }
108
+
109
+ // Sample path to OpenAPI 3.0 examples:
110
+ // responses -> 200 -> content -> application/json -> example -> { }
111
+ // responses -> 200 -> content -> application/json -> examples -> example1 -> { }
112
+ const existingContainer = responses[response.name].content[header.value]
113
+ if (!existingContainer){
114
+ responses[response.name].content[header.value] = { example: body };
115
+ } else if (existingContainer.examples) {
116
+ const count = Object.keys(existingContainer.examples).length + 1
117
+ const exampleName = 'example' + count
118
+ responses[response.name].content[header.value].examples[exampleName] = body.$ref ? body : { value: body };
119
+ } else if (existingContainer.example) {
120
+ const existingExample = existingContainer.example
121
+ responses[response.name].content[header.value].examples = {
122
+ example1: existingExample.$ref ? existingExample : { value: existingExample },
123
+ example2: body.$ref ? body : { value: body }
124
+ }
125
+ delete responses[response.name].content[header.value].example
126
+ } else if (!existingContainer.example) {
127
+ responses[response.name].content[header.value].example = body
128
+ }
129
+ return responses
130
+ }
131
+
132
+ module.exports.processResponses = (examples, options) => {
133
+ let responses = {};
134
+ const { openApi3 } = options;
135
+ for (var l = 0; l < examples.length; l++) {
136
+ var example = examples[l];
137
+ for (var m = 0; m < example.responses.length; m++) {
138
+ var response = example.responses[m];
139
+
140
+ if (!responses[response.name]) {
141
+ responses[response.name] = { description: {}, headers: {} }
142
+ }
143
+
144
+ responses[response.name].description = response.description || http.STATUS_CODES[response.name];
145
+ if (openApi3) {
146
+ // Does not overwrite and allows multiple examples.
147
+ if (!responses[response.name].content){
148
+ responses[response.name].content = {};
149
+ }
150
+ } else {
151
+ // Overwrites every time
152
+ responses[response.name].examples = {}
153
+ }
154
+
155
+ const schema = getResponseSchema(response, options)
156
+
157
+ if (schema){
158
+ responses = setResponseSchema(responses, response, schema, options)
159
+ }
160
+
161
+ for (var n = 0; n < response.headers.length; n++) {
162
+ var header = response.headers[n];
163
+ if (header.name.toLowerCase() === 'content-type') {
164
+ let body = parseResponseBody(response.body, header, options)
165
+ if (body || body === '') {
166
+ responses = setResponseExample(responses, response, header, body, options)
167
+ }
168
+ } else if (header.name.toLowerCase() !== 'authorization') {
169
+ if (openApi3) {
170
+ responses[response.name].headers[header.name] = {
171
+ schema: { 'type': 'string' }
172
+ }
173
+ } else {
174
+ responses[response.name].headers[header.name] = { 'type': 'string' }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ return responses;
181
+ }