@things-factory/integration-base 6.2.38 → 6.2.40
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/dist-server/engine/connector/operato-connector.js +151 -17
- package/dist-server/engine/connector/operato-connector.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/helps/integration/connector/operato-connector.ja.md +11 -0
- package/helps/integration/connector/operato-connector.kr.md +11 -1
- package/helps/integration/connector/operato-connector.md +10 -0
- package/helps/integration/connector/operato-connector.ms.md +10 -0
- package/helps/integration/connector/operato-connector.zh.md +10 -0
- package/package.json +2 -2
- package/server/engine/connector/operato-connector.ts +189 -23
- package/translations/en.json +2 -1
- package/translations/ja.json +2 -1
- package/translations/ko.json +2 -1
- package/translations/ms.json +2 -1
- package/translations/zh.json +2 -1
@@ -1,10 +1,21 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.OperatoConnector = void 0;
|
3
|
+
exports.OperatoConnector = exports.SUBSCRIPTION_URI = exports.GRAPHQL_URI = void 0;
|
4
|
+
const tslib_1 = require("tslib");
|
4
5
|
require("cross-fetch/polyfill");
|
5
6
|
const core_1 = require("@apollo/client/core");
|
6
7
|
const context_1 = require("@apollo/client/link/context");
|
8
|
+
// for subscription
|
9
|
+
const ws_1 = tslib_1.__importDefault(require("ws"));
|
10
|
+
const graphql_ws_1 = require("graphql-ws");
|
11
|
+
const subscriptions_1 = require("@apollo/client/link/subscriptions");
|
12
|
+
const utilities_1 = require("@apollo/client/utilities");
|
13
|
+
const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
|
7
14
|
const connection_manager_1 = require("../connection-manager");
|
15
|
+
const service_1 = require("../../service");
|
16
|
+
const shell_1 = require("@things-factory/shell");
|
17
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
18
|
+
const debug = require('debug')('things-factory:integration-base:operato-connector-subscription');
|
8
19
|
const defaultOptions = {
|
9
20
|
watchQuery: {
|
10
21
|
fetchPolicy: 'no-cache',
|
@@ -21,42 +32,160 @@ const defaultOptions = {
|
|
21
32
|
const cache = new core_1.InMemoryCache({
|
22
33
|
addTypename: false
|
23
34
|
});
|
35
|
+
exports.GRAPHQL_URI = '/graphql';
|
36
|
+
exports.SUBSCRIPTION_URI = exports.GRAPHQL_URI;
|
37
|
+
async function checkPermission(privilegeObject, user, domain) {
|
38
|
+
if (!privilegeObject || !privilegeObject.privilege || !privilegeObject.category) {
|
39
|
+
return true;
|
40
|
+
}
|
41
|
+
const { owner: domainOwnerGranted, super: superUserGranted, category, privilege } = privilegeObject;
|
42
|
+
return ((domainOwnerGranted && (await process.domainOwnerGranted(domain, user))) ||
|
43
|
+
(superUserGranted && (await process.superUserGranted(domain, user))) ||
|
44
|
+
(category && privilege && (await auth_base_1.User.hasPrivilege(privilege, category, domain, user))));
|
45
|
+
}
|
46
|
+
;
|
47
|
+
``;
|
24
48
|
class OperatoConnector {
|
49
|
+
constructor() {
|
50
|
+
this.subscriptions = [];
|
51
|
+
}
|
25
52
|
async ready(connectionConfigs) {
|
26
53
|
await Promise.all(connectionConfigs.map(this.connect.bind(this)));
|
27
54
|
connection_manager_1.ConnectionManager.logger.info('graphql-connector connections are ready');
|
28
55
|
}
|
29
56
|
async connect(connection) {
|
30
|
-
var { endpoint: uri, params: { authKey, domain } } = connection;
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
connection_manager_1.ConnectionManager.logger.error(`[Network error - ${networkError.statusCode}] ${networkError}`);
|
57
|
+
var { endpoint: uri, params: { authKey, domain, subscriptionHandlers = {} } } = connection;
|
58
|
+
if (!authKey || !domain) {
|
59
|
+
throw new Error('some connection paramter missing.');
|
60
|
+
}
|
61
|
+
var domainOwner = await (0, shell_1.getRepository)(auth_base_1.User).findOne({
|
62
|
+
where: {
|
63
|
+
id: connection.domain.owner
|
38
64
|
}
|
65
|
+
});
|
66
|
+
this.context = {
|
67
|
+
domain: connection.domain,
|
68
|
+
user: domainOwner
|
69
|
+
/* TODO: domainOwner 대신 특정 유저를 지정할 수 있도록 개선해야함. 모든 커넥션에 유저를 지정하는 기능으로 일반화할 필요가 있는 지 고민해야함 */
|
39
70
|
};
|
40
71
|
const httpLink = (0, core_1.createHttpLink)({
|
41
72
|
uri: uri
|
42
73
|
});
|
43
|
-
|
74
|
+
/*
|
75
|
+
CHECKPOINT:
|
76
|
+
1. GraphqQLWsLink를 사용하면 setContext를 통한 추가 헤더 설정이 무시됩니다.
|
77
|
+
따라서, GraphQLWsLink를 사용하려면, connectionParams를 통해 헤더를 설정해야 합니다.
|
78
|
+
|
79
|
+
2. 서버에서 실행시, webSocketImpl을 명시적으로 지정해야 합니다.
|
80
|
+
*/
|
81
|
+
const wsLink = new subscriptions_1.GraphQLWsLink((0, graphql_ws_1.createClient)({
|
82
|
+
url: uri.replace(/^http/, 'ws'),
|
83
|
+
keepAlive: 10000,
|
84
|
+
retryAttempts: 1000000,
|
85
|
+
shouldRetry: e => true,
|
86
|
+
webSocketImpl: ws_1.default,
|
87
|
+
connectionParams: {
|
88
|
+
headers: {
|
89
|
+
'x-things-factory-domain': domain,
|
90
|
+
authorization: authKey ? `Bearer ${authKey}` : ''
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}));
|
94
|
+
const splitLink = (0, core_1.split)(({ query }) => {
|
95
|
+
const def = (0, utilities_1.getMainDefinition)(query);
|
96
|
+
return def.kind === 'OperationDefinition' && def.operation === 'subscription';
|
97
|
+
}, wsLink, (0, context_1.setContext)((_, { headers }) => {
|
98
|
+
return {
|
99
|
+
headers: Object.assign(Object.assign({}, headers), { 'x-things-factory-domain': domain, authorization: authKey ? `Bearer ${authKey}` : '' })
|
100
|
+
};
|
101
|
+
}).concat(httpLink));
|
102
|
+
var client = new core_1.ApolloClient({
|
44
103
|
defaultOptions,
|
45
104
|
cache,
|
46
|
-
link:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
105
|
+
link: splitLink
|
106
|
+
});
|
107
|
+
var subscriptions = [];
|
108
|
+
Object.keys(subscriptionHandlers).forEach(async (tag) => {
|
109
|
+
if (!tag || !subscriptionHandlers[tag])
|
110
|
+
return;
|
111
|
+
let scenarioName = subscriptionHandlers[tag];
|
112
|
+
// fetch a scenario
|
113
|
+
var selectedScenario = await (0, shell_1.getRepository)(service_1.Scenario).findOne({
|
114
|
+
where: {
|
115
|
+
name: scenarioName
|
116
|
+
},
|
117
|
+
relations: ['steps', 'domain']
|
118
|
+
});
|
119
|
+
const subscription = client.subscribe({
|
120
|
+
query: (0, graphql_tag_1.default) `
|
121
|
+
subscription {
|
122
|
+
data(tag: "${tag}") {
|
123
|
+
tag
|
124
|
+
data
|
125
|
+
}
|
126
|
+
}
|
127
|
+
`
|
128
|
+
});
|
129
|
+
var subscriptionObserver = subscription.subscribe({
|
130
|
+
next: async (data) => {
|
131
|
+
var _a;
|
132
|
+
debug('received pubsub msg.:', data === null || data === void 0 ? void 0 : data.data);
|
133
|
+
await this.runScenario(subscriptions, (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.data);
|
134
|
+
},
|
135
|
+
error: error => {
|
136
|
+
connection_manager_1.ConnectionManager.logger.error(`(${connection.name}:${connection.endpoint}) subscription error: ${error}`);
|
137
|
+
},
|
138
|
+
complete: () => {
|
139
|
+
connection_manager_1.ConnectionManager.logger.info(`(${connection.name}:${connection.endpoint}) subscription complete`);
|
140
|
+
}
|
141
|
+
});
|
142
|
+
connection_manager_1.ConnectionManager.logger.info(`(${connection.name}:${connection.endpoint}) subscription closed flag: ${subscriptionObserver.closed}`);
|
143
|
+
subscriptions.push({
|
144
|
+
tag,
|
145
|
+
scenario: selectedScenario,
|
146
|
+
subscriptionObserver
|
147
|
+
});
|
148
|
+
connection_manager_1.ConnectionManager.logger.info(`(${tag}:${scenarioName}) subscription closed flag: ${subscriptionObserver.closed}`);
|
149
|
+
});
|
150
|
+
client['subscriptions'] = subscriptions;
|
151
|
+
connection_manager_1.ConnectionManager.addConnectionInstance(connection, client);
|
52
152
|
connection_manager_1.ConnectionManager.logger.info(`graphql-connector connection(${connection.name}:${connection.endpoint}) is connected`);
|
53
153
|
}
|
54
154
|
async disconnect(connection) {
|
55
155
|
var client = connection_manager_1.ConnectionManager.getConnectionInstance(connection);
|
156
|
+
let subscriptions = client['subscriptions'];
|
157
|
+
subscriptions.forEach(subscription => subscription.subscriptionObserver.unsubscribe());
|
56
158
|
client.stop();
|
57
159
|
connection_manager_1.ConnectionManager.removeConnectionInstance(connection);
|
58
160
|
connection_manager_1.ConnectionManager.logger.info(`graphql-connector connection(${connection.name}) is disconnected`);
|
59
161
|
}
|
162
|
+
async runScenario(subscriptions, variables) {
|
163
|
+
var _a;
|
164
|
+
const { domain, user } = this.context;
|
165
|
+
const { tag } = variables;
|
166
|
+
if (!tag) {
|
167
|
+
throw new Error(`tag is invalid - ${tag}`);
|
168
|
+
}
|
169
|
+
var scenario = (_a = subscriptions.find(subscription => subscription.tag === tag)) === null || _a === void 0 ? void 0 : _a.scenario;
|
170
|
+
if (!scenario) {
|
171
|
+
throw new Error(`scenario is not found - ${tag}`);
|
172
|
+
}
|
173
|
+
if (!(await checkPermission(scenario.privilege, user, domain))) {
|
174
|
+
const { category, privilege } = scenario.privilege || {};
|
175
|
+
throw new Error(`Unauthorized! ${category}-${privilege} privilege required`);
|
176
|
+
}
|
177
|
+
/* create a scenario instance */
|
178
|
+
let instanceName = scenario.name + '-' + String(Date.now());
|
179
|
+
var instance = new service_1.ScenarioInstance(instanceName, scenario, {
|
180
|
+
user,
|
181
|
+
domain,
|
182
|
+
variables,
|
183
|
+
client: shell_1.GraphqlLocalClient.client
|
184
|
+
});
|
185
|
+
// run scenario
|
186
|
+
await instance.run();
|
187
|
+
return instance;
|
188
|
+
}
|
60
189
|
get parameterSpec() {
|
61
190
|
return [
|
62
191
|
{
|
@@ -68,6 +197,11 @@ class OperatoConnector {
|
|
68
197
|
type: 'string',
|
69
198
|
name: 'domain',
|
70
199
|
label: 'domain'
|
200
|
+
},
|
201
|
+
{
|
202
|
+
type: 'tag-scenarios',
|
203
|
+
name: 'subscriptionHandlers',
|
204
|
+
label: 'subscription-handlers'
|
71
205
|
}
|
72
206
|
];
|
73
207
|
}
|
@@ -78,7 +212,7 @@ class OperatoConnector {
|
|
78
212
|
return 'integration/connector/operato-connector';
|
79
213
|
}
|
80
214
|
get description() {
|
81
|
-
return 'Operato
|
215
|
+
return 'Operato Graphql Connector';
|
82
216
|
}
|
83
217
|
}
|
84
218
|
exports.OperatoConnector = OperatoConnector;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"operato-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/operato-connector.ts"],"names":[],"mappings":";;;AAAA,gCAA6B;AAE7B,8CAAiF;AACjF,yDAAwD;AAExD,8DAAyD;AAGzD,MAAM,cAAc,GAAQ;IAC1B,UAAU,EAAE;QACV,WAAW,EAAE,UAAU;QACvB,WAAW,EAAE,QAAQ;KACtB;IACD,KAAK,EAAE;QACL,WAAW,EAAE,UAAU;QACvB,WAAW,EAAE,KAAK;KACnB;IACD,MAAM,EAAE;QACN,WAAW,EAAE,KAAK;KACnB;CACF,CAAA;AAED,MAAM,KAAK,GAAG,IAAI,oBAAa,CAAC;IAC9B,WAAW,EAAE,KAAK;CACnB,CAAC,CAAA;AAEF,MAAa,gBAAgB;IAC3B,KAAK,CAAC,KAAK,CAAC,iBAAiB;QAC3B,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAEjE,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAA;IAC1E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAU;QACtB,IAAI,EACF,QAAQ,EAAE,GAAG,EACb,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAC5B,GAAG,UAAU,CAAA;QAEd,IAAI,aAAa,GAAQ,CAAC,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE,EAAE;YAC3D,IAAI,aAAa;gBACf,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE;oBACjD,sCAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,OAAO,eAAe,SAAS,WAAW,IAAI,EAAE,CAAC,CAAA;gBAC9G,CAAC,CAAC,CAAA;YAEJ,IAAI,YAAY,EAAE;gBAChB,sCAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,YAAY,CAAC,UAAU,KAAK,YAAY,EAAE,CAAC,CAAA;aAC/F;QACH,CAAC,CAAA;QAED,MAAM,QAAQ,GAAG,IAAA,qBAAc,EAAC;YAC9B,GAAG,EAAE,GAAG;SACT,CAAC,CAAA;QAEF,sCAAiB,CAAC,qBAAqB,CACrC,UAAU,EACV,IAAI,mBAAY,CAAC;YACf,cAAc;YACd,KAAK;YACL,IAAI,EAAE,IAAA,oBAAU,EAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;gBAClC,OAAO;oBACL,OAAO,kCACF,OAAO,KACV,yBAAyB,EAAE,MAAM,EACjC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,GAClD;iBACF,CAAA;YACH,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;SACpB,CAAC,CACH,CAAA;QAED,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAC3B,gCAAgC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,gBAAgB,CACvF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,UAAU;QACzB,IAAI,MAAM,GAAG,sCAAiB,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAA;QAChE,MAAM,CAAC,IAAI,EAAE,CAAA;QACb,sCAAiB,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAA;QAEtD,sCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,gCAAgC,UAAU,CAAC,IAAI,mBAAmB,CAAC,CAAA;IACnG,CAAC;IAED,IAAI,aAAa;QACf,OAAO;YACL;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,UAAU;aAClB;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,QAAQ;aAChB;SACF,CAAA;IACH,CAAC;IAED,IAAI,YAAY;QACd,OAAO,CAAC,SAAS,CAAC,CAAA;IACpB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,yCAAyC,CAAA;IAClD,CAAC;IAED,IAAI,WAAW;QACb,OAAO,2BAA2B,CAAA;IACpC,CAAC;CACF;AApFD,4CAoFC;AAED,sCAAiB,CAAC,iBAAiB,CAAC,mBAAmB,EAAE,IAAI,gBAAgB,EAAE,CAAC,CAAA","sourcesContent":["import 'cross-fetch/polyfill'\n\nimport { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'\nimport { setContext } from '@apollo/client/link/context'\n\nimport { ConnectionManager } from '../connection-manager'\nimport { Connector } from '../types'\n\nconst defaultOptions: any = {\n watchQuery: {\n fetchPolicy: 'no-cache',\n errorPolicy: 'ignore'\n },\n query: {\n fetchPolicy: 'no-cache', //'network-only'\n errorPolicy: 'all'\n },\n mutate: {\n errorPolicy: 'all'\n }\n}\n\nconst cache = new InMemoryCache({\n addTypename: false\n})\n\nexport class OperatoConnector implements Connector {\n async ready(connectionConfigs) {\n await Promise.all(connectionConfigs.map(this.connect.bind(this)))\n\n ConnectionManager.logger.info('graphql-connector connections are ready')\n }\n\n async connect(connection) {\n var {\n endpoint: uri,\n params: { authKey, domain }\n } = connection\n\n var ERROR_HANDLER: any = ({ graphQLErrors, networkError }) => {\n if (graphQLErrors)\n graphQLErrors.map(({ message, locations, path }) => {\n ConnectionManager.logger.error(`[GraphQL error] Message: ${message}, Location: ${locations}, Path: ${path}`)\n })\n\n if (networkError) {\n ConnectionManager.logger.error(`[Network error - ${networkError.statusCode}] ${networkError}`)\n }\n }\n\n const httpLink = createHttpLink({\n uri: uri\n })\n\n ConnectionManager.addConnectionInstance(\n connection,\n new ApolloClient({\n defaultOptions,\n cache,\n link: setContext((_, { headers }) => {\n return {\n headers: {\n ...headers,\n 'x-things-factory-domain': domain,\n authorization: authKey ? `Bearer ${authKey}` : ''\n }\n }\n }).concat(httpLink)\n })\n )\n\n ConnectionManager.logger.info(\n `graphql-connector connection(${connection.name}:${connection.endpoint}) is connected`\n )\n }\n\n async disconnect(connection) {\n var client = ConnectionManager.getConnectionInstance(connection)\n client.stop()\n ConnectionManager.removeConnectionInstance(connection)\n\n ConnectionManager.logger.info(`graphql-connector connection(${connection.name}) is disconnected`)\n }\n\n get parameterSpec() {\n return [\n {\n type: 'string',\n name: 'authKey',\n label: 'auth-key'\n },\n {\n type: 'string',\n name: 'domain',\n label: 'domain'\n }\n ]\n }\n\n get taskPrefixes() {\n return ['graphql']\n }\n\n get help() {\n return 'integration/connector/operato-connector'\n }\n\n get description() {\n return 'Operato Grpahql Connector'\n }\n}\n\nConnectionManager.registerConnector('operato-connector', new OperatoConnector())\n"]}
|
1
|
+
{"version":3,"file":"operato-connector.js","sourceRoot":"","sources":["../../../server/engine/connector/operato-connector.ts"],"names":[],"mappings":"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","sourcesContent":["import 'cross-fetch/polyfill'\n\nimport { ApolloClient, InMemoryCache, createHttpLink, split } from '@apollo/client/core'\nimport { setContext } from '@apollo/client/link/context'\n\n// for subscription\nimport WebSocket from 'ws'\nimport { createClient } from 'graphql-ws'\nimport { GraphQLWsLink } from '@apollo/client/link/subscriptions'\nimport { Observable, getMainDefinition } from '@apollo/client/utilities'\nimport gql from 'graphql-tag'\n\nimport { ConnectionManager } from '../connection-manager'\nimport { Connector } from '../types'\nimport { Scenario, ScenarioInstance } from '../../service'\n\nimport { getRepository, GraphqlLocalClient, Domain } from '@things-factory/shell'\nimport { PrivilegeObject, User } from '@things-factory/auth-base'\n\nconst debug = require('debug')('things-factory:integration-base:operato-connector-subscription')\n\nconst defaultOptions: any = {\n watchQuery: {\n fetchPolicy: 'no-cache',\n errorPolicy: 'ignore'\n },\n query: {\n fetchPolicy: 'no-cache', //'network-only'\n errorPolicy: 'all'\n },\n mutate: {\n errorPolicy: 'all'\n }\n}\n\nconst cache = new InMemoryCache({\n addTypename: false\n})\n\nexport const GRAPHQL_URI = '/graphql'\nexport const SUBSCRIPTION_URI = GRAPHQL_URI\n\nasync function checkPermission(privilegeObject: PrivilegeObject, user: User, domain: Domain): Promise<boolean> {\n if (!privilegeObject || !privilegeObject.privilege || !privilegeObject.category) {\n return true\n }\n\n const { owner: domainOwnerGranted, super: superUserGranted, category, privilege } = privilegeObject\n\n return (\n (domainOwnerGranted && (await process.domainOwnerGranted(domain, user))) ||\n (superUserGranted && (await process.superUserGranted(domain, user))) ||\n (category && privilege && (await User.hasPrivilege(privilege, category, domain, user)))\n )\n}\n\ninterface SubscriberData {\n tag: string\n scenario: any\n subscriptionObserver: any\n}\n;``\n\nexport class OperatoConnector implements Connector {\n private subscriptions: SubscriberData[] = []\n private context: any\n\n async ready(connectionConfigs) {\n await Promise.all(connectionConfigs.map(this.connect.bind(this)))\n\n ConnectionManager.logger.info('graphql-connector connections are ready')\n }\n\n async connect(connection) {\n var {\n endpoint: uri,\n params: { authKey, domain, subscriptionHandlers = {} }\n } = connection\n\n if (!authKey || !domain) {\n throw new Error('some connection paramter missing.')\n }\n\n var domainOwner = await getRepository(User).findOne({\n where: {\n id: connection.domain.owner\n }\n })\n\n this.context = {\n domain: connection.domain,\n user: domainOwner\n /* TODO: domainOwner 대신 특정 유저를 지정할 수 있도록 개선해야함. 모든 커넥션에 유저를 지정하는 기능으로 일반화할 필요가 있는 지 고민해야함 */\n }\n\n const httpLink = createHttpLink({\n uri: uri\n })\n\n /* \n CHECKPOINT: \n 1. GraphqQLWsLink를 사용하면 setContext를 통한 추가 헤더 설정이 무시됩니다.\n 따라서, GraphQLWsLink를 사용하려면, connectionParams를 통해 헤더를 설정해야 합니다.\n \n 2. 서버에서 실행시, webSocketImpl을 명시적으로 지정해야 합니다.\n */\n const wsLink = new GraphQLWsLink(\n createClient({\n url: uri.replace(/^http/, 'ws'),\n keepAlive: 10_000,\n retryAttempts: 1_000_000,\n shouldRetry: e => true,\n webSocketImpl: WebSocket,\n connectionParams: {\n headers: {\n 'x-things-factory-domain': domain,\n authorization: authKey ? `Bearer ${authKey}` : ''\n }\n }\n })\n )\n\n const splitLink = split(\n ({ query }) => {\n const def = getMainDefinition(query)\n return def.kind === 'OperationDefinition' && def.operation === 'subscription'\n },\n wsLink,\n setContext((_, { headers }) => {\n return {\n headers: {\n ...headers,\n 'x-things-factory-domain': domain,\n authorization: authKey ? `Bearer ${authKey}` : ''\n }\n }\n }).concat(httpLink)\n )\n\n var client = new ApolloClient({\n defaultOptions,\n cache,\n link: splitLink\n })\n\n var subscriptions: SubscriberData[] = []\n Object.keys(subscriptionHandlers).forEach(async tag => {\n if (!tag || !subscriptionHandlers[tag]) return\n\n let scenarioName = subscriptionHandlers[tag]\n\n // fetch a scenario\n var selectedScenario = await getRepository(Scenario).findOne({\n where: {\n name: scenarioName\n },\n relations: ['steps', 'domain']\n })\n\n const subscription = client.subscribe({\n query: gql`\n subscription {\n data(tag: \"${tag}\") {\n tag\n data\n }\n }\n `\n })\n\n var subscriptionObserver = subscription.subscribe({\n next: async data => {\n debug('received pubsub msg.:', data?.data)\n await this.runScenario(subscriptions, data?.data?.data)\n },\n error: error => {\n ConnectionManager.logger.error(`(${connection.name}:${connection.endpoint}) subscription error: ${error}`)\n },\n complete: () => {\n ConnectionManager.logger.info(`(${connection.name}:${connection.endpoint}) subscription complete`)\n }\n })\n\n ConnectionManager.logger.info(\n `(${connection.name}:${connection.endpoint}) subscription closed flag: ${subscriptionObserver.closed}`\n )\n\n subscriptions.push({\n tag,\n scenario: selectedScenario,\n subscriptionObserver\n })\n ConnectionManager.logger.info(`(${tag}:${scenarioName}) subscription closed flag: ${subscriptionObserver.closed}`)\n })\n\n client['subscriptions'] = subscriptions\n ConnectionManager.addConnectionInstance(connection, client)\n\n ConnectionManager.logger.info(\n `graphql-connector connection(${connection.name}:${connection.endpoint}) is connected`\n )\n }\n\n async disconnect(connection) {\n var client = ConnectionManager.getConnectionInstance(connection)\n let subscriptions: SubscriberData[] = client['subscriptions']\n subscriptions.forEach(subscription => subscription.subscriptionObserver.unsubscribe())\n client.stop()\n ConnectionManager.removeConnectionInstance(connection)\n\n ConnectionManager.logger.info(`graphql-connector connection(${connection.name}) is disconnected`)\n }\n\n async runScenario(subscriptions: SubscriberData[], variables: any): Promise<ScenarioInstance> {\n const { domain, user } = this.context\n const { tag } = variables\n\n if (!tag) {\n throw new Error(`tag is invalid - ${tag}`)\n }\n\n var scenario = subscriptions.find(subscription => subscription.tag === tag)?.scenario\n if (!scenario) {\n throw new Error(`scenario is not found - ${tag}`)\n }\n\n if (!(await checkPermission(scenario.privilege, user, domain))) {\n const { category, privilege } = scenario.privilege || {}\n throw new Error(`Unauthorized! ${category}-${privilege} privilege required`)\n }\n\n /* create a scenario instance */\n let instanceName = scenario.name + '-' + String(Date.now())\n var instance = new ScenarioInstance(instanceName, scenario, {\n user,\n domain,\n variables,\n client: GraphqlLocalClient.client\n })\n\n // run scenario\n await instance.run()\n return instance\n }\n\n get parameterSpec() {\n return [\n {\n type: 'string',\n name: 'authKey',\n label: 'auth-key'\n },\n {\n type: 'string',\n name: 'domain',\n label: 'domain'\n },\n {\n type: 'tag-scenarios',\n name: 'subscriptionHandlers',\n label: 'subscription-handlers'\n }\n ]\n }\n\n get taskPrefixes() {\n return ['graphql']\n }\n\n get help() {\n return 'integration/connector/operato-connector'\n }\n\n get description() {\n return 'Operato Graphql Connector'\n }\n}\n\nConnectionManager.registerConnector('operato-connector', new OperatoConnector())\n"]}
|