@steedos/service-api 2.6.1-beta.3 → 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.
Files changed (2) hide show
  1. package/index.js +140 -5
  2. 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 (useDataLoader) {
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",
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.3",
8
- "@steedos/router": "2.6.1-beta.3",
9
- "@steedos/service-object-graphql": "2.6.1-beta.3",
10
- "@steedos/service-ui": "2.6.1-beta.3",
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": "aadc252f0493eacc639632ba9c3baae7a22df442"
24
+ "gitHead": "d77961251196c5352622d977e554660796ed6208"
24
25
  }