Skip to content

Commit 7e53b8e

Browse files
committed
Merge with v1.5.0
2 parents 26a8b70 + 1735b44 commit 7e53b8e

File tree

8 files changed

+988
-7
lines changed

8 files changed

+988
-7
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1515
- Last argument of Selectors will stop being assigned as "defaultValue". To define default value, it will be mandatory to pass an options object as last argument, containing a "defaultValue" property.
1616

1717
## [1.5.0] - 2019-10-14
18+
### Added
19+
- Add `stats` property containing counters of method actions executions.
20+
1821
### Changed
1922
- Upgrade devDependencies
2023

docs/selector/api.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,21 @@
4343
* error - `<Error>` If read method returns an error, it will be accessible at this property.
4444
* loading - `<Boolean>` Will be true while Selector read is in progress.
4545
* value - `<Any>` Value returned by `read` method.
46+
* stats - `<Object>` Object containing stats about method executions:
47+
* dispatch - `<Number>` Counter of times that read method has been dispatched when there was no cache.
48+
* success - `<Number>` Counter of times that read method has been resolved. No matter if result came from cache or not.
49+
* error - `<Number>` Counter of times that read method has been rejected. No matter if result came from cache or not.
4650
* Returns
4751
* `<Any>` - Result of the parser function.
4852
* create, update, delete `selector.create(data)` These methods can be used only when Selector returns another source.
4953
* Statics:
5054
* error - `<Error>` If read method returns an error, it will be accessible at this property.
5155
* loading - `<Boolean>` Will be true while Selector read is in progress.
5256
* value - `<Any>` Value returned by `read` method.
57+
* stats - `<Object>` Object containing stats about method executions:
58+
* dispatch - `<Number>` Counter of times that read method has been dispatched when there was no cache.
59+
* success - `<Number>` Counter of times that read method has been resolved. No matter if result came from cache or not.
60+
* error - `<Number>` Counter of times that read method has been rejected. No matter if result came from cache or not.
5361
* Arguments
5462
* data - `<Any>` Data that will be passed to the correspondant create, update or delete method of the returned source.
5563
* Returns

docs/selector/asynchronous-mutable-properties.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Asynchronous mutable properties
22

3-
Selectors methods have three properties that change value depending of the current status: `loading`, `error`, `value`
3+
Selectors methods have four properties that change value depending of the current status: `loading`, `error`, `value` and `stats`
44

55
```js
66
console.log(books.read.value) // undefined
@@ -15,3 +15,26 @@ console.log(books.read.loading) // false
1515
console.log(books.read.value) // value returned by parser function.
1616

1717
```
18+
19+
The `stats` property can be used to determine how many times a selector method has been dispatched, successfully read or errored.
20+
21+
It contains three counters corresponding to each method "action":
22+
23+
* `dispatch` - Increased each time the read method is dispatched and there is no cache.
24+
* `success` - Increased each time the read method is called and it is resolved. No matter if result comes from cache or not.
25+
* `error` - Increased each time the read method is called and it is rejected. No matter if result comes from cache or not.
26+
27+
```js
28+
console.log(books.read.stats.dispatch) // 0
29+
console.log(books.read.stats.error) // 0
30+
console.log(books.read.stats.success) // 0
31+
32+
booksWithAuthors.read();
33+
34+
console.log(books.read.stats.dispatch) // 1
35+
36+
await booksWithAuthors.read()
37+
38+
console.log(books.read.stats.success) // 2
39+
40+
```

src/Origin.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ export class Origin {
121121
loading: methods[methodName].loading,
122122
...data
123123
};
124+
const actionName = actions[methodName][action];
125+
methods[methodName].stats = {
126+
...methods[methodName].stats,
127+
[action]: methods[methodName].stats[action] + 1
128+
};
129+
124130
if (!isEqual(oldData, newData)) {
125131
methods[methodName].value = newData.value;
126132
methods[methodName].loading = newData.loading;
@@ -129,7 +135,7 @@ export class Origin {
129135
this._emitChangeAny({
130136
source: this._queries[id],
131137
method: methodName,
132-
action,
138+
action: actionName,
133139
params
134140
});
135141
}
@@ -139,13 +145,14 @@ export class Origin {
139145
if (this._hasToPublishMethod(methodName)) {
140146
const dispatchMethod = extraParams => {
141147
const methodPromise = this[`_${methodName}`](query, extraParams);
142-
if (!methodPromise.isFulfilled) {
148+
if (!methodPromise.isFulfilled && !methodPromise.dispatchEmitted) {
149+
methodPromise.dispatchEmitted = true;
143150
updateData(
144151
{
145152
loading: true
146153
},
147154
methodName,
148-
actions[methodName].dispatch,
155+
"dispatch",
149156
extraParams
150157
);
151158
}
@@ -158,7 +165,7 @@ export class Origin {
158165
value: result
159166
},
160167
methodName,
161-
actions[methodName].success,
168+
"success",
162169
extraParams
163170
);
164171
methodPromise.isFulfilled = true;
@@ -171,7 +178,7 @@ export class Origin {
171178
error
172179
},
173180
methodName,
174-
actions[methodName].error,
181+
"error",
175182
extraParams
176183
);
177184
methodPromise.isFulfilled = true;
@@ -189,6 +196,11 @@ export class Origin {
189196
: undefined;
190197
methods[methodName].error = null;
191198
methods[methodName].loading = false;
199+
methods[methodName].stats = {
200+
dispatch: 0,
201+
success: 0,
202+
error: 0
203+
};
192204
methods[methodName]._source = methods;
193205
methods[methodName]._isSourceMethod = true;
194206
methods[methodName]._methodName = methodName;
@@ -205,7 +217,8 @@ export class Origin {
205217
methods[methodName].getters = {
206218
error: createGetter("error"),
207219
loading: createGetter("loading"),
208-
value: createGetter("value")
220+
value: createGetter("value"),
221+
stats: createGetter("stats")
209222
};
210223
}
211224
});

test/Origin.stats.js

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
const test = require("mocha-sinon-chai");
2+
3+
const { Origin, sources } = require("../src/Origin");
4+
5+
test.describe("Origin stats", () => {
6+
let sandbox;
7+
let TestOrigin;
8+
let testOrigin;
9+
let queriedOrigin;
10+
let hasToReject;
11+
12+
test.beforeEach(() => {
13+
hasToReject = false;
14+
sandbox = test.sinon.createSandbox();
15+
TestOrigin = class extends Origin {
16+
_read(query) {
17+
const cached = this._cache.get(query);
18+
if (cached) {
19+
return cached;
20+
}
21+
const resultPromise = new Promise((resolve, reject) => {
22+
setTimeout(() => {
23+
hasToReject ? reject(new Error()) : resolve();
24+
}, 50);
25+
});
26+
this._cache.set(query, resultPromise);
27+
return resultPromise;
28+
}
29+
};
30+
testOrigin = new TestOrigin("foo-id");
31+
queriedOrigin = testOrigin.query("foo");
32+
});
33+
34+
test.afterEach(() => {
35+
sandbox.restore();
36+
sources.clear();
37+
});
38+
39+
test.describe("without query", () => {
40+
test.describe("dispatch property", () => {
41+
test.it("should return 0 until read is dispatched", () => {
42+
test.expect(testOrigin.read.stats.dispatch).to.equal(0);
43+
});
44+
45+
test.it("should return 1 when read is dispatched", () => {
46+
testOrigin.read();
47+
test.expect(testOrigin.read.stats.dispatch).to.equal(1);
48+
});
49+
50+
test.it(
51+
"should return 1 when read is dispatched many times and cache is not cleaned",
52+
() => {
53+
testOrigin.read();
54+
testOrigin.read();
55+
testOrigin.read();
56+
testOrigin.read();
57+
test.expect(testOrigin.read.stats.dispatch).to.equal(1);
58+
}
59+
);
60+
61+
test.it(
62+
"should return 2 when read is dispatched many times and cache is cleaned once",
63+
() => {
64+
testOrigin.read();
65+
testOrigin.clean();
66+
testOrigin.read();
67+
testOrigin.read();
68+
testOrigin.read();
69+
test.expect(testOrigin.read.stats.dispatch).to.equal(2);
70+
}
71+
);
72+
});
73+
74+
test.describe("success property", () => {
75+
test.it("should return 0 until read method has finished", () => {
76+
test.expect(testOrigin.read.stats.success).to.equal(0);
77+
});
78+
79+
test.it("should return 1 when read has finished", () => {
80+
return testOrigin.read().then(() => {
81+
return test.expect(testOrigin.read.stats.success).to.equal(1);
82+
});
83+
});
84+
85+
test.it("should return 4 when read has finished and it has been called 4 times", () => {
86+
return Promise.all([
87+
testOrigin.read(),
88+
testOrigin.read(),
89+
testOrigin.read(),
90+
testOrigin.read()
91+
]).then(() => {
92+
return test.expect(testOrigin.read.stats.success).to.equal(4);
93+
});
94+
});
95+
});
96+
97+
test.describe("error property", () => {
98+
test.it("should return 0 until read method has finished", () => {
99+
test.expect(testOrigin.read.stats.error).to.equal(0);
100+
});
101+
102+
test.it("should return 1 when read has finished with an error", () => {
103+
hasToReject = true;
104+
return testOrigin.read().then(
105+
() => {
106+
return test.assert.fail();
107+
},
108+
() => {
109+
return test.expect(testOrigin.read.stats.error).to.equal(1);
110+
}
111+
);
112+
});
113+
114+
test.it(
115+
"should return 4 when read has finished with error and it has been called 4 times",
116+
() => {
117+
hasToReject = true;
118+
return Promise.all([
119+
testOrigin.read(),
120+
testOrigin.read(),
121+
testOrigin.read(),
122+
testOrigin.read()
123+
]).then(
124+
() => {
125+
return test.assert.fail();
126+
},
127+
() => {
128+
return test.expect(testOrigin.read.stats.error).to.equal(4);
129+
}
130+
);
131+
}
132+
);
133+
});
134+
});
135+
136+
test.describe("with query", () => {
137+
test.describe("dispatch property", () => {
138+
test.it("should return 0 until read is dispatched", () => {
139+
test.expect(queriedOrigin.read.stats.dispatch).to.equal(0);
140+
});
141+
142+
test.it("should return 1 when read is dispatched", () => {
143+
queriedOrigin.read();
144+
test.expect(queriedOrigin.read.stats.dispatch).to.equal(1);
145+
});
146+
147+
test.it(
148+
"should return 1 when read is dispatched many times and cache is not cleaned",
149+
() => {
150+
queriedOrigin.read();
151+
queriedOrigin.read();
152+
queriedOrigin.read();
153+
queriedOrigin.read();
154+
test.expect(queriedOrigin.read.stats.dispatch).to.equal(1);
155+
}
156+
);
157+
158+
test.it(
159+
"should return 2 when read is dispatched many times and cache is cleaned once",
160+
() => {
161+
queriedOrigin.read();
162+
queriedOrigin.clean();
163+
queriedOrigin.read();
164+
queriedOrigin.read();
165+
queriedOrigin.read();
166+
test.expect(queriedOrigin.read.stats.dispatch).to.equal(2);
167+
}
168+
);
169+
});
170+
171+
test.describe("success property", () => {
172+
test.it("should return 0 until read method has finished", () => {
173+
test.expect(queriedOrigin.read.stats.success).to.equal(0);
174+
});
175+
176+
test.it("should return 1 when read has finished", () => {
177+
return queriedOrigin.read().then(() => {
178+
return test.expect(queriedOrigin.read.stats.success).to.equal(1);
179+
});
180+
});
181+
182+
test.it("should return 4 when read has finished and it has been called 4 times", () => {
183+
return Promise.all([
184+
queriedOrigin.read(),
185+
queriedOrigin.read(),
186+
queriedOrigin.read(),
187+
queriedOrigin.read()
188+
]).then(() => {
189+
return test.expect(queriedOrigin.read.stats.success).to.equal(4);
190+
});
191+
});
192+
});
193+
194+
test.describe("error property", () => {
195+
test.it("should return 0 until read method has finished", () => {
196+
test.expect(queriedOrigin.read.stats.error).to.equal(0);
197+
});
198+
199+
test.it("should return 1 when read has finished with an error", () => {
200+
hasToReject = true;
201+
return queriedOrigin.read().then(
202+
() => {
203+
return test.assert.fail();
204+
},
205+
() => {
206+
return test.expect(queriedOrigin.read.stats.error).to.equal(1);
207+
}
208+
);
209+
});
210+
211+
test.it(
212+
"should return 4 when read has finished with error and it has been called 4 times",
213+
() => {
214+
hasToReject = true;
215+
return Promise.all([
216+
queriedOrigin.read(),
217+
queriedOrigin.read(),
218+
queriedOrigin.read(),
219+
queriedOrigin.read()
220+
]).then(
221+
() => {
222+
return test.assert.fail();
223+
},
224+
() => {
225+
return test.expect(queriedOrigin.read.stats.error).to.equal(4);
226+
}
227+
);
228+
}
229+
);
230+
});
231+
});
232+
});

0 commit comments

Comments
 (0)