@expo/entity 0.55.0 → 0.57.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/build/src/AuthorizationResultBasedEntityAssociationLoader.d.ts +1 -1
- package/build/src/AuthorizationResultBasedEntityAssociationLoader.js.map +1 -1
- package/build/src/AuthorizationResultBasedEntityLoader.d.ts +18 -24
- package/build/src/AuthorizationResultBasedEntityLoader.js +37 -56
- package/build/src/AuthorizationResultBasedEntityLoader.js.map +1 -1
- package/build/src/AuthorizationResultBasedEntityMutator.js +26 -19
- package/build/src/AuthorizationResultBasedEntityMutator.js.map +1 -1
- package/build/src/EnforcingEntityCreator.d.ts +1 -1
- package/build/src/EnforcingEntityCreator.js +1 -1
- package/build/src/EnforcingEntityLoader.d.ts +1 -58
- package/build/src/EnforcingEntityLoader.js +0 -65
- package/build/src/EnforcingEntityLoader.js.map +1 -1
- package/build/src/Entity.d.ts +6 -0
- package/build/src/Entity.js +6 -0
- package/build/src/Entity.js.map +1 -1
- package/build/src/EntityCompanion.d.ts +2 -2
- package/build/src/EntityCompanion.js.map +1 -1
- package/build/src/EntityCompanionProvider.d.ts +1 -1
- package/build/src/EntityCompanionProvider.js +4 -4
- package/build/src/EntityConfiguration.d.ts +1 -1
- package/build/src/EntityConfiguration.js +1 -2
- package/build/src/EntityConfiguration.js.map +1 -1
- package/build/src/{EntityLoaderUtils.d.ts → EntityConstructionUtils.d.ts} +15 -29
- package/build/src/EntityConstructionUtils.js +118 -0
- package/build/src/EntityConstructionUtils.js.map +1 -0
- package/build/src/EntityDatabaseAdapter.d.ts +10 -108
- package/build/src/EntityDatabaseAdapter.js +14 -76
- package/build/src/EntityDatabaseAdapter.js.map +1 -1
- package/build/src/EntityFieldDefinition.d.ts +1 -1
- package/build/src/EntityInvalidationUtils.d.ts +41 -0
- package/build/src/EntityInvalidationUtils.js +71 -0
- package/build/src/EntityInvalidationUtils.js.map +1 -0
- package/build/src/EntityLoader.d.ts +0 -6
- package/build/src/EntityLoader.js +0 -7
- package/build/src/EntityLoader.js.map +1 -1
- package/build/src/EntityLoaderFactory.d.ts +4 -0
- package/build/src/EntityLoaderFactory.js +10 -3
- package/build/src/EntityLoaderFactory.js.map +1 -1
- package/build/src/EntityPrivacyPolicy.d.ts +27 -0
- package/build/src/EntityPrivacyPolicy.js +22 -1
- package/build/src/EntityPrivacyPolicy.js.map +1 -1
- package/build/src/EntitySecondaryCacheLoader.d.ts +14 -3
- package/build/src/EntitySecondaryCacheLoader.js +21 -4
- package/build/src/EntitySecondaryCacheLoader.js.map +1 -1
- package/build/src/ReadonlyEntity.d.ts +4 -5
- package/build/src/ReadonlyEntity.js +7 -8
- package/build/src/ReadonlyEntity.js.map +1 -1
- package/build/src/ViewerContext.d.ts +6 -6
- package/build/src/ViewerContext.js +8 -8
- package/build/src/ViewerScopedEntityCompanion.d.ts +1 -1
- package/build/src/ViewerScopedEntityCompanion.js.map +1 -1
- package/build/src/ViewerScopedEntityLoaderFactory.d.ts +4 -0
- package/build/src/ViewerScopedEntityLoaderFactory.js +6 -0
- package/build/src/ViewerScopedEntityLoaderFactory.js.map +1 -1
- package/build/src/errors/EntityDatabaseAdapterError.d.ts +4 -0
- package/build/src/errors/EntityDatabaseAdapterError.js +13 -1
- package/build/src/errors/EntityDatabaseAdapterError.js.map +1 -1
- package/build/src/errors/EntityError.d.ts +2 -1
- package/build/src/errors/EntityError.js +1 -0
- package/build/src/errors/EntityError.js.map +1 -1
- package/build/src/index.d.ts +2 -1
- package/build/src/index.js +2 -1
- package/build/src/index.js.map +1 -1
- package/build/src/internal/EntityDataManager.d.ts +8 -16
- package/build/src/internal/EntityDataManager.js +8 -18
- package/build/src/internal/EntityDataManager.js.map +1 -1
- package/build/src/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/src/internal/EntityLoadInterfaces.d.ts +2 -0
- package/build/src/internal/EntityLoadInterfaces.js +2 -0
- package/build/src/internal/EntityLoadInterfaces.js.map +1 -1
- package/build/src/internal/EntityTableDataCoordinator.d.ts +2 -0
- package/build/src/internal/EntityTableDataCoordinator.js +4 -0
- package/build/src/internal/EntityTableDataCoordinator.js.map +1 -1
- package/build/src/metrics/EntityMetricsUtils.d.ts +1 -0
- package/build/src/metrics/EntityMetricsUtils.js +15 -1
- package/build/src/metrics/EntityMetricsUtils.js.map +1 -1
- package/build/src/metrics/IEntityMetricsAdapter.d.ts +4 -1
- package/build/src/metrics/IEntityMetricsAdapter.js +3 -0
- package/build/src/metrics/IEntityMetricsAdapter.js.map +1 -1
- package/build/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.d.ts +2 -2
- package/build/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.d.ts +2 -2
- package/build/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.d.ts +2 -2
- package/build/src/rules/PrivacyPolicyRule.d.ts +2 -2
- package/build/src/utils/EntityPrivacyUtils.js +11 -20
- package/build/src/utils/EntityPrivacyUtils.js.map +1 -1
- package/build/src/utils/collections/maps.d.ts +2 -2
- package/build/src/utils/collections/maps.js +2 -2
- package/package.json +4 -4
- package/src/AuthorizationResultBasedEntityAssociationLoader.ts +4 -7
- package/src/AuthorizationResultBasedEntityLoader.ts +58 -88
- package/src/AuthorizationResultBasedEntityMutator.ts +35 -20
- package/src/EnforcingEntityCreator.ts +1 -1
- package/src/EnforcingEntityLoader.ts +1 -95
- package/src/Entity.ts +6 -0
- package/src/EntityCompanion.ts +2 -2
- package/src/EntityCompanionProvider.ts +4 -4
- package/src/EntityConfiguration.ts +8 -5
- package/src/EntityConstructionUtils.ts +168 -0
- package/src/EntityDatabaseAdapter.ts +32 -222
- package/src/EntityFieldDefinition.ts +1 -1
- package/src/{EntityLoaderUtils.ts → EntityInvalidationUtils.ts} +5 -96
- package/src/EntityLoader.ts +0 -16
- package/src/EntityLoaderFactory.ts +50 -10
- package/src/EntityPrivacyPolicy.ts +44 -1
- package/src/EntitySecondaryCacheLoader.ts +54 -3
- package/src/ReadonlyEntity.ts +9 -11
- package/src/ViewerContext.ts +10 -10
- package/src/ViewerScopedEntityCompanion.ts +1 -1
- package/src/ViewerScopedEntityLoaderFactory.ts +37 -0
- package/src/__tests__/AuthorizationResultBasedEntityLoader-constructor-test.ts +3 -5
- package/src/__tests__/AuthorizationResultBasedEntityLoader-test.ts +34 -419
- package/src/__tests__/ComposedCacheAdapter-test.ts +3 -3
- package/src/__tests__/EnforcingEntityLoader-test.ts +2 -134
- package/src/__tests__/EntityCompanion-test.ts +18 -0
- package/src/__tests__/EntityConfiguration-test.ts +4 -4
- package/src/__tests__/EntityDatabaseAdapter-test.ts +33 -68
- package/src/__tests__/EntityEdges-test.ts +10 -10
- package/src/__tests__/EntityLoader-test.ts +6 -4
- package/src/__tests__/EntityMutator-test.ts +27 -15
- package/src/__tests__/EntityPrivacyPolicy-test.ts +102 -0
- package/src/__tests__/EntityQueryContext-test.ts +11 -11
- package/src/__tests__/EntitySecondaryCacheLoader-test.ts +10 -5
- package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -6
- package/src/__tests__/GenericEntityCacheAdapter-test.ts +18 -15
- package/src/__tests__/GenericSecondaryEntityCache-test.ts +27 -5
- package/src/__tests__/ReadonlyEntity-test.ts +6 -4
- package/src/__tests__/ViewerContext-test.ts +4 -4
- package/src/__tests__/ViewerScopedEntityCompanion-test.ts +1 -0
- package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +0 -17
- package/src/errors/EntityDatabaseAdapterError.ts +14 -0
- package/src/errors/EntityError.ts +1 -0
- package/src/errors/__tests__/EntityDatabaseAdapterError-test.ts +9 -0
- package/src/errors/__tests__/EntityError-test.ts +13 -5
- package/src/index.ts +2 -1
- package/src/internal/EntityDataManager.ts +19 -54
- package/src/internal/EntityFieldTransformationUtils.ts +5 -5
- package/src/internal/EntityLoadInterfaces.ts +2 -0
- package/src/internal/EntityTableDataCoordinator.ts +2 -2
- package/src/internal/__tests__/CompositeFieldHolder-test.ts +8 -2
- package/src/internal/__tests__/EntityDataManager-test.ts +71 -202
- package/src/internal/__tests__/ReadThroughEntityCache-test.ts +39 -24
- package/src/metrics/EntityMetricsUtils.ts +23 -0
- package/src/metrics/IEntityMetricsAdapter.ts +3 -0
- package/src/metrics/__tests__/EntityMetricsUtils-test.ts +120 -0
- package/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.ts +2 -2
- package/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.ts +2 -2
- package/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.ts +2 -2
- package/src/rules/PrivacyPolicyRule.ts +2 -2
- package/src/rules/__tests__/AllowIfAllSubRulesAllowPrivacyPolicyRule-test.ts +4 -4
- package/src/rules/__tests__/AllowIfAnySubRuleAllowsPrivacyPolicyRule-test.ts +4 -4
- package/src/rules/__tests__/AllowIfInParentCascadeDeletionPrivacyPolicyRule-test.ts +11 -1
- package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -2
- package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -2
- package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -2
- package/src/rules/__tests__/EvaluateIfEntityFieldPredicatePrivacyPolicyRule-test.ts +3 -3
- package/src/utils/EntityPrivacyUtils.ts +18 -29
- package/src/utils/__testfixtures__/PrivacyPolicyRuleTestUtils.ts +2 -2
- package/src/utils/__testfixtures__/StubDatabaseAdapter.ts +13 -101
- package/src/utils/__tests__/EntityCreationUtils-test.ts +6 -6
- package/src/utils/__tests__/EntityPrivacyUtils-test.ts +2 -2
- package/src/utils/collections/maps.ts +2 -2
- package/build/src/EntityLoaderUtils.js +0 -147
- package/build/src/EntityLoaderUtils.js.map +0 -1
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { AuthorizationResultBasedEntityLoader } from './AuthorizationResultBasedEntityLoader';
|
|
2
2
|
import { EntityCompositeField, EntityCompositeFieldValue } from './EntityConfiguration';
|
|
3
|
-
import {
|
|
4
|
-
FieldEqualityCondition,
|
|
5
|
-
QuerySelectionModifiers,
|
|
6
|
-
QuerySelectionModifiersWithOrderByRaw,
|
|
7
|
-
} from './EntityDatabaseAdapter';
|
|
8
3
|
import { EntityPrivacyPolicy } from './EntityPrivacyPolicy';
|
|
9
4
|
import { ReadonlyEntity } from './ReadonlyEntity';
|
|
10
5
|
import { ViewerContext } from './ViewerContext';
|
|
@@ -29,7 +24,7 @@ export class EnforcingEntityLoader<
|
|
|
29
24
|
TEntity,
|
|
30
25
|
TSelectedFields
|
|
31
26
|
>,
|
|
32
|
-
TSelectedFields extends keyof TFields,
|
|
27
|
+
TSelectedFields extends keyof TFields = keyof TFields,
|
|
33
28
|
> {
|
|
34
29
|
constructor(
|
|
35
30
|
private readonly entityLoader: AuthorizationResultBasedEntityLoader<
|
|
@@ -219,93 +214,4 @@ export class EnforcingEntityLoader<
|
|
|
219
214
|
const entityResults = await this.entityLoader.loadManyByIDsNullableAsync(ids);
|
|
220
215
|
return mapMap(entityResults, (result) => result?.enforceValue() ?? null);
|
|
221
216
|
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Loads the first entity matching the selection constructed from the conjunction of specified
|
|
225
|
-
* operands, or null if no matching entity exists. Entities loaded using this method are not
|
|
226
|
-
* batched or cached.
|
|
227
|
-
*
|
|
228
|
-
* This is a convenience method for {@link loadManyByFieldEqualityConjunctionAsync}. However, the
|
|
229
|
-
* `orderBy` option must be specified to define what "first" means. If ordering doesn't matter,
|
|
230
|
-
* explicitly pass in an empty array.
|
|
231
|
-
*
|
|
232
|
-
* @param fieldEqualityOperands - list of field equality selection operand specifications
|
|
233
|
-
* @param querySelectionModifiers - orderBy and optional offset for the query
|
|
234
|
-
* @returns the first entity that matches the query or null if no entity matches the query
|
|
235
|
-
* @throws EntityNotAuthorizedError when viewer is not authorized to view the returned entity
|
|
236
|
-
*/
|
|
237
|
-
async loadFirstByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
|
|
238
|
-
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
|
|
239
|
-
querySelectionModifiers: Omit<QuerySelectionModifiers<TFields>, 'limit'> &
|
|
240
|
-
Required<Pick<QuerySelectionModifiers<TFields>, 'orderBy'>>,
|
|
241
|
-
): Promise<TEntity | null> {
|
|
242
|
-
const entityResult = await this.entityLoader.loadFirstByFieldEqualityConjunctionAsync(
|
|
243
|
-
fieldEqualityOperands,
|
|
244
|
-
querySelectionModifiers,
|
|
245
|
-
);
|
|
246
|
-
return entityResult ? entityResult.enforceValue() : null;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Loads many entities matching the selection constructed from the conjunction of specified operands.
|
|
251
|
-
* Entities loaded using this method are not batched or cached.
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* fieldEqualityOperands:
|
|
255
|
-
* `[{fieldName: 'hello', fieldValue: 1}, {fieldName: 'world', fieldValues: [2, 3]}]`
|
|
256
|
-
* Entities returned with a SQL EntityDatabaseAdapter:
|
|
257
|
-
* `WHERE hello = 1 AND world = ANY({2, 3})`
|
|
258
|
-
*
|
|
259
|
-
* @param fieldEqualityOperands - list of field equality selection operand specifications
|
|
260
|
-
* @param querySelectionModifiers - limit, offset, and orderBy for the query
|
|
261
|
-
* @returns array of entities that match the query
|
|
262
|
-
* @throws EntityNotAuthorizedError when viewer is not authorized to view one or more of the returned entities
|
|
263
|
-
*/
|
|
264
|
-
async loadManyByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
|
|
265
|
-
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
|
|
266
|
-
querySelectionModifiers: QuerySelectionModifiers<TFields> = {},
|
|
267
|
-
): Promise<readonly TEntity[]> {
|
|
268
|
-
const entityResults = await this.entityLoader.loadManyByFieldEqualityConjunctionAsync(
|
|
269
|
-
fieldEqualityOperands,
|
|
270
|
-
querySelectionModifiers,
|
|
271
|
-
);
|
|
272
|
-
return entityResults.map((result) => result.enforceValue());
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Loads many entities matching the raw WHERE clause. Corresponds to the knex `whereRaw` argument format.
|
|
277
|
-
*
|
|
278
|
-
* @remarks
|
|
279
|
-
* Important notes:
|
|
280
|
-
* - Fields in clause are database column names instead of transformed entity field names.
|
|
281
|
-
* - Entities loaded using this method are not batched or cached.
|
|
282
|
-
* - Not all database adapters implement the ability to execute this method of fetching entities.
|
|
283
|
-
*
|
|
284
|
-
* @example
|
|
285
|
-
* rawWhereClause: `id = ?`
|
|
286
|
-
* bindings: `[1]`
|
|
287
|
-
* Entites returned `WHERE id = 1`
|
|
288
|
-
*
|
|
289
|
-
* http://knexjs.org/#Builder-whereRaw
|
|
290
|
-
* http://knexjs.org/#Raw-Bindings
|
|
291
|
-
*
|
|
292
|
-
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
|
|
293
|
-
* @param bindings - array of positional bindings or object of named bindings
|
|
294
|
-
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
|
|
295
|
-
* @returns array of entities that match the query
|
|
296
|
-
* @throws EntityNotAuthorizedError when viewer is not authorized to view one or more of the returned entities
|
|
297
|
-
* @throws Error when rawWhereClause or bindings are invalid
|
|
298
|
-
*/
|
|
299
|
-
async loadManyByRawWhereClauseAsync(
|
|
300
|
-
rawWhereClause: string,
|
|
301
|
-
bindings: any[] | object,
|
|
302
|
-
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields> = {},
|
|
303
|
-
): Promise<readonly TEntity[]> {
|
|
304
|
-
const entityResults = await this.entityLoader.loadManyByRawWhereClauseAsync(
|
|
305
|
-
rawWhereClause,
|
|
306
|
-
bindings,
|
|
307
|
-
querySelectionModifiers,
|
|
308
|
-
);
|
|
309
|
-
return entityResults.map((result) => result.enforceValue());
|
|
310
|
-
}
|
|
311
217
|
}
|
package/src/Entity.ts
CHANGED
|
@@ -32,6 +32,12 @@ import { ViewerContext } from './ViewerContext';
|
|
|
32
32
|
*
|
|
33
33
|
* All concrete entity implementations should extend this class and provide their
|
|
34
34
|
* own EntityCompanionDefinition.
|
|
35
|
+
*
|
|
36
|
+
* Generic type parameters:
|
|
37
|
+
* TFields - the shape of the underlying data for this entity, typically corresponding to a database table schema. The mapping from TFields to the actual database schema is defined in the EntityCompanionDefinition for this entity.
|
|
38
|
+
* TIDField - the key of the ID field in TFields, which must be non-nullable and is used to uniquely identify individual entities
|
|
39
|
+
* TViewerContext - the type of ViewerContext that can be used with this entity
|
|
40
|
+
* TSelectedFields - the keys of fields in TFields that belong to this entity; used when there are multiple entities backed by the same underlying table with different field subsets
|
|
35
41
|
*/
|
|
36
42
|
export abstract class Entity<
|
|
37
43
|
TFields extends Record<string, any>,
|
package/src/EntityCompanion.ts
CHANGED
|
@@ -59,8 +59,8 @@ export class EntityCompanion<
|
|
|
59
59
|
TPrivacyPolicy,
|
|
60
60
|
TSelectedFields
|
|
61
61
|
>,
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
public readonly tableDataCoordinator: EntityTableDataCoordinator<TFields, TIDField>,
|
|
63
|
+
public readonly metricsAdapter: IEntityMetricsAdapter,
|
|
64
64
|
) {
|
|
65
65
|
this.privacyPolicy = new entityCompanionDefinition.privacyPolicyClass();
|
|
66
66
|
this.entityLoaderFactory = new EntityLoaderFactory<
|
|
@@ -208,13 +208,13 @@ export class EntityCompanionProvider {
|
|
|
208
208
|
});
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
getQueryContextProviderForDatabaseAdapterFlavor(
|
|
212
212
|
databaseAdapterFlavor: DatabaseAdapterFlavor,
|
|
213
213
|
): EntityQueryContextProvider {
|
|
214
214
|
const entityDatabaseAdapterFlavor = this.databaseAdapterFlavors.get(databaseAdapterFlavor);
|
|
215
215
|
invariant(
|
|
216
216
|
entityDatabaseAdapterFlavor,
|
|
217
|
-
`No database
|
|
217
|
+
`No database adapter configuration found for flavor: ${databaseAdapterFlavor}`,
|
|
218
218
|
);
|
|
219
219
|
|
|
220
220
|
return entityDatabaseAdapterFlavor.queryContextProvider;
|
|
@@ -233,7 +233,7 @@ export class EntityCompanionProvider {
|
|
|
233
233
|
);
|
|
234
234
|
invariant(
|
|
235
235
|
entityDatabaseAdapterFlavor,
|
|
236
|
-
`No database
|
|
236
|
+
`No database adapter configuration found for flavor: ${entityConfiguration.databaseAdapterFlavor}`,
|
|
237
237
|
);
|
|
238
238
|
|
|
239
239
|
const entityCacheAdapterFlavor = this.cacheAdapterFlavors.get(
|
|
@@ -241,7 +241,7 @@ export class EntityCompanionProvider {
|
|
|
241
241
|
);
|
|
242
242
|
invariant(
|
|
243
243
|
entityCacheAdapterFlavor,
|
|
244
|
-
`No cache
|
|
244
|
+
`No cache adapter configuration found for flavor: ${entityConfiguration.cacheAdapterFlavor}`,
|
|
245
245
|
);
|
|
246
246
|
|
|
247
247
|
return new EntityTableDataCoordinator(
|
|
@@ -67,7 +67,9 @@ export class CompositeFieldInfo<
|
|
|
67
67
|
keyDefinition.compositeField.length === new Set(keyDefinition.compositeField).size,
|
|
68
68
|
'Composite field must have unique sub-fields',
|
|
69
69
|
);
|
|
70
|
-
const compositeFieldHolder = new CompositeFieldHolder(
|
|
70
|
+
const compositeFieldHolder = new CompositeFieldHolder<TFields, TIDField>(
|
|
71
|
+
keyDefinition.compositeField,
|
|
72
|
+
);
|
|
71
73
|
return [
|
|
72
74
|
compositeFieldHolder.serialize(),
|
|
73
75
|
{ compositeFieldHolder, cache: keyDefinition.cache ?? false },
|
|
@@ -79,8 +81,9 @@ export class CompositeFieldInfo<
|
|
|
79
81
|
public getCompositeFieldHolderForCompositeField(
|
|
80
82
|
compositeField: EntityCompositeField<TFields>,
|
|
81
83
|
): CompositeFieldHolder<TFields, TIDField> | undefined {
|
|
82
|
-
return this.compositeFieldInfoMap.get(
|
|
83
|
-
|
|
84
|
+
return this.compositeFieldInfoMap.get(
|
|
85
|
+
new CompositeFieldHolder<TFields, TIDField>(compositeField).serialize(),
|
|
86
|
+
)?.compositeFieldHolder;
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
public getAllCompositeFieldHolders(): readonly CompositeFieldHolder<TFields, TIDField>[] {
|
|
@@ -89,7 +92,7 @@ export class CompositeFieldInfo<
|
|
|
89
92
|
|
|
90
93
|
public canCacheCompositeField(compositeField: EntityCompositeField<TFields>): boolean {
|
|
91
94
|
const compositeFieldInfo = this.compositeFieldInfoMap.get(
|
|
92
|
-
new CompositeFieldHolder(compositeField).serialize(),
|
|
95
|
+
new CompositeFieldHolder<TFields, TIDField>(compositeField).serialize(),
|
|
93
96
|
);
|
|
94
97
|
invariant(
|
|
95
98
|
compositeFieldInfo,
|
|
@@ -107,7 +110,7 @@ export class EntityConfiguration<
|
|
|
107
110
|
TFields extends Record<string, any>,
|
|
108
111
|
TIDField extends keyof TFields,
|
|
109
112
|
> {
|
|
110
|
-
readonly idField:
|
|
113
|
+
readonly idField: TIDField;
|
|
111
114
|
readonly tableName: string;
|
|
112
115
|
readonly cacheableKeys: ReadonlySet<keyof TFields>;
|
|
113
116
|
readonly compositeFieldInfo: CompositeFieldInfo<TFields, TIDField>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Result, asyncResult, result } from '@expo/results';
|
|
2
|
+
import invariant from 'invariant';
|
|
3
|
+
import nullthrows from 'nullthrows';
|
|
4
|
+
|
|
5
|
+
import { IEntityClass } from './Entity';
|
|
6
|
+
import { EntityConfiguration } from './EntityConfiguration';
|
|
7
|
+
import { EntityPrivacyPolicy, EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
|
|
8
|
+
import { EntityQueryContext } from './EntityQueryContext';
|
|
9
|
+
import { ReadonlyEntity } from './ReadonlyEntity';
|
|
10
|
+
import { ViewerContext } from './ViewerContext';
|
|
11
|
+
import { pick } from './entityUtils';
|
|
12
|
+
import { EntityInvalidFieldValueError } from './errors/EntityInvalidFieldValueError';
|
|
13
|
+
import { IEntityMetricsAdapter } from './metrics/IEntityMetricsAdapter';
|
|
14
|
+
import { mapMapAsync } from './utils/collections/maps';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Common entity loader utilities for entity construction and authorization.
|
|
18
|
+
* Methods are exposed publicly since in rare cases they may need to be called manually.
|
|
19
|
+
*/
|
|
20
|
+
export class EntityConstructionUtils<
|
|
21
|
+
TFields extends Record<string, any>,
|
|
22
|
+
TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>,
|
|
23
|
+
TViewerContext extends ViewerContext,
|
|
24
|
+
TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>,
|
|
25
|
+
TPrivacyPolicy extends EntityPrivacyPolicy<
|
|
26
|
+
TFields,
|
|
27
|
+
TIDField,
|
|
28
|
+
TViewerContext,
|
|
29
|
+
TEntity,
|
|
30
|
+
TSelectedFields
|
|
31
|
+
>,
|
|
32
|
+
TSelectedFields extends keyof TFields,
|
|
33
|
+
> {
|
|
34
|
+
constructor(
|
|
35
|
+
private readonly viewerContext: TViewerContext,
|
|
36
|
+
private readonly queryContext: EntityQueryContext,
|
|
37
|
+
private readonly privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
|
|
38
|
+
TFields,
|
|
39
|
+
TIDField,
|
|
40
|
+
TViewerContext,
|
|
41
|
+
TEntity,
|
|
42
|
+
TSelectedFields
|
|
43
|
+
>,
|
|
44
|
+
private readonly entityConfiguration: EntityConfiguration<TFields, TIDField>,
|
|
45
|
+
private readonly entityClass: IEntityClass<
|
|
46
|
+
TFields,
|
|
47
|
+
TIDField,
|
|
48
|
+
TViewerContext,
|
|
49
|
+
TEntity,
|
|
50
|
+
TPrivacyPolicy,
|
|
51
|
+
TSelectedFields
|
|
52
|
+
>,
|
|
53
|
+
private readonly entitySelectedFields: TSelectedFields[] | undefined,
|
|
54
|
+
private readonly privacyPolicy: TPrivacyPolicy,
|
|
55
|
+
protected readonly metricsAdapter: IEntityMetricsAdapter,
|
|
56
|
+
) {}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Construct an entity from a fields object (applying field selection if applicable),
|
|
60
|
+
* checking that the ID field is specified.
|
|
61
|
+
*
|
|
62
|
+
* @param fieldsObject - fields object
|
|
63
|
+
*/
|
|
64
|
+
public constructEntity(fieldsObject: TFields): TEntity {
|
|
65
|
+
const idField = this.entityConfiguration.idField;
|
|
66
|
+
const id = nullthrows(fieldsObject[idField], 'must provide ID to create an entity');
|
|
67
|
+
const entitySelectedFields =
|
|
68
|
+
this.entitySelectedFields ?? Array.from(this.entityConfiguration.schema.keys());
|
|
69
|
+
const selectedFields = pick(fieldsObject, entitySelectedFields);
|
|
70
|
+
return new this.entityClass({
|
|
71
|
+
viewerContext: this.viewerContext,
|
|
72
|
+
id: id as TFields[TIDField],
|
|
73
|
+
databaseFields: fieldsObject,
|
|
74
|
+
selectedFields,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Construct and authorize entities from fields map, returning error results for entities that fail
|
|
80
|
+
* to construct or fail to authorize.
|
|
81
|
+
*
|
|
82
|
+
* @param map - map from an arbitrary key type to an array of entity field objects
|
|
83
|
+
*/
|
|
84
|
+
public async constructAndAuthorizeEntitiesAsync<K>(
|
|
85
|
+
map: ReadonlyMap<K, readonly Readonly<TFields>[]>,
|
|
86
|
+
): Promise<ReadonlyMap<K, readonly Result<TEntity>[]>> {
|
|
87
|
+
return await mapMapAsync(map, async (fieldObjects) => {
|
|
88
|
+
return await this.constructAndAuthorizeEntitiesArrayAsync(fieldObjects);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Construct and authorize entities from field objects array, returning error results for entities that fail
|
|
94
|
+
* to construct or fail to authorize.
|
|
95
|
+
*
|
|
96
|
+
* @param fieldObjects - array of field objects
|
|
97
|
+
*/
|
|
98
|
+
public async constructAndAuthorizeEntitiesArrayAsync(
|
|
99
|
+
fieldObjects: readonly Readonly<TFields>[],
|
|
100
|
+
): Promise<readonly Result<TEntity>[]> {
|
|
101
|
+
const uncheckedEntityResults = this.tryConstructEntities(fieldObjects);
|
|
102
|
+
return await Promise.all(
|
|
103
|
+
uncheckedEntityResults.map((uncheckedEntityResult) =>
|
|
104
|
+
this.authorizeEntityResultAsync(uncheckedEntityResult),
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private async authorizeEntityResultAsync(
|
|
110
|
+
uncheckedEntityResult: Result<TEntity>,
|
|
111
|
+
): Promise<Result<TEntity>> {
|
|
112
|
+
if (!uncheckedEntityResult.ok) {
|
|
113
|
+
return uncheckedEntityResult;
|
|
114
|
+
}
|
|
115
|
+
return await asyncResult(
|
|
116
|
+
this.privacyPolicy.authorizeReadAsync(
|
|
117
|
+
this.viewerContext,
|
|
118
|
+
this.queryContext,
|
|
119
|
+
this.privacyPolicyEvaluationContext,
|
|
120
|
+
uncheckedEntityResult.value,
|
|
121
|
+
this.metricsAdapter,
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public async constructAndAuthorizeEntityAsync(
|
|
127
|
+
fieldsObject: Readonly<TFields>,
|
|
128
|
+
): Promise<Result<TEntity>> {
|
|
129
|
+
const uncheckedEntityResult = this.tryConstructEntity(fieldsObject);
|
|
130
|
+
return await this.authorizeEntityResultAsync(uncheckedEntityResult);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Validate that field values are valid according to the field's validation function.
|
|
135
|
+
*
|
|
136
|
+
* @param fieldName - field name to validate
|
|
137
|
+
* @param fieldValues - field values to validate
|
|
138
|
+
* @throws EntityInvalidFieldValueError when a field value is invalid
|
|
139
|
+
*/
|
|
140
|
+
public validateFieldAndValues<N extends keyof Pick<TFields, TSelectedFields>>(
|
|
141
|
+
fieldName: N,
|
|
142
|
+
fieldValues: readonly TFields[N][],
|
|
143
|
+
): void {
|
|
144
|
+
const fieldDefinition = this.entityConfiguration.schema.get(fieldName);
|
|
145
|
+
invariant(fieldDefinition, `must have field definition for field = ${String(fieldName)}`);
|
|
146
|
+
for (const fieldValue of fieldValues) {
|
|
147
|
+
const isInputValid = fieldDefinition.validateInputValue(fieldValue);
|
|
148
|
+
if (!isInputValid) {
|
|
149
|
+
throw new EntityInvalidFieldValueError(this.entityClass, fieldName, fieldValue);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private tryConstructEntities(fieldsObjects: readonly TFields[]): readonly Result<TEntity>[] {
|
|
155
|
+
return fieldsObjects.map((fieldsObject) => this.tryConstructEntity(fieldsObject));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private tryConstructEntity(fieldsObject: TFields): Result<TEntity> {
|
|
159
|
+
try {
|
|
160
|
+
return result(this.constructEntity(fieldsObject));
|
|
161
|
+
} catch (e) {
|
|
162
|
+
if (!(e instanceof Error)) {
|
|
163
|
+
throw e;
|
|
164
|
+
}
|
|
165
|
+
return result(e);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -17,127 +17,6 @@ import {
|
|
|
17
17
|
} from './internal/EntityFieldTransformationUtils';
|
|
18
18
|
import { IEntityLoadKey, IEntityLoadValue } from './internal/EntityLoadInterfaces';
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Equality operand that is used for selecting entities with a field with a single value.
|
|
22
|
-
*/
|
|
23
|
-
export interface SingleValueFieldEqualityCondition<
|
|
24
|
-
TFields extends Record<string, any>,
|
|
25
|
-
N extends keyof TFields = keyof TFields,
|
|
26
|
-
> {
|
|
27
|
-
fieldName: N;
|
|
28
|
-
fieldValue: TFields[N];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Equality operand that is used for selecting entities with a field matching one of multiple values.
|
|
33
|
-
*/
|
|
34
|
-
export interface MultiValueFieldEqualityCondition<
|
|
35
|
-
TFields extends Record<string, any>,
|
|
36
|
-
N extends keyof TFields = keyof TFields,
|
|
37
|
-
> {
|
|
38
|
-
fieldName: N;
|
|
39
|
-
fieldValues: readonly TFields[N][];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* A single equality operand for use in a selection clause.
|
|
44
|
-
* See EntityLoader.loadManyByFieldEqualityConjunctionAsync documentation for examples.
|
|
45
|
-
*/
|
|
46
|
-
export type FieldEqualityCondition<
|
|
47
|
-
TFields extends Record<string, any>,
|
|
48
|
-
N extends keyof TFields = keyof TFields,
|
|
49
|
-
> = SingleValueFieldEqualityCondition<TFields, N> | MultiValueFieldEqualityCondition<TFields, N>;
|
|
50
|
-
|
|
51
|
-
export function isSingleValueFieldEqualityCondition<
|
|
52
|
-
TFields extends Record<string, any>,
|
|
53
|
-
N extends keyof TFields = keyof TFields,
|
|
54
|
-
>(
|
|
55
|
-
condition: FieldEqualityCondition<TFields, N>,
|
|
56
|
-
): condition is SingleValueFieldEqualityCondition<TFields, N> {
|
|
57
|
-
return (condition as SingleValueFieldEqualityCondition<TFields, N>).fieldValue !== undefined;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface TableFieldSingleValueEqualityCondition {
|
|
61
|
-
tableField: string;
|
|
62
|
-
tableValue: any;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface TableFieldMultiValueEqualityCondition {
|
|
66
|
-
tableField: string;
|
|
67
|
-
tableValues: readonly any[];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Ordering options for `orderBy` clauses.
|
|
72
|
-
*/
|
|
73
|
-
export enum OrderByOrdering {
|
|
74
|
-
/**
|
|
75
|
-
* Ascending order (lowest to highest).
|
|
76
|
-
* Ascending order puts smaller values first, where "smaller" is defined in terms of the %3C operator.
|
|
77
|
-
*/
|
|
78
|
-
ASCENDING = 'asc',
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Descending order (highest to lowest).
|
|
82
|
-
* Descending order puts larger values first, where "larger" is defined in terms of the %3E operator.
|
|
83
|
-
*/
|
|
84
|
-
DESCENDING = 'desc',
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* SQL modifiers that only affect the selection but not the projection.
|
|
89
|
-
*/
|
|
90
|
-
export interface QuerySelectionModifiers<TFields extends Record<string, any>> {
|
|
91
|
-
/**
|
|
92
|
-
* Order the entities by specified columns and orders.
|
|
93
|
-
*/
|
|
94
|
-
orderBy?: {
|
|
95
|
-
/**
|
|
96
|
-
* The field name to order by.
|
|
97
|
-
*/
|
|
98
|
-
fieldName: keyof TFields;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* The OrderByOrdering to order by.
|
|
102
|
-
*/
|
|
103
|
-
order: OrderByOrdering;
|
|
104
|
-
}[];
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Skip the specified number of entities queried before returning.
|
|
108
|
-
*/
|
|
109
|
-
offset?: number;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Limit the number of entities returned.
|
|
113
|
-
*/
|
|
114
|
-
limit?: number;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export interface QuerySelectionModifiersWithOrderByRaw<
|
|
118
|
-
TFields extends Record<string, any>,
|
|
119
|
-
> extends QuerySelectionModifiers<TFields> {
|
|
120
|
-
/**
|
|
121
|
-
* Order the entities by a raw SQL `ORDER BY` clause.
|
|
122
|
-
*/
|
|
123
|
-
orderByRaw?: string;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface TableQuerySelectionModifiers {
|
|
127
|
-
orderBy:
|
|
128
|
-
| {
|
|
129
|
-
columnName: string;
|
|
130
|
-
order: OrderByOrdering;
|
|
131
|
-
}[]
|
|
132
|
-
| undefined;
|
|
133
|
-
offset: number | undefined;
|
|
134
|
-
limit: number | undefined;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface TableQuerySelectionModifiersWithOrderByRaw extends TableQuerySelectionModifiers {
|
|
138
|
-
orderByRaw: string | undefined;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
20
|
/**
|
|
142
21
|
* A database adapter is an interface by which entity objects can be
|
|
143
22
|
* fetched, inserted, updated, and deleted from a database. This base class
|
|
@@ -148,9 +27,9 @@ export abstract class EntityDatabaseAdapter<
|
|
|
148
27
|
TFields extends Record<string, any>,
|
|
149
28
|
TIDField extends keyof TFields,
|
|
150
29
|
> {
|
|
151
|
-
|
|
30
|
+
protected readonly fieldTransformerMap: FieldTransformerMap;
|
|
152
31
|
|
|
153
|
-
constructor(
|
|
32
|
+
constructor(protected readonly entityConfiguration: EntityConfiguration<TFields, TIDField>) {
|
|
154
33
|
this.fieldTransformerMap = this.getFieldTransformerMap();
|
|
155
34
|
}
|
|
156
35
|
|
|
@@ -222,91 +101,51 @@ export abstract class EntityDatabaseAdapter<
|
|
|
222
101
|
): Promise<object[]>;
|
|
223
102
|
|
|
224
103
|
/**
|
|
225
|
-
* Fetch
|
|
226
|
-
*
|
|
104
|
+
* Fetch one objects where key is equal to value, null if no matching object exists.
|
|
105
|
+
* Returned object is not guaranteed to be deterministic. Most concrete implementations will implement this
|
|
106
|
+
* with a "first" or "limit 1" query.
|
|
227
107
|
*
|
|
228
108
|
* @param queryContext - query context with which to perform the fetch
|
|
229
|
-
* @param
|
|
230
|
-
* @param
|
|
231
|
-
* @returns
|
|
109
|
+
* @param key - load key being queried
|
|
110
|
+
* @param values - load value being queried
|
|
111
|
+
* @returns object that matches the query for the value
|
|
232
112
|
*/
|
|
233
|
-
async
|
|
113
|
+
async fetchOneWhereAsync<
|
|
114
|
+
TLoadKey extends IEntityLoadKey<TFields, TIDField, TSerializedLoadValue, TLoadValue>,
|
|
115
|
+
TSerializedLoadValue,
|
|
116
|
+
TLoadValue extends IEntityLoadValue<TSerializedLoadValue>,
|
|
117
|
+
>(
|
|
234
118
|
queryContext: EntityQueryContext,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
): Promise<
|
|
238
|
-
const
|
|
239
|
-
const
|
|
240
|
-
for (const operand of fieldEqualityOperands) {
|
|
241
|
-
if (isSingleValueFieldEqualityCondition(operand)) {
|
|
242
|
-
tableFieldSingleValueOperands.push({
|
|
243
|
-
tableField: getDatabaseFieldForEntityField(this.entityConfiguration, operand.fieldName),
|
|
244
|
-
tableValue: operand.fieldValue,
|
|
245
|
-
});
|
|
246
|
-
} else {
|
|
247
|
-
tableFieldMultipleValueOperands.push({
|
|
248
|
-
tableField: getDatabaseFieldForEntityField(this.entityConfiguration, operand.fieldName),
|
|
249
|
-
tableValues: operand.fieldValues,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
}
|
|
119
|
+
key: TLoadKey,
|
|
120
|
+
value: TLoadValue,
|
|
121
|
+
): Promise<Readonly<TFields> | null> {
|
|
122
|
+
const keyDatabaseColumns = key.getDatabaseColumns(this.entityConfiguration);
|
|
123
|
+
const valueDatabaseValue = key.getDatabaseValues(value);
|
|
253
124
|
|
|
254
|
-
const
|
|
125
|
+
const result = await this.fetchOneWhereInternalAsync(
|
|
255
126
|
queryContext.getQueryInterface(),
|
|
256
127
|
this.entityConfiguration.tableName,
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
this.convertToTableQueryModifiers(querySelectionModifiers),
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
return results.map((result) =>
|
|
263
|
-
transformDatabaseObjectToFields(this.entityConfiguration, this.fieldTransformerMap, result),
|
|
128
|
+
keyDatabaseColumns,
|
|
129
|
+
valueDatabaseValue,
|
|
264
130
|
);
|
|
265
|
-
}
|
|
266
131
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
tableFieldSingleValueEqualityOperands: TableFieldSingleValueEqualityCondition[],
|
|
271
|
-
tableFieldMultiValueEqualityOperands: TableFieldMultiValueEqualityCondition[],
|
|
272
|
-
querySelectionModifiers: TableQuerySelectionModifiers,
|
|
273
|
-
): Promise<object[]>;
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Fetch many objects matching the raw WHERE clause.
|
|
277
|
-
*
|
|
278
|
-
* @param queryContext - query context with which to perform the fetch
|
|
279
|
-
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
|
|
280
|
-
* @param bindings - array of positional bindings or object of named bindings
|
|
281
|
-
* @param querySelectionModifiers - limit, offset, and orderBy for the query
|
|
282
|
-
* @returns array of objects matching the query
|
|
283
|
-
*/
|
|
284
|
-
async fetchManyByRawWhereClauseAsync(
|
|
285
|
-
queryContext: EntityQueryContext,
|
|
286
|
-
rawWhereClause: string,
|
|
287
|
-
bindings: any[] | object,
|
|
288
|
-
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>,
|
|
289
|
-
): Promise<readonly Readonly<TFields>[]> {
|
|
290
|
-
const results = await this.fetchManyByRawWhereClauseInternalAsync(
|
|
291
|
-
queryContext.getQueryInterface(),
|
|
292
|
-
this.entityConfiguration.tableName,
|
|
293
|
-
rawWhereClause,
|
|
294
|
-
bindings,
|
|
295
|
-
this.convertToTableQueryModifiersWithOrderByRaw(querySelectionModifiers),
|
|
296
|
-
);
|
|
132
|
+
if (!result) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
297
135
|
|
|
298
|
-
return
|
|
299
|
-
|
|
136
|
+
return transformDatabaseObjectToFields(
|
|
137
|
+
this.entityConfiguration,
|
|
138
|
+
this.fieldTransformerMap,
|
|
139
|
+
result,
|
|
300
140
|
);
|
|
301
141
|
}
|
|
302
142
|
|
|
303
|
-
protected abstract
|
|
143
|
+
protected abstract fetchOneWhereInternalAsync(
|
|
304
144
|
queryInterface: any,
|
|
305
145
|
tableName: string,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
): Promise<object[]>;
|
|
146
|
+
tableColumns: readonly string[],
|
|
147
|
+
tableTuple: readonly any[],
|
|
148
|
+
): Promise<object | null>;
|
|
310
149
|
|
|
311
150
|
/**
|
|
312
151
|
* Insert an object.
|
|
@@ -446,33 +285,4 @@ export abstract class EntityDatabaseAdapter<
|
|
|
446
285
|
tableIdField: string,
|
|
447
286
|
id: any,
|
|
448
287
|
): Promise<number>;
|
|
449
|
-
|
|
450
|
-
private convertToTableQueryModifiersWithOrderByRaw(
|
|
451
|
-
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>,
|
|
452
|
-
): TableQuerySelectionModifiersWithOrderByRaw {
|
|
453
|
-
return {
|
|
454
|
-
...this.convertToTableQueryModifiers(querySelectionModifiers),
|
|
455
|
-
orderByRaw: querySelectionModifiers.orderByRaw,
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
private convertToTableQueryModifiers(
|
|
460
|
-
querySelectionModifiers: QuerySelectionModifiers<TFields>,
|
|
461
|
-
): TableQuerySelectionModifiers {
|
|
462
|
-
const orderBy = querySelectionModifiers.orderBy;
|
|
463
|
-
return {
|
|
464
|
-
orderBy:
|
|
465
|
-
orderBy !== undefined
|
|
466
|
-
? orderBy.map((orderBySpecification) => ({
|
|
467
|
-
columnName: getDatabaseFieldForEntityField(
|
|
468
|
-
this.entityConfiguration,
|
|
469
|
-
orderBySpecification.fieldName,
|
|
470
|
-
),
|
|
471
|
-
order: orderBySpecification.order,
|
|
472
|
-
}))
|
|
473
|
-
: undefined,
|
|
474
|
-
offset: querySelectionModifiers.offset,
|
|
475
|
-
limit: querySelectionModifiers.limit,
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
288
|
}
|
|
@@ -103,7 +103,7 @@ export interface EntityAssociationDefinition<
|
|
|
103
103
|
* Field by which to load the instance of associatedEntityClass. If not provided, the
|
|
104
104
|
* associatedEntityClass instance is fetched by its ID.
|
|
105
105
|
*/
|
|
106
|
-
associatedEntityLookupByField?:
|
|
106
|
+
associatedEntityLookupByField?: TAssociatedSelectedFields;
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
109
|
* What action to perform on the entity at the other end of this edge when the entity on the source end of
|