Skip to content

Commit

Permalink
WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
Syndesi committed Mar 17, 2024
1 parent 00bf7f9 commit dc25a5f
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 14 deletions.
28 changes: 26 additions & 2 deletions src/EmberNexus.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'reflect-metadata';

import { LRUCache } from 'lru-cache';
import { Container } from 'typedi';

import GetElementEndpoint from '~/Endpoint/Element/GetElementEndpoint';
Expand All @@ -10,8 +11,31 @@ import { Relation } from '~/Type/Definition/Relation';
import { Uuid } from '~/Type/Definition/Uuid';

class EmberNexus {
test(uuid: Uuid): Promise<Node | Relation> {
return Container.get(GetElementEndpoint).getElement(uuid);
private cache: LRUCache<Uuid, Node | Relation>;

constructor() {
this.cache = new LRUCache<Uuid, Node | Relation>({
max: Container.get(WebSdkConfiguration).getElementCacheMaxEntries(),
});
}

getElement(uuid: Uuid): Promise<Node | Relation> {
return new Promise<Node | Relation>((resolve) => {
if (this.cache.has(uuid)) {
const element = this.cache.get(uuid);
if (element !== undefined) {
return resolve(element);
}
}
return resolve(
Container.get(GetElementEndpoint)
.getElement(uuid)
.then((element) => {
this.cache.set(uuid, element);
return element;
}),
);
});
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/Service/WebSdkConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { Token } from '~/Type/Definition/Token';
class WebSdkConfiguration {
private token: Token | null;
private apiHost: string;
private elementCacheMaxEntries: number;

constructor() {
this.token = null;
this.apiHost = '';
this.elementCacheMaxEntries = 100;
}

hasToken(): boolean {
Expand All @@ -30,6 +32,15 @@ class WebSdkConfiguration {
this.apiHost = apiHost;
return this;
}

getElementCacheMaxEntries(): number {
return this.elementCacheMaxEntries;
}

setElementCacheMaxEntries(value: number): WebSdkConfiguration {
this.elementCacheMaxEntries = value;
return this;
}
}

export { WebSdkConfiguration };
62 changes: 62 additions & 0 deletions test/Feature/EmberNexus/EmberNexusGetElementNode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { expect } from 'chai';
import { HttpResponse, http } from 'msw';
// eslint-disable-next-line import/no-unresolved
import { setupServer } from 'msw/node';
import { Container } from 'typedi';

import { EmberNexus } from '~/EmberNexus';
import { Logger } from '~/Service/Logger';
import { WebSdkConfiguration } from '~/Service/WebSdkConfiguration';
import { validateUuidFromString } from '~/Type/Definition/Uuid';

import { TestLogger } from '../TestLogger';

const mockServer = setupServer(
http.get('http://mock-api/2ce13d4f-bd96-4b71-b4e7-d43bd9948836', () => {
return HttpResponse.json(
{
type: 'Data',
id: '2ce13d4f-bd96-4b71-b4e7-d43bd9948836',
data: {
created: '2023-10-06T20:27:56+00:00',
updated: '2023-10-06T20:27:56+00:00',
name: 'Test Data',
},
},
{
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
},
);
}),
);

const testLogger: TestLogger = new TestLogger();
Container.set(Logger, testLogger);
Container.get(WebSdkConfiguration).setApiHost('http://mock-api');

test('EmberNexus should handle node request', async () => {
mockServer.listen();
const uuid = validateUuidFromString('2ce13d4f-bd96-4b71-b4e7-d43bd9948836');

const emberNexus = new EmberNexus();
const node = await emberNexus.getElement(uuid);

expect(
testLogger.assertDebugHappened(
'Executing HTTP GET request against url http://mock-api/2ce13d4f-bd96-4b71-b4e7-d43bd9948836 .',
),
).to.be.true;

expect(node).to.have.keys('id', 'type', 'data');
expect(node).to.not.have.keys('start', 'end');
expect(node.id).to.equal(uuid);
expect(node.type).to.equal('Data');
expect(node.data.created).to.be.instanceof(Date);
expect(node.data.updated).to.be.instanceof(Date);
expect(Object.keys(node.data)).to.have.lengthOf(3);

mockServer.close();
});
57 changes: 57 additions & 0 deletions test/Feature/EmberNexus/EmberNexusGetElementNodeWithCache.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { expect } from 'chai';
import { HttpResponse, http } from 'msw';
// eslint-disable-next-line import/no-unresolved
import { setupServer } from 'msw/node';
import { Container } from 'typedi';

import { EmberNexus } from '~/EmberNexus';
import { Logger } from '~/Service/Logger';
import { WebSdkConfiguration } from '~/Service/WebSdkConfiguration';
import { validateUuidFromString } from '~/Type/Definition/Uuid';

import { TestLogger } from '../TestLogger';

const mockServer = setupServer(
http.get('http://mock-api/2ce13d4f-bd96-4b71-b4e7-d43bd9948836', () => {
return HttpResponse.json(
{
type: 'Data',
id: '2ce13d4f-bd96-4b71-b4e7-d43bd9948836',
data: {
created: '2023-10-06T20:27:56+00:00',
updated: '2023-10-06T20:27:56+00:00',
name: 'Test Data',
},
},
{
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
},
);
}),
);

const testLogger: TestLogger = new TestLogger();
Container.set(Logger, testLogger);
Container.get(WebSdkConfiguration).setApiHost('http://mock-api');

test('EmberNexus should use the cache for repeated node requests', async () => {
mockServer.listen();
const uuid = validateUuidFromString('2ce13d4f-bd96-4b71-b4e7-d43bd9948836');

const emberNexus = new EmberNexus();
const node1 = await emberNexus.getElement(uuid);
const node2 = await emberNexus.getElement(uuid);

expect(
testLogger.assertDebugHappened(
'Executing HTTP GET request against url http://mock-api/2ce13d4f-bd96-4b71-b4e7-d43bd9948836 .',
),
).to.be.true;

expect(node1).to.be.deep.equal(node2);

mockServer.close();
});
63 changes: 63 additions & 0 deletions test/Feature/EmberNexus/EmberNexusGetElementRelation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { expect } from 'chai';
import { HttpResponse, http } from 'msw';
// eslint-disable-next-line import/no-unresolved
import { setupServer } from 'msw/node';
import { Container } from 'typedi';

import { EmberNexus } from '~/EmberNexus';
import { Logger } from '~/Service/Logger';
import { WebSdkConfiguration } from '~/Service/WebSdkConfiguration';
import { validateUuidFromString } from '~/Type/Definition/Uuid';

import { TestLogger } from '../TestLogger';

const mockServer = setupServer(
http.get('http://mock-api/eb243613-832b-411a-a161-616cdd75e2f9', () => {
return HttpResponse.json(
{
type: 'RELATION',
id: 'eb243613-832b-411a-a161-616cdd75e2f9',
start: 'ab5a01aa-6a02-42a9-9613-69d9453d0064',
end: '0b624f56-a8ed-43f0-a250-4a821679a51f',
data: {
created: '2023-10-06T20:27:56+00:00',
updated: '2023-10-06T20:27:56+00:00',
name: 'Test relation',
},
},
{
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
},
);
}),
);

const testLogger: TestLogger = new TestLogger();
Container.set(Logger, testLogger);
Container.get(WebSdkConfiguration).setApiHost('http://mock-api');

test('EmberNexus should handle relation request', async () => {
mockServer.listen();
const uuid = validateUuidFromString('eb243613-832b-411a-a161-616cdd75e2f9');

const emberNexus = new EmberNexus();
const relation = await emberNexus.getElement(uuid);

expect(
testLogger.assertDebugHappened(
'Executing HTTP GET request against url http://mock-api/eb243613-832b-411a-a161-616cdd75e2f9 .',
),
).to.be.true;

expect(relation).to.have.keys('id', 'start', 'end', 'type', 'data');
expect(relation.id).to.equal(uuid);
expect(relation.type).to.equal('RELATION');
expect(relation.data.created).to.be.instanceof(Date);
expect(relation.data.updated).to.be.instanceof(Date);
expect(Object.keys(relation.data)).to.have.lengthOf(3);

mockServer.close();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { expect } from 'chai';
import { HttpResponse, http } from 'msw';
// eslint-disable-next-line import/no-unresolved
import { setupServer } from 'msw/node';
import { Container } from 'typedi';

import { EmberNexus } from '~/EmberNexus';
import { Logger } from '~/Service/Logger';
import { WebSdkConfiguration } from '~/Service/WebSdkConfiguration';
import { validateUuidFromString } from '~/Type/Definition/Uuid';

import { TestLogger } from '../TestLogger';

const mockServer = setupServer(
http.get('http://mock-api/32893cda-42db-4287-ac00-e73f250e1eeb', () => {
return HttpResponse.json(
{
type: 'RELATION',
id: '32893cda-42db-4287-ac00-e73f250e1eeb',
start: 'ab5a01aa-6a02-42a9-9613-69d9453d0064',
end: '0b624f56-a8ed-43f0-a250-4a821679a51f',
data: {
created: '2023-10-06T20:27:56+00:00',
updated: '2023-10-06T20:27:56+00:00',
name: 'Test relation',
},
},
{
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
},
);
}),
);

const testLogger: TestLogger = new TestLogger();
Container.set(Logger, testLogger);
Container.get(WebSdkConfiguration).setApiHost('http://mock-api');

test('EmberNexus should use the cache for repeated relation request', async () => {
mockServer.listen();
const uuid = validateUuidFromString('32893cda-42db-4287-ac00-e73f250e1eeb');

const emberNexus = new EmberNexus();
const relation1 = await emberNexus.getElement(uuid);
const relation2 = await emberNexus.getElement(uuid);

expect(
testLogger.assertDebugHappened(
'Executing HTTP GET request against url http://mock-api/32893cda-42db-4287-ac00-e73f250e1eeb .',
),
).to.be.true;

expect(relation1).to.be.deep.equal(relation2);

mockServer.close();
});
28 changes: 16 additions & 12 deletions test/Feature/TestLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,44 @@ class TestLogger implements LoggerInterface {
return undefined;
}

assertDebugHappened(message: string): boolean {
assertDebugHappened(message: string, assertTimes: number = 1): boolean {
let timesHappened = 0;
for (let i = 0; i < this.debugCalls.length; i++) {
if (this.debugCalls[i][0] === message) {
return true;
timesHappened++;
}
}
return false;
return assertTimes === timesHappened;
}

assertErrorHappened(message: string): boolean {
assertErrorHappened(message: string, assertTimes: number = 1): boolean {
let timesHappened = 0;
for (let i = 0; i < this.errorCalls.length; i++) {
if (this.errorCalls[i][0] === message) {
return true;
timesHappened++;
}
}
return false;
return assertTimes === timesHappened;
}

assertInfoHappened(message: string): boolean {
assertInfoHappened(message: string, assertTimes: number = 1): boolean {
let timesHappened = 0;
for (let i = 0; i < this.infoCalls.length; i++) {
if (this.infoCalls[i][0] === message) {
return true;
timesHappened++;
}
}
return false;
return assertTimes === timesHappened;
}

assertWarnHappened(message: string): boolean {
assertWarnHappened(message: string, assertTimes: number = 1): boolean {
let timesHappened = 0;
for (let i = 0; i < this.warnCalls.length; i++) {
if (this.warnCalls[i][0] === message) {
return true;
timesHappened++;
}
}
return false;
return assertTimes === timesHappened;
}

printAllCalls(): void {
Expand Down

0 comments on commit dc25a5f

Please sign in to comment.