@steedos/service-api 2.6.1-beta.5 → 2.6.1-beta.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 +152 -12
- package/package.json +12 -6
package/index.js
CHANGED
|
@@ -13,11 +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
|
|
17
|
-
|
|
16
|
+
const DataLoader = require("dataloader");
|
|
17
|
+
const { LRUMap } = require('lru_map');
|
|
18
18
|
const validator = require('validator');
|
|
19
|
-
const enablePlayground = validator.toBoolean(process.env.STEEDOS_GRAPHQL_ENABLE_CONSOLE || 'true', true)
|
|
20
|
-
|
|
19
|
+
const enablePlayground = validator.toBoolean(process.env.STEEDOS_GRAPHQL_ENABLE_CONSOLE || 'true', true);
|
|
20
|
+
const openBrowser = require('react-dev-utils/openBrowser');
|
|
21
21
|
const mixinOptions = {
|
|
22
22
|
|
|
23
23
|
// Global GraphQL typeDefs
|
|
@@ -101,6 +101,8 @@ module.exports = {
|
|
|
101
101
|
ServiceObjectGraphql
|
|
102
102
|
],
|
|
103
103
|
|
|
104
|
+
projectStarted: false,
|
|
105
|
+
|
|
104
106
|
// More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html
|
|
105
107
|
settings: {
|
|
106
108
|
server: false,
|
|
@@ -316,6 +318,76 @@ module.exports = {
|
|
|
316
318
|
},
|
|
317
319
|
|
|
318
320
|
methods: {
|
|
321
|
+
/**
|
|
322
|
+
* Get the unique key assigned to the DataLoader map
|
|
323
|
+
* @param {string} actionName - Fully qualified action name to bind to dataloader
|
|
324
|
+
* @param {Object.<string, any>} staticParams - Static parameters to use in dataloader
|
|
325
|
+
* @param {Object.<string, any>} args - Arguments passed to GraphQL child resolver
|
|
326
|
+
* @returns {string} Key to the dataloader instance
|
|
327
|
+
*/
|
|
328
|
+
getObjectDataLoaderMapKey(objectName) {
|
|
329
|
+
if (objectName) {
|
|
330
|
+
return `object:${objectName}`;
|
|
331
|
+
}
|
|
332
|
+
// 如果没有objectName,则抛出错误信息
|
|
333
|
+
throw new Error("objectName is required");
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
async objectDataLoaderHandler(actionName, staticParams, rootParams, graphqlCtx) {
|
|
338
|
+
const rootKeys = Object.keys(rootParams);
|
|
339
|
+
const {root, args, context, resolveInfo} = graphqlCtx;
|
|
340
|
+
const dataLoaderMapKey = this.getObjectDataLoaderMapKey(
|
|
341
|
+
staticParams.__objectName || staticParams.objectName
|
|
342
|
+
);
|
|
343
|
+
const objectDataLoaders = this.objectDataLoaders;
|
|
344
|
+
// if a dataLoader batching parameter is specified, then all root params can be data loaded;
|
|
345
|
+
// otherwise use only the primary rootParam
|
|
346
|
+
const primaryDataLoaderRootKey = rootKeys[0]; // for dataloader, use the first root key only
|
|
347
|
+
const dataLoaderBatchParam = this.dataLoaderBatchParams.get(actionName);
|
|
348
|
+
const dataLoaderUseAllRootKeys = dataLoaderBatchParam != null;
|
|
349
|
+
|
|
350
|
+
// check to see if the DataLoader has already been added to the GraphQL context; if not then add it for subsequent use
|
|
351
|
+
let dataLoader;
|
|
352
|
+
// console.log(`objectDataLoaders.has(dataLoaderMapKey)`, objectDataLoaders.has(dataLoaderMapKey))
|
|
353
|
+
if (objectDataLoaders.has(dataLoaderMapKey)) {
|
|
354
|
+
dataLoader = objectDataLoaders.get(dataLoaderMapKey);
|
|
355
|
+
} else {
|
|
356
|
+
const batchedParamKey =
|
|
357
|
+
dataLoaderBatchParam || rootParams[primaryDataLoaderRootKey];
|
|
358
|
+
dataLoader = this.buildDataLoader(
|
|
359
|
+
context.ctx,
|
|
360
|
+
actionName,
|
|
361
|
+
batchedParamKey,
|
|
362
|
+
staticParams,
|
|
363
|
+
args,
|
|
364
|
+
{ hashCacheKey: dataLoaderUseAllRootKeys } // must hash the cache key if not loading scalar
|
|
365
|
+
);
|
|
366
|
+
objectDataLoaders.set(dataLoaderMapKey, dataLoader);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
let dataLoaderKey;
|
|
370
|
+
if (dataLoaderUseAllRootKeys) {
|
|
371
|
+
if (root && rootKeys) {
|
|
372
|
+
dataLoaderKey = {};
|
|
373
|
+
|
|
374
|
+
rootKeys.forEach(key => {
|
|
375
|
+
_.set(dataLoaderKey, rootParams[key], _.get(root, key));
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
dataLoaderKey = root && _.get(root, primaryDataLoaderRootKey);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (dataLoaderKey == null) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return Array.isArray(dataLoaderKey)
|
|
387
|
+
? await dataLoader.loadMany(dataLoaderKey)
|
|
388
|
+
: await dataLoader.load(dataLoaderKey);
|
|
389
|
+
},
|
|
390
|
+
|
|
319
391
|
/**
|
|
320
392
|
* Authenticate the request. It check the `Authorization` token value in the request header.
|
|
321
393
|
* Check the token value & resolve the user by the token.
|
|
@@ -372,16 +444,18 @@ module.exports = {
|
|
|
372
444
|
createActionResolver(actionName, def = {}) {
|
|
373
445
|
const {
|
|
374
446
|
dataLoader: useDataLoader = false,
|
|
447
|
+
objectDataLoader: useObjectDataLoader = false,
|
|
375
448
|
nullIfError = false,
|
|
376
449
|
params: staticParams = {},
|
|
377
450
|
rootParams = {},
|
|
378
451
|
fileUploadArg = null,
|
|
379
452
|
} = def;
|
|
380
453
|
const rootKeys = Object.keys(rootParams);
|
|
381
|
-
|
|
382
454
|
return async (root, args, context, resolveInfo) => {
|
|
383
455
|
try {
|
|
384
|
-
if
|
|
456
|
+
if(useObjectDataLoader){
|
|
457
|
+
return await this.objectDataLoaderHandler(actionName, staticParams, rootParams, {root, args, context, resolveInfo});
|
|
458
|
+
}else if (useDataLoader) {
|
|
385
459
|
const dataLoaderMapKey = this.getDataLoaderMapKey(
|
|
386
460
|
actionName,
|
|
387
461
|
staticParams,
|
|
@@ -820,11 +894,65 @@ module.exports = {
|
|
|
820
894
|
}
|
|
821
895
|
},
|
|
822
896
|
|
|
897
|
+
/**
|
|
898
|
+
* Build a DataLoader instance
|
|
899
|
+
*
|
|
900
|
+
* @param {Object} ctx - Moleculer context
|
|
901
|
+
* @param {string} actionName - Fully qualified action name to bind to dataloader
|
|
902
|
+
* @param {string} batchedParamKey - Parameter key to use for loaded values
|
|
903
|
+
* @param {Object} staticParams - Static parameters to use in dataloader
|
|
904
|
+
* @param {Object} args - Arguments passed to GraphQL child resolver
|
|
905
|
+
* @param {Object} [options={}] - Optional arguments
|
|
906
|
+
* @param {Boolean} [options.hashCacheKey=false] - Use a hash for the cacheKeyFn
|
|
907
|
+
* @returns {DataLoader} Dataloader instance
|
|
908
|
+
*/
|
|
909
|
+
buildDataLoader(
|
|
910
|
+
ctx,
|
|
911
|
+
actionName,
|
|
912
|
+
batchedParamKey,
|
|
913
|
+
staticParams,
|
|
914
|
+
args,
|
|
915
|
+
{ hashCacheKey = false } = {}
|
|
916
|
+
) {
|
|
917
|
+
const batchLoadFn = keys => {
|
|
918
|
+
const rootParams = { [batchedParamKey]: keys };
|
|
919
|
+
return ctx.call(actionName, _.defaultsDeep({}, args, rootParams, staticParams));
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
const dataLoaderOptions = this.dataLoaderOptions.get(actionName) || {};
|
|
923
|
+
const cacheKeyFn = hashCacheKey && (key => hash(key));
|
|
924
|
+
const options = {
|
|
925
|
+
...(cacheKeyFn && { cacheKeyFn }),
|
|
926
|
+
cacheMap: new LRUMap(1000),
|
|
927
|
+
...dataLoaderOptions,
|
|
928
|
+
};
|
|
929
|
+
return new DataLoader(batchLoadFn, options);
|
|
930
|
+
}
|
|
931
|
+
|
|
823
932
|
},
|
|
824
933
|
created() {
|
|
934
|
+
this.objectDataLoaders = new Map();
|
|
825
935
|
this.app = SteedosRouter.staticRouter();
|
|
826
936
|
},
|
|
827
937
|
events:{
|
|
938
|
+
'@objectRecordEvent.*.*': function(ctx){
|
|
939
|
+
const { objectApiName, isUpdate, isDelete, id, doc } = ctx.params;
|
|
940
|
+
if(objectApiName && (isUpdate || isDelete)){
|
|
941
|
+
const key = this.getObjectDataLoaderMapKey(
|
|
942
|
+
objectApiName
|
|
943
|
+
);
|
|
944
|
+
let dataLoaderKeys = [id];
|
|
945
|
+
if(objectApiName === 'space_users'){
|
|
946
|
+
dataLoaderKeys.push(doc.user)
|
|
947
|
+
}
|
|
948
|
+
const loader = this.objectDataLoaders.get(key);
|
|
949
|
+
if(loader){
|
|
950
|
+
for(const dataLoaderKey of dataLoaderKeys){
|
|
951
|
+
loader.clear(dataLoaderKey);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
},
|
|
828
956
|
'service-ui.started': function(){
|
|
829
957
|
this.app.use("/", this.express());
|
|
830
958
|
},
|
|
@@ -838,12 +966,15 @@ module.exports = {
|
|
|
838
966
|
console.log(`Project is running at ${process.env.ROOT_URL}`);
|
|
839
967
|
console.log('');
|
|
840
968
|
if (process.env.STEEDOS_AUTO_OPEN_BROWSER != 'false') { // 默认打开,如果不想打开,设置STEEDOS_AUTO_OPEN_BROWSER=false
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
969
|
+
setTimeout(function () {
|
|
970
|
+
try {
|
|
971
|
+
openBrowser(process.env.ROOT_URL);
|
|
972
|
+
} catch (error) {
|
|
973
|
+
console.error(error);
|
|
974
|
+
console.error('auto open browser failed.');
|
|
975
|
+
}
|
|
976
|
+
}, 100)
|
|
977
|
+
|
|
847
978
|
}
|
|
848
979
|
}
|
|
849
980
|
}
|
|
@@ -871,5 +1002,14 @@ module.exports = {
|
|
|
871
1002
|
global.SteedosApi = {
|
|
872
1003
|
express: this.express
|
|
873
1004
|
}
|
|
1005
|
+
},
|
|
1006
|
+
|
|
1007
|
+
actions: {
|
|
1008
|
+
getObjectDisplayData: {
|
|
1009
|
+
handler(ctx) {
|
|
1010
|
+
const { objectApiName, recordId, fields } = ctx.params;
|
|
1011
|
+
return this.getObjectDisplayData(objectApiName, recordId, fields);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
874
1014
|
}
|
|
875
1015
|
};
|
package/package.json
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steedos/service-api",
|
|
3
|
-
"version": "2.6.1-beta.
|
|
3
|
+
"version": "2.6.1-beta.7",
|
|
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.7",
|
|
8
|
+
"@steedos/router": "2.6.1-beta.7",
|
|
9
|
+
"@steedos/service-object-graphql": "2.6.1-beta.7",
|
|
10
|
+
"@steedos/service-ui": "2.6.1-beta.7",
|
|
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",
|
|
18
|
+
"react-dev-utils": "12.0.1",
|
|
17
19
|
"validator": "^13.6.0"
|
|
18
20
|
},
|
|
19
21
|
"private": false,
|
|
20
22
|
"publishConfig": {
|
|
21
23
|
"access": "public"
|
|
22
24
|
},
|
|
23
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "b12f271460ef3686face095e875aa38e8ddc4c7f",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/graphql-iso-date": "^3.4.0",
|
|
28
|
+
"@types/react-dev-utils": "^9.0.11"
|
|
29
|
+
}
|
|
24
30
|
}
|