@expo/entity-database-adapter-knex 0.62.0 → 0.64.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/README.md +1 -1
- package/build/src/AuthorizationResultBasedKnexEntityLoader.d.ts +15 -5
- package/build/src/AuthorizationResultBasedKnexEntityLoader.js +24 -7
- package/build/src/BasePostgresEntityDatabaseAdapter.d.ts +19 -11
- package/build/src/BasePostgresEntityDatabaseAdapter.js +37 -13
- package/build/src/BaseSQLQueryBuilder.d.ts +2 -2
- package/build/src/BaseSQLQueryBuilder.js +2 -2
- package/build/src/EnforcingKnexEntityLoader.d.ts +14 -28
- package/build/src/EnforcingKnexEntityLoader.js +17 -30
- package/build/src/PostgresEntityDatabaseAdapter.d.ts +7 -2
- package/build/src/PostgresEntityDatabaseAdapter.js +25 -15
- package/build/src/SQLOperator.d.ts +15 -14
- package/build/src/SQLOperator.js +11 -8
- package/build/src/internal/EntityKnexDataManager.d.ts +2 -10
- package/build/src/internal/EntityKnexDataManager.js +12 -22
- package/package.json +16 -16
- package/src/AuthorizationResultBasedKnexEntityLoader.ts +29 -14
- package/src/BasePostgresEntityDatabaseAdapter.ts +60 -29
- package/src/BaseSQLQueryBuilder.ts +2 -2
- package/src/EnforcingKnexEntityLoader.ts +20 -38
- package/src/PostgresEntityDatabaseAdapter.ts +66 -29
- package/src/SQLOperator.ts +23 -22
- package/src/__integration-tests__/PostgresEntityIntegration-test.ts +140 -116
- package/src/__tests__/AuthorizationResultBasedKnexEntityLoader-test.ts +0 -75
- package/src/__tests__/BasePostgresEntityDatabaseAdapter-test.ts +21 -28
- package/src/__tests__/EnforcingKnexEntityLoader-test.ts +0 -52
- package/src/__tests__/SQLOperator-test.ts +2 -29
- package/src/__tests__/fixtures/StubPostgresDatabaseAdapter.ts +26 -12
- package/src/internal/EntityKnexDataManager.ts +28 -31
- package/src/internal/__tests__/EntityKnexDataManager-test.ts +1 -57
package/src/SQLOperator.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from 'assert';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Supported SQL value types that can be safely parameterized.
|
|
@@ -12,7 +13,6 @@ export type SupportedSQLValue =
|
|
|
12
13
|
| Date
|
|
13
14
|
| Buffer
|
|
14
15
|
| bigint
|
|
15
|
-
| undefined // Will be treated as NULL
|
|
16
16
|
| readonly SupportedSQLValue[] // For IN clauses and array types
|
|
17
17
|
| Readonly<{ [key: string]: unknown }>; // For JSON/JSONB columns
|
|
18
18
|
|
|
@@ -41,7 +41,7 @@ export class SQLFragment<TFields extends Record<string, any>> {
|
|
|
41
41
|
*/
|
|
42
42
|
getKnexBindings(
|
|
43
43
|
getColumnForField: (fieldName: keyof TFields) => string,
|
|
44
|
-
): readonly
|
|
44
|
+
): readonly Knex.RawBinding[] {
|
|
45
45
|
return this.bindings.map((b) => {
|
|
46
46
|
switch (b.type) {
|
|
47
47
|
case 'entityField':
|
|
@@ -49,7 +49,10 @@ export class SQLFragment<TFields extends Record<string, any>> {
|
|
|
49
49
|
case 'identifier':
|
|
50
50
|
return b.name;
|
|
51
51
|
case 'value':
|
|
52
|
-
|
|
52
|
+
// Needs a cast since bigint is supported by knex postgres dialect but not all dialects, and thus isn't included
|
|
53
|
+
// in the type. Because we only use the postgres dialect in this adapter, it's safe to allow it here.
|
|
54
|
+
// https://github.com/knex/knex/issues/5013#issuecomment-3368744254
|
|
55
|
+
return b.value as Knex.RawBinding;
|
|
53
56
|
}
|
|
54
57
|
});
|
|
55
58
|
}
|
|
@@ -133,8 +136,8 @@ export class SQLFragment<TFields extends Record<string, any>> {
|
|
|
133
136
|
* Handles all SupportedSQLValue types.
|
|
134
137
|
*/
|
|
135
138
|
private static formatDebugValue(value: SupportedSQLValue): string {
|
|
136
|
-
// Handle null
|
|
137
|
-
if (value === null
|
|
139
|
+
// Handle null
|
|
140
|
+
if (value === null) {
|
|
138
141
|
return 'NULL';
|
|
139
142
|
}
|
|
140
143
|
|
|
@@ -303,7 +306,7 @@ export function sql<TFields extends Record<string, any>>(
|
|
|
303
306
|
strings.forEach((string, i) => {
|
|
304
307
|
sqlString += string;
|
|
305
308
|
if (i < values.length) {
|
|
306
|
-
const value = values[i]
|
|
309
|
+
const value = values[i]!;
|
|
307
310
|
|
|
308
311
|
if (value instanceof SQLFragment) {
|
|
309
312
|
// Handle nested SQL fragments
|
|
@@ -344,7 +347,7 @@ type PickSupportedSQLValueKeys<T> = {
|
|
|
344
347
|
}[keyof T];
|
|
345
348
|
|
|
346
349
|
type PickStringValueKeys<T> = {
|
|
347
|
-
[K in keyof T]: T[K] extends string | null
|
|
350
|
+
[K in keyof T]: T[K] extends string | null ? K : never;
|
|
348
351
|
}[keyof T];
|
|
349
352
|
|
|
350
353
|
type JsonSerializable =
|
|
@@ -368,13 +371,13 @@ export class SQLChainableFragment<
|
|
|
368
371
|
> extends SQLFragment<TFields> {
|
|
369
372
|
/**
|
|
370
373
|
* Generates an equality condition (`= value`).
|
|
371
|
-
* Automatically converts `null
|
|
374
|
+
* Automatically converts `null` to `IS NULL`.
|
|
372
375
|
*
|
|
373
376
|
* @param value - The value to compare against
|
|
374
377
|
* @returns A {@link SQLFragment} representing the equality condition
|
|
375
378
|
*/
|
|
376
|
-
eq(value: TValue | null
|
|
377
|
-
if (value === null
|
|
379
|
+
eq(value: TValue | null): SQLFragment<TFields> {
|
|
380
|
+
if (value === null) {
|
|
378
381
|
return this.isNull();
|
|
379
382
|
}
|
|
380
383
|
return sql`${this} = ${value}`;
|
|
@@ -382,13 +385,13 @@ export class SQLChainableFragment<
|
|
|
382
385
|
|
|
383
386
|
/**
|
|
384
387
|
* Generates an inequality condition (`!= value`).
|
|
385
|
-
* Automatically converts `null
|
|
388
|
+
* Automatically converts `null` to `IS NOT NULL`.
|
|
386
389
|
*
|
|
387
390
|
* @param value - The value to compare against
|
|
388
391
|
* @returns A {@link SQLFragment} representing the inequality condition
|
|
389
392
|
*/
|
|
390
|
-
neq(value: TValue | null
|
|
391
|
-
if (value === null
|
|
393
|
+
neq(value: TValue | null): SQLFragment<TFields> {
|
|
394
|
+
if (value === null) {
|
|
392
395
|
return this.isNotNull();
|
|
393
396
|
}
|
|
394
397
|
return sql`${this} != ${value}`;
|
|
@@ -635,9 +638,7 @@ type ExtractFragmentFields<T> = T extends SQLFragment<infer F> ? F : never;
|
|
|
635
638
|
// Conditional value types for expression overloads.
|
|
636
639
|
// Uses SQLChainableFragment<any, ...> so that TExpr alone drives inference (single type param).
|
|
637
640
|
type FragmentValueNullable<TFragment> =
|
|
638
|
-
TFragment extends SQLChainableFragment<any, infer TValue>
|
|
639
|
-
? TValue | null | undefined
|
|
640
|
-
: SupportedSQLValue;
|
|
641
|
+
TFragment extends SQLChainableFragment<any, infer TValue> ? TValue | null : SupportedSQLValue;
|
|
641
642
|
|
|
642
643
|
type FragmentValue<TFragment> =
|
|
643
644
|
TFragment extends SQLChainableFragment<any, infer TValue> ? TValue : SupportedSQLValue;
|
|
@@ -950,7 +951,7 @@ function isNotNullHelper<TFields extends Record<string, any>>(
|
|
|
950
951
|
|
|
951
952
|
/**
|
|
952
953
|
* Generates an equality condition (`= value`) from a fragment.
|
|
953
|
-
* Automatically converts `null
|
|
954
|
+
* Automatically converts `null` to `IS NULL`.
|
|
954
955
|
*
|
|
955
956
|
* @param fragment - A SQLFragment or SQLChainableFragment to compare
|
|
956
957
|
* @param value - The value to compare against
|
|
@@ -961,7 +962,7 @@ function eqHelper<TFragment extends SQLFragment<any>>(
|
|
|
961
962
|
): SQLFragment<ExtractFragmentFields<TFragment>>;
|
|
962
963
|
/**
|
|
963
964
|
* Generates an equality condition (`= value`) from a field name.
|
|
964
|
-
* Automatically converts `null
|
|
965
|
+
* Automatically converts `null` to `IS NULL`.
|
|
965
966
|
*
|
|
966
967
|
* @param fieldName - The entity field name to compare
|
|
967
968
|
* @param value - The value to compare against
|
|
@@ -979,7 +980,7 @@ function eqHelper<TFields extends Record<string, any>>(
|
|
|
979
980
|
|
|
980
981
|
/**
|
|
981
982
|
* Generates an inequality condition (`!= value`) from a fragment.
|
|
982
|
-
* Automatically converts `null
|
|
983
|
+
* Automatically converts `null` to `IS NOT NULL`.
|
|
983
984
|
*
|
|
984
985
|
* @param fragment - A SQLFragment or SQLChainableFragment to compare
|
|
985
986
|
* @param value - The value to compare against
|
|
@@ -990,7 +991,7 @@ function neqHelper<TFragment extends SQLFragment<any>>(
|
|
|
990
991
|
): SQLFragment<ExtractFragmentFields<TFragment>>;
|
|
991
992
|
/**
|
|
992
993
|
* Generates an inequality condition (`!= value`) from a field name.
|
|
993
|
-
* Automatically converts `null
|
|
994
|
+
* Automatically converts `null` to `IS NOT NULL`.
|
|
994
995
|
*
|
|
995
996
|
* @param fieldName - The entity field name to compare
|
|
996
997
|
* @param value - The value to compare against
|
|
@@ -1521,12 +1522,12 @@ export const SQLExpression = {
|
|
|
1521
1522
|
isNotNull: isNotNullHelper,
|
|
1522
1523
|
|
|
1523
1524
|
/**
|
|
1524
|
-
* Equality operator. Automatically converts null
|
|
1525
|
+
* Equality operator. Automatically converts null to IS NULL.
|
|
1525
1526
|
*/
|
|
1526
1527
|
eq: eqHelper,
|
|
1527
1528
|
|
|
1528
1529
|
/**
|
|
1529
|
-
* Inequality operator. Automatically converts null
|
|
1530
|
+
* Inequality operator. Automatically converts null to IS NOT NULL.
|
|
1530
1531
|
*/
|
|
1531
1532
|
neq: neqHelper,
|
|
1532
1533
|
|
|
@@ -959,6 +959,103 @@ describe('postgres entity integration', () => {
|
|
|
959
959
|
});
|
|
960
960
|
});
|
|
961
961
|
|
|
962
|
+
describe('counting with countBySQLAsync', () => {
|
|
963
|
+
it('counts entities matching a SQL fragment', async () => {
|
|
964
|
+
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
965
|
+
|
|
966
|
+
await enforceAsyncResult(
|
|
967
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
968
|
+
.setField('name', 'Alice')
|
|
969
|
+
.setField('hasACat', true)
|
|
970
|
+
.setField('hasADog', false)
|
|
971
|
+
.createAsync(),
|
|
972
|
+
);
|
|
973
|
+
|
|
974
|
+
await enforceAsyncResult(
|
|
975
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
976
|
+
.setField('name', 'Bob')
|
|
977
|
+
.setField('hasACat', false)
|
|
978
|
+
.setField('hasADog', true)
|
|
979
|
+
.createAsync(),
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
await enforceAsyncResult(
|
|
983
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
984
|
+
.setField('name', 'Charlie')
|
|
985
|
+
.setField('hasACat', true)
|
|
986
|
+
.setField('hasADog', true)
|
|
987
|
+
.createAsync(),
|
|
988
|
+
);
|
|
989
|
+
|
|
990
|
+
const catOwnerCount = await PostgresTestEntity.knexLoader(vc1).countBySQLAsync(
|
|
991
|
+
sql`has_a_cat = ${true}`,
|
|
992
|
+
);
|
|
993
|
+
expect(catOwnerCount).toBe(2);
|
|
994
|
+
|
|
995
|
+
const dogOwnerCount = await PostgresTestEntity.knexLoader(vc1).countBySQLAsync(
|
|
996
|
+
sql`has_a_dog = ${true}`,
|
|
997
|
+
);
|
|
998
|
+
expect(dogOwnerCount).toBe(2);
|
|
999
|
+
|
|
1000
|
+
const allCount = await PostgresTestEntity.knexLoader(vc1).countBySQLAsync(sql`TRUE`);
|
|
1001
|
+
expect(allCount).toBe(3);
|
|
1002
|
+
|
|
1003
|
+
const noneCount = await PostgresTestEntity.knexLoader(vc1).countBySQLAsync(sql`FALSE`);
|
|
1004
|
+
expect(noneCount).toBe(0);
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
describe('counting with countByFieldEqualityConjunctionAsync', () => {
|
|
1009
|
+
it('counts entities matching field equality conditions', async () => {
|
|
1010
|
+
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
1011
|
+
|
|
1012
|
+
await enforceAsyncResult(
|
|
1013
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1014
|
+
.setField('name', 'hello')
|
|
1015
|
+
.setField('hasACat', false)
|
|
1016
|
+
.setField('hasADog', true)
|
|
1017
|
+
.createAsync(),
|
|
1018
|
+
);
|
|
1019
|
+
|
|
1020
|
+
await enforceAsyncResult(
|
|
1021
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1022
|
+
.setField('name', 'world')
|
|
1023
|
+
.setField('hasACat', false)
|
|
1024
|
+
.setField('hasADog', true)
|
|
1025
|
+
.createAsync(),
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
await enforceAsyncResult(
|
|
1029
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1030
|
+
.setField('name', 'wat')
|
|
1031
|
+
.setField('hasACat', false)
|
|
1032
|
+
.setField('hasADog', false)
|
|
1033
|
+
.createAsync(),
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
const count1 = await PostgresTestEntity.knexLoader(vc1).countByFieldEqualityConjunctionAsync([
|
|
1037
|
+
{ fieldName: 'hasACat', fieldValue: false },
|
|
1038
|
+
{ fieldName: 'hasADog', fieldValue: true },
|
|
1039
|
+
]);
|
|
1040
|
+
expect(count1).toBe(2);
|
|
1041
|
+
|
|
1042
|
+
const count2 = await PostgresTestEntity.knexLoader(vc1).countByFieldEqualityConjunctionAsync([
|
|
1043
|
+
{ fieldName: 'hasADog', fieldValues: [true, false] },
|
|
1044
|
+
]);
|
|
1045
|
+
expect(count2).toBe(3);
|
|
1046
|
+
|
|
1047
|
+
const count3 = await PostgresTestEntity.knexLoader(vc1).countByFieldEqualityConjunctionAsync([
|
|
1048
|
+
{ fieldName: 'name', fieldValue: 'hello' },
|
|
1049
|
+
]);
|
|
1050
|
+
expect(count3).toBe(1);
|
|
1051
|
+
|
|
1052
|
+
const count4 = await PostgresTestEntity.knexLoader(vc1).countByFieldEqualityConjunctionAsync(
|
|
1053
|
+
[],
|
|
1054
|
+
);
|
|
1055
|
+
expect(count4).toBe(3);
|
|
1056
|
+
});
|
|
1057
|
+
});
|
|
1058
|
+
|
|
962
1059
|
describe('conjunction field equality loading', () => {
|
|
963
1060
|
it('supports single fieldValue and multiple fieldValues', async () => {
|
|
964
1061
|
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
@@ -1085,6 +1182,49 @@ describe('postgres entity integration', () => {
|
|
|
1085
1182
|
expect(results.map((e) => e.getField('name'))).toEqual(['alpha', 'gamma', 'beta']);
|
|
1086
1183
|
});
|
|
1087
1184
|
|
|
1185
|
+
it('supports fieldFragment orderBy with entityField', async () => {
|
|
1186
|
+
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
1187
|
+
|
|
1188
|
+
await enforceAsyncResult(
|
|
1189
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1190
|
+
.setField('name', 'alpha')
|
|
1191
|
+
.setField('hasACat', true)
|
|
1192
|
+
.createAsync(),
|
|
1193
|
+
);
|
|
1194
|
+
|
|
1195
|
+
await enforceAsyncResult(
|
|
1196
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1197
|
+
.setField('name', 'beta')
|
|
1198
|
+
.setField('hasACat', false)
|
|
1199
|
+
.createAsync(),
|
|
1200
|
+
);
|
|
1201
|
+
|
|
1202
|
+
await enforceAsyncResult(
|
|
1203
|
+
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1204
|
+
.setField('name', 'gamma')
|
|
1205
|
+
.setField('hasACat', true)
|
|
1206
|
+
.createAsync(),
|
|
1207
|
+
);
|
|
1208
|
+
|
|
1209
|
+
// Order by entityField references, which translate entity field names to DB column names
|
|
1210
|
+
const results = await PostgresTestEntity.knexLoader(
|
|
1211
|
+
vc1,
|
|
1212
|
+
).loadManyByFieldEqualityConjunctionAsync([], {
|
|
1213
|
+
orderBy: [
|
|
1214
|
+
{
|
|
1215
|
+
fieldFragment: sql`${entityField('hasACat')}`,
|
|
1216
|
+
order: OrderByOrdering.DESCENDING,
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
fieldFragment: sql`${entityField('name')}`,
|
|
1220
|
+
order: OrderByOrdering.ASCENDING,
|
|
1221
|
+
},
|
|
1222
|
+
],
|
|
1223
|
+
});
|
|
1224
|
+
expect(results).toHaveLength(3);
|
|
1225
|
+
expect(results.map((e) => e.getField('name'))).toEqual(['alpha', 'gamma', 'beta']);
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1088
1228
|
it('rejects fieldFragment containing trailing ASC or DESC', async () => {
|
|
1089
1229
|
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
1090
1230
|
|
|
@@ -1165,122 +1305,6 @@ describe('postgres entity integration', () => {
|
|
|
1165
1305
|
});
|
|
1166
1306
|
});
|
|
1167
1307
|
|
|
1168
|
-
describe('raw where clause loading', () => {
|
|
1169
|
-
it('loads by raw where clause', async () => {
|
|
1170
|
-
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
1171
|
-
await enforceAsyncResult(
|
|
1172
|
-
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1173
|
-
.setField('name', 'hello')
|
|
1174
|
-
.setField('hasACat', false)
|
|
1175
|
-
.setField('hasADog', true)
|
|
1176
|
-
.createAsync(),
|
|
1177
|
-
);
|
|
1178
|
-
|
|
1179
|
-
const results = await PostgresTestEntity.knexLoader(vc1).loadManyByRawWhereClauseAsync(
|
|
1180
|
-
'name = ?',
|
|
1181
|
-
['hello'],
|
|
1182
|
-
);
|
|
1183
|
-
|
|
1184
|
-
expect(results).toHaveLength(1);
|
|
1185
|
-
});
|
|
1186
|
-
|
|
1187
|
-
it('throws with invalid where clause', async () => {
|
|
1188
|
-
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
1189
|
-
await enforceAsyncResult(
|
|
1190
|
-
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1191
|
-
.setField('name', 'hello')
|
|
1192
|
-
.setField('hasACat', false)
|
|
1193
|
-
.setField('hasADog', true)
|
|
1194
|
-
.createAsync(),
|
|
1195
|
-
);
|
|
1196
|
-
|
|
1197
|
-
await expect(
|
|
1198
|
-
PostgresTestEntity.knexLoader(vc1).loadManyByRawWhereClauseAsync('invalid_column = ?', [
|
|
1199
|
-
'hello',
|
|
1200
|
-
]),
|
|
1201
|
-
).rejects.toThrow();
|
|
1202
|
-
});
|
|
1203
|
-
|
|
1204
|
-
it('supports query modifiers', async () => {
|
|
1205
|
-
const vc1 = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
|
1206
|
-
|
|
1207
|
-
await enforceAsyncResult(
|
|
1208
|
-
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1209
|
-
.setField('name', 'a')
|
|
1210
|
-
.setField('hasADog', true)
|
|
1211
|
-
.createAsync(),
|
|
1212
|
-
);
|
|
1213
|
-
|
|
1214
|
-
await enforceAsyncResult(
|
|
1215
|
-
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1216
|
-
.setField('name', 'b')
|
|
1217
|
-
.setField('hasADog', true)
|
|
1218
|
-
.createAsync(),
|
|
1219
|
-
);
|
|
1220
|
-
|
|
1221
|
-
await enforceAsyncResult(
|
|
1222
|
-
PostgresTestEntity.creatorWithAuthorizationResults(vc1)
|
|
1223
|
-
.setField('name', 'c')
|
|
1224
|
-
.setField('hasADog', true)
|
|
1225
|
-
.createAsync(),
|
|
1226
|
-
);
|
|
1227
|
-
|
|
1228
|
-
const results = await PostgresTestEntity.knexLoader(vc1).loadManyByRawWhereClauseAsync(
|
|
1229
|
-
'has_a_dog = ?',
|
|
1230
|
-
[true],
|
|
1231
|
-
{
|
|
1232
|
-
limit: 2,
|
|
1233
|
-
offset: 1,
|
|
1234
|
-
orderBy: [
|
|
1235
|
-
{
|
|
1236
|
-
fieldName: 'name',
|
|
1237
|
-
order: OrderByOrdering.ASCENDING,
|
|
1238
|
-
},
|
|
1239
|
-
],
|
|
1240
|
-
},
|
|
1241
|
-
);
|
|
1242
|
-
|
|
1243
|
-
expect(results).toHaveLength(2);
|
|
1244
|
-
expect(results.map((e) => e.getField('name'))).toEqual(['b', 'c']);
|
|
1245
|
-
|
|
1246
|
-
const resultsMultipleOrderBy = await PostgresTestEntity.knexLoader(
|
|
1247
|
-
vc1,
|
|
1248
|
-
).loadManyByRawWhereClauseAsync('has_a_dog = ?', [true], {
|
|
1249
|
-
orderBy: [
|
|
1250
|
-
{
|
|
1251
|
-
fieldName: 'hasADog',
|
|
1252
|
-
order: OrderByOrdering.ASCENDING,
|
|
1253
|
-
},
|
|
1254
|
-
{
|
|
1255
|
-
fieldName: 'name',
|
|
1256
|
-
order: OrderByOrdering.DESCENDING,
|
|
1257
|
-
},
|
|
1258
|
-
],
|
|
1259
|
-
});
|
|
1260
|
-
|
|
1261
|
-
expect(resultsMultipleOrderBy).toHaveLength(3);
|
|
1262
|
-
expect(resultsMultipleOrderBy.map((e) => e.getField('name'))).toEqual(['c', 'b', 'a']);
|
|
1263
|
-
|
|
1264
|
-
const resultsOrderByRaw = await PostgresTestEntity.knexLoader(
|
|
1265
|
-
vc1,
|
|
1266
|
-
).loadManyByRawWhereClauseAsync('has_a_dog = ?', [true], {
|
|
1267
|
-
orderBy: [
|
|
1268
|
-
{
|
|
1269
|
-
fieldFragment: sql`${entityField('hasADog')}`,
|
|
1270
|
-
order: OrderByOrdering.ASCENDING,
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
fieldFragment: sql`${entityField('name')}`,
|
|
1274
|
-
order: OrderByOrdering.DESCENDING,
|
|
1275
|
-
},
|
|
1276
|
-
],
|
|
1277
|
-
});
|
|
1278
|
-
|
|
1279
|
-
expect(resultsOrderByRaw).toHaveLength(3);
|
|
1280
|
-
expect(resultsOrderByRaw.map((e) => e.getField('name'))).toEqual(['c', 'b', 'a']);
|
|
1281
|
-
});
|
|
1282
|
-
});
|
|
1283
|
-
|
|
1284
1308
|
describe('trigger transaction behavior', () => {
|
|
1285
1309
|
describe('create', () => {
|
|
1286
1310
|
it('rolls back transaction when trigger throws except afterCommit', async () => {
|
|
@@ -205,81 +205,6 @@ describe(AuthorizationResultBasedKnexEntityLoader, () => {
|
|
|
205
205
|
).once();
|
|
206
206
|
});
|
|
207
207
|
|
|
208
|
-
it('loads entities with loadManyByRawWhereClauseAsync', async () => {
|
|
209
|
-
const privacyPolicy = new TestEntityPrivacyPolicy();
|
|
210
|
-
const spiedPrivacyPolicy = spy(privacyPolicy);
|
|
211
|
-
const viewerContext = instance(mock(ViewerContext));
|
|
212
|
-
const privacyPolicyEvaluationContext =
|
|
213
|
-
instance(
|
|
214
|
-
mock<
|
|
215
|
-
EntityPrivacyPolicyEvaluationContext<
|
|
216
|
-
TestFields,
|
|
217
|
-
'customIdField',
|
|
218
|
-
ViewerContext,
|
|
219
|
-
TestEntity
|
|
220
|
-
>
|
|
221
|
-
>(),
|
|
222
|
-
);
|
|
223
|
-
const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
|
|
224
|
-
const queryContext = instance(mock<EntityQueryContext>());
|
|
225
|
-
|
|
226
|
-
const knexDataManagerMock =
|
|
227
|
-
mock<EntityKnexDataManager<TestFields, 'customIdField'>>(EntityKnexDataManager);
|
|
228
|
-
when(
|
|
229
|
-
knexDataManagerMock.loadManyByRawWhereClauseAsync(
|
|
230
|
-
queryContext,
|
|
231
|
-
anything(),
|
|
232
|
-
anything(),
|
|
233
|
-
anything(),
|
|
234
|
-
),
|
|
235
|
-
).thenResolve([
|
|
236
|
-
{
|
|
237
|
-
customIdField: 'id',
|
|
238
|
-
stringField: 'huh',
|
|
239
|
-
intField: 4,
|
|
240
|
-
testIndexedField: '4',
|
|
241
|
-
dateField: new Date(),
|
|
242
|
-
nullableField: null,
|
|
243
|
-
},
|
|
244
|
-
]);
|
|
245
|
-
const knexDataManager = instance(knexDataManagerMock);
|
|
246
|
-
|
|
247
|
-
const constructionUtils = new EntityConstructionUtils(
|
|
248
|
-
viewerContext,
|
|
249
|
-
queryContext,
|
|
250
|
-
privacyPolicyEvaluationContext,
|
|
251
|
-
testEntityConfiguration,
|
|
252
|
-
TestEntity,
|
|
253
|
-
/* entitySelectedFields */ undefined,
|
|
254
|
-
privacyPolicy,
|
|
255
|
-
metricsAdapter,
|
|
256
|
-
);
|
|
257
|
-
const knexEntityLoader = new AuthorizationResultBasedKnexEntityLoader(
|
|
258
|
-
queryContext,
|
|
259
|
-
knexDataManager,
|
|
260
|
-
metricsAdapter,
|
|
261
|
-
constructionUtils,
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
const result = await knexEntityLoader.loadManyByRawWhereClauseAsync('id = ?', [1], {
|
|
265
|
-
orderBy: [{ fieldName: 'testIndexedField', order: OrderByOrdering.DESCENDING }],
|
|
266
|
-
});
|
|
267
|
-
expect(result).toHaveLength(1);
|
|
268
|
-
expect(result[0]).not.toBeNull();
|
|
269
|
-
expect(result[0]!.ok).toBe(true);
|
|
270
|
-
expect(result[0]!.enforceValue().getField('testIndexedField')).toEqual('4');
|
|
271
|
-
|
|
272
|
-
verify(
|
|
273
|
-
spiedPrivacyPolicy.authorizeReadAsync(
|
|
274
|
-
viewerContext,
|
|
275
|
-
queryContext,
|
|
276
|
-
privacyPolicyEvaluationContext,
|
|
277
|
-
anyOfClass(TestEntity),
|
|
278
|
-
anything(),
|
|
279
|
-
),
|
|
280
|
-
).once();
|
|
281
|
-
});
|
|
282
|
-
|
|
283
208
|
describe('loads entities with loadManyBySQL', () => {
|
|
284
209
|
it('returns entities with authorization results', async () => {
|
|
285
210
|
const privacyPolicy = new TestEntityPrivacyPolicy();
|
|
@@ -18,9 +18,8 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
|
|
|
18
18
|
private readonly fetchResults: object[];
|
|
19
19
|
private readonly fetchOneResult: object | null;
|
|
20
20
|
private readonly insertResults: object[];
|
|
21
|
-
private readonly updateResults:
|
|
21
|
+
private readonly updateResults: { updatedRowCount: number };
|
|
22
22
|
private readonly fetchEqualityConditionResults: object[];
|
|
23
|
-
private readonly fetchRawWhereResults: object[];
|
|
24
23
|
private readonly fetchSQLFragmentResults: object[];
|
|
25
24
|
private readonly deleteCount: number;
|
|
26
25
|
|
|
@@ -28,18 +27,16 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
|
|
|
28
27
|
fetchResults = [],
|
|
29
28
|
fetchOneResult = null,
|
|
30
29
|
insertResults = [],
|
|
31
|
-
updateResults =
|
|
30
|
+
updateResults = { updatedRowCount: 0 },
|
|
32
31
|
fetchEqualityConditionResults = [],
|
|
33
|
-
fetchRawWhereResults = [],
|
|
34
32
|
fetchSQLFragmentResults = [],
|
|
35
33
|
deleteCount = 0,
|
|
36
34
|
}: {
|
|
37
35
|
fetchResults?: object[];
|
|
38
36
|
fetchOneResult?: object | null;
|
|
39
37
|
insertResults?: object[];
|
|
40
|
-
updateResults?:
|
|
38
|
+
updateResults?: { updatedRowCount: number };
|
|
41
39
|
fetchEqualityConditionResults?: object[];
|
|
42
|
-
fetchRawWhereResults?: object[];
|
|
43
40
|
fetchSQLFragmentResults?: object[];
|
|
44
41
|
deleteCount?: number;
|
|
45
42
|
}) {
|
|
@@ -49,7 +46,6 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
|
|
|
49
46
|
this.insertResults = insertResults;
|
|
50
47
|
this.updateResults = updateResults;
|
|
51
48
|
this.fetchEqualityConditionResults = fetchEqualityConditionResults;
|
|
52
|
-
this.fetchRawWhereResults = fetchRawWhereResults;
|
|
53
49
|
this.fetchSQLFragmentResults = fetchSQLFragmentResults;
|
|
54
50
|
this.deleteCount = deleteCount;
|
|
55
51
|
}
|
|
@@ -76,15 +72,6 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
|
|
|
76
72
|
return this.fetchOneResult;
|
|
77
73
|
}
|
|
78
74
|
|
|
79
|
-
protected async fetchManyByRawWhereClauseInternalAsync(
|
|
80
|
-
_queryInterface: any,
|
|
81
|
-
_tableName: string,
|
|
82
|
-
_rawWhereClause: string,
|
|
83
|
-
_bindings: object | any[],
|
|
84
|
-
): Promise<object[]> {
|
|
85
|
-
return this.fetchRawWhereResults;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
75
|
protected async fetchManyBySQLFragmentInternalAsync(
|
|
89
76
|
_queryInterface: any,
|
|
90
77
|
_tableName: string,
|
|
@@ -102,6 +89,23 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
|
|
|
102
89
|
return this.fetchEqualityConditionResults;
|
|
103
90
|
}
|
|
104
91
|
|
|
92
|
+
protected async countByFieldEqualityConjunctionInternalAsync(
|
|
93
|
+
_queryInterface: any,
|
|
94
|
+
_tableName: string,
|
|
95
|
+
_tableFieldSingleValueEqualityOperands: TableFieldSingleValueEqualityCondition[],
|
|
96
|
+
_tableFieldMultiValueEqualityOperands: TableFieldMultiValueEqualityCondition[],
|
|
97
|
+
): Promise<number> {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected async countBySQLFragmentInternalAsync(
|
|
102
|
+
_queryInterface: any,
|
|
103
|
+
_tableName: string,
|
|
104
|
+
_sqlFragment: any,
|
|
105
|
+
): Promise<number> {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
105
109
|
protected async insertInternalAsync(
|
|
106
110
|
_queryInterface: any,
|
|
107
111
|
_tableName: string,
|
|
@@ -116,7 +120,7 @@ class TestEntityDatabaseAdapter extends BasePostgresEntityDatabaseAdapter<
|
|
|
116
120
|
_tableIdField: string,
|
|
117
121
|
_id: any,
|
|
118
122
|
_object: object,
|
|
119
|
-
): Promise<
|
|
123
|
+
): Promise<{ updatedRowCount: number }> {
|
|
120
124
|
return this.updateResults;
|
|
121
125
|
}
|
|
122
126
|
|
|
@@ -148,15 +152,4 @@ describe(BasePostgresEntityDatabaseAdapter, () => {
|
|
|
148
152
|
expect(results).toEqual([{ stringField: 'hello' }]);
|
|
149
153
|
});
|
|
150
154
|
});
|
|
151
|
-
|
|
152
|
-
describe('fetchManyWithRawWhereClause', () => {
|
|
153
|
-
it('transforms object', async () => {
|
|
154
|
-
const queryContext = instance(mock(EntityQueryContext));
|
|
155
|
-
const adapter = new TestEntityDatabaseAdapter({
|
|
156
|
-
fetchRawWhereResults: [{ string_field: 'hello' }],
|
|
157
|
-
});
|
|
158
|
-
const results = await adapter.fetchManyByRawWhereClauseAsync(queryContext, 'hello', [], {});
|
|
159
|
-
expect(results).toEqual([{ stringField: 'hello' }]);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
155
|
});
|
|
@@ -137,58 +137,6 @@ describe(EnforcingKnexEntityLoader, () => {
|
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
describe('loadManyByRawWhereClause', () => {
|
|
141
|
-
it('throws when result is unsuccessful', async () => {
|
|
142
|
-
const nonEnforcingKnexEntityLoaderMock = mock(
|
|
143
|
-
AuthorizationResultBasedKnexEntityLoader<any, any, any, any, any, any>,
|
|
144
|
-
);
|
|
145
|
-
const rejection = new Error();
|
|
146
|
-
when(
|
|
147
|
-
nonEnforcingKnexEntityLoaderMock.loadManyByRawWhereClauseAsync(
|
|
148
|
-
anything(),
|
|
149
|
-
anything(),
|
|
150
|
-
anything(),
|
|
151
|
-
),
|
|
152
|
-
).thenResolve([result(rejection)]);
|
|
153
|
-
const nonEnforcingKnexEntityLoader = instance(nonEnforcingKnexEntityLoaderMock);
|
|
154
|
-
const enforcingKnexEntityLoader = new EnforcingKnexEntityLoader(
|
|
155
|
-
nonEnforcingKnexEntityLoader,
|
|
156
|
-
instance(mock(EntityQueryContext)),
|
|
157
|
-
instance(mock(EntityKnexDataManager)),
|
|
158
|
-
instance(mock<IEntityMetricsAdapter>()),
|
|
159
|
-
instance(mock(EntityConstructionUtils)),
|
|
160
|
-
);
|
|
161
|
-
await expect(
|
|
162
|
-
enforcingKnexEntityLoader.loadManyByRawWhereClauseAsync(anything(), anything(), anything()),
|
|
163
|
-
).rejects.toThrow(rejection);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('returns value when result is successful', async () => {
|
|
167
|
-
const nonEnforcingKnexEntityLoaderMock = mock(
|
|
168
|
-
AuthorizationResultBasedKnexEntityLoader<any, any, any, any, any, any>,
|
|
169
|
-
);
|
|
170
|
-
const resolved = {};
|
|
171
|
-
when(
|
|
172
|
-
nonEnforcingKnexEntityLoaderMock.loadManyByRawWhereClauseAsync(
|
|
173
|
-
anything(),
|
|
174
|
-
anything(),
|
|
175
|
-
anything(),
|
|
176
|
-
),
|
|
177
|
-
).thenResolve([result(resolved)]);
|
|
178
|
-
const nonEnforcingKnexEntityLoader = instance(nonEnforcingKnexEntityLoaderMock);
|
|
179
|
-
const enforcingKnexEntityLoader = new EnforcingKnexEntityLoader(
|
|
180
|
-
nonEnforcingKnexEntityLoader,
|
|
181
|
-
instance(mock(EntityQueryContext)),
|
|
182
|
-
instance(mock(EntityKnexDataManager)),
|
|
183
|
-
instance(mock<IEntityMetricsAdapter>()),
|
|
184
|
-
instance(mock(EntityConstructionUtils)),
|
|
185
|
-
);
|
|
186
|
-
await expect(
|
|
187
|
-
enforcingKnexEntityLoader.loadManyByRawWhereClauseAsync(anything(), anything(), anything()),
|
|
188
|
-
).resolves.toEqual([resolved]);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
140
|
describe('loadManyBySQL', () => {
|
|
193
141
|
it('throws when result is unsuccessful', async () => {
|
|
194
142
|
const nonEnforcingKnexEntityLoaderMock = mock(
|