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