sofa-api 0.12.0-alpha.0 → 0.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.
- package/README.md +61 -1
- package/index.js +64 -47
- package/index.mjs +65 -48
- package/open-api/index.d.ts +17 -6
- package/open-api/operations.d.ts +6 -2
- package/open-api/types.d.ts +6 -2
- package/package.json +4 -4
- package/sofa.d.ts +2 -0
- package/types.d.ts +7 -4
- package/open-api/interfaces.d.ts +0 -325
package/README.md
CHANGED
|
@@ -150,7 +150,7 @@ Sofa allows you to cutomize the http method, path and response status. For examp
|
|
|
150
150
|
```typescript
|
|
151
151
|
api.use(
|
|
152
152
|
'/api',
|
|
153
|
-
|
|
153
|
+
useSofa({
|
|
154
154
|
schema,
|
|
155
155
|
routes: {
|
|
156
156
|
'Query.feed': { method: 'POST' },
|
|
@@ -341,6 +341,66 @@ app.use(
|
|
|
341
341
|
writeFileSync('./swagger.json', JSON.stringify(openApi.get(), null, 2));
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
+
OpenAPI (Swagger) with custom tags, summary and description
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
const openApi = OpenAPI({
|
|
348
|
+
schema,
|
|
349
|
+
info: {
|
|
350
|
+
title: 'Example API',
|
|
351
|
+
version: '3.0.0',
|
|
352
|
+
},
|
|
353
|
+
tags: [
|
|
354
|
+
{
|
|
355
|
+
name: 'Book',
|
|
356
|
+
description: 'Book related operations',
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: 'Author',
|
|
360
|
+
description: 'Author related operations',
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
});
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
@Resolver(Book)
|
|
368
|
+
export class BookResolver {
|
|
369
|
+
@Query(() => Book, { description: 'Get book by id' }) // custom summary tag
|
|
370
|
+
getBookById(@Arg('id', () => Int) id: number) {
|
|
371
|
+
return 'book';
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
const routes: SofaConfig['routes'] = {
|
|
378
|
+
'Query.getBookById': {
|
|
379
|
+
method: 'POST',
|
|
380
|
+
path: '/book/:id',
|
|
381
|
+
tags: ['Book'],
|
|
382
|
+
description: 'This is a custom detailed description for getBookById',
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const createSofaMiddleware = (
|
|
387
|
+
schema: GraphQLSchema,
|
|
388
|
+
openApi: ReturnType<typeof OpenAPI>,
|
|
389
|
+
basePath = ''
|
|
390
|
+
): ReturnType<typeof useSofa> => {
|
|
391
|
+
return useSofa({
|
|
392
|
+
routes,
|
|
393
|
+
basePath,
|
|
394
|
+
schema,
|
|
395
|
+
onRoute(info) {
|
|
396
|
+
openApi.addRoute(info, { basePath });
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
// writes every recorder route
|
|
401
|
+
openApi.save('./swagger.yml');
|
|
402
|
+
```
|
|
403
|
+
|
|
344
404
|
## License
|
|
345
405
|
|
|
346
406
|
[MIT](https://github.com/Urigo/sofa/blob/master/LICENSE) © Uri Goldshtein
|
package/index.js
CHANGED
|
@@ -9,7 +9,6 @@ const graphql = require('graphql');
|
|
|
9
9
|
const ittyRouter = require('itty-router');
|
|
10
10
|
const utils = require('@graphql-tools/utils');
|
|
11
11
|
const paramCase = require('param-case');
|
|
12
|
-
const uuid = require('uuid');
|
|
13
12
|
const fetch = require('@whatwg-node/fetch');
|
|
14
13
|
const colors = _interopDefault(require('ansi-colors'));
|
|
15
14
|
const server = require('@whatwg-node/server');
|
|
@@ -114,20 +113,17 @@ class SubscriptionManager {
|
|
|
114
113
|
}
|
|
115
114
|
start(event, contextValue) {
|
|
116
115
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
117
|
-
const id =
|
|
116
|
+
const id = fetch.crypto.randomUUID();
|
|
118
117
|
const name = event.subscription;
|
|
119
118
|
if (!this.operations.has(name)) {
|
|
120
119
|
throw new Error(`Subscription '${name}' is not available`);
|
|
121
120
|
}
|
|
122
|
-
const { document, operationName, variables } = this.operations.get(name);
|
|
123
121
|
logger.info(`[Subscription] Start ${id}`, event);
|
|
124
122
|
const result = yield this.execute({
|
|
125
123
|
id,
|
|
126
124
|
name,
|
|
127
125
|
url: event.url,
|
|
128
|
-
|
|
129
|
-
operationName,
|
|
130
|
-
variables,
|
|
126
|
+
variables: event.variables,
|
|
131
127
|
contextValue,
|
|
132
128
|
});
|
|
133
129
|
if (typeof result !== 'undefined') {
|
|
@@ -166,9 +162,9 @@ class SubscriptionManager {
|
|
|
166
162
|
}, contextValue);
|
|
167
163
|
});
|
|
168
164
|
}
|
|
169
|
-
execute({ id,
|
|
165
|
+
execute({ id, name, url, variables, contextValue, }) {
|
|
170
166
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
171
|
-
const variableNodes = this.operations.get(name)
|
|
167
|
+
const { document, operationName, variables: variableNodes } = this.operations.get(name);
|
|
172
168
|
const variableValues = variableNodes.reduce((values, variable) => {
|
|
173
169
|
const value = parseVariable({
|
|
174
170
|
value: variables[variable.variable.name.value],
|
|
@@ -178,7 +174,7 @@ class SubscriptionManager {
|
|
|
178
174
|
if (typeof value === 'undefined') {
|
|
179
175
|
return values;
|
|
180
176
|
}
|
|
181
|
-
return Object.assign(Object.assign({}, values), { [name]: value });
|
|
177
|
+
return Object.assign(Object.assign({}, values), { [variable.variable.name.value]: value });
|
|
182
178
|
}, {});
|
|
183
179
|
const execution = yield this.sofa.subscribe({
|
|
184
180
|
schema: this.sofa.schema,
|
|
@@ -301,12 +297,11 @@ function createRouter(sofa) {
|
|
|
301
297
|
router.post('/webhook', (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
302
298
|
const { subscription, variables, url } = yield request.json();
|
|
303
299
|
try {
|
|
304
|
-
const contextValue = yield sofa.contextFactory(serverContext);
|
|
305
300
|
const result = yield subscriptionManager.start({
|
|
306
301
|
subscription,
|
|
307
302
|
variables,
|
|
308
303
|
url,
|
|
309
|
-
},
|
|
304
|
+
}, Object.assign(Object.assign({}, serverContext), { request }));
|
|
310
305
|
return new fetch.Response(JSON.stringify(result), {
|
|
311
306
|
status: 200,
|
|
312
307
|
statusText: 'OK',
|
|
@@ -328,7 +323,8 @@ function createRouter(sofa) {
|
|
|
328
323
|
const body = yield request.json();
|
|
329
324
|
const variables = body.variables;
|
|
330
325
|
try {
|
|
331
|
-
const
|
|
326
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
327
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
332
328
|
const result = yield subscriptionManager.update({
|
|
333
329
|
id,
|
|
334
330
|
variables,
|
|
@@ -371,7 +367,7 @@ function createRouter(sofa) {
|
|
|
371
367
|
return router;
|
|
372
368
|
}
|
|
373
369
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
374
|
-
var _a, _b, _c, _d;
|
|
370
|
+
var _a, _b, _c, _d, _e, _f;
|
|
375
371
|
logger.debug(`[Router] Creating ${fieldName} query`);
|
|
376
372
|
const queryType = sofa.schema.getQueryType();
|
|
377
373
|
const operationNode = utils.buildOperationNodeForField({
|
|
@@ -405,6 +401,8 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
405
401
|
document: operation,
|
|
406
402
|
path: route.path,
|
|
407
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 : '',
|
|
408
406
|
};
|
|
409
407
|
}
|
|
410
408
|
function createMutationRoute({ sofa, router, fieldName, }) {
|
|
@@ -438,6 +436,8 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
438
436
|
document: operation,
|
|
439
437
|
path,
|
|
440
438
|
method,
|
|
439
|
+
tags: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) || [],
|
|
440
|
+
description: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) || '',
|
|
441
441
|
};
|
|
442
442
|
}
|
|
443
443
|
function useHandler(config) {
|
|
@@ -469,7 +469,8 @@ function useHandler(config) {
|
|
|
469
469
|
}
|
|
470
470
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
471
471
|
}, {});
|
|
472
|
-
const
|
|
472
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
473
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
473
474
|
const result = yield sofa.execute({
|
|
474
475
|
schema: sofa.schema,
|
|
475
476
|
document: operation,
|
|
@@ -479,8 +480,9 @@ function useHandler(config) {
|
|
|
479
480
|
});
|
|
480
481
|
if (result.errors) {
|
|
481
482
|
const defaultErrorHandler = (errors) => {
|
|
482
|
-
return new fetch.Response(errors[0], {
|
|
483
|
+
return new fetch.Response(JSON.stringify(errors[0]), {
|
|
483
484
|
status: 500,
|
|
485
|
+
headers: { 'Content-Type': 'application/json' },
|
|
484
486
|
});
|
|
485
487
|
};
|
|
486
488
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
@@ -619,7 +621,7 @@ function mapToRef(type) {
|
|
|
619
621
|
return `#/components/schemas/${type}`;
|
|
620
622
|
}
|
|
621
623
|
|
|
622
|
-
function buildSchemaObjectFromType(type) {
|
|
624
|
+
function buildSchemaObjectFromType(type, opts) {
|
|
623
625
|
const required = [];
|
|
624
626
|
const properties = {};
|
|
625
627
|
const fields = type.getFields();
|
|
@@ -628,28 +630,27 @@ function buildSchemaObjectFromType(type) {
|
|
|
628
630
|
if (graphql.isNonNullType(field.type)) {
|
|
629
631
|
required.push(field.name);
|
|
630
632
|
}
|
|
631
|
-
properties[fieldName] = resolveField(field);
|
|
633
|
+
properties[fieldName] = resolveField(field, opts);
|
|
632
634
|
if (field.description) {
|
|
633
635
|
properties[fieldName].description = field.description;
|
|
634
636
|
}
|
|
635
637
|
}
|
|
636
638
|
return Object.assign(Object.assign(Object.assign({ type: 'object' }, (required.length ? { required } : {})), { properties }), (type.description ? { description: type.description } : {}));
|
|
637
639
|
}
|
|
638
|
-
function resolveField(field) {
|
|
639
|
-
return resolveFieldType(field.type);
|
|
640
|
+
function resolveField(field, opts) {
|
|
641
|
+
return resolveFieldType(field.type, opts);
|
|
640
642
|
}
|
|
641
643
|
// array -> [type]
|
|
642
644
|
// type -> $ref
|
|
643
645
|
// scalar -> swagger primitive
|
|
644
|
-
function resolveFieldType(type) {
|
|
645
|
-
var _a, _b;
|
|
646
|
+
function resolveFieldType(type, opts) {
|
|
646
647
|
if (graphql.isNonNullType(type)) {
|
|
647
|
-
return resolveFieldType(type.ofType);
|
|
648
|
+
return resolveFieldType(type.ofType, opts);
|
|
648
649
|
}
|
|
649
650
|
if (graphql.isListType(type)) {
|
|
650
651
|
return {
|
|
651
652
|
type: 'array',
|
|
652
|
-
items: resolveFieldType(type.ofType),
|
|
653
|
+
items: resolveFieldType(type.ofType, opts),
|
|
653
654
|
};
|
|
654
655
|
}
|
|
655
656
|
if (graphql.isObjectType(type)) {
|
|
@@ -658,14 +659,15 @@ function resolveFieldType(type) {
|
|
|
658
659
|
};
|
|
659
660
|
}
|
|
660
661
|
if (graphql.isScalarType(type)) {
|
|
661
|
-
return (mapToPrimitive(type.name) ||
|
|
662
|
+
return (mapToPrimitive(type.name) ||
|
|
663
|
+
opts.customScalars[type.name] || {
|
|
662
664
|
type: 'object',
|
|
663
665
|
});
|
|
664
666
|
}
|
|
665
667
|
if (graphql.isEnumType(type)) {
|
|
666
668
|
return {
|
|
667
669
|
type: 'string',
|
|
668
|
-
enum:
|
|
670
|
+
enum: type.getValues().map((value) => value.name),
|
|
669
671
|
};
|
|
670
672
|
}
|
|
671
673
|
return {
|
|
@@ -673,36 +675,41 @@ function resolveFieldType(type) {
|
|
|
673
675
|
};
|
|
674
676
|
}
|
|
675
677
|
|
|
676
|
-
function buildPathFromOperation({ url, schema, operation, useRequestBody, }) {
|
|
678
|
+
function buildPathFromOperation({ url, schema, operation, useRequestBody, tags, description, customScalars, }) {
|
|
677
679
|
const info = getOperationInfo(operation);
|
|
678
|
-
const
|
|
679
|
-
return Object.assign(Object.assign({
|
|
680
|
+
const summary = resolveSummary(schema, info.operation);
|
|
681
|
+
return Object.assign(Object.assign({ tags,
|
|
682
|
+
description,
|
|
683
|
+
summary, operationId: info.name }, (useRequestBody
|
|
680
684
|
? {
|
|
681
685
|
requestBody: {
|
|
682
686
|
content: {
|
|
683
687
|
'application/json': {
|
|
684
|
-
schema: resolveRequestBody(info.operation.variableDefinitions
|
|
688
|
+
schema: resolveRequestBody(info.operation.variableDefinitions, {
|
|
689
|
+
customScalars,
|
|
690
|
+
}),
|
|
685
691
|
},
|
|
686
692
|
},
|
|
687
693
|
},
|
|
688
694
|
}
|
|
689
695
|
: {
|
|
690
|
-
parameters: resolveParameters(url, info.operation.variableDefinitions),
|
|
696
|
+
parameters: resolveParameters(url, info.operation.variableDefinitions, { customScalars }),
|
|
691
697
|
})), { responses: {
|
|
692
698
|
200: {
|
|
693
|
-
description,
|
|
699
|
+
description: summary,
|
|
694
700
|
content: {
|
|
695
701
|
'application/json': {
|
|
696
702
|
schema: resolveResponse({
|
|
697
703
|
schema,
|
|
698
704
|
operation: info.operation,
|
|
705
|
+
customScalars,
|
|
699
706
|
}),
|
|
700
707
|
},
|
|
701
708
|
},
|
|
702
709
|
},
|
|
703
710
|
} });
|
|
704
711
|
}
|
|
705
|
-
function resolveParameters(url, variables) {
|
|
712
|
+
function resolveParameters(url, variables, opts) {
|
|
706
713
|
if (!variables) {
|
|
707
714
|
return [];
|
|
708
715
|
}
|
|
@@ -711,11 +718,11 @@ function resolveParameters(url, variables) {
|
|
|
711
718
|
in: isInPath(url, variable.variable.name.value) ? 'path' : 'query',
|
|
712
719
|
name: variable.variable.name.value,
|
|
713
720
|
required: variable.type.kind === graphql.Kind.NON_NULL_TYPE,
|
|
714
|
-
schema: resolveParamSchema(variable.type),
|
|
721
|
+
schema: resolveParamSchema(variable.type, opts),
|
|
715
722
|
};
|
|
716
723
|
});
|
|
717
724
|
}
|
|
718
|
-
function resolveRequestBody(variables) {
|
|
725
|
+
function resolveRequestBody(variables, opts) {
|
|
719
726
|
if (!variables) {
|
|
720
727
|
return {};
|
|
721
728
|
}
|
|
@@ -725,48 +732,49 @@ function resolveRequestBody(variables) {
|
|
|
725
732
|
if (variable.type.kind === graphql.Kind.NON_NULL_TYPE) {
|
|
726
733
|
required.push(variable.variable.name.value);
|
|
727
734
|
}
|
|
728
|
-
properties[variable.variable.name.value] = resolveParamSchema(variable.type);
|
|
735
|
+
properties[variable.variable.name.value] = resolveParamSchema(variable.type, opts);
|
|
729
736
|
});
|
|
730
737
|
return Object.assign({ type: 'object', properties }, (required.length ? { required } : {}));
|
|
731
738
|
}
|
|
732
739
|
// array -> [type]
|
|
733
740
|
// type -> $ref
|
|
734
741
|
// scalar -> swagger primitive
|
|
735
|
-
function resolveParamSchema(type) {
|
|
742
|
+
function resolveParamSchema(type, opts) {
|
|
736
743
|
if (type.kind === graphql.Kind.NON_NULL_TYPE) {
|
|
737
|
-
return resolveParamSchema(type.type);
|
|
744
|
+
return resolveParamSchema(type.type, opts);
|
|
738
745
|
}
|
|
739
746
|
if (type.kind === graphql.Kind.LIST_TYPE) {
|
|
740
747
|
return {
|
|
741
748
|
type: 'array',
|
|
742
|
-
items: resolveParamSchema(type.type),
|
|
749
|
+
items: resolveParamSchema(type.type, opts),
|
|
743
750
|
};
|
|
744
751
|
}
|
|
745
752
|
const primitive = mapToPrimitive(type.name.value);
|
|
746
|
-
return (primitive ||
|
|
753
|
+
return (primitive ||
|
|
754
|
+
opts.customScalars[type.name.value] || {
|
|
747
755
|
$ref: mapToRef(type.name.value),
|
|
748
756
|
});
|
|
749
757
|
}
|
|
750
|
-
function resolveResponse({ schema, operation, }) {
|
|
758
|
+
function resolveResponse({ schema, operation, customScalars, }) {
|
|
751
759
|
const operationType = operation.operation;
|
|
752
760
|
const rootField = operation.selectionSet.selections[0];
|
|
753
761
|
if (rootField.kind === graphql.Kind.FIELD) {
|
|
754
762
|
if (operationType === 'query') {
|
|
755
763
|
const queryType = schema.getQueryType();
|
|
756
764
|
const field = queryType.getFields()[rootField.name.value];
|
|
757
|
-
return resolveFieldType(field.type);
|
|
765
|
+
return resolveFieldType(field.type, { customScalars });
|
|
758
766
|
}
|
|
759
767
|
if (operationType === 'mutation') {
|
|
760
768
|
const mutationType = schema.getMutationType();
|
|
761
769
|
const field = mutationType.getFields()[rootField.name.value];
|
|
762
|
-
return resolveFieldType(field.type);
|
|
770
|
+
return resolveFieldType(field.type, { customScalars });
|
|
763
771
|
}
|
|
764
772
|
}
|
|
765
773
|
}
|
|
766
774
|
function isInPath(url, param) {
|
|
767
775
|
return url.indexOf(`{${param}}`) !== -1;
|
|
768
776
|
}
|
|
769
|
-
function
|
|
777
|
+
function resolveSummary(schema, operation) {
|
|
770
778
|
const selection = operation.selectionSet.selections[0];
|
|
771
779
|
const fieldName = selection.name.value;
|
|
772
780
|
const typeDefinition = schema.getType(titleCase.titleCase(operation.operation));
|
|
@@ -787,13 +795,13 @@ function isObjectTypeDefinitionNode(node) {
|
|
|
787
795
|
return node.kind === graphql.Kind.OBJECT_TYPE_DEFINITION;
|
|
788
796
|
}
|
|
789
797
|
|
|
790
|
-
function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
798
|
+
function OpenAPI({ schema, info, servers, components, security, tags, customScalars = {}, }) {
|
|
791
799
|
const types = schema.getTypeMap();
|
|
792
800
|
const swagger = {
|
|
793
801
|
openapi: '3.0.0',
|
|
794
802
|
info,
|
|
795
803
|
servers,
|
|
796
|
-
tags,
|
|
804
|
+
tags: [],
|
|
797
805
|
paths: {},
|
|
798
806
|
components: {
|
|
799
807
|
schemas: {},
|
|
@@ -803,7 +811,9 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
803
811
|
const type = types[typeName];
|
|
804
812
|
if ((graphql.isObjectType(type) || graphql.isInputObjectType(type)) &&
|
|
805
813
|
!graphql.isIntrospectionType(type)) {
|
|
806
|
-
swagger.components.schemas[typeName] = buildSchemaObjectFromType(type
|
|
814
|
+
swagger.components.schemas[typeName] = buildSchemaObjectFromType(type, {
|
|
815
|
+
customScalars,
|
|
816
|
+
});
|
|
807
817
|
}
|
|
808
818
|
}
|
|
809
819
|
if (components) {
|
|
@@ -812,6 +822,9 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
812
822
|
if (security) {
|
|
813
823
|
swagger.security = security;
|
|
814
824
|
}
|
|
825
|
+
if (tags) {
|
|
826
|
+
swagger.tags = tags;
|
|
827
|
+
}
|
|
815
828
|
return {
|
|
816
829
|
addRoute(info, config) {
|
|
817
830
|
const basePath = (config === null || config === void 0 ? void 0 : config.basePath) || '';
|
|
@@ -820,11 +833,15 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
820
833
|
if (!swagger.paths[path]) {
|
|
821
834
|
swagger.paths[path] = {};
|
|
822
835
|
}
|
|
823
|
-
swagger.paths[path]
|
|
836
|
+
const pathsObj = swagger.paths[path];
|
|
837
|
+
pathsObj[info.method.toLowerCase()] = buildPathFromOperation({
|
|
824
838
|
url: path,
|
|
825
839
|
operation: info.document,
|
|
826
840
|
schema,
|
|
827
841
|
useRequestBody: ['POST', 'PUT', 'PATCH'].includes(info.method),
|
|
842
|
+
tags: info.tags || [],
|
|
843
|
+
description: info.description || '',
|
|
844
|
+
customScalars,
|
|
828
845
|
});
|
|
829
846
|
},
|
|
830
847
|
get() {
|
package/index.mjs
CHANGED
|
@@ -3,8 +3,7 @@ import { getOperationAST, Kind, isScalarType, isEqualType, GraphQLBoolean, isInp
|
|
|
3
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, Response } from '@whatwg-node/fetch';
|
|
6
|
+
import { crypto, fetch, Response } from '@whatwg-node/fetch';
|
|
8
7
|
import colors from 'ansi-colors';
|
|
9
8
|
import { createServerAdapter } from '@whatwg-node/server';
|
|
10
9
|
import { titleCase } from 'title-case';
|
|
@@ -108,20 +107,17 @@ class SubscriptionManager {
|
|
|
108
107
|
}
|
|
109
108
|
start(event, contextValue) {
|
|
110
109
|
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
-
const id =
|
|
110
|
+
const id = crypto.randomUUID();
|
|
112
111
|
const name = event.subscription;
|
|
113
112
|
if (!this.operations.has(name)) {
|
|
114
113
|
throw new Error(`Subscription '${name}' is not available`);
|
|
115
114
|
}
|
|
116
|
-
const { document, operationName, variables } = this.operations.get(name);
|
|
117
115
|
logger.info(`[Subscription] Start ${id}`, event);
|
|
118
116
|
const result = yield this.execute({
|
|
119
117
|
id,
|
|
120
118
|
name,
|
|
121
119
|
url: event.url,
|
|
122
|
-
|
|
123
|
-
operationName,
|
|
124
|
-
variables,
|
|
120
|
+
variables: event.variables,
|
|
125
121
|
contextValue,
|
|
126
122
|
});
|
|
127
123
|
if (typeof result !== 'undefined') {
|
|
@@ -160,9 +156,9 @@ class SubscriptionManager {
|
|
|
160
156
|
}, contextValue);
|
|
161
157
|
});
|
|
162
158
|
}
|
|
163
|
-
execute({ id,
|
|
159
|
+
execute({ id, name, url, variables, contextValue, }) {
|
|
164
160
|
return __awaiter(this, void 0, void 0, function* () {
|
|
165
|
-
const variableNodes = this.operations.get(name)
|
|
161
|
+
const { document, operationName, variables: variableNodes } = this.operations.get(name);
|
|
166
162
|
const variableValues = variableNodes.reduce((values, variable) => {
|
|
167
163
|
const value = parseVariable({
|
|
168
164
|
value: variables[variable.variable.name.value],
|
|
@@ -172,7 +168,7 @@ class SubscriptionManager {
|
|
|
172
168
|
if (typeof value === 'undefined') {
|
|
173
169
|
return values;
|
|
174
170
|
}
|
|
175
|
-
return Object.assign(Object.assign({}, values), { [name]: value });
|
|
171
|
+
return Object.assign(Object.assign({}, values), { [variable.variable.name.value]: value });
|
|
176
172
|
}, {});
|
|
177
173
|
const execution = yield this.sofa.subscribe({
|
|
178
174
|
schema: this.sofa.schema,
|
|
@@ -295,12 +291,11 @@ function createRouter(sofa) {
|
|
|
295
291
|
router.post('/webhook', (request, serverContext) => __awaiter(this, void 0, void 0, function* () {
|
|
296
292
|
const { subscription, variables, url } = yield request.json();
|
|
297
293
|
try {
|
|
298
|
-
const contextValue = yield sofa.contextFactory(serverContext);
|
|
299
294
|
const result = yield subscriptionManager.start({
|
|
300
295
|
subscription,
|
|
301
296
|
variables,
|
|
302
297
|
url,
|
|
303
|
-
},
|
|
298
|
+
}, Object.assign(Object.assign({}, serverContext), { request }));
|
|
304
299
|
return new Response(JSON.stringify(result), {
|
|
305
300
|
status: 200,
|
|
306
301
|
statusText: 'OK',
|
|
@@ -322,7 +317,8 @@ function createRouter(sofa) {
|
|
|
322
317
|
const body = yield request.json();
|
|
323
318
|
const variables = body.variables;
|
|
324
319
|
try {
|
|
325
|
-
const
|
|
320
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
321
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
326
322
|
const result = yield subscriptionManager.update({
|
|
327
323
|
id,
|
|
328
324
|
variables,
|
|
@@ -365,7 +361,7 @@ function createRouter(sofa) {
|
|
|
365
361
|
return router;
|
|
366
362
|
}
|
|
367
363
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
368
|
-
var _a, _b, _c, _d;
|
|
364
|
+
var _a, _b, _c, _d, _e, _f;
|
|
369
365
|
logger.debug(`[Router] Creating ${fieldName} query`);
|
|
370
366
|
const queryType = sofa.schema.getQueryType();
|
|
371
367
|
const operationNode = buildOperationNodeForField({
|
|
@@ -399,6 +395,8 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
399
395
|
document: operation,
|
|
400
396
|
path: route.path,
|
|
401
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 : '',
|
|
402
400
|
};
|
|
403
401
|
}
|
|
404
402
|
function createMutationRoute({ sofa, router, fieldName, }) {
|
|
@@ -432,6 +430,8 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
432
430
|
document: operation,
|
|
433
431
|
path,
|
|
434
432
|
method,
|
|
433
|
+
tags: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) || [],
|
|
434
|
+
description: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) || '',
|
|
435
435
|
};
|
|
436
436
|
}
|
|
437
437
|
function useHandler(config) {
|
|
@@ -463,7 +463,8 @@ function useHandler(config) {
|
|
|
463
463
|
}
|
|
464
464
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
465
465
|
}, {});
|
|
466
|
-
const
|
|
466
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
467
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
467
468
|
const result = yield sofa.execute({
|
|
468
469
|
schema: sofa.schema,
|
|
469
470
|
document: operation,
|
|
@@ -473,8 +474,9 @@ function useHandler(config) {
|
|
|
473
474
|
});
|
|
474
475
|
if (result.errors) {
|
|
475
476
|
const defaultErrorHandler = (errors) => {
|
|
476
|
-
return new Response(errors[0], {
|
|
477
|
+
return new Response(JSON.stringify(errors[0]), {
|
|
477
478
|
status: 500,
|
|
479
|
+
headers: { 'Content-Type': 'application/json' },
|
|
478
480
|
});
|
|
479
481
|
};
|
|
480
482
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
@@ -613,7 +615,7 @@ function mapToRef(type) {
|
|
|
613
615
|
return `#/components/schemas/${type}`;
|
|
614
616
|
}
|
|
615
617
|
|
|
616
|
-
function buildSchemaObjectFromType(type) {
|
|
618
|
+
function buildSchemaObjectFromType(type, opts) {
|
|
617
619
|
const required = [];
|
|
618
620
|
const properties = {};
|
|
619
621
|
const fields = type.getFields();
|
|
@@ -622,28 +624,27 @@ function buildSchemaObjectFromType(type) {
|
|
|
622
624
|
if (isNonNullType(field.type)) {
|
|
623
625
|
required.push(field.name);
|
|
624
626
|
}
|
|
625
|
-
properties[fieldName] = resolveField(field);
|
|
627
|
+
properties[fieldName] = resolveField(field, opts);
|
|
626
628
|
if (field.description) {
|
|
627
629
|
properties[fieldName].description = field.description;
|
|
628
630
|
}
|
|
629
631
|
}
|
|
630
632
|
return Object.assign(Object.assign(Object.assign({ type: 'object' }, (required.length ? { required } : {})), { properties }), (type.description ? { description: type.description } : {}));
|
|
631
633
|
}
|
|
632
|
-
function resolveField(field) {
|
|
633
|
-
return resolveFieldType(field.type);
|
|
634
|
+
function resolveField(field, opts) {
|
|
635
|
+
return resolveFieldType(field.type, opts);
|
|
634
636
|
}
|
|
635
637
|
// array -> [type]
|
|
636
638
|
// type -> $ref
|
|
637
639
|
// scalar -> swagger primitive
|
|
638
|
-
function resolveFieldType(type) {
|
|
639
|
-
var _a, _b;
|
|
640
|
+
function resolveFieldType(type, opts) {
|
|
640
641
|
if (isNonNullType(type)) {
|
|
641
|
-
return resolveFieldType(type.ofType);
|
|
642
|
+
return resolveFieldType(type.ofType, opts);
|
|
642
643
|
}
|
|
643
644
|
if (isListType(type)) {
|
|
644
645
|
return {
|
|
645
646
|
type: 'array',
|
|
646
|
-
items: resolveFieldType(type.ofType),
|
|
647
|
+
items: resolveFieldType(type.ofType, opts),
|
|
647
648
|
};
|
|
648
649
|
}
|
|
649
650
|
if (isObjectType(type)) {
|
|
@@ -652,14 +653,15 @@ function resolveFieldType(type) {
|
|
|
652
653
|
};
|
|
653
654
|
}
|
|
654
655
|
if (isScalarType(type)) {
|
|
655
|
-
return (mapToPrimitive(type.name) ||
|
|
656
|
+
return (mapToPrimitive(type.name) ||
|
|
657
|
+
opts.customScalars[type.name] || {
|
|
656
658
|
type: 'object',
|
|
657
659
|
});
|
|
658
660
|
}
|
|
659
661
|
if (isEnumType(type)) {
|
|
660
662
|
return {
|
|
661
663
|
type: 'string',
|
|
662
|
-
enum:
|
|
664
|
+
enum: type.getValues().map((value) => value.name),
|
|
663
665
|
};
|
|
664
666
|
}
|
|
665
667
|
return {
|
|
@@ -667,36 +669,41 @@ function resolveFieldType(type) {
|
|
|
667
669
|
};
|
|
668
670
|
}
|
|
669
671
|
|
|
670
|
-
function buildPathFromOperation({ url, schema, operation, useRequestBody, }) {
|
|
672
|
+
function buildPathFromOperation({ url, schema, operation, useRequestBody, tags, description, customScalars, }) {
|
|
671
673
|
const info = getOperationInfo(operation);
|
|
672
|
-
const
|
|
673
|
-
return Object.assign(Object.assign({
|
|
674
|
+
const summary = resolveSummary(schema, info.operation);
|
|
675
|
+
return Object.assign(Object.assign({ tags,
|
|
676
|
+
description,
|
|
677
|
+
summary, operationId: info.name }, (useRequestBody
|
|
674
678
|
? {
|
|
675
679
|
requestBody: {
|
|
676
680
|
content: {
|
|
677
681
|
'application/json': {
|
|
678
|
-
schema: resolveRequestBody(info.operation.variableDefinitions
|
|
682
|
+
schema: resolveRequestBody(info.operation.variableDefinitions, {
|
|
683
|
+
customScalars,
|
|
684
|
+
}),
|
|
679
685
|
},
|
|
680
686
|
},
|
|
681
687
|
},
|
|
682
688
|
}
|
|
683
689
|
: {
|
|
684
|
-
parameters: resolveParameters(url, info.operation.variableDefinitions),
|
|
690
|
+
parameters: resolveParameters(url, info.operation.variableDefinitions, { customScalars }),
|
|
685
691
|
})), { responses: {
|
|
686
692
|
200: {
|
|
687
|
-
description,
|
|
693
|
+
description: summary,
|
|
688
694
|
content: {
|
|
689
695
|
'application/json': {
|
|
690
696
|
schema: resolveResponse({
|
|
691
697
|
schema,
|
|
692
698
|
operation: info.operation,
|
|
699
|
+
customScalars,
|
|
693
700
|
}),
|
|
694
701
|
},
|
|
695
702
|
},
|
|
696
703
|
},
|
|
697
704
|
} });
|
|
698
705
|
}
|
|
699
|
-
function resolveParameters(url, variables) {
|
|
706
|
+
function resolveParameters(url, variables, opts) {
|
|
700
707
|
if (!variables) {
|
|
701
708
|
return [];
|
|
702
709
|
}
|
|
@@ -705,11 +712,11 @@ function resolveParameters(url, variables) {
|
|
|
705
712
|
in: isInPath(url, variable.variable.name.value) ? 'path' : 'query',
|
|
706
713
|
name: variable.variable.name.value,
|
|
707
714
|
required: variable.type.kind === Kind.NON_NULL_TYPE,
|
|
708
|
-
schema: resolveParamSchema(variable.type),
|
|
715
|
+
schema: resolveParamSchema(variable.type, opts),
|
|
709
716
|
};
|
|
710
717
|
});
|
|
711
718
|
}
|
|
712
|
-
function resolveRequestBody(variables) {
|
|
719
|
+
function resolveRequestBody(variables, opts) {
|
|
713
720
|
if (!variables) {
|
|
714
721
|
return {};
|
|
715
722
|
}
|
|
@@ -719,48 +726,49 @@ function resolveRequestBody(variables) {
|
|
|
719
726
|
if (variable.type.kind === Kind.NON_NULL_TYPE) {
|
|
720
727
|
required.push(variable.variable.name.value);
|
|
721
728
|
}
|
|
722
|
-
properties[variable.variable.name.value] = resolveParamSchema(variable.type);
|
|
729
|
+
properties[variable.variable.name.value] = resolveParamSchema(variable.type, opts);
|
|
723
730
|
});
|
|
724
731
|
return Object.assign({ type: 'object', properties }, (required.length ? { required } : {}));
|
|
725
732
|
}
|
|
726
733
|
// array -> [type]
|
|
727
734
|
// type -> $ref
|
|
728
735
|
// scalar -> swagger primitive
|
|
729
|
-
function resolveParamSchema(type) {
|
|
736
|
+
function resolveParamSchema(type, opts) {
|
|
730
737
|
if (type.kind === Kind.NON_NULL_TYPE) {
|
|
731
|
-
return resolveParamSchema(type.type);
|
|
738
|
+
return resolveParamSchema(type.type, opts);
|
|
732
739
|
}
|
|
733
740
|
if (type.kind === Kind.LIST_TYPE) {
|
|
734
741
|
return {
|
|
735
742
|
type: 'array',
|
|
736
|
-
items: resolveParamSchema(type.type),
|
|
743
|
+
items: resolveParamSchema(type.type, opts),
|
|
737
744
|
};
|
|
738
745
|
}
|
|
739
746
|
const primitive = mapToPrimitive(type.name.value);
|
|
740
|
-
return (primitive ||
|
|
747
|
+
return (primitive ||
|
|
748
|
+
opts.customScalars[type.name.value] || {
|
|
741
749
|
$ref: mapToRef(type.name.value),
|
|
742
750
|
});
|
|
743
751
|
}
|
|
744
|
-
function resolveResponse({ schema, operation, }) {
|
|
752
|
+
function resolveResponse({ schema, operation, customScalars, }) {
|
|
745
753
|
const operationType = operation.operation;
|
|
746
754
|
const rootField = operation.selectionSet.selections[0];
|
|
747
755
|
if (rootField.kind === Kind.FIELD) {
|
|
748
756
|
if (operationType === 'query') {
|
|
749
757
|
const queryType = schema.getQueryType();
|
|
750
758
|
const field = queryType.getFields()[rootField.name.value];
|
|
751
|
-
return resolveFieldType(field.type);
|
|
759
|
+
return resolveFieldType(field.type, { customScalars });
|
|
752
760
|
}
|
|
753
761
|
if (operationType === 'mutation') {
|
|
754
762
|
const mutationType = schema.getMutationType();
|
|
755
763
|
const field = mutationType.getFields()[rootField.name.value];
|
|
756
|
-
return resolveFieldType(field.type);
|
|
764
|
+
return resolveFieldType(field.type, { customScalars });
|
|
757
765
|
}
|
|
758
766
|
}
|
|
759
767
|
}
|
|
760
768
|
function isInPath(url, param) {
|
|
761
769
|
return url.indexOf(`{${param}}`) !== -1;
|
|
762
770
|
}
|
|
763
|
-
function
|
|
771
|
+
function resolveSummary(schema, operation) {
|
|
764
772
|
const selection = operation.selectionSet.selections[0];
|
|
765
773
|
const fieldName = selection.name.value;
|
|
766
774
|
const typeDefinition = schema.getType(titleCase(operation.operation));
|
|
@@ -781,13 +789,13 @@ function isObjectTypeDefinitionNode(node) {
|
|
|
781
789
|
return node.kind === Kind.OBJECT_TYPE_DEFINITION;
|
|
782
790
|
}
|
|
783
791
|
|
|
784
|
-
function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
792
|
+
function OpenAPI({ schema, info, servers, components, security, tags, customScalars = {}, }) {
|
|
785
793
|
const types = schema.getTypeMap();
|
|
786
794
|
const swagger = {
|
|
787
795
|
openapi: '3.0.0',
|
|
788
796
|
info,
|
|
789
797
|
servers,
|
|
790
|
-
tags,
|
|
798
|
+
tags: [],
|
|
791
799
|
paths: {},
|
|
792
800
|
components: {
|
|
793
801
|
schemas: {},
|
|
@@ -797,7 +805,9 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
797
805
|
const type = types[typeName];
|
|
798
806
|
if ((isObjectType(type) || isInputObjectType(type)) &&
|
|
799
807
|
!isIntrospectionType(type)) {
|
|
800
|
-
swagger.components.schemas[typeName] = buildSchemaObjectFromType(type
|
|
808
|
+
swagger.components.schemas[typeName] = buildSchemaObjectFromType(type, {
|
|
809
|
+
customScalars,
|
|
810
|
+
});
|
|
801
811
|
}
|
|
802
812
|
}
|
|
803
813
|
if (components) {
|
|
@@ -806,6 +816,9 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
806
816
|
if (security) {
|
|
807
817
|
swagger.security = security;
|
|
808
818
|
}
|
|
819
|
+
if (tags) {
|
|
820
|
+
swagger.tags = tags;
|
|
821
|
+
}
|
|
809
822
|
return {
|
|
810
823
|
addRoute(info, config) {
|
|
811
824
|
const basePath = (config === null || config === void 0 ? void 0 : config.basePath) || '';
|
|
@@ -814,11 +827,15 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
814
827
|
if (!swagger.paths[path]) {
|
|
815
828
|
swagger.paths[path] = {};
|
|
816
829
|
}
|
|
817
|
-
swagger.paths[path]
|
|
830
|
+
const pathsObj = swagger.paths[path];
|
|
831
|
+
pathsObj[info.method.toLowerCase()] = buildPathFromOperation({
|
|
818
832
|
url: path,
|
|
819
833
|
operation: info.document,
|
|
820
834
|
schema,
|
|
821
835
|
useRequestBody: ['POST', 'PUT', 'PATCH'].includes(info.method),
|
|
836
|
+
tags: info.tags || [],
|
|
837
|
+
description: info.description || '',
|
|
838
|
+
customScalars,
|
|
822
839
|
});
|
|
823
840
|
},
|
|
824
841
|
get() {
|
package/open-api/index.d.ts
CHANGED
|
@@ -1,15 +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
23
|
basePath?: string;
|
|
13
24
|
}): void;
|
|
14
|
-
get():
|
|
25
|
+
get(): OpenAPIV3.Document<{}>;
|
|
15
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;
|
package/open-api/types.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { GraphQLObjectType, GraphQLInputObjectType, GraphQLType } from 'graphql';
|
|
2
|
-
export declare function buildSchemaObjectFromType(type: GraphQLObjectType | GraphQLInputObjectType
|
|
3
|
-
|
|
2
|
+
export declare function buildSchemaObjectFromType(type: GraphQLObjectType | GraphQLInputObjectType, opts: {
|
|
3
|
+
customScalars: Record<string, any>;
|
|
4
|
+
}): any;
|
|
5
|
+
export declare function resolveFieldType(type: GraphQLType, opts: {
|
|
6
|
+
customScalars: Record<string, any>;
|
|
7
|
+
}): any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sofa-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Create REST APIs with GraphQL",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"@whatwg-node/server": "^0.4.1",
|
|
13
13
|
"ansi-colors": "4.1.3",
|
|
14
14
|
"itty-router": "^2.6.1",
|
|
15
|
+
"openapi-types": "12.0.2",
|
|
15
16
|
"param-case": "3.0.4",
|
|
16
17
|
"title-case": "3.0.3",
|
|
17
|
-
"tslib": "2.4.0"
|
|
18
|
-
"uuid": "8.3.2"
|
|
18
|
+
"tslib": "2.4.0"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
},
|
|
51
51
|
"./package.json": "./package.json"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|
package/sofa.d.ts
CHANGED
package/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DefaultServerAdapterContext } from '@whatwg-node/server';
|
|
1
2
|
import { DocumentNode } from 'graphql';
|
|
2
3
|
export declare type ContextValue = Record<string, any>;
|
|
3
4
|
export declare type Ignore = string[];
|
|
@@ -6,9 +7,11 @@ export interface RouteInfo {
|
|
|
6
7
|
document: DocumentNode;
|
|
7
8
|
path: string;
|
|
8
9
|
method: Method;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
description?: string;
|
|
9
12
|
}
|
|
10
13
|
export declare type OnRoute = (info: RouteInfo) => void;
|
|
11
|
-
export declare type ContextFn = (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
14
|
+
export declare type ContextFn = (serverContext: DefaultSofaServerContext) => Promise<ContextValue> | ContextValue;
|
|
15
|
+
export declare type DefaultSofaServerContext = DefaultServerAdapterContext & {
|
|
16
|
+
request: Request;
|
|
17
|
+
};
|
package/open-api/interfaces.d.ts
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
declare type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
2
|
-
export declare namespace OpenAPI {
|
|
3
|
-
export interface Contact {
|
|
4
|
-
/**
|
|
5
|
-
* The identifying name of the contact person/organization.
|
|
6
|
-
*/
|
|
7
|
-
name?: string;
|
|
8
|
-
/**
|
|
9
|
-
* The URL pointing to the contact information. MUST be in the format of a URL.
|
|
10
|
-
*/
|
|
11
|
-
url?: string;
|
|
12
|
-
/**
|
|
13
|
-
* The email address of the contact person/organization. MUST be in the format of an email address.
|
|
14
|
-
*/
|
|
15
|
-
email?: string;
|
|
16
|
-
}
|
|
17
|
-
export interface License {
|
|
18
|
-
/**
|
|
19
|
-
* The license name used for the API.
|
|
20
|
-
*/
|
|
21
|
-
name: string;
|
|
22
|
-
/**
|
|
23
|
-
* A URL to the license used for the API. MUST be in the format of a URL.
|
|
24
|
-
*/
|
|
25
|
-
url?: string;
|
|
26
|
-
}
|
|
27
|
-
export interface Info {
|
|
28
|
-
/**
|
|
29
|
-
* The title of the application.
|
|
30
|
-
*/
|
|
31
|
-
title: string;
|
|
32
|
-
/**
|
|
33
|
-
* A short description of the application. CommonMark syntax MAY be used for rich text representation.
|
|
34
|
-
*/
|
|
35
|
-
description?: string;
|
|
36
|
-
/**
|
|
37
|
-
* A URL to the Terms of Service for the API. MUST be in the format of a URL.
|
|
38
|
-
*/
|
|
39
|
-
termsOfService?: string;
|
|
40
|
-
/**
|
|
41
|
-
* The contact information for the exposed API.
|
|
42
|
-
*/
|
|
43
|
-
contact?: Contact;
|
|
44
|
-
/**
|
|
45
|
-
* The license information for the exposed API.
|
|
46
|
-
*/
|
|
47
|
-
license?: License;
|
|
48
|
-
/**
|
|
49
|
-
* The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API implementation version).
|
|
50
|
-
*/
|
|
51
|
-
version: string;
|
|
52
|
-
}
|
|
53
|
-
export interface Reference {
|
|
54
|
-
/**
|
|
55
|
-
* The reference string.
|
|
56
|
-
*/
|
|
57
|
-
$ref: string;
|
|
58
|
-
}
|
|
59
|
-
export interface Parameter {
|
|
60
|
-
/**
|
|
61
|
-
* The name of the parameter. Parameter names are case sensitive.
|
|
62
|
-
* - If in is "path", the name field MUST correspond to the associated path segment from the path field in the Paths Object. See Path Templating for further information.
|
|
63
|
-
* - If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the parameter definition SHALL be ignored.
|
|
64
|
-
* - For all other cases, the name corresponds to the parameter name used by the in property
|
|
65
|
-
*/
|
|
66
|
-
name: string;
|
|
67
|
-
/**
|
|
68
|
-
* The location of the parameter. Possible values are "query", "header", "path" or "cookie".
|
|
69
|
-
*/
|
|
70
|
-
in: 'query' | 'header' | 'path' | 'cookie';
|
|
71
|
-
/**
|
|
72
|
-
* A brief description of the parameter. This could contain examples of use. CommonMark syntax MAY be used for rich text representation.
|
|
73
|
-
*/
|
|
74
|
-
description?: string;
|
|
75
|
-
/**
|
|
76
|
-
* Determines whether this parameter is mandatory. If the parameter location is "path", this property is REQUIRED and its value MUST be true. Otherwise, the property MAY be included and its default value is false.
|
|
77
|
-
*/
|
|
78
|
-
required: boolean;
|
|
79
|
-
/**
|
|
80
|
-
* Specifies that a parameter is deprecated and SHOULD be transitioned out of usage. Default value is false.
|
|
81
|
-
*/
|
|
82
|
-
deprecated?: boolean;
|
|
83
|
-
/**
|
|
84
|
-
* Sets the ability to pass empty-valued parameters. This is valid only for query parameters and allows sending a parameter with an empty value. Default value is false. If style is used, and if behavior is n/a (cannot be serialized), the value of allowEmptyValue SHALL be ignored. Use of this property is NOT RECOMMENDED, as it is likely to be removed in a later revision.
|
|
85
|
-
*/
|
|
86
|
-
allowEmptyValue?: boolean;
|
|
87
|
-
schema?: Reference | Schema;
|
|
88
|
-
}
|
|
89
|
-
export interface RequestBody {
|
|
90
|
-
/**
|
|
91
|
-
* A brief description of the request body. This could contain examples of use. CommonMark syntax MAY be used for rich text representation.
|
|
92
|
-
*/
|
|
93
|
-
description?: string;
|
|
94
|
-
/**
|
|
95
|
-
* The content of the request body. The key is a media type or media type range and the value describes it. For requests that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/*
|
|
96
|
-
*/
|
|
97
|
-
content: Array<string>;
|
|
98
|
-
/**
|
|
99
|
-
* Determines if the request body is required in the request. Defaults to false.
|
|
100
|
-
*/
|
|
101
|
-
required?: boolean;
|
|
102
|
-
}
|
|
103
|
-
export type Header = Omit<Parameter, 'name' | 'in'>;
|
|
104
|
-
export interface Link {
|
|
105
|
-
/**
|
|
106
|
-
* A relative or absolute reference to an OAS operation. This field is mutually exclusive of the operationId field, and MUST point to an Operation Object. Relative operationRef values MAY be used to locate an existing Operation Object in the OpenAPI definition.
|
|
107
|
-
*/
|
|
108
|
-
operationRef?: string;
|
|
109
|
-
/**
|
|
110
|
-
* The name of an existing, resolvable OAS operation, as defined with a unique operationId. This field is mutually exclusive of the operationRef field.
|
|
111
|
-
*/
|
|
112
|
-
operationId?: string;
|
|
113
|
-
/**
|
|
114
|
-
* A map representing parameters to pass to an operation as specified with operationId or identified via operationRef. The key is the parameter name to be used, whereas the value can be a constant or an expression to be evaluated and passed to the linked operation. The parameter name can be qualified using the parameter location [{in}.]{name} for operations that use the same parameter name in different locations (e.g. path.id).
|
|
115
|
-
*/
|
|
116
|
-
parameters?: Record<string, any>;
|
|
117
|
-
/**
|
|
118
|
-
* A literal value or {expression} to use as a request body when calling the target operation.
|
|
119
|
-
*/
|
|
120
|
-
requestBody?: any;
|
|
121
|
-
/**
|
|
122
|
-
* A description of the link. CommonMark syntax MAY be used for rich text representation.
|
|
123
|
-
*/
|
|
124
|
-
description?: string;
|
|
125
|
-
}
|
|
126
|
-
interface Response {
|
|
127
|
-
/**
|
|
128
|
-
* A short description of the response. CommonMark syntax MAY be used for rich text representation.
|
|
129
|
-
*/
|
|
130
|
-
description: string;
|
|
131
|
-
headers?: {
|
|
132
|
-
[header: string]: Header | Reference;
|
|
133
|
-
};
|
|
134
|
-
links?: {
|
|
135
|
-
[link: string]: Link | Reference;
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
interface Responses {
|
|
139
|
-
default: Response | Reference;
|
|
140
|
-
[httpStatusCode: number]: Response | Reference;
|
|
141
|
-
}
|
|
142
|
-
export interface Operation {
|
|
143
|
-
/**
|
|
144
|
-
* A list of tags for API documentation control.
|
|
145
|
-
* Tags can be used for logical grouping of operations by resources or any other qualifier.
|
|
146
|
-
*/
|
|
147
|
-
tags?: string[];
|
|
148
|
-
/**
|
|
149
|
-
* A short summary of what the operation does.
|
|
150
|
-
*/
|
|
151
|
-
summary?: string;
|
|
152
|
-
/**
|
|
153
|
-
* A verbose explanation of the operation behavior. CommonMark syntax MAY be used for rich text representation.
|
|
154
|
-
*/
|
|
155
|
-
description?: string;
|
|
156
|
-
/**
|
|
157
|
-
* Additional external documentation for this operation.
|
|
158
|
-
*/
|
|
159
|
-
externalDocs?: {
|
|
160
|
-
/**
|
|
161
|
-
* The URL for the target documentation. Value MUST be in the format of a URL.
|
|
162
|
-
*/
|
|
163
|
-
url: string;
|
|
164
|
-
/**
|
|
165
|
-
* A short description of the target documentation. CommonMark syntax MAY be used for rich text representation.
|
|
166
|
-
*/
|
|
167
|
-
description?: string;
|
|
168
|
-
};
|
|
169
|
-
/**
|
|
170
|
-
* Unique string used to identify the operation.
|
|
171
|
-
* The id MUST be unique among all operations described in the API.
|
|
172
|
-
* The operationId value is case-sensitive.
|
|
173
|
-
* Tools and libraries MAY use the operationId to uniquely identify an operation,
|
|
174
|
-
* therefore, it is RECOMMENDED to follow common programming naming conventions.
|
|
175
|
-
*/
|
|
176
|
-
operationId?: string;
|
|
177
|
-
/**
|
|
178
|
-
* A list of parameters that are applicable for this operation.
|
|
179
|
-
* If a parameter is already defined at the Path Item, the new definition will override it but can never remove it.
|
|
180
|
-
* The list MUST NOT include duplicated parameters.
|
|
181
|
-
* A unique parameter is defined by a combination of a name and location.
|
|
182
|
-
* The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters.
|
|
183
|
-
*/
|
|
184
|
-
parameters?: Array<Parameter | Reference>;
|
|
185
|
-
/**
|
|
186
|
-
* The request body applicable for this operation. The requestBody is only supported in HTTP methods where the HTTP 1.1 specification RFC7231 has explicitly defined semantics for request bodies. In other cases where the HTTP spec is vague, requestBody SHALL be ignored by consumers.
|
|
187
|
-
*/
|
|
188
|
-
requestBody?: RequestBody | Reference;
|
|
189
|
-
/**
|
|
190
|
-
* The list of possible responses as they are returned from executing this operation.
|
|
191
|
-
*/
|
|
192
|
-
responses?: Responses;
|
|
193
|
-
/**
|
|
194
|
-
* Declares this operation to be deprecated. Consumers SHOULD refrain from usage of the declared operation. Default value is false.
|
|
195
|
-
*/
|
|
196
|
-
deprecated?: boolean;
|
|
197
|
-
}
|
|
198
|
-
export interface PathItem {
|
|
199
|
-
/**
|
|
200
|
-
* Allows for an external definition of this path item.
|
|
201
|
-
* The referenced structure MUST be in the format of a Path Item Object.
|
|
202
|
-
* If there are conflicts between the referenced definition and this Path Item's definition, the behavior is undefined.
|
|
203
|
-
*/
|
|
204
|
-
$ref?: string;
|
|
205
|
-
/**
|
|
206
|
-
* An optional, string summary, intended to apply to all operations in this path.
|
|
207
|
-
*/
|
|
208
|
-
summary?: string;
|
|
209
|
-
/**
|
|
210
|
-
* An optional, string description, intended to apply to all operations in this path.
|
|
211
|
-
* CommonMark syntax MAY be used for rich text representation.
|
|
212
|
-
*/
|
|
213
|
-
description?: string;
|
|
214
|
-
/**
|
|
215
|
-
* A definition of a GET operation on this path.
|
|
216
|
-
*/
|
|
217
|
-
get?: Operation;
|
|
218
|
-
/**
|
|
219
|
-
* A definition of a PUT operation on this path.
|
|
220
|
-
*/
|
|
221
|
-
put?: Operation;
|
|
222
|
-
/**
|
|
223
|
-
* A definition of a POST operation on this path.
|
|
224
|
-
*/
|
|
225
|
-
post?: Operation;
|
|
226
|
-
/**
|
|
227
|
-
* A definition of a DELETE operation on this path.
|
|
228
|
-
*/
|
|
229
|
-
delete?: Operation;
|
|
230
|
-
/**
|
|
231
|
-
* A definition of a OPTIONS operation on this path.
|
|
232
|
-
*/
|
|
233
|
-
options?: Operation;
|
|
234
|
-
/**
|
|
235
|
-
* A definition of a HEAD operation on this path.
|
|
236
|
-
*/
|
|
237
|
-
head?: Operation;
|
|
238
|
-
/**
|
|
239
|
-
* A definition of a PATCH operation on this path.
|
|
240
|
-
*/
|
|
241
|
-
patch?: Operation;
|
|
242
|
-
/**
|
|
243
|
-
* A definition of a TRACE operation on this path.
|
|
244
|
-
*/
|
|
245
|
-
trace?: Operation;
|
|
246
|
-
/**
|
|
247
|
-
* A list of parameters that are applicable for all the operations described under this path. These parameters can be overridden at the operation level, but cannot be removed there. The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location. The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters.
|
|
248
|
-
*/
|
|
249
|
-
parameters?: Array<Parameter | Reference>;
|
|
250
|
-
}
|
|
251
|
-
export interface XML {
|
|
252
|
-
/**
|
|
253
|
-
* Replaces the name of the element/attribute used for the described schema property. When defined within items, it will affect the name of the individual XML elements within the list. When defined alongside type being array (outside the items), it will affect the wrapping element and only if wrapped is true. If wrapped is false, it will be ignored.
|
|
254
|
-
*/
|
|
255
|
-
name?: string;
|
|
256
|
-
/**
|
|
257
|
-
* The URI of the namespace definition. Value MUST be in the form of an absolute URI.
|
|
258
|
-
*/
|
|
259
|
-
namespace?: string;
|
|
260
|
-
/**
|
|
261
|
-
* The prefix to be used for the name.
|
|
262
|
-
*/
|
|
263
|
-
prefix?: string;
|
|
264
|
-
/**
|
|
265
|
-
* Declares whether the property definition translates to an attribute instead of an element. Default value is false.
|
|
266
|
-
*/
|
|
267
|
-
attribute?: boolean;
|
|
268
|
-
/**
|
|
269
|
-
* MAY be used only for an array definition. Signifies whether the array is wrapped (for example, <books><book/><book/></books>) or unwrapped (<book/><book/>). Default value is false. The definition takes effect only when defined alongside type being array (outside the items).
|
|
270
|
-
*/
|
|
271
|
-
wrapped?: boolean;
|
|
272
|
-
}
|
|
273
|
-
export interface Schema {
|
|
274
|
-
/**
|
|
275
|
-
* Allows sending a null value for the defined schema. Default value is false.
|
|
276
|
-
*/
|
|
277
|
-
nullable?: boolean;
|
|
278
|
-
/**
|
|
279
|
-
* Relevant only for Schema "properties" definitions. Declares the property as "read only". This means that it MAY be sent as part of a response but SHOULD NOT be sent as part of the request. If the property is marked as readOnly being true and is in the required list, the required will take effect on the response only. A property MUST NOT be marked as both readOnly and writeOnly being true. Default value is false.
|
|
280
|
-
*/
|
|
281
|
-
readOnly?: boolean;
|
|
282
|
-
/**
|
|
283
|
-
* Relevant only for Schema "properties" definitions. Declares the property as "write only". Therefore, it MAY be sent as part of a request but SHOULD NOT be sent as part of the response. If the property is marked as writeOnly being true and is in the required list, the required will take effect on the request only. A property MUST NOT be marked as both readOnly and writeOnly being true. Default value is false.
|
|
284
|
-
*/
|
|
285
|
-
writeOnly?: boolean;
|
|
286
|
-
/**
|
|
287
|
-
* This MAY be used only on properties schemas. It has no effect on root schemas. Adds additional metadata to describe the XML representation of this property.
|
|
288
|
-
*/
|
|
289
|
-
xml?: XML;
|
|
290
|
-
/**
|
|
291
|
-
* Specifies that a schema is deprecated and SHOULD be transitioned out of usage. Default value is false.
|
|
292
|
-
*/
|
|
293
|
-
deprecated?: boolean;
|
|
294
|
-
}
|
|
295
|
-
export interface Components {
|
|
296
|
-
/**
|
|
297
|
-
* An object to hold reusable Schema Objects.
|
|
298
|
-
*/
|
|
299
|
-
schemas?: Record<string, Schema | Reference>;
|
|
300
|
-
}
|
|
301
|
-
export interface OpenAPI {
|
|
302
|
-
/**
|
|
303
|
-
* This string MUST be the semantic version number of the OpenAPI Specification version that the OpenAPI document uses.
|
|
304
|
-
* The openapi field SHOULD be used by tooling specifications and clients to interpret the OpenAPI document.
|
|
305
|
-
* This is not related to the API info.version string.
|
|
306
|
-
*/
|
|
307
|
-
openapi: string;
|
|
308
|
-
/**
|
|
309
|
-
* Provides metadata about the API. The metadata MAY be used by tooling as required.
|
|
310
|
-
*/
|
|
311
|
-
info: Info;
|
|
312
|
-
/**
|
|
313
|
-
* The available paths and operations for the API.
|
|
314
|
-
*/
|
|
315
|
-
paths: {
|
|
316
|
-
[path: string]: PathItem;
|
|
317
|
-
};
|
|
318
|
-
/**
|
|
319
|
-
* An element to hold various schemas for the specification.
|
|
320
|
-
*/
|
|
321
|
-
components?: Components;
|
|
322
|
-
}
|
|
323
|
-
export {};
|
|
324
|
-
}
|
|
325
|
-
export {};
|