@steedos/service-api 2.4.5 → 2.4.7
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/index.js +333 -74
- package/package.json +5 -5
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const ApiGateway = require("moleculer-web");
|
|
4
4
|
const steedosAuth = require('@steedos/auth');
|
|
5
5
|
const { ApolloService } = require("moleculer-apollo-server");
|
|
6
|
+
const { MoleculerServerError } = require("moleculer").Errors;
|
|
6
7
|
const GraphQLJSON = require('graphql-type-json');
|
|
7
8
|
const {
|
|
8
9
|
// GraphQLDate,
|
|
@@ -12,6 +13,75 @@ const {
|
|
|
12
13
|
const SteedosRouter = require('@steedos/router');
|
|
13
14
|
const _ = require('lodash');
|
|
14
15
|
|
|
16
|
+
const mixinOptions = {
|
|
17
|
+
|
|
18
|
+
// Global GraphQL typeDefs
|
|
19
|
+
typeDefs: ['scalar Date', `scalar JSON`],
|
|
20
|
+
|
|
21
|
+
// Global resolvers
|
|
22
|
+
resolvers: {
|
|
23
|
+
JSON: GraphQLJSON,
|
|
24
|
+
Date: GraphQLDateTime
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// API Gateway route options
|
|
28
|
+
routeOptions: {
|
|
29
|
+
path: "/graphql",
|
|
30
|
+
// cors: true, //会导致response返回 access-control-allow-origin: *, 并进一步导致客户端请求时withCredentials: true参数带来的跨域错误
|
|
31
|
+
whitelist: [
|
|
32
|
+
"graphql"
|
|
33
|
+
],
|
|
34
|
+
// Route-level Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares
|
|
35
|
+
use: [],
|
|
36
|
+
// Enable authentication. Implement the logic into `authenticate` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authentication
|
|
37
|
+
authentication: true,
|
|
38
|
+
// Enable authorization. Implement the logic into `authorize` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authorization
|
|
39
|
+
authorization: true,
|
|
40
|
+
// The auto-alias feature allows you to declare your route alias directly in your services.
|
|
41
|
+
// The gateway will dynamically build the full routes from service schema.
|
|
42
|
+
autoAliases: true,
|
|
43
|
+
aliases: {},
|
|
44
|
+
// Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options
|
|
45
|
+
callingOptions: {},
|
|
46
|
+
bodyParsers: {
|
|
47
|
+
json: {
|
|
48
|
+
strict: false,
|
|
49
|
+
limit: "10MB"
|
|
50
|
+
},
|
|
51
|
+
urlencoded: {
|
|
52
|
+
extended: true,
|
|
53
|
+
limit: "10MB"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
// Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy
|
|
57
|
+
mappingPolicy: "all", // Available values: "all", "restrict"
|
|
58
|
+
// Enable/disable logging
|
|
59
|
+
logging: true,
|
|
60
|
+
// Route error handler
|
|
61
|
+
onError(req, res, err) {
|
|
62
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
63
|
+
res.writeHead(err.code || 500);
|
|
64
|
+
res.end(JSON.stringify(err));
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server.html
|
|
69
|
+
serverOptions: {
|
|
70
|
+
tracing: process.env.NODE_ENV !== 'production',
|
|
71
|
+
|
|
72
|
+
engine: {
|
|
73
|
+
apiKey: process.env.APOLLO_ENGINE_KEY
|
|
74
|
+
},
|
|
75
|
+
subscriptions: false,
|
|
76
|
+
playground: {
|
|
77
|
+
settings: {
|
|
78
|
+
'request.credentials': 'same-origin'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
introspection: true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
15
85
|
/**
|
|
16
86
|
* @typedef {import('moleculer').Context} Context Moleculer's Context
|
|
17
87
|
* @typedef {import('http').IncomingMessage} IncomingRequest Incoming HTTP Request
|
|
@@ -21,74 +91,7 @@ module.exports = {
|
|
|
21
91
|
name: "api",
|
|
22
92
|
mixins: [ApiGateway,
|
|
23
93
|
// GraphQL Apollo Server
|
|
24
|
-
ApolloService(
|
|
25
|
-
|
|
26
|
-
// Global GraphQL typeDefs
|
|
27
|
-
typeDefs: ['scalar Date', `scalar JSON`],
|
|
28
|
-
|
|
29
|
-
// Global resolvers
|
|
30
|
-
resolvers: {
|
|
31
|
-
JSON: GraphQLJSON,
|
|
32
|
-
Date: GraphQLDateTime
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
// API Gateway route options
|
|
36
|
-
routeOptions: {
|
|
37
|
-
path: "/graphql",
|
|
38
|
-
// cors: true, //会导致response返回 access-control-allow-origin: *, 并进一步导致客户端请求时withCredentials: true参数带来的跨域错误
|
|
39
|
-
whitelist: [
|
|
40
|
-
"graphql"
|
|
41
|
-
],
|
|
42
|
-
// Route-level Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares
|
|
43
|
-
use: [],
|
|
44
|
-
// Enable authentication. Implement the logic into `authenticate` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authentication
|
|
45
|
-
authentication: true,
|
|
46
|
-
// Enable authorization. Implement the logic into `authorize` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authorization
|
|
47
|
-
authorization: true,
|
|
48
|
-
// The auto-alias feature allows you to declare your route alias directly in your services.
|
|
49
|
-
// The gateway will dynamically build the full routes from service schema.
|
|
50
|
-
autoAliases: true,
|
|
51
|
-
aliases: {},
|
|
52
|
-
// Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options
|
|
53
|
-
callingOptions: {},
|
|
54
|
-
bodyParsers: {
|
|
55
|
-
json: {
|
|
56
|
-
strict: false,
|
|
57
|
-
limit: "10MB"
|
|
58
|
-
},
|
|
59
|
-
urlencoded: {
|
|
60
|
-
extended: true,
|
|
61
|
-
limit: "10MB"
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
// Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy
|
|
65
|
-
mappingPolicy: "all", // Available values: "all", "restrict"
|
|
66
|
-
// Enable/disable logging
|
|
67
|
-
logging: true,
|
|
68
|
-
// Route error handler
|
|
69
|
-
onError(req, res, err) {
|
|
70
|
-
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
71
|
-
res.writeHead(err.code || 500);
|
|
72
|
-
res.end(JSON.stringify(err));
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
// https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server.html
|
|
77
|
-
serverOptions: {
|
|
78
|
-
tracing: process.env.NODE_ENV !== 'production',
|
|
79
|
-
|
|
80
|
-
engine: {
|
|
81
|
-
apiKey: process.env.APOLLO_ENGINE_KEY
|
|
82
|
-
},
|
|
83
|
-
subscriptions: false,
|
|
84
|
-
playground: {
|
|
85
|
-
settings: {
|
|
86
|
-
'request.credentials': 'same-origin'
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
introspection: true
|
|
90
|
-
}
|
|
91
|
-
})
|
|
94
|
+
ApolloService(mixinOptions)
|
|
92
95
|
],
|
|
93
96
|
|
|
94
97
|
// More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html
|
|
@@ -107,7 +110,7 @@ module.exports = {
|
|
|
107
110
|
routes: [
|
|
108
111
|
{
|
|
109
112
|
aliases: {
|
|
110
|
-
"GET /api/health"(req, res){
|
|
113
|
+
"GET /api/health"(req, res) {
|
|
111
114
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
112
115
|
res.end(JSON.stringify({ status: 'ok' }));
|
|
113
116
|
}
|
|
@@ -259,7 +262,7 @@ module.exports = {
|
|
|
259
262
|
* @param {String} actionName
|
|
260
263
|
* @param {Object?} def
|
|
261
264
|
*/
|
|
262
|
-
|
|
265
|
+
createActionResolver(actionName, def = {}) {
|
|
263
266
|
const {
|
|
264
267
|
dataLoader: useDataLoader = false,
|
|
265
268
|
nullIfError = false,
|
|
@@ -361,7 +364,7 @@ module.exports = {
|
|
|
361
364
|
);
|
|
362
365
|
}
|
|
363
366
|
|
|
364
|
-
return await context.ctx.call(actionName, mergedParams, { meta: {resolveInfo} });
|
|
367
|
+
return await context.ctx.call(actionName, mergedParams, { meta: { resolveInfo } });
|
|
365
368
|
}
|
|
366
369
|
} catch (err) {
|
|
367
370
|
if (nullIfError) {
|
|
@@ -376,11 +379,267 @@ module.exports = {
|
|
|
376
379
|
};
|
|
377
380
|
},
|
|
378
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Generate GraphQL Schema
|
|
384
|
+
*
|
|
385
|
+
* @param {Object[]} services
|
|
386
|
+
* @returns {Object} Generated schema
|
|
387
|
+
*/
|
|
388
|
+
generateGraphQLSchema(services) {
|
|
389
|
+
let str;
|
|
390
|
+
try {
|
|
391
|
+
let typeDefs = [];
|
|
392
|
+
let resolvers = {};
|
|
393
|
+
let schemaDirectives = null;
|
|
394
|
+
|
|
395
|
+
if (mixinOptions.typeDefs) {
|
|
396
|
+
typeDefs = typeDefs.concat(mixinOptions.typeDefs);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (mixinOptions.resolvers) {
|
|
400
|
+
resolvers = _.cloneDeep(mixinOptions.resolvers);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (mixinOptions.schemaDirectives) {
|
|
404
|
+
schemaDirectives = _.cloneDeep(mixinOptions.schemaDirectives);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
let queries = [];
|
|
408
|
+
let mutations = [];
|
|
409
|
+
let subscriptions = [];
|
|
410
|
+
let types = [];
|
|
411
|
+
let interfaces = [];
|
|
412
|
+
let unions = [];
|
|
413
|
+
let enums = [];
|
|
414
|
+
let inputs = [];
|
|
415
|
+
|
|
416
|
+
const processedServices = new Set();
|
|
417
|
+
|
|
418
|
+
services.forEach(service => {
|
|
419
|
+
const serviceName = this.getServiceName(service);
|
|
420
|
+
|
|
421
|
+
// Skip multiple instances of services
|
|
422
|
+
if (processedServices.has(serviceName)) return;
|
|
423
|
+
processedServices.add(serviceName);
|
|
424
|
+
|
|
425
|
+
if (service.settings && service.settings.graphql) {
|
|
426
|
+
// --- COMPILE SERVICE-LEVEL DEFINITIONS ---
|
|
427
|
+
if (_.isObject(service.settings.graphql)) {
|
|
428
|
+
const globalDef = service.settings.graphql;
|
|
429
|
+
|
|
430
|
+
if (globalDef.query) {
|
|
431
|
+
queries = queries.concat(globalDef.query);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (globalDef.mutation) {
|
|
435
|
+
mutations = mutations.concat(globalDef.mutation);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (globalDef.subscription) {
|
|
439
|
+
subscriptions = subscriptions.concat(globalDef.subscription);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (globalDef.type) {
|
|
443
|
+
types = types.concat(globalDef.type);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (globalDef.interface) {
|
|
447
|
+
interfaces = interfaces.concat(globalDef.interface);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (globalDef.union) {
|
|
451
|
+
unions = unions.concat(globalDef.union);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (globalDef.enum) {
|
|
455
|
+
enums = enums.concat(globalDef.enum);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (globalDef.input) {
|
|
459
|
+
inputs = inputs.concat(globalDef.input);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (globalDef.resolvers) {
|
|
463
|
+
resolvers = Object.entries(globalDef.resolvers).reduce(
|
|
464
|
+
(acc, [name, resolver]) => {
|
|
465
|
+
acc[name] = _.merge(
|
|
466
|
+
acc[name] || {},
|
|
467
|
+
this.createServiceResolvers(serviceName, resolver)
|
|
468
|
+
);
|
|
469
|
+
return acc;
|
|
470
|
+
},
|
|
471
|
+
resolvers
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// --- COMPILE ACTION-LEVEL DEFINITIONS ---
|
|
478
|
+
const resolver = {};
|
|
479
|
+
|
|
480
|
+
Object.values(service.actions).forEach(action => {
|
|
481
|
+
const { graphql: def } = action;
|
|
482
|
+
if (
|
|
483
|
+
mixinOptions.checkActionVisibility &&
|
|
484
|
+
action.visibility != null &&
|
|
485
|
+
action.visibility != "published"
|
|
486
|
+
)
|
|
487
|
+
return;
|
|
488
|
+
|
|
489
|
+
if (def && _.isObject(def)) {
|
|
490
|
+
if (def.query) {
|
|
491
|
+
if (!resolver["Query"]) resolver.Query = {};
|
|
492
|
+
|
|
493
|
+
_.castArray(def.query).forEach(query => {
|
|
494
|
+
const name = this.getFieldName(query);
|
|
495
|
+
queries.push(query);
|
|
496
|
+
resolver.Query[name] = this.createActionResolver(
|
|
497
|
+
action.name
|
|
498
|
+
);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (def.mutation) {
|
|
503
|
+
if (!resolver["Mutation"]) resolver.Mutation = {};
|
|
504
|
+
|
|
505
|
+
_.castArray(def.mutation).forEach(mutation => {
|
|
506
|
+
const name = this.getFieldName(mutation);
|
|
507
|
+
mutations.push(mutation);
|
|
508
|
+
resolver.Mutation[name] = this.createActionResolver(
|
|
509
|
+
action.name,
|
|
510
|
+
{
|
|
511
|
+
fileUploadArg: def.fileUploadArg,
|
|
512
|
+
}
|
|
513
|
+
);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (def.subscription) {
|
|
518
|
+
if (!resolver["Subscription"]) resolver.Subscription = {};
|
|
519
|
+
|
|
520
|
+
_.castArray(def.subscription).forEach(subscription => {
|
|
521
|
+
const name = this.getFieldName(subscription);
|
|
522
|
+
subscriptions.push(subscription);
|
|
523
|
+
resolver.Subscription[name] =
|
|
524
|
+
this.createAsyncIteratorResolver(
|
|
525
|
+
action.name,
|
|
526
|
+
def.tags,
|
|
527
|
+
def.filter
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (def.type) {
|
|
533
|
+
types = types.concat(def.type);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (def.interface) {
|
|
537
|
+
interfaces = interfaces.concat(def.interface);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (def.union) {
|
|
541
|
+
unions = unions.concat(def.union);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (def.enum) {
|
|
545
|
+
enums = enums.concat(def.enum);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (def.input) {
|
|
549
|
+
inputs = inputs.concat(def.input);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
if (Object.keys(resolver).length > 0) {
|
|
555
|
+
resolvers = _.merge(resolvers, resolver);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
if (
|
|
560
|
+
queries.length > 0 ||
|
|
561
|
+
types.length > 0 ||
|
|
562
|
+
mutations.length > 0 ||
|
|
563
|
+
subscriptions.length > 0 ||
|
|
564
|
+
interfaces.length > 0 ||
|
|
565
|
+
unions.length > 0 ||
|
|
566
|
+
enums.length > 0 ||
|
|
567
|
+
inputs.length > 0
|
|
568
|
+
) {
|
|
569
|
+
str = "";
|
|
570
|
+
if (queries.length > 0) {
|
|
571
|
+
str += `
|
|
572
|
+
type Query {
|
|
573
|
+
${queries.join("\n")}
|
|
574
|
+
}
|
|
575
|
+
`;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (mutations.length > 0) {
|
|
579
|
+
str += `
|
|
580
|
+
type Mutation {
|
|
581
|
+
${mutations.join("\n")}
|
|
582
|
+
}
|
|
583
|
+
`;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (subscriptions.length > 0) {
|
|
587
|
+
str += `
|
|
588
|
+
type Subscription {
|
|
589
|
+
${subscriptions.join("\n")}
|
|
590
|
+
}
|
|
591
|
+
`;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (types.length > 0) {
|
|
595
|
+
str += `
|
|
596
|
+
${types.join("\n")}
|
|
597
|
+
`;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (interfaces.length > 0) {
|
|
601
|
+
str += `
|
|
602
|
+
${interfaces.join("\n")}
|
|
603
|
+
`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (unions.length > 0) {
|
|
607
|
+
str += `
|
|
608
|
+
${unions.join("\n")}
|
|
609
|
+
`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (enums.length > 0) {
|
|
613
|
+
str += `
|
|
614
|
+
${enums.join("\n")}
|
|
615
|
+
`;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (inputs.length > 0) {
|
|
619
|
+
str += `
|
|
620
|
+
${inputs.join("\n")}
|
|
621
|
+
`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
typeDefs.push(str);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return this.makeExecutableSchema({ typeDefs, resolvers, schemaDirectives });
|
|
628
|
+
} catch (err) {
|
|
629
|
+
throw new MoleculerServerError(
|
|
630
|
+
"Unable to compile GraphQL schema",
|
|
631
|
+
500,
|
|
632
|
+
"UNABLE_COMPILE_GRAPHQL_SCHEMA",
|
|
633
|
+
{ err }
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
|
|
379
638
|
},
|
|
380
|
-
created(){
|
|
639
|
+
created() {
|
|
381
640
|
this.app = SteedosRouter.staticRouter();
|
|
382
641
|
},
|
|
383
|
-
async started
|
|
642
|
+
async started() {
|
|
384
643
|
|
|
385
644
|
this.broker.createService(require("@steedos/service-ui"));
|
|
386
645
|
|
|
@@ -396,7 +655,7 @@ module.exports = {
|
|
|
396
655
|
// });
|
|
397
656
|
// }
|
|
398
657
|
|
|
399
|
-
this.broker.waitForServices('~packages-@steedos/service-ui').then(()=>{
|
|
658
|
+
this.broker.waitForServices('~packages-@steedos/service-ui').then(() => {
|
|
400
659
|
this.app.use("/", this.express());
|
|
401
660
|
})
|
|
402
661
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steedos/service-api",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.7",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@steedos/auth": "2.4.
|
|
8
|
-
"@steedos/router": "2.4.
|
|
9
|
-
"@steedos/service-ui": "2.4.
|
|
7
|
+
"@steedos/auth": "2.4.7",
|
|
8
|
+
"@steedos/router": "2.4.7",
|
|
9
|
+
"@steedos/service-ui": "2.4.7",
|
|
10
10
|
"graphql": "^15.8.0",
|
|
11
11
|
"graphql-iso-date": "^3.6.1",
|
|
12
12
|
"graphql-type-json": "^0.3.2",
|
|
@@ -18,5 +18,5 @@
|
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
|
20
20
|
},
|
|
21
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "a58a5e537a2ff15dda57bf1c31d00f8a9ae19879"
|
|
22
22
|
}
|