apib2swagger 1.12.0 → 1.13.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 +1 -1
  3. package/.github/workflows/nodejs.yml +1 -1
  4. package/.travis.yml +2 -1
  5. package/Dockerfile +1 -1
  6. package/LICENSE +0 -0
  7. package/README.md +29 -3
  8. package/bin/apib2swagger.js +43 -17
  9. package/index.js +81 -366
  10. package/package.json +5 -3
  11. package/src/escape_json_pointer.js +0 -0
  12. package/src/mson_to_json_schema.js +12 -10
  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 +0 -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 +0 -0
  46. package/test/output/01. Simplest API.ref.json +0 -0
  47. package/test/output/02. Resource and Actions.json +0 -0
  48. package/test/output/02. Resource and Actions.ref.json +0 -0
  49. package/test/output/03. Named Resource and Actions.json +0 -0
  50. package/test/output/03. Named Resource and Actions.ref.json +0 -0
  51. package/test/output/04. Grouping Resources.json +0 -0
  52. package/test/output/04. Grouping Resources.ref.json +0 -0
  53. package/test/output/05. Responses.json +0 -0
  54. package/test/output/05. Responses.ref.json +0 -0
  55. package/test/output/06. Requests.json +0 -0
  56. package/test/output/06. Requests.ref.json +0 -0
  57. package/test/output/07. Parameters.json +0 -0
  58. package/test/output/07. Parameters.ref.json +0 -0
  59. package/test/output/08. Attributes.json +0 -0
  60. package/test/output/08. Attributes.ref.json +0 -0
  61. package/test/output/09. Advanced Attributes.json +0 -0
  62. package/test/output/09. Advanced Attributes.ref.json +0 -0
  63. package/test/output/10. Data Structures.json +0 -0
  64. package/test/output/10. Data Structures.ref.json +0 -0
  65. package/test/output/11. Resource Model.json +0 -0
  66. package/test/output/11. Resource Model.ref.json +0 -0
  67. package/test/output/12. Advanced Action.json +0 -0
  68. package/test/output/12. Advanced Action.ref.json +0 -0
  69. package/test/output/13. Named Endpoints.json +0 -0
  70. package/test/output/13. Named Endpoints.ref.json +0 -0
  71. package/test/output/Attributes.json +0 -0
  72. package/test/output/Attributes.ref.json +0 -0
  73. package/test/output/Gist Fox API + Auth.json +0 -0
  74. package/test/output/Gist Fox API + Auth.ref.json +0 -0
  75. package/test/output/Issue-#15.json +0 -0
  76. package/test/output/Issue-#15.ref.json +0 -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 +0 -0
  80. package/test/output/Issue-#23.ref.json +0 -0
  81. package/test/output/Issue-#26.json +0 -0
  82. package/test/output/Issue-#26.ref.json +0 -0
  83. package/test/output/Issue-#29.json +0 -0
  84. package/test/output/Issue-#29.ref.json +0 -0
  85. package/test/output/Issue-#33.json +0 -0
  86. package/test/output/Issue-#33.ref.json +0 -0
  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 +0 -0
  90. package/test/output/Issue-#36.ref.json +0 -0
  91. package/test/output/Issue-#38.bearer.json +0 -0
  92. package/test/output/Issue-#38.json +0 -0
  93. package/test/output/Issue-#38.ref.json +0 -0
  94. package/test/output/Issue-#49.json +0 -0
  95. package/test/output/Issue-#49.ref.json +0 -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 +0 -0
  107. package/test/output/Schema.ref.json +0 -0
  108. package/test/output/apiblueprint_uber.json +0 -0
  109. package/test/output/apiblueprint_uber.ref.json +0 -0
  110. package/test/output/apiblueprint_valid_simple.json +0 -0
  111. package/test/output/apiblueprint_valid_simple.ref.json +0 -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 +24 -9
  116. package/test/util.test.js +151 -0
  117. package/.npmignore +0 -35
  118. package/package-lock.json +0 -2021
package/index.js CHANGED
@@ -1,36 +1,55 @@
1
1
  var url = require('url'),
2
- http = require('http'),
3
2
  drafter = require('drafter.js'),
4
3
  UriTemplate = require('uritemplate'),
5
- GenerateSchema = require('generate-schema');
4
+ jsonSchemaFromMSON = require('./src/mson_to_json_schema');
6
5
 
7
- var jsonSchemaFromMSON = require('./src/mson_to_json_schema'),
8
- escapeJSONPointer = require('./src/escape_json_pointer');
6
+ const { searchDataStructure, generateSchemaFromExample } = require('./src/util')
7
+ const { processRequests } = require('./src/requests')
8
+ const { processResponses } = require('./src/responses')
9
+ const { processParameters } = require('./src/parameters')
9
10
 
10
11
  var apib2swagger = module.exports.convertParsed = function (apib, options) {
11
- //console.log(JSON.stringify(apib, null, 4));
12
- var swagger = {};
13
- swagger.swagger = '2.0';
14
- swagger.info = {
15
- 'title': apib.name,
16
- 'version': '',
12
+ const { openApi3, infoTitle } = options
13
+ var output = {};
14
+ if (openApi3) {
15
+ output.openapi = '3.0.3';
16
+ } else {
17
+ output.swagger = '2.0';
18
+ }
19
+ output.info = {
20
+ 'title': infoTitle || apib.name,
21
+ 'version': openApi3 ? '1.0.0' : '',
17
22
  'description': apib.description
18
23
  }
19
24
  apib.metadata.forEach(function (meta) {
20
- //console.log(meta);
21
25
  if (meta.name.toLowerCase() === 'host') {
22
- var urlParts = url.parse(meta.value);
23
- swagger.host = urlParts.host;
24
- swagger.basePath = urlParts.pathname;
25
- swagger.schemes = [urlParts.protocol.replace(':', '')];
26
- } else if (meta.name.toLowerCase() === 'version') {
27
- swagger.info.version = meta.value;
26
+ if (openApi3) {
27
+ output.servers = [
28
+ {
29
+ url: meta.value
30
+ }
31
+ ];
32
+ } else {
33
+ var urlParts = url.parse(meta.value);
34
+ output.host = urlParts.host;
35
+ output.basePath = urlParts.pathname;
36
+ output.schemes = [urlParts.protocol.replace(':', '')];
37
+ }
38
+ }
39
+ if (meta.name.toLowerCase() === 'version' && meta.value) {
40
+ output.info.version = meta.value
28
41
  }
29
42
  });
30
- swagger.paths = {};
31
- swagger.definitions = {};
32
- swagger.securityDefinitions = {};
33
- var converterContext = { swagger: swagger, options: options };
43
+ output.paths = {};
44
+
45
+ if (openApi3) {
46
+ output.components = { schemas: {} }
47
+ } else {
48
+ output.definitions = {}
49
+ output.securityDefinitions = {};
50
+ }
51
+
52
+ var converterContext = { swagger: output, options: options };
34
53
  var tags = {};
35
54
  apib.content.filter(function (content) {
36
55
  return content.element === 'category';
@@ -42,21 +61,32 @@ var apib2swagger = module.exports.convertParsed = function (apib, options) {
42
61
  category.content.forEach(function (content) {
43
62
  if (content.element === 'resource') {
44
63
  // (name, description) in Resource section are discarded
45
- swaggerDefinitions(swagger.definitions, content);
64
+ const definitions = swaggerDefinitions(content, options);
65
+ if (openApi3) {
66
+ output.components.schemas = { ...output.components.schemas, ...definitions }
67
+ } else {
68
+ output.definitions = { ...output.definitions, ...definitions }
69
+ }
46
70
  swaggerPaths(converterContext, groupName, content);
47
71
  } else if (content.element === 'copy') {
48
72
  // group description here
49
73
  tags[groupName].description = content.content;
50
74
  } else if (content.element === 'dataStructure') {
51
- swagger.definitions[content.content[0].meta.id] = jsonSchemaFromMSON(content);
75
+ const { id } = content.content[0].meta
76
+ const schema = jsonSchemaFromMSON(content, options);
77
+ if (openApi3) {
78
+ output.components.schemas[id] = schema
79
+ } else {
80
+ output.definitions[id] = schema
81
+ }
52
82
  }
53
83
  });
54
84
  });
55
- swagger.tags = [];
85
+ output.tags = [];
56
86
  for (var key in tags) {
57
- swagger.tags.push(tags[key]);
87
+ output.tags.push(tags[key]);
58
88
  }
59
- return swagger;
89
+ return output;
60
90
  }
61
91
 
62
92
  function swaggerPathName(uriTemplate) {
@@ -73,29 +103,43 @@ function swaggerPathName(uriTemplate) {
73
103
  return decodeURIComponent(uriTemplate.expand(params));
74
104
  }
75
105
 
76
- var swaggerDefinitions = function (definitions, resource) {
106
+ var swaggerDefinitions = function (resource, options) {
77
107
  var scheme;
108
+ const result = {}
109
+ const { openApi3 } = options
78
110
  if (resource.name) {
79
- scheme = searchDataStructure(resource.content); // Attributes 1
80
- definitions[resource.name] = scheme ? scheme : {};
111
+ scheme = searchDataStructure(resource.content, options); // Attributes 1
112
+ if (openApi3) {
113
+ if (scheme) {
114
+ result[resource.name] = scheme
115
+ }
116
+ } else {
117
+ result[resource.name] = scheme ? scheme : {};
118
+ }
81
119
  }
82
120
  const model = resource.model;
83
121
  if (model.content && model.name) {
84
- scheme = searchDataStructure(model.content); // Attribute 2
122
+ scheme = searchDataStructure(model.content, options); // Attribute 2
85
123
  // fall back to body
86
124
  if (!scheme && model.content.length > 0) {
87
- scheme = generateSchemaFromExample(model.headers, model.content[0].content);
125
+ scheme = generateSchemaFromExample(model.headers, model.content[0].content, options);
126
+ }
127
+ if (openApi3) {
128
+ if (scheme) {
129
+ result[model.name + 'Model'] = scheme
130
+ }
131
+ } else {
132
+ result[model.name + 'Model'] = scheme ? scheme : {};
88
133
  }
89
- definitions[model.name + 'Model'] = scheme ? scheme : {};
90
134
  }
135
+ return result
91
136
  };
92
137
 
93
138
  var swaggerPaths = function (context, tag, resource) {
94
139
  var paths = context.swagger.paths;
95
140
  var uriTemplate = UriTemplate.parse(resource.uriTemplate),
96
141
  pathName = swaggerPathName(uriTemplate);
97
- //path.parameters = swaggerParameters(resource.parameters, uriTemplate);
98
- var pathParams = swaggerParameters(resource.parameters, uriTemplate); // for swagger ui
142
+ var pathParams = processParameters(resource.parameters, uriTemplate, context.options); // for swagger ui
99
143
  for (var k = 0; k < resource.actions.length; k++) {
100
144
  var action = resource.actions[k];
101
145
  if (!action.attributes.uriTemplate) {
@@ -112,12 +156,12 @@ var swaggerPaths = function (context, tag, resource) {
112
156
 
113
157
  var swaggerOperation = function (context, pathParams, uriTemplate, action, tag) {
114
158
  var operation = {
115
- 'responses': swaggerResponses(action.examples, context.options),
159
+ 'responses': processResponses(action.examples, context.options),
116
160
  'summary': action.name,
117
161
  'operationId': action.name,
118
162
  'description': action.description,
119
163
  'tags': tag ? [tag] : [],
120
- 'parameters': pathParams.concat(swaggerParameters(action.parameters, uriTemplate))
164
+ 'parameters': pathParams.concat(processParameters(action.parameters, uriTemplate, context.options))
121
165
  };
122
166
  var produces = {}, producesExist = false;
123
167
  for (var key in operation.responses) {
@@ -133,332 +177,7 @@ var swaggerOperation = function (context, pathParams, uriTemplate, action, tag)
133
177
  operation.produces.push(mime);
134
178
  }
135
179
  }
136
- // body parameter (schema)
137
- var schema = [],
138
- scheme = searchDataStructure(action.content); // Attributes 3
139
- if (scheme) schema.push(scheme);
140
- for (var j = 0; j < action.examples.length; j++) {
141
- var example = action.examples[j];
142
- for (var l = 0; l < example.requests.length; l++) {
143
- var request = example.requests[l];
144
-
145
- // TODO should apply to Model Section?
146
- var security = swaggerSecurity(context, request.headers);
147
- if (security) {
148
- if (!operation.security) {
149
- operation.security = [security];
150
- } else {
151
- // TODO remove duplications
152
- operation.security.push(security);
153
- }
154
- }
155
-
156
- var headers = swaggerHeaders(context, request.headers);
157
- if (headers) {
158
- const existingHeaders = operation.parameters
159
- .filter(param => param.in === 'header')
160
- .map(param => param.name.toLowerCase());
161
- const nonDuplicateHeaders = headers.filter(header => !existingHeaders.includes(header.name.toLowerCase()));
162
- operation.parameters = operation.parameters.concat(nonDuplicateHeaders);
163
- }
164
-
165
- if (request.schema) { // Schema section in Request section
166
- try {
167
- // referencing Model's Schema is also here (no need to referencing defenitions)
168
- scheme = JSON.parse(request.schema);
169
- delete scheme['$schema'];
170
-
171
- // if we have example values in the body then insert them into the json schema
172
- var body = JSON.parse(request.body);
173
- if (scheme['type'] === 'object') {
174
- scheme.example = body;
175
- } else if (scheme['type'] === 'array') {
176
- scheme.items.example = body;
177
- }
178
- if (scheme) schema.push(scheme);
179
- } catch (e) { }
180
- } else {
181
- scheme = searchDataStructure(request.content); // Attributes 4
182
- if (scheme) schema.push(scheme);
183
- if (request.reference) {
184
- schema.push({ '$ref': '#/definitions/' + escapeJSONPointer(request.reference.id + 'Model') });
185
- }
186
- // fall back to body
187
- if (request.body && (schema == null || schema.length == 0)) {
188
- scheme = generateSchemaFromExample(request.headers, request.body);
189
- if (scheme) schema.push(scheme);
190
- }
191
- }
192
- }
193
- }
194
- if (schema.length == 1) {
195
- operation.parameters.push({ name: 'body', in: 'body', schema: schema[0] });
196
- } else if (schema.length > 1) {
197
- operation.parameters.push({ name: 'body', in: 'body', schema: { anyOf: schema } });
198
- }
199
- return operation;
200
- }
201
-
202
- var swaggerHeaders = function (context, headers) {
203
- var params = [];
204
- const skipParams = ['content-type', 'authorization']; // handled in another way
205
- for (let i = 0; i < headers.length; i++) {
206
- const element = headers[i];
207
- if (skipParams.includes(element.name.toLowerCase())) continue;
208
- var param = {
209
- 'name': element.name,
210
- 'in': 'header',
211
- 'description': `e.g. ${element.value}`,
212
- 'required': false,
213
- 'x-example': element.value,
214
- 'type': 'string' // TODO: string, number, boolean, integer, array
215
- };
216
- params.push(param);
217
- }
218
- return params;
219
- };
220
-
221
- // generate security and securityDefinitions from authorization headers
222
- function swaggerSecurity(context, headers) {
223
- var security = null;
224
- headers.filter(function (header) {
225
- return header.name.toLowerCase() === 'authorization';
226
- }).forEach(function (header) {
227
- if (header.value.match(/^Basic /)) {
228
- if (!security) security = {};
229
- security['basic'] = [];
230
- context.swagger.securityDefinitions['basic'] = { type: 'basic' };
231
- } else if (header.value.match(/^Bearer /)) {
232
- if (!security) security = {};
233
- if (context.options.bearerAsApikey) {
234
- security['bearer'] = [];
235
- context.swagger.securityDefinitions['bearer'] = {
236
- type: 'apiKey', in: 'header', name: 'Authorization'
237
- };
238
- } else {
239
- security['oauth2'] = [];
240
- context.swagger.securityDefinitions['oauth2'] = {
241
- type: 'oauth2', flow: 'accessCode',
242
- authorizationUrl: '', tokenUrl: '', scopes: {}
243
- };
244
- }
245
- }
246
- });
247
- return security;
248
- }
249
-
250
- function swaggerParameters(parameters, uriTemplate) {
251
- var PARAM_TYPES = {
252
- 'string': 'string',
253
- 'number': 'number',
254
- 'integer': 'integer',
255
- 'boolean': 'boolean', 'bool': 'boolean',
256
- 'array': 'array',
257
- 'file': 'file'
258
- }
259
- var params = [];
260
- //console.log(parameters);
261
- for (var l = 0; l < parameters.length; l++) {
262
- var parameter = parameters[l];
263
- //console.log(parameter);
264
- // in = ["query", "header", "path", "formData", "body"]
265
- var param = {
266
- 'name': parameter.name,
267
- 'in': getParamType(parameter.name, uriTemplate),
268
- 'description': parameter.description,
269
- 'required': parameter.required
270
- };
271
-
272
- if (parameter.example) {
273
- param['x-example'] = parameter.example
274
- }
275
-
276
- var paramType = undefined;
277
- if (PARAM_TYPES.hasOwnProperty(parameter.type)) {
278
- paramType = PARAM_TYPES[parameter.type];
279
- } else {
280
- paramType = 'string';
281
- }
282
-
283
- var allowedValues = []
284
- for (var j = 0; j < parameter.values.length; j++) {
285
- allowedValues.push(parameter.values[j].value);
286
- }
287
-
288
- var parameterDefault = undefined;
289
- if (parameter.default) {
290
- if (paramType === 'number' || paramType === 'integer') {
291
- parameterDefault = Number(parameter.default);
292
- } else {
293
- parameterDefault = parameter.default;
294
- }
295
- }
296
-
297
- // Body parameters has all info in schema
298
- if (param.in === 'body') {
299
- param.schema = {
300
- 'type': paramType
301
- }
302
- if (allowedValues.length > 0) {
303
- param.schema.enum = allowedValues;
304
- }
305
- if (parameterDefault) {
306
- param.schema.default = parameterDefault;
307
- }
308
- }
309
- else {
310
- param.type = paramType;
311
- if (parameterDefault) {
312
- param.default = parameterDefault;
313
- }
314
- if (allowedValues.length > 0) {
315
- param.enum = allowedValues;
316
- }
317
- }
318
- params.push(param);
319
- }
320
- return params;
321
- }
322
-
323
- var searchDataStructure = function (contents) {
324
- for (var i = 0; i < contents.length; i++) {
325
- var content = contents[i];
326
- if (content.element !== "dataStructure") continue;
327
- return jsonSchemaFromMSON(content);
328
- }
329
- };
330
-
331
- function generateSchemaFromExample(headers, example) {
332
- if (!headers || !headers.some(header => (
333
- header.name === 'Content-Type' && header.value.match(/application\/.*json/)
334
- ))) {
335
- return null;
336
- }
337
- try {
338
- const body = JSON.parse(example);
339
- const scheme = GenerateSchema.json("", body);
340
- if (!scheme) {
341
- return null;
342
- }
343
- delete scheme.title;
344
- delete scheme.$schema;
345
- // if we have example values in the body then insert them into the json schema
346
- if (scheme['type'] === 'object') {
347
- scheme.example = body;
348
- } else if (scheme['type'] === 'array') {
349
- scheme.items.example = body;
350
- }
351
- return scheme;
352
- } catch (e) {
353
- return null;
354
- }
355
- }
356
-
357
- function getParamType(name, uriTemplate) {
358
- if (!uriTemplate) return 'body';
359
- for (var i = 0; i < uriTemplate.expressions.length; i++) {
360
- var exp = uriTemplate.expressions[i];
361
- if (!exp.varspecs) continue;
362
- for (var j = 0; j < exp.varspecs.length; j++) {
363
- var spec = exp.varspecs[j];
364
- if (spec.varname === name) {
365
- return exp.operator.symbol === '?' ? 'query' : 'path';
366
- }
367
- }
368
- }
369
- return 'body'; // TODO: decide 'header', 'formData', 'body'
370
- }
371
-
372
- function fixArraySchema(schema) {
373
- if (schema.type === 'array') {
374
- if (!schema.hasOwnProperty('items')) schema.items = {};
375
- } else if (schema.type === 'object') {
376
- for (var k in schema.properties) fixArraySchema(schema.properties[k]);
377
- }
378
- }
379
-
380
- function swaggerResponses(examples, options) {
381
- var responses = {};
382
- //console.log(examples);
383
- for (var l = 0; l < examples.length; l++) {
384
- var example = examples[l];
385
- //console.log(example);
386
- for (var m = 0; m < example.responses.length; m++) {
387
- var response = example.responses[m];
388
- //console.log(response);
389
- var swaggerResponse = {
390
- "description": response.description || http.STATUS_CODES[response.name],
391
- "headers": {},
392
- "examples": {}
393
- };
394
- if (options.preferReference) { // MSON then schema
395
- var schema = searchDataStructure(response.content); // Attributes in response
396
- if (schema) {
397
- swaggerResponse.schema = schema;
398
- } else if (response.reference) {
399
- swaggerResponse.schema = {
400
- '$ref': '#/definitions/' + escapeJSONPointer(response.reference.id + 'Model')
401
- };
402
- } else if (response.schema) {
403
- try {
404
- swaggerResponse.schema = JSON.parse(response.schema);
405
- delete swaggerResponse.schema['$schema'];
406
- fixArraySchema(swaggerResponse.schema); // work around for Swagger UI / Editor
407
- } catch (e) { }
408
- }
409
- } else { // schema then MSON
410
- if (response.schema) {
411
- try {
412
- swaggerResponse.schema = JSON.parse(response.schema);
413
- delete swaggerResponse.schema['$schema'];
414
- fixArraySchema(swaggerResponse.schema); // work around for Swagger UI / Editor
415
- } catch (e) { }
416
- }
417
- if (!swaggerResponse.schema) {
418
- var schema = searchDataStructure(response.content); // Attributes in response
419
- if (schema) swaggerResponse.schema = schema;
420
- }
421
- if (!swaggerResponse.schema && response.reference) {
422
- swaggerResponse.schema = {
423
- '$ref': '#/definitions/' + escapeJSONPointer(response.reference.id + 'Model')
424
- };
425
- }
426
- }
427
- if (!swaggerResponse.schema) {
428
- // fall back to body
429
- // if (response.body) {
430
- // schema = GenerateSchema.json("", JSON.parse(response.body));
431
- // if (schema) {
432
- // delete schema.title;
433
- // delete schema.$schema;
434
- // swaggerResponse.schema = schema;
435
- // }
436
- // }
437
-
438
- // use object
439
- // if (response.body) {
440
- // swaggerResponse.schema = {type: "object"};
441
- // }
442
- }
443
- for (var n = 0; n < response.headers.length; n++) {
444
- var header = response.headers[n];
445
- if (header.name.toLowerCase() === 'content-type') {
446
- if (header.value.match(/application\/.*json/)) {
447
- try {
448
- swaggerResponse.examples[header.value] = JSON.parse(response.body);
449
- //swaggerResponse.schema = {type: "object"};
450
- } catch (e) { }
451
- continue;
452
- }
453
- swaggerResponse.examples[header.value] = response.body;
454
- } else if (header.name.toLowerCase() !== 'authorization') {
455
- swaggerResponse.headers[header.name] = { 'type': 'string' }
456
- }
457
- }
458
- responses[response.name] = swaggerResponse;
459
- }
460
- }
461
- return responses;
180
+ return processRequests(operation, action, context)
462
181
  }
463
182
 
464
183
  exports.noconvert = function (data, callback) {
@@ -477,10 +196,6 @@ exports.convert = function (data, options, callback) {
477
196
  }
478
197
  try {
479
198
  var result = drafter.parse(data, { type: 'ast' });
480
- //for (var i = 0; i < result.warnings.length; i++) {
481
- // var warn = result.warnings[i];
482
- // console.log(warn);
483
- //}
484
199
  var swagger = apib2swagger(result.ast, options);
485
200
  return callback(null, { swagger: swagger });
486
201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apib2swagger",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Convert API Blueprint to Swagger.",
5
5
  "bin": "./bin/apib2swagger.js",
6
6
  "scripts": {
@@ -21,14 +21,16 @@
21
21
  "drafter.js": "^2.6.2",
22
22
  "generate-schema": "^2.5.0",
23
23
  "js-yaml": "^3.13.1",
24
+ "lodash.isequal": "^4.5.0",
24
25
  "nopt": "^3.0.6",
25
26
  "uritemplate": "^0.3.4"
26
27
  },
27
28
  "devDependencies": {
28
- "coveralls": "^3.0.11",
29
+ "coveralls": "^3.1.1",
29
30
  "cross-env": "^5.2.0",
30
31
  "mocha": "^5.2.0",
31
32
  "nyc": "^13.3.0",
33
+ "sinon": "^12.0.1",
32
34
  "tv4": "^1.2.7"
33
35
  }
34
- }
36
+ }
File without changes
@@ -1,9 +1,9 @@
1
1
  var escapeJSONPointer = require('./escape_json_pointer');
2
2
 
3
- function convertMsonToJsonSchema(content) {
3
+ function convertMsonToJsonSchema(content, options) {
4
4
  // for apib._version = "4.0"
5
5
  var mson = content.content[0];
6
- var schema = convert(mson);
6
+ var schema = convert(mson, options);
7
7
  if (schema.type === 'array') {
8
8
  var fixedType = false;
9
9
  if (mson.attributes && mson.attributes.typeAttributes) {
@@ -18,16 +18,18 @@ function convertMsonToJsonSchema(content) {
18
18
  return schema;
19
19
  }
20
20
 
21
- function convert(mson) {
21
+ function convert(mson, options) {
22
+ const { openApi3 } = options
23
+ const componentsPath = openApi3 ? '#/components/schemas/' : '#/definitions/'
22
24
  // mson.element = "boolean", "string", "number", "array", "enum", "object", CustomType
23
25
  switch (mson.element) {
24
26
  case 'array':
25
27
  if (!mson.content || mson.content.length === 0) {
26
28
  return { type: 'array', items: {} };
27
29
  } else if (mson.content.length === 1) {
28
- return { type: 'array', items: convert(mson.content[0]) };
30
+ return { type: 'array', items: convert(mson.content[0], options) };
29
31
  } else if (mson.content.length > 1) {
30
- return { type: 'array', items: { 'anyOf': mson.content.map(convert) } };
32
+ return { type: 'array', items: { 'anyOf': mson.content.map((m) => convert(m, options)) } };
31
33
  }
32
34
  case 'enum':
33
35
  return convertEnum(mson.content);
@@ -39,7 +41,7 @@ function convert(mson) {
39
41
  return { type: mson.element };
40
42
  default:
41
43
  if (!mson.content) {
42
- return { '$ref': '#/definitions/' + escapeJSONPointer(mson.element) };
44
+ return { '$ref': componentsPath + escapeJSONPointer(mson.element) };
43
45
  }
44
46
  break;
45
47
  }
@@ -51,7 +53,7 @@ function convert(mson) {
51
53
  for (var j = 0; mson.content && j < mson.content.length; j++) {
52
54
  var member = mson.content[j];
53
55
  if (member.element !== "member") continue;
54
- schema.properties[member.content.key.content] = convert(member.content.value);
56
+ schema.properties[member.content.key.content] = convert(member.content.value, options);
55
57
  if (member.meta && member.meta.description) {
56
58
  schema.properties[member.content.key.content].description = member.meta.description;
57
59
  }
@@ -66,8 +68,8 @@ function convert(mson) {
66
68
  schema.required.push(member.content.key.content);
67
69
  break;
68
70
  case 'nullable':
69
- schema.properties[member.content.key.content].type = [schema.properties[member.content.key.content].type, 'null']
70
- break;
71
+ schema.properties[member.content.key.content].type = [schema.properties[member.content.key.content].type, 'null']
72
+ break;
71
73
  }
72
74
  });
73
75
  }
@@ -82,7 +84,7 @@ function convert(mson) {
82
84
  }
83
85
 
84
86
  if (mson.element !== 'object') {
85
- return { 'allOf': [{ '$ref': '#/definitions/' + escapeJSONPointer(mson.element) }, schema] };
87
+ return { 'allOf': [{ '$ref': componentsPath + escapeJSONPointer(mson.element) }, schema] };
86
88
  }
87
89
 
88
90
  return schema;