@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.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/build/src/AuthorizationResultBasedKnexEntityLoader.d.ts +15 -5
  3. package/build/src/AuthorizationResultBasedKnexEntityLoader.js +24 -7
  4. package/build/src/BasePostgresEntityDatabaseAdapter.d.ts +19 -11
  5. package/build/src/BasePostgresEntityDatabaseAdapter.js +37 -13
  6. package/build/src/BaseSQLQueryBuilder.d.ts +2 -2
  7. package/build/src/BaseSQLQueryBuilder.js +2 -2
  8. package/build/src/EnforcingKnexEntityLoader.d.ts +14 -28
  9. package/build/src/EnforcingKnexEntityLoader.js +17 -30
  10. package/build/src/PostgresEntityDatabaseAdapter.d.ts +7 -2
  11. package/build/src/PostgresEntityDatabaseAdapter.js +25 -15
  12. package/build/src/SQLOperator.d.ts +15 -14
  13. package/build/src/SQLOperator.js +11 -8
  14. package/build/src/internal/EntityKnexDataManager.d.ts +2 -10
  15. package/build/src/internal/EntityKnexDataManager.js +12 -22
  16. package/package.json +16 -16
  17. package/src/AuthorizationResultBasedKnexEntityLoader.ts +29 -14
  18. package/src/BasePostgresEntityDatabaseAdapter.ts +60 -29
  19. package/src/BaseSQLQueryBuilder.ts +2 -2
  20. package/src/EnforcingKnexEntityLoader.ts +20 -38
  21. package/src/PostgresEntityDatabaseAdapter.ts +66 -29
  22. package/src/SQLOperator.ts +23 -22
  23. package/src/__integration-tests__/PostgresEntityIntegration-test.ts +140 -116
  24. package/src/__tests__/AuthorizationResultBasedKnexEntityLoader-test.ts +0 -75
  25. package/src/__tests__/BasePostgresEntityDatabaseAdapter-test.ts +21 -28
  26. package/src/__tests__/EnforcingKnexEntityLoader-test.ts +0 -52
  27. package/src/__tests__/SQLOperator-test.ts +2 -29
  28. package/src/__tests__/fixtures/StubPostgresDatabaseAdapter.ts +26 -12
  29. package/src/internal/EntityKnexDataManager.ts +28 -31
  30. package/src/internal/__tests__/EntityKnexDataManager-test.ts +1 -57
@@ -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 SupportedSQLValue[] {
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
- return b.value;
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 and undefined
137
- if (value === null || value === undefined) {
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 | undefined ? K : never;
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`/`undefined` to `IS 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 | undefined): SQLFragment<TFields> {
377
- if (value === null || value === undefined) {
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`/`undefined` to `IS NOT 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 | undefined): SQLFragment<TFields> {
391
- if (value === null || value === undefined) {
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`/`undefined` to `IS 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`/`undefined` to `IS 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`/`undefined` to `IS NOT 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`/`undefined` to `IS NOT 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/undefined to IS 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/undefined to IS NOT 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: object[];
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?: object[];
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<object[]> {
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(