sofa-api 0.11.2 → 0.12.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.
- package/README.md +70 -41
- package/index.d.ts +1 -36
- package/index.js +142 -187
- package/index.mjs +144 -187
- package/open-api/index.d.ts +19 -9
- package/open-api/operations.d.ts +6 -2
- package/open-api/types.d.ts +6 -2
- package/package.json +8 -9
- package/router.d.ts +6 -0
- package/sofa.d.ts +12 -5
- package/types.d.ts +7 -2
- package/express.d.ts +0 -25
- package/open-api/interfaces.d.ts +0 -325
package/index.js
CHANGED
|
@@ -6,14 +6,12 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
|
|
|
6
6
|
|
|
7
7
|
const tslib = require('tslib');
|
|
8
8
|
const graphql = require('graphql');
|
|
9
|
-
const
|
|
9
|
+
const ittyRouter = require('itty-router');
|
|
10
10
|
const utils = require('@graphql-tools/utils');
|
|
11
11
|
const paramCase = require('param-case');
|
|
12
|
-
const
|
|
13
|
-
const crossUndiciFetch = require('cross-undici-fetch');
|
|
12
|
+
const fetch = require('@whatwg-node/fetch');
|
|
14
13
|
const colors = _interopDefault(require('ansi-colors'));
|
|
15
|
-
const
|
|
16
|
-
const fs = require('fs');
|
|
14
|
+
const server = require('@whatwg-node/server');
|
|
17
15
|
const titleCase = require('title-case');
|
|
18
16
|
|
|
19
17
|
function getOperationInfo(doc) {
|
|
@@ -62,7 +60,7 @@ function resolveVariable({ value, type, schema, }) {
|
|
|
62
60
|
return value;
|
|
63
61
|
}
|
|
64
62
|
if (type.kind === graphql.Kind.LIST_TYPE) {
|
|
65
|
-
return (Array.isArray(value) ? value : [value]).map(val => resolveVariable({
|
|
63
|
+
return (Array.isArray(value) ? value : [value]).map((val) => resolveVariable({
|
|
66
64
|
value: val,
|
|
67
65
|
type: type.type,
|
|
68
66
|
schema,
|
|
@@ -115,20 +113,17 @@ class SubscriptionManager {
|
|
|
115
113
|
}
|
|
116
114
|
start(event, contextValue) {
|
|
117
115
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
118
|
-
const id =
|
|
116
|
+
const id = fetch.crypto.randomUUID();
|
|
119
117
|
const name = event.subscription;
|
|
120
118
|
if (!this.operations.has(name)) {
|
|
121
119
|
throw new Error(`Subscription '${name}' is not available`);
|
|
122
120
|
}
|
|
123
|
-
const { document, operationName, variables } = this.operations.get(name);
|
|
124
121
|
logger.info(`[Subscription] Start ${id}`, event);
|
|
125
122
|
const result = yield this.execute({
|
|
126
123
|
id,
|
|
127
124
|
name,
|
|
128
125
|
url: event.url,
|
|
129
|
-
|
|
130
|
-
operationName,
|
|
131
|
-
variables,
|
|
126
|
+
variables: event.variables,
|
|
132
127
|
contextValue,
|
|
133
128
|
});
|
|
134
129
|
if (typeof result !== 'undefined') {
|
|
@@ -167,9 +162,9 @@ class SubscriptionManager {
|
|
|
167
162
|
}, contextValue);
|
|
168
163
|
});
|
|
169
164
|
}
|
|
170
|
-
execute({ id,
|
|
165
|
+
execute({ id, name, url, variables, contextValue, }) {
|
|
171
166
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
172
|
-
const variableNodes = this.operations.get(name)
|
|
167
|
+
const { document, operationName, variables: variableNodes } = this.operations.get(name);
|
|
173
168
|
const variableValues = variableNodes.reduce((values, variable) => {
|
|
174
169
|
const value = parseVariable({
|
|
175
170
|
value: variables[variable.variable.name.value],
|
|
@@ -179,9 +174,9 @@ class SubscriptionManager {
|
|
|
179
174
|
if (typeof value === 'undefined') {
|
|
180
175
|
return values;
|
|
181
176
|
}
|
|
182
|
-
return Object.assign(Object.assign({}, values), { [name]: value });
|
|
177
|
+
return Object.assign(Object.assign({}, values), { [variable.variable.name.value]: value });
|
|
183
178
|
}, {});
|
|
184
|
-
const execution = yield
|
|
179
|
+
const execution = yield this.sofa.subscribe({
|
|
185
180
|
schema: this.sofa.schema,
|
|
186
181
|
document,
|
|
187
182
|
operationName,
|
|
@@ -236,12 +231,12 @@ class SubscriptionManager {
|
|
|
236
231
|
}
|
|
237
232
|
const { url } = this.clients.get(id);
|
|
238
233
|
logger.info(`[Subscription] Trigger ${id}`);
|
|
239
|
-
const response = yield
|
|
234
|
+
const response = yield fetch.fetch(url, {
|
|
240
235
|
method: 'POST',
|
|
241
236
|
body: JSON.stringify(result),
|
|
242
237
|
headers: {
|
|
243
238
|
'Content-Type': 'application/json',
|
|
244
|
-
}
|
|
239
|
+
},
|
|
245
240
|
});
|
|
246
241
|
yield response.text();
|
|
247
242
|
});
|
|
@@ -277,7 +272,9 @@ class SubscriptionManager {
|
|
|
277
272
|
|
|
278
273
|
function createRouter(sofa) {
|
|
279
274
|
logger.debug('[Sofa] Creating router');
|
|
280
|
-
const router =
|
|
275
|
+
const router = ittyRouter.Router({
|
|
276
|
+
base: sofa.basePath,
|
|
277
|
+
});
|
|
281
278
|
const queryType = sofa.schema.getQueryType();
|
|
282
279
|
const mutationType = sofa.schema.getMutationType();
|
|
283
280
|
const subscriptionManager = new SubscriptionManager(sofa);
|
|
@@ -297,95 +294,80 @@ function createRouter(sofa) {
|
|
|
297
294
|
}
|
|
298
295
|
});
|
|
299
296
|
}
|
|
300
|
-
router.post('/webhook', (
|
|
301
|
-
const { subscription, variables, url } =
|
|
297
|
+
router.post('/webhook', (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
298
|
+
const { subscription, variables, url } = yield request.json();
|
|
302
299
|
try {
|
|
300
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
303
301
|
const result = yield subscriptionManager.start({
|
|
304
302
|
subscription,
|
|
305
303
|
variables,
|
|
306
304
|
url,
|
|
307
305
|
}, contextValue);
|
|
308
|
-
return {
|
|
309
|
-
type: 'result',
|
|
306
|
+
return new fetch.Response(JSON.stringify(result), {
|
|
310
307
|
status: 200,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
308
|
+
statusText: 'OK',
|
|
309
|
+
headers: {
|
|
310
|
+
'Content-Type': 'application/json',
|
|
311
|
+
},
|
|
312
|
+
});
|
|
314
313
|
}
|
|
315
314
|
catch (error) {
|
|
316
|
-
return {
|
|
317
|
-
type: 'error',
|
|
315
|
+
return new fetch.Response(JSON.stringify(error), {
|
|
318
316
|
status: 500,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
};
|
|
317
|
+
statusText: 'Subscription failed',
|
|
318
|
+
});
|
|
322
319
|
}
|
|
323
320
|
}));
|
|
324
|
-
router.post('/webhook/:id', (
|
|
325
|
-
|
|
321
|
+
router.post('/webhook/:id', (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
322
|
+
var _a;
|
|
323
|
+
const id = (_a = request.params) === null || _a === void 0 ? void 0 : _a.id;
|
|
324
|
+
const body = yield request.json();
|
|
326
325
|
const variables = body.variables;
|
|
327
326
|
try {
|
|
327
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
328
328
|
const result = yield subscriptionManager.update({
|
|
329
329
|
id,
|
|
330
330
|
variables,
|
|
331
331
|
}, contextValue);
|
|
332
|
-
return {
|
|
333
|
-
type: 'result',
|
|
332
|
+
return new fetch.Response(JSON.stringify(result), {
|
|
334
333
|
status: 200,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
334
|
+
statusText: 'OK',
|
|
335
|
+
headers: {
|
|
336
|
+
'Content-Type': 'application/json',
|
|
337
|
+
},
|
|
338
|
+
});
|
|
338
339
|
}
|
|
339
340
|
catch (error) {
|
|
340
|
-
return {
|
|
341
|
-
type: 'error',
|
|
341
|
+
return new fetch.Response(JSON.stringify(error), {
|
|
342
342
|
status: 500,
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
};
|
|
343
|
+
statusText: 'Subscription failed to update',
|
|
344
|
+
});
|
|
346
345
|
}
|
|
347
346
|
}));
|
|
348
|
-
router.delete('/webhook/:id', (
|
|
349
|
-
|
|
347
|
+
router.delete('/webhook/:id', (request) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
348
|
+
var _b;
|
|
349
|
+
const id = (_b = request.params) === null || _b === void 0 ? void 0 : _b.id;
|
|
350
350
|
try {
|
|
351
351
|
const result = yield subscriptionManager.stop(id);
|
|
352
|
-
return {
|
|
353
|
-
type: 'result',
|
|
352
|
+
return new fetch.Response(JSON.stringify(result), {
|
|
354
353
|
status: 200,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
354
|
+
statusText: 'OK',
|
|
355
|
+
headers: {
|
|
356
|
+
'Content-Type': 'application/json',
|
|
357
|
+
},
|
|
358
|
+
});
|
|
358
359
|
}
|
|
359
360
|
catch (error) {
|
|
360
|
-
return {
|
|
361
|
-
type: 'error',
|
|
361
|
+
return new fetch.Response(JSON.stringify(error), {
|
|
362
362
|
status: 500,
|
|
363
|
-
|
|
364
|
-
error,
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
}));
|
|
368
|
-
return ({ method, url, body, contextValue }) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
369
|
-
if (!url.startsWith(sofa.basePath)) {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
// trim base path and search
|
|
373
|
-
const [slicedUrl] = url.slice(sofa.basePath.length).split('?');
|
|
374
|
-
const trouterMethod = method.toUpperCase();
|
|
375
|
-
const obj = router.find(trouterMethod, slicedUrl);
|
|
376
|
-
for (const handler of obj.handlers) {
|
|
377
|
-
return yield handler({
|
|
378
|
-
url,
|
|
379
|
-
body,
|
|
380
|
-
params: obj.params,
|
|
381
|
-
contextValue,
|
|
363
|
+
statusText: 'Subscription failed to stop',
|
|
382
364
|
});
|
|
383
365
|
}
|
|
384
|
-
|
|
385
|
-
|
|
366
|
+
}));
|
|
367
|
+
return router;
|
|
386
368
|
}
|
|
387
369
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
388
|
-
var _a, _b, _c, _d;
|
|
370
|
+
var _a, _b, _c, _d, _e, _f;
|
|
389
371
|
logger.debug(`[Router] Creating ${fieldName} query`);
|
|
390
372
|
const queryType = sofa.schema.getQueryType();
|
|
391
373
|
const operationNode = utils.buildOperationNodeForField({
|
|
@@ -413,12 +395,14 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
413
395
|
path: (_c = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.path) !== null && _c !== void 0 ? _c : getPath(fieldName, isSingle && hasIdArgument),
|
|
414
396
|
responseStatus: (_d = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.responseStatus) !== null && _d !== void 0 ? _d : 200,
|
|
415
397
|
};
|
|
416
|
-
router[route.method
|
|
398
|
+
router[route.method](route.path, useHandler({ info, route, fieldName, sofa, operation }));
|
|
417
399
|
logger.debug(`[Router] ${fieldName} query available at ${route.method} ${route.path}`);
|
|
418
400
|
return {
|
|
419
401
|
document: operation,
|
|
420
402
|
path: route.path,
|
|
421
403
|
method: route.method.toUpperCase(),
|
|
404
|
+
tags: (_e = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) !== null && _e !== void 0 ? _e : [],
|
|
405
|
+
description: (_f = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _f !== void 0 ? _f : '',
|
|
422
406
|
};
|
|
423
407
|
}
|
|
424
408
|
function createMutationRoute({ sofa, router, fieldName, }) {
|
|
@@ -446,22 +430,37 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
446
430
|
responseStatus: (_d = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.responseStatus) !== null && _d !== void 0 ? _d : 200,
|
|
447
431
|
};
|
|
448
432
|
const { method, path } = route;
|
|
449
|
-
router[method
|
|
433
|
+
router[method](path, useHandler({ info, route, fieldName, sofa, operation }));
|
|
450
434
|
logger.debug(`[Router] ${fieldName} mutation available at ${method} ${path}`);
|
|
451
435
|
return {
|
|
452
436
|
document: operation,
|
|
453
437
|
path,
|
|
454
438
|
method,
|
|
439
|
+
tags: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) || [],
|
|
440
|
+
description: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) || '',
|
|
455
441
|
};
|
|
456
442
|
}
|
|
457
443
|
function useHandler(config) {
|
|
458
444
|
const { sofa, operation, fieldName } = config;
|
|
459
445
|
const info = config.info;
|
|
460
|
-
return (
|
|
446
|
+
return (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
447
|
+
var _a;
|
|
448
|
+
let body = {};
|
|
449
|
+
if (request.body != null) {
|
|
450
|
+
const strBody = yield request.text();
|
|
451
|
+
if (strBody) {
|
|
452
|
+
body = JSON.parse(strBody);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
461
455
|
const variableValues = info.variables.reduce((variables, variable) => {
|
|
462
456
|
const name = variable.variable.name.value;
|
|
463
457
|
const value = parseVariable({
|
|
464
|
-
value: pickParam({
|
|
458
|
+
value: pickParam({
|
|
459
|
+
url: request.url,
|
|
460
|
+
body,
|
|
461
|
+
params: request.params || {},
|
|
462
|
+
name,
|
|
463
|
+
}),
|
|
465
464
|
variable,
|
|
466
465
|
schema: sofa.schema,
|
|
467
466
|
});
|
|
@@ -470,36 +469,36 @@ function useHandler(config) {
|
|
|
470
469
|
}
|
|
471
470
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
472
471
|
}, {});
|
|
472
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
473
473
|
const result = yield sofa.execute({
|
|
474
474
|
schema: sofa.schema,
|
|
475
|
-
|
|
475
|
+
document: operation,
|
|
476
476
|
contextValue,
|
|
477
477
|
variableValues,
|
|
478
478
|
operationName: info.operation.name && info.operation.name.value,
|
|
479
479
|
});
|
|
480
480
|
if (result.errors) {
|
|
481
481
|
const defaultErrorHandler = (errors) => {
|
|
482
|
-
return {
|
|
483
|
-
type: 'error',
|
|
482
|
+
return new fetch.Response(errors[0], {
|
|
484
483
|
status: 500,
|
|
485
|
-
|
|
486
|
-
};
|
|
484
|
+
});
|
|
487
485
|
};
|
|
488
486
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
489
487
|
return errorHandler(result.errors);
|
|
490
488
|
}
|
|
491
|
-
return {
|
|
492
|
-
type: 'result',
|
|
489
|
+
return new fetch.Response(JSON.stringify((_a = result.data) === null || _a === void 0 ? void 0 : _a[fieldName]), {
|
|
493
490
|
status: config.route.responseStatus,
|
|
494
|
-
|
|
495
|
-
|
|
491
|
+
headers: {
|
|
492
|
+
'Content-Type': 'application/json',
|
|
493
|
+
},
|
|
494
|
+
});
|
|
496
495
|
});
|
|
497
496
|
}
|
|
498
497
|
function getPath(fieldName, hasId = false) {
|
|
499
498
|
return `/${convertName(fieldName)}${hasId ? '/:id' : ''}`;
|
|
500
499
|
}
|
|
501
500
|
function pickParam({ name, url, params, body, }) {
|
|
502
|
-
if (
|
|
501
|
+
if (name in params) {
|
|
503
502
|
return params[name];
|
|
504
503
|
}
|
|
505
504
|
const searchParams = new URLSearchParams(url.split('?')[1]);
|
|
@@ -519,9 +518,25 @@ function createSofa(config) {
|
|
|
519
518
|
const depthLimit = config.depthLimit || 1;
|
|
520
519
|
logger.debug(`[Sofa] models: ${models.join(', ')}`);
|
|
521
520
|
logger.debug(`[Sofa] ignore: ${ignore.join(', ')}`);
|
|
522
|
-
return Object.assign({ execute: graphql.
|
|
521
|
+
return Object.assign({ execute: graphql.execute,
|
|
522
|
+
subscribe: graphql.subscribe,
|
|
523
|
+
models,
|
|
523
524
|
ignore,
|
|
524
|
-
depthLimit
|
|
525
|
+
depthLimit,
|
|
526
|
+
contextFactory(serverContext) {
|
|
527
|
+
if (config.context != null) {
|
|
528
|
+
if (isContextFn(config.context)) {
|
|
529
|
+
return config.context(serverContext);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
return config.context;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return serverContext;
|
|
536
|
+
} }, config);
|
|
537
|
+
}
|
|
538
|
+
function isContextFn(context) {
|
|
539
|
+
return typeof context === 'function';
|
|
525
540
|
}
|
|
526
541
|
// Objects and Unions are the only things that are used to define return types
|
|
527
542
|
// and both might contain an ID
|
|
@@ -604,7 +619,7 @@ function mapToRef(type) {
|
|
|
604
619
|
return `#/components/schemas/${type}`;
|
|
605
620
|
}
|
|
606
621
|
|
|
607
|
-
function buildSchemaObjectFromType(type) {
|
|
622
|
+
function buildSchemaObjectFromType(type, opts) {
|
|
608
623
|
const required = [];
|
|
609
624
|
const properties = {};
|
|
610
625
|
const fields = type.getFields();
|
|
@@ -613,28 +628,27 @@ function buildSchemaObjectFromType(type) {
|
|
|
613
628
|
if (graphql.isNonNullType(field.type)) {
|
|
614
629
|
required.push(field.name);
|
|
615
630
|
}
|
|
616
|
-
properties[fieldName] = resolveField(field);
|
|
631
|
+
properties[fieldName] = resolveField(field, opts);
|
|
617
632
|
if (field.description) {
|
|
618
633
|
properties[fieldName].description = field.description;
|
|
619
634
|
}
|
|
620
635
|
}
|
|
621
636
|
return Object.assign(Object.assign(Object.assign({ type: 'object' }, (required.length ? { required } : {})), { properties }), (type.description ? { description: type.description } : {}));
|
|
622
637
|
}
|
|
623
|
-
function resolveField(field) {
|
|
624
|
-
return resolveFieldType(field.type);
|
|
638
|
+
function resolveField(field, opts) {
|
|
639
|
+
return resolveFieldType(field.type, opts);
|
|
625
640
|
}
|
|
626
641
|
// array -> [type]
|
|
627
642
|
// type -> $ref
|
|
628
643
|
// scalar -> swagger primitive
|
|
629
|
-
function resolveFieldType(type) {
|
|
630
|
-
var _a, _b;
|
|
644
|
+
function resolveFieldType(type, opts) {
|
|
631
645
|
if (graphql.isNonNullType(type)) {
|
|
632
|
-
return resolveFieldType(type.ofType);
|
|
646
|
+
return resolveFieldType(type.ofType, opts);
|
|
633
647
|
}
|
|
634
648
|
if (graphql.isListType(type)) {
|
|
635
649
|
return {
|
|
636
650
|
type: 'array',
|
|
637
|
-
items: resolveFieldType(type.ofType),
|
|
651
|
+
items: resolveFieldType(type.ofType, opts),
|
|
638
652
|
};
|
|
639
653
|
}
|
|
640
654
|
if (graphql.isObjectType(type)) {
|
|
@@ -643,14 +657,15 @@ function resolveFieldType(type) {
|
|
|
643
657
|
};
|
|
644
658
|
}
|
|
645
659
|
if (graphql.isScalarType(type)) {
|
|
646
|
-
return (mapToPrimitive(type.name) ||
|
|
660
|
+
return (mapToPrimitive(type.name) ||
|
|
661
|
+
opts.customScalars[type.name] || {
|
|
647
662
|
type: 'object',
|
|
648
663
|
});
|
|
649
664
|
}
|
|
650
665
|
if (graphql.isEnumType(type)) {
|
|
651
666
|
return {
|
|
652
667
|
type: 'string',
|
|
653
|
-
enum:
|
|
668
|
+
enum: type.getValues().map((value) => value.name),
|
|
654
669
|
};
|
|
655
670
|
}
|
|
656
671
|
return {
|
|
@@ -658,10 +673,12 @@ function resolveFieldType(type) {
|
|
|
658
673
|
};
|
|
659
674
|
}
|
|
660
675
|
|
|
661
|
-
function buildPathFromOperation({ url, schema, operation, useRequestBody, }) {
|
|
676
|
+
function buildPathFromOperation({ url, schema, operation, useRequestBody, tags, description, customScalars, }) {
|
|
662
677
|
const info = getOperationInfo(operation);
|
|
663
|
-
const
|
|
664
|
-
return Object.assign(Object.assign({
|
|
678
|
+
const summary = resolveSummary(schema, info.operation);
|
|
679
|
+
return Object.assign(Object.assign({ tags,
|
|
680
|
+
description,
|
|
681
|
+
summary, operationId: info.name }, (useRequestBody
|
|
665
682
|
? {
|
|
666
683
|
requestBody: {
|
|
667
684
|
content: {
|
|
@@ -675,12 +692,13 @@ function buildPathFromOperation({ url, schema, operation, useRequestBody, }) {
|
|
|
675
692
|
parameters: resolveParameters(url, info.operation.variableDefinitions),
|
|
676
693
|
})), { responses: {
|
|
677
694
|
200: {
|
|
678
|
-
description,
|
|
695
|
+
description: summary,
|
|
679
696
|
content: {
|
|
680
697
|
'application/json': {
|
|
681
698
|
schema: resolveResponse({
|
|
682
699
|
schema,
|
|
683
700
|
operation: info.operation,
|
|
701
|
+
customScalars,
|
|
684
702
|
}),
|
|
685
703
|
},
|
|
686
704
|
},
|
|
@@ -706,7 +724,7 @@ function resolveRequestBody(variables) {
|
|
|
706
724
|
}
|
|
707
725
|
const properties = {};
|
|
708
726
|
const required = [];
|
|
709
|
-
variables.forEach(variable => {
|
|
727
|
+
variables.forEach((variable) => {
|
|
710
728
|
if (variable.type.kind === graphql.Kind.NON_NULL_TYPE) {
|
|
711
729
|
required.push(variable.variable.name.value);
|
|
712
730
|
}
|
|
@@ -732,26 +750,26 @@ function resolveParamSchema(type) {
|
|
|
732
750
|
$ref: mapToRef(type.name.value),
|
|
733
751
|
});
|
|
734
752
|
}
|
|
735
|
-
function resolveResponse({ schema, operation, }) {
|
|
753
|
+
function resolveResponse({ schema, operation, customScalars, }) {
|
|
736
754
|
const operationType = operation.operation;
|
|
737
755
|
const rootField = operation.selectionSet.selections[0];
|
|
738
756
|
if (rootField.kind === graphql.Kind.FIELD) {
|
|
739
757
|
if (operationType === 'query') {
|
|
740
758
|
const queryType = schema.getQueryType();
|
|
741
759
|
const field = queryType.getFields()[rootField.name.value];
|
|
742
|
-
return resolveFieldType(field.type);
|
|
760
|
+
return resolveFieldType(field.type, { customScalars });
|
|
743
761
|
}
|
|
744
762
|
if (operationType === 'mutation') {
|
|
745
763
|
const mutationType = schema.getMutationType();
|
|
746
764
|
const field = mutationType.getFields()[rootField.name.value];
|
|
747
|
-
return resolveFieldType(field.type);
|
|
765
|
+
return resolveFieldType(field.type, { customScalars });
|
|
748
766
|
}
|
|
749
767
|
}
|
|
750
768
|
}
|
|
751
769
|
function isInPath(url, param) {
|
|
752
770
|
return url.indexOf(`{${param}}`) !== -1;
|
|
753
771
|
}
|
|
754
|
-
function
|
|
772
|
+
function resolveSummary(schema, operation) {
|
|
755
773
|
const selection = operation.selectionSet.selections[0];
|
|
756
774
|
const fieldName = selection.name.value;
|
|
757
775
|
const typeDefinition = schema.getType(titleCase.titleCase(operation.operation));
|
|
@@ -762,7 +780,7 @@ function resolveDescription(schema, operation) {
|
|
|
762
780
|
if (!isObjectTypeDefinitionNode(definitionNode)) {
|
|
763
781
|
return '';
|
|
764
782
|
}
|
|
765
|
-
const fieldNode = definitionNode.fields.find(field => field.name.value === fieldName);
|
|
783
|
+
const fieldNode = definitionNode.fields.find((field) => field.name.value === fieldName);
|
|
766
784
|
const descriptionDefinition = fieldNode && fieldNode.description;
|
|
767
785
|
return descriptionDefinition && descriptionDefinition.value
|
|
768
786
|
? descriptionDefinition.value
|
|
@@ -772,13 +790,13 @@ function isObjectTypeDefinitionNode(node) {
|
|
|
772
790
|
return node.kind === graphql.Kind.OBJECT_TYPE_DEFINITION;
|
|
773
791
|
}
|
|
774
792
|
|
|
775
|
-
function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
793
|
+
function OpenAPI({ schema, info, servers, components, security, tags, customScalars = {}, }) {
|
|
776
794
|
const types = schema.getTypeMap();
|
|
777
795
|
const swagger = {
|
|
778
796
|
openapi: '3.0.0',
|
|
779
797
|
info,
|
|
780
798
|
servers,
|
|
781
|
-
tags,
|
|
799
|
+
tags: [],
|
|
782
800
|
paths: {},
|
|
783
801
|
components: {
|
|
784
802
|
schemas: {},
|
|
@@ -788,7 +806,9 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
788
806
|
const type = types[typeName];
|
|
789
807
|
if ((graphql.isObjectType(type) || graphql.isInputObjectType(type)) &&
|
|
790
808
|
!graphql.isIntrospectionType(type)) {
|
|
791
|
-
swagger.components.schemas[typeName] = buildSchemaObjectFromType(type
|
|
809
|
+
swagger.components.schemas[typeName] = buildSchemaObjectFromType(type, {
|
|
810
|
+
customScalars,
|
|
811
|
+
});
|
|
792
812
|
}
|
|
793
813
|
}
|
|
794
814
|
if (components) {
|
|
@@ -797,6 +817,9 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
797
817
|
if (security) {
|
|
798
818
|
swagger.security = security;
|
|
799
819
|
}
|
|
820
|
+
if (tags) {
|
|
821
|
+
swagger.tags = tags;
|
|
822
|
+
}
|
|
800
823
|
return {
|
|
801
824
|
addRoute(info, config) {
|
|
802
825
|
const basePath = (config === null || config === void 0 ? void 0 : config.basePath) || '';
|
|
@@ -805,94 +828,26 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
805
828
|
if (!swagger.paths[path]) {
|
|
806
829
|
swagger.paths[path] = {};
|
|
807
830
|
}
|
|
808
|
-
swagger.paths[path]
|
|
831
|
+
const pathsObj = swagger.paths[path];
|
|
832
|
+
pathsObj[info.method.toLowerCase()] = buildPathFromOperation({
|
|
809
833
|
url: path,
|
|
810
834
|
operation: info.document,
|
|
811
835
|
schema,
|
|
812
836
|
useRequestBody: ['POST', 'PUT', 'PATCH'].includes(info.method),
|
|
837
|
+
tags: info.tags || [],
|
|
838
|
+
description: info.description || '',
|
|
839
|
+
customScalars,
|
|
813
840
|
});
|
|
814
841
|
},
|
|
815
842
|
get() {
|
|
816
843
|
return swagger;
|
|
817
844
|
},
|
|
818
|
-
save(filepath) {
|
|
819
|
-
const isJSON = /\.json$/i;
|
|
820
|
-
const isYAML = /.ya?ml$/i;
|
|
821
|
-
if (isJSON.test(filepath)) {
|
|
822
|
-
writeOutput(filepath, JSON.stringify(swagger, null, 2));
|
|
823
|
-
}
|
|
824
|
-
else if (isYAML.test(filepath)) {
|
|
825
|
-
writeOutput(filepath, jsYaml.dump(swagger));
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
throw new Error('We only support JSON and YAML files');
|
|
829
|
-
}
|
|
830
|
-
},
|
|
831
845
|
};
|
|
832
|
-
}
|
|
833
|
-
function writeOutput(filepath, contents) {
|
|
834
|
-
fs.writeFileSync(filepath, contents, {
|
|
835
|
-
encoding: 'utf-8',
|
|
836
|
-
});
|
|
837
846
|
}
|
|
838
847
|
|
|
839
|
-
function
|
|
840
|
-
return
|
|
841
|
-
}
|
|
842
|
-
function useSofa(_a) {
|
|
843
|
-
var { context } = _a, config = tslib.__rest(_a, ["context"]);
|
|
844
|
-
const invokeSofa = createSofaRouter(config);
|
|
845
|
-
return (req, res, next) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
846
|
-
var _b;
|
|
847
|
-
try {
|
|
848
|
-
let contextValue = { req };
|
|
849
|
-
if (context) {
|
|
850
|
-
if (typeof context === 'function') {
|
|
851
|
-
contextValue = yield context({ req, res });
|
|
852
|
-
}
|
|
853
|
-
else {
|
|
854
|
-
contextValue = context;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
const response = yield invokeSofa({
|
|
858
|
-
method: req.method,
|
|
859
|
-
url: (_b = req.originalUrl) !== null && _b !== void 0 ? _b : req.url,
|
|
860
|
-
body: req.body,
|
|
861
|
-
contextValue,
|
|
862
|
-
});
|
|
863
|
-
if (response == null) {
|
|
864
|
-
next();
|
|
865
|
-
}
|
|
866
|
-
else {
|
|
867
|
-
const headers = {
|
|
868
|
-
'Content-Type': 'application/json',
|
|
869
|
-
};
|
|
870
|
-
if (response.statusMessage) {
|
|
871
|
-
res.writeHead(response.status, response.statusMessage, headers);
|
|
872
|
-
}
|
|
873
|
-
else {
|
|
874
|
-
res.writeHead(response.status, headers);
|
|
875
|
-
}
|
|
876
|
-
if (response.type === 'result') {
|
|
877
|
-
res.end(JSON.stringify(response.body));
|
|
878
|
-
}
|
|
879
|
-
if (response.type === 'error') {
|
|
880
|
-
res.end(JSON.stringify(response.error));
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
catch (error) {
|
|
885
|
-
next(error);
|
|
886
|
-
}
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
function createSofaRouter(config) {
|
|
890
|
-
const sofa = createSofa(config);
|
|
891
|
-
const router = createRouter(sofa);
|
|
892
|
-
return router;
|
|
848
|
+
function useSofa(config) {
|
|
849
|
+
return server.createServerAdapter(createRouter(createSofa(config)));
|
|
893
850
|
}
|
|
894
851
|
|
|
895
852
|
exports.OpenAPI = OpenAPI;
|
|
896
|
-
exports.createSofaRouter = createSofaRouter;
|
|
897
|
-
exports.isContextFn = isContextFn;
|
|
898
853
|
exports.useSofa = useSofa;
|