UNPKG

2.85 kBJavaScriptView Raw
1/** Copyright (c) 2018 Uber Technologies, Inc.
2 *
3 * This source code is licensed under the MIT license found in the
4 * LICENSE file in the root directory of this source tree.
5 *
6 * @flow
7 */
8
9/* eslint-env browser */
10
11import {createPlugin, type Context} from 'fusion-core';
12import {UniversalEventsToken} from 'fusion-plugin-universal-events';
13import {FetchToken} from 'fusion-tokens';
14import type {Fetch} from 'fusion-tokens';
15
16import {type HandlerType, RPCHandlersConfigToken} from './tokens.js';
17import type {RPCPluginType, IEmitter, RPCConfigType} from './types.js';
18import {formatApiPath} from './utils.js';
19
20type InitializationOpts = {
21 fetch: Fetch,
22 emitter: IEmitter,
23 rpcConfig: ?RPCConfigType,
24};
25
26const statKey = 'rpc:method-client';
27
28class 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 // TODO(#3) handle args instanceof FormData
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
94const 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
108export default ((__BROWSER__ && pluginFactory(): any): RPCPluginType);