@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.
@@ -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
- * Used to keep `_ids` in sync with `_objects`
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 = this.createProxy([]);
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
- const objects = await db.query(table, qb);
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 = this.createProxy(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 and objects', () => {
17
- const objects = [createMockRecord('1'), createMockRecord('2')];
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).toEqual(objects);
19
+ expect(refArray._objects).toBeUndefined();
22
20
  });
23
21
 
24
- it('should update ids when objects are modified', async () => {
25
- const objects = [createMockRecord('1'), createMockRecord('2')];
26
- const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2'], objects);
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
- // Delete an object using pop
39
- refArrayObjects.pop();
40
- expect(refArray._ids).toEqual(['1', '4']);
26
+ refArray.set(mockObjects);
27
+ const objects = await refArray.get();
41
28
 
42
- // Delete an object using splice
43
- refArrayObjects.splice(0, 1);
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 update ids when objects are set', () => {
51
- const objects = [createMockRecord('1'), createMockRecord('2')];
52
- const refArray = new ReferenceArray<MockRecord>('mock_table', ['1', '2'], objects);
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(['5', '6']);
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 refArrayObjects = await refArray.get();
87
- expect(refArrayObjects).toEqual([]);
57
+ const objects = await refArray.get();
58
+ objects.push(createMockRecord('3'));
59
+ objects[1] = createMockRecord('4');
60
+ objects.pop();
88
61
 
89
- refArrayObjects.push(createMockRecord('1'));
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
- // Test if the proxy is correctly applied
35
- if (deserialized._objects) {
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
  });