@expo/entity-database-adapter-knex 0.55.0 → 0.58.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/AuthorizationResultBasedKnexEntityLoader.d.ts +278 -0
- package/build/src/AuthorizationResultBasedKnexEntityLoader.js +127 -0
- package/build/src/AuthorizationResultBasedKnexEntityLoader.js.map +1 -0
- package/build/src/BasePostgresEntityDatabaseAdapter.d.ts +150 -0
- package/build/src/BasePostgresEntityDatabaseAdapter.js +119 -0
- package/build/src/BasePostgresEntityDatabaseAdapter.js.map +1 -0
- package/build/src/BaseSQLQueryBuilder.d.ts +61 -0
- package/build/src/BaseSQLQueryBuilder.js +87 -0
- package/build/src/BaseSQLQueryBuilder.js.map +1 -0
- package/build/src/EnforcingKnexEntityLoader.d.ts +124 -0
- package/build/src/EnforcingKnexEntityLoader.js +166 -0
- package/build/src/EnforcingKnexEntityLoader.js.map +1 -0
- package/build/src/KnexEntityLoaderFactory.d.ts +25 -0
- package/build/src/KnexEntityLoaderFactory.js +39 -0
- package/build/src/KnexEntityLoaderFactory.js.map +1 -0
- package/build/src/PaginationStrategy.d.ts +30 -0
- package/build/src/PaginationStrategy.js +35 -0
- package/build/src/PaginationStrategy.js.map +1 -0
- package/build/src/PostgresEntity.d.ts +25 -0
- package/build/src/PostgresEntity.js +39 -0
- package/build/src/PostgresEntity.js.map +1 -0
- package/build/src/PostgresEntityDatabaseAdapter.d.ts +12 -5
- package/build/src/PostgresEntityDatabaseAdapter.js +33 -11
- package/build/src/PostgresEntityDatabaseAdapter.js.map +1 -1
- package/build/src/PostgresEntityDatabaseAdapterProvider.d.ts +9 -0
- package/build/src/PostgresEntityDatabaseAdapterProvider.js +5 -1
- package/build/src/PostgresEntityDatabaseAdapterProvider.js.map +1 -1
- package/build/src/ReadonlyPostgresEntity.d.ts +25 -0
- package/build/src/ReadonlyPostgresEntity.js +39 -0
- package/build/src/ReadonlyPostgresEntity.js.map +1 -0
- package/build/src/SQLOperator.d.ts +267 -0
- package/build/src/SQLOperator.js +474 -0
- package/build/src/SQLOperator.js.map +1 -0
- package/build/src/index.d.ts +15 -0
- package/build/src/index.js +15 -0
- package/build/src/index.js.map +1 -1
- package/build/src/internal/EntityKnexDataManager.d.ts +147 -0
- package/build/src/internal/EntityKnexDataManager.js +453 -0
- package/build/src/internal/EntityKnexDataManager.js.map +1 -0
- package/build/src/internal/getKnexDataManager.d.ts +3 -0
- package/build/src/internal/getKnexDataManager.js +19 -0
- package/build/src/internal/getKnexDataManager.js.map +1 -0
- package/build/src/internal/getKnexEntityLoaderFactory.d.ts +3 -0
- package/build/src/internal/getKnexEntityLoaderFactory.js +11 -0
- package/build/src/internal/getKnexEntityLoaderFactory.js.map +1 -0
- package/build/src/internal/utilityTypes.d.ts +5 -0
- package/build/src/internal/utilityTypes.js +5 -0
- package/build/src/internal/utilityTypes.js.map +1 -0
- package/build/src/internal/weakMaps.d.ts +9 -0
- package/build/src/internal/weakMaps.js +20 -0
- package/build/src/internal/weakMaps.js.map +1 -0
- package/build/src/knexLoader.d.ts +18 -0
- package/build/src/knexLoader.js +31 -0
- package/build/src/knexLoader.js.map +1 -0
- package/package.json +6 -5
- package/src/AuthorizationResultBasedKnexEntityLoader.ts +537 -0
- package/src/BasePostgresEntityDatabaseAdapter.ts +317 -0
- package/src/BaseSQLQueryBuilder.ts +114 -0
- package/src/EnforcingKnexEntityLoader.ts +271 -0
- package/src/KnexEntityLoaderFactory.ts +130 -0
- package/src/PaginationStrategy.ts +32 -0
- package/src/PostgresEntity.ts +118 -0
- package/src/PostgresEntityDatabaseAdapter.ts +81 -24
- package/src/PostgresEntityDatabaseAdapterProvider.ts +11 -1
- package/src/ReadonlyPostgresEntity.ts +115 -0
- package/src/SQLOperator.ts +630 -0
- package/src/__integration-tests__/EntityCreationUtils-test.ts +25 -31
- package/src/__integration-tests__/PostgresEntityIntegration-test.ts +3192 -330
- package/src/__integration-tests__/PostgresEntityQueryContextProvider-test.ts +7 -7
- package/src/__testfixtures__/PostgresTestEntity.ts +17 -3
- package/src/__tests__/AuthorizationResultBasedKnexEntityLoader-test.ts +1167 -0
- package/src/__tests__/BasePostgresEntityDatabaseAdapter-test.ts +160 -0
- package/src/__tests__/EnforcingKnexEntityLoader-test.ts +384 -0
- package/src/__tests__/EntityFields-test.ts +1 -1
- package/src/__tests__/PostgresEntity-test.ts +172 -0
- package/src/__tests__/ReadonlyEntity-test.ts +32 -0
- package/src/__tests__/SQLOperator-test.ts +871 -0
- package/src/__tests__/fixtures/StubPostgresDatabaseAdapter.ts +302 -0
- package/src/__tests__/fixtures/StubPostgresDatabaseAdapterProvider.ts +17 -0
- package/src/__tests__/fixtures/TestEntity.ts +131 -0
- package/src/__tests__/fixtures/TestPaginationEntity.ts +107 -0
- package/src/__tests__/fixtures/createUnitTestPostgresEntityCompanionProvider.ts +42 -0
- package/src/index.ts +15 -0
- package/src/internal/EntityKnexDataManager.ts +832 -0
- package/src/internal/__tests__/EntityKnexDataManager-test.ts +378 -0
- package/src/internal/__tests__/weakMaps-test.ts +25 -0
- package/src/internal/getKnexDataManager.ts +43 -0
- package/src/internal/getKnexEntityLoaderFactory.ts +60 -0
- package/src/internal/utilityTypes.ts +11 -0
- package/src/internal/weakMaps.ts +19 -0
- package/src/knexLoader.ts +110 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BasePostgresEntityDatabaseAdapter = exports.OrderByOrdering = exports.NullsOrdering = void 0;
|
|
4
|
+
exports.isSingleValueFieldEqualityCondition = isSingleValueFieldEqualityCondition;
|
|
5
|
+
const entity_1 = require("@expo/entity");
|
|
6
|
+
function isSingleValueFieldEqualityCondition(condition) {
|
|
7
|
+
return condition.fieldValue !== undefined;
|
|
8
|
+
}
|
|
9
|
+
var NullsOrdering;
|
|
10
|
+
(function (NullsOrdering) {
|
|
11
|
+
NullsOrdering["FIRST"] = "first";
|
|
12
|
+
NullsOrdering["LAST"] = "last";
|
|
13
|
+
})(NullsOrdering || (exports.NullsOrdering = NullsOrdering = {}));
|
|
14
|
+
/**
|
|
15
|
+
* Ordering options for `orderBy` clauses.
|
|
16
|
+
*/
|
|
17
|
+
var OrderByOrdering;
|
|
18
|
+
(function (OrderByOrdering) {
|
|
19
|
+
/**
|
|
20
|
+
* Ascending order (lowest to highest).
|
|
21
|
+
* Ascending order puts smaller values first, where "smaller" is defined in terms of the %3C operator.
|
|
22
|
+
*/
|
|
23
|
+
OrderByOrdering["ASCENDING"] = "asc";
|
|
24
|
+
/**
|
|
25
|
+
* Descending order (highest to lowest).
|
|
26
|
+
* Descending order puts larger values first, where "larger" is defined in terms of the %3E operator.
|
|
27
|
+
*/
|
|
28
|
+
OrderByOrdering["DESCENDING"] = "desc";
|
|
29
|
+
})(OrderByOrdering || (exports.OrderByOrdering = OrderByOrdering = {}));
|
|
30
|
+
class BasePostgresEntityDatabaseAdapter extends entity_1.EntityDatabaseAdapter {
|
|
31
|
+
/**
|
|
32
|
+
* Get the maximum page size for pagination.
|
|
33
|
+
* @returns maximum page size if configured, undefined otherwise
|
|
34
|
+
*/
|
|
35
|
+
get paginationMaxPageSize() {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fetch many objects matching the conjunction of where clauses constructed from
|
|
40
|
+
* specified field equality operands.
|
|
41
|
+
*
|
|
42
|
+
* @param queryContext - query context with which to perform the fetch
|
|
43
|
+
* @param fieldEqualityOperands - list of field equality where clause operand specifications
|
|
44
|
+
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
|
|
45
|
+
* @returns array of objects matching the query
|
|
46
|
+
*/
|
|
47
|
+
async fetchManyByFieldEqualityConjunctionAsync(queryContext, fieldEqualityOperands, querySelectionModifiers) {
|
|
48
|
+
const tableFieldSingleValueOperands = [];
|
|
49
|
+
const tableFieldMultipleValueOperands = [];
|
|
50
|
+
for (const operand of fieldEqualityOperands) {
|
|
51
|
+
if (isSingleValueFieldEqualityCondition(operand)) {
|
|
52
|
+
tableFieldSingleValueOperands.push({
|
|
53
|
+
tableField: (0, entity_1.getDatabaseFieldForEntityField)(this.entityConfiguration, operand.fieldName),
|
|
54
|
+
tableValue: operand.fieldValue,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
tableFieldMultipleValueOperands.push({
|
|
59
|
+
tableField: (0, entity_1.getDatabaseFieldForEntityField)(this.entityConfiguration, operand.fieldName),
|
|
60
|
+
tableValues: operand.fieldValues,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const results = await this.fetchManyByFieldEqualityConjunctionInternalAsync(queryContext.getQueryInterface(), this.entityConfiguration.tableName, tableFieldSingleValueOperands, tableFieldMultipleValueOperands, this.convertToTableQueryModifiers(querySelectionModifiers));
|
|
65
|
+
return results.map((result) => (0, entity_1.transformDatabaseObjectToFields)(this.entityConfiguration, this.fieldTransformerMap, result));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetch many objects matching the raw WHERE clause.
|
|
69
|
+
*
|
|
70
|
+
* @param queryContext - query context with which to perform the fetch
|
|
71
|
+
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
|
|
72
|
+
* @param bindings - array of positional bindings or object of named bindings
|
|
73
|
+
* @param querySelectionModifiers - limit, offset, and orderBy for the query
|
|
74
|
+
* @returns array of objects matching the query
|
|
75
|
+
*/
|
|
76
|
+
async fetchManyByRawWhereClauseAsync(queryContext, rawWhereClause, bindings, querySelectionModifiers) {
|
|
77
|
+
const results = await this.fetchManyByRawWhereClauseInternalAsync(queryContext.getQueryInterface(), this.entityConfiguration.tableName, rawWhereClause, bindings, this.convertToTableQueryModifiers(querySelectionModifiers));
|
|
78
|
+
return results.map((result) => (0, entity_1.transformDatabaseObjectToFields)(this.entityConfiguration, this.fieldTransformerMap, result));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fetch many objects matching the SQL fragment.
|
|
82
|
+
*
|
|
83
|
+
* @param queryContext - query context with which to perform the fetch
|
|
84
|
+
* @param sqlFragment - SQLFragment for the WHERE clause of the query
|
|
85
|
+
* @param querySelectionModifiers - limit, offset, and orderByFragment for the query
|
|
86
|
+
* @returns array of objects matching the query
|
|
87
|
+
*/
|
|
88
|
+
async fetchManyBySQLFragmentAsync(queryContext, sqlFragment, querySelectionModifiers) {
|
|
89
|
+
const results = await this.fetchManyBySQLFragmentInternalAsync(queryContext.getQueryInterface(), this.entityConfiguration.tableName, sqlFragment, this.convertToTableQueryModifiers(querySelectionModifiers));
|
|
90
|
+
return results.map((result) => (0, entity_1.transformDatabaseObjectToFields)(this.entityConfiguration, this.fieldTransformerMap, result));
|
|
91
|
+
}
|
|
92
|
+
convertToTableQueryModifiers(querySelectionModifiers) {
|
|
93
|
+
const orderBy = querySelectionModifiers.orderBy;
|
|
94
|
+
return {
|
|
95
|
+
orderBy: orderBy !== undefined
|
|
96
|
+
? orderBy.map((orderBySpecification) => {
|
|
97
|
+
if ('fieldName' in orderBySpecification) {
|
|
98
|
+
return {
|
|
99
|
+
columnName: (0, entity_1.getDatabaseFieldForEntityField)(this.entityConfiguration, orderBySpecification.fieldName),
|
|
100
|
+
order: orderBySpecification.order,
|
|
101
|
+
nulls: orderBySpecification.nulls,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return {
|
|
106
|
+
columnFragment: orderBySpecification.fieldFragment,
|
|
107
|
+
order: orderBySpecification.order,
|
|
108
|
+
nulls: orderBySpecification.nulls,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
: undefined,
|
|
113
|
+
offset: querySelectionModifiers.offset,
|
|
114
|
+
limit: querySelectionModifiers.limit,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.BasePostgresEntityDatabaseAdapter = BasePostgresEntityDatabaseAdapter;
|
|
119
|
+
//# sourceMappingURL=BasePostgresEntityDatabaseAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BasePostgresEntityDatabaseAdapter.js","sourceRoot":"","sources":["../../src/BasePostgresEntityDatabaseAdapter.ts"],"names":[],"mappings":";;;AAyCA,kFAOC;AAhDD,yCAKsB;AAoCtB,SAAgB,mCAAmC,CAIjD,SAA6C;IAE7C,OAAQ,SAA2D,CAAC,UAAU,KAAK,SAAS,CAAC;AAC/F,CAAC;AAYD,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,gCAAe,CAAA;IACf,8BAAa,CAAA;AACf,CAAC,EAHW,aAAa,6BAAb,aAAa,QAGxB;AAED;;GAEG;AACH,IAAY,eAYX;AAZD,WAAY,eAAe;IACzB;;;OAGG;IACH,oCAAiB,CAAA;IAEjB;;;OAGG;IACH,sCAAmB,CAAA;AACrB,CAAC,EAZW,eAAe,+BAAf,eAAe,QAY1B;AA4ED,MAAsB,iCAGpB,SAAQ,8BAAwC;IAChD;;;OAGG;IACH,IAAI,qBAAqB;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD;;;;;;;;OAQG;IACH,KAAK,CAAC,wCAAwC,CAC5C,YAAgC,EAChC,qBAAoE,EACpE,uBAAiE;QAEjE,MAAM,6BAA6B,GAA6C,EAAE,CAAC;QACnF,MAAM,+BAA+B,GAA4C,EAAE,CAAC;QACpF,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;YAC5C,IAAI,mCAAmC,CAAC,OAAO,CAAC,EAAE,CAAC;gBACjD,6BAA6B,CAAC,IAAI,CAAC;oBACjC,UAAU,EAAE,IAAA,uCAA8B,EAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,SAAS,CAAC;oBACvF,UAAU,EAAE,OAAO,CAAC,UAAU;iBAC/B,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,+BAA+B,CAAC,IAAI,CAAC;oBACnC,UAAU,EAAE,IAAA,uCAA8B,EAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,SAAS,CAAC;oBACvF,WAAW,EAAE,OAAO,CAAC,WAAW;iBACjC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,gDAAgD,CACzE,YAAY,CAAC,iBAAiB,EAAE,EAChC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAClC,6BAA6B,EAC7B,+BAA+B,EAC/B,IAAI,CAAC,4BAA4B,CAAC,uBAAuB,CAAC,CAC3D,CAAC;QAEF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,IAAA,wCAA+B,EAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAC5F,CAAC;IACJ,CAAC;IAUD;;;;;;;;OAQG;IACH,KAAK,CAAC,8BAA8B,CAClC,YAAgC,EAChC,cAAsB,EACtB,QAAwB,EACxB,uBAAiE;QAEjE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sCAAsC,CAC/D,YAAY,CAAC,iBAAiB,EAAE,EAChC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAClC,cAAc,EACd,QAAQ,EACR,IAAI,CAAC,4BAA4B,CAAC,uBAAuB,CAAC,CAC3D,CAAC;QAEF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,IAAA,wCAA+B,EAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAC5F,CAAC;IACJ,CAAC;IAUD;;;;;;;OAOG;IACH,KAAK,CAAC,2BAA2B,CAC/B,YAAgC,EAChC,WAAiC,EACjC,uBAAiE;QAEjE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,mCAAmC,CAC5D,YAAY,CAAC,iBAAiB,EAAE,EAChC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAClC,WAAW,EACX,IAAI,CAAC,4BAA4B,CAAC,uBAAuB,CAAC,CAC3D,CAAC;QAEF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5B,IAAA,wCAA+B,EAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAC5F,CAAC;IACJ,CAAC;IASO,4BAA4B,CAClC,uBAAiE;QAEjE,MAAM,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC;QAChD,OAAO;YACL,OAAO,EACL,OAAO,KAAK,SAAS;gBACnB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,oBAAoB,EAA+B,EAAE;oBAChE,IAAI,WAAW,IAAI,oBAAoB,EAAE,CAAC;wBACxC,OAAO;4BACL,UAAU,EAAE,IAAA,uCAA8B,EACxC,IAAI,CAAC,mBAAmB,EACxB,oBAAoB,CAAC,SAAS,CAC/B;4BACD,KAAK,EAAE,oBAAoB,CAAC,KAAK;4BACjC,KAAK,EAAE,oBAAoB,CAAC,KAAK;yBAClC,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,OAAO;4BACL,cAAc,EAAE,oBAAoB,CAAC,aAAa;4BAClD,KAAK,EAAE,oBAAoB,CAAC,KAAK;4BACjC,KAAK,EAAE,oBAAoB,CAAC,KAAK;yBAClC,CAAC;oBACJ,CAAC;gBACH,CAAC,CAAC;gBACJ,CAAC,CAAC,SAAS;YACf,MAAM,EAAE,uBAAuB,CAAC,MAAM;YACtC,KAAK,EAAE,uBAAuB,CAAC,KAAK;SACrC,CAAC;IACJ,CAAC;CACF;AAhKD,8EAgKC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { EntityLoaderOrderByClause, EntityLoaderQuerySelectionModifiers } from './AuthorizationResultBasedKnexEntityLoader';
|
|
2
|
+
import { NullsOrdering, OrderByOrdering } from './BasePostgresEntityDatabaseAdapter';
|
|
3
|
+
import { SQLFragment } from './SQLOperator';
|
|
4
|
+
/**
|
|
5
|
+
* Base SQL query builder that provides common functionality for building SQL queries.
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class BaseSQLQueryBuilder<TFields extends Record<string, any>, TSelectedFields extends keyof TFields, TResultType> {
|
|
8
|
+
private readonly sqlFragment;
|
|
9
|
+
private readonly modifiers;
|
|
10
|
+
private executed;
|
|
11
|
+
constructor(sqlFragment: SQLFragment<Pick<TFields, TSelectedFields>>, modifiers: {
|
|
12
|
+
limit?: number;
|
|
13
|
+
offset?: number;
|
|
14
|
+
orderBy?: readonly EntityLoaderOrderByClause<TFields, TSelectedFields>[];
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Limit the number of results
|
|
18
|
+
*/
|
|
19
|
+
limit(n: number): this;
|
|
20
|
+
/**
|
|
21
|
+
* Skip a number of results
|
|
22
|
+
*/
|
|
23
|
+
offset(n: number): this;
|
|
24
|
+
/**
|
|
25
|
+
* Order by a field. Can be called multiple times to add multiple order bys.
|
|
26
|
+
*/
|
|
27
|
+
orderBy(fieldName: TSelectedFields, order?: OrderByOrdering, nulls?: NullsOrdering | undefined): this;
|
|
28
|
+
/**
|
|
29
|
+
* Order by a SQL fragment expression.
|
|
30
|
+
* Provides type-safe, parameterized ORDER BY clauses
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* query.orderByFragment(
|
|
35
|
+
* sql`(data->>'createdAt')::timestamp`,
|
|
36
|
+
* OrderByOrdering.DESCENDING,
|
|
37
|
+
* );
|
|
38
|
+
* // Generates ORDER BY clause that orders by the createdAt field in the JSONB data column, cast to a timestamp, in descending order.
|
|
39
|
+
* // Note that the SQL fragment is parameterized, so it is safe from SQL injection.
|
|
40
|
+
* // The generated SQL would look like: ORDER BY (data->>'createdAt')::timestamp DESC
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @param fragment - The SQL fragment to order by. Must not include the ASC/DESC keyword, as ordering direction is determined by the `order` parameter.
|
|
44
|
+
* @param order - The ordering direction (ascending or descending). Defaults to ascending.
|
|
45
|
+
*/
|
|
46
|
+
orderBySQL(fragment: SQLFragment<Pick<TFields, TSelectedFields>>, order?: OrderByOrdering, nulls?: NullsOrdering | undefined): this;
|
|
47
|
+
/**
|
|
48
|
+
* Get the current modifiers as QuerySelectionModifiersWithOrderByFragment<TFields>
|
|
49
|
+
*/
|
|
50
|
+
protected getModifiers(): EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>;
|
|
51
|
+
/**
|
|
52
|
+
* Get the SQL fragment
|
|
53
|
+
*/
|
|
54
|
+
protected getSQLFragment(): SQLFragment<Pick<TFields, TSelectedFields>>;
|
|
55
|
+
/**
|
|
56
|
+
* Execute the query and return results.
|
|
57
|
+
* Implementation depends on the specific loader type.
|
|
58
|
+
*/
|
|
59
|
+
executeAsync(): Promise<readonly TResultType[]>;
|
|
60
|
+
protected abstract executeInternalAsync(): Promise<readonly TResultType[]>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseSQLQueryBuilder = void 0;
|
|
4
|
+
const BasePostgresEntityDatabaseAdapter_1 = require("./BasePostgresEntityDatabaseAdapter");
|
|
5
|
+
/**
|
|
6
|
+
* Base SQL query builder that provides common functionality for building SQL queries.
|
|
7
|
+
*/
|
|
8
|
+
class BaseSQLQueryBuilder {
|
|
9
|
+
sqlFragment;
|
|
10
|
+
modifiers;
|
|
11
|
+
executed = false;
|
|
12
|
+
constructor(sqlFragment, modifiers) {
|
|
13
|
+
this.sqlFragment = sqlFragment;
|
|
14
|
+
this.modifiers = modifiers;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Limit the number of results
|
|
18
|
+
*/
|
|
19
|
+
limit(n) {
|
|
20
|
+
this.modifiers.limit = n;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Skip a number of results
|
|
25
|
+
*/
|
|
26
|
+
offset(n) {
|
|
27
|
+
this.modifiers.offset = n;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Order by a field. Can be called multiple times to add multiple order bys.
|
|
32
|
+
*/
|
|
33
|
+
orderBy(fieldName, order = BasePostgresEntityDatabaseAdapter_1.OrderByOrdering.ASCENDING, nulls = undefined) {
|
|
34
|
+
this.modifiers.orderBy = [...(this.modifiers.orderBy ?? []), { fieldName, order, nulls }];
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Order by a SQL fragment expression.
|
|
39
|
+
* Provides type-safe, parameterized ORDER BY clauses
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* query.orderByFragment(
|
|
44
|
+
* sql`(data->>'createdAt')::timestamp`,
|
|
45
|
+
* OrderByOrdering.DESCENDING,
|
|
46
|
+
* );
|
|
47
|
+
* // Generates ORDER BY clause that orders by the createdAt field in the JSONB data column, cast to a timestamp, in descending order.
|
|
48
|
+
* // Note that the SQL fragment is parameterized, so it is safe from SQL injection.
|
|
49
|
+
* // The generated SQL would look like: ORDER BY (data->>'createdAt')::timestamp DESC
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @param fragment - The SQL fragment to order by. Must not include the ASC/DESC keyword, as ordering direction is determined by the `order` parameter.
|
|
53
|
+
* @param order - The ordering direction (ascending or descending). Defaults to ascending.
|
|
54
|
+
*/
|
|
55
|
+
orderBySQL(fragment, order = BasePostgresEntityDatabaseAdapter_1.OrderByOrdering.ASCENDING, nulls = undefined) {
|
|
56
|
+
this.modifiers.orderBy = [
|
|
57
|
+
...(this.modifiers.orderBy ?? []),
|
|
58
|
+
{ fieldFragment: fragment, order, nulls },
|
|
59
|
+
];
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the current modifiers as QuerySelectionModifiersWithOrderByFragment<TFields>
|
|
64
|
+
*/
|
|
65
|
+
getModifiers() {
|
|
66
|
+
return this.modifiers;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the SQL fragment
|
|
70
|
+
*/
|
|
71
|
+
getSQLFragment() {
|
|
72
|
+
return this.sqlFragment;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Execute the query and return results.
|
|
76
|
+
* Implementation depends on the specific loader type.
|
|
77
|
+
*/
|
|
78
|
+
async executeAsync() {
|
|
79
|
+
if (this.executed) {
|
|
80
|
+
throw new Error('Query has already been executed. Create a new query builder to execute again.');
|
|
81
|
+
}
|
|
82
|
+
this.executed = true;
|
|
83
|
+
return await this.executeInternalAsync();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.BaseSQLQueryBuilder = BaseSQLQueryBuilder;
|
|
87
|
+
//# sourceMappingURL=BaseSQLQueryBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseSQLQueryBuilder.js","sourceRoot":"","sources":["../../src/BaseSQLQueryBuilder.ts"],"names":[],"mappings":";;;AAIA,2FAAqF;AAGrF;;GAEG;AACH,MAAsB,mBAAmB;IAQpB;IACA;IAJX,QAAQ,GAAG,KAAK,CAAC;IAEzB,YACmB,WAAwD,EACxD,SAIhB;QALgB,gBAAW,GAAX,WAAW,CAA6C;QACxD,cAAS,GAAT,SAAS,CAIzB;IACA,CAAC;IAEJ;;OAEG;IACH,KAAK,CAAC,CAAS;QACb,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,CAAS;QACd,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,OAAO,CACL,SAA0B,EAC1B,QAAyB,mDAAe,CAAC,SAAS,EAClD,QAAmC,SAAS;QAE5C,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1F,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,UAAU,CACR,QAAqD,EACrD,QAAyB,mDAAe,CAAC,SAAS,EAClD,QAAmC,SAAS;QAE5C,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG;YACvB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC;YACjC,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE;SAC1C,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACO,YAAY;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;OAEG;IACO,cAAc;QACtB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,YAAY;QACvB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,OAAO,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC3C,CAAC;CAGF;AAvGD,kDAuGC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { EntityConstructionUtils, EntityPrivacyPolicy, EntityQueryContext, IEntityMetricsAdapter, ReadonlyEntity, ViewerContext } from '@expo/entity';
|
|
2
|
+
import { AuthorizationResultBasedKnexEntityLoader, EntityLoaderLoadPageArgs, EntityLoaderQuerySelectionModifiers } from './AuthorizationResultBasedKnexEntityLoader';
|
|
3
|
+
import { FieldEqualityCondition } from './BasePostgresEntityDatabaseAdapter';
|
|
4
|
+
import { BaseSQLQueryBuilder } from './BaseSQLQueryBuilder';
|
|
5
|
+
import { SQLFragment } from './SQLOperator';
|
|
6
|
+
import type { Connection, EntityKnexDataManager } from './internal/EntityKnexDataManager';
|
|
7
|
+
/**
|
|
8
|
+
* Enforcing knex entity loader for non-data-loader-based load methods.
|
|
9
|
+
* All loads through this loader will throw if the load is not successful.
|
|
10
|
+
*/
|
|
11
|
+
export declare class EnforcingKnexEntityLoader<TFields extends Record<string, any>, TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>, TViewerContext extends ViewerContext, TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>, TPrivacyPolicy extends EntityPrivacyPolicy<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>, TSelectedFields extends keyof TFields> {
|
|
12
|
+
private readonly knexEntityLoader;
|
|
13
|
+
private readonly queryContext;
|
|
14
|
+
private readonly knexDataManager;
|
|
15
|
+
protected readonly metricsAdapter: IEntityMetricsAdapter;
|
|
16
|
+
private readonly constructionUtils;
|
|
17
|
+
constructor(knexEntityLoader: AuthorizationResultBasedKnexEntityLoader<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>, queryContext: EntityQueryContext, knexDataManager: EntityKnexDataManager<TFields, TIDField>, metricsAdapter: IEntityMetricsAdapter, constructionUtils: EntityConstructionUtils<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>);
|
|
18
|
+
/**
|
|
19
|
+
* Load the first entity matching the conjunction of field equality operands and
|
|
20
|
+
* query modifiers.
|
|
21
|
+
*
|
|
22
|
+
* This is a convenience method for {@link loadManyByFieldEqualityConjunctionAsync}. However, the
|
|
23
|
+
* orderBy query modifier is required to ensure consistent results if more than one entity matches
|
|
24
|
+
* the filters.
|
|
25
|
+
*
|
|
26
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view the entity
|
|
27
|
+
* @returns the first entity matching the filters, or null if none match
|
|
28
|
+
*/
|
|
29
|
+
loadFirstByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(fieldEqualityOperands: FieldEqualityCondition<TFields, N>[], querySelectionModifiers: Omit<EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>, 'limit'> & Required<Pick<EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>, 'orderBy'>>): Promise<TEntity | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Load entities matching the conjunction of field equality operands and
|
|
32
|
+
* query modifiers.
|
|
33
|
+
*
|
|
34
|
+
* Typically this is used for complex queries that cannot be expressed through simpler
|
|
35
|
+
* convenience methods such as {@link loadManyByFieldEqualingAsync}.
|
|
36
|
+
*
|
|
37
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view the entity
|
|
38
|
+
* @returns entities matching the filters
|
|
39
|
+
*/
|
|
40
|
+
loadManyByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(fieldEqualityOperands: FieldEqualityCondition<TFields, N>[], querySelectionModifiers?: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>): Promise<readonly TEntity[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Load entities with a raw SQL WHERE clause.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* Load entities with SQL function
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const entitiesWithJsonKey = await ExampleEntity.loader(vc)
|
|
48
|
+
* .loadManyByRawWhereClauseAsync(
|
|
49
|
+
* "json_column->>'key_name' = ?",
|
|
50
|
+
* ['value'],
|
|
51
|
+
* );
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* Load entities with tuple matching
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const entities = await ExampleEntity.loader(vc)
|
|
58
|
+
* .loadManyByRawWhereClauseAsync(
|
|
59
|
+
* '(column_1, column_2) IN ((?, ?), (?, ?))',
|
|
60
|
+
* [value1, value2, value3, value4],
|
|
61
|
+
* );
|
|
62
|
+
* ```
|
|
63
|
+
* @param rawWhereClause - SQL WHERE clause. Interpolated values should be specified as ?-placeholders or :key_name
|
|
64
|
+
* @param bindings - values to bind to the placeholders in the WHERE clause
|
|
65
|
+
* @param querySelectionModifiers - limit, offset, and orderBy for the query.
|
|
66
|
+
* @returns entities matching the WHERE clause
|
|
67
|
+
* @throws EntityNotAuthorizedError when viewer is not authorized to view one or more of the returned entities
|
|
68
|
+
* @throws Error when rawWhereClause or bindings are invalid
|
|
69
|
+
*
|
|
70
|
+
* @deprecated Use loadManyBySQL instead for safer value bindings and more flexible query building.
|
|
71
|
+
*/
|
|
72
|
+
loadManyByRawWhereClauseAsync(rawWhereClause: string, bindings: any[] | object, querySelectionModifiers?: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>): Promise<readonly TEntity[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Load entities using a SQL query builder. When executed, all queries will enforce authorization and throw if not authorized.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* const entities = await ExampleEntity.loader(vc)
|
|
79
|
+
* .loadManyBySQL(sql`age >= ${18} AND status = ${'active'}`)
|
|
80
|
+
* .orderBy('createdAt', 'DESC')
|
|
81
|
+
* .limit(10)
|
|
82
|
+
* .executeAsync();
|
|
83
|
+
*
|
|
84
|
+
* const { between, inArray } = SQLFragmentHelpers;
|
|
85
|
+
* const filtered = await ExampleEntity.loader(vc)
|
|
86
|
+
* .loadManyBySQL(
|
|
87
|
+
* sql`${between('age', 18, 65)} AND ${inArray('role', ['admin', 'moderator'])}`
|
|
88
|
+
* )
|
|
89
|
+
* .executeAsync();
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
loadManyBySQL(fragment: SQLFragment<Pick<TFields, TSelectedFields>>, modifiers?: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>): EnforcingSQLQueryBuilder<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>;
|
|
93
|
+
/**
|
|
94
|
+
* Load a page of entities with Relay-style cursor pagination.
|
|
95
|
+
*
|
|
96
|
+
* @param args - Pagination arguments with pagination and either first/after or last/before
|
|
97
|
+
* @returns a page of entities matching the pagination arguments
|
|
98
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view any returned entity
|
|
99
|
+
*/
|
|
100
|
+
loadPageAsync(args: EntityLoaderLoadPageArgs<TFields, TSelectedFields>): Promise<Connection<TEntity>>;
|
|
101
|
+
/**
|
|
102
|
+
* Get cursor for a given entity that matches what loadPageAsync would produce.
|
|
103
|
+
* Useful for constructing pagination cursors for entities returned from other loader methods that can then be passed to loadPageAsync for pagination.
|
|
104
|
+
* Most commonly used for testing pagination behavior.
|
|
105
|
+
*
|
|
106
|
+
* @param entity - The entity to get the pagination cursor for.
|
|
107
|
+
* @returns The pagination cursor for the given entity.
|
|
108
|
+
*/
|
|
109
|
+
getPaginationCursorForEntity(entity: TEntity): string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* SQL query builder for EnforcingKnexEntityLoader.
|
|
113
|
+
* Provides a fluent API for building and executing SQL queries with enforced authorization.
|
|
114
|
+
*/
|
|
115
|
+
export declare class EnforcingSQLQueryBuilder<TFields extends Record<string, any>, TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>, TViewerContext extends ViewerContext, TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>, TPrivacyPolicy extends EntityPrivacyPolicy<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>, TSelectedFields extends keyof TFields> extends BaseSQLQueryBuilder<TFields, TSelectedFields, TEntity> {
|
|
116
|
+
private readonly knexEntityLoader;
|
|
117
|
+
constructor(knexEntityLoader: AuthorizationResultBasedKnexEntityLoader<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>, sqlFragment: SQLFragment<Pick<TFields, TSelectedFields>>, modifiers: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>);
|
|
118
|
+
/**
|
|
119
|
+
* Execute the query.
|
|
120
|
+
* @returns entities matching the query
|
|
121
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view any entity
|
|
122
|
+
*/
|
|
123
|
+
executeInternalAsync(): Promise<readonly TEntity[]>;
|
|
124
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnforcingSQLQueryBuilder = exports.EnforcingKnexEntityLoader = void 0;
|
|
4
|
+
const BaseSQLQueryBuilder_1 = require("./BaseSQLQueryBuilder");
|
|
5
|
+
/**
|
|
6
|
+
* Enforcing knex entity loader for non-data-loader-based load methods.
|
|
7
|
+
* All loads through this loader will throw if the load is not successful.
|
|
8
|
+
*/
|
|
9
|
+
class EnforcingKnexEntityLoader {
|
|
10
|
+
knexEntityLoader;
|
|
11
|
+
queryContext;
|
|
12
|
+
knexDataManager;
|
|
13
|
+
metricsAdapter;
|
|
14
|
+
constructionUtils;
|
|
15
|
+
constructor(knexEntityLoader, queryContext, knexDataManager, metricsAdapter, constructionUtils) {
|
|
16
|
+
this.knexEntityLoader = knexEntityLoader;
|
|
17
|
+
this.queryContext = queryContext;
|
|
18
|
+
this.knexDataManager = knexDataManager;
|
|
19
|
+
this.metricsAdapter = metricsAdapter;
|
|
20
|
+
this.constructionUtils = constructionUtils;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load the first entity matching the conjunction of field equality operands and
|
|
24
|
+
* query modifiers.
|
|
25
|
+
*
|
|
26
|
+
* This is a convenience method for {@link loadManyByFieldEqualityConjunctionAsync}. However, the
|
|
27
|
+
* orderBy query modifier is required to ensure consistent results if more than one entity matches
|
|
28
|
+
* the filters.
|
|
29
|
+
*
|
|
30
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view the entity
|
|
31
|
+
* @returns the first entity matching the filters, or null if none match
|
|
32
|
+
*/
|
|
33
|
+
async loadFirstByFieldEqualityConjunctionAsync(fieldEqualityOperands, querySelectionModifiers) {
|
|
34
|
+
const entityResult = await this.knexEntityLoader.loadFirstByFieldEqualityConjunctionAsync(fieldEqualityOperands, querySelectionModifiers);
|
|
35
|
+
return entityResult?.enforceValue() ?? null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load entities matching the conjunction of field equality operands and
|
|
39
|
+
* query modifiers.
|
|
40
|
+
*
|
|
41
|
+
* Typically this is used for complex queries that cannot be expressed through simpler
|
|
42
|
+
* convenience methods such as {@link loadManyByFieldEqualingAsync}.
|
|
43
|
+
*
|
|
44
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view the entity
|
|
45
|
+
* @returns entities matching the filters
|
|
46
|
+
*/
|
|
47
|
+
async loadManyByFieldEqualityConjunctionAsync(fieldEqualityOperands, querySelectionModifiers = {}) {
|
|
48
|
+
const entityResults = await this.knexEntityLoader.loadManyByFieldEqualityConjunctionAsync(fieldEqualityOperands, querySelectionModifiers);
|
|
49
|
+
return entityResults.map((result) => result.enforceValue());
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Load entities with a raw SQL WHERE clause.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* Load entities with SQL function
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const entitiesWithJsonKey = await ExampleEntity.loader(vc)
|
|
58
|
+
* .loadManyByRawWhereClauseAsync(
|
|
59
|
+
* "json_column->>'key_name' = ?",
|
|
60
|
+
* ['value'],
|
|
61
|
+
* );
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* Load entities with tuple matching
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const entities = await ExampleEntity.loader(vc)
|
|
68
|
+
* .loadManyByRawWhereClauseAsync(
|
|
69
|
+
* '(column_1, column_2) IN ((?, ?), (?, ?))',
|
|
70
|
+
* [value1, value2, value3, value4],
|
|
71
|
+
* );
|
|
72
|
+
* ```
|
|
73
|
+
* @param rawWhereClause - SQL WHERE clause. Interpolated values should be specified as ?-placeholders or :key_name
|
|
74
|
+
* @param bindings - values to bind to the placeholders in the WHERE clause
|
|
75
|
+
* @param querySelectionModifiers - limit, offset, and orderBy for the query.
|
|
76
|
+
* @returns entities matching the WHERE clause
|
|
77
|
+
* @throws EntityNotAuthorizedError when viewer is not authorized to view one or more of the returned entities
|
|
78
|
+
* @throws Error when rawWhereClause or bindings are invalid
|
|
79
|
+
*
|
|
80
|
+
* @deprecated Use loadManyBySQL instead for safer value bindings and more flexible query building.
|
|
81
|
+
*/
|
|
82
|
+
async loadManyByRawWhereClauseAsync(rawWhereClause, bindings, querySelectionModifiers = {}) {
|
|
83
|
+
const entityResults = await this.knexEntityLoader.loadManyByRawWhereClauseAsync(rawWhereClause, bindings, querySelectionModifiers);
|
|
84
|
+
return entityResults.map((result) => result.enforceValue());
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Load entities using a SQL query builder. When executed, all queries will enforce authorization and throw if not authorized.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const entities = await ExampleEntity.loader(vc)
|
|
92
|
+
* .loadManyBySQL(sql`age >= ${18} AND status = ${'active'}`)
|
|
93
|
+
* .orderBy('createdAt', 'DESC')
|
|
94
|
+
* .limit(10)
|
|
95
|
+
* .executeAsync();
|
|
96
|
+
*
|
|
97
|
+
* const { between, inArray } = SQLFragmentHelpers;
|
|
98
|
+
* const filtered = await ExampleEntity.loader(vc)
|
|
99
|
+
* .loadManyBySQL(
|
|
100
|
+
* sql`${between('age', 18, 65)} AND ${inArray('role', ['admin', 'moderator'])}`
|
|
101
|
+
* )
|
|
102
|
+
* .executeAsync();
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
loadManyBySQL(fragment, modifiers = {}) {
|
|
106
|
+
return new EnforcingSQLQueryBuilder(this.knexEntityLoader, fragment, modifiers);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Load a page of entities with Relay-style cursor pagination.
|
|
110
|
+
*
|
|
111
|
+
* @param args - Pagination arguments with pagination and either first/after or last/before
|
|
112
|
+
* @returns a page of entities matching the pagination arguments
|
|
113
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view any returned entity
|
|
114
|
+
*/
|
|
115
|
+
async loadPageAsync(args) {
|
|
116
|
+
const pageResult = await this.knexDataManager.loadPageAsync(this.queryContext, args);
|
|
117
|
+
const edges = await Promise.all(pageResult.edges.map(async (edge) => {
|
|
118
|
+
const entityResult = await this.constructionUtils.constructAndAuthorizeEntityAsync(edge.node);
|
|
119
|
+
const entity = entityResult.enforceValue();
|
|
120
|
+
return {
|
|
121
|
+
...edge,
|
|
122
|
+
node: entity,
|
|
123
|
+
};
|
|
124
|
+
}));
|
|
125
|
+
return {
|
|
126
|
+
edges,
|
|
127
|
+
pageInfo: pageResult.pageInfo,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get cursor for a given entity that matches what loadPageAsync would produce.
|
|
132
|
+
* Useful for constructing pagination cursors for entities returned from other loader methods that can then be passed to loadPageAsync for pagination.
|
|
133
|
+
* Most commonly used for testing pagination behavior.
|
|
134
|
+
*
|
|
135
|
+
* @param entity - The entity to get the pagination cursor for.
|
|
136
|
+
* @returns The pagination cursor for the given entity.
|
|
137
|
+
*/
|
|
138
|
+
getPaginationCursorForEntity(entity) {
|
|
139
|
+
return this.knexEntityLoader.getPaginationCursorForEntity(entity);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.EnforcingKnexEntityLoader = EnforcingKnexEntityLoader;
|
|
143
|
+
/**
|
|
144
|
+
* SQL query builder for EnforcingKnexEntityLoader.
|
|
145
|
+
* Provides a fluent API for building and executing SQL queries with enforced authorization.
|
|
146
|
+
*/
|
|
147
|
+
class EnforcingSQLQueryBuilder extends BaseSQLQueryBuilder_1.BaseSQLQueryBuilder {
|
|
148
|
+
knexEntityLoader;
|
|
149
|
+
constructor(knexEntityLoader, sqlFragment, modifiers) {
|
|
150
|
+
super(sqlFragment, modifiers);
|
|
151
|
+
this.knexEntityLoader = knexEntityLoader;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Execute the query.
|
|
155
|
+
* @returns entities matching the query
|
|
156
|
+
* @throws EntityNotAuthorizedError if viewer is not authorized to view any entity
|
|
157
|
+
*/
|
|
158
|
+
async executeInternalAsync() {
|
|
159
|
+
const entityResults = await this.knexEntityLoader
|
|
160
|
+
.loadManyBySQL(this.getSQLFragment(), this.getModifiers())
|
|
161
|
+
.executeAsync();
|
|
162
|
+
return entityResults.map((result) => result.enforceValue());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
exports.EnforcingSQLQueryBuilder = EnforcingSQLQueryBuilder;
|
|
166
|
+
//# sourceMappingURL=EnforcingKnexEntityLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnforcingKnexEntityLoader.js","sourceRoot":"","sources":["../../src/EnforcingKnexEntityLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EntityCompanion, EntityPrivacyPolicy, EntityPrivacyPolicyEvaluationContext, EntityQueryContext, ReadonlyEntity, ViewerContext, IEntityMetricsAdapter } from '@expo/entity';
|
|
2
|
+
import { AuthorizationResultBasedKnexEntityLoader } from './AuthorizationResultBasedKnexEntityLoader';
|
|
3
|
+
import { EnforcingKnexEntityLoader } from './EnforcingKnexEntityLoader';
|
|
4
|
+
import { EntityKnexDataManager } from './internal/EntityKnexDataManager';
|
|
5
|
+
/**
|
|
6
|
+
* The primary entry point for loading entities via knex queries (non-data-loader methods).
|
|
7
|
+
*/
|
|
8
|
+
export declare class KnexEntityLoaderFactory<TFields extends Record<string, any>, TIDField extends keyof NonNullable<Pick<TFields, TSelectedFields>>, TViewerContext extends ViewerContext, TEntity extends ReadonlyEntity<TFields, TIDField, TViewerContext, TSelectedFields>, TPrivacyPolicy extends EntityPrivacyPolicy<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>, TSelectedFields extends keyof TFields> {
|
|
9
|
+
private readonly entityCompanion;
|
|
10
|
+
private readonly knexDataManager;
|
|
11
|
+
protected readonly metricsAdapter: IEntityMetricsAdapter;
|
|
12
|
+
constructor(entityCompanion: EntityCompanion<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>, knexDataManager: EntityKnexDataManager<TFields, TIDField>, metricsAdapter: IEntityMetricsAdapter);
|
|
13
|
+
/**
|
|
14
|
+
* Vend knex loader for loading an entity in a given query context.
|
|
15
|
+
* @param viewerContext - viewer context of loading user
|
|
16
|
+
* @param queryContext - query context in which to perform the load
|
|
17
|
+
*/
|
|
18
|
+
forLoad(viewerContext: TViewerContext, queryContext: EntityQueryContext, privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>): AuthorizationResultBasedKnexEntityLoader<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>;
|
|
19
|
+
/**
|
|
20
|
+
* Vend enforcing knex loader for loading an entity in a given query context.
|
|
21
|
+
* @param viewerContext - viewer context of loading user
|
|
22
|
+
* @param queryContext - query context in which to perform the load
|
|
23
|
+
*/
|
|
24
|
+
forLoadEnforcing(viewerContext: TViewerContext, queryContext: EntityQueryContext, privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<TFields, TIDField, TViewerContext, TEntity, TSelectedFields>): EnforcingKnexEntityLoader<TFields, TIDField, TViewerContext, TEntity, TPrivacyPolicy, TSelectedFields>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KnexEntityLoaderFactory = void 0;
|
|
4
|
+
const entity_1 = require("@expo/entity");
|
|
5
|
+
const AuthorizationResultBasedKnexEntityLoader_1 = require("./AuthorizationResultBasedKnexEntityLoader");
|
|
6
|
+
const EnforcingKnexEntityLoader_1 = require("./EnforcingKnexEntityLoader");
|
|
7
|
+
/**
|
|
8
|
+
* The primary entry point for loading entities via knex queries (non-data-loader methods).
|
|
9
|
+
*/
|
|
10
|
+
class KnexEntityLoaderFactory {
|
|
11
|
+
entityCompanion;
|
|
12
|
+
knexDataManager;
|
|
13
|
+
metricsAdapter;
|
|
14
|
+
constructor(entityCompanion, knexDataManager, metricsAdapter) {
|
|
15
|
+
this.entityCompanion = entityCompanion;
|
|
16
|
+
this.knexDataManager = knexDataManager;
|
|
17
|
+
this.metricsAdapter = metricsAdapter;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Vend knex loader for loading an entity in a given query context.
|
|
21
|
+
* @param viewerContext - viewer context of loading user
|
|
22
|
+
* @param queryContext - query context in which to perform the load
|
|
23
|
+
*/
|
|
24
|
+
forLoad(viewerContext, queryContext, privacyPolicyEvaluationContext) {
|
|
25
|
+
const constructionUtils = new entity_1.EntityConstructionUtils(viewerContext, queryContext, privacyPolicyEvaluationContext, this.entityCompanion.entityCompanionDefinition.entityConfiguration, this.entityCompanion.entityCompanionDefinition.entityClass, this.entityCompanion.entityCompanionDefinition.entitySelectedFields, this.entityCompanion.privacyPolicy, this.metricsAdapter);
|
|
26
|
+
return new AuthorizationResultBasedKnexEntityLoader_1.AuthorizationResultBasedKnexEntityLoader(queryContext, this.knexDataManager, this.metricsAdapter, constructionUtils);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Vend enforcing knex loader for loading an entity in a given query context.
|
|
30
|
+
* @param viewerContext - viewer context of loading user
|
|
31
|
+
* @param queryContext - query context in which to perform the load
|
|
32
|
+
*/
|
|
33
|
+
forLoadEnforcing(viewerContext, queryContext, privacyPolicyEvaluationContext) {
|
|
34
|
+
const constructionUtils = new entity_1.EntityConstructionUtils(viewerContext, queryContext, privacyPolicyEvaluationContext, this.entityCompanion.entityCompanionDefinition.entityConfiguration, this.entityCompanion.entityCompanionDefinition.entityClass, this.entityCompanion.entityCompanionDefinition.entitySelectedFields, this.entityCompanion.privacyPolicy, this.metricsAdapter);
|
|
35
|
+
return new EnforcingKnexEntityLoader_1.EnforcingKnexEntityLoader(this.forLoad(viewerContext, queryContext, privacyPolicyEvaluationContext), queryContext, this.knexDataManager, this.metricsAdapter, constructionUtils);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.KnexEntityLoaderFactory = KnexEntityLoaderFactory;
|
|
39
|
+
//# sourceMappingURL=KnexEntityLoaderFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KnexEntityLoaderFactory.js","sourceRoot":"","sources":["../../src/KnexEntityLoaderFactory.ts"],"names":[],"mappings":";;;AAAA,yCASsB;AAEtB,yGAAsG;AACtG,2EAAwE;AAGxE;;GAEG;AACH,MAAa,uBAAuB;IAef;IAQA;IACE;IAVrB,YACmB,eAOhB,EACgB,eAAyD,EACvD,cAAqC;QATvC,oBAAe,GAAf,eAAe,CAO/B;QACgB,oBAAe,GAAf,eAAe,CAA0C;QACvD,mBAAc,GAAd,cAAc,CAAuB;IACvD,CAAC;IAEJ;;;;OAIG;IACH,OAAO,CACL,aAA6B,EAC7B,YAAgC,EAChC,8BAMC;QASD,MAAM,iBAAiB,GAAG,IAAI,gCAAuB,CACnD,aAAa,EACb,YAAY,EACZ,8BAA8B,EAC9B,IAAI,CAAC,eAAe,CAAC,yBAAyB,CAAC,mBAAmB,EAClE,IAAI,CAAC,eAAe,CAAC,yBAAyB,CAAC,WAAW,EAC1D,IAAI,CAAC,eAAe,CAAC,yBAAyB,CAAC,oBAAoB,EACnE,IAAI,CAAC,eAAe,CAAC,aAAa,EAClC,IAAI,CAAC,cAAc,CACpB,CAAC;QAEF,OAAO,IAAI,mFAAwC,CACjD,YAAY,EACZ,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,cAAc,EACnB,iBAAiB,CAClB,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,gBAAgB,CACd,aAA6B,EAC7B,YAAgC,EAChC,8BAMC;QASD,MAAM,iBAAiB,GAAG,IAAI,gCAAuB,CACnD,aAAa,EACb,YAAY,EACZ,8BAA8B,EAC9B,IAAI,CAAC,eAAe,CAAC,yBAAyB,CAAC,mBAAmB,EAClE,IAAI,CAAC,eAAe,CAAC,yBAAyB,CAAC,WAAW,EAC1D,IAAI,CAAC,eAAe,CAAC,yBAAyB,CAAC,oBAAoB,EACnE,IAAI,CAAC,eAAe,CAAC,aAAa,EAClC,IAAI,CAAC,cAAc,CACpB,CAAC;QAEF,OAAO,IAAI,qDAAyB,CAClC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,EAAE,8BAA8B,CAAC,EACzE,YAAY,EACZ,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,cAAc,EACnB,iBAAiB,CAClB,CAAC;IACJ,CAAC;CACF;AA/GD,0DA+GC"}
|