apib2swagger 1.12.0 → 1.14.1

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 (124) 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 +30 -9
  8. package/bin/apib2swagger.js +44 -18
  9. package/index.js +82 -366
  10. package/package.json +7 -4
  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 +357 -0
  15. package/src/responses.js +186 -0
  16. package/src/util.js +125 -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/Issue-#57.md +14 -0
  29. package/test/input/OpenAPI3_Issue-#58.md +31 -0
  30. package/test/input/OpenAPI3_attributes.md +36 -0
  31. package/test/input/OpenAPI3_headers.md +74 -0
  32. package/test/input/OpenAPI3_includes.md +22 -0
  33. package/test/input/OpenAPI3_requests.md +162 -0
  34. package/test/input/OpenAPI3_responses.md +177 -0
  35. package/test/input/Schema.md +0 -0
  36. package/test/input/apiblueprint_uber.md +0 -0
  37. package/test/input/apiblueprint_valid_simple.md +0 -0
  38. package/test/input/includes/resource-request-body-2.json +3 -0
  39. package/test/input/includes/resource-request-body.json +3 -0
  40. package/test/input/includes/resource-request-schema.json +9 -0
  41. package/test/input/includes/resource-response-body-2.json +3 -0
  42. package/test/input/includes/resource-response-body.json +3 -0
  43. package/test/input/includes/resource-response-schema.json +9 -0
  44. package/test/input/schema_without_body.md +17 -0
  45. package/test/meta_schema.json +0 -0
  46. package/test/openAPI_30_schema.json +1662 -0
  47. package/test/output/01. Simplest API.json +0 -0
  48. package/test/output/01. Simplest API.ref.json +0 -0
  49. package/test/output/02. Resource and Actions.json +0 -0
  50. package/test/output/02. Resource and Actions.ref.json +0 -0
  51. package/test/output/03. Named Resource and Actions.json +0 -0
  52. package/test/output/03. Named Resource and Actions.ref.json +0 -0
  53. package/test/output/04. Grouping Resources.json +0 -0
  54. package/test/output/04. Grouping Resources.ref.json +0 -0
  55. package/test/output/05. Responses.json +0 -0
  56. package/test/output/05. Responses.ref.json +0 -0
  57. package/test/output/06. Requests.json +0 -0
  58. package/test/output/06. Requests.ref.json +0 -0
  59. package/test/output/07. Parameters.json +0 -0
  60. package/test/output/07. Parameters.ref.json +0 -0
  61. package/test/output/08. Attributes.json +0 -0
  62. package/test/output/08. Attributes.ref.json +0 -0
  63. package/test/output/09. Advanced Attributes.json +0 -0
  64. package/test/output/09. Advanced Attributes.ref.json +0 -0
  65. package/test/output/10. Data Structures.json +0 -0
  66. package/test/output/10. Data Structures.ref.json +0 -0
  67. package/test/output/11. Resource Model.json +0 -0
  68. package/test/output/11. Resource Model.ref.json +0 -0
  69. package/test/output/12. Advanced Action.json +0 -0
  70. package/test/output/12. Advanced Action.ref.json +0 -0
  71. package/test/output/13. Named Endpoints.json +0 -0
  72. package/test/output/13. Named Endpoints.ref.json +0 -0
  73. package/test/output/Attributes.json +0 -0
  74. package/test/output/Attributes.ref.json +0 -0
  75. package/test/output/Gist Fox API + Auth.json +0 -0
  76. package/test/output/Gist Fox API + Auth.ref.json +0 -0
  77. package/test/output/Issue-#15.json +0 -0
  78. package/test/output/Issue-#15.ref.json +0 -0
  79. package/test/output/Issue-#22.json +0 -0
  80. package/test/output/Issue-#22.ref.json +0 -0
  81. package/test/output/Issue-#23.json +0 -0
  82. package/test/output/Issue-#23.ref.json +0 -0
  83. package/test/output/Issue-#26.json +0 -0
  84. package/test/output/Issue-#26.ref.json +0 -0
  85. package/test/output/Issue-#29.json +0 -0
  86. package/test/output/Issue-#29.ref.json +0 -0
  87. package/test/output/Issue-#33.json +0 -0
  88. package/test/output/Issue-#33.ref.json +0 -0
  89. package/test/output/Issue-#35.json +0 -0
  90. package/test/output/Issue-#35.ref.json +0 -0
  91. package/test/output/Issue-#36.json +0 -0
  92. package/test/output/Issue-#36.ref.json +0 -0
  93. package/test/output/Issue-#38.bearer.json +0 -0
  94. package/test/output/Issue-#38.json +0 -0
  95. package/test/output/Issue-#38.ref.json +0 -0
  96. package/test/output/Issue-#49.json +0 -0
  97. package/test/output/Issue-#49.ref.json +0 -0
  98. package/test/output/Issue-#57.json +44 -0
  99. package/test/output/Issue-#57.ref.json +44 -0
  100. package/test/output/OpenAPI3_Issue-#58.json +202 -0
  101. package/test/output/OpenAPI3_Issue-#58.ref.json +163 -0
  102. package/test/output/OpenAPI3_attributes.json +199 -0
  103. package/test/output/OpenAPI3_attributes.ref.json +173 -0
  104. package/test/output/OpenAPI3_headers.json +169 -0
  105. package/test/output/OpenAPI3_headers.ref.json +169 -0
  106. package/test/output/OpenAPI3_includes.json +66 -0
  107. package/test/output/OpenAPI3_includes.ref.json +66 -0
  108. package/test/output/OpenAPI3_requests.json +326 -0
  109. package/test/output/OpenAPI3_requests.ref.json +321 -0
  110. package/test/output/OpenAPI3_responses.json +255 -0
  111. package/test/output/OpenAPI3_responses.ref.json +245 -0
  112. package/test/output/Schema.json +0 -0
  113. package/test/output/Schema.ref.json +0 -0
  114. package/test/output/apiblueprint_uber.json +0 -0
  115. package/test/output/apiblueprint_uber.ref.json +0 -0
  116. package/test/output/apiblueprint_valid_simple.json +0 -0
  117. package/test/output/apiblueprint_valid_simple.ref.json +0 -0
  118. package/test/output/schema_without_body.json +52 -0
  119. package/test/output/schema_without_body.ref.json +52 -0
  120. package/test/swagger_20_schema.json +0 -0
  121. package/test/test.js +26 -9
  122. package/test/util.test.js +151 -0
  123. package/.npmignore +0 -35
  124. package/package-lock.json +0 -2021
package/index.js CHANGED
@@ -1,36 +1,56 @@
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')
10
+ const toOpenApi = require('json-schema-to-openapi-schema')
9
11
 
10
12
  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': '',
13
+ const { openApi3, infoTitle } = options
14
+ var output = {};
15
+ if (openApi3) {
16
+ output.openapi = '3.0.3';
17
+ } else {
18
+ output.swagger = '2.0';
19
+ }
20
+ output.info = {
21
+ 'title': infoTitle || apib.name,
22
+ 'version': openApi3 ? '1.0.0' : '',
17
23
  'description': apib.description
18
24
  }
19
25
  apib.metadata.forEach(function (meta) {
20
- //console.log(meta);
21
26
  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;
27
+ if (openApi3) {
28
+ output.servers = [
29
+ {
30
+ url: meta.value
31
+ }
32
+ ];
33
+ } else {
34
+ var urlParts = url.parse(meta.value);
35
+ output.host = urlParts.host;
36
+ output.basePath = urlParts.pathname;
37
+ output.schemes = [urlParts.protocol.replace(':', '')];
38
+ }
39
+ }
40
+ if (meta.name.toLowerCase() === 'version' && meta.value) {
41
+ output.info.version = meta.value
28
42
  }
29
43
  });
30
- swagger.paths = {};
31
- swagger.definitions = {};
32
- swagger.securityDefinitions = {};
33
- var converterContext = { swagger: swagger, options: options };
44
+ output.paths = {};
45
+
46
+ if (openApi3) {
47
+ output.components = { schemas: {} }
48
+ } else {
49
+ output.definitions = {}
50
+ output.securityDefinitions = {};
51
+ }
52
+
53
+ var converterContext = { swagger: output, options: options };
34
54
  var tags = {};
35
55
  apib.content.filter(function (content) {
36
56
  return content.element === 'category';
@@ -42,21 +62,32 @@ var apib2swagger = module.exports.convertParsed = function (apib, options) {
42
62
  category.content.forEach(function (content) {
43
63
  if (content.element === 'resource') {
44
64
  // (name, description) in Resource section are discarded
45
- swaggerDefinitions(swagger.definitions, content);
65
+ const definitions = swaggerDefinitions(content, options);
66
+ if (openApi3) {
67
+ output.components.schemas = { ...output.components.schemas, ...definitions }
68
+ } else {
69
+ output.definitions = { ...output.definitions, ...definitions }
70
+ }
46
71
  swaggerPaths(converterContext, groupName, content);
47
72
  } else if (content.element === 'copy') {
48
73
  // group description here
49
74
  tags[groupName].description = content.content;
50
75
  } else if (content.element === 'dataStructure') {
51
- swagger.definitions[content.content[0].meta.id] = jsonSchemaFromMSON(content);
76
+ const { id } = content.content[0].meta
77
+ const schema = jsonSchemaFromMSON(content, options);
78
+ if (openApi3) {
79
+ output.components.schemas[id] = toOpenApi(schema)
80
+ } else {
81
+ output.definitions[id] = schema
82
+ }
52
83
  }
53
84
  });
54
85
  });
55
- swagger.tags = [];
86
+ output.tags = [];
56
87
  for (var key in tags) {
57
- swagger.tags.push(tags[key]);
88
+ output.tags.push(tags[key]);
58
89
  }
59
- return swagger;
90
+ return output;
60
91
  }
61
92
 
62
93
  function swaggerPathName(uriTemplate) {
@@ -73,29 +104,43 @@ function swaggerPathName(uriTemplate) {
73
104
  return decodeURIComponent(uriTemplate.expand(params));
74
105
  }
75
106
 
76
- var swaggerDefinitions = function (definitions, resource) {
107
+ var swaggerDefinitions = function (resource, options) {
77
108
  var scheme;
109
+ const result = {}
110
+ const { openApi3 } = options
78
111
  if (resource.name) {
79
- scheme = searchDataStructure(resource.content); // Attributes 1
80
- definitions[resource.name] = scheme ? scheme : {};
112
+ scheme = searchDataStructure(resource.content, options); // Attributes 1
113
+ if (openApi3) {
114
+ if (scheme) {
115
+ result[resource.name] = toOpenApi(scheme)
116
+ }
117
+ } else {
118
+ result[resource.name] = scheme ? scheme : {};
119
+ }
81
120
  }
82
121
  const model = resource.model;
83
122
  if (model.content && model.name) {
84
- scheme = searchDataStructure(model.content); // Attribute 2
123
+ scheme = searchDataStructure(model.content, options); // Attribute 2
85
124
  // fall back to body
86
125
  if (!scheme && model.content.length > 0) {
87
- scheme = generateSchemaFromExample(model.headers, model.content[0].content);
126
+ scheme = generateSchemaFromExample(model.headers, model.content[0].content, options);
127
+ }
128
+ if (openApi3) {
129
+ if (scheme) {
130
+ result[model.name + 'Model'] = toOpenApi(scheme)
131
+ }
132
+ } else {
133
+ result[model.name + 'Model'] = scheme ? scheme : {};
88
134
  }
89
- definitions[model.name + 'Model'] = scheme ? scheme : {};
90
135
  }
136
+ return result
91
137
  };
92
138
 
93
139
  var swaggerPaths = function (context, tag, resource) {
94
140
  var paths = context.swagger.paths;
95
141
  var uriTemplate = UriTemplate.parse(resource.uriTemplate),
96
142
  pathName = swaggerPathName(uriTemplate);
97
- //path.parameters = swaggerParameters(resource.parameters, uriTemplate);
98
- var pathParams = swaggerParameters(resource.parameters, uriTemplate); // for swagger ui
143
+ var pathParams = processParameters(resource.parameters, uriTemplate, context.options); // for swagger ui
99
144
  for (var k = 0; k < resource.actions.length; k++) {
100
145
  var action = resource.actions[k];
101
146
  if (!action.attributes.uriTemplate) {
@@ -112,12 +157,12 @@ var swaggerPaths = function (context, tag, resource) {
112
157
 
113
158
  var swaggerOperation = function (context, pathParams, uriTemplate, action, tag) {
114
159
  var operation = {
115
- 'responses': swaggerResponses(action.examples, context.options),
160
+ 'responses': processResponses(action.examples, context.options),
116
161
  'summary': action.name,
117
162
  'operationId': action.name,
118
163
  'description': action.description,
119
164
  'tags': tag ? [tag] : [],
120
- 'parameters': pathParams.concat(swaggerParameters(action.parameters, uriTemplate))
165
+ 'parameters': pathParams.concat(processParameters(action.parameters, uriTemplate, context.options))
121
166
  };
122
167
  var produces = {}, producesExist = false;
123
168
  for (var key in operation.responses) {
@@ -133,332 +178,7 @@ var swaggerOperation = function (context, pathParams, uriTemplate, action, tag)
133
178
  operation.produces.push(mime);
134
179
  }
135
180
  }
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;
181
+ return processRequests(operation, action, context)
462
182
  }
463
183
 
464
184
  exports.noconvert = function (data, callback) {
@@ -477,10 +197,6 @@ exports.convert = function (data, options, callback) {
477
197
  }
478
198
  try {
479
199
  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
200
  var swagger = apib2swagger(result.ast, options);
485
201
  return callback(null, { swagger: swagger });
486
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apib2swagger",
3
- "version": "1.12.0",
3
+ "version": "1.14.1",
4
4
  "description": "Convert API Blueprint to Swagger.",
5
5
  "bin": "./bin/apib2swagger.js",
6
6
  "scripts": {
@@ -20,15 +20,18 @@
20
20
  "apib-include-directive": "^0.1.0",
21
21
  "drafter.js": "^2.6.2",
22
22
  "generate-schema": "^2.5.0",
23
- "js-yaml": "^3.13.1",
23
+ "js-yaml": "^4.1.0",
24
+ "json-schema-to-openapi-schema": "^0.4.0",
25
+ "lodash.isequal": "^4.5.0",
24
26
  "nopt": "^3.0.6",
25
27
  "uritemplate": "^0.3.4"
26
28
  },
27
29
  "devDependencies": {
28
- "coveralls": "^3.0.11",
30
+ "coveralls": "^3.1.1",
29
31
  "cross-env": "^5.2.0",
30
32
  "mocha": "^5.2.0",
31
33
  "nyc": "^13.3.0",
34
+ "sinon": "^12.0.1",
32
35
  "tv4": "^1.2.7"
33
36
  }
34
- }
37
+ }
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;