apib2swagger 1.13.0 → 1.14.2

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.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # apib2swagger
2
2
 
3
- [![Build Status](https://travis-ci.org/kminami/apib2swagger.svg?branch=master)](https://travis-ci.org/kminami/apib2swagger)
3
+ ![Build Status](https://github.com/kminami/apib2swagger/actions/workflows/nodejs.yml/badge.svg)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/kminami/apib2swagger/badge.svg?branch=master)](https://coveralls.io/github/kminami/apib2swagger?branch=master)
5
5
  [![npm version](https://badge.fury.io/js/apib2swagger.svg)](https://badge.fury.io/js/apib2swagger)
6
6
 
7
- Convert [API Blueprint][] to [Swagger][].
7
+ Convert [API Blueprint](https://apiblueprint.org/) to [Swagger 2.0](http://swagger.io/) or [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification).
8
8
 
9
9
  Supported versions:
10
10
  - API Blueprint 1A9
@@ -91,16 +91,11 @@ cat api.md | npx apib2swagger > swagger.json
91
91
  You can also run apib2swagger inside a docker container.
92
92
 
93
93
  ```bash
94
- $ docker run -it --rm -v $(pwd):/docs kminami/apib2swagger -i /docs/api.md -o /docs/swagger.json
94
+ $ docker run -it --rm -v $(pwd):/docs ghcr.io/kminami/apib2swagger -i /docs/api.md -o /docs/swagger.json
95
95
  ```
96
96
 
97
- You can use container images in GitHub Container Registory like `ghcr.io/kminami/apib2swagger` or `ghcr.io/kminami/apib2swagger:1.12.0` instead of Docker Hub.
98
-
99
97
  ## License
100
98
 
101
99
  Copyright (c) 2021 Keisuke Minami
102
100
 
103
101
  MIT
104
-
105
- [API Blueprint]: https://apiblueprint.org/ "API Blueprint"
106
- [Swagger]: http://swagger.io/ "Swagger"
@@ -111,6 +111,12 @@ function processBlueprint(blueprint, opts) {
111
111
  };
112
112
  apib2swagger.convert(blueprint, options, function(error, result) {
113
113
  if (error) {
114
+ if (error.stack && error.result) {
115
+ // override toString() of drafter's error to expand error.result.error.location
116
+ console.log(error.stack);
117
+ console.log(JSON.stringify(error.result, 0, 2));
118
+ return;
119
+ }
114
120
  console.log(error);
115
121
  return;
116
122
  }
@@ -126,7 +132,7 @@ function processBlueprint(blueprint, opts) {
126
132
  return runServer(swagger);
127
133
  }
128
134
  if (opts.yaml) {
129
- var data = yaml.safeDump(swagger);
135
+ var data = yaml.dump(swagger);
130
136
  if (output !== '-') {
131
137
  fs.writeFileSync(output, data);
132
138
  } else {
package/index.js CHANGED
@@ -7,6 +7,7 @@ const { searchDataStructure, generateSchemaFromExample } = require('./src/util')
7
7
  const { processRequests } = require('./src/requests')
8
8
  const { processResponses } = require('./src/responses')
9
9
  const { processParameters } = require('./src/parameters')
10
+ const toOpenApi = require('json-schema-to-openapi-schema')
10
11
 
11
12
  var apib2swagger = module.exports.convertParsed = function (apib, options) {
12
13
  const { openApi3, infoTitle } = options
@@ -75,7 +76,7 @@ var apib2swagger = module.exports.convertParsed = function (apib, options) {
75
76
  const { id } = content.content[0].meta
76
77
  const schema = jsonSchemaFromMSON(content, options);
77
78
  if (openApi3) {
78
- output.components.schemas[id] = schema
79
+ output.components.schemas[id] = toOpenApi(schema)
79
80
  } else {
80
81
  output.definitions[id] = schema
81
82
  }
@@ -111,7 +112,7 @@ var swaggerDefinitions = function (resource, options) {
111
112
  scheme = searchDataStructure(resource.content, options); // Attributes 1
112
113
  if (openApi3) {
113
114
  if (scheme) {
114
- result[resource.name] = scheme
115
+ result[resource.name] = toOpenApi(scheme)
115
116
  }
116
117
  } else {
117
118
  result[resource.name] = scheme ? scheme : {};
@@ -126,7 +127,7 @@ var swaggerDefinitions = function (resource, options) {
126
127
  }
127
128
  if (openApi3) {
128
129
  if (scheme) {
129
- result[model.name + 'Model'] = scheme
130
+ result[model.name + 'Model'] = toOpenApi(scheme)
130
131
  }
131
132
  } else {
132
133
  result[model.name + 'Model'] = scheme ? scheme : {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apib2swagger",
3
- "version": "1.13.0",
3
+ "version": "1.14.2",
4
4
  "description": "Convert API Blueprint to Swagger.",
5
5
  "bin": "./bin/apib2swagger.js",
6
6
  "scripts": {
@@ -20,7 +20,8 @@
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",
24
25
  "lodash.isequal": "^4.5.0",
25
26
  "nopt": "^3.0.6",
26
27
  "uritemplate": "^0.3.4"
package/src/requests.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { hasFileRef, getRefFromInclude, searchDataStructure, generateSchemaFromExample } = require('./util')
2
2
  const isEqual = require('lodash.isequal')
3
3
  const escapeJSONPointer = require('./escape_json_pointer')
4
+ const toOpenApi = require('json-schema-to-openapi-schema')
4
5
 
5
6
  const swaggerHeaders = function (options, headers) {
6
7
  var params = [];
@@ -250,8 +251,14 @@ const setOpenApiRequestSchema = (operation, schema) => {
250
251
  if (!operation.requestBody.content[schema[0].contentType]){
251
252
  operation.requestBody.content[schema[0].contentType] = schema[0].scheme
252
253
  } else {
253
- operation.requestBody.content[schema[0].contentType].schema = schema[0].scheme
254
+ operation.requestBody.content[schema[0].contentType].schema = schema[0].scheme
254
255
  }
256
+ Object.keys(operation.requestBody.content).forEach(contentType => {
257
+ const s = operation.requestBody.content[contentType].schema;
258
+ if (s) {
259
+ operation.requestBody.content[contentType].schema = toOpenApi(s)
260
+ }
261
+ })
255
262
  return operation
256
263
  }
257
264
 
@@ -283,6 +290,12 @@ const setOpenApiRequestSchema = (operation, schema) => {
283
290
  ]
284
291
  }
285
292
  })
293
+ Object.keys(operation.requestBody.content).forEach(contentType => {
294
+ const s = operation.requestBody.content[contentType].schema;
295
+ if (s) {
296
+ operation.requestBody.content[contentType].schema = toOpenApi(s)
297
+ }
298
+ })
286
299
  return operation
287
300
  }
288
301
 
package/src/responses.js CHANGED
@@ -2,6 +2,7 @@ const { fixArraySchema, hasFileRef, getRefFromInclude, searchDataStructure } = r
2
2
  const escapeJSONPointer = require('./escape_json_pointer')
3
3
  const isEqual = require('lodash.isequal')
4
4
  const http = require('http')
5
+ const toOpenApi = require('json-schema-to-openapi-schema')
5
6
 
6
7
  const parseResponseSchema = (schema, options) => {
7
8
  if (!schema) return
@@ -64,6 +65,10 @@ const setResponseSchema = (responses, response, schema, options) => {
64
65
  responses[response.name].schema = schema
65
66
  return responses
66
67
  }
68
+ // Convert JSON Schema draft04 to OpenAPI 3.0.x (type: null -> nullable: true)
69
+ // TODO: Skip this for OpenAPI 3.1.x or later.
70
+ schema = toOpenApi(schema)
71
+
67
72
  // In openAPI 3 the schema lives under the content type
68
73
  const contentTypeHeader = response.headers.find((h) => h.name.toLowerCase() === 'content-type')
69
74
  if (!contentTypeHeader.value) {
package/src/util.js CHANGED
@@ -87,10 +87,8 @@ module.exports.generateSchemaFromExample = function (headers, example, options)
87
87
  delete scheme.$schema;
88
88
  // if we have example values in the body then insert them into the json schema
89
89
  if (!options.openApi3) {
90
- if (scheme['type'] === 'object') {
90
+ if (scheme['type'] === 'object' || scheme['type'] === 'array') {
91
91
  scheme.example = body;
92
- } else if (scheme['type'] === 'array') {
93
- scheme.items.example = body;
94
92
  }
95
93
  }
96
94
  return scheme;
@@ -0,0 +1,14 @@
1
+ # DELETE /domains
2
+
3
+ + Request (application/json)
4
+
5
+ Domain deletes are limited to 10,000 domains per request.
6
+
7
+ + Body
8
+
9
+ [
10
+ "example1.com",
11
+ "example2.com"
12
+ ]
13
+
14
+ + Response 204 (application/json)
@@ -0,0 +1,31 @@
1
+ # Group Users
2
+
3
+ ## A user [/user]
4
+
5
+ ### Get one user [GET]
6
+ + Response 200 (application/json)
7
+ + Attributes (User)
8
+ + Response 500 (application/json)
9
+
10
+ ## Some users [/users]
11
+
12
+ ### Post a user [POST]
13
+ + Request (application/json)
14
+ + Attributes(User)
15
+ + Response 201 (application/json)
16
+ + Attributes (User)
17
+ + Response 500 (application/json)
18
+
19
+ ### Get a collection of users [GET]
20
+ + Response 200 (application/json)
21
+ + Attributes (array[User])
22
+ + Response 500 (application/json)
23
+
24
+ # Data Structures
25
+
26
+ ## User (object)
27
+ + id (string, required)
28
+ + name (string, nullable, required)
29
+
30
+ ## Users (object)
31
+ + `users`(array[User], nullable, required)
@@ -0,0 +1,44 @@
1
+ {
2
+ "swagger": "2.0",
3
+ "info": {
4
+ "title": "",
5
+ "version": "",
6
+ "description": ""
7
+ },
8
+ "paths": {
9
+ "/domains": {
10
+ "delete": {
11
+ "responses": {
12
+ "204": {
13
+ "description": "No Content",
14
+ "headers": {},
15
+ "examples": {}
16
+ }
17
+ },
18
+ "summary": "",
19
+ "operationId": "",
20
+ "description": "",
21
+ "tags": [],
22
+ "parameters": [
23
+ {
24
+ "name": "body",
25
+ "in": "body",
26
+ "schema": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "string"
30
+ },
31
+ "example": [
32
+ "example1.com",
33
+ "example2.com"
34
+ ]
35
+ }
36
+ }
37
+ ]
38
+ }
39
+ }
40
+ },
41
+ "definitions": {},
42
+ "securityDefinitions": {},
43
+ "tags": []
44
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "swagger": "2.0",
3
+ "info": {
4
+ "title": "",
5
+ "version": "",
6
+ "description": ""
7
+ },
8
+ "paths": {
9
+ "/domains": {
10
+ "delete": {
11
+ "responses": {
12
+ "204": {
13
+ "description": "No Content",
14
+ "headers": {},
15
+ "examples": {}
16
+ }
17
+ },
18
+ "summary": "",
19
+ "operationId": "",
20
+ "description": "",
21
+ "tags": [],
22
+ "parameters": [
23
+ {
24
+ "name": "body",
25
+ "in": "body",
26
+ "schema": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "string"
30
+ },
31
+ "example": [
32
+ "example1.com",
33
+ "example2.com"
34
+ ]
35
+ }
36
+ }
37
+ ]
38
+ }
39
+ }
40
+ },
41
+ "definitions": {},
42
+ "securityDefinitions": {},
43
+ "tags": []
44
+ }
@@ -0,0 +1,202 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "",
5
+ "version": "1.0.0",
6
+ "description": ""
7
+ },
8
+ "paths": {
9
+ "/user": {
10
+ "get": {
11
+ "responses": {
12
+ "200": {
13
+ "description": "OK",
14
+ "headers": {},
15
+ "content": {
16
+ "application/json": {
17
+ "schema": {
18
+ "type": "object",
19
+ "properties": {
20
+ "id": {
21
+ "type": "string"
22
+ },
23
+ "name": {
24
+ "type": "string",
25
+ "nullable": true
26
+ }
27
+ },
28
+ "required": [
29
+ "id",
30
+ "name"
31
+ ]
32
+ },
33
+ "example": {
34
+ "id": "",
35
+ "name": null
36
+ }
37
+ }
38
+ }
39
+ },
40
+ "500": {
41
+ "description": "Internal Server Error",
42
+ "headers": {},
43
+ "content": {}
44
+ }
45
+ },
46
+ "summary": "Get one user",
47
+ "operationId": "Get one user",
48
+ "description": "",
49
+ "tags": [
50
+ "Users"
51
+ ],
52
+ "parameters": []
53
+ }
54
+ },
55
+ "/users": {
56
+ "post": {
57
+ "responses": {
58
+ "201": {
59
+ "description": "Created",
60
+ "headers": {},
61
+ "content": {
62
+ "application/json": {
63
+ "schema": {
64
+ "type": "object",
65
+ "properties": {
66
+ "id": {
67
+ "type": "string"
68
+ },
69
+ "name": {
70
+ "type": "string",
71
+ "nullable": true
72
+ }
73
+ },
74
+ "required": [
75
+ "id",
76
+ "name"
77
+ ]
78
+ },
79
+ "example": {
80
+ "id": "",
81
+ "name": null
82
+ }
83
+ }
84
+ }
85
+ },
86
+ "500": {
87
+ "description": "Internal Server Error",
88
+ "headers": {},
89
+ "content": {}
90
+ }
91
+ },
92
+ "summary": "Post a user",
93
+ "operationId": "Post a user",
94
+ "description": "",
95
+ "tags": [
96
+ "Users"
97
+ ],
98
+ "parameters": [],
99
+ "requestBody": {
100
+ "content": {
101
+ "application/json": {
102
+ "example": {
103
+ "id": "",
104
+ "name": null
105
+ },
106
+ "schema": {
107
+ "type": "object",
108
+ "properties": {
109
+ "id": {
110
+ "type": "string"
111
+ },
112
+ "name": {
113
+ "type": "string",
114
+ "nullable": true
115
+ }
116
+ },
117
+ "required": [
118
+ "id",
119
+ "name"
120
+ ]
121
+ }
122
+ }
123
+ }
124
+ }
125
+ },
126
+ "get": {
127
+ "responses": {
128
+ "200": {
129
+ "description": "OK",
130
+ "headers": {},
131
+ "content": {
132
+ "application/json": {
133
+ "schema": {
134
+ "type": "array",
135
+ "items": {}
136
+ },
137
+ "example": [
138
+ {
139
+ "id": "",
140
+ "name": null
141
+ }
142
+ ]
143
+ }
144
+ }
145
+ },
146
+ "500": {
147
+ "description": "Internal Server Error",
148
+ "headers": {},
149
+ "content": {}
150
+ }
151
+ },
152
+ "summary": "Get a collection of users",
153
+ "operationId": "Get a collection of users",
154
+ "description": "",
155
+ "tags": [
156
+ "Users"
157
+ ],
158
+ "parameters": []
159
+ }
160
+ }
161
+ },
162
+ "components": {
163
+ "schemas": {
164
+ "User": {
165
+ "type": "object",
166
+ "required": [
167
+ "id",
168
+ "name"
169
+ ],
170
+ "properties": {
171
+ "id": {
172
+ "type": "string"
173
+ },
174
+ "name": {
175
+ "type": "string",
176
+ "nullable": true
177
+ }
178
+ }
179
+ },
180
+ "Users": {
181
+ "type": "object",
182
+ "required": [
183
+ "users"
184
+ ],
185
+ "properties": {
186
+ "users": {
187
+ "type": "array",
188
+ "items": {
189
+ "$ref": "#/components/schemas/User"
190
+ },
191
+ "nullable": true
192
+ }
193
+ }
194
+ }
195
+ }
196
+ },
197
+ "tags": [
198
+ {
199
+ "name": "Users"
200
+ }
201
+ ]
202
+ }
@@ -0,0 +1,163 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "",
5
+ "version": "1.0.0",
6
+ "description": ""
7
+ },
8
+ "paths": {
9
+ "/user": {
10
+ "get": {
11
+ "responses": {
12
+ "200": {
13
+ "description": "OK",
14
+ "headers": {},
15
+ "content": {
16
+ "application/json": {
17
+ "schema": {
18
+ "$ref": "#/components/schemas/User"
19
+ },
20
+ "example": {
21
+ "id": "",
22
+ "name": null
23
+ }
24
+ }
25
+ }
26
+ },
27
+ "500": {
28
+ "description": "Internal Server Error",
29
+ "headers": {},
30
+ "content": {}
31
+ }
32
+ },
33
+ "summary": "Get one user",
34
+ "operationId": "Get one user",
35
+ "description": "",
36
+ "tags": [
37
+ "Users"
38
+ ],
39
+ "parameters": []
40
+ }
41
+ },
42
+ "/users": {
43
+ "post": {
44
+ "responses": {
45
+ "201": {
46
+ "description": "Created",
47
+ "headers": {},
48
+ "content": {
49
+ "application/json": {
50
+ "schema": {
51
+ "$ref": "#/components/schemas/User"
52
+ },
53
+ "example": {
54
+ "id": "",
55
+ "name": null
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "500": {
61
+ "description": "Internal Server Error",
62
+ "headers": {},
63
+ "content": {}
64
+ }
65
+ },
66
+ "summary": "Post a user",
67
+ "operationId": "Post a user",
68
+ "description": "",
69
+ "tags": [
70
+ "Users"
71
+ ],
72
+ "parameters": [],
73
+ "requestBody": {
74
+ "content": {
75
+ "application/json": {
76
+ "example": {
77
+ "id": "",
78
+ "name": null
79
+ },
80
+ "schema": {
81
+ "$ref": "#/components/schemas/User"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ },
87
+ "get": {
88
+ "responses": {
89
+ "200": {
90
+ "description": "OK",
91
+ "headers": {},
92
+ "content": {
93
+ "application/json": {
94
+ "schema": {
95
+ "type": "array",
96
+ "items": {}
97
+ },
98
+ "example": [
99
+ {
100
+ "id": "",
101
+ "name": null
102
+ }
103
+ ]
104
+ }
105
+ }
106
+ },
107
+ "500": {
108
+ "description": "Internal Server Error",
109
+ "headers": {},
110
+ "content": {}
111
+ }
112
+ },
113
+ "summary": "Get a collection of users",
114
+ "operationId": "Get a collection of users",
115
+ "description": "",
116
+ "tags": [
117
+ "Users"
118
+ ],
119
+ "parameters": []
120
+ }
121
+ }
122
+ },
123
+ "components": {
124
+ "schemas": {
125
+ "User": {
126
+ "type": "object",
127
+ "required": [
128
+ "id",
129
+ "name"
130
+ ],
131
+ "properties": {
132
+ "id": {
133
+ "type": "string"
134
+ },
135
+ "name": {
136
+ "type": "string",
137
+ "nullable": true
138
+ }
139
+ }
140
+ },
141
+ "Users": {
142
+ "type": "object",
143
+ "required": [
144
+ "users"
145
+ ],
146
+ "properties": {
147
+ "users": {
148
+ "type": "array",
149
+ "items": {
150
+ "$ref": "#/components/schemas/User"
151
+ },
152
+ "nullable": true
153
+ }
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "tags": [
159
+ {
160
+ "name": "Users"
161
+ }
162
+ ]
163
+ }
package/test/test.js CHANGED
@@ -46,6 +46,7 @@ var remote = 'https://raw.githubusercontent.com/apiaryio/api-blueprint/format-1A
46
46
  'Issue-#36.md',
47
47
  'Issue-#38.md',
48
48
  'Issue-#49.md',
49
+ 'Issue-#57.md',
49
50
  'apiblueprint_uber.md',
50
51
  'apiblueprint_valid_simple.md',
51
52
  'schema_without_body.md',
@@ -53,7 +54,8 @@ var remote = 'https://raw.githubusercontent.com/apiaryio/api-blueprint/format-1A
53
54
  'OpenAPI3_requests.md',
54
55
  'OpenAPI3_headers.md',
55
56
  'OpenAPI3_includes.md',
56
- 'OpenAPI3_attributes.md'
57
+ 'OpenAPI3_attributes.md',
58
+ 'OpenAPI3_Issue-#58.md'
57
59
  ];
58
60
 
59
61
  var fetch = function (file) {