1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | import {createPlugin, type Context} from 'fusion-core';
|
12 | import {UniversalEventsToken} from 'fusion-plugin-universal-events';
|
13 | import {FetchToken} from 'fusion-tokens';
|
14 | import type {Fetch} from 'fusion-tokens';
|
15 |
|
16 | import {type HandlerType, RPCHandlersConfigToken} from './tokens.js';
|
17 | import type {RPCPluginType, IEmitter, RPCConfigType} from './types.js';
|
18 | import {formatApiPath} from './utils.js';
|
19 |
|
20 | type InitializationOpts = {
|
21 | fetch: Fetch,
|
22 | emitter: IEmitter,
|
23 | rpcConfig: ?RPCConfigType,
|
24 | };
|
25 |
|
26 | const statKey = 'rpc:method-client';
|
27 |
|
28 | class RPC {
|
29 | ctx: ?Context;
|
30 | emitter: ?IEmitter;
|
31 | handlers: ?HandlerType;
|
32 | fetch: ?Fetch;
|
33 | config: ?RPCConfigType;
|
34 | apiPath: string;
|
35 | constructor({fetch, emitter, rpcConfig}: InitializationOpts) {
|
36 | this.fetch = fetch;
|
37 | this.config = rpcConfig || {};
|
38 | this.emitter = emitter;
|
39 |
|
40 | this.apiPath = formatApiPath(
|
41 | rpcConfig && rpcConfig.apiPath ? rpcConfig.apiPath : 'api'
|
42 | );
|
43 | }
|
44 |
|
45 | request<TArgs, TResult>(
|
46 | rpcId: string,
|
47 | args: TArgs,
|
48 | headers: ?{[string]: string}
|
49 | ): Promise<TResult> {
|
50 | if (!this.fetch) {
|
51 | throw new Error('fusion-plugin-rpc requires `fetch`');
|
52 | }
|
53 | if (!this.emitter) {
|
54 | throw new Error('Missing emitter registered to UniversalEventsToken');
|
55 | }
|
56 | const fetch = this.fetch;
|
57 | const emitter = this.emitter;
|
58 | const apiPath = this.apiPath;
|
59 |
|
60 | const startTime = Date.now();
|
61 |
|
62 |
|
63 | return fetch(`${apiPath}${rpcId}`, {
|
64 | method: 'POST',
|
65 | headers: {
|
66 | 'Content-Type': 'application/json',
|
67 | ...(headers || {}),
|
68 | },
|
69 | body: JSON.stringify(args || {}),
|
70 | })
|
71 | .then(r => r.json())
|
72 | .then(args => {
|
73 | const {status, data} = args;
|
74 | if (status === 'success') {
|
75 | emitter.emit(statKey, {
|
76 | method: rpcId,
|
77 | status: 'success',
|
78 | timing: Date.now() - startTime,
|
79 | });
|
80 | return data;
|
81 | } else {
|
82 | emitter.emit(statKey, {
|
83 | method: rpcId,
|
84 | error: data,
|
85 | status: 'failure',
|
86 | timing: Date.now() - startTime,
|
87 | });
|
88 | return Promise.reject(data);
|
89 | }
|
90 | });
|
91 | }
|
92 | }
|
93 |
|
94 | const pluginFactory: () => RPCPluginType = () =>
|
95 | createPlugin({
|
96 | deps: {
|
97 | fetch: FetchToken,
|
98 | emitter: UniversalEventsToken,
|
99 | rpcConfig: RPCHandlersConfigToken.optional,
|
100 | },
|
101 | provides: deps => {
|
102 | const {fetch = window.fetch, emitter, rpcConfig} = deps;
|
103 |
|
104 | return {from: () => new RPC({fetch, emitter, rpcConfig})};
|
105 | },
|
106 | });
|
107 |
|
108 | export default ((__BROWSER__ && pluginFactory(): any): RPCPluginType);
|