@ocap/gql 1.6.5 → 1.6.10

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 (3) hide show
  1. package/lib/index.js +113 -66
  2. package/lib/ws.js +31 -0
  3. package/package.json +12 -6
package/lib/index.js CHANGED
@@ -1,74 +1,94 @@
1
+ const get = require('lodash/get');
2
+ const { createServer } = require('http');
1
3
  const { graphqlHTTP } = require('express-graphql');
2
4
  const { buildSchema } = require('graphql');
3
5
  const schemaSource = require('@ocap/schema');
4
6
 
5
- const wrapResolver = async (keys, callback) => {
6
- try {
7
- const result = await callback();
8
-
9
- const resultsMap = {};
10
- if (Array.isArray(keys)) {
11
- keys.forEach((key) => {
12
- resultsMap[key] = result[key];
13
- });
14
- } else {
15
- resultsMap[keys] = result;
16
- }
17
-
18
- return {
19
- code: 'OK',
20
- page: result ? result.paging || {} : null,
21
- ...resultsMap,
22
- };
23
- } catch (err) {
24
- console.error('GraphQL API Error', err);
25
- const tmpKeys = Array.isArray(keys) ? keys : [keys];
26
- const tmpKeysMap = tmpKeys.reduce((acc, k) => {
27
- acc[k] = null;
28
- return acc;
29
- }, {});
30
-
31
- return {
32
- code: err.code,
33
- message: err.message,
34
- ...tmpKeysMap,
35
- };
7
+ // const debug = require('debug')(require('../package.json').name);
8
+ const createWebSocketServer = require('./ws');
9
+
10
+ const wrapResolver = async (name, keys, callback) => {
11
+ const result = await callback();
12
+
13
+ const resultsMap = {};
14
+ if (Array.isArray(keys)) {
15
+ keys.forEach((key) => {
16
+ resultsMap[key] = result[key];
17
+ });
18
+ } else {
19
+ resultsMap[keys] = result;
36
20
  }
21
+
22
+ return {
23
+ code: 'OK',
24
+ page: result ? result.paging || {} : null,
25
+ ...resultsMap,
26
+ };
37
27
  };
38
28
 
39
- const createResolvers = (resolver) => ({
40
- sendTx: (args) => wrapResolver('hash', () => resolver.sendTx(args)),
41
- subscribe: (args) => wrapResolver('value', () => resolver.subscribe(args)),
42
- unsubscribe: (args) => wrapResolver('result', () => resolver.unsubscribe(args)),
43
-
44
- getAccountState: (args) => wrapResolver('state', () => resolver.getAccountState(args)),
45
- getAssetState: (args) => wrapResolver('state', () => resolver.getAssetState(args)),
46
-
47
- getBlock: (args) => wrapResolver('block', () => resolver.getBlock(args)),
48
- getBlocks: (args) => wrapResolver('blocks', () => resolver.getBlocks(args)),
49
- getChainInfo: (args) => wrapResolver('info', () => resolver.getChainInfo(args)),
50
- getConfig: (args) => wrapResolver('config', () => resolver.getConfig(args)),
51
- getDelegateState: (args) => wrapResolver('state', () => resolver.getDelegateState(args)),
52
- getForgeState: (args) => wrapResolver('state', () => resolver.getForgeState(args)),
53
- getForgeStats: (args) => wrapResolver('forgeStats', () => resolver.getForgeStats(args)),
54
- getHealthStatus: (args) => wrapResolver('healthStatus', () => resolver.getHealthStatus(args)),
55
- getNetInfo: (args) => wrapResolver('netInfo', () => resolver.getNetInfo(args)),
56
- getNodeInfo: (args) => wrapResolver('info', () => resolver.getNodeInfo(args)),
57
- getSwapState: (args) => wrapResolver('state', () => resolver.getSwapState(args)),
58
- getSwapStatistics: (args) => wrapResolver('statistics', () => resolver.getSwapStatistics(args)),
59
- getTx: (args) => wrapResolver('info', () => resolver.getTx(args)),
60
- getUnconfirmedTxs: (args) => wrapResolver('unconfirmedTxs', () => resolver.getUnconfirmedTxs(args)),
61
- getValidatorsInfo: (args) => wrapResolver('validatorsInfo', () => resolver.getValidatorsInfo(args)),
62
- listAssetTransactions: (args) => wrapResolver('transactions', () => resolver.listAssetTransactions(args)),
63
- listAssets: (args) => wrapResolver(['assets', 'account'], () => resolver.listAssets(args)),
64
- listBlocks: (args) => wrapResolver('blocks', () => resolver.listBlocks(args)),
65
- listStakes: (args) => wrapResolver('stakes', () => resolver.listStakes(args)),
66
- listSwap: (args) => wrapResolver('swap', () => resolver.listSwap(args)),
67
- listTopAccounts: (args) => wrapResolver('accounts', () => resolver.listTopAccounts(args)),
68
- listTransactions: (args) => wrapResolver('transactions', () => resolver.listTransactions(args)),
69
- });
70
-
71
- module.exports = (resolver, graphiql = true) =>
29
+ // eslint-disable-next-line no-unused-vars
30
+ const formatContext = (ctx = {}) => {
31
+ const featureSwitch = {};
32
+ return { featureSwitch };
33
+ };
34
+
35
+ const resolvers = [
36
+ { fn: 'sendTx', dataKey: 'hash' },
37
+
38
+ { fn: 'subscribe', dataKey: 'value' },
39
+ { fn: 'unsubscribe', dataKey: 'result' },
40
+
41
+ { fn: 'getAccountState', dataKey: 'state' },
42
+ { fn: 'getAssetState', dataKey: 'state' },
43
+ { fn: 'getFactoryState', dataKey: 'state' },
44
+ { fn: 'getDelegateState', dataKey: 'state' },
45
+ { fn: 'getTokenState', dataKey: 'state' },
46
+ { fn: 'getForgeState', dataKey: 'state' },
47
+ { fn: 'getStakeState', dataKey: 'state' },
48
+ { fn: 'getEvidenceState', dataKey: 'state' },
49
+ { fn: 'getRollupState', dataKey: 'state' },
50
+ { fn: 'getRollupBlock', dataKey: 'block' },
51
+
52
+ { fn: 'getAccountTokens', dataKey: 'tokens' },
53
+ { fn: 'getBlock', dataKey: 'block' },
54
+ { fn: 'getBlocks', dataKey: 'blocks' },
55
+ { fn: 'getChainInfo', dataKey: 'info' },
56
+ { fn: 'getConfig', dataKey: 'config' },
57
+ { fn: 'getForgeStats', dataKey: 'forgeStats' },
58
+ { fn: 'getNetInfo', dataKey: 'netInfo' },
59
+ { fn: 'getNodeInfo', dataKey: 'info' },
60
+ { fn: 'getTx', dataKey: 'info' },
61
+ { fn: 'getUnconfirmedTxs', dataKey: 'unconfirmedTxs' },
62
+ { fn: 'getValidatorsInfo', dataKey: 'validatorsInfo' },
63
+
64
+ { fn: 'listTransactions', dataKey: ['transactions'] },
65
+ { fn: 'listAssets', dataKey: ['assets', 'account'] },
66
+ { fn: 'listAssetTransactions', dataKey: ['transactions'] },
67
+ { fn: 'listFactories', dataKey: ['factories'] },
68
+ { fn: 'listTokens', dataKey: ['tokens'] },
69
+ { fn: 'listTopAccounts', dataKey: ['accounts'] },
70
+ { fn: 'listBlocks', dataKey: ['blocks'] },
71
+ { fn: 'listStakes', dataKey: ['stakes'] },
72
+ { fn: 'listRollups', dataKey: ['rollups'] },
73
+ { fn: 'listRollupBlocks', dataKey: ['blocks'] },
74
+ { fn: 'listRollupValidators', dataKey: ['validators'] },
75
+
76
+ { fn: 'search', dataKey: ['results'] },
77
+ ];
78
+
79
+ const createResolvers = (resolver) =>
80
+ resolvers.reduce((acc, { fn, dataKey }) => {
81
+ acc[fn] = (args, ctx) => wrapResolver(fn, dataKey, () => resolver[fn](args, formatContext(ctx)));
82
+ return acc;
83
+ }, {});
84
+
85
+ const defaultErrorHandler = (err) => {
86
+ if (process.env.NODE_ENV !== 'test') {
87
+ console.error('GraphQLError', err.originalError || err);
88
+ }
89
+ };
90
+
91
+ const createHttpHandler = ({ resolver, onError = defaultErrorHandler, graphiql = true }) =>
72
92
  graphqlHTTP({
73
93
  schema: buildSchema(schemaSource),
74
94
  rootValue: createResolvers(resolver),
@@ -76,7 +96,34 @@ module.exports = (resolver, graphiql = true) =>
76
96
  // NOTE: following is required because of following gql query validation issue
77
97
  // https://stackoverflow.com/questions/56695262/graphql-error-fieldsconflict-fields-have-different-list-shapes
78
98
  customValidateFn: () => true,
99
+
100
+ // format custom error code
101
+ customFormatErrorFn: (err) => {
102
+ onError(err);
103
+
104
+ const isProd = process.env.NODE_ENV === 'production';
105
+ return {
106
+ code: get(err, 'originalError.code', 'INTERNAL'),
107
+ message: err.message,
108
+ locations: err.locations || [],
109
+ stack: isProd ? [] : get(err, 'originalError.stack', '').split('\n'),
110
+ path: err.path,
111
+ };
112
+ },
79
113
  });
80
114
 
81
- module.exports.wrapResolver = wrapResolver;
82
- module.exports.createResolvers = createResolvers;
115
+ const createSocketHandler = ({ app, indexdb }) => {
116
+ const httpServer = createServer(app);
117
+ const wsServer = createWebSocketServer({ indexdb });
118
+ wsServer.attach(httpServer);
119
+
120
+ return httpServer;
121
+ };
122
+
123
+ module.exports = {
124
+ createHttpHandler,
125
+ createSocketHandler,
126
+ wrapResolver,
127
+ createResolvers,
128
+ formatContext,
129
+ };
package/lib/ws.js ADDED
@@ -0,0 +1,31 @@
1
+ const { WsServer } = require('@arcblock/ws');
2
+
3
+ module.exports = function createWebsocketServer({ indexdb }) {
4
+ const wsServer = new WsServer({});
5
+
6
+ const mutableTables = ['account', 'asset', 'delegation', 'rollup', 'stake'];
7
+ const appendOnlyTables = ['tx', 'token', 'rollupBlock', 'factory'];
8
+ const map = { insert: 'create', update: 'update' };
9
+
10
+ // For entities that support updating
11
+ mutableTables.forEach((table) => {
12
+ ['insert', 'update'].forEach((action) => {
13
+ indexdb[table].on(action, (data) => wsServer.broadcast([table, map[action]].join('.'), data));
14
+ });
15
+ });
16
+
17
+ // For entities that are immutable after creation
18
+ appendOnlyTables.forEach((table) => {
19
+ ['insert'].forEach((action) => {
20
+ indexdb[table].on(action, (data) => wsServer.broadcast([table, map[action]].join('.'), data));
21
+ });
22
+ });
23
+
24
+ // For tx sub topic
25
+ indexdb.tx.on('insert', (tx) => {
26
+ const typeUrl = tx.tx.itxJson.type_url.split(':').pop();
27
+ wsServer.broadcast(`tx.${typeUrl}`, tx);
28
+ });
29
+
30
+ return wsServer;
31
+ };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.6.5",
6
+ "version": "1.6.10",
7
7
  "description": "Resolver middleware for ocap adapters",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -12,20 +12,26 @@
12
12
  "scripts": {
13
13
  "lint": "eslint tests lib",
14
14
  "lint:fix": "eslint --fix tests lib",
15
- "test": "node tools/jest.js",
15
+ "test": "jest --forceExit --detectOpenHandles",
16
16
  "coverage": "npm run test -- --coverage"
17
17
  },
18
18
  "keywords": [],
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
+ "contributors": [
21
+ "wangshijun <shijun@arcblock.io> (https://github.com/wangshijun)"
22
+ ],
20
23
  "license": "MIT",
21
24
  "dependencies": {
22
- "@ocap/schema": "^1.6.5",
25
+ "@arcblock/ws": "1.6.10",
26
+ "@ocap/schema": "1.6.10",
27
+ "debug": "^4.3.3",
23
28
  "express-graphql": "^0.12.0",
24
- "graphql": "14.6.0"
29
+ "graphql": "14.6.0",
30
+ "lodash": "^4.17.21"
25
31
  },
26
32
  "devDependencies": {
27
33
  "express": "^4.17.1",
28
- "jest": "^26.6.3"
34
+ "jest": "^27.3.1"
29
35
  },
30
- "gitHead": "5779448f13824de38978df3c84c9da0c1e1ad989"
36
+ "gitHead": "ab272e8db3a15c6571cc7fae7cc3d3e0fdd4bdb1"
31
37
  }