aboutsummaryrefslogtreecommitdiffstats
path: root/src/stores/lib/Request.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/stores/lib/Request.js')
-rw-r--r--src/stores/lib/Request.js112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/stores/lib/Request.js b/src/stores/lib/Request.js
new file mode 100644
index 000000000..4a6925cc5
--- /dev/null
+++ b/src/stores/lib/Request.js
@@ -0,0 +1,112 @@
1import { observable, action, computed } from 'mobx';
2import { isEqual } from 'lodash/fp';
3
4export default class Request {
5 static _hooks = [];
6
7 static registerHook(hook) {
8 Request._hooks.push(hook);
9 }
10
11 @observable result = null;
12 @observable error = null;
13 @observable isExecuting = false;
14 @observable isError = false;
15 @observable wasExecuted = false;
16
17 _promise = Promise;
18 _api = {};
19 _method = '';
20 _isWaitingForResponse = false;
21 _currentApiCall = null;
22
23 constructor(api, method) {
24 this._api = api;
25 this._method = method;
26 }
27
28 execute(...callArgs) {
29 // Do not continue if this request is already loading
30 if (this._isWaitingForResponse) return this;
31
32 if (!this._api[this._method]) {
33 throw new Error(`Missing method <${this._method}> on api object:`, this._api);
34 }
35
36 // This timeout is necessary to avoid warnings from mobx
37 // regarding triggering actions as side-effect of getters
38 setTimeout(action(() => {
39 this.isExecuting = true;
40 }), 0);
41
42 // Issue api call & save it as promise that is handled to update the results of the operation
43 this._promise = new Promise((resolve, reject) => {
44 this._api[this._method](...callArgs)
45 .then((result) => {
46 setTimeout(action(() => {
47 this.result = result;
48 if (this._currentApiCall) this._currentApiCall.result = result;
49 this.isExecuting = false;
50 this.isError = false;
51 this.wasExecuted = true;
52 this._isWaitingForResponse = false;
53 this._triggerHooks();
54 resolve(result);
55 }), 1);
56 return result;
57 })
58 .catch(action((error) => {
59 setTimeout(action(() => {
60 this.error = error;
61 this.isExecuting = false;
62 this.isError = true;
63 this.wasExecuted = true;
64 this._isWaitingForResponse = false;
65 this._triggerHooks();
66 reject(error);
67 }), 1);
68 }));
69 });
70
71 this._isWaitingForResponse = true;
72 this._currentApiCall = { args: callArgs, result: null };
73 return this;
74 }
75
76 reload() {
77 return this.execute(...this._currentApiCall.args);
78 }
79
80 isExecutingWithArgs(...args) {
81 return this.isExecuting && this._currentApiCall && isEqual(this._currentApiCall.args, args);
82 }
83
84 @computed get isExecutingFirstTime() {
85 return !this.wasExecuted && this.isExecuting;
86 }
87
88 then(...args) {
89 if (!this._promise) throw new Error('You have to call Request::execute before you can access it as promise');
90 return this._promise.then(...args);
91 }
92
93 catch(...args) {
94 if (!this._promise) throw new Error('You have to call Request::execute before you can access it as promise');
95 return this._promise.catch(...args);
96 }
97
98 _triggerHooks() {
99 Request._hooks.forEach(hook => hook(this));
100 }
101
102 reset() {
103 this.result = null;
104 this.isExecuting = false;
105 this.isError = false;
106 this.wasExecuted = false;
107 this._isWaitingForResponse = false;
108 this._promise = Promise;
109
110 return this;
111 }
112}