@steedos/service-api 2.6.1-beta.5 → 2.6.1-beta.6
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 +140 -5
- package/package.json +7 -6
package/index.js
CHANGED
|
@@ -14,10 +14,10 @@ const SteedosRouter = require('@steedos/router');
|
|
|
14
14
|
const _ = require('lodash');
|
|
15
15
|
const ServiceObjectGraphql = require('@steedos/service-object-graphql')
|
|
16
16
|
const open = require('open');
|
|
17
|
-
|
|
17
|
+
const DataLoader = require("dataloader");
|
|
18
|
+
const { LRUMap } = require('lru_map');
|
|
18
19
|
const validator = require('validator');
|
|
19
|
-
const enablePlayground = validator.toBoolean(process.env.STEEDOS_GRAPHQL_ENABLE_CONSOLE || 'true', true)
|
|
20
|
-
|
|
20
|
+
const enablePlayground = validator.toBoolean(process.env.STEEDOS_GRAPHQL_ENABLE_CONSOLE || 'true', true);
|
|
21
21
|
const mixinOptions = {
|
|
22
22
|
|
|
23
23
|
// Global GraphQL typeDefs
|
|
@@ -316,6 +316,76 @@ module.exports = {
|
|
|
316
316
|
},
|
|
317
317
|
|
|
318
318
|
methods: {
|
|
319
|
+
/**
|
|
320
|
+
* Get the unique key assigned to the DataLoader map
|
|
321
|
+
* @param {string} actionName - Fully qualified action name to bind to dataloader
|
|
322
|
+
* @param {Object.<string, any>} staticParams - Static parameters to use in dataloader
|
|
323
|
+
* @param {Object.<string, any>} args - Arguments passed to GraphQL child resolver
|
|
324
|
+
* @returns {string} Key to the dataloader instance
|
|
325
|
+
*/
|
|
326
|
+
getObjectDataLoaderMapKey(objectName) {
|
|
327
|
+
if (objectName) {
|
|
328
|
+
return `object:${objectName}`;
|
|
329
|
+
}
|
|
330
|
+
// 如果没有objectName,则抛出错误信息
|
|
331
|
+
throw new Error("objectName is required");
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
async objectDataLoaderHandler(actionName, staticParams, rootParams, graphqlCtx) {
|
|
336
|
+
const rootKeys = Object.keys(rootParams);
|
|
337
|
+
const {root, args, context, resolveInfo} = graphqlCtx;
|
|
338
|
+
const dataLoaderMapKey = this.getObjectDataLoaderMapKey(
|
|
339
|
+
staticParams.__objectName || staticParams.objectName
|
|
340
|
+
);
|
|
341
|
+
const objectDataLoaders = this.objectDataLoaders;
|
|
342
|
+
// if a dataLoader batching parameter is specified, then all root params can be data loaded;
|
|
343
|
+
// otherwise use only the primary rootParam
|
|
344
|
+
const primaryDataLoaderRootKey = rootKeys[0]; // for dataloader, use the first root key only
|
|
345
|
+
const dataLoaderBatchParam = this.dataLoaderBatchParams.get(actionName);
|
|
346
|
+
const dataLoaderUseAllRootKeys = dataLoaderBatchParam != null;
|
|
347
|
+
|
|
348
|
+
// check to see if the DataLoader has already been added to the GraphQL context; if not then add it for subsequent use
|
|
349
|
+
let dataLoader;
|
|
350
|
+
// console.log(`objectDataLoaders.has(dataLoaderMapKey)`, objectDataLoaders.has(dataLoaderMapKey))
|
|
351
|
+
if (objectDataLoaders.has(dataLoaderMapKey)) {
|
|
352
|
+
dataLoader = objectDataLoaders.get(dataLoaderMapKey);
|
|
353
|
+
} else {
|
|
354
|
+
const batchedParamKey =
|
|
355
|
+
dataLoaderBatchParam || rootParams[primaryDataLoaderRootKey];
|
|
356
|
+
dataLoader = this.buildDataLoader(
|
|
357
|
+
context.ctx,
|
|
358
|
+
actionName,
|
|
359
|
+
batchedParamKey,
|
|
360
|
+
staticParams,
|
|
361
|
+
args,
|
|
362
|
+
{ hashCacheKey: dataLoaderUseAllRootKeys } // must hash the cache key if not loading scalar
|
|
363
|
+
);
|
|
364
|
+
objectDataLoaders.set(dataLoaderMapKey, dataLoader);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
let dataLoaderKey;
|
|
368
|
+
if (dataLoaderUseAllRootKeys) {
|
|
369
|
+
if (root && rootKeys) {
|
|
370
|
+
dataLoaderKey = {};
|
|
371
|
+
|
|
372
|
+
rootKeys.forEach(key => {
|
|
373
|
+
_.set(dataLoaderKey, rootParams[key], _.get(root, key));
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
dataLoaderKey = root && _.get(root, primaryDataLoaderRootKey);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (dataLoaderKey == null) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return Array.isArray(dataLoaderKey)
|
|
385
|
+
? await dataLoader.loadMany(dataLoaderKey)
|
|
386
|
+
: await dataLoader.load(dataLoaderKey);
|
|
387
|
+
},
|
|
388
|
+
|
|
319
389
|
/**
|
|
320
390
|
* Authenticate the request. It check the `Authorization` token value in the request header.
|
|
321
391
|
* Check the token value & resolve the user by the token.
|
|
@@ -372,16 +442,18 @@ module.exports = {
|
|
|
372
442
|
createActionResolver(actionName, def = {}) {
|
|
373
443
|
const {
|
|
374
444
|
dataLoader: useDataLoader = false,
|
|
445
|
+
objectDataLoader: useObjectDataLoader = false,
|
|
375
446
|
nullIfError = false,
|
|
376
447
|
params: staticParams = {},
|
|
377
448
|
rootParams = {},
|
|
378
449
|
fileUploadArg = null,
|
|
379
450
|
} = def;
|
|
380
451
|
const rootKeys = Object.keys(rootParams);
|
|
381
|
-
|
|
382
452
|
return async (root, args, context, resolveInfo) => {
|
|
383
453
|
try {
|
|
384
|
-
if
|
|
454
|
+
if(useObjectDataLoader){
|
|
455
|
+
return await this.objectDataLoaderHandler(actionName, staticParams, rootParams, {root, args, context, resolveInfo});
|
|
456
|
+
}else if (useDataLoader) {
|
|
385
457
|
const dataLoaderMapKey = this.getDataLoaderMapKey(
|
|
386
458
|
actionName,
|
|
387
459
|
staticParams,
|
|
@@ -820,11 +892,65 @@ module.exports = {
|
|
|
820
892
|
}
|
|
821
893
|
},
|
|
822
894
|
|
|
895
|
+
/**
|
|
896
|
+
* Build a DataLoader instance
|
|
897
|
+
*
|
|
898
|
+
* @param {Object} ctx - Moleculer context
|
|
899
|
+
* @param {string} actionName - Fully qualified action name to bind to dataloader
|
|
900
|
+
* @param {string} batchedParamKey - Parameter key to use for loaded values
|
|
901
|
+
* @param {Object} staticParams - Static parameters to use in dataloader
|
|
902
|
+
* @param {Object} args - Arguments passed to GraphQL child resolver
|
|
903
|
+
* @param {Object} [options={}] - Optional arguments
|
|
904
|
+
* @param {Boolean} [options.hashCacheKey=false] - Use a hash for the cacheKeyFn
|
|
905
|
+
* @returns {DataLoader} Dataloader instance
|
|
906
|
+
*/
|
|
907
|
+
buildDataLoader(
|
|
908
|
+
ctx,
|
|
909
|
+
actionName,
|
|
910
|
+
batchedParamKey,
|
|
911
|
+
staticParams,
|
|
912
|
+
args,
|
|
913
|
+
{ hashCacheKey = false } = {}
|
|
914
|
+
) {
|
|
915
|
+
const batchLoadFn = keys => {
|
|
916
|
+
const rootParams = { [batchedParamKey]: keys };
|
|
917
|
+
return ctx.call(actionName, _.defaultsDeep({}, args, rootParams, staticParams));
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
const dataLoaderOptions = this.dataLoaderOptions.get(actionName) || {};
|
|
921
|
+
const cacheKeyFn = hashCacheKey && (key => hash(key));
|
|
922
|
+
const options = {
|
|
923
|
+
...(cacheKeyFn && { cacheKeyFn }),
|
|
924
|
+
cacheMap: new LRUMap(1000),
|
|
925
|
+
...dataLoaderOptions,
|
|
926
|
+
};
|
|
927
|
+
return new DataLoader(batchLoadFn, options);
|
|
928
|
+
}
|
|
929
|
+
|
|
823
930
|
},
|
|
824
931
|
created() {
|
|
932
|
+
this.objectDataLoaders = new Map();
|
|
825
933
|
this.app = SteedosRouter.staticRouter();
|
|
826
934
|
},
|
|
827
935
|
events:{
|
|
936
|
+
'@objectRecordEvent.*.*': function(ctx){
|
|
937
|
+
const { objectApiName, isUpdate, isDelete, id, doc } = ctx.params;
|
|
938
|
+
if(objectApiName && (isUpdate || isDelete)){
|
|
939
|
+
const key = this.getObjectDataLoaderMapKey(
|
|
940
|
+
objectApiName
|
|
941
|
+
);
|
|
942
|
+
let dataLoaderKeys = [id];
|
|
943
|
+
if(objectApiName === 'space_users'){
|
|
944
|
+
dataLoaderKeys.push(doc.user)
|
|
945
|
+
}
|
|
946
|
+
const loader = this.objectDataLoaders.get(key);
|
|
947
|
+
if(loader){
|
|
948
|
+
for(const dataLoaderKey of dataLoaderKeys){
|
|
949
|
+
loader.clear(dataLoaderKey);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
},
|
|
828
954
|
'service-ui.started': function(){
|
|
829
955
|
this.app.use("/", this.express());
|
|
830
956
|
},
|
|
@@ -871,5 +997,14 @@ module.exports = {
|
|
|
871
997
|
global.SteedosApi = {
|
|
872
998
|
express: this.express
|
|
873
999
|
}
|
|
1000
|
+
},
|
|
1001
|
+
|
|
1002
|
+
actions: {
|
|
1003
|
+
getObjectDisplayData: {
|
|
1004
|
+
handler(ctx) {
|
|
1005
|
+
const { objectApiName, recordId, fields } = ctx.params;
|
|
1006
|
+
return this.getObjectDisplayData(objectApiName, recordId, fields);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
874
1009
|
}
|
|
875
1010
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steedos/service-api",
|
|
3
|
-
"version": "2.6.1-beta.
|
|
3
|
+
"version": "2.6.1-beta.6",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@steedos/auth": "2.6.1-beta.
|
|
8
|
-
"@steedos/router": "2.6.1-beta.
|
|
9
|
-
"@steedos/service-object-graphql": "2.6.1-beta.
|
|
10
|
-
"@steedos/service-ui": "2.6.1-beta.
|
|
7
|
+
"@steedos/auth": "2.6.1-beta.6",
|
|
8
|
+
"@steedos/router": "2.6.1-beta.6",
|
|
9
|
+
"@steedos/service-object-graphql": "2.6.1-beta.6",
|
|
10
|
+
"@steedos/service-ui": "2.6.1-beta.6",
|
|
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": "d77961251196c5352622d977e554660796ed6208"
|
|
24
25
|
}
|