@snowtop/ent 0.1.0-alpha7 → 0.1.0-alpha8
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/action.d.ts +2 -0
- package/action/executor.d.ts +1 -1
- package/action/orchestrator.d.ts +10 -2
- package/action/orchestrator.js +128 -34
- package/core/base.d.ts +5 -1
- package/core/base.js +16 -0
- package/core/clause.d.ts +24 -3
- package/core/clause.js +246 -5
- package/core/config.d.ts +18 -0
- package/core/config.js +17 -0
- package/core/db.d.ts +3 -3
- package/core/db.js +2 -0
- package/core/ent.d.ts +2 -4
- package/core/ent.js +70 -23
- package/core/loaders/assoc_edge_loader.d.ts +1 -1
- package/core/loaders/assoc_edge_loader.js +5 -4
- package/core/loaders/index_loader.js +1 -0
- package/core/loaders/object_loader.d.ts +7 -2
- package/core/loaders/object_loader.js +59 -4
- package/core/privacy.js +3 -0
- package/core/viewer.d.ts +1 -0
- package/core/viewer.js +4 -0
- package/graphql/graphql.d.ts +3 -2
- package/graphql/graphql.js +22 -21
- package/graphql/node_resolver.d.ts +0 -1
- package/index.d.ts +16 -1
- package/index.js +18 -5
- package/package.json +2 -2
- package/parse_schema/parse.d.ts +11 -4
- package/parse_schema/parse.js +50 -6
- package/schema/base_schema.d.ts +36 -1
- package/schema/base_schema.js +48 -2
- package/schema/index.d.ts +2 -2
- package/schema/index.js +8 -1
- package/schema/schema.d.ts +50 -1
- package/schema/schema.js +113 -5
- package/scripts/custom_graphql.js +122 -15
- package/scripts/transform_schema.js +204 -55
- package/testutils/builder.d.ts +5 -1
- package/testutils/builder.js +46 -2
- package/testutils/context/test_context.d.ts +2 -2
- package/testutils/context/test_context.js +7 -1
- package/testutils/db/test_db.d.ts +2 -1
- package/testutils/db/test_db.js +13 -4
- package/testutils/ent-graphql-tests/index.d.ts +2 -0
- package/testutils/ent-graphql-tests/index.js +7 -5
- package/testutils/fake_data/fake_contact.d.ts +2 -6
- package/testutils/fake_data/fake_contact.js +9 -16
- package/testutils/fake_data/fake_event.d.ts +2 -6
- package/testutils/fake_data/fake_event.js +17 -24
- package/testutils/fake_data/fake_user.d.ts +2 -6
- package/testutils/fake_data/fake_user.js +10 -17
- package/testutils/fake_data/test_helpers.js +1 -1
package/core/clause.js
CHANGED
|
@@ -19,10 +19,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.sensitiveValue = exports.In = exports.Or = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = void 0;
|
|
22
|
+
exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayLessEq = exports.ArrayGreaterEq = exports.ArrayLess = exports.ArrayGreater = exports.ArrayNotEq = exports.ArrayEq = void 0;
|
|
23
23
|
const db_1 = __importStar(require("./db"));
|
|
24
24
|
function isSensitive(val) {
|
|
25
|
-
return (
|
|
25
|
+
return (val !== null &&
|
|
26
|
+
typeof val === "object" &&
|
|
27
|
+
val.logValue !== undefined);
|
|
26
28
|
}
|
|
27
29
|
function rawValue(val) {
|
|
28
30
|
if (isSensitive(val)) {
|
|
@@ -31,17 +33,105 @@ function rawValue(val) {
|
|
|
31
33
|
return val;
|
|
32
34
|
}
|
|
33
35
|
class simpleClause {
|
|
34
|
-
constructor(col, value, op) {
|
|
36
|
+
constructor(col, value, op, handleSqliteNull) {
|
|
35
37
|
this.col = col;
|
|
36
38
|
this.value = value;
|
|
37
39
|
this.op = op;
|
|
40
|
+
this.handleSqliteNull = handleSqliteNull;
|
|
38
41
|
}
|
|
39
42
|
clause(idx) {
|
|
43
|
+
const sqliteClause = this.sqliteNull();
|
|
44
|
+
if (sqliteClause) {
|
|
45
|
+
return sqliteClause.clause(idx);
|
|
46
|
+
}
|
|
40
47
|
if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
|
|
41
48
|
return `${this.col} ${this.op} $${idx}`;
|
|
42
49
|
}
|
|
43
50
|
return `${this.col} ${this.op} ?`;
|
|
44
51
|
}
|
|
52
|
+
sqliteNull() {
|
|
53
|
+
if (!this.handleSqliteNull || this.value !== null) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (db_1.default.getDialect() !== db_1.Dialect.SQLite) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
return this.handleSqliteNull;
|
|
60
|
+
}
|
|
61
|
+
values() {
|
|
62
|
+
const sqliteClause = this.sqliteNull();
|
|
63
|
+
if (sqliteClause) {
|
|
64
|
+
return sqliteClause.values();
|
|
65
|
+
}
|
|
66
|
+
if (isSensitive(this.value)) {
|
|
67
|
+
return [this.value.value()];
|
|
68
|
+
}
|
|
69
|
+
return [this.value];
|
|
70
|
+
}
|
|
71
|
+
logValues() {
|
|
72
|
+
const sqliteClause = this.sqliteNull();
|
|
73
|
+
if (sqliteClause) {
|
|
74
|
+
return sqliteClause.logValues();
|
|
75
|
+
}
|
|
76
|
+
if (isSensitive(this.value)) {
|
|
77
|
+
return [this.value.logValue()];
|
|
78
|
+
}
|
|
79
|
+
return [this.value];
|
|
80
|
+
}
|
|
81
|
+
instanceKey() {
|
|
82
|
+
const sqliteClause = this.sqliteNull();
|
|
83
|
+
if (sqliteClause) {
|
|
84
|
+
return sqliteClause.instanceKey();
|
|
85
|
+
}
|
|
86
|
+
return `${this.col}${this.op}${rawValue(this.value)}`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
class isNullClause {
|
|
90
|
+
constructor(col) {
|
|
91
|
+
this.col = col;
|
|
92
|
+
}
|
|
93
|
+
clause(idx) {
|
|
94
|
+
return `${this.col} IS NULL`;
|
|
95
|
+
}
|
|
96
|
+
values() {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
logValues() {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
instanceKey() {
|
|
103
|
+
return `${this.col} IS NULL`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
class isNotNullClause {
|
|
107
|
+
constructor(col) {
|
|
108
|
+
this.col = col;
|
|
109
|
+
}
|
|
110
|
+
clause(idx) {
|
|
111
|
+
return `${this.col} IS NOT NULL`;
|
|
112
|
+
}
|
|
113
|
+
values() {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
logValues() {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
instanceKey() {
|
|
120
|
+
return `${this.col} IS NOT NULL`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
class arraySimpleClause {
|
|
124
|
+
constructor(col, value, op) {
|
|
125
|
+
this.col = col;
|
|
126
|
+
this.value = value;
|
|
127
|
+
this.op = op;
|
|
128
|
+
}
|
|
129
|
+
clause(idx) {
|
|
130
|
+
if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
|
|
131
|
+
return `$${idx} ${this.op} ANY(${this.col})`;
|
|
132
|
+
}
|
|
133
|
+
return `${this.col} ${this.op} ?`;
|
|
134
|
+
}
|
|
45
135
|
values() {
|
|
46
136
|
if (isSensitive(this.value)) {
|
|
47
137
|
return [this.value.value()];
|
|
@@ -145,12 +235,100 @@ class compositeClause {
|
|
|
145
235
|
return keys.join(this.sep);
|
|
146
236
|
}
|
|
147
237
|
}
|
|
238
|
+
class tsQueryClause {
|
|
239
|
+
constructor(col, val, tsVectorCol) {
|
|
240
|
+
this.col = col;
|
|
241
|
+
this.val = val;
|
|
242
|
+
this.tsVectorCol = tsVectorCol;
|
|
243
|
+
}
|
|
244
|
+
isTsQuery(val) {
|
|
245
|
+
return typeof val !== "string";
|
|
246
|
+
}
|
|
247
|
+
getInfo() {
|
|
248
|
+
if (this.isTsQuery(this.val)) {
|
|
249
|
+
return { value: this.val.value, language: this.val.language };
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
language: "english",
|
|
253
|
+
value: this.val,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
clause(idx) {
|
|
257
|
+
const { language } = this.getInfo();
|
|
258
|
+
if (db_1.Dialect.Postgres === db_1.default.getDialect()) {
|
|
259
|
+
if (this.tsVectorCol) {
|
|
260
|
+
return `to_tsvector(${this.col}) @@ ${this.getFunction()}('${language}', $${idx})`;
|
|
261
|
+
}
|
|
262
|
+
return `${this.col} @@ ${this.getFunction()}('${language}', $${idx})`;
|
|
263
|
+
}
|
|
264
|
+
// FYI this doesn't actually work for sqlite since different
|
|
265
|
+
return `${this.col} @@ ${this.getFunction()}('${language}', ?)`;
|
|
266
|
+
}
|
|
267
|
+
values() {
|
|
268
|
+
const { value } = this.getInfo();
|
|
269
|
+
return [value];
|
|
270
|
+
}
|
|
271
|
+
logValues() {
|
|
272
|
+
const { value } = this.getInfo();
|
|
273
|
+
return [value];
|
|
274
|
+
}
|
|
275
|
+
getFunction() {
|
|
276
|
+
return "to_tsquery";
|
|
277
|
+
}
|
|
278
|
+
instanceKey() {
|
|
279
|
+
const { language, value } = this.getInfo();
|
|
280
|
+
if (this.tsVectorCol) {
|
|
281
|
+
return `to_tsvector(${this.col})@@${this.getFunction()}:${language}:${value}`;
|
|
282
|
+
}
|
|
283
|
+
return `${this.col}@@${this.getFunction()}:${language}:${value}`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
class plainToTsQueryClause extends tsQueryClause {
|
|
287
|
+
getFunction() {
|
|
288
|
+
return "plainto_tsquery";
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
class phraseToTsQueryClause extends tsQueryClause {
|
|
292
|
+
getFunction() {
|
|
293
|
+
return "phraseto_tsquery";
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
class websearchTosQueryClause extends tsQueryClause {
|
|
297
|
+
getFunction() {
|
|
298
|
+
return "websearch_to_tsquery";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// TODO we need to check sqlite version...
|
|
302
|
+
function ArrayEq(col, value) {
|
|
303
|
+
return new arraySimpleClause(col, value, "=");
|
|
304
|
+
}
|
|
305
|
+
exports.ArrayEq = ArrayEq;
|
|
306
|
+
function ArrayNotEq(col, value) {
|
|
307
|
+
return new arraySimpleClause(col, value, "!=");
|
|
308
|
+
}
|
|
309
|
+
exports.ArrayNotEq = ArrayNotEq;
|
|
310
|
+
function ArrayGreater(col, value) {
|
|
311
|
+
return new arraySimpleClause(col, value, ">");
|
|
312
|
+
}
|
|
313
|
+
exports.ArrayGreater = ArrayGreater;
|
|
314
|
+
function ArrayLess(col, value) {
|
|
315
|
+
return new arraySimpleClause(col, value, "<");
|
|
316
|
+
}
|
|
317
|
+
exports.ArrayLess = ArrayLess;
|
|
318
|
+
function ArrayGreaterEq(col, value) {
|
|
319
|
+
return new arraySimpleClause(col, value, ">=");
|
|
320
|
+
}
|
|
321
|
+
exports.ArrayGreaterEq = ArrayGreaterEq;
|
|
322
|
+
function ArrayLessEq(col, value) {
|
|
323
|
+
return new arraySimpleClause(col, value, "<=");
|
|
324
|
+
}
|
|
325
|
+
exports.ArrayLessEq = ArrayLessEq;
|
|
148
326
|
function Eq(col, value) {
|
|
149
|
-
return new simpleClause(col, value, "=");
|
|
327
|
+
return new simpleClause(col, value, "=", new isNullClause(col));
|
|
150
328
|
}
|
|
151
329
|
exports.Eq = Eq;
|
|
152
330
|
function NotEq(col, value) {
|
|
153
|
-
return new simpleClause(col, value, "!=");
|
|
331
|
+
return new simpleClause(col, value, "!=", new isNotNullClause(col));
|
|
154
332
|
}
|
|
155
333
|
exports.NotEq = NotEq;
|
|
156
334
|
function Greater(col, value) {
|
|
@@ -173,6 +351,15 @@ function And(...args) {
|
|
|
173
351
|
return new compositeClause(args, " AND ");
|
|
174
352
|
}
|
|
175
353
|
exports.And = And;
|
|
354
|
+
function AndOptional(...args) {
|
|
355
|
+
// @ts-ignore
|
|
356
|
+
let filtered = args.filter((v) => v !== undefined);
|
|
357
|
+
if (filtered.length === 1) {
|
|
358
|
+
return filtered[0];
|
|
359
|
+
}
|
|
360
|
+
return And(...filtered);
|
|
361
|
+
}
|
|
362
|
+
exports.AndOptional = AndOptional;
|
|
176
363
|
function Or(...args) {
|
|
177
364
|
return new compositeClause(args, " OR ");
|
|
178
365
|
}
|
|
@@ -182,6 +369,60 @@ function In(col, ...values) {
|
|
|
182
369
|
return new inClause(col, values);
|
|
183
370
|
}
|
|
184
371
|
exports.In = In;
|
|
372
|
+
// if string defaults to english
|
|
373
|
+
// https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES
|
|
374
|
+
// to_tsquery
|
|
375
|
+
// plainto_tsquery
|
|
376
|
+
// phraseto_tsquery;
|
|
377
|
+
// websearch_to_tsquery
|
|
378
|
+
function TsQuery(col, val) {
|
|
379
|
+
return new tsQueryClause(col, val);
|
|
380
|
+
}
|
|
381
|
+
exports.TsQuery = TsQuery;
|
|
382
|
+
function PlainToTsQuery(col, val) {
|
|
383
|
+
return new plainToTsQueryClause(col, val);
|
|
384
|
+
}
|
|
385
|
+
exports.PlainToTsQuery = PlainToTsQuery;
|
|
386
|
+
function PhraseToTsQuery(col, val) {
|
|
387
|
+
return new phraseToTsQueryClause(col, val);
|
|
388
|
+
}
|
|
389
|
+
exports.PhraseToTsQuery = PhraseToTsQuery;
|
|
390
|
+
function WebsearchToTsQuery(col, val) {
|
|
391
|
+
return new websearchTosQueryClause(col, val);
|
|
392
|
+
}
|
|
393
|
+
exports.WebsearchToTsQuery = WebsearchToTsQuery;
|
|
394
|
+
// TsVectorColTsQuery is used when the column is not a tsvector field e.g.
|
|
395
|
+
// when there's an index just on the field and is not a combination of multiple fields
|
|
396
|
+
function TsVectorColTsQuery(col, val) {
|
|
397
|
+
return new tsQueryClause(col, val, true);
|
|
398
|
+
}
|
|
399
|
+
exports.TsVectorColTsQuery = TsVectorColTsQuery;
|
|
400
|
+
// TsVectorPlainToTsQuery is used when the column is not a tsvector field e.g.
|
|
401
|
+
// when there's an index just on the field and is not a combination of multiple fields
|
|
402
|
+
// TODO do these 4 need TsQuery because would be nice to have language?
|
|
403
|
+
// it seems to default to the config of the column
|
|
404
|
+
function TsVectorPlainToTsQuery(col, val) {
|
|
405
|
+
return new plainToTsQueryClause(col, val, true);
|
|
406
|
+
}
|
|
407
|
+
exports.TsVectorPlainToTsQuery = TsVectorPlainToTsQuery;
|
|
408
|
+
// TsVectorPhraseToTsQuery is used when the column is not a tsvector field e.g.
|
|
409
|
+
// when there's an index just on the field and is not a combination of multiple fields
|
|
410
|
+
function TsVectorPhraseToTsQuery(col, val) {
|
|
411
|
+
return new phraseToTsQueryClause(col, val, true);
|
|
412
|
+
}
|
|
413
|
+
exports.TsVectorPhraseToTsQuery = TsVectorPhraseToTsQuery;
|
|
414
|
+
// TsVectorWebsearchToTsQuery is used when the column is not a tsvector field e.g.
|
|
415
|
+
// when there's an index just on the field and is not a combination of multiple fields
|
|
416
|
+
function TsVectorWebsearchToTsQuery(col, val) {
|
|
417
|
+
return new websearchTosQueryClause(col, val, true);
|
|
418
|
+
}
|
|
419
|
+
exports.TsVectorWebsearchToTsQuery = TsVectorWebsearchToTsQuery;
|
|
420
|
+
// TODO would be nice to support this with building blocks but not supporting for now
|
|
421
|
+
// AND: foo & bar,
|
|
422
|
+
// OR: foo | bar
|
|
423
|
+
// followed by: foo <-> bar
|
|
424
|
+
// NOT: !foo
|
|
425
|
+
// starts_with: theo:*
|
|
185
426
|
// wrap a query in the db with this to ensure that it doesn't show up in the logs
|
|
186
427
|
// e.g. if querying for password, SSN, etc
|
|
187
428
|
// we'll pass the right fields to query and log something along the lines of `****`
|
package/core/config.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Database, DBDict } from "./db";
|
|
3
3
|
declare type logType = "query" | "warn" | "info" | "error" | "debug";
|
|
4
|
+
declare enum graphqlMutationName {
|
|
5
|
+
NOUN_VERB = "NounVerb",
|
|
6
|
+
VERB_NOUN = "VerbNoun"
|
|
7
|
+
}
|
|
8
|
+
declare enum graphQLFieldFormat {
|
|
9
|
+
LOWER_CAMEL = "lowerCamel",
|
|
10
|
+
SNAKE_CASE = "snake_case"
|
|
11
|
+
}
|
|
12
|
+
declare enum fieldPrivacyEvaluated {
|
|
13
|
+
AT_ENT_LOAD = "at_ent_load",
|
|
14
|
+
ON_DEMAND = "on_demand"
|
|
15
|
+
}
|
|
4
16
|
export interface Config {
|
|
5
17
|
dbConnectionString?: string;
|
|
6
18
|
dbFile?: string;
|
|
7
19
|
db?: Database | DBDict;
|
|
8
20
|
log?: logType | logType[];
|
|
9
21
|
codegen?: CodegenConfig;
|
|
22
|
+
customGraphQLJSONPath?: string;
|
|
10
23
|
}
|
|
11
24
|
interface CodegenConfig {
|
|
12
25
|
defaultEntPolicy?: PrivacyConfig;
|
|
@@ -17,6 +30,11 @@ interface CodegenConfig {
|
|
|
17
30
|
generatedHeader?: string;
|
|
18
31
|
disableBase64Encoding?: boolean;
|
|
19
32
|
generateRootResolvers?: boolean;
|
|
33
|
+
defaultGraphQLMutationName?: graphqlMutationName;
|
|
34
|
+
defaultGraphQLFieldFormat?: graphQLFieldFormat;
|
|
35
|
+
schemaSQLFilePath?: boolean;
|
|
36
|
+
databaseToCompareTo?: string;
|
|
37
|
+
fieldPrivacyEvaluated?: fieldPrivacyEvaluated;
|
|
20
38
|
}
|
|
21
39
|
interface PrettierConfig {
|
|
22
40
|
custom?: boolean;
|
package/core/config.js
CHANGED
|
@@ -28,6 +28,23 @@ const js_yaml_1 = require("js-yaml");
|
|
|
28
28
|
const db_1 = __importDefault(require("./db"));
|
|
29
29
|
const path = __importStar(require("path"));
|
|
30
30
|
const logger_1 = require("./logger");
|
|
31
|
+
// ent.config.ts eventually. for now ent.yml
|
|
32
|
+
// or ent.yml?
|
|
33
|
+
var graphqlMutationName;
|
|
34
|
+
(function (graphqlMutationName) {
|
|
35
|
+
graphqlMutationName["NOUN_VERB"] = "NounVerb";
|
|
36
|
+
graphqlMutationName["VERB_NOUN"] = "VerbNoun";
|
|
37
|
+
})(graphqlMutationName || (graphqlMutationName = {}));
|
|
38
|
+
var graphQLFieldFormat;
|
|
39
|
+
(function (graphQLFieldFormat) {
|
|
40
|
+
graphQLFieldFormat["LOWER_CAMEL"] = "lowerCamel";
|
|
41
|
+
graphQLFieldFormat["SNAKE_CASE"] = "snake_case";
|
|
42
|
+
})(graphQLFieldFormat || (graphQLFieldFormat = {}));
|
|
43
|
+
var fieldPrivacyEvaluated;
|
|
44
|
+
(function (fieldPrivacyEvaluated) {
|
|
45
|
+
fieldPrivacyEvaluated["AT_ENT_LOAD"] = "at_ent_load";
|
|
46
|
+
fieldPrivacyEvaluated["ON_DEMAND"] = "on_demand";
|
|
47
|
+
})(fieldPrivacyEvaluated || (fieldPrivacyEvaluated = {}));
|
|
31
48
|
function setConfig(cfg) {
|
|
32
49
|
if (cfg.log) {
|
|
33
50
|
(0, logger_1.setLogLevels)(cfg.log);
|
package/core/db.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Pool,
|
|
2
|
-
export interface Database {
|
|
1
|
+
import { Pool, PoolClient, PoolConfig } from "pg";
|
|
2
|
+
export interface Database extends PoolConfig {
|
|
3
3
|
database?: string;
|
|
4
4
|
user?: string;
|
|
5
5
|
password?: string;
|
|
@@ -16,7 +16,7 @@ export declare enum Dialect {
|
|
|
16
16
|
}
|
|
17
17
|
interface DatabaseInfo {
|
|
18
18
|
dialect: Dialect;
|
|
19
|
-
config:
|
|
19
|
+
config: PoolConfig;
|
|
20
20
|
filePath?: string;
|
|
21
21
|
}
|
|
22
22
|
export default class DB {
|
package/core/db.js
CHANGED
package/core/ent.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Queryer, SyncQueryer } from "./db";
|
|
2
|
-
import { Viewer, Ent, ID, LoadRowsOptions, LoadRowOptions, Data, DataOptions, QueryableDataOptions, EditRowOptions, LoadEntOptions, LoadCustomEntOptions, EdgeQueryableDataOptions, Context, SelectBaseDataOptions, CreateRowOptions, QueryDataOptions
|
|
2
|
+
import { Viewer, Ent, ID, LoadRowsOptions, LoadRowOptions, Data, DataOptions, QueryableDataOptions, EditRowOptions, LoadEntOptions, LoadCustomEntOptions, EdgeQueryableDataOptions, Context, SelectBaseDataOptions, CreateRowOptions, QueryDataOptions } from "./base";
|
|
3
3
|
import { Executor } from "../action/action";
|
|
4
4
|
import * as clause from "./clause";
|
|
5
5
|
import { Builder } from "../action";
|
|
@@ -23,8 +23,6 @@ export declare type CustomQuery = string | rawQueryOptions | clause.Clause | Que
|
|
|
23
23
|
export declare function loadCustomData(options: SelectBaseDataOptions, query: CustomQuery, context: Context | undefined): Promise<Data[]>;
|
|
24
24
|
export declare function loadDerivedEnt<T extends Ent>(viewer: Viewer, data: Data, loader: new (viewer: Viewer, data: Data) => T): Promise<T | null>;
|
|
25
25
|
export declare function loadDerivedEntX<T extends Ent>(viewer: Viewer, data: Data, loader: new (viewer: Viewer, data: Data) => T): Promise<T>;
|
|
26
|
-
export declare function applyPrivacyPolicyForEnt<T extends Ent>(viewer: Viewer, ent: T | null): Promise<T | null>;
|
|
27
|
-
export declare function applyPrivacyPolicyForEntX<T extends Ent>(viewer: Viewer, ent: T): Promise<T>;
|
|
28
26
|
export declare function loadRowX(options: LoadRowOptions): Promise<Data>;
|
|
29
27
|
export declare function loadRow(options: LoadRowOptions): Promise<Data | null>;
|
|
30
28
|
export declare function performRawQuery(query: string, values: any[], logValues?: any[]): Promise<Data[]>;
|
|
@@ -52,7 +50,7 @@ export interface DataOperation<T extends Ent = Ent> {
|
|
|
52
50
|
}
|
|
53
51
|
export interface EditNodeOptions<T extends Ent> extends EditRowOptions {
|
|
54
52
|
fieldsToResolve: string[];
|
|
55
|
-
|
|
53
|
+
loadEntOptions: LoadEntOptions<T>;
|
|
56
54
|
placeholderID?: ID;
|
|
57
55
|
}
|
|
58
56
|
export declare class EditNodeOperation<T extends Ent> implements DataOperation {
|
package/core/ent.js
CHANGED
|
@@ -22,8 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.
|
|
26
|
-
exports.getEdgeTypeInGroup = void 0;
|
|
25
|
+
exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
|
|
27
26
|
const db_1 = __importStar(require("./db"));
|
|
28
27
|
const privacy_1 = require("./privacy");
|
|
29
28
|
const clause = __importStar(require("./clause"));
|
|
@@ -140,8 +139,7 @@ async function loadEntXFromClause(viewer, options, clause) {
|
|
|
140
139
|
context: viewer.context,
|
|
141
140
|
};
|
|
142
141
|
const row = await loadRowX(rowOptions);
|
|
143
|
-
|
|
144
|
-
return await applyPrivacyPolicyForEntX(viewer, ent);
|
|
142
|
+
return await applyPrivacyPolicyForRowX(viewer, options, row);
|
|
145
143
|
}
|
|
146
144
|
exports.loadEntXFromClause = loadEntXFromClause;
|
|
147
145
|
async function loadEnts(viewer, options, ...ids) {
|
|
@@ -212,7 +210,7 @@ async function loadCustomEnts(viewer, options, query) {
|
|
|
212
210
|
const result = new Array(rows.length);
|
|
213
211
|
await Promise.all(rows.map(async (row, idx) => {
|
|
214
212
|
const ent = new options.ent(viewer, row);
|
|
215
|
-
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
|
|
213
|
+
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
216
214
|
if (privacyEnt) {
|
|
217
215
|
result[idx] = privacyEnt;
|
|
218
216
|
}
|
|
@@ -258,30 +256,60 @@ exports.loadCustomData = loadCustomData;
|
|
|
258
256
|
// Derived ents
|
|
259
257
|
async function loadDerivedEnt(viewer, data, loader) {
|
|
260
258
|
const ent = new loader(viewer, data);
|
|
261
|
-
return await applyPrivacyPolicyForEnt(viewer, ent
|
|
259
|
+
return await applyPrivacyPolicyForEnt(viewer, ent, data, {
|
|
260
|
+
ent: loader,
|
|
261
|
+
});
|
|
262
262
|
}
|
|
263
263
|
exports.loadDerivedEnt = loadDerivedEnt;
|
|
264
264
|
async function loadDerivedEntX(viewer, data, loader) {
|
|
265
265
|
const ent = new loader(viewer, data);
|
|
266
|
-
return await applyPrivacyPolicyForEntX(viewer, ent);
|
|
266
|
+
return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
|
|
267
267
|
}
|
|
268
268
|
exports.loadDerivedEntX = loadDerivedEntX;
|
|
269
|
-
|
|
269
|
+
// everything calls into this two so should be fine
|
|
270
|
+
// TODO is there a smarter way to not instantiate two objects here?
|
|
271
|
+
async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
|
|
270
272
|
if (ent) {
|
|
271
273
|
const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.privacyPolicy, ent);
|
|
272
|
-
if (visible) {
|
|
273
|
-
return
|
|
274
|
+
if (!visible) {
|
|
275
|
+
return null;
|
|
274
276
|
}
|
|
277
|
+
return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
|
|
275
278
|
}
|
|
276
279
|
return null;
|
|
277
280
|
}
|
|
278
|
-
|
|
279
|
-
async function applyPrivacyPolicyForEntX(viewer, ent) {
|
|
281
|
+
async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
|
|
280
282
|
// this will throw
|
|
281
283
|
await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.privacyPolicy, ent);
|
|
284
|
+
return doFieldPrivacy(viewer, ent, data, options);
|
|
285
|
+
}
|
|
286
|
+
async function doFieldPrivacy(viewer, ent, data, options) {
|
|
287
|
+
if (!options.fieldPrivacy) {
|
|
288
|
+
return ent;
|
|
289
|
+
}
|
|
290
|
+
const promises = [];
|
|
291
|
+
let somethingChanged = false;
|
|
292
|
+
for (const [k, policy] of options.fieldPrivacy) {
|
|
293
|
+
promises.push((async () => {
|
|
294
|
+
// don't do anything if key is null or for some reason missing
|
|
295
|
+
const curr = data[k];
|
|
296
|
+
if (curr === null || curr === undefined) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
|
|
300
|
+
if (!r) {
|
|
301
|
+
data[k] = null;
|
|
302
|
+
somethingChanged = true;
|
|
303
|
+
}
|
|
304
|
+
})());
|
|
305
|
+
}
|
|
306
|
+
await Promise.all(promises);
|
|
307
|
+
if (somethingChanged) {
|
|
308
|
+
// have to create new instance
|
|
309
|
+
return new options.ent(viewer, data);
|
|
310
|
+
}
|
|
282
311
|
return ent;
|
|
283
312
|
}
|
|
284
|
-
exports.applyPrivacyPolicyForEntX = applyPrivacyPolicyForEntX;
|
|
285
313
|
function logQuery(query, logValues) {
|
|
286
314
|
(0, logger_1.log)("query", {
|
|
287
315
|
query: query,
|
|
@@ -327,6 +355,8 @@ async function loadRow(options) {
|
|
|
327
355
|
return res.rows[0];
|
|
328
356
|
}
|
|
329
357
|
catch (e) {
|
|
358
|
+
// an example of an error being suppressed
|
|
359
|
+
// another one. TODO https://github.com/lolopinto/ent/issues/862
|
|
330
360
|
(0, logger_1.log)("error", e);
|
|
331
361
|
return null;
|
|
332
362
|
}
|
|
@@ -439,6 +469,8 @@ class EditNodeOperation {
|
|
|
439
469
|
};
|
|
440
470
|
if (this.existingEnt) {
|
|
441
471
|
if (this.hasData(options.fields)) {
|
|
472
|
+
// even this with returning * may not always work if transformed...
|
|
473
|
+
// we can have a transformed flag to see if it should be returned?
|
|
442
474
|
this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
|
|
443
475
|
}
|
|
444
476
|
else {
|
|
@@ -450,20 +482,36 @@ class EditNodeOperation {
|
|
|
450
482
|
}
|
|
451
483
|
}
|
|
452
484
|
reloadRow(queryer, id, options) {
|
|
485
|
+
// TODO this isn't always an ObjectLoader. should throw or figure out a way to get query
|
|
486
|
+
// and run this on its own...
|
|
487
|
+
const loader = this.options.loadEntOptions.loaderFactory.createLoader(options.context);
|
|
488
|
+
const opts = loader.getOptions();
|
|
489
|
+
let cls = clause.Eq(options.key, id);
|
|
490
|
+
if (opts.clause) {
|
|
491
|
+
let optionClause;
|
|
492
|
+
if (typeof opts.clause === "function") {
|
|
493
|
+
optionClause = opts.clause();
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
optionClause = opts.clause;
|
|
497
|
+
}
|
|
498
|
+
if (optionClause) {
|
|
499
|
+
cls = clause.And(optionClause, cls);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
453
502
|
const query = buildQuery({
|
|
454
|
-
fields: ["*"],
|
|
503
|
+
fields: opts.fields.length ? opts.fields : ["*"],
|
|
455
504
|
tableName: options.tableName,
|
|
456
|
-
clause:
|
|
505
|
+
clause: cls,
|
|
457
506
|
});
|
|
458
507
|
// special case log here because we're not going through any of the normal
|
|
459
508
|
// methods here because those are async and this is sync
|
|
460
509
|
// this is the only place we're doing this so only handling here
|
|
461
510
|
logQuery(query, [id]);
|
|
462
511
|
const r = queryer.querySync(query, [id]);
|
|
463
|
-
if (r.rows.length
|
|
464
|
-
|
|
512
|
+
if (r.rows.length === 1) {
|
|
513
|
+
this.row = r.rows[0];
|
|
465
514
|
}
|
|
466
|
-
this.row = r.rows[0];
|
|
467
515
|
}
|
|
468
516
|
performWriteSync(queryer, context) {
|
|
469
517
|
let options = {
|
|
@@ -492,7 +540,7 @@ class EditNodeOperation {
|
|
|
492
540
|
if (!this.row) {
|
|
493
541
|
return null;
|
|
494
542
|
}
|
|
495
|
-
return new this.options.ent(viewer, this.row);
|
|
543
|
+
return new this.options.loadEntOptions.ent(viewer, this.row);
|
|
496
544
|
}
|
|
497
545
|
}
|
|
498
546
|
exports.EditNodeOperation = EditNodeOperation;
|
|
@@ -1146,20 +1194,19 @@ async function applyPrivacyPolicyForRow(viewer, options, row) {
|
|
|
1146
1194
|
return null;
|
|
1147
1195
|
}
|
|
1148
1196
|
const ent = new options.ent(viewer, row);
|
|
1149
|
-
return await applyPrivacyPolicyForEnt(viewer, ent);
|
|
1197
|
+
return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
|
|
1150
1198
|
}
|
|
1151
1199
|
exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
|
|
1152
1200
|
async function applyPrivacyPolicyForRowX(viewer, options, row) {
|
|
1153
1201
|
const ent = new options.ent(viewer, row);
|
|
1154
|
-
return await applyPrivacyPolicyForEntX(viewer, ent);
|
|
1202
|
+
return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
|
|
1155
1203
|
}
|
|
1156
1204
|
exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
|
|
1157
1205
|
async function applyPrivacyPolicyForRows(viewer, rows, options) {
|
|
1158
1206
|
let m = new Map();
|
|
1159
1207
|
// apply privacy logic
|
|
1160
1208
|
await Promise.all(rows.map(async (row) => {
|
|
1161
|
-
|
|
1162
|
-
let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent);
|
|
1209
|
+
let privacyEnt = await applyPrivacyPolicyForRow(viewer, options, row);
|
|
1163
1210
|
if (privacyEnt) {
|
|
1164
1211
|
m.set(privacyEnt.id, privacyEnt);
|
|
1165
1212
|
}
|
|
@@ -32,7 +32,7 @@ export declare class AssocEdgeLoaderFactory<T extends AssocEdge> implements Load
|
|
|
32
32
|
name: string;
|
|
33
33
|
constructor(edgeType: string, edgeCtr: AssocEdgeConstructor<T> | (() => AssocEdgeConstructor<T>));
|
|
34
34
|
createLoader(context?: Context): AssocLoader<T>;
|
|
35
|
-
private
|
|
35
|
+
private isConstructor;
|
|
36
36
|
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): AssocLoader<T>;
|
|
37
37
|
}
|
|
38
38
|
export {};
|
|
@@ -161,16 +161,17 @@ class AssocEdgeLoaderFactory {
|
|
|
161
161
|
createLoader(context) {
|
|
162
162
|
return this.createConfigurableLoader({}, context);
|
|
163
163
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
isConstructor(edgeCtr) {
|
|
165
|
+
return (edgeCtr.prototype &&
|
|
166
|
+
edgeCtr.prototype.constructor &&
|
|
167
|
+
edgeCtr.prototype.constructor.name.length > 0);
|
|
167
168
|
}
|
|
168
169
|
createConfigurableLoader(options, context) {
|
|
169
170
|
let edgeCtr = this.edgeCtr;
|
|
170
171
|
// in generated code, the edge is not necessarily defined at the time of loading
|
|
171
172
|
// so we call this as follows:
|
|
172
173
|
// const loader = new AssocEdgeLoaderFactory(EdgeType.Foo, ()=>DerivedEdgeClass);
|
|
173
|
-
if (this.
|
|
174
|
+
if (!this.isConstructor(edgeCtr)) {
|
|
174
175
|
edgeCtr = edgeCtr();
|
|
175
176
|
}
|
|
176
177
|
// rename to make TS happy
|
|
@@ -4,6 +4,7 @@ exports.IndexLoaderFactory = void 0;
|
|
|
4
4
|
const query_loader_1 = require("./query_loader");
|
|
5
5
|
// we're keeping this for legacy reasons so as to not break existing callers
|
|
6
6
|
// and to decouple the change here but all callers can safely be changed here to use QueryLoaderFactory
|
|
7
|
+
// @deprecated use QueryLoaderFactory
|
|
7
8
|
class IndexLoaderFactory {
|
|
8
9
|
constructor(options, col, opts) {
|
|
9
10
|
this.factory = new query_loader_1.QueryLoaderFactory({
|
|
@@ -7,17 +7,22 @@ export declare class ObjectLoader<T> implements Loader<T, Data | null> {
|
|
|
7
7
|
private primedLoaders;
|
|
8
8
|
private memoizedInitPrime;
|
|
9
9
|
constructor(options: SelectDataOptions, context?: Context | undefined, toPrime?: ObjectLoaderFactory<T>[] | undefined);
|
|
10
|
+
getOptions(): SelectDataOptions;
|
|
10
11
|
private initPrime;
|
|
11
12
|
load(key: T): Promise<Data | null>;
|
|
12
13
|
clearAll(): void;
|
|
13
14
|
loadMany(keys: T[]): Promise<Data[]>;
|
|
14
15
|
prime(data: Data): void;
|
|
15
16
|
}
|
|
17
|
+
interface ObjectLoaderOptions extends SelectDataOptions {
|
|
18
|
+
instanceKey?: string;
|
|
19
|
+
}
|
|
16
20
|
export declare class ObjectLoaderFactory<T> implements LoaderFactory<T, Data | null> {
|
|
17
|
-
options:
|
|
21
|
+
options: ObjectLoaderOptions;
|
|
18
22
|
name: string;
|
|
19
23
|
private toPrime;
|
|
20
|
-
constructor(options:
|
|
24
|
+
constructor(options: ObjectLoaderOptions);
|
|
21
25
|
createLoader(context?: Context): ObjectLoader<T>;
|
|
22
26
|
addToPrime(factory: ObjectLoaderFactory<T>): void;
|
|
23
27
|
}
|
|
28
|
+
export {};
|