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