@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.
Files changed (2) hide show
  1. package/index.js +152 -12
  2. 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 open = require('open');
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 (useDataLoader) {
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
- try {
842
- open(process.env.ROOT_URL);
843
- } catch (error) {
844
- console.error(error);
845
- console.error('auto open browser failed.');
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.5",
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.5",
8
- "@steedos/router": "2.6.1-beta.5",
9
- "@steedos/service-object-graphql": "2.6.1-beta.5",
10
- "@steedos/service-ui": "2.6.1-beta.5",
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": "28d9ffe3cc73f4a89abea60c659f3b89eaaf378b"
25
+ "gitHead": "b12f271460ef3686face095e875aa38e8ddc4c7f",
26
+ "devDependencies": {
27
+ "@types/graphql-iso-date": "^3.4.0",
28
+ "@types/react-dev-utils": "^9.0.11"
29
+ }
24
30
  }