@snowtop/ent 0.1.0-alpha12 → 0.1.0-alpha121
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 +37 -31
- package/action/action.js +22 -7
- package/action/executor.d.ts +3 -3
- package/action/executor.js +8 -3
- package/action/experimental_action.d.ts +32 -22
- package/action/experimental_action.js +35 -9
- package/action/index.d.ts +2 -0
- package/action/index.js +7 -1
- package/action/orchestrator.d.ts +33 -14
- package/action/orchestrator.js +251 -54
- package/action/privacy.d.ts +2 -2
- package/action/relative_value.d.ts +47 -0
- package/action/relative_value.js +125 -0
- package/action/transaction.d.ts +10 -0
- package/action/transaction.js +23 -0
- package/auth/auth.d.ts +1 -1
- package/core/base.d.ts +60 -37
- package/core/base.js +7 -1
- package/core/clause.d.ts +84 -40
- package/core/clause.js +358 -64
- package/core/config.d.ts +12 -1
- package/core/config.js +7 -1
- package/core/const.d.ts +3 -0
- package/core/const.js +6 -0
- package/core/context.d.ts +6 -4
- package/core/context.js +20 -2
- package/core/convert.d.ts +1 -1
- package/core/date.js +1 -5
- package/core/db.d.ts +11 -8
- package/core/db.js +20 -8
- package/core/ent.d.ts +82 -30
- package/core/ent.js +632 -193
- package/core/global_schema.d.ts +7 -0
- package/core/global_schema.js +51 -0
- package/core/loaders/assoc_count_loader.d.ts +3 -2
- package/core/loaders/assoc_count_loader.js +10 -2
- package/core/loaders/assoc_edge_loader.d.ts +2 -2
- package/core/loaders/assoc_edge_loader.js +8 -11
- package/core/loaders/index.d.ts +1 -1
- package/core/loaders/index.js +1 -3
- package/core/loaders/index_loader.d.ts +3 -3
- package/core/loaders/loader.d.ts +2 -2
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +11 -10
- package/core/loaders/object_loader.js +70 -60
- package/core/loaders/query_loader.d.ts +7 -13
- package/core/loaders/query_loader.js +52 -11
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.js +5 -1
- package/core/logger.d.ts +1 -1
- package/core/logger.js +1 -0
- package/core/privacy.d.ts +25 -24
- package/core/privacy.js +21 -25
- package/core/query/assoc_query.d.ts +7 -6
- package/core/query/assoc_query.js +9 -1
- package/core/query/custom_clause_query.d.ts +27 -0
- package/core/query/custom_clause_query.js +84 -0
- package/core/query/custom_query.d.ts +20 -5
- package/core/query/custom_query.js +87 -12
- package/core/query/index.d.ts +1 -0
- package/core/query/index.js +3 -1
- package/core/query/query.d.ts +8 -4
- package/core/query/query.js +101 -53
- package/core/query/shared_assoc_test.d.ts +2 -1
- package/core/query/shared_assoc_test.js +35 -45
- package/core/query/shared_test.d.ts +8 -1
- package/core/query/shared_test.js +470 -236
- package/core/viewer.d.ts +3 -3
- package/core/viewer.js +1 -1
- package/graphql/graphql.d.ts +15 -7
- package/graphql/graphql.js +23 -7
- package/graphql/index.d.ts +1 -1
- package/graphql/index.js +3 -4
- package/graphql/query/connection_type.d.ts +9 -9
- package/graphql/query/edge_connection.d.ts +9 -9
- package/graphql/query/page_info.d.ts +1 -1
- package/graphql/query/shared_assoc_test.js +1 -1
- package/graphql/query/shared_edge_connection.js +1 -19
- package/graphql/scalars/orderby_direction.d.ts +2 -0
- package/graphql/scalars/orderby_direction.js +15 -0
- package/imports/index.d.ts +6 -1
- package/imports/index.js +19 -4
- package/index.d.ts +13 -5
- package/index.js +21 -7
- package/package.json +17 -16
- package/parse_schema/parse.d.ts +31 -9
- package/parse_schema/parse.js +152 -12
- package/schema/base_schema.d.ts +5 -3
- package/schema/base_schema.js +6 -0
- package/schema/field.d.ts +78 -21
- package/schema/field.js +219 -72
- package/schema/index.d.ts +2 -2
- package/schema/index.js +5 -1
- package/schema/json_field.d.ts +16 -4
- package/schema/json_field.js +32 -2
- package/schema/schema.d.ts +89 -20
- package/schema/schema.js +13 -14
- package/schema/struct_field.d.ts +15 -3
- package/schema/struct_field.js +71 -22
- package/schema/union_field.d.ts +1 -1
- package/scripts/custom_compiler.js +10 -6
- package/scripts/custom_graphql.js +124 -31
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/move_types.js +117 -0
- package/scripts/read_schema.js +20 -5
- package/testutils/action/complex_schemas.d.ts +69 -0
- package/testutils/action/complex_schemas.js +398 -0
- package/testutils/builder.d.ts +43 -47
- package/testutils/builder.js +76 -49
- package/testutils/db/fixture.d.ts +10 -0
- package/testutils/db/fixture.js +26 -0
- package/testutils/db/{test_db.d.ts → temp_db.d.ts} +24 -8
- package/testutils/db/{test_db.js → temp_db.js} +182 -45
- package/testutils/db/value.d.ts +7 -0
- package/testutils/db/value.js +251 -0
- package/testutils/db_mock.d.ts +16 -4
- package/testutils/db_mock.js +51 -6
- package/testutils/db_time_zone.d.ts +4 -0
- package/testutils/db_time_zone.js +41 -0
- package/testutils/ent-graphql-tests/index.d.ts +7 -1
- package/testutils/ent-graphql-tests/index.js +52 -23
- package/testutils/fake_data/const.d.ts +2 -1
- package/testutils/fake_data/const.js +3 -0
- package/testutils/fake_data/fake_contact.d.ts +8 -4
- package/testutils/fake_data/fake_contact.js +15 -8
- package/testutils/fake_data/fake_event.d.ts +5 -2
- package/testutils/fake_data/fake_event.js +9 -7
- package/testutils/fake_data/fake_tag.d.ts +36 -0
- package/testutils/fake_data/fake_tag.js +89 -0
- package/testutils/fake_data/fake_user.d.ts +10 -7
- package/testutils/fake_data/fake_user.js +18 -16
- package/testutils/fake_data/index.js +5 -1
- package/testutils/fake_data/internal.d.ts +2 -0
- package/testutils/fake_data/internal.js +7 -1
- package/testutils/fake_data/tag_query.d.ts +13 -0
- package/testutils/fake_data/tag_query.js +43 -0
- package/testutils/fake_data/test_helpers.d.ts +11 -4
- package/testutils/fake_data/test_helpers.js +28 -12
- package/testutils/fake_data/user_query.d.ts +13 -6
- package/testutils/fake_data/user_query.js +54 -22
- package/testutils/fake_log.d.ts +3 -3
- package/testutils/parse_sql.d.ts +6 -0
- package/testutils/parse_sql.js +16 -2
- package/testutils/test_edge_global_schema.d.ts +15 -0
- package/testutils/test_edge_global_schema.js +62 -0
- package/testutils/write.d.ts +2 -2
- package/testutils/write.js +33 -7
- package/tsc/ast.d.ts +25 -2
- package/tsc/ast.js +141 -17
- package/tsc/compilerOptions.js +5 -1
- package/tsc/move_generated.d.ts +1 -0
- package/tsc/move_generated.js +164 -0
- package/tsc/transform.d.ts +22 -0
- package/tsc/transform.js +181 -0
- package/tsc/transform_action.d.ts +22 -0
- package/tsc/transform_action.js +183 -0
- package/tsc/transform_ent.d.ts +17 -0
- package/tsc/transform_ent.js +60 -0
- package/tsc/transform_schema.d.ts +27 -0
- package/{scripts → tsc}/transform_schema.js +146 -117
- package/graphql/enums.d.ts +0 -3
- package/graphql/enums.js +0 -25
- package/scripts/move_generated.js +0 -142
- package/scripts/transform_code.js +0 -113
- package/scripts/transform_schema.d.ts +0 -1
- /package/scripts/{move_generated.d.ts → migrate_v0.1.d.ts} +0 -0
- /package/scripts/{transform_code.d.ts → move_types.d.ts} +0 -0
package/action/orchestrator.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
@@ -11,6 +34,8 @@ const privacy_1 = require("../core/privacy");
|
|
|
11
34
|
const executor_1 = require("./executor");
|
|
12
35
|
const logger_1 = require("../core/logger");
|
|
13
36
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
37
|
+
const clause = __importStar(require("../core/clause"));
|
|
38
|
+
const util_1 = require("util");
|
|
14
39
|
var edgeDirection;
|
|
15
40
|
(function (edgeDirection) {
|
|
16
41
|
edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
|
|
@@ -68,6 +93,10 @@ class Orchestrator {
|
|
|
68
93
|
this.existingEnt = this.options.builder.existingEnt;
|
|
69
94
|
this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
|
|
70
95
|
}
|
|
96
|
+
// don't type this because we don't care
|
|
97
|
+
__getOptions() {
|
|
98
|
+
return this.options;
|
|
99
|
+
}
|
|
71
100
|
addEdge(edge, op) {
|
|
72
101
|
this.edgeSet.add(edge.edgeType);
|
|
73
102
|
let m1 = this.edges.get(edge.edgeType) || new Map();
|
|
@@ -152,6 +181,10 @@ class Orchestrator {
|
|
|
152
181
|
if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
|
|
153
182
|
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
154
183
|
}
|
|
184
|
+
if (this.options.expressions &&
|
|
185
|
+
this.actualOperation !== action_1.WriteOperation.Edit) {
|
|
186
|
+
throw new Error(`expressions are only supported in edit operations for now`);
|
|
187
|
+
}
|
|
155
188
|
const opts = {
|
|
156
189
|
fields: this.validatedFields,
|
|
157
190
|
tableName: this.options.tableName,
|
|
@@ -159,6 +192,8 @@ class Orchestrator {
|
|
|
159
192
|
key: this.options.key,
|
|
160
193
|
loadEntOptions: this.options.loaderOptions,
|
|
161
194
|
placeholderID: this.options.builder.placeholderID,
|
|
195
|
+
whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
|
|
196
|
+
expressions: this.options.expressions,
|
|
162
197
|
};
|
|
163
198
|
if (this.logValues) {
|
|
164
199
|
opts.fieldsToLog = this.logValues;
|
|
@@ -212,7 +247,7 @@ class Orchestrator {
|
|
|
212
247
|
ops.push(edgeOp);
|
|
213
248
|
const edgeData = edgeDatas.get(edgeType);
|
|
214
249
|
if (!edgeData) {
|
|
215
|
-
throw new Error(`could not load edge data for ${edgeType}`);
|
|
250
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
216
251
|
}
|
|
217
252
|
if (edgeData.symmetricEdge) {
|
|
218
253
|
ops.push(edgeOp.symmetricEdge());
|
|
@@ -238,12 +273,44 @@ class Orchestrator {
|
|
|
238
273
|
}
|
|
239
274
|
return new EntCannotDeleteEntError(privacyPolicy, action, this.existingEnt);
|
|
240
275
|
}
|
|
241
|
-
getEntForPrivacyPolicyImpl(editedData) {
|
|
276
|
+
async getEntForPrivacyPolicyImpl(schemaFields, editedData) {
|
|
242
277
|
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
243
278
|
return this.existingEnt;
|
|
244
279
|
}
|
|
280
|
+
// need to format fields if possible because ent constructors expect data that's
|
|
281
|
+
// in the format that's coming from the db
|
|
282
|
+
// required for object fields...
|
|
283
|
+
const formatted = { ...editedData };
|
|
284
|
+
for (const [fieldName, field] of schemaFields) {
|
|
285
|
+
if (!field.format) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
289
|
+
let val = formatted[dbKey];
|
|
290
|
+
if (!val) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (field.valid) {
|
|
294
|
+
let valid = field.valid(val);
|
|
295
|
+
if (util_1.types.isPromise(valid)) {
|
|
296
|
+
valid = await valid;
|
|
297
|
+
}
|
|
298
|
+
// if not valid, don't format and don't pass to ent?
|
|
299
|
+
// or just early throw here
|
|
300
|
+
if (!valid) {
|
|
301
|
+
continue;
|
|
302
|
+
// throw new Error(`invalid field ${fieldName} with value ${val}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// nested so it's not JSON stringified or anything like that
|
|
306
|
+
val = field.format(formatted[dbKey], true);
|
|
307
|
+
if (util_1.types.isPromise(val)) {
|
|
308
|
+
val = await val;
|
|
309
|
+
}
|
|
310
|
+
formatted[dbKey] = val;
|
|
311
|
+
}
|
|
245
312
|
// we create an unsafe ent to be used for privacy policies
|
|
246
|
-
return new this.options.builder.ent(this.options.builder.viewer,
|
|
313
|
+
return new this.options.builder.ent(this.options.builder.viewer, formatted);
|
|
247
314
|
}
|
|
248
315
|
getSQLStatementOperation() {
|
|
249
316
|
switch (this.actualOperation) {
|
|
@@ -273,8 +340,8 @@ class Orchestrator {
|
|
|
273
340
|
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
274
341
|
return this.existingEnt;
|
|
275
342
|
}
|
|
276
|
-
const { editedData } = await this.memoizedGetFields();
|
|
277
|
-
return this.getEntForPrivacyPolicyImpl(editedData);
|
|
343
|
+
const { schemaFields, editedData } = await this.memoizedGetFields();
|
|
344
|
+
return this.getEntForPrivacyPolicyImpl(schemaFields, editedData);
|
|
278
345
|
}
|
|
279
346
|
// this gets the fields that were explicitly set plus any default or transformed values
|
|
280
347
|
// mainly exists to get default fields e.g. default id to be used in triggers
|
|
@@ -284,6 +351,16 @@ class Orchestrator {
|
|
|
284
351
|
const { editedData } = await this.memoizedGetFields();
|
|
285
352
|
return editedData;
|
|
286
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* @returns validated and formatted fields that would be written to the db
|
|
356
|
+
* throws an error if called before valid() or validX() has been called
|
|
357
|
+
*/
|
|
358
|
+
getValidatedFields() {
|
|
359
|
+
if (this.validatedFields === null) {
|
|
360
|
+
throw new Error(`trying to call getValidatedFields before validating fields`);
|
|
361
|
+
}
|
|
362
|
+
return this.validatedFields;
|
|
363
|
+
}
|
|
287
364
|
// Note: this is memoized. call memoizedGetFields instead
|
|
288
365
|
async getFieldsInfo() {
|
|
289
366
|
const action = this.options.action;
|
|
@@ -312,51 +389,94 @@ class Orchestrator {
|
|
|
312
389
|
// * triggers
|
|
313
390
|
// * validators
|
|
314
391
|
let privacyPolicy = action?.getPrivacyPolicy();
|
|
392
|
+
let privacyError = null;
|
|
315
393
|
if (privacyPolicy) {
|
|
316
|
-
|
|
394
|
+
try {
|
|
395
|
+
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, await this.getEntForPrivacyPolicyImpl(schemaFields, editedData), this.throwError.bind(this));
|
|
396
|
+
}
|
|
397
|
+
catch (err) {
|
|
398
|
+
privacyError = err;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// privacyError should return first since it's less confusing
|
|
402
|
+
if (privacyError !== null) {
|
|
403
|
+
return [privacyError];
|
|
317
404
|
}
|
|
318
405
|
// have to run triggers which update fields first before field and other validators
|
|
319
406
|
// so running this first to build things up
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
407
|
+
if (action?.getTriggers) {
|
|
408
|
+
await this.triggers(action, builder, action.getTriggers());
|
|
409
|
+
}
|
|
410
|
+
let validators = [];
|
|
411
|
+
if (action?.getValidators) {
|
|
412
|
+
validators = action.getValidators();
|
|
323
413
|
}
|
|
324
|
-
let validators = action?.validators || [];
|
|
325
414
|
// not ideal we're calling this twice. fix...
|
|
326
415
|
// needed for now. may need to rewrite some of this?
|
|
327
416
|
const editedFields2 = await this.options.editedFields();
|
|
328
|
-
await Promise.all([
|
|
417
|
+
const [errors, errs2] = await Promise.all([
|
|
329
418
|
this.formatAndValidateFields(schemaFields, editedFields2),
|
|
330
419
|
this.validators(validators, action, builder),
|
|
331
420
|
]);
|
|
421
|
+
errors.push(...errs2);
|
|
422
|
+
return errors;
|
|
332
423
|
}
|
|
333
424
|
async triggers(action, builder, triggers) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (Array.isArray(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
425
|
+
let groups = [];
|
|
426
|
+
let lastArray = 0;
|
|
427
|
+
let prevWasArray = false;
|
|
428
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
429
|
+
let t = triggers[i];
|
|
430
|
+
if (Array.isArray(t)) {
|
|
431
|
+
if (!prevWasArray) {
|
|
432
|
+
// @ts-ignore
|
|
433
|
+
groups.push(triggers.slice(lastArray, i));
|
|
344
434
|
}
|
|
435
|
+
groups.push(t);
|
|
436
|
+
prevWasArray = true;
|
|
437
|
+
lastArray++;
|
|
345
438
|
}
|
|
346
|
-
else
|
|
347
|
-
|
|
439
|
+
else {
|
|
440
|
+
if (i === triggers.length - 1) {
|
|
441
|
+
// @ts-ignore
|
|
442
|
+
groups.push(triggers.slice(lastArray, i + 1));
|
|
443
|
+
}
|
|
444
|
+
prevWasArray = false;
|
|
348
445
|
}
|
|
349
|
-
}
|
|
446
|
+
}
|
|
447
|
+
for (const triggers of groups) {
|
|
448
|
+
await Promise.all(triggers.map(async (trigger) => {
|
|
449
|
+
let ret = await trigger.changeset(builder, action.getInput());
|
|
450
|
+
if (Array.isArray(ret)) {
|
|
451
|
+
ret = await Promise.all(ret);
|
|
452
|
+
}
|
|
453
|
+
if (Array.isArray(ret)) {
|
|
454
|
+
for (const v of ret) {
|
|
455
|
+
if (typeof v === "object") {
|
|
456
|
+
this.changesets.push(v);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
else if (ret) {
|
|
461
|
+
this.changesets.push(ret);
|
|
462
|
+
}
|
|
463
|
+
}));
|
|
464
|
+
}
|
|
350
465
|
}
|
|
351
466
|
async validators(validators, action, builder) {
|
|
352
|
-
|
|
353
|
-
validators.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
467
|
+
const errors = [];
|
|
468
|
+
await Promise.all(validators.map(async (v) => {
|
|
469
|
+
try {
|
|
470
|
+
const r = await v.validate(builder, action.getInput());
|
|
471
|
+
if (r instanceof Error) {
|
|
472
|
+
errors.push(r);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch (err) {
|
|
476
|
+
errors.push(err);
|
|
357
477
|
}
|
|
358
|
-
});
|
|
359
|
-
|
|
478
|
+
}));
|
|
479
|
+
return errors;
|
|
360
480
|
}
|
|
361
481
|
isBuilder(val) {
|
|
362
482
|
return val.placeholderID !== undefined;
|
|
@@ -376,24 +496,30 @@ class Orchestrator {
|
|
|
376
496
|
// if action transformations. always do it
|
|
377
497
|
// if disable transformations set, don't do schema transform and just do the right thing
|
|
378
498
|
// else apply schema tranformation if it exists
|
|
379
|
-
let transformed;
|
|
499
|
+
let transformed = null;
|
|
500
|
+
const sqlOp = this.getSQLStatementOperation();
|
|
380
501
|
if (action?.transformWrite) {
|
|
381
502
|
transformed = await action.transformWrite({
|
|
382
|
-
|
|
383
|
-
|
|
503
|
+
builder,
|
|
504
|
+
input,
|
|
505
|
+
op: sqlOp,
|
|
384
506
|
data: editedFields,
|
|
385
|
-
existingEnt: this.existingEnt,
|
|
386
507
|
});
|
|
387
508
|
}
|
|
388
509
|
else if (!this.disableTransformations) {
|
|
389
510
|
transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
|
|
390
|
-
|
|
391
|
-
|
|
511
|
+
builder,
|
|
512
|
+
input,
|
|
513
|
+
op: sqlOp,
|
|
392
514
|
data: editedFields,
|
|
393
|
-
existingEnt: this.existingEnt,
|
|
394
515
|
});
|
|
395
516
|
}
|
|
396
517
|
if (transformed) {
|
|
518
|
+
if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
|
|
519
|
+
if (!transformed.existingEnt) {
|
|
520
|
+
throw new Error(`cannot transform an insert operation without providing an existing ent`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
397
523
|
if (transformed.data) {
|
|
398
524
|
updateInput = true;
|
|
399
525
|
for (const k in transformed.data) {
|
|
@@ -412,9 +538,16 @@ class Orchestrator {
|
|
|
412
538
|
// this.defaultFieldsByFieldName[k] = val;
|
|
413
539
|
}
|
|
414
540
|
}
|
|
541
|
+
if (transformed.changeset) {
|
|
542
|
+
const ct = await transformed.changeset();
|
|
543
|
+
this.changesets.push(ct);
|
|
544
|
+
}
|
|
415
545
|
this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
|
|
416
546
|
if (transformed.existingEnt) {
|
|
547
|
+
// @ts-ignore
|
|
417
548
|
this.existingEnt = transformed.existingEnt;
|
|
549
|
+
// modify existing ent in builder. it's readonly in generated ents but doesn't apply here
|
|
550
|
+
builder.existingEnt = transformed.existingEnt;
|
|
418
551
|
}
|
|
419
552
|
}
|
|
420
553
|
// transforming before doing default fields so that we don't create a new id
|
|
@@ -423,6 +556,7 @@ class Orchestrator {
|
|
|
423
556
|
let value = editedFields.get(fieldName);
|
|
424
557
|
let defaultValue = undefined;
|
|
425
558
|
let dbKey = this.getStorageKey(fieldName);
|
|
559
|
+
let updateOnlyIfOther = field.onlyUpdateIfOtherFieldsBeingSet_BETA;
|
|
426
560
|
if (value === undefined) {
|
|
427
561
|
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
428
562
|
if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
|
|
@@ -436,11 +570,17 @@ class Orchestrator {
|
|
|
436
570
|
if (defaultValue === undefined) {
|
|
437
571
|
throw new Error(`defaultValueOnCreate() returned undefined for field ${fieldName}`);
|
|
438
572
|
}
|
|
573
|
+
if (util_1.types.isPromise(defaultValue)) {
|
|
574
|
+
defaultValue = await defaultValue;
|
|
575
|
+
}
|
|
439
576
|
}
|
|
440
577
|
}
|
|
441
578
|
if (field.defaultValueOnEdit &&
|
|
442
579
|
this.actualOperation === action_1.WriteOperation.Edit) {
|
|
443
580
|
defaultValue = field.defaultValueOnEdit(builder, input);
|
|
581
|
+
if (util_1.types.isPromise(defaultValue)) {
|
|
582
|
+
defaultValue = await defaultValue;
|
|
583
|
+
}
|
|
444
584
|
}
|
|
445
585
|
}
|
|
446
586
|
if (value !== undefined) {
|
|
@@ -448,7 +588,12 @@ class Orchestrator {
|
|
|
448
588
|
}
|
|
449
589
|
if (defaultValue !== undefined) {
|
|
450
590
|
updateInput = true;
|
|
451
|
-
|
|
591
|
+
if (updateOnlyIfOther) {
|
|
592
|
+
defaultData[dbKey] = defaultValue;
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
data[dbKey] = defaultValue;
|
|
596
|
+
}
|
|
452
597
|
this.defaultFieldsByFieldName[fieldName] = defaultValue;
|
|
453
598
|
this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
|
|
454
599
|
}
|
|
@@ -476,7 +621,7 @@ class Orchestrator {
|
|
|
476
621
|
// now format and validate...
|
|
477
622
|
if (value === null) {
|
|
478
623
|
if (!field.nullable) {
|
|
479
|
-
|
|
624
|
+
return new Error(`field ${fieldName} set to null for non-nullable field`);
|
|
480
625
|
}
|
|
481
626
|
}
|
|
482
627
|
else if (value === undefined) {
|
|
@@ -487,14 +632,14 @@ class Orchestrator {
|
|
|
487
632
|
// server default allowed
|
|
488
633
|
field.serverDefault === undefined &&
|
|
489
634
|
this.actualOperation === action_1.WriteOperation.Insert) {
|
|
490
|
-
|
|
635
|
+
return new Error(`required field ${fieldName} not set`);
|
|
491
636
|
}
|
|
492
637
|
}
|
|
493
638
|
else if (this.isBuilder(value)) {
|
|
494
639
|
if (field.valid) {
|
|
495
640
|
const valid = await field.valid(value);
|
|
496
641
|
if (!valid) {
|
|
497
|
-
|
|
642
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
498
643
|
}
|
|
499
644
|
}
|
|
500
645
|
// keep track of dependencies to resolve
|
|
@@ -507,7 +652,7 @@ class Orchestrator {
|
|
|
507
652
|
// TODO this could be async. handle this better
|
|
508
653
|
const valid = await field.valid(value);
|
|
509
654
|
if (!valid) {
|
|
510
|
-
|
|
655
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
511
656
|
}
|
|
512
657
|
}
|
|
513
658
|
if (field.format) {
|
|
@@ -517,26 +662,52 @@ class Orchestrator {
|
|
|
517
662
|
return value;
|
|
518
663
|
}
|
|
519
664
|
async formatAndValidateFields(schemaFields, editedFields) {
|
|
665
|
+
const errors = [];
|
|
520
666
|
const op = this.actualOperation;
|
|
521
667
|
if (op === action_1.WriteOperation.Delete) {
|
|
522
|
-
return;
|
|
668
|
+
return [];
|
|
523
669
|
}
|
|
524
670
|
// build up data to be saved...
|
|
525
671
|
let data = {};
|
|
526
672
|
let logValues = {};
|
|
673
|
+
let needsFullDataChecks = [];
|
|
527
674
|
for (const [fieldName, field] of schemaFields) {
|
|
528
675
|
let value = editedFields.get(fieldName);
|
|
676
|
+
if (field.validateWithFullData) {
|
|
677
|
+
needsFullDataChecks.push(fieldName);
|
|
678
|
+
}
|
|
529
679
|
if (value === undefined && op === action_1.WriteOperation.Insert) {
|
|
530
680
|
// null allowed
|
|
531
681
|
value = this.defaultFieldsByFieldName[fieldName];
|
|
532
682
|
}
|
|
533
683
|
let dbKey = this.getStorageKey(fieldName);
|
|
534
|
-
|
|
684
|
+
let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
|
|
685
|
+
if (ret instanceof Error) {
|
|
686
|
+
errors.push(ret);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
value = ret;
|
|
690
|
+
}
|
|
535
691
|
if (value !== undefined) {
|
|
536
692
|
data[dbKey] = value;
|
|
537
693
|
logValues[dbKey] = field.logValue(value);
|
|
538
694
|
}
|
|
539
695
|
}
|
|
696
|
+
for (const fieldName of needsFullDataChecks) {
|
|
697
|
+
const field = schemaFields.get(fieldName);
|
|
698
|
+
let value = editedFields.get(fieldName);
|
|
699
|
+
// @ts-ignore...
|
|
700
|
+
// type hackery because it's hard
|
|
701
|
+
const v = await field.validateWithFullData(value, this.options.builder);
|
|
702
|
+
if (!v) {
|
|
703
|
+
if (value === undefined) {
|
|
704
|
+
errors.push(new Error(`field ${fieldName} set to undefined when it can't be nullable`));
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
errors.push(new Error(`field ${fieldName} set to null when it can't be nullable`));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
540
711
|
// we ignored default values while editing.
|
|
541
712
|
// if we're editing and there's data, add default values
|
|
542
713
|
if (op === action_1.WriteOperation.Edit && this.hasData(data)) {
|
|
@@ -546,26 +717,45 @@ class Orchestrator {
|
|
|
546
717
|
let dbKey = this.getStorageKey(fieldName);
|
|
547
718
|
// no value, let's just default
|
|
548
719
|
if (data[dbKey] === undefined) {
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
720
|
+
const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
|
|
721
|
+
if (ret instanceof Error) {
|
|
722
|
+
errors.push(ret);
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
data[dbKey] = ret;
|
|
726
|
+
logValues[dbKey] = field.logValue(ret);
|
|
727
|
+
}
|
|
552
728
|
}
|
|
553
729
|
}
|
|
554
730
|
}
|
|
555
731
|
this.validatedFields = data;
|
|
556
732
|
this.logValues = logValues;
|
|
733
|
+
return errors;
|
|
557
734
|
}
|
|
558
735
|
async valid() {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
catch (e) {
|
|
563
|
-
(0, logger_1.log)("error", e);
|
|
736
|
+
const errors = await this.validate();
|
|
737
|
+
if (errors.length) {
|
|
738
|
+
errors.map((err) => (0, logger_1.log)("error", err));
|
|
564
739
|
return false;
|
|
565
740
|
}
|
|
566
741
|
return true;
|
|
567
742
|
}
|
|
568
743
|
async validX() {
|
|
744
|
+
const errors = await this.validate();
|
|
745
|
+
if (errors.length) {
|
|
746
|
+
// just throw the first one...
|
|
747
|
+
// TODO we should ideally throw all of them
|
|
748
|
+
throw errors[0];
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* @experimental API that's not guaranteed to remain in the future which returns
|
|
753
|
+
* a list of errors encountered
|
|
754
|
+
* 0 errors indicates valid
|
|
755
|
+
* NOTE that this currently doesn't catch errors returned by validators().
|
|
756
|
+
* If those throws, this still throws and doesn't return them
|
|
757
|
+
*/
|
|
758
|
+
async validWithErrors() {
|
|
569
759
|
return this.validate();
|
|
570
760
|
}
|
|
571
761
|
async build() {
|
|
@@ -573,6 +763,7 @@ class Orchestrator {
|
|
|
573
763
|
await this.validX();
|
|
574
764
|
let ops = [this.buildMainOp()];
|
|
575
765
|
await this.buildEdgeOps(ops);
|
|
766
|
+
// TODO throw if we try and create a new changeset after previously creating one
|
|
576
767
|
return new EntChangeset(this.options.viewer, this.options.builder.placeholderID, this.options.loaderOptions.ent, ops, this.dependencies, this.changesets, this.options);
|
|
577
768
|
}
|
|
578
769
|
async viewerForEntLoad(data) {
|
|
@@ -580,7 +771,7 @@ class Orchestrator {
|
|
|
580
771
|
if (!action || !action.viewerForEntLoad) {
|
|
581
772
|
return this.options.viewer;
|
|
582
773
|
}
|
|
583
|
-
return action.viewerForEntLoad(data);
|
|
774
|
+
return action.viewerForEntLoad(data, action.builder.viewer.context);
|
|
584
775
|
}
|
|
585
776
|
async returnedRow() {
|
|
586
777
|
if (this.mainOp && this.mainOp.returnedRow) {
|
|
@@ -615,6 +806,9 @@ class Orchestrator {
|
|
|
615
806
|
}
|
|
616
807
|
}
|
|
617
808
|
exports.Orchestrator = Orchestrator;
|
|
809
|
+
function randomNum() {
|
|
810
|
+
return Math.random().toString(10).substring(2);
|
|
811
|
+
}
|
|
618
812
|
class EntChangeset {
|
|
619
813
|
constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
|
|
620
814
|
this.viewer = viewer;
|
|
@@ -625,6 +819,9 @@ class EntChangeset {
|
|
|
625
819
|
this.changesets = changesets;
|
|
626
820
|
this.options = options;
|
|
627
821
|
}
|
|
822
|
+
static changesetFrom(builder, ops) {
|
|
823
|
+
return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
|
|
824
|
+
}
|
|
628
825
|
executor() {
|
|
629
826
|
if (this._executor) {
|
|
630
827
|
return this._executor;
|
package/action/privacy.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { Builder } from "./action";
|
|
|
2
2
|
import { Viewer, ID, Ent, PrivacyResult, PrivacyPolicyRule } from "../core/base";
|
|
3
3
|
export declare class DenyIfBuilder implements PrivacyPolicyRule {
|
|
4
4
|
private id?;
|
|
5
|
-
constructor(id?: ID | Builder<Ent> | undefined);
|
|
5
|
+
constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
|
|
6
6
|
apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
|
|
7
7
|
}
|
|
8
8
|
export declare class AllowIfBuilder implements PrivacyPolicyRule {
|
|
9
9
|
private id?;
|
|
10
|
-
constructor(id?: ID | Builder<Ent> | undefined);
|
|
10
|
+
constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
|
|
11
11
|
apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
|
|
12
12
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Clause } from "../core/clause";
|
|
2
|
+
export interface RelativeFieldValue<T = BigInt | number> {
|
|
3
|
+
delta: T;
|
|
4
|
+
sqlExpression: (col: string) => Clause;
|
|
5
|
+
eval: (curr: T) => T;
|
|
6
|
+
}
|
|
7
|
+
export interface RelativeNumberValue<T> {
|
|
8
|
+
add?: T;
|
|
9
|
+
subtract?: T;
|
|
10
|
+
divide?: T;
|
|
11
|
+
multiply?: T;
|
|
12
|
+
modulo?: T;
|
|
13
|
+
}
|
|
14
|
+
declare function addNumber(delta: number): RelativeFieldValue<number>;
|
|
15
|
+
declare function addNumber(delta: BigInt): RelativeFieldValue<BigInt>;
|
|
16
|
+
declare function subtractNumber(delta: number): RelativeFieldValue<number>;
|
|
17
|
+
declare function subtractNumber(delta: BigInt): RelativeFieldValue<BigInt>;
|
|
18
|
+
declare function multiplyNumber(delta: number): RelativeFieldValue<number>;
|
|
19
|
+
declare function multiplyNumber(delta: BigInt): RelativeFieldValue<BigInt>;
|
|
20
|
+
declare function divideNumber(delta: number): RelativeFieldValue<number>;
|
|
21
|
+
declare function divideNumber(delta: BigInt): RelativeFieldValue<BigInt>;
|
|
22
|
+
declare function moduloNumber(delta: number): RelativeFieldValue<number>;
|
|
23
|
+
declare function moduloNumber(delta: BigInt): RelativeFieldValue<BigInt>;
|
|
24
|
+
export declare const NumberOps: {
|
|
25
|
+
addNumber: typeof addNumber;
|
|
26
|
+
moduloNumber: typeof moduloNumber;
|
|
27
|
+
divideNumber: typeof divideNumber;
|
|
28
|
+
subtractNumber: typeof subtractNumber;
|
|
29
|
+
multiplyNumber: typeof multiplyNumber;
|
|
30
|
+
};
|
|
31
|
+
export declare function convertRelativeInput(rel: RelativeNumberValue<BigInt>, col: string, existing: BigInt): {
|
|
32
|
+
value: BigInt;
|
|
33
|
+
clause: Clause;
|
|
34
|
+
};
|
|
35
|
+
export declare function convertRelativeInput(rel: RelativeNumberValue<number>, col: string, existing: number): {
|
|
36
|
+
value: number;
|
|
37
|
+
clause: Clause;
|
|
38
|
+
};
|
|
39
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number>, col: string, existing: number, expressions: Map<string, Clause>): number;
|
|
40
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number> | undefined, col: string, existing: number, expressions: Map<string, Clause>): number | undefined;
|
|
41
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number> | null, col: string, existing: number | null, expressions: Map<string, Clause>): number | null;
|
|
42
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number> | null | undefined, col: string, existing: number | null, expressions: Map<string, Clause>): number | undefined | null;
|
|
43
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt>, col: string, existing: BigInt, expressions: Map<string, Clause>): BigInt;
|
|
44
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt> | undefined, col: string, existing: BigInt, expressions: Map<string, Clause>): BigInt | undefined;
|
|
45
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt> | null, col: string, existing: BigInt | null, expressions: Map<string, Clause>): BigInt | null;
|
|
46
|
+
export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt> | null | undefined, col: string, existing: BigInt | null, expressions: Map<string, Clause>): BigInt | null | undefined;
|
|
47
|
+
export {};
|