Skip to content

Commit 19c2b09

Browse files
committed
Refactoring to move "itemsDefs" in a dedicated file
1 parent d8bb88a commit 19c2b09

3 files changed

Lines changed: 125 additions & 85 deletions

File tree

src/HDSModel-ItemsDefs.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
const HDSItemDef = require('./HDSItemDef');
2+
3+
/**
4+
* Items - Extension of HDSModel
5+
*/
6+
class HDSModelItemsDefs {
7+
/**
8+
* @type {HDSModel}
9+
*/
10+
#model;
11+
12+
/**
13+
* ItemDefs Cache
14+
* KeyValue of itemsDefs
15+
*/
16+
#itemsDefs;
17+
18+
/**
19+
* get itemsData by streamId and eventType
20+
*/
21+
#modelDataByStreamIdEventTypes;
22+
23+
constructor (model) {
24+
this.#model = model;
25+
this.#itemsDefs = {};
26+
this.#modelDataByStreamIdEventTypes = {};
27+
loadModelDataByStreamIdEventTypes(this.#model.modelData.items, this.#modelDataByStreamIdEventTypes);
28+
}
29+
30+
/**
31+
* get item for a key
32+
* @param {string} key
33+
* @param {boolean} [throwErrorIfNotFound] default `true`
34+
*/
35+
forKey (key, throwErrorIfNotFound = true) {
36+
if (this.#itemsDefs[key]) return this.#itemsDefs[key];
37+
const defData = this.#model.modelData.items[key];
38+
if (!defData) {
39+
if (throwErrorIfNotFound) throw new Error('Cannot find item definition with key: ' + key);
40+
return null;
41+
}
42+
this.#itemsDefs[key] = new HDSItemDef(key, defData);
43+
return this.#itemsDefs[key];
44+
}
45+
46+
/**
47+
* get a definition for an event
48+
* @param {Event} event
49+
* @param {boolean} [throwErrorIfNotFound] default `true`
50+
*/
51+
forEvent (event, throwErrorIfNotFound = true) {
52+
const candidates = [];
53+
for (const streamId of event.streamIds) {
54+
const keyStreamIdEventType = streamId + ':' + event.type;
55+
const candidate = this.#modelDataByStreamIdEventTypes[keyStreamIdEventType];
56+
if (candidate) candidates.push(candidate);
57+
}
58+
if (candidates.length === 0) {
59+
if (throwErrorIfNotFound) throw new Error('Cannot find definition for event: ' + JSON.stringify(event));
60+
return null;
61+
}
62+
if (candidates.length > 1) {
63+
throw new Error(`Found multiple matching definitions "${candidates.map(c => (c.key)).join(', ')}" for event: ${JSON.stringify(event)}`);
64+
}
65+
return this.forKey(candidates[0].key, throwErrorIfNotFound);
66+
}
67+
}
68+
69+
module.exports = HDSModelItemsDefs;
70+
71+
/**
72+
* @private
73+
* Add key to model items and
74+
* load modeldata item into modelDataByStreamIdEventTypes for fast search
75+
*/
76+
function loadModelDataByStreamIdEventTypes (model, map) {
77+
for (const item of Object.values(model)) {
78+
const eventTypes = [];
79+
if (item.eventType) {
80+
eventTypes.push(item.eventType);
81+
} else {
82+
eventTypes.push(...Object.keys(item.variations.eventType));
83+
}
84+
for (const eventType of eventTypes) {
85+
const keyStreamIdEventType = item.streamId + ':' + eventType;
86+
if (map[keyStreamIdEventType]) {
87+
// should be tested with a faulty model
88+
throw new Error(`Duplicate streamId + eventType "${keyStreamIdEventType}" for item ${JSON.stringify(item)}`);
89+
}
90+
map[keyStreamIdEventType] = item;
91+
}
92+
}
93+
}

src/HDSModel.js

Lines changed: 18 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
const HDSItemDef = require('./HDSItemDef');
2-
1+
const HDSModelItemsDefs = require('./HDSModel-ItemsDefs');
32
class HDSModel {
43
/**
54
* JSON definition file
@@ -15,15 +14,9 @@ class HDSModel {
1514
#modelData;
1615

1716
/**
18-
* ItemDefs Cache
19-
* KeyValue of itemsDefs
17+
* @type {HDSModelItemsDefs}
2018
*/
21-
#itemsDefs;
22-
23-
/**
24-
* get itemsDefs by streamId and eventType
25-
*/
26-
#modelDataByStreamIdEventTypes;
19+
#modelItemsDefs;
2720

2821
/**
2922
* streamsById
@@ -38,8 +31,6 @@ class HDSModel {
3831
*/
3932
constructor (modelUrl) {
4033
this.#modelUrl = modelUrl;
41-
this.#itemsDefs = {};
42-
this.#modelDataByStreamIdEventTypes = {};
4334
this.#modelStreamsById = {};
4435
}
4536

@@ -51,47 +42,29 @@ class HDSModel {
5142
const resultText = await response.text();
5243
const result = JSON.parse(resultText);
5344
this.#modelData = result;
54-
loadModelDataByStreamIdEventTypes(this.#modelData.items, this.#modelDataByStreamIdEventTypes);
45+
// add key to items before freezing;
46+
for (const [key, item] of Object.entries(this.#modelData.items)) {
47+
item.key = key;
48+
}
49+
5550
loadModelStreamsById(this.#modelData.streams, this.#modelStreamsById);
5651
deepFreeze(this.#modelData); // make sure it cannot be modified
5752
}
5853

5954
/**
60-
* get item for a key
61-
* @param {string} key
62-
* @param {boolean} [throwErrorIfNotFound] default `true`
55+
* RAW model data
6356
*/
64-
itemDefForKey (key, throwErrorIfNotFound = true) {
65-
if (this.#itemsDefs[key]) return this.#itemsDefs[key];
66-
const defData = this.#modelData.items[key];
67-
if (!defData) {
68-
if (throwErrorIfNotFound) throw new Error('Cannot find item definition with key: ' + key);
69-
return null;
70-
}
71-
this.#itemsDefs[key] = new HDSItemDef(key, defData);
72-
return this.#itemsDefs[key];
57+
get modelData () {
58+
if (!this.#modelData) throw new Error('Model not loaded call `await model.load()` first.');
59+
return this.#modelData;
7360
}
7461

7562
/**
76-
* get a definition for an event
77-
* @param {Event} event
78-
* @param {boolean} [throwErrorIfNotFound] default `true`
63+
* @type HDSModelItemsDefs
7964
*/
80-
itemDefForEvent (event, throwErrorIfNotFound = true) {
81-
const candidates = [];
82-
for (const streamId of event.streamIds) {
83-
const keyStreamIdEventType = streamId + ':' + event.type;
84-
const candidate = this.#modelDataByStreamIdEventTypes[keyStreamIdEventType];
85-
if (candidate) candidates.push(candidate);
86-
}
87-
if (candidates.length === 0) {
88-
if (throwErrorIfNotFound) throw new Error('Cannot find definition for event: ' + JSON.stringify(event));
89-
return null;
90-
}
91-
if (candidates.length > 1) {
92-
throw new Error(`Found multiple matching definitions "${candidates.map(c => (c.key)).join(', ')}" for event: ${JSON.stringify(event)}`);
93-
}
94-
return this.itemDefForKey(candidates[0].key, throwErrorIfNotFound);
65+
get itemsDefs () {
66+
if (!this.#modelItemsDefs) this.#modelItemsDefs = new HDSModelItemsDefs(this);
67+
return this.#modelItemsDefs;
9568
}
9669

9770
/**
@@ -102,7 +75,7 @@ class HDSModel {
10275
const result = [];
10376
const streams = new Map(); // tempMap to keep streams already in
10477
for (const itemKey of itemKeys) {
105-
const itemDef = this.itemDefForKey(itemKey);
78+
const itemDef = this.itemsDefs.forKey(itemKey);
10679
const streamParentIds = this.streamGetParentsIds(itemDef.data.streamId, true, [itemDef.data.streamId]);
10780
for (const streamId of streamParentIds) {
10881
if (streams.has(streamId)) continue;
@@ -195,7 +168,7 @@ class HDSModel {
195168
}
196169
// add streamId not already in
197170
for (const itemKey of itemKeys) {
198-
const itemDef = this.itemDefForKey(itemKey);
171+
const itemDef = this.itemsDefs.forKey(itemKey);
199172
const streamId = itemDef.data.streamId;
200173
if (!streamsRequested[streamId]) { // new streamId
201174
const auth = { streamId, level: opts.defaultLevel };
@@ -276,32 +249,6 @@ function deepFreeze (object) {
276249
return Object.freeze(object);
277250
}
278251

279-
/**
280-
* @private
281-
* Add key to model items and
282-
* load modeldata item into modelDataByStreamIdEventTypes for fast search
283-
*/
284-
function loadModelDataByStreamIdEventTypes (model, map) {
285-
for (const [key, item] of Object.entries(model)) {
286-
// add key to item
287-
item.key = key;
288-
const eventTypes = [];
289-
if (item.eventType) {
290-
eventTypes.push(item.eventType);
291-
} else {
292-
eventTypes.push(...Object.keys(item.variations.eventType));
293-
}
294-
for (const eventType of eventTypes) {
295-
const keyStreamIdEventType = item.streamId + ':' + eventType;
296-
if (map[keyStreamIdEventType]) {
297-
// should be tested with a faulty model
298-
throw new Error(`Duplicate streamId + eventType "${keyStreamIdEventType}" for item ${JSON.stringify(item)}`);
299-
}
300-
map[keyStreamIdEventType] = item;
301-
}
302-
}
303-
}
304-
305252
/**
306253
* @param {Array<stream>} streams
307254
* @param {Object<string, stream>} map - key value map

tests/hdsModel.test.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,33 @@ describe('[MODX] Model', () => {
1515
it('[MODL] Load model for item with multiple types: body-weight', async () => {
1616
const modelLoad = new HDSModel(modelURL);
1717
await modelLoad.load();
18-
const itemDef = modelLoad.itemDefForKey('body-weight');
18+
const itemDef = modelLoad.itemsDefs.forKey('body-weight');
1919
assert.equal(itemDef.data.streamId, 'body-weight');
2020
assert.deepEqual(itemDef.eventTypes, ['mass/kg', 'mass/lb']);
2121
assert.equal(itemDef.key, 'body-weight');
2222
});
2323

24-
it('[MODL] Load model for item with single type: body-vulva-wetness-feeling', async () => {
24+
it('[MODM] Load model for item with single type: body-vulva-wetness-feeling', async () => {
2525
const modelLoad = new HDSModel(modelURL);
2626
await modelLoad.load();
27-
const itemDef = modelLoad.itemDefForKey('body-vulva-wetness-feeling');
27+
const itemDef = modelLoad.itemsDefs.forKey('body-vulva-wetness-feeling');
2828
assert.deepEqual(itemDef.eventTypes, ['ratio/generic']);
2929
});
3030

3131
// ---------- items ------------ //
3232

3333
describe('[MOIX] items', function () {
34-
it('[MOIE] Throw error if itemDefForKey not found', async () => {
34+
it('[MOIE] Throw error if itemsDefs.forKey not found', async () => {
3535
try {
36-
model.itemDefForKey('dummy');
36+
model.itemsDefs.forKey('dummy');
3737
throw new Error('Should throw Error');
3838
} catch (e) {
3939
assert.equal(e.message, 'Cannot find item definition with key: dummy');
4040
}
4141
});
4242

4343
it('[MOIN] Return null with throwErrorIfNotFound = false', async () => {
44-
const notFound = model.itemDefForKey('dummy', false);
44+
const notFound = model.itemsDefs.forKey('dummy', false);
4545
assert.equal(notFound, null);
4646
});
4747
});
@@ -53,40 +53,40 @@ describe('[MODX] Model', () => {
5353
streamIds: ['body-weight', 'dummy'],
5454
type: 'mass/kg'
5555
};
56-
const itemDef = model.itemDefForEvent(fakeEvent);
56+
const itemDef = model.itemsDefs.forEvent(fakeEvent);
5757
assert.equal(itemDef.data.streamId, 'body-weight');
5858
assert.deepEqual(itemDef.eventTypes, ['mass/kg', 'mass/lb']);
5959
});
6060

61-
it('[MOEE] Throw error if itemDefForEvent not found', async () => {
61+
it('[MOEE] Throw error if itemsDefs.forEvent not found', async () => {
6262
const fakeEvent = {
6363
streamIds: ['dummy'],
6464
type: 'mass/kg'
6565
};
6666
try {
67-
model.itemDefForEvent(fakeEvent);
67+
model.itemsDefs.forEvent(fakeEvent);
6868
throw new Error('Should throw Error');
6969
} catch (e) {
7070
assert.equal(e.message, 'Cannot find definition for event: {"streamIds":["dummy"],"type":"mass/kg"}');
7171
}
7272
});
7373

74-
it('[MOEN] Return if itemDefForEvent not found and throwErrorIfNotFound = false', async () => {
74+
it('[MOEN] Return if itemsDefs.forEvent not found and throwErrorIfNotFound = false', async () => {
7575
const fakeEvent = {
7676
streamIds: ['dummy'],
7777
type: 'mass/kg'
7878
};
79-
const notFound = model.itemDefForEvent(fakeEvent, false);
79+
const notFound = model.itemsDefs.forEvent(fakeEvent, false);
8080
assert.equal(notFound, null);
8181
});
8282

83-
it('[MOED] Throw error if itemDefForEvent finds duplicates', async () => {
83+
it('[MOED] Throw error if itemsDefs.forEvent finds duplicates', async () => {
8484
const fakeEvent = {
8585
streamIds: ['body-vulva-wetness-feeling', 'body-vulva-mucus-stretch'],
8686
type: 'ratio/generic'
8787
};
8888
try {
89-
model.itemDefForEvent(fakeEvent);
89+
model.itemsDefs.forEvent(fakeEvent);
9090
throw new Error('Should throw Error');
9191
} catch (e) {
9292
assert.equal(e.message, 'Found multiple matching definitions "body-vulva-wetness-feeling, body-vulva-mucus-stretch" for event: {"streamIds":["body-vulva-wetness-feeling","body-vulva-mucus-stretch"],"type":"ratio/generic"}');
@@ -128,7 +128,7 @@ describe('[MODX] Model', () => {
128128
// keeè a list of streams check that necessary streams exists
129129
const streamIdsToCheck = {};
130130
for (const itemKey of itemKeys) {
131-
const streamId = model.itemDefForKey(itemKey).data.streamId;
131+
const streamId = model.itemsDefs.forKey(itemKey).data.streamId;
132132
streamIdsToCheck[streamId] = true;
133133
}
134134
const parentExist = {}; // list of parent id in order

0 commit comments

Comments
 (0)