@snowtop/ent 0.1.12 → 0.1.13
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/action/experimental_action.js +2 -2
- package/action/operations.js +4 -2
- package/core/base.d.ts +12 -10
- package/core/clause.d.ts +4 -3
- package/core/clause.js +98 -43
- package/core/config.d.ts +6 -0
- package/core/context.d.ts +6 -14
- package/core/context.js +9 -4
- package/core/db.js +1 -1
- package/core/ent.d.ts +1 -0
- package/core/ent.js +30 -11
- package/core/loaders/assoc_count_loader.js +1 -1
- package/core/loaders/assoc_edge_loader.d.ts +3 -0
- package/core/loaders/assoc_edge_loader.js +18 -0
- package/core/loaders/object_loader.js +2 -2
- package/core/loaders/query_loader.d.ts +3 -3
- package/core/loaders/query_loader.js +2 -1
- package/core/loaders/raw_count_loader.js +1 -1
- package/core/query/assoc_query.d.ts +22 -0
- package/core/query/assoc_query.js +101 -2
- package/core/query/custom_query.js +2 -9
- package/core/query/query.d.ts +1 -0
- package/core/query/query.js +72 -11
- package/core/query/shared_assoc_test.js +404 -7
- package/core/query/shared_test.js +9 -37
- package/core/query_impl.d.ts +2 -1
- package/core/query_impl.js +25 -7
- package/graphql/query/edge_connection.js +2 -2
- package/package.json +2 -2
- package/parse_schema/parse.d.ts +2 -2
- package/parse_schema/parse.js +3 -3
- package/schema/struct_field.d.ts +4 -2
- package/schema/struct_field.js +33 -4
- package/scripts/custom_graphql.js +1 -1
- package/testutils/builder.d.ts +1 -1
- package/testutils/builder.js +4 -4
- package/testutils/ent-graphql-tests/index.js +2 -2
- package/testutils/fake_data/fake_contact.js +1 -1
- package/testutils/fake_data/fake_event.js +1 -1
- package/testutils/fake_data/test_helpers.js +2 -2
- package/testutils/fake_data/user_query.js +1 -1
- package/testutils/query.d.ts +9 -0
- package/testutils/query.js +45 -0
- package/testutils/write.js +3 -3
|
@@ -64,11 +64,11 @@ class BaseAction {
|
|
|
64
64
|
}
|
|
65
65
|
async save() {
|
|
66
66
|
await this.builder.save();
|
|
67
|
-
return
|
|
67
|
+
return this.builder.editedEnt();
|
|
68
68
|
}
|
|
69
69
|
async saveX() {
|
|
70
70
|
await this.builder.saveX();
|
|
71
|
-
return
|
|
71
|
+
return this.builder.editedEntX();
|
|
72
72
|
}
|
|
73
73
|
getInput() {
|
|
74
74
|
return this.input;
|
package/action/operations.js
CHANGED
|
@@ -358,7 +358,7 @@ class EdgeOperation {
|
|
|
358
358
|
async performDeleteWrite(q, edgeData, edge, context) {
|
|
359
359
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
360
360
|
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
361
|
-
return (0, ent_1.deleteRows)(q, params.options, params.clause);
|
|
361
|
+
return (0, ent_1.deleteRows)(q, { ...params.options, context }, params.clause);
|
|
362
362
|
}
|
|
363
363
|
else {
|
|
364
364
|
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
@@ -369,13 +369,14 @@ class EdgeOperation {
|
|
|
369
369
|
whereClause: params.clause,
|
|
370
370
|
fields: params.updateData,
|
|
371
371
|
fieldsToLog: params.updateData,
|
|
372
|
+
context,
|
|
372
373
|
});
|
|
373
374
|
}
|
|
374
375
|
}
|
|
375
376
|
performDeleteWriteSync(q, edgeData, edge, context) {
|
|
376
377
|
const params = this.getDeleteRowParams(edgeData, edge, context);
|
|
377
378
|
if (params.op === schema_1.SQLStatementOperation.Delete) {
|
|
378
|
-
return (0, ent_1.deleteRowsSync)(q, params.options, params.clause);
|
|
379
|
+
return (0, ent_1.deleteRowsSync)(q, { ...params.options, context }, params.clause);
|
|
379
380
|
}
|
|
380
381
|
else {
|
|
381
382
|
if (params.op !== schema_1.SQLStatementOperation.Update) {
|
|
@@ -385,6 +386,7 @@ class EdgeOperation {
|
|
|
385
386
|
tableName: params.options.tableName,
|
|
386
387
|
whereClause: params.clause,
|
|
387
388
|
fields: params.updateData,
|
|
389
|
+
context,
|
|
388
390
|
});
|
|
389
391
|
}
|
|
390
392
|
}
|
package/core/base.d.ts
CHANGED
|
@@ -26,21 +26,16 @@ export interface PrimableLoader<K, V> extends Loader<K, V> {
|
|
|
26
26
|
prime(d: V): void;
|
|
27
27
|
primeAll?(d: V): void;
|
|
28
28
|
}
|
|
29
|
+
export type QueryOptions = Required<Pick<LoadRowsOptions, "clause" | "fields" | "tableName">> & Pick<LoadRowsOptions, "orderby" | "join">;
|
|
29
30
|
interface cache {
|
|
30
31
|
getLoader<K, V>(name: string, create: () => Loader<K, V>): Loader<K, V>;
|
|
31
32
|
getLoaderWithLoadMany<K, V>(name: string, create: () => LoaderWithLoadMany<K, V>): LoaderWithLoadMany<K, V>;
|
|
32
|
-
getCachedRows(options:
|
|
33
|
-
getCachedRow(options:
|
|
34
|
-
primeCache(options:
|
|
35
|
-
primeCache(options:
|
|
33
|
+
getCachedRows(options: QueryOptions): Data[] | null;
|
|
34
|
+
getCachedRow(options: QueryOptions): Data | null;
|
|
35
|
+
primeCache(options: QueryOptions, rows: Data[]): void;
|
|
36
|
+
primeCache(options: QueryOptions, rows: Data): void;
|
|
36
37
|
clearCache(): void;
|
|
37
38
|
}
|
|
38
|
-
interface queryOptions {
|
|
39
|
-
fields: string[];
|
|
40
|
-
tableName: string;
|
|
41
|
-
clause: clause.Clause;
|
|
42
|
-
orderby?: OrderBy;
|
|
43
|
-
}
|
|
44
39
|
export interface Context<TViewer extends Viewer = Viewer> {
|
|
45
40
|
getViewer(): TViewer;
|
|
46
41
|
cache?: cache;
|
|
@@ -67,6 +62,7 @@ export interface EntConstructor<TEnt extends Ent, TViewer extends Viewer = Viewe
|
|
|
67
62
|
export type ID = string | number;
|
|
68
63
|
export interface DataOptions {
|
|
69
64
|
tableName: string;
|
|
65
|
+
alias?: string;
|
|
70
66
|
context?: Context;
|
|
71
67
|
}
|
|
72
68
|
export interface SelectBaseDataOptions extends DataOptions {
|
|
@@ -79,6 +75,11 @@ export interface SelectDataOptions extends SelectBaseDataOptions {
|
|
|
79
75
|
}
|
|
80
76
|
export interface QueryableDataOptions extends SelectBaseDataOptions, QueryDataOptions {
|
|
81
77
|
}
|
|
78
|
+
interface JoinOptions<T2 extends Data = Data, K2 = keyof T2> {
|
|
79
|
+
tableName: string;
|
|
80
|
+
alias?: string;
|
|
81
|
+
clause: clause.Clause<T2, K2>;
|
|
82
|
+
}
|
|
82
83
|
export interface QueryDataOptions<T extends Data = Data, K = keyof T> {
|
|
83
84
|
distinct?: boolean;
|
|
84
85
|
clause: clause.Clause<T, K>;
|
|
@@ -86,6 +87,7 @@ export interface QueryDataOptions<T extends Data = Data, K = keyof T> {
|
|
|
86
87
|
groupby?: K;
|
|
87
88
|
limit?: number;
|
|
88
89
|
disableTransformations?: boolean;
|
|
90
|
+
join?: JoinOptions;
|
|
89
91
|
}
|
|
90
92
|
export interface LoadRowOptions extends QueryableDataOptions {
|
|
91
93
|
}
|
package/core/clause.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Data, ID, SelectDataOptions } from "./base";
|
|
2
2
|
export interface Clause<T extends Data = Data, K = keyof T> {
|
|
3
|
-
clause(idx: number): string;
|
|
3
|
+
clause(idx: number, alias?: string): string;
|
|
4
4
|
columns(): K[];
|
|
5
5
|
values(): any[];
|
|
6
6
|
instanceKey(): string;
|
|
@@ -19,7 +19,7 @@ export declare class inClause<T extends Data, K = keyof T> implements Clause<T,
|
|
|
19
19
|
protected op: InClauseOperator;
|
|
20
20
|
static getPostgresInClauseValuesThreshold(): number;
|
|
21
21
|
constructor(col: K, value: any[], type?: string);
|
|
22
|
-
clause(idx: number): string;
|
|
22
|
+
clause(idx: number, alias?: string): string;
|
|
23
23
|
columns(): K[];
|
|
24
24
|
values(): any[];
|
|
25
25
|
logValues(): any[];
|
|
@@ -124,5 +124,6 @@ export declare function Subtract<T extends Data, K = keyof T>(col: K, value: any
|
|
|
124
124
|
export declare function Multiply<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
|
|
125
125
|
export declare function Divide<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
|
|
126
126
|
export declare function Modulo<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
|
|
127
|
-
export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options:
|
|
127
|
+
export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Pick<SelectDataOptions, "clause">, cls: Clause<V, K>, checkIntersection?: boolean): Clause<V, K>;
|
|
128
|
+
export declare function Expression<T extends Data, K = keyof T>(expression: string): Clause<T, K>;
|
|
128
129
|
export {};
|
package/core/clause.js
CHANGED
|
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsSubQuery = exports.JSONKeyInList = exports.JSONBKeyInList = exports.JSONKeyExists = exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.DBTypeNotIn = exports.TextNotIn = exports.IntegerNotIn = exports.UuidNotIn = exports.DBTypeIn = exports.TextIn = exports.IntegerIn = exports.UuidIn = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.notInClause = exports.inClause = void 0;
|
|
27
|
-
exports.getCombinedClause = void 0;
|
|
27
|
+
exports.Expression = exports.getCombinedClause = void 0;
|
|
28
28
|
const db_1 = __importStar(require("./db"));
|
|
29
29
|
const query_impl_1 = require("./query_impl");
|
|
30
30
|
function isSensitive(val) {
|
|
@@ -38,6 +38,12 @@ function rawValue(val) {
|
|
|
38
38
|
}
|
|
39
39
|
return val;
|
|
40
40
|
}
|
|
41
|
+
function renderCol(col, alias) {
|
|
42
|
+
if (alias) {
|
|
43
|
+
return `${alias}.${col}`;
|
|
44
|
+
}
|
|
45
|
+
return col;
|
|
46
|
+
}
|
|
41
47
|
class simpleClause {
|
|
42
48
|
constructor(col, value, op, handleNull) {
|
|
43
49
|
this.col = col;
|
|
@@ -45,15 +51,15 @@ class simpleClause {
|
|
|
45
51
|
this.op = op;
|
|
46
52
|
this.handleNull = handleNull;
|
|
47
53
|
}
|
|
48
|
-
clause(idx) {
|
|
54
|
+
clause(idx, alias) {
|
|
49
55
|
const nullClause = this.nullClause();
|
|
50
56
|
if (nullClause) {
|
|
51
|
-
return nullClause.clause(idx);
|
|
57
|
+
return nullClause.clause(idx, alias);
|
|
52
58
|
}
|
|
53
59
|
if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
|
|
54
|
-
return `${this.col} ${this.op} $${idx}`;
|
|
60
|
+
return `${renderCol(this.col, alias)} ${this.op} $${idx}`;
|
|
55
61
|
}
|
|
56
|
-
return `${this.col} ${this.op} ?`;
|
|
62
|
+
return `${renderCol(this.col, alias)} ${this.op} ?`;
|
|
57
63
|
}
|
|
58
64
|
nullClause() {
|
|
59
65
|
if (!this.handleNull || this.value !== null) {
|
|
@@ -92,13 +98,17 @@ class simpleClause {
|
|
|
92
98
|
return `${this.col}${this.op}${rawValue(this.value)}`;
|
|
93
99
|
}
|
|
94
100
|
}
|
|
101
|
+
// NB: we're not using alias in this class in clause method
|
|
102
|
+
// if we end up with a subclass that does, we need to handle it
|
|
95
103
|
class queryClause {
|
|
96
|
-
constructor(dependentQueryOptions
|
|
104
|
+
constructor(dependentQueryOptions, // private value: any, // private op: string, // private handleNull?: Clause<T, K>,
|
|
105
|
+
prefix) {
|
|
97
106
|
this.dependentQueryOptions = dependentQueryOptions;
|
|
107
|
+
this.prefix = prefix;
|
|
98
108
|
}
|
|
99
|
-
clause(idx) {
|
|
109
|
+
clause(idx, alias) {
|
|
100
110
|
const q = (0, query_impl_1.buildQuery)(this.dependentQueryOptions);
|
|
101
|
-
return
|
|
111
|
+
return `${this.prefix} (${q})`;
|
|
102
112
|
}
|
|
103
113
|
columns() {
|
|
104
114
|
// @ts-ignore
|
|
@@ -111,15 +121,21 @@ class queryClause {
|
|
|
111
121
|
return this.dependentQueryOptions.clause.logValues();
|
|
112
122
|
}
|
|
113
123
|
instanceKey() {
|
|
114
|
-
return
|
|
124
|
+
return `${this.prefix.toLowerCase()}:${this.dependentQueryOptions.tableName}:${this.dependentQueryOptions.clause.instanceKey()}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
class existsQueryClause extends queryClause {
|
|
128
|
+
constructor(dependentQueryOptions) {
|
|
129
|
+
super(dependentQueryOptions, "EXISTS");
|
|
130
|
+
this.dependentQueryOptions = dependentQueryOptions;
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
class isNullClause {
|
|
118
134
|
constructor(col) {
|
|
119
135
|
this.col = col;
|
|
120
136
|
}
|
|
121
|
-
clause(_idx) {
|
|
122
|
-
return `${this.col} IS NULL`;
|
|
137
|
+
clause(_idx, alias) {
|
|
138
|
+
return `${renderCol(this.col, alias)} IS NULL`;
|
|
123
139
|
}
|
|
124
140
|
columns() {
|
|
125
141
|
return [];
|
|
@@ -138,8 +154,8 @@ class isNotNullClause {
|
|
|
138
154
|
constructor(col) {
|
|
139
155
|
this.col = col;
|
|
140
156
|
}
|
|
141
|
-
clause(idx) {
|
|
142
|
-
return `${this.col} IS NOT NULL`;
|
|
157
|
+
clause(idx, alias) {
|
|
158
|
+
return `${renderCol(this.col, alias)} IS NOT NULL`;
|
|
143
159
|
}
|
|
144
160
|
columns() {
|
|
145
161
|
return [];
|
|
@@ -154,17 +170,37 @@ class isNotNullClause {
|
|
|
154
170
|
return `${this.col} IS NOT NULL`;
|
|
155
171
|
}
|
|
156
172
|
}
|
|
173
|
+
class simpleExpression {
|
|
174
|
+
constructor(expression) {
|
|
175
|
+
this.expression = expression;
|
|
176
|
+
}
|
|
177
|
+
clause(idx, alias) {
|
|
178
|
+
return this.expression;
|
|
179
|
+
}
|
|
180
|
+
columns() {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
values() {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
logValues() {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
instanceKey() {
|
|
190
|
+
return `${this.expression}`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
157
193
|
class arraySimpleClause {
|
|
158
194
|
constructor(col, value, op) {
|
|
159
195
|
this.col = col;
|
|
160
196
|
this.value = value;
|
|
161
197
|
this.op = op;
|
|
162
198
|
}
|
|
163
|
-
clause(idx) {
|
|
199
|
+
clause(idx, alias) {
|
|
164
200
|
if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
|
|
165
|
-
return `$${idx} ${this.op} ANY(${this.col})`;
|
|
201
|
+
return `$${idx} ${this.op} ANY(${renderCol(this.col, alias)})`;
|
|
166
202
|
}
|
|
167
|
-
return `${this.col} ${this.op} ?`;
|
|
203
|
+
return `${renderCol(this.col, alias)} ${this.op} ?`;
|
|
168
204
|
}
|
|
169
205
|
columns() {
|
|
170
206
|
return [this.col];
|
|
@@ -192,12 +228,12 @@ class postgresArrayOperator {
|
|
|
192
228
|
this.op = op;
|
|
193
229
|
this.not = not;
|
|
194
230
|
}
|
|
195
|
-
clause(idx) {
|
|
231
|
+
clause(idx, alias) {
|
|
196
232
|
if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
|
|
197
233
|
if (this.not) {
|
|
198
|
-
return `NOT ${this.col} ${this.op} $${idx}`;
|
|
234
|
+
return `NOT ${renderCol(this.col, alias)} ${this.op} $${idx}`;
|
|
199
235
|
}
|
|
200
|
-
return `${this.col} ${this.op} $${idx}`;
|
|
236
|
+
return `${renderCol(this.col, alias)} ${this.op} $${idx}`;
|
|
201
237
|
}
|
|
202
238
|
throw new Error(`not supported`);
|
|
203
239
|
}
|
|
@@ -262,14 +298,14 @@ class inClause {
|
|
|
262
298
|
this.type = type;
|
|
263
299
|
this.op = "IN";
|
|
264
300
|
}
|
|
265
|
-
clause(idx) {
|
|
301
|
+
clause(idx, alias) {
|
|
266
302
|
// do a simple = when only one item
|
|
267
303
|
if (this.value.length === 1) {
|
|
268
304
|
if (this.op === "IN") {
|
|
269
|
-
return new simpleClause(this.col, this.value[0], "=").clause(idx);
|
|
305
|
+
return new simpleClause(this.col, this.value[0], "=").clause(idx, alias);
|
|
270
306
|
}
|
|
271
307
|
else {
|
|
272
|
-
return new simpleClause(this.col, this.value[0], "!=").clause(idx);
|
|
308
|
+
return new simpleClause(this.col, this.value[0], "!=").clause(idx, alias);
|
|
273
309
|
}
|
|
274
310
|
}
|
|
275
311
|
const postgres = db_1.default.getDialect() === db_1.Dialect.Postgres;
|
|
@@ -302,7 +338,7 @@ class inClause {
|
|
|
302
338
|
if (postgresValuesList) {
|
|
303
339
|
inValue = `VALUES${inValue}`;
|
|
304
340
|
}
|
|
305
|
-
return `${this.col} ${this.op} (${inValue})`;
|
|
341
|
+
return `${renderCol(this.col, alias)} ${this.op} (${inValue})`;
|
|
306
342
|
// TODO we need to return idx at end to query builder...
|
|
307
343
|
// or anything that's doing a composite query so next clause knows where to start
|
|
308
344
|
// or change to a sqlx.Rebind format
|
|
@@ -343,10 +379,10 @@ class compositeClause {
|
|
|
343
379
|
this.sep = sep;
|
|
344
380
|
this.compositeOp = this.sep;
|
|
345
381
|
}
|
|
346
|
-
clause(idx) {
|
|
382
|
+
clause(idx, alias) {
|
|
347
383
|
let clauses = [];
|
|
348
384
|
for (const clause of this.clauses) {
|
|
349
|
-
let cls = clause.clause(idx);
|
|
385
|
+
let cls = clause.clause(idx, alias);
|
|
350
386
|
// if composite clause and a different op, add parens so that we enforce order of precedence
|
|
351
387
|
if (clause.compositeOp && clause.compositeOp !== this.sep) {
|
|
352
388
|
cls = `(${cls})`;
|
|
@@ -408,16 +444,16 @@ class tsQueryClause {
|
|
|
408
444
|
value: this.val,
|
|
409
445
|
};
|
|
410
446
|
}
|
|
411
|
-
clause(idx) {
|
|
447
|
+
clause(idx, alias) {
|
|
412
448
|
const { language } = this.getInfo();
|
|
413
449
|
if (db_1.Dialect.Postgres === db_1.default.getDialect()) {
|
|
414
450
|
if (this.tsVectorCol) {
|
|
415
|
-
return `to_tsvector(${this.col}) @@ ${this.getFunction()}('${language}', $${idx})`;
|
|
451
|
+
return `to_tsvector(${renderCol(this.col, alias)}) @@ ${this.getFunction()}('${language}', $${idx})`;
|
|
416
452
|
}
|
|
417
|
-
return `${this.col} @@ ${this.getFunction()}('${language}', $${idx})`;
|
|
453
|
+
return `${renderCol(this.col, alias)} @@ ${this.getFunction()}('${language}', $${idx})`;
|
|
418
454
|
}
|
|
419
455
|
// FYI this doesn't actually work for sqlite since different
|
|
420
|
-
return `${this.col} @@ ${this.getFunction()}('${language}', ?)`;
|
|
456
|
+
return `${renderCol(this.col, alias)} @@ ${this.getFunction()}('${language}', ?)`;
|
|
421
457
|
}
|
|
422
458
|
columns() {
|
|
423
459
|
return [this.col];
|
|
@@ -716,11 +752,11 @@ class jSONPathValuePredicateClause {
|
|
|
716
752
|
this.value = value;
|
|
717
753
|
this.pred = pred;
|
|
718
754
|
}
|
|
719
|
-
clause(idx) {
|
|
755
|
+
clause(idx, alias) {
|
|
720
756
|
if (db_1.default.getDialect() !== db_1.Dialect.Postgres) {
|
|
721
757
|
throw new Error(`not supported`);
|
|
722
758
|
}
|
|
723
|
-
return `${this.col} @@ $${idx}`;
|
|
759
|
+
return `${renderCol(this.col, alias)} @@ $${idx}`;
|
|
724
760
|
}
|
|
725
761
|
columns() {
|
|
726
762
|
return [this.col];
|
|
@@ -762,7 +798,7 @@ function JSONBKeyInList(dbCol, jsonCol, val) {
|
|
|
762
798
|
// @ts-ignore
|
|
763
799
|
Eq(JSONObjectFieldKeyAsText("json_element", jsonCol), val)),
|
|
764
800
|
};
|
|
765
|
-
return new
|
|
801
|
+
return new existsQueryClause(opts);
|
|
766
802
|
}
|
|
767
803
|
exports.JSONBKeyInList = JSONBKeyInList;
|
|
768
804
|
function JSONKeyInList(dbCol, jsonCol, val) {
|
|
@@ -774,7 +810,7 @@ function JSONKeyInList(dbCol, jsonCol, val) {
|
|
|
774
810
|
// @ts-ignore
|
|
775
811
|
Eq(JSONObjectFieldKeyAsText("json_element", jsonCol), val)),
|
|
776
812
|
};
|
|
777
|
-
return new
|
|
813
|
+
return new existsQueryClause(opts);
|
|
778
814
|
}
|
|
779
815
|
exports.JSONKeyInList = JSONKeyInList;
|
|
780
816
|
// TODO need a better name for this lol
|
|
@@ -787,15 +823,15 @@ class paginationMultipleColumnsSubQueryClause {
|
|
|
787
823
|
this.uniqueCol = uniqueCol;
|
|
788
824
|
this.val = val;
|
|
789
825
|
}
|
|
790
|
-
buildSimpleQuery(clause, idx) {
|
|
791
|
-
return `SELECT ${this.col} FROM ${this.tableName} WHERE ${clause.clause(idx)}`;
|
|
826
|
+
buildSimpleQuery(clause, idx, alias) {
|
|
827
|
+
return `SELECT ${renderCol(this.col, alias)} FROM ${this.tableName} WHERE ${clause.clause(idx, alias)}`;
|
|
792
828
|
}
|
|
793
|
-
clause(idx) {
|
|
794
|
-
const eq1 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx);
|
|
795
|
-
const eq2 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx + 1);
|
|
796
|
-
const op = new simpleClause(this.uniqueCol, this.val, this.op).clause(idx + 2);
|
|
829
|
+
clause(idx, alias) {
|
|
830
|
+
const eq1 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx, alias);
|
|
831
|
+
const eq2 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx + 1, alias);
|
|
832
|
+
const op = new simpleClause(this.uniqueCol, this.val, this.op).clause(idx + 2, alias);
|
|
797
833
|
// nest in () to make sure it's scoped correctly
|
|
798
|
-
return `(${this.col} ${this.op} (${eq1}) OR (${this.col} = (${eq2}) AND ${op}))`;
|
|
834
|
+
return `(${renderCol(this.col, alias)} ${this.op} (${eq1}) OR (${renderCol(this.col, alias)} = (${eq2}) AND ${op}))`;
|
|
799
835
|
}
|
|
800
836
|
columns() {
|
|
801
837
|
return [this.col];
|
|
@@ -836,7 +872,7 @@ function Modulo(col, value) {
|
|
|
836
872
|
return new simpleClause(col, value, "%", new isNullClause(col));
|
|
837
873
|
}
|
|
838
874
|
exports.Modulo = Modulo;
|
|
839
|
-
function getCombinedClause(options, cls) {
|
|
875
|
+
function getCombinedClause(options, cls, checkIntersection = false) {
|
|
840
876
|
if (options.clause) {
|
|
841
877
|
let optionClause;
|
|
842
878
|
if (typeof options.clause === "function") {
|
|
@@ -846,10 +882,29 @@ function getCombinedClause(options, cls) {
|
|
|
846
882
|
optionClause = options.clause;
|
|
847
883
|
}
|
|
848
884
|
if (optionClause) {
|
|
849
|
-
|
|
850
|
-
|
|
885
|
+
let and = true;
|
|
886
|
+
if (checkIntersection) {
|
|
887
|
+
// this should be the smaller one
|
|
888
|
+
const transformedCols = new Set(optionClause.columns());
|
|
889
|
+
const queriedCols = cls.columns();
|
|
890
|
+
const has = new Set();
|
|
891
|
+
for (const col of queriedCols) {
|
|
892
|
+
if (transformedCols.has(col)) {
|
|
893
|
+
has.add(col);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
and = transformedCols.size > 0 && has.size !== transformedCols.size;
|
|
897
|
+
}
|
|
898
|
+
if (and) {
|
|
899
|
+
// @ts-expect-error different types
|
|
900
|
+
cls = And(cls, optionClause);
|
|
901
|
+
}
|
|
851
902
|
}
|
|
852
903
|
}
|
|
853
904
|
return cls;
|
|
854
905
|
}
|
|
855
906
|
exports.getCombinedClause = getCombinedClause;
|
|
907
|
+
function Expression(expression) {
|
|
908
|
+
return new simpleExpression(expression);
|
|
909
|
+
}
|
|
910
|
+
exports.Expression = Expression;
|
package/core/config.d.ts
CHANGED
|
@@ -23,10 +23,15 @@ export interface Config {
|
|
|
23
23
|
}
|
|
24
24
|
export interface ConfigWithCodegen extends Config {
|
|
25
25
|
codegen?: CodegenConfig;
|
|
26
|
+
databaseMigration?: DatabaseMigrationConfig;
|
|
26
27
|
customGraphQLJSONPath?: string;
|
|
27
28
|
dynamicScriptCustomGraphQLJSONPath?: string;
|
|
28
29
|
globalSchemaPath?: string;
|
|
29
30
|
}
|
|
31
|
+
interface DatabaseMigrationConfig {
|
|
32
|
+
custom_sql_include?: string[];
|
|
33
|
+
custom_sql_exclude?: string[];
|
|
34
|
+
}
|
|
30
35
|
interface CodegenConfig {
|
|
31
36
|
defaultEntPolicy?: PrivacyConfig;
|
|
32
37
|
defaultActionPolicy?: PrivacyConfig;
|
|
@@ -45,6 +50,7 @@ interface CodegenConfig {
|
|
|
45
50
|
customAssocEdgePath?: importedObject;
|
|
46
51
|
globalImportPath?: string;
|
|
47
52
|
userOveriddenFiles?: string[];
|
|
53
|
+
transformLoadMethod?: string;
|
|
48
54
|
transformDeleteMethod?: string;
|
|
49
55
|
}
|
|
50
56
|
interface PrettierConfig {
|
package/core/context.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Viewer, Data, Loader, LoaderWithLoadMany } from "./base";
|
|
2
|
+
import { Viewer, Data, Loader, LoaderWithLoadMany, QueryOptions } from "./base";
|
|
3
3
|
import { IncomingMessage, ServerResponse } from "http";
|
|
4
|
-
import * as clause from "./clause";
|
|
5
4
|
import { Context } from "./base";
|
|
6
|
-
import { OrderBy } from "./query_impl";
|
|
7
5
|
export interface RequestContext<TViewer extends Viewer = Viewer> extends Context<TViewer> {
|
|
8
6
|
authViewer(viewer: TViewer): Promise<void>;
|
|
9
7
|
logout(): Promise<void>;
|
|
@@ -13,21 +11,15 @@ export interface RequestContext<TViewer extends Viewer = Viewer> extends Context
|
|
|
13
11
|
export declare class ContextCache {
|
|
14
12
|
loaders: Map<string, Loader<any, any>>;
|
|
15
13
|
loaderWithLoadMany: Map<string, LoaderWithLoadMany<any, any>>;
|
|
14
|
+
discardedLoaders: Loader<any, any>[];
|
|
16
15
|
getLoader<K, V>(name: string, create: () => Loader<K, V>): Loader<K, V>;
|
|
17
16
|
getLoaderWithLoadMany<K, V>(name: string, create: () => LoaderWithLoadMany<K, V>): LoaderWithLoadMany<K, V>;
|
|
18
17
|
private itemMap;
|
|
19
18
|
private listMap;
|
|
20
19
|
private getkey;
|
|
21
|
-
getCachedRows(options:
|
|
22
|
-
getCachedRow(options:
|
|
23
|
-
primeCache(options:
|
|
24
|
-
primeCache(options:
|
|
20
|
+
getCachedRows(options: QueryOptions): Data[] | null;
|
|
21
|
+
getCachedRow(options: QueryOptions): Data | null;
|
|
22
|
+
primeCache(options: QueryOptions, rows: Data[]): void;
|
|
23
|
+
primeCache(options: QueryOptions, rows: Data): void;
|
|
25
24
|
clearCache(): void;
|
|
26
25
|
}
|
|
27
|
-
interface queryOptions {
|
|
28
|
-
fields: string[];
|
|
29
|
-
tableName: string;
|
|
30
|
-
clause: clause.Clause;
|
|
31
|
-
orderby?: OrderBy;
|
|
32
|
-
}
|
|
33
|
-
export {};
|
package/core/context.js
CHANGED
|
@@ -8,6 +8,9 @@ class ContextCache {
|
|
|
8
8
|
this.loaders = new Map();
|
|
9
9
|
// we should eventually combine the two but better for typing to be separate for now
|
|
10
10
|
this.loaderWithLoadMany = new Map();
|
|
11
|
+
// keep track of discarded loaders in case someone ends up holding onto a reference
|
|
12
|
+
// so that we can call clearAll() on them
|
|
13
|
+
this.discardedLoaders = [];
|
|
11
14
|
// we have a per-table map to make it easier to purge and have less things to compare with
|
|
12
15
|
this.itemMap = new Map();
|
|
13
16
|
this.listMap = new Map();
|
|
@@ -42,6 +45,9 @@ class ContextCache {
|
|
|
42
45
|
if (options.orderby) {
|
|
43
46
|
parts.push((0, query_impl_1.getOrderByPhrase)(options.orderby));
|
|
44
47
|
}
|
|
48
|
+
if (options.join) {
|
|
49
|
+
parts.push((0, query_impl_1.getJoinPhrase)(options.join));
|
|
50
|
+
}
|
|
45
51
|
return parts.join(",");
|
|
46
52
|
}
|
|
47
53
|
getCachedRows(options) {
|
|
@@ -87,15 +93,14 @@ class ContextCache {
|
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
clearCache() {
|
|
96
|
+
this.discardedLoaders.forEach((l) => l.clearAll());
|
|
90
97
|
for (const [_key, loader] of this.loaders) {
|
|
91
|
-
// may not need this since we're clearing the loaders themselves...
|
|
92
|
-
// but may have some benefits by explicitily doing so?
|
|
93
98
|
loader.clearAll();
|
|
99
|
+
this.discardedLoaders.push(loader);
|
|
94
100
|
}
|
|
95
101
|
for (const [_key, loader] of this.loaderWithLoadMany) {
|
|
96
|
-
// may not need this since we're clearing the loaders themselves...
|
|
97
|
-
// but may have some benefits by explicitily doing so?
|
|
98
102
|
loader.clearAll();
|
|
103
|
+
this.discardedLoaders.push(loader);
|
|
99
104
|
}
|
|
100
105
|
this.loaders.clear();
|
|
101
106
|
this.loaderWithLoadMany.clear();
|
package/core/db.js
CHANGED
package/core/ent.d.ts
CHANGED
|
@@ -175,6 +175,7 @@ interface loadEdgeForIDOptions<T extends AssocEdge> extends loadCustomEdgesOptio
|
|
|
175
175
|
id2: ID;
|
|
176
176
|
}
|
|
177
177
|
export declare function loadEdgeForID2<T extends AssocEdge>(options: loadEdgeForIDOptions<T>): Promise<T | undefined>;
|
|
178
|
+
export declare function loadTwoWayEdges<T extends AssocEdge>(opts: loadCustomEdgesOptions<T>): Promise<T[]>;
|
|
178
179
|
export declare function loadNodesByEdge<T extends Ent>(viewer: Viewer, id1: ID, edgeType: string, options: LoadEntOptions<T>): Promise<T[]>;
|
|
179
180
|
export declare function applyPrivacyPolicyForRow<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data): Promise<TEnt | null>;
|
|
180
181
|
export declare function applyPrivacyPolicyForRows<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, rows: Data[], options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt[]>;
|
package/core/ent.js
CHANGED
|
@@ -26,8 +26,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.
|
|
30
|
-
exports.getEdgeTypeInGroup = void 0;
|
|
29
|
+
exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadTwoWayEdges = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.getDefaultLimit = exports.setDefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.buildGroupQuery = exports.loadRows = exports.performRawQuery = exports.___setLogQueryErrorWithError = exports.loadRow = exports.loadRowX = exports.logQuery = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomCount = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = exports.getEntLoader = exports.rowIsError = void 0;
|
|
30
|
+
exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = void 0;
|
|
31
31
|
const db_1 = __importStar(require("./db"));
|
|
32
32
|
const privacy_1 = require("./privacy");
|
|
33
33
|
const clause = __importStar(require("./clause"));
|
|
@@ -301,7 +301,7 @@ async function loadEntXFromClause(viewer, options, clause) {
|
|
|
301
301
|
context: viewer.context,
|
|
302
302
|
};
|
|
303
303
|
const row = await loadRowX(rowOptions);
|
|
304
|
-
return
|
|
304
|
+
return applyPrivacyPolicyForRowX(viewer, options, row);
|
|
305
305
|
}
|
|
306
306
|
exports.loadEntXFromClause = loadEntXFromClause;
|
|
307
307
|
async function loadEnts(viewer, options, ...ids) {
|
|
@@ -447,7 +447,7 @@ async function loadCustomDataImpl(options, query, context) {
|
|
|
447
447
|
// this will have rudimentary caching but nothing crazy
|
|
448
448
|
let cls = query.clause;
|
|
449
449
|
if (!query.disableTransformations) {
|
|
450
|
-
cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
|
|
450
|
+
cls = clause.getCombinedClause(options.loaderFactory.options, query.clause, true);
|
|
451
451
|
}
|
|
452
452
|
return loadRows({
|
|
453
453
|
...query,
|
|
@@ -474,7 +474,7 @@ exports.loadDerivedEnt = loadDerivedEnt;
|
|
|
474
474
|
// won't have caching yet either
|
|
475
475
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
476
476
|
const ent = new loader(viewer, data);
|
|
477
|
-
return
|
|
477
|
+
return applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
478
478
|
}
|
|
479
479
|
exports.loadDerivedEntX = loadDerivedEntX;
|
|
480
480
|
// everything calls into this two so should be fine
|
|
@@ -978,7 +978,7 @@ function getEdgeClauseAndFields(cls, options) {
|
|
|
978
978
|
}
|
|
979
979
|
exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
|
|
980
980
|
async function loadCustomEdges(options) {
|
|
981
|
-
const { cls: actualClause, fields, defaultOptions, tableName, } = await
|
|
981
|
+
const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEdgesInfo(options);
|
|
982
982
|
const rows = await loadRows({
|
|
983
983
|
tableName,
|
|
984
984
|
fields: fields,
|
|
@@ -992,7 +992,7 @@ async function loadCustomEdges(options) {
|
|
|
992
992
|
});
|
|
993
993
|
}
|
|
994
994
|
exports.loadCustomEdges = loadCustomEdges;
|
|
995
|
-
async function
|
|
995
|
+
async function loadEdgesInfo(options, id2) {
|
|
996
996
|
const { id1, edgeType } = options;
|
|
997
997
|
const edgeData = await loadEdgeData(edgeType);
|
|
998
998
|
if (!edgeData) {
|
|
@@ -1037,7 +1037,7 @@ async function loadUniqueNode(viewer, id1, edgeType, options) {
|
|
|
1037
1037
|
if (!edge) {
|
|
1038
1038
|
return null;
|
|
1039
1039
|
}
|
|
1040
|
-
return
|
|
1040
|
+
return loadEnt(viewer, edge.id2, options);
|
|
1041
1041
|
}
|
|
1042
1042
|
exports.loadUniqueNode = loadUniqueNode;
|
|
1043
1043
|
async function loadRawEdgeCountX(options) {
|
|
@@ -1058,10 +1058,10 @@ async function loadRawEdgeCountX(options) {
|
|
|
1058
1058
|
}
|
|
1059
1059
|
exports.loadRawEdgeCountX = loadRawEdgeCountX;
|
|
1060
1060
|
async function loadEdgeForID2(options) {
|
|
1061
|
-
const { cls: actualClause, fields, tableName, } = await
|
|
1061
|
+
const { cls: actualClause, fields, tableName, } = await loadEdgesInfo(options, options.id2);
|
|
1062
1062
|
const row = await loadRow({
|
|
1063
1063
|
tableName,
|
|
1064
|
-
fields
|
|
1064
|
+
fields,
|
|
1065
1065
|
clause: actualClause,
|
|
1066
1066
|
context: options.context,
|
|
1067
1067
|
});
|
|
@@ -1070,6 +1070,25 @@ async function loadEdgeForID2(options) {
|
|
|
1070
1070
|
}
|
|
1071
1071
|
}
|
|
1072
1072
|
exports.loadEdgeForID2 = loadEdgeForID2;
|
|
1073
|
+
async function loadTwoWayEdges(opts) {
|
|
1074
|
+
const { cls: actualClause, fields, tableName } = await loadEdgesInfo(opts);
|
|
1075
|
+
const rows = await loadRows({
|
|
1076
|
+
tableName,
|
|
1077
|
+
alias: "t1",
|
|
1078
|
+
fields,
|
|
1079
|
+
clause: actualClause,
|
|
1080
|
+
context: opts.context,
|
|
1081
|
+
join: {
|
|
1082
|
+
tableName,
|
|
1083
|
+
alias: "t2",
|
|
1084
|
+
clause: clause.And(
|
|
1085
|
+
// these are not values so need this to not think they're values...
|
|
1086
|
+
clause.Expression("t1.id1 = t2.id2"), clause.Expression("t1.id2 = t2.id1")),
|
|
1087
|
+
},
|
|
1088
|
+
});
|
|
1089
|
+
return rows;
|
|
1090
|
+
}
|
|
1091
|
+
exports.loadTwoWayEdges = loadTwoWayEdges;
|
|
1073
1092
|
async function loadNodesByEdge(viewer, id1, edgeType, options) {
|
|
1074
1093
|
// load edges
|
|
1075
1094
|
const rows = await loadEdges({
|
|
@@ -1093,7 +1112,7 @@ async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
|
|
|
1093
1112
|
}
|
|
1094
1113
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1095
1114
|
const ent = new options.ent(viewer, row);
|
|
1096
|
-
return
|
|
1115
|
+
return applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1097
1116
|
}
|
|
1098
1117
|
// deprecated. doesn't use entcache
|
|
1099
1118
|
async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
|