@steedos/service-api 2.5.6-beta.1 → 2.5.6-beta.4
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 +172 -4
- package/package.json +7 -6
package/index.js
CHANGED
|
@@ -13,9 +13,11 @@ const {
|
|
|
13
13
|
const SteedosRouter = require('@steedos/router');
|
|
14
14
|
const _ = require('lodash');
|
|
15
15
|
const ServiceObjectGraphql = require('@steedos/service-object-graphql')
|
|
16
|
+
const open = require('open');
|
|
17
|
+
const DataLoader = require("dataloader");
|
|
18
|
+
const { LRUMap } = require('lru_map');
|
|
16
19
|
const validator = require('validator');
|
|
17
|
-
const enablePlayground = validator.toBoolean(process.env.STEEDOS_GRAPHQL_ENABLE_CONSOLE || 'true', true)
|
|
18
|
-
|
|
20
|
+
const enablePlayground = validator.toBoolean(process.env.STEEDOS_GRAPHQL_ENABLE_CONSOLE || 'true', true);
|
|
19
21
|
const mixinOptions = {
|
|
20
22
|
|
|
21
23
|
// Global GraphQL typeDefs
|
|
@@ -313,6 +315,76 @@ module.exports = {
|
|
|
313
315
|
},
|
|
314
316
|
|
|
315
317
|
methods: {
|
|
318
|
+
/**
|
|
319
|
+
* Get the unique key assigned to the DataLoader map
|
|
320
|
+
* @param {string} actionName - Fully qualified action name to bind to dataloader
|
|
321
|
+
* @param {Object.<string, any>} staticParams - Static parameters to use in dataloader
|
|
322
|
+
* @param {Object.<string, any>} args - Arguments passed to GraphQL child resolver
|
|
323
|
+
* @returns {string} Key to the dataloader instance
|
|
324
|
+
*/
|
|
325
|
+
getObjectDataLoaderMapKey(objectName) {
|
|
326
|
+
if (objectName) {
|
|
327
|
+
return `object:${objectName}`;
|
|
328
|
+
}
|
|
329
|
+
// 如果没有objectName,则抛出错误信息
|
|
330
|
+
throw new Error("objectName is required");
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
async objectDataLoaderHandler(actionName, staticParams, rootParams, graphqlCtx) {
|
|
335
|
+
const rootKeys = Object.keys(rootParams);
|
|
336
|
+
const {root, args, context, resolveInfo} = graphqlCtx;
|
|
337
|
+
const dataLoaderMapKey = this.getObjectDataLoaderMapKey(
|
|
338
|
+
staticParams.__objectName || staticParams.objectName
|
|
339
|
+
);
|
|
340
|
+
const objectDataLoaders = this.objectDataLoaders;
|
|
341
|
+
// if a dataLoader batching parameter is specified, then all root params can be data loaded;
|
|
342
|
+
// otherwise use only the primary rootParam
|
|
343
|
+
const primaryDataLoaderRootKey = rootKeys[0]; // for dataloader, use the first root key only
|
|
344
|
+
const dataLoaderBatchParam = this.dataLoaderBatchParams.get(actionName);
|
|
345
|
+
const dataLoaderUseAllRootKeys = dataLoaderBatchParam != null;
|
|
346
|
+
|
|
347
|
+
// check to see if the DataLoader has already been added to the GraphQL context; if not then add it for subsequent use
|
|
348
|
+
let dataLoader;
|
|
349
|
+
// console.log(`objectDataLoaders.has(dataLoaderMapKey)`, objectDataLoaders.has(dataLoaderMapKey))
|
|
350
|
+
if (objectDataLoaders.has(dataLoaderMapKey)) {
|
|
351
|
+
dataLoader = objectDataLoaders.get(dataLoaderMapKey);
|
|
352
|
+
} else {
|
|
353
|
+
const batchedParamKey =
|
|
354
|
+
dataLoaderBatchParam || rootParams[primaryDataLoaderRootKey];
|
|
355
|
+
dataLoader = this.buildDataLoader(
|
|
356
|
+
context.ctx,
|
|
357
|
+
actionName,
|
|
358
|
+
batchedParamKey,
|
|
359
|
+
staticParams,
|
|
360
|
+
args,
|
|
361
|
+
{ hashCacheKey: dataLoaderUseAllRootKeys } // must hash the cache key if not loading scalar
|
|
362
|
+
);
|
|
363
|
+
objectDataLoaders.set(dataLoaderMapKey, dataLoader);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let dataLoaderKey;
|
|
367
|
+
if (dataLoaderUseAllRootKeys) {
|
|
368
|
+
if (root && rootKeys) {
|
|
369
|
+
dataLoaderKey = {};
|
|
370
|
+
|
|
371
|
+
rootKeys.forEach(key => {
|
|
372
|
+
_.set(dataLoaderKey, rootParams[key], _.get(root, key));
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
dataLoaderKey = root && _.get(root, primaryDataLoaderRootKey);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (dataLoaderKey == null) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return Array.isArray(dataLoaderKey)
|
|
384
|
+
? await dataLoader.loadMany(dataLoaderKey)
|
|
385
|
+
: await dataLoader.load(dataLoaderKey);
|
|
386
|
+
},
|
|
387
|
+
|
|
316
388
|
/**
|
|
317
389
|
* Authenticate the request. It check the `Authorization` token value in the request header.
|
|
318
390
|
* Check the token value & resolve the user by the token.
|
|
@@ -369,16 +441,18 @@ module.exports = {
|
|
|
369
441
|
createActionResolver(actionName, def = {}) {
|
|
370
442
|
const {
|
|
371
443
|
dataLoader: useDataLoader = false,
|
|
444
|
+
objectDataLoader: useObjectDataLoader = false,
|
|
372
445
|
nullIfError = false,
|
|
373
446
|
params: staticParams = {},
|
|
374
447
|
rootParams = {},
|
|
375
448
|
fileUploadArg = null,
|
|
376
449
|
} = def;
|
|
377
450
|
const rootKeys = Object.keys(rootParams);
|
|
378
|
-
|
|
379
451
|
return async (root, args, context, resolveInfo) => {
|
|
380
452
|
try {
|
|
381
|
-
if
|
|
453
|
+
if(useObjectDataLoader){
|
|
454
|
+
return await this.objectDataLoaderHandler(actionName, staticParams, rootParams, {root, args, context, resolveInfo});
|
|
455
|
+
}else if (useDataLoader) {
|
|
382
456
|
const dataLoaderMapKey = this.getDataLoaderMapKey(
|
|
383
457
|
actionName,
|
|
384
458
|
staticParams,
|
|
@@ -566,6 +640,13 @@ module.exports = {
|
|
|
566
640
|
},
|
|
567
641
|
resolvers
|
|
568
642
|
);
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
for (const objectName in this.ObjectsUIResolvers) {
|
|
646
|
+
if(resolvers[objectName]){
|
|
647
|
+
resolvers[objectName]['_ui'] = this.ObjectsUIResolvers[objectName];
|
|
648
|
+
}
|
|
649
|
+
}
|
|
569
650
|
}
|
|
570
651
|
}
|
|
571
652
|
}
|
|
@@ -810,10 +891,88 @@ module.exports = {
|
|
|
810
891
|
}
|
|
811
892
|
},
|
|
812
893
|
|
|
894
|
+
/**
|
|
895
|
+
* Build a DataLoader instance
|
|
896
|
+
*
|
|
897
|
+
* @param {Object} ctx - Moleculer context
|
|
898
|
+
* @param {string} actionName - Fully qualified action name to bind to dataloader
|
|
899
|
+
* @param {string} batchedParamKey - Parameter key to use for loaded values
|
|
900
|
+
* @param {Object} staticParams - Static parameters to use in dataloader
|
|
901
|
+
* @param {Object} args - Arguments passed to GraphQL child resolver
|
|
902
|
+
* @param {Object} [options={}] - Optional arguments
|
|
903
|
+
* @param {Boolean} [options.hashCacheKey=false] - Use a hash for the cacheKeyFn
|
|
904
|
+
* @returns {DataLoader} Dataloader instance
|
|
905
|
+
*/
|
|
906
|
+
buildDataLoader(
|
|
907
|
+
ctx,
|
|
908
|
+
actionName,
|
|
909
|
+
batchedParamKey,
|
|
910
|
+
staticParams,
|
|
911
|
+
args,
|
|
912
|
+
{ hashCacheKey = false } = {}
|
|
913
|
+
) {
|
|
914
|
+
const batchLoadFn = keys => {
|
|
915
|
+
const rootParams = { [batchedParamKey]: keys };
|
|
916
|
+
return ctx.call(actionName, _.defaultsDeep({}, args, rootParams, staticParams));
|
|
917
|
+
};
|
|
918
|
+
|
|
919
|
+
const dataLoaderOptions = this.dataLoaderOptions.get(actionName) || {};
|
|
920
|
+
const cacheKeyFn = hashCacheKey && (key => hash(key));
|
|
921
|
+
const options = {
|
|
922
|
+
...(cacheKeyFn && { cacheKeyFn }),
|
|
923
|
+
cacheMap: new LRUMap(1000),
|
|
924
|
+
...dataLoaderOptions,
|
|
925
|
+
};
|
|
926
|
+
return new DataLoader(batchLoadFn, options);
|
|
927
|
+
}
|
|
928
|
+
|
|
813
929
|
},
|
|
814
930
|
created() {
|
|
931
|
+
this.objectDataLoaders = new Map();
|
|
815
932
|
this.app = SteedosRouter.staticRouter();
|
|
816
933
|
},
|
|
934
|
+
events:{
|
|
935
|
+
'@objectRecordEvent.*.*': function(ctx){
|
|
936
|
+
const { objectApiName, isUpdate, isDelete, id, doc } = ctx.params;
|
|
937
|
+
if(objectApiName && (isUpdate || isDelete)){
|
|
938
|
+
const key = this.getObjectDataLoaderMapKey(
|
|
939
|
+
objectApiName
|
|
940
|
+
);
|
|
941
|
+
let dataLoaderKeys = [id];
|
|
942
|
+
if(objectApiName === 'space_users'){
|
|
943
|
+
dataLoaderKeys.push(doc.user)
|
|
944
|
+
}
|
|
945
|
+
const loader = this.objectDataLoaders.get(key);
|
|
946
|
+
if(loader){
|
|
947
|
+
for(const dataLoaderKey of dataLoaderKeys){
|
|
948
|
+
loader.clear(dataLoaderKey);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
'service-ui.started': function(){
|
|
954
|
+
this.app.use("/", this.express());
|
|
955
|
+
},
|
|
956
|
+
"$packages.changed": function(){
|
|
957
|
+
if (!this.projectStarted) {
|
|
958
|
+
this.projectStarted = true
|
|
959
|
+
// 开发环境, 显示耗时
|
|
960
|
+
if(process.env.NODE_ENV == 'development' && global.__startDate){
|
|
961
|
+
console.log('耗时: ' + (new Date().getTime() - global.__startDate.getTime()) + ' ms');
|
|
962
|
+
}
|
|
963
|
+
console.log(`Project is running at ${process.env.ROOT_URL}`);
|
|
964
|
+
console.log('');
|
|
965
|
+
if (process.env.STEEDOS_AUTO_OPEN_BROWSER != 'false') { // 默认打开,如果不想打开,设置STEEDOS_AUTO_OPEN_BROWSER=false
|
|
966
|
+
try {
|
|
967
|
+
open(process.env.ROOT_URL);
|
|
968
|
+
} catch (error) {
|
|
969
|
+
console.error(error);
|
|
970
|
+
console.error('auto open browser failed.');
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
},
|
|
817
976
|
async started() {
|
|
818
977
|
|
|
819
978
|
this.broker.createService(require("@steedos/service-ui"));
|
|
@@ -837,5 +996,14 @@ module.exports = {
|
|
|
837
996
|
global.SteedosApi = {
|
|
838
997
|
express: this.express
|
|
839
998
|
}
|
|
999
|
+
},
|
|
1000
|
+
|
|
1001
|
+
actions: {
|
|
1002
|
+
getObjectDisplayData: {
|
|
1003
|
+
handler(ctx) {
|
|
1004
|
+
const { objectApiName, recordId, fields } = ctx.params;
|
|
1005
|
+
return this.getObjectDisplayData(objectApiName, recordId, fields);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
840
1008
|
}
|
|
841
1009
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steedos/service-api",
|
|
3
|
-
"version": "2.5.6-beta.
|
|
3
|
+
"version": "2.5.6-beta.4",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@steedos/auth": "2.5.6-beta.
|
|
8
|
-
"@steedos/router": "2.5.6-beta.
|
|
9
|
-
"@steedos/service-object-graphql": "2.5.6-beta.
|
|
10
|
-
"@steedos/service-ui": "2.5.6-beta.
|
|
7
|
+
"@steedos/auth": "2.5.6-beta.4",
|
|
8
|
+
"@steedos/router": "2.5.6-beta.4",
|
|
9
|
+
"@steedos/service-object-graphql": "2.5.6-beta.4",
|
|
10
|
+
"@steedos/service-ui": "2.5.6-beta.4",
|
|
11
11
|
"graphql": "^15.8.0",
|
|
12
12
|
"graphql-iso-date": "^3.6.1",
|
|
13
13
|
"graphql-type-json": "^0.3.2",
|
|
14
|
+
"lru_map": "^0.4.1",
|
|
14
15
|
"moleculer": "^0.14.25",
|
|
15
16
|
"moleculer-apollo-server": "^0.3.6",
|
|
16
17
|
"moleculer-web": "^0.10.4",
|
|
@@ -20,5 +21,5 @@
|
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public"
|
|
22
23
|
},
|
|
23
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "865682da22058bd2a954753eeeab891612e6b529"
|
|
24
25
|
}
|