Skip to content

Commit 3e0a610

Browse files
committed
Introducting HDSItemDef and maps
1 parent c8bd4b4 commit 3e0a610

3 files changed

Lines changed: 138 additions & 7 deletions

File tree

src/HDSItemDef.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class HDSItemDef {
2+
#data;
3+
4+
constructor (definitionData) {
5+
this.#data = definitionData;
6+
}
7+
8+
get types () {
9+
if (this.#data.eventType) return [this.#data.eventType];
10+
return Object.keys(this.#data.variations.eventType);
11+
}
12+
13+
get data () {
14+
return this.#data;
15+
}
16+
}
17+
18+
module.exports = HDSItemDef;

src/HDSModel.js

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const HDSItemDef = require('./HDSItemDef');
2+
13
class HDSModel {
24
/**
35
* JSON definition file
@@ -12,13 +14,26 @@ class HDSModel {
1214
*/
1315
#modelData;
1416

17+
/**
18+
* ItemDefs Cache
19+
* KeyValue of itemsDefs
20+
*/
21+
#itemsDefs;
22+
23+
/**
24+
* get itemsDefs by streamId and eventType
25+
*/
26+
#modelDataByStreamIdEventTypes;
27+
1528
/**
1629
* JSON definition file
1730
* Should come from service/info assets.hds-model
1831
* @type {string}
1932
*/
2033
constructor (modelUrl) {
2134
this.#modelUrl = modelUrl;
35+
this.#itemsDefs = {};
36+
this.#modelDataByStreamIdEventTypes = {};
2237
}
2338

2439
/**
@@ -28,15 +43,47 @@ class HDSModel {
2843
const response = await fetch(this.#modelUrl);
2944
const resultText = await response.text();
3045
const result = JSON.parse(resultText);
31-
this.#modelData = deepFreeze(result);
46+
this.#modelData = result;
47+
loadModelDataByStreamIdEventTypes(this.#modelData.items, this.#modelDataByStreamIdEventTypes);
48+
deepFreeze(this.#modelData); // make sure it cannot be modified
3249
}
3350

3451
/**
3552
* get item for a key
3653
* @param {string} key
54+
* @param {boolean} [throwErrorIfNotFound] default `true`
55+
*/
56+
itemDefForKey (key, throwErrorIfNotFound = true) {
57+
if (this.#itemsDefs[key]) return this.#itemsDefs[key];
58+
const defData = this.#modelData.items[key];
59+
if (!defData) {
60+
if (throwErrorIfNotFound) throw new Error('Cannot find item definition with key: ' + key);
61+
return null;
62+
}
63+
this.#itemsDefs[key] = new HDSItemDef(defData);
64+
return this.#itemsDefs[key];
65+
}
66+
67+
/**
68+
* get a definition for an event
69+
* @param {Event} event
70+
* @param {boolean} [throwErrorIfNotFound] default `true`
3771
*/
38-
itemForKey (key) {
39-
return this.#modelData.items[key];
72+
itemDefForEvent (event, throwErrorIfNotFound = true) {
73+
const candidates = [];
74+
for (const streamId of event.streamIds) {
75+
const keyStreamIdEventType = streamId + ':' + event.type;
76+
const candidate = this.#modelDataByStreamIdEventTypes[keyStreamIdEventType];
77+
if (candidate) candidates.push(candidate);
78+
}
79+
if (candidates.length === 0) {
80+
if (throwErrorIfNotFound) throw new Error('Cannot find definition for event: ' + JSON.stringify(event));
81+
return null;
82+
}
83+
if (candidates.length > 1) {
84+
throw new Error(`Found multiple matching definitions "${candidates.map(c => (c.key)).join(', ')}" for event: ${JSON.stringify(event)}`);
85+
}
86+
return this.itemDefForKey(candidates[0].key, throwErrorIfNotFound);
4087
}
4188
}
4289

@@ -62,3 +109,26 @@ function deepFreeze (object) {
62109

63110
return Object.freeze(object);
64111
}
112+
113+
/**
114+
* @private
115+
* Add key to model items and
116+
* load modeldata item into modelDataByStreamIdEventTypes for fast search
117+
*/
118+
function loadModelDataByStreamIdEventTypes (model, map) {
119+
for (const [key, item] of Object.entries(model)) {
120+
// add key to item
121+
item.key = key;
122+
const eventTypes = [];
123+
if (item.eventType) {
124+
eventTypes.push(item.eventType);
125+
} else {
126+
eventTypes.push(...Object.keys(item.variations.eventType));
127+
}
128+
for (const eventType of eventTypes) {
129+
const keyStreamIdEventType = item.streamId + ':' + eventType;
130+
if (map[keyStreamIdEventType]) throw new Error(`Duplicate sreamId + eventType "${keyStreamIdEventType}" for item ${JSON.stringify(item)}`);
131+
map[keyStreamIdEventType] = item;
132+
}
133+
}
134+
}

tests/hdsModel.test.js

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,53 @@ const modelURL = 'https://model.datasafe.dev/pack.json';
66
const { HDSModel } = require('../');
77

88
describe('[MODX] Model', () => {
9-
it('[MODL] Load model', async () => {
10-
const model = new HDSModel(modelURL);
9+
let model;
10+
before(async () => {
11+
model = new HDSModel(modelURL);
1112
await model.load();
12-
const item = model.itemForKey('body-weight');
13-
assert.equal(item.streamId, 'body-weight');
13+
});
14+
15+
it('[MODL] Load model', async () => {
16+
const modelLoad = new HDSModel(modelURL);
17+
await modelLoad.load();
18+
const itemDef = modelLoad.itemDefForKey('body-weight');
19+
assert.equal(itemDef.data.streamId, 'body-weight');
20+
assert.deepEqual(itemDef.types, ['mass/kg', 'mass/lb']);
21+
});
22+
23+
it('[MODS] Get definition from event data', async () => {
24+
const fakeEvent = {
25+
streamIds: ['body-weight', 'dummy'],
26+
type: 'mass/kg'
27+
};
28+
const itemDef = model.itemDefForEvent(fakeEvent);
29+
assert.equal(itemDef.data.streamId, 'body-weight');
30+
assert.deepEqual(itemDef.types, ['mass/kg', 'mass/lb']);
31+
});
32+
33+
it('[MOEN] Throw error if itemDefForEvent not found', async () => {
34+
const fakeEvent = {
35+
streamIds: ['dummy'],
36+
type: 'mass/kg'
37+
};
38+
try {
39+
model.itemDefForEvent(fakeEvent);
40+
throw new Error('Should throw Error');
41+
} catch (e) {
42+
assert.equal(e.message, 'Cannot find definition for event: {"streamIds":["dummy"],"type":"mass/kg"}');
43+
}
44+
});
45+
46+
it('[MOED] Throw error if itemDefForEvent finds duplicates', async () => {
47+
const fakeEvent = {
48+
streamIds: ['body-vulva-wetness-feeling', 'body-vulva-mucus-stretch'],
49+
type: 'ratio/generic'
50+
};
51+
try {
52+
model.itemDefForEvent(fakeEvent);
53+
throw new Error('Should throw Error');
54+
} catch (e) {
55+
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"}');
56+
}
1457
});
1558
});

0 commit comments

Comments
 (0)