@nymphjs/nymph 1.0.0-beta.57 → 1.0.0-beta.59
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 +10 -0
- package/README.md +0 -8
- package/dist/Entity.d.ts +65 -6
- package/dist/Entity.js +53 -1
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +1 -0
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +15 -70
- package/dist/Nymph.d.ts +14 -6
- package/dist/Nymph.js +37 -11
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +2 -2
- package/dist/driver/NymphDriver.d.ts +47 -11
- package/dist/driver/NymphDriver.js +28 -5
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +1 -1
- package/dist/driver/testArtifacts.js +77 -3
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +11 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +2 -6
- package/dist/testArtifacts.js +4 -18
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +9 -7
- package/dist/testMocks.js +19 -10
- package/dist/testMocks.js.map +1 -1
- package/package.json +3 -3
- package/src/Entity.test.ts +1 -0
- package/src/Entity.ts +113 -15
- package/src/Entity.types.ts +18 -70
- package/src/Nymph.ts +55 -15
- package/src/Nymph.types.ts +2 -2
- package/src/driver/NymphDriver.ts +74 -67
- package/src/driver/testArtifacts.ts +102 -1
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/index.ts +2 -0
- package/src/testArtifacts.ts +8 -27
- package/src/testMocks.ts +32 -17
|
@@ -5,7 +5,9 @@ import strtotime from 'locutus/php/datetime/strtotime';
|
|
|
5
5
|
import { guid } from '@nymphjs/guid';
|
|
6
6
|
|
|
7
7
|
import type Nymph from '../Nymph';
|
|
8
|
-
import {
|
|
8
|
+
import type { Selector, Options, FormattedSelector } from '../Nymph.types';
|
|
9
|
+
import Entity, { type EntityInstanceType } from '../Entity';
|
|
10
|
+
import type {
|
|
9
11
|
EntityConstructor,
|
|
10
12
|
EntityData,
|
|
11
13
|
EntityInterface,
|
|
@@ -13,7 +15,6 @@ import {
|
|
|
13
15
|
SerializedEntityData,
|
|
14
16
|
} from '../Entity.types';
|
|
15
17
|
import { InvalidParametersError, UnableToConnectError } from '../errors';
|
|
16
|
-
import { Selector, Options, FormattedSelector } from '../Nymph.types';
|
|
17
18
|
import { xor } from '../utils';
|
|
18
19
|
|
|
19
20
|
// from: https://stackoverflow.com/a/6969486/664915
|
|
@@ -97,11 +98,11 @@ export default abstract class NymphDriver {
|
|
|
97
98
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
98
99
|
options?: Options<T>,
|
|
99
100
|
...selectors: Selector[]
|
|
100
|
-
): Promise<
|
|
101
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
101
102
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
102
103
|
options?: Options<T>,
|
|
103
104
|
...selectors: Selector[]
|
|
104
|
-
): Promise<
|
|
105
|
+
): Promise<EntityInstanceType<T>[] | string[] | number>;
|
|
105
106
|
abstract getUID(name: string): Promise<number | null>;
|
|
106
107
|
abstract import(filename: string, transaction?: boolean): Promise<boolean>;
|
|
107
108
|
abstract newUID(name: string): Promise<number | null>;
|
|
@@ -662,12 +663,12 @@ export default abstract class NymphDriver {
|
|
|
662
663
|
|
|
663
664
|
protected iterateSelectorsForQuery(
|
|
664
665
|
selectors: FormattedSelector[],
|
|
665
|
-
callback: (
|
|
666
|
-
key: string
|
|
667
|
-
value: any
|
|
668
|
-
typeIsOr: boolean
|
|
669
|
-
typeIsNot: boolean
|
|
670
|
-
) => string,
|
|
666
|
+
callback: (data: {
|
|
667
|
+
key: string;
|
|
668
|
+
value: any;
|
|
669
|
+
typeIsOr: boolean;
|
|
670
|
+
typeIsNot: boolean;
|
|
671
|
+
}) => string,
|
|
671
672
|
) {
|
|
672
673
|
const queryParts = [];
|
|
673
674
|
for (const curSelector of selectors) {
|
|
@@ -680,7 +681,7 @@ export default abstract class NymphDriver {
|
|
|
680
681
|
if (key === 'type') {
|
|
681
682
|
continue;
|
|
682
683
|
}
|
|
683
|
-
let curQuery = callback(key, value, typeIsOr, typeIsNot);
|
|
684
|
+
let curQuery = callback({ key, value, typeIsOr, typeIsNot });
|
|
684
685
|
if (curQuery) {
|
|
685
686
|
if (curSelectorQuery) {
|
|
686
687
|
curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -699,11 +700,11 @@ export default abstract class NymphDriver {
|
|
|
699
700
|
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
700
701
|
options: Options<T> & { return: 'count' },
|
|
701
702
|
selectors: Selector[],
|
|
702
|
-
performQueryCallback: (
|
|
703
|
-
options: Options<T
|
|
704
|
-
|
|
705
|
-
etype: string
|
|
706
|
-
) => {
|
|
703
|
+
performQueryCallback: (data: {
|
|
704
|
+
options: Options<T>;
|
|
705
|
+
selectors: FormattedSelector[];
|
|
706
|
+
etype: string;
|
|
707
|
+
}) => {
|
|
707
708
|
result: any;
|
|
708
709
|
},
|
|
709
710
|
rowFetchCallback: () => any,
|
|
@@ -723,11 +724,11 @@ export default abstract class NymphDriver {
|
|
|
723
724
|
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
724
725
|
options: Options<T> & { return: 'guid' },
|
|
725
726
|
selectors: Selector[],
|
|
726
|
-
performQueryCallback: (
|
|
727
|
-
options: Options<T
|
|
728
|
-
|
|
729
|
-
etype: string
|
|
730
|
-
) => {
|
|
727
|
+
performQueryCallback: (data: {
|
|
728
|
+
options: Options<T>;
|
|
729
|
+
selectors: FormattedSelector[];
|
|
730
|
+
etype: string;
|
|
731
|
+
}) => {
|
|
731
732
|
result: any;
|
|
732
733
|
},
|
|
733
734
|
rowFetchCallback: () => any,
|
|
@@ -747,11 +748,11 @@ export default abstract class NymphDriver {
|
|
|
747
748
|
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
748
749
|
options: Options<T>,
|
|
749
750
|
selectors: Selector[],
|
|
750
|
-
performQueryCallback: (
|
|
751
|
-
options: Options<T
|
|
752
|
-
|
|
753
|
-
etype: string
|
|
754
|
-
) => {
|
|
751
|
+
performQueryCallback: (data: {
|
|
752
|
+
options: Options<T>;
|
|
753
|
+
selectors: FormattedSelector[];
|
|
754
|
+
etype: string;
|
|
755
|
+
}) => {
|
|
755
756
|
result: any;
|
|
756
757
|
},
|
|
757
758
|
rowFetchCallback: () => any,
|
|
@@ -767,15 +768,18 @@ export default abstract class NymphDriver {
|
|
|
767
768
|
name: string;
|
|
768
769
|
svalue: string;
|
|
769
770
|
},
|
|
770
|
-
): {
|
|
771
|
+
): {
|
|
772
|
+
result: any;
|
|
773
|
+
process: () => EntityInstanceType<T>[] | Error;
|
|
774
|
+
};
|
|
771
775
|
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
772
776
|
options: Options<T>,
|
|
773
777
|
selectors: Selector[],
|
|
774
|
-
performQueryCallback: (
|
|
775
|
-
options: Options<T
|
|
776
|
-
|
|
777
|
-
etype: string
|
|
778
|
-
) => {
|
|
778
|
+
performQueryCallback: (data: {
|
|
779
|
+
options: Options<T>;
|
|
780
|
+
selectors: FormattedSelector[];
|
|
781
|
+
etype: string;
|
|
782
|
+
}) => {
|
|
779
783
|
result: any;
|
|
780
784
|
},
|
|
781
785
|
rowFetchCallback: () => any,
|
|
@@ -793,7 +797,7 @@ export default abstract class NymphDriver {
|
|
|
793
797
|
},
|
|
794
798
|
): {
|
|
795
799
|
result: any;
|
|
796
|
-
process: () =>
|
|
800
|
+
process: () => EntityInstanceType<T>[] | string[] | number | Error;
|
|
797
801
|
} {
|
|
798
802
|
if (!this.isConnected()) {
|
|
799
803
|
throw new UnableToConnectError('not connected to DB');
|
|
@@ -811,7 +815,7 @@ export default abstract class NymphDriver {
|
|
|
811
815
|
}
|
|
812
816
|
}
|
|
813
817
|
|
|
814
|
-
let entities:
|
|
818
|
+
let entities: EntityInstanceType<T>[] | string[] = [];
|
|
815
819
|
let count = 0;
|
|
816
820
|
const EntityClass =
|
|
817
821
|
options.class ?? (this.nymph.getEntityClass('Entity') as T);
|
|
@@ -833,7 +837,7 @@ export default abstract class NymphDriver {
|
|
|
833
837
|
(Object.keys(selectors[0]).length === 2 ||
|
|
834
838
|
(Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))
|
|
835
839
|
) {
|
|
836
|
-
const entity = this.pullCache<
|
|
840
|
+
const entity = this.pullCache<EntityInstanceType<T>>(
|
|
837
841
|
selectors[0]['guid'],
|
|
838
842
|
EntityClass.class,
|
|
839
843
|
!!options.skipAc,
|
|
@@ -871,11 +875,11 @@ export default abstract class NymphDriver {
|
|
|
871
875
|
const [options, ...selectors] = qref;
|
|
872
876
|
this.nymph.runQueryCallbacks(options, selectors);
|
|
873
877
|
}
|
|
874
|
-
const { result } = performQueryCallback(
|
|
878
|
+
const { result } = performQueryCallback({
|
|
875
879
|
options,
|
|
876
|
-
formattedSelectors.selectors,
|
|
880
|
+
selectors: formattedSelectors.selectors,
|
|
877
881
|
etype,
|
|
878
|
-
);
|
|
882
|
+
});
|
|
879
883
|
|
|
880
884
|
return {
|
|
881
885
|
result,
|
|
@@ -915,9 +919,7 @@ export default abstract class NymphDriver {
|
|
|
915
919
|
// Make sure that $row is incremented :)
|
|
916
920
|
row = rowFetchCallback();
|
|
917
921
|
}
|
|
918
|
-
const entity = EntityClass.factorySync() as
|
|
919
|
-
T['factorySync']
|
|
920
|
-
>;
|
|
922
|
+
const entity = EntityClass.factorySync() as EntityInstanceType<T>;
|
|
921
923
|
entity.$nymph = this.nymph;
|
|
922
924
|
if (options.skipAc != null) {
|
|
923
925
|
entity.$useSkipAc(!!options.skipAc);
|
|
@@ -957,24 +959,26 @@ export default abstract class NymphDriver {
|
|
|
957
959
|
|
|
958
960
|
protected async saveEntityRowLike(
|
|
959
961
|
entity: EntityInterface,
|
|
960
|
-
saveNewEntityCallback: (
|
|
961
|
-
entity: EntityInterface
|
|
962
|
-
guid: string
|
|
963
|
-
tags: string[]
|
|
964
|
-
data: EntityData
|
|
965
|
-
sdata: SerializedEntityData
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
962
|
+
saveNewEntityCallback: (data: {
|
|
963
|
+
entity: EntityInterface;
|
|
964
|
+
guid: string;
|
|
965
|
+
tags: string[];
|
|
966
|
+
data: EntityData;
|
|
967
|
+
sdata: SerializedEntityData;
|
|
968
|
+
uniques: string[];
|
|
969
|
+
cdate: number;
|
|
970
|
+
etype: string;
|
|
971
|
+
}) => Promise<boolean>,
|
|
972
|
+
saveExistingEntityCallback: (data: {
|
|
973
|
+
entity: EntityInterface;
|
|
974
|
+
guid: string;
|
|
975
|
+
tags: string[];
|
|
976
|
+
data: EntityData;
|
|
977
|
+
sdata: SerializedEntityData;
|
|
978
|
+
uniques: string[];
|
|
979
|
+
mdate: number;
|
|
980
|
+
etype: string;
|
|
981
|
+
}) => Promise<boolean>,
|
|
978
982
|
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
979
983
|
commitTransactionCallback:
|
|
980
984
|
| ((success: boolean) => Promise<boolean>)
|
|
@@ -985,6 +989,7 @@ export default abstract class NymphDriver {
|
|
|
985
989
|
const tags = difference(entity.tags, ['']);
|
|
986
990
|
const data = entity.$getData();
|
|
987
991
|
const sdata = entity.$getSData();
|
|
992
|
+
const uniques = await entity.$getUniques();
|
|
988
993
|
const EntityClass = entity.constructor as EntityConstructor;
|
|
989
994
|
const etype = EntityClass.ETYPE;
|
|
990
995
|
if (startTransactionCallback) {
|
|
@@ -994,15 +999,16 @@ export default abstract class NymphDriver {
|
|
|
994
999
|
if (entity.guid == null) {
|
|
995
1000
|
const cdate = mdate;
|
|
996
1001
|
const newId = guid();
|
|
997
|
-
success = await saveNewEntityCallback(
|
|
1002
|
+
success = await saveNewEntityCallback({
|
|
998
1003
|
entity,
|
|
999
|
-
newId,
|
|
1004
|
+
guid: newId,
|
|
1000
1005
|
tags,
|
|
1001
1006
|
data,
|
|
1002
1007
|
sdata,
|
|
1008
|
+
uniques,
|
|
1003
1009
|
cdate,
|
|
1004
1010
|
etype,
|
|
1005
|
-
);
|
|
1011
|
+
});
|
|
1006
1012
|
if (success) {
|
|
1007
1013
|
entity.guid = newId;
|
|
1008
1014
|
entity.cdate = cdate;
|
|
@@ -1013,15 +1019,16 @@ export default abstract class NymphDriver {
|
|
|
1013
1019
|
if (this.nymph.config.cache) {
|
|
1014
1020
|
this.cleanCache(entity.guid);
|
|
1015
1021
|
}
|
|
1016
|
-
success = await saveExistingEntityCallback(
|
|
1022
|
+
success = await saveExistingEntityCallback({
|
|
1017
1023
|
entity,
|
|
1018
|
-
entity.guid,
|
|
1024
|
+
guid: entity.guid,
|
|
1019
1025
|
tags,
|
|
1020
1026
|
data,
|
|
1021
1027
|
sdata,
|
|
1028
|
+
uniques,
|
|
1022
1029
|
mdate,
|
|
1023
1030
|
etype,
|
|
1024
|
-
);
|
|
1031
|
+
});
|
|
1025
1032
|
if (success) {
|
|
1026
1033
|
entity.mdate = mdate;
|
|
1027
1034
|
}
|
|
@@ -1051,7 +1058,7 @@ export default abstract class NymphDriver {
|
|
|
1051
1058
|
* @param useSkipAc Whether to tell the entity to use skip_ac.
|
|
1052
1059
|
* @returns The entity or null if it's not cached.
|
|
1053
1060
|
*/
|
|
1054
|
-
protected pullCache<T extends
|
|
1061
|
+
protected pullCache<T extends Entity>(
|
|
1055
1062
|
guid: string,
|
|
1056
1063
|
className: string,
|
|
1057
1064
|
useSkipAc = false,
|
|
@@ -1125,7 +1132,7 @@ export default abstract class NymphDriver {
|
|
|
1125
1132
|
// Find which entity has been accessed the least.
|
|
1126
1133
|
const least =
|
|
1127
1134
|
Object.entries(this.entityCount)
|
|
1128
|
-
.sort(([
|
|
1135
|
+
.sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
|
|
1129
1136
|
.pop()?.[0] ?? null;
|
|
1130
1137
|
if (least == null) {
|
|
1131
1138
|
// This should never happen.
|
|
@@ -3,6 +3,7 @@ import strtotime from 'locutus/php/datetime/strtotime';
|
|
|
3
3
|
import { guid } from '@nymphjs/guid';
|
|
4
4
|
|
|
5
5
|
import type Nymph from '../Nymph';
|
|
6
|
+
import { EntityUniqueConstraintError } from '../errors';
|
|
6
7
|
import {
|
|
7
8
|
TestBModel as TestBModelClass,
|
|
8
9
|
TestModel as TestModelClass,
|
|
@@ -10,7 +11,7 @@ import {
|
|
|
10
11
|
TestEmptyModel as TestEmptyModelClass,
|
|
11
12
|
} from '../testArtifacts';
|
|
12
13
|
|
|
13
|
-
export function
|
|
14
|
+
export function EntitiesTest(
|
|
14
15
|
nymph: Nymph,
|
|
15
16
|
it: (name: string, fn: () => void) => void,
|
|
16
17
|
) {
|
|
@@ -1804,6 +1805,106 @@ These hats are absolutely fantastic.`;
|
|
|
1804
1805
|
|
|
1805
1806
|
expect(entity).toBeNull();
|
|
1806
1807
|
});
|
|
1808
|
+
|
|
1809
|
+
it('allows non-duplicate unique strings', async () => {
|
|
1810
|
+
// Creating entity...
|
|
1811
|
+
const uniqueEntityA = await TestModel.factory();
|
|
1812
|
+
uniqueEntityA.name = 'Entity Test ' + new Date().toLocaleString();
|
|
1813
|
+
uniqueEntityA.uniques = ['test a'];
|
|
1814
|
+
|
|
1815
|
+
// Saving entity...
|
|
1816
|
+
expect(await uniqueEntityA.$save()).toEqual(true);
|
|
1817
|
+
expect(uniqueEntityA.guid).not.toBeNull();
|
|
1818
|
+
|
|
1819
|
+
// Creating entity...
|
|
1820
|
+
const uniqueEntityB = await TestModel.factory();
|
|
1821
|
+
uniqueEntityB.name = 'Entity Test ' + new Date().toLocaleString();
|
|
1822
|
+
uniqueEntityB.uniques = ['test b'];
|
|
1823
|
+
|
|
1824
|
+
// Saving entity...
|
|
1825
|
+
expect(await uniqueEntityB.$save()).toEqual(true);
|
|
1826
|
+
expect(uniqueEntityB.guid).not.toBeNull();
|
|
1827
|
+
|
|
1828
|
+
expect(uniqueEntityA.guid).not.toEqual(uniqueEntityB.guid);
|
|
1829
|
+
|
|
1830
|
+
expect(await uniqueEntityA.$delete()).toEqual(true);
|
|
1831
|
+
expect(await uniqueEntityB.$delete()).toEqual(true);
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
it('throws on duplicate unique strings', async () => {
|
|
1835
|
+
// Creating entity...
|
|
1836
|
+
const uniqueEntityA = await TestModel.factory();
|
|
1837
|
+
uniqueEntityA.name = 'Entity Test ' + new Date().toLocaleString();
|
|
1838
|
+
uniqueEntityA.uniques = ['test a'];
|
|
1839
|
+
|
|
1840
|
+
// Saving entity...
|
|
1841
|
+
expect(await uniqueEntityA.$save()).toEqual(true);
|
|
1842
|
+
expect(uniqueEntityA.guid).not.toBeNull();
|
|
1843
|
+
|
|
1844
|
+
// Creating entity...
|
|
1845
|
+
const uniqueEntityB = await TestModel.factory();
|
|
1846
|
+
uniqueEntityB.name = 'Entity Test ' + new Date().toLocaleString();
|
|
1847
|
+
uniqueEntityB.uniques = ['test a'];
|
|
1848
|
+
|
|
1849
|
+
// Saving entity...
|
|
1850
|
+
try {
|
|
1851
|
+
await uniqueEntityB.$save();
|
|
1852
|
+
|
|
1853
|
+
throw Error("Shouldn't get past the uniqueness check.");
|
|
1854
|
+
} catch (e: any) {
|
|
1855
|
+
expect(e).toBeInstanceOf(EntityUniqueConstraintError);
|
|
1856
|
+
}
|
|
1857
|
+
expect(uniqueEntityB.guid).toBeNull();
|
|
1858
|
+
|
|
1859
|
+
expect(await uniqueEntityA.$delete()).toEqual(true);
|
|
1860
|
+
});
|
|
1861
|
+
|
|
1862
|
+
it('allows duplicate unique string if first transaction is rolled back', async () => {
|
|
1863
|
+
// Creating transaction entity...
|
|
1864
|
+
const tnymphA = await nymph.startTransaction('test-unique-a');
|
|
1865
|
+
const TestModelA = tnymphA.getEntityClass(TestModel);
|
|
1866
|
+
const uniqueEntityA = await TestModelA.factory();
|
|
1867
|
+
uniqueEntityA.name = 'Entity Test ' + new Date().toLocaleString();
|
|
1868
|
+
uniqueEntityA.uniques = ['test a'];
|
|
1869
|
+
|
|
1870
|
+
// Saving entity...
|
|
1871
|
+
expect(await uniqueEntityA.$save()).toEqual(true);
|
|
1872
|
+
expect(uniqueEntityA.guid).not.toBeNull();
|
|
1873
|
+
|
|
1874
|
+
// Rolling back transaction...
|
|
1875
|
+
expect(await tnymphA.rollback('test-unique-a')).toEqual(true);
|
|
1876
|
+
|
|
1877
|
+
// Creating transaction entity...
|
|
1878
|
+
const tnymphB = await nymph.startTransaction('test-unique-b');
|
|
1879
|
+
const TestModelB = tnymphB.getEntityClass(TestModel);
|
|
1880
|
+
const uniqueEntityB = await TestModelB.factory();
|
|
1881
|
+
uniqueEntityB.name = 'Entity Test ' + new Date().toLocaleString();
|
|
1882
|
+
uniqueEntityB.uniques = ['test a'];
|
|
1883
|
+
|
|
1884
|
+
// Saving entity...
|
|
1885
|
+
expect(await uniqueEntityB.$save()).toEqual(true);
|
|
1886
|
+
expect(uniqueEntityB.guid).not.toBeNull();
|
|
1887
|
+
|
|
1888
|
+
expect(uniqueEntityA.guid).not.toEqual(uniqueEntityB.guid);
|
|
1889
|
+
|
|
1890
|
+
expect(await tnymphB.commit('test-unique-b')).toEqual(true);
|
|
1891
|
+
|
|
1892
|
+
const uniqueEntityACheck = await nymph.getEntity(
|
|
1893
|
+
{ class: TestModel },
|
|
1894
|
+
{ type: '&', guid: uniqueEntityA.guid ?? '' },
|
|
1895
|
+
);
|
|
1896
|
+
expect(uniqueEntityACheck).toBeNull();
|
|
1897
|
+
|
|
1898
|
+
const uniqueEntityBCheck = await nymph.getEntity(
|
|
1899
|
+
{ class: TestModel },
|
|
1900
|
+
{ type: '&', guid: uniqueEntityB.guid ?? '' },
|
|
1901
|
+
);
|
|
1902
|
+
expect(uniqueEntityBCheck).not.toBeNull();
|
|
1903
|
+
|
|
1904
|
+
if (uniqueEntityBCheck != null) {
|
|
1905
|
+
expect(await uniqueEntityBCheck.$delete()).toEqual(true);
|
|
1906
|
+
}
|
|
1907
|
+
});
|
|
1807
1908
|
}
|
|
1808
1909
|
|
|
1809
1910
|
export function UIDTest(
|
package/src/errors/index.ts
CHANGED
package/src/testArtifacts.ts
CHANGED
|
@@ -17,8 +17,10 @@ export type TestModelData = {
|
|
|
17
17
|
[k: string]: TestModel & TestModelData;
|
|
18
18
|
};
|
|
19
19
|
parent?: TestModel & TestModelData;
|
|
20
|
-
// For
|
|
20
|
+
// For import/export tests.
|
|
21
21
|
index?: string;
|
|
22
|
+
// For uniqueness tests.
|
|
23
|
+
uniques?: string[];
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -34,19 +36,16 @@ export class TestModel extends Entity<TestModelData> {
|
|
|
34
36
|
protected $protectedTags = ['test', 'notag'];
|
|
35
37
|
protected $allowlistTags? = ['newtag'];
|
|
36
38
|
|
|
37
|
-
static async factory(guid?: string): Promise<TestModel & TestModelData> {
|
|
38
|
-
return (await super.factory(guid)) as TestModel & TestModelData;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static factorySync(): TestModel & TestModelData {
|
|
42
|
-
return super.factorySync() as TestModel & TestModelData;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
39
|
constructor() {
|
|
46
40
|
super();
|
|
47
41
|
|
|
48
42
|
this.$addTag('test');
|
|
49
43
|
this.$data.boolean = true;
|
|
44
|
+
this.$data.uniques = [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public async $getUniques() {
|
|
48
|
+
return this.$data.uniques ?? [];
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
public $useProtectedData() {
|
|
@@ -75,14 +74,6 @@ export class TestModel extends Entity<TestModelData> {
|
|
|
75
74
|
export class TestBModel extends TestModel {
|
|
76
75
|
static ETYPE = 'test_b_model';
|
|
77
76
|
static class = 'TestBModel';
|
|
78
|
-
|
|
79
|
-
static async factory(guid?: string): Promise<TestBModel & TestModelData> {
|
|
80
|
-
return (await super.factory(guid)) as TestBModel & TestModelData;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
static factorySync(): TestBModel & TestModelData {
|
|
84
|
-
return super.factorySync() as TestBModel & TestModelData;
|
|
85
|
-
}
|
|
86
77
|
}
|
|
87
78
|
|
|
88
79
|
export type TestEmptyModelData = {};
|
|
@@ -93,14 +84,4 @@ export type TestEmptyModelData = {};
|
|
|
93
84
|
export class TestEmptyModel extends Entity<TestEmptyModelData> {
|
|
94
85
|
static ETYPE = 'test_empty_model';
|
|
95
86
|
static class = 'TestEmptyModel';
|
|
96
|
-
|
|
97
|
-
static async factory(
|
|
98
|
-
guid?: string,
|
|
99
|
-
): Promise<TestEmptyModel & TestEmptyModelData> {
|
|
100
|
-
return (await super.factory(guid)) as TestEmptyModel & TestEmptyModelData;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
static factorySync(): TestEmptyModel & TestEmptyModelData {
|
|
104
|
-
return super.factorySync() as TestEmptyModel & TestEmptyModelData;
|
|
105
|
-
}
|
|
106
87
|
}
|
package/src/testMocks.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { guid } from '@nymphjs/guid';
|
|
2
2
|
|
|
3
3
|
import type Nymph from './Nymph';
|
|
4
|
-
import {
|
|
4
|
+
import type { EntityInstanceType } from './Entity';
|
|
5
|
+
import type {
|
|
6
|
+
EntityConstructor,
|
|
7
|
+
EntityInterface,
|
|
8
|
+
EntityJson,
|
|
9
|
+
} from './Entity.types';
|
|
5
10
|
import { Options, Selector } from './Nymph.types';
|
|
6
11
|
import { ClassNotAvailableError } from './errors';
|
|
7
12
|
import Entity from './Entity';
|
|
@@ -22,7 +27,7 @@ export class MockNymphDriver {
|
|
|
22
27
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
23
28
|
options: Options<T>,
|
|
24
29
|
...selectors: Selector[]
|
|
25
|
-
): Promise<
|
|
30
|
+
): Promise<EntityInstanceType<T> | null>;
|
|
26
31
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
27
32
|
options: Options<T> & { return: 'guid' },
|
|
28
33
|
guid: string,
|
|
@@ -30,19 +35,19 @@ export class MockNymphDriver {
|
|
|
30
35
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
31
36
|
options: Options<T>,
|
|
32
37
|
guid: string,
|
|
33
|
-
): Promise<
|
|
38
|
+
): Promise<EntityInstanceType<T> | null>;
|
|
34
39
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
35
40
|
options: Options<T> = {},
|
|
36
41
|
...selectors: Selector[] | string[]
|
|
37
|
-
): Promise<
|
|
42
|
+
): Promise<EntityInstanceType<T> | string | null> {
|
|
38
43
|
const guid =
|
|
39
44
|
typeof selectors[0] === 'string' ? selectors[0] : selectors[0].guid;
|
|
40
45
|
if (!options || !guid || typeof guid !== 'string' || !(guid in entities)) {
|
|
41
46
|
return null;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
const entity
|
|
45
|
-
(options.class
|
|
49
|
+
const entity = (
|
|
50
|
+
(options.class ?? this.nymph.getEntityClass('Entity')) as T
|
|
46
51
|
).factorySync();
|
|
47
52
|
const entityJson = entities[guid];
|
|
48
53
|
entity.guid = guid;
|
|
@@ -51,7 +56,7 @@ export class MockNymphDriver {
|
|
|
51
56
|
entity.tags = entityJson.tags;
|
|
52
57
|
entity.$putData(entityJson.data, {});
|
|
53
58
|
|
|
54
|
-
return entity as
|
|
59
|
+
return entity as EntityInstanceType<T>;
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
|
|
@@ -68,21 +73,20 @@ export class MockNymph {
|
|
|
68
73
|
this.Entity = this.addEntityClass(Entity);
|
|
69
74
|
}
|
|
70
75
|
|
|
71
|
-
public addEntityClass<T extends EntityConstructor>(
|
|
76
|
+
public addEntityClass<T extends EntityConstructor>(EntityClass: T): T {
|
|
72
77
|
const nymph = this;
|
|
73
|
-
class
|
|
78
|
+
this.entityClasses[EntityClass.class] = class extends EntityClass {
|
|
74
79
|
static nymph: Nymph = nymph as unknown as Nymph;
|
|
75
80
|
|
|
76
81
|
constructor(...args: any[]) {
|
|
77
82
|
super(...args);
|
|
78
83
|
}
|
|
79
|
-
}
|
|
80
|
-
this.entityClasses[
|
|
81
|
-
return NymphEntity;
|
|
84
|
+
};
|
|
85
|
+
return this.entityClasses[EntityClass.class] as T;
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
public getEntityClass<T extends EntityConstructor>(className: T): T;
|
|
85
88
|
public getEntityClass(className: string): EntityConstructor;
|
|
89
|
+
public getEntityClass<T extends EntityConstructor>(className: T): T;
|
|
86
90
|
public getEntityClass<T extends EntityConstructor = EntityConstructor>(
|
|
87
91
|
className: T | string,
|
|
88
92
|
): T | EntityConstructor {
|
|
@@ -95,7 +99,18 @@ export class MockNymph {
|
|
|
95
99
|
if (key in this.entityClasses) {
|
|
96
100
|
return this.entityClasses[key];
|
|
97
101
|
}
|
|
98
|
-
throw new ClassNotAvailableError(
|
|
102
|
+
throw new ClassNotAvailableError(`Tried to use missing class "${key}".`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getEntityClassByEtype(etype: string): EntityConstructor {
|
|
106
|
+
for (const EntityClass of Object.values(this.entityClasses)) {
|
|
107
|
+
if (EntityClass.ETYPE === etype) {
|
|
108
|
+
return EntityClass;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
throw new ClassNotAvailableError(
|
|
112
|
+
`Tried to use missing class by etype "${etype}".`,
|
|
113
|
+
);
|
|
99
114
|
}
|
|
100
115
|
|
|
101
116
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
@@ -105,7 +120,7 @@ export class MockNymph {
|
|
|
105
120
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
106
121
|
options: Options<T>,
|
|
107
122
|
...selectors: Selector[]
|
|
108
|
-
): Promise<
|
|
123
|
+
): Promise<EntityInstanceType<T> | null>;
|
|
109
124
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
110
125
|
options: Options<T> & { return: 'guid' },
|
|
111
126
|
guid: string,
|
|
@@ -113,11 +128,11 @@ export class MockNymph {
|
|
|
113
128
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
114
129
|
options: Options<T>,
|
|
115
130
|
guid: string,
|
|
116
|
-
): Promise<
|
|
131
|
+
): Promise<EntityInstanceType<T> | null>;
|
|
117
132
|
public async getEntity<T extends EntityConstructor = EntityConstructor>(
|
|
118
133
|
options: Options<T> = {},
|
|
119
134
|
...selectors: Selector[] | string[]
|
|
120
|
-
): Promise<
|
|
135
|
+
): Promise<EntityInstanceType<T> | string | null> {
|
|
121
136
|
// @ts-ignore: The selector type is correct here.
|
|
122
137
|
return await this.driver.getEntity(options, ...selectors);
|
|
123
138
|
}
|