openapi-jsonrpc-jsdoc 1.4.0 → 1.4.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.
Files changed (3) hide show
  1. package/README.md +3 -5
  2. package/index.mjs +158 -59
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@ npm i openapi-jsonrpc-jsdoc
8
8
 
9
9
  ## Examples
10
10
 
11
- ### Create JSON-RPC Method
11
+ ### Usage Example
12
12
 
13
13
  ```js
14
14
  // api/api-v1.js
@@ -28,11 +28,9 @@ module.exports = (parameters) => {
28
28
  }
29
29
  ```
30
30
 
31
- ### Run package
31
+ ### Generate OpenAPI JSON
32
32
 
33
33
  ```js
34
- // index.js
35
- const fs = require('fs');
36
34
  const openapiJSONRpcJSDoc = require('openapi-jsonrpc-jsdoc');
37
35
  openapiJSONRpcJSDoc({
38
36
  api: '/',
@@ -44,7 +42,7 @@ openapiJSONRpcJSDoc({
44
42
  packageUrl: './package.json',
45
43
  files: './api/*.js',
46
44
  }).then(data => {
47
- fs.writeFileSync('openapi.json', JSON.stringify(data, null, 2));
45
+ JSON.stringify(data, null, 2);// openapi.json
48
46
  });
49
47
  ```
50
48
 
package/index.mjs CHANGED
@@ -1,5 +1,132 @@
1
1
  import jsdoc from 'jsdoc-api';
2
2
 
3
+ function extractName(name) {
4
+ try {
5
+ return name.match(/\.(.+)/i)[1]; //eslint-disable-line
6
+ } catch {
7
+ return name;
8
+ }
9
+ }
10
+
11
+ function resolveSchemaFromTypeNames(names) {
12
+ let type;
13
+ let format;
14
+ let items;
15
+ let oneOf;
16
+ let nullable;
17
+ let constant;
18
+ let enumData;
19
+
20
+ if (names.length === 0) {
21
+ type = 'null';
22
+ } else if (names.length === 1) {
23
+ [type] = names;
24
+ } else {
25
+ type = 'enum';
26
+ }
27
+
28
+ switch (type) {
29
+ case 'Array.<string>': {
30
+ type = 'array';
31
+ items = {type: 'string'};
32
+ break;
33
+ }
34
+ case 'Array.<number>': {
35
+ type = 'array';
36
+ items = {type: 'number'};
37
+ break;
38
+ }
39
+ case 'URL': {
40
+ type = 'string';
41
+ format = 'url';
42
+ break;
43
+ }
44
+ case 'String':
45
+ case 'string': {
46
+ type = 'string';
47
+ break;
48
+ }
49
+ case 'Number':
50
+ case 'number': {
51
+ type = 'number';
52
+ break;
53
+ }
54
+ case 'Boolean':
55
+ case 'boolean': {
56
+ type = 'boolean';
57
+ break;
58
+ }
59
+ case 'Date': {
60
+ type = 'string';
61
+ format = 'date-time';
62
+ break;
63
+ }
64
+ case 'enum': {
65
+ enumData = names;
66
+ if (enumData.includes('null')) {
67
+ nullable = true;
68
+ enumData = enumData.filter(n => n !== 'null');
69
+ }
70
+ oneOf = enumData.map((n) => {
71
+ if (!Number.isNaN(Number(n))) {
72
+ return Number(n);
73
+ }
74
+ return n;
75
+ });
76
+
77
+ if (enumData.every(n => Number.isInteger(Number(n)))) {
78
+ type = 'integer';
79
+ } else if (enumData.every(n => !Number.isNaN(Number(n)))) {
80
+ type = 'number';
81
+ } else if (enumData.every(n => n?.toLowerCase() === 'boolean')) {
82
+ type = 'boolean';
83
+ } else if (enumData.every(n => n?.toLowerCase() === 'number')) {
84
+ type = 'number';
85
+ } else if (enumData.every(n => n?.toLowerCase() === 'string')) {
86
+ type = 'string';
87
+ } else if (enumData.every(n => n?.toLowerCase() === 'date')) {
88
+ type = 'string';
89
+ format = 'date-time';
90
+ } else if (enumData.every(n => n === 'URL')) {
91
+ type = 'string';
92
+ format = 'url';
93
+ } else if (enumData.every(n => n?.toLowerCase() === 'true')) {
94
+ type = 'boolean';
95
+ constant = true;
96
+ } else if (enumData.every(n => n?.toLowerCase() === 'false')) {
97
+ type = 'boolean';
98
+ constant = false;
99
+ } else {
100
+ type = 'string';
101
+ }
102
+
103
+ if (enumData?.length === 1 && oneOf?.length === 1) {
104
+ if (enumData[0] === oneOf[0]) {
105
+ if (!format) {
106
+ [format] = enumData;
107
+ }
108
+ oneOf = undefined;
109
+ enumData = undefined;
110
+ }
111
+ }
112
+ break;
113
+ }
114
+ default: {
115
+ break;
116
+ }
117
+ }
118
+
119
+ return {
120
+ type,
121
+ format,
122
+ oneOf,
123
+ nullable,
124
+ items,
125
+ constant,
126
+ enumData,
127
+ };
128
+ }
129
+
3
130
  export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {}, packageUrl, servers, api = '/' }) {
4
131
  const allData = await jsdoc.explain({
5
132
  files: Array.isArray(files) ? files : [files],
@@ -157,69 +284,31 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
157
284
  if (parameter.type.names[0] === 'object') {
158
285
  return accumulator;
159
286
  }
160
- let type;
161
- if (parameter.type.names.length === 0) {
162
- type = 'null';
163
- } else if (parameter.type.names.length === 1) {
164
- type = parameter.type.names[0];
165
- } else {
166
- type = 'enum';
167
- }
168
- let items;
169
- let enumData;
170
- let oneOf;
171
287
 
172
- switch (type) {
173
- case 'Array.<string>': {
174
- type = 'array';
175
- items = { type: 'string' };
176
- break;
177
- }
178
- case 'Array.<number>': {
179
- type = 'array';
180
- items = { type: 'number' };
181
- break;
182
- }
183
- case 'enum': {
184
- enumData = parameter.type.names;
185
- oneOf = parameter.type.names.map((n) => {
186
- if (!Number.isNaN(Number(n))) {
187
- return Number(n);
188
- }
189
- return n;
190
- });
191
- if (parameter.type.names.every(n => Number.isInteger(Number(n)))) {
192
- type = 'integer';
193
- } else if (parameter.type.names.every(n => !Number.isNaN(Number(n)))) {
194
- type = 'number';
195
- } else {
196
- type = 'string';
197
- }
198
- break;
199
- }
200
- default: {
201
- break;
202
- }
203
- }
204
288
  const description = parameter.description;
205
- let name;
206
- try {
207
- name = parameter.name.match(/\.(.+)/i)[1]; //eslint-disable-line
208
- } catch {
209
- name = parameter.name;
210
- }
289
+ const name = extractName(parameter.name);
290
+ accumulator.properties[name] = accumulator.properties[name] ?? {};
291
+
292
+ const {
293
+ items,
294
+ constant,
295
+ enumData,
296
+ type,
297
+ format,
298
+ oneOf,
299
+ nullable,
300
+ } = resolveSchemaFromTypeNames(parameter.type.names)
211
301
  if (!parameter.optional) {
212
302
  accumulator.required.push(name);
213
303
  }
214
- accumulator.properties[name] = accumulator.properties[name] ?? {};
215
- if (type) {
216
- accumulator.properties[name].type = type;
304
+ if (nullable) {
305
+ accumulator.properties[name].nullable = nullable;
217
306
  }
218
- if (description) {
219
- accumulator.properties[name].description = description;
307
+ if (constant) {
308
+ accumulator.properties[name].const = constant;
220
309
  }
221
- if (items) {
222
- accumulator.properties[name].items = items;
310
+ if (format) {
311
+ accumulator.properties[name].format = format;
223
312
  }
224
313
  if (enumData) {
225
314
  accumulator.properties[name].enum = enumData;
@@ -227,7 +316,15 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
227
316
  if (oneOf) {
228
317
  accumulator.properties[name].oneOf = oneOf;
229
318
  }
230
-
319
+ if (type) {
320
+ accumulator.properties[name].type = type;
321
+ }
322
+ if (description) {
323
+ accumulator.properties[name].description = description;
324
+ }
325
+ if (items) {
326
+ accumulator.properties[name].items = items;
327
+ }
231
328
  return accumulator;
232
329
  },
233
330
  {
@@ -238,8 +335,10 @@ export default async function openapiJsonrpcJsdoc({ files, securitySchemes = {},
238
335
  properties: {},
239
336
  },
240
337
  );
241
- const schemaPostJsdoc = schema.post.requestBody.content['application/json'].schema;
242
- schemaPostJsdoc.properties.params = propertiesParameters;
338
+ if (exampleJSON !== null) {
339
+ const schemaPostJsdoc = schema.post.requestBody.content['application/json'].schema;
340
+ schemaPostJsdoc.properties.params = propertiesParameters;
341
+ }
243
342
  }
244
343
  temporaryDocument.paths[`${api}${apiName}`] = schema;
245
344
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openapi-jsonrpc-jsdoc",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Transform JSDoc-annotated JSON-RPC 2.0 methods into OpenAPI specifications.",
5
5
  "main": "index.mjs",
6
6
  "type": "module",