@proteinjs/db 1.3.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/dist/generated/index.d.ts +1 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +4 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/src/Columns.d.ts.map +1 -1
- package/dist/src/Columns.js +12 -2
- package/dist/src/Columns.js.map +1 -1
- package/dist/src/DbInitStartupTask.d.ts +7 -0
- package/dist/src/DbInitStartupTask.d.ts.map +1 -0
- package/dist/src/DbInitStartupTask.js +61 -0
- package/dist/src/DbInitStartupTask.js.map +1 -0
- package/dist/src/reference/ReferenceArray.d.ts +3 -4
- package/dist/src/reference/ReferenceArray.d.ts.map +1 -1
- package/dist/src/reference/ReferenceArray.js +22 -51
- package/dist/src/reference/ReferenceArray.js.map +1 -1
- package/dist/test/ReferenceArray.test.js +38 -54
- package/dist/test/ReferenceArray.test.js.map +1 -1
- package/dist/test/ReferenceArraySerializer.test.js +41 -8
- package/dist/test/ReferenceArraySerializer.test.js.map +1 -1
- package/dist/test/reusable/CrudTests.d.ts +1 -1
- package/generated/index.ts +18 -18
- package/package.json +6 -4
- package/src/Columns.ts +11 -1
- package/src/DbInitStartupTask.ts +10 -0
- package/src/reference/ReferenceArray.ts +23 -46
- package/test/ReferenceArray.test.ts +29 -58
- package/test/ReferenceArraySerializer.test.ts +48 -7
|
@@ -4,7 +4,7 @@ import { Record } from '../Record';
|
|
|
4
4
|
import { tableByName } from '../Table';
|
|
5
5
|
import { ReferenceArraySerializerId } from '../serializers/ReferenceArraySerializer';
|
|
6
6
|
import { QueryBuilderFactory } from '../QueryBuilderFactory';
|
|
7
|
-
|
|
7
|
+
import { Logger } from '@proteinjs/util';
|
|
8
8
|
/**
|
|
9
9
|
* The object returned by Db functions for each field of type ReferenceArrayColumn in a record.
|
|
10
10
|
* The reason for this is to make loading of reference records on-demand. For theoretically
|
|
@@ -21,78 +21,55 @@ import { QueryBuilderFactory } from '../QueryBuilderFactory';
|
|
|
21
21
|
*/
|
|
22
22
|
export class ReferenceArray<T extends Record> implements CustomSerializableObject {
|
|
23
23
|
public __serializerId = ReferenceArraySerializerId;
|
|
24
|
-
|
|
25
24
|
constructor(
|
|
26
25
|
public _table: string,
|
|
27
26
|
public _ids: string[],
|
|
28
27
|
public _objects?: T[]
|
|
29
|
-
) {
|
|
30
|
-
if (this._objects) {
|
|
31
|
-
this._objects = this.createProxy(this._objects);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
28
|
+
) {}
|
|
34
29
|
|
|
35
30
|
static fromObjects<T extends Record>(table: string, objects: (T | (Partial<T> & { id: string }))[]) {
|
|
36
31
|
const ids = objects.map((object) => object.id);
|
|
37
32
|
return new ReferenceArray<T>(table, ids, objects as T[]);
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*/
|
|
43
|
-
private createProxy(objects: T[]): T[] {
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
45
|
-
const referenceArray = this;
|
|
46
|
-
const handler: ProxyHandler<T[]> = {
|
|
47
|
-
get(target: T[], property: string | symbol, receiver: any) {
|
|
48
|
-
const value = Reflect.get(target, property, receiver);
|
|
49
|
-
if (typeof value === 'function' && ['push', 'pop', 'splice'].includes(property as string)) {
|
|
50
|
-
return function (...args: any[]) {
|
|
51
|
-
const result = (target as any)[property](...args);
|
|
52
|
-
referenceArray._ids = target.map((obj: T) => obj.id);
|
|
53
|
-
return result;
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
return value;
|
|
57
|
-
},
|
|
58
|
-
set(target: T[], property: string | symbol, value: any, receiver: any) {
|
|
59
|
-
const result = Reflect.set(target, property, value, receiver);
|
|
60
|
-
if (typeof property === 'number' || !isNaN(Number(property))) {
|
|
61
|
-
referenceArray._ids = target.map((obj: T) => obj.id);
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
64
|
-
},
|
|
65
|
-
deleteProperty(target: T[], property: string | symbol) {
|
|
66
|
-
if (typeof property === 'number' || !isNaN(Number(property))) {
|
|
67
|
-
target.splice(Number(property), 1);
|
|
68
|
-
}
|
|
69
|
-
referenceArray._ids = target.map((obj: T) => obj.id);
|
|
70
|
-
return true;
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
return new Proxy(objects, handler);
|
|
35
|
+
isLoaded(): boolean {
|
|
36
|
+
return this._objects !== undefined;
|
|
74
37
|
}
|
|
75
38
|
|
|
76
39
|
async get(): Promise<T[]> {
|
|
77
40
|
if (!this._objects) {
|
|
78
41
|
if (this._ids.length < 1) {
|
|
79
|
-
this._objects =
|
|
42
|
+
this._objects = [];
|
|
80
43
|
} else {
|
|
81
44
|
const table = tableByName(this._table);
|
|
82
45
|
const db = getDb();
|
|
83
46
|
const qb = new QueryBuilderFactory().getQueryBuilder(table);
|
|
84
47
|
qb.condition({ field: 'id', operator: 'IN', value: this._ids });
|
|
85
48
|
qb.sort([{ field: 'id', byValues: this._ids }]);
|
|
86
|
-
|
|
87
|
-
this._objects = this.createProxy(objects);
|
|
49
|
+
this._objects = await db.query(table, qb);
|
|
88
50
|
}
|
|
89
51
|
}
|
|
90
52
|
|
|
91
53
|
return this._objects;
|
|
92
54
|
}
|
|
93
55
|
|
|
56
|
+
getIfExists(): T[] | undefined {
|
|
57
|
+
if (this._objects) {
|
|
58
|
+
return this._objects;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getLength(): number {
|
|
65
|
+
if (this._objects) {
|
|
66
|
+
return this._objects.length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this._ids.length;
|
|
70
|
+
}
|
|
71
|
+
|
|
94
72
|
set(objects: T[]) {
|
|
95
|
-
this._objects =
|
|
96
|
-
this._ids = objects.map((object) => object.id);
|
|
73
|
+
this._objects = objects;
|
|
97
74
|
}
|
|
98
75
|
}
|
|
@@ -13,81 +13,52 @@ class MockRecord implements Record {
|
|
|
13
13
|
describe('ReferenceArray', () => {
|
|
14
14
|
const createMockRecord = (id: string) => new MockRecord(id, moment(), moment());
|
|
15
15
|
|
|
16
|
-
it('should initialize with correct ids
|
|
17
|
-
const
|
|
18
|
-
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2'], objects);
|
|
19
|
-
|
|
16
|
+
it('should initialize with correct ids', () => {
|
|
17
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2']);
|
|
20
18
|
expect(refArray._ids).toEqual(['1', '2']);
|
|
21
|
-
expect(refArray._objects).
|
|
19
|
+
expect(refArray._objects).toBeUndefined();
|
|
22
20
|
});
|
|
23
21
|
|
|
24
|
-
it('should
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
// Push a new object
|
|
29
|
-
const refArrayObjects = await refArray.get();
|
|
30
|
-
if (refArrayObjects) {
|
|
31
|
-
refArrayObjects.push(createMockRecord('3'));
|
|
32
|
-
expect(refArray._ids).toEqual(['1', '2', '3']);
|
|
33
|
-
|
|
34
|
-
// Modify an object
|
|
35
|
-
refArrayObjects[1] = createMockRecord('4');
|
|
36
|
-
expect(refArray._ids).toEqual(['1', '4', '3']);
|
|
22
|
+
it('should set and get objects', async () => {
|
|
23
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2']);
|
|
24
|
+
const mockObjects = [createMockRecord('1'), createMockRecord('2')];
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
expect(refArray._ids).toEqual(['1', '4']);
|
|
26
|
+
refArray.set(mockObjects);
|
|
27
|
+
const objects = await refArray.get();
|
|
41
28
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
expect(refArray._ids).toEqual(['4']);
|
|
45
|
-
} else {
|
|
46
|
-
fail('refArrayObjects is undefined');
|
|
47
|
-
}
|
|
29
|
+
expect(objects).toEqual(mockObjects);
|
|
30
|
+
expect(refArray._objects).toBeDefined();
|
|
48
31
|
});
|
|
49
32
|
|
|
50
|
-
it('should
|
|
51
|
-
const
|
|
52
|
-
const
|
|
33
|
+
it('should set objects without updating ids', () => {
|
|
34
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2']);
|
|
35
|
+
const newObjects = [createMockRecord('3'), createMockRecord('4')];
|
|
53
36
|
|
|
54
|
-
const newObjects = [createMockRecord('5'), createMockRecord('6')];
|
|
55
37
|
refArray.set(newObjects);
|
|
56
|
-
expect(refArray._ids).toEqual(['
|
|
38
|
+
expect(refArray._ids).toEqual(['1', '2']); // ids remain unchanged
|
|
57
39
|
expect(refArray._objects).toEqual(newObjects);
|
|
58
40
|
});
|
|
59
41
|
|
|
60
|
-
it('should not leave null or undefined entries in ids array when objects are deleted', () => {
|
|
61
|
-
const objects = [createMockRecord('1'), createMockRecord('2'), createMockRecord('3')];
|
|
62
|
-
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2', '3'], objects);
|
|
63
|
-
|
|
64
|
-
if (refArray._objects) {
|
|
65
|
-
// Delete the second object using splice
|
|
66
|
-
refArray._objects.splice(1, 1);
|
|
67
|
-
expect(refArray._ids).toEqual(['1', '3']);
|
|
68
|
-
|
|
69
|
-
// Delete the first object using splice
|
|
70
|
-
refArray._objects.splice(0, 1);
|
|
71
|
-
expect(refArray._ids).toEqual(['3']);
|
|
72
|
-
|
|
73
|
-
// Delete the remaining object
|
|
74
|
-
refArray._objects.pop();
|
|
75
|
-
expect(refArray._ids).toEqual([]);
|
|
76
|
-
} else {
|
|
77
|
-
fail('refArray._objects is undefined');
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
42
|
it('should handle initializing with an empty array', async () => {
|
|
82
43
|
const refArray = new ReferenceArray<MockRecord>('mock_table', []);
|
|
83
44
|
|
|
84
45
|
expect(refArray._ids).toEqual([]);
|
|
46
|
+
expect(refArray._objects).toBeUndefined();
|
|
47
|
+
|
|
48
|
+
const objects = await refArray.get();
|
|
49
|
+
expect(objects).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should not update ids when objects are modified', async () => {
|
|
53
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2']);
|
|
54
|
+
const initialObjects = [createMockRecord('1'), createMockRecord('2')];
|
|
55
|
+
refArray.set(initialObjects);
|
|
85
56
|
|
|
86
|
-
const
|
|
87
|
-
|
|
57
|
+
const objects = await refArray.get();
|
|
58
|
+
objects.push(createMockRecord('3'));
|
|
59
|
+
objects[1] = createMockRecord('4');
|
|
60
|
+
objects.pop();
|
|
88
61
|
|
|
89
|
-
|
|
90
|
-
expect(refArray._ids).toEqual(['1']);
|
|
91
|
-
expect(refArrayObjects.length).toEqual(1);
|
|
62
|
+
expect(refArray._ids).toEqual(['1', '2']); // ids remain unchanged
|
|
92
63
|
});
|
|
93
64
|
});
|
|
@@ -27,16 +27,57 @@ describe('ReferenceArraySerializer', () => {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
const deserialized = serializer.deserialize(serialized);
|
|
30
|
+
expect(deserialized).toBeInstanceOf(ReferenceArray);
|
|
30
31
|
expect(deserialized._table).toEqual('mock_table');
|
|
31
32
|
expect(deserialized._ids).toEqual(['1', '2']);
|
|
32
33
|
expect(deserialized._objects).toEqual(objects);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle empty ReferenceArray', () => {
|
|
37
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', []);
|
|
38
|
+
const serializer = new ReferenceArraySerializer();
|
|
39
|
+
|
|
40
|
+
const serialized = serializer.serialize(refArray);
|
|
41
|
+
expect(serialized).toEqual({
|
|
42
|
+
_table: 'mock_table',
|
|
43
|
+
_ids: [],
|
|
44
|
+
_objects: undefined,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const deserialized = serializer.deserialize(serialized);
|
|
48
|
+
expect(deserialized).toBeInstanceOf(ReferenceArray);
|
|
49
|
+
expect(deserialized._table).toEqual('mock_table');
|
|
50
|
+
expect(deserialized._ids).toEqual([]);
|
|
51
|
+
expect(deserialized._objects).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle ReferenceArray with ids but no objects', () => {
|
|
55
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2']);
|
|
56
|
+
const serializer = new ReferenceArraySerializer();
|
|
57
|
+
|
|
58
|
+
const serialized = serializer.serialize(refArray);
|
|
59
|
+
expect(serialized).toEqual({
|
|
60
|
+
_table: 'mock_table',
|
|
61
|
+
_ids: ['1', '2'],
|
|
62
|
+
_objects: undefined,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const deserialized = serializer.deserialize(serialized);
|
|
66
|
+
expect(deserialized).toBeInstanceOf(ReferenceArray);
|
|
67
|
+
expect(deserialized._table).toEqual('mock_table');
|
|
68
|
+
expect(deserialized._ids).toEqual(['1', '2']);
|
|
69
|
+
expect(deserialized._objects).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should maintain separate ids and objects after deserialization', () => {
|
|
73
|
+
const objects = [createMockRecord('1'), createMockRecord('2')];
|
|
74
|
+
const refArray = new ReferenceArray<MockRecord>('mock_table', ['3', '4'], objects);
|
|
75
|
+
const serializer = new ReferenceArraySerializer();
|
|
76
|
+
|
|
77
|
+
const serialized = serializer.serialize(refArray);
|
|
78
|
+
const deserialized = serializer.deserialize(serialized);
|
|
33
79
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
deserialized._objects.push(createMockRecord('3'));
|
|
37
|
-
expect(deserialized._ids).toEqual(['1', '2', '3']);
|
|
38
|
-
} else {
|
|
39
|
-
fail('deserialized._objects is undefined');
|
|
40
|
-
}
|
|
80
|
+
expect(deserialized._ids).toEqual(['3', '4']);
|
|
81
|
+
expect(deserialized._objects?.map((obj) => obj.id)).toEqual(['1', '2']);
|
|
41
82
|
});
|
|
42
83
|
});
|