@snowtop/ent 0.1.0-alpha10 → 0.1.0-alpha101
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 +40 -16
- package/action/orchestrator.js +230 -62
- 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 +49 -26
- package/core/base.js +7 -1
- package/core/clause.d.ts +88 -7
- package/core/clause.js +355 -63
- package/core/config.d.ts +12 -1
- package/core/config.js +7 -1
- package/core/context.d.ts +5 -3
- 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 +81 -25
- package/core/ent.js +636 -193
- 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 +2 -2
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +6 -5
- package/core/loaders/object_loader.js +67 -59
- package/core/loaders/query_loader.d.ts +6 -12
- 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 +26 -0
- package/core/query/custom_clause_query.js +78 -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 +469 -236
- package/core/viewer.d.ts +3 -3
- package/core/viewer.js +1 -1
- package/graphql/graphql.d.ts +14 -7
- package/graphql/graphql.js +23 -7
- package/graphql/index.d.ts +0 -1
- package/graphql/index.js +1 -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/imports/index.d.ts +6 -1
- package/imports/index.js +19 -4
- package/index.d.ts +12 -5
- package/index.js +20 -7
- package/package.json +17 -16
- package/parse_schema/parse.d.ts +29 -9
- package/parse_schema/parse.js +118 -11
- package/schema/base_schema.d.ts +5 -3
- package/schema/base_schema.js +5 -0
- package/schema/field.d.ts +74 -20
- package/schema/field.js +174 -69
- package/schema/index.d.ts +2 -2
- package/schema/index.js +5 -1
- package/schema/json_field.d.ts +13 -1
- package/schema/json_field.js +28 -1
- package/schema/schema.d.ts +81 -18
- package/schema/schema.js +24 -17
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +57 -21
- package/scripts/custom_compiler.js +10 -6
- package/scripts/custom_graphql.js +117 -30
- package/scripts/{transform_code.d.ts → migrate_v0.1.d.ts} +0 -0
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/{transform_schema.d.ts → move_types.d.ts} +0 -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 +46 -47
- package/testutils/builder.js +108 -65
- 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} +179 -44
- 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 +27 -8
- 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 +7 -3
- 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 +7 -4
- 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 +26 -2
- package/tsc/ast.js +163 -17
- package/tsc/compilerOptions.d.ts +2 -1
- package/tsc/compilerOptions.js +11 -2
- 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 +59 -0
- package/tsc/transform_schema.d.ts +27 -0
- package/{scripts → tsc}/transform_schema.js +145 -119
- package/graphql/enums.d.ts +0 -3
- package/graphql/enums.js +0 -25
- package/scripts/transform_code.js +0 -114
package/action/orchestrator.js
CHANGED
|
@@ -1,13 +1,40 @@
|
|
|
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
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
2
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
29
|
exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
|
|
4
30
|
const ent_1 = require("../core/ent");
|
|
5
31
|
const schema_1 = require("../schema/schema");
|
|
6
32
|
const action_1 = require("../action");
|
|
7
|
-
const camel_case_1 = require("camel-case");
|
|
8
33
|
const privacy_1 = require("../core/privacy");
|
|
9
34
|
const executor_1 = require("./executor");
|
|
10
35
|
const logger_1 = require("../core/logger");
|
|
36
|
+
const memoizee_1 = __importDefault(require("memoizee"));
|
|
37
|
+
const clause = __importStar(require("../core/clause"));
|
|
11
38
|
var edgeDirection;
|
|
12
39
|
(function (edgeDirection) {
|
|
13
40
|
edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
|
|
@@ -63,6 +90,11 @@ class Orchestrator {
|
|
|
63
90
|
this.viewer = options.viewer;
|
|
64
91
|
this.actualOperation = this.options.operation;
|
|
65
92
|
this.existingEnt = this.options.builder.existingEnt;
|
|
93
|
+
this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
|
|
94
|
+
}
|
|
95
|
+
// don't type this because we don't care
|
|
96
|
+
__getOptions() {
|
|
97
|
+
return this.options;
|
|
66
98
|
}
|
|
67
99
|
addEdge(edge, op) {
|
|
68
100
|
this.edgeSet.add(edge.edgeType);
|
|
@@ -148,6 +180,10 @@ class Orchestrator {
|
|
|
148
180
|
if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
|
|
149
181
|
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
150
182
|
}
|
|
183
|
+
if (this.options.expressions &&
|
|
184
|
+
this.actualOperation !== action_1.WriteOperation.Edit) {
|
|
185
|
+
throw new Error(`expressions are only supported in edit operations for now`);
|
|
186
|
+
}
|
|
151
187
|
const opts = {
|
|
152
188
|
fields: this.validatedFields,
|
|
153
189
|
tableName: this.options.tableName,
|
|
@@ -155,6 +191,8 @@ class Orchestrator {
|
|
|
155
191
|
key: this.options.key,
|
|
156
192
|
loadEntOptions: this.options.loaderOptions,
|
|
157
193
|
placeholderID: this.options.builder.placeholderID,
|
|
194
|
+
whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
|
|
195
|
+
expressions: this.options.expressions,
|
|
158
196
|
};
|
|
159
197
|
if (this.logValues) {
|
|
160
198
|
opts.fieldsToLog = this.logValues;
|
|
@@ -208,7 +246,7 @@ class Orchestrator {
|
|
|
208
246
|
ops.push(edgeOp);
|
|
209
247
|
const edgeData = edgeDatas.get(edgeType);
|
|
210
248
|
if (!edgeData) {
|
|
211
|
-
throw new Error(`could not load edge data for ${edgeType}`);
|
|
249
|
+
throw new Error(`could not load edge data for '${edgeType}'`);
|
|
212
250
|
}
|
|
213
251
|
if (edgeData.symmetricEdge) {
|
|
214
252
|
ops.push(edgeOp.symmetricEdge());
|
|
@@ -269,9 +307,28 @@ class Orchestrator {
|
|
|
269
307
|
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
270
308
|
return this.existingEnt;
|
|
271
309
|
}
|
|
272
|
-
const { editedData } = await this.
|
|
310
|
+
const { editedData } = await this.memoizedGetFields();
|
|
273
311
|
return this.getEntForPrivacyPolicyImpl(editedData);
|
|
274
312
|
}
|
|
313
|
+
// this gets the fields that were explicitly set plus any default or transformed values
|
|
314
|
+
// mainly exists to get default fields e.g. default id to be used in triggers
|
|
315
|
+
// NOTE: this API may change in the future
|
|
316
|
+
// doesn't work to get ids for autoincrement keys
|
|
317
|
+
async getEditedData() {
|
|
318
|
+
const { editedData } = await this.memoizedGetFields();
|
|
319
|
+
return editedData;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* @returns validated and formatted fields that would be written to the db
|
|
323
|
+
* throws an error if called before valid() or validX() has been called
|
|
324
|
+
*/
|
|
325
|
+
getValidatedFields() {
|
|
326
|
+
if (this.validatedFields === null) {
|
|
327
|
+
throw new Error(`trying to call getValidatedFields before validating fields`);
|
|
328
|
+
}
|
|
329
|
+
return this.validatedFields;
|
|
330
|
+
}
|
|
331
|
+
// Note: this is memoized. call memoizedGetFields instead
|
|
275
332
|
async getFieldsInfo() {
|
|
276
333
|
const action = this.options.action;
|
|
277
334
|
const builder = this.options.builder;
|
|
@@ -290,7 +347,7 @@ class Orchestrator {
|
|
|
290
347
|
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
291
348
|
}
|
|
292
349
|
}
|
|
293
|
-
const { schemaFields, editedData } = await this.
|
|
350
|
+
const { schemaFields, editedData } = await this.memoizedGetFields();
|
|
294
351
|
const action = this.options.action;
|
|
295
352
|
const builder = this.options.builder;
|
|
296
353
|
// this runs in following phases:
|
|
@@ -299,55 +356,104 @@ class Orchestrator {
|
|
|
299
356
|
// * triggers
|
|
300
357
|
// * validators
|
|
301
358
|
let privacyPolicy = action?.getPrivacyPolicy();
|
|
359
|
+
let privacyError = null;
|
|
302
360
|
if (privacyPolicy) {
|
|
303
|
-
|
|
361
|
+
try {
|
|
362
|
+
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
privacyError = err;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// privacyError should return first since it's less confusing
|
|
369
|
+
if (privacyError !== null) {
|
|
370
|
+
return [privacyError];
|
|
304
371
|
}
|
|
305
372
|
// have to run triggers which update fields first before field and other validators
|
|
306
373
|
// so running this first to build things up
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
374
|
+
if (action?.getTriggers) {
|
|
375
|
+
await this.triggers(action, builder, action.getTriggers());
|
|
376
|
+
}
|
|
377
|
+
let validators = [];
|
|
378
|
+
if (action?.getValidators) {
|
|
379
|
+
validators = action.getValidators();
|
|
310
380
|
}
|
|
311
|
-
let validators = action?.validators || [];
|
|
312
381
|
// not ideal we're calling this twice. fix...
|
|
313
382
|
// needed for now. may need to rewrite some of this?
|
|
314
383
|
const editedFields2 = await this.options.editedFields();
|
|
315
|
-
await Promise.all([
|
|
384
|
+
const [errors, errs2] = await Promise.all([
|
|
316
385
|
this.formatAndValidateFields(schemaFields, editedFields2),
|
|
317
386
|
this.validators(validators, action, builder),
|
|
318
387
|
]);
|
|
388
|
+
errors.push(...errs2);
|
|
389
|
+
return errors;
|
|
319
390
|
}
|
|
320
391
|
async triggers(action, builder, triggers) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (Array.isArray(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
392
|
+
let groups = [];
|
|
393
|
+
let lastArray = 0;
|
|
394
|
+
let prevWasArray = false;
|
|
395
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
396
|
+
let t = triggers[i];
|
|
397
|
+
if (Array.isArray(t)) {
|
|
398
|
+
if (!prevWasArray) {
|
|
399
|
+
// @ts-ignore
|
|
400
|
+
groups.push(triggers.slice(lastArray, i));
|
|
331
401
|
}
|
|
402
|
+
groups.push(t);
|
|
403
|
+
prevWasArray = true;
|
|
404
|
+
lastArray++;
|
|
332
405
|
}
|
|
333
|
-
else
|
|
334
|
-
|
|
406
|
+
else {
|
|
407
|
+
if (i === triggers.length - 1) {
|
|
408
|
+
// @ts-ignore
|
|
409
|
+
groups.push(triggers.slice(lastArray, i + 1));
|
|
410
|
+
}
|
|
411
|
+
prevWasArray = false;
|
|
335
412
|
}
|
|
336
|
-
}
|
|
413
|
+
}
|
|
414
|
+
for (const triggers of groups) {
|
|
415
|
+
await Promise.all(triggers.map(async (trigger) => {
|
|
416
|
+
let ret = await trigger.changeset(builder, action.getInput());
|
|
417
|
+
if (Array.isArray(ret)) {
|
|
418
|
+
ret = await Promise.all(ret);
|
|
419
|
+
}
|
|
420
|
+
if (Array.isArray(ret)) {
|
|
421
|
+
for (const v of ret) {
|
|
422
|
+
if (typeof v === "object") {
|
|
423
|
+
this.changesets.push(v);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else if (ret) {
|
|
428
|
+
this.changesets.push(ret);
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
337
432
|
}
|
|
338
433
|
async validators(validators, action, builder) {
|
|
339
|
-
|
|
340
|
-
validators.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
434
|
+
const errors = [];
|
|
435
|
+
await Promise.all(validators.map(async (v) => {
|
|
436
|
+
try {
|
|
437
|
+
const r = await v.validate(builder, action.getInput());
|
|
438
|
+
if (r instanceof Error) {
|
|
439
|
+
errors.push(r);
|
|
440
|
+
}
|
|
344
441
|
}
|
|
345
|
-
|
|
346
|
-
|
|
442
|
+
catch (err) {
|
|
443
|
+
errors.push(err);
|
|
444
|
+
}
|
|
445
|
+
}));
|
|
446
|
+
return errors;
|
|
347
447
|
}
|
|
348
448
|
isBuilder(val) {
|
|
349
449
|
return val.placeholderID !== undefined;
|
|
350
450
|
}
|
|
451
|
+
getInputKey(k) {
|
|
452
|
+
return this.options.fieldInfo[k].inputKey;
|
|
453
|
+
}
|
|
454
|
+
getStorageKey(k) {
|
|
455
|
+
return this.options.fieldInfo[k].dbCol;
|
|
456
|
+
}
|
|
351
457
|
async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
|
|
352
458
|
let data = {};
|
|
353
459
|
let defaultData = {};
|
|
@@ -357,24 +463,30 @@ class Orchestrator {
|
|
|
357
463
|
// if action transformations. always do it
|
|
358
464
|
// if disable transformations set, don't do schema transform and just do the right thing
|
|
359
465
|
// else apply schema tranformation if it exists
|
|
360
|
-
let transformed;
|
|
466
|
+
let transformed = null;
|
|
467
|
+
const sqlOp = this.getSQLStatementOperation();
|
|
361
468
|
if (action?.transformWrite) {
|
|
362
469
|
transformed = await action.transformWrite({
|
|
363
|
-
|
|
364
|
-
|
|
470
|
+
builder,
|
|
471
|
+
input,
|
|
472
|
+
op: sqlOp,
|
|
365
473
|
data: editedFields,
|
|
366
|
-
existingEnt: this.existingEnt,
|
|
367
474
|
});
|
|
368
475
|
}
|
|
369
476
|
else if (!this.disableTransformations) {
|
|
370
477
|
transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
|
|
371
|
-
|
|
372
|
-
|
|
478
|
+
builder,
|
|
479
|
+
input,
|
|
480
|
+
op: sqlOp,
|
|
373
481
|
data: editedFields,
|
|
374
|
-
existingEnt: this.existingEnt,
|
|
375
482
|
});
|
|
376
483
|
}
|
|
377
484
|
if (transformed) {
|
|
485
|
+
if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
|
|
486
|
+
if (!transformed.existingEnt) {
|
|
487
|
+
throw new Error(`cannot transform an insert operation without providing an existing ent`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
378
490
|
if (transformed.data) {
|
|
379
491
|
updateInput = true;
|
|
380
492
|
for (const k in transformed.data) {
|
|
@@ -386,17 +498,23 @@ class Orchestrator {
|
|
|
386
498
|
if (field.format) {
|
|
387
499
|
val = field.format(transformed.data[k]);
|
|
388
500
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(k)] = val;
|
|
501
|
+
data[this.getStorageKey(k)] = val;
|
|
502
|
+
this.defaultFieldsByTSName[this.getInputKey(k)] = val;
|
|
392
503
|
// hmm do we need this?
|
|
393
504
|
// TODO how to do this for local tests?
|
|
394
505
|
// this.defaultFieldsByFieldName[k] = val;
|
|
395
506
|
}
|
|
396
507
|
}
|
|
508
|
+
if (transformed.changeset) {
|
|
509
|
+
const ct = await transformed.changeset();
|
|
510
|
+
this.changesets.push(ct);
|
|
511
|
+
}
|
|
397
512
|
this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
|
|
398
513
|
if (transformed.existingEnt) {
|
|
514
|
+
// @ts-ignore
|
|
399
515
|
this.existingEnt = transformed.existingEnt;
|
|
516
|
+
// modify existing ent in builder. it's readonly in generated ents but doesn't apply here
|
|
517
|
+
builder.existingEnt = transformed.existingEnt;
|
|
400
518
|
}
|
|
401
519
|
}
|
|
402
520
|
// transforming before doing default fields so that we don't create a new id
|
|
@@ -404,7 +522,7 @@ class Orchestrator {
|
|
|
404
522
|
for (const [fieldName, field] of schemaFields) {
|
|
405
523
|
let value = editedFields.get(fieldName);
|
|
406
524
|
let defaultValue = undefined;
|
|
407
|
-
let dbKey =
|
|
525
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
408
526
|
if (value === undefined) {
|
|
409
527
|
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
410
528
|
if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
|
|
@@ -432,8 +550,7 @@ class Orchestrator {
|
|
|
432
550
|
updateInput = true;
|
|
433
551
|
defaultData[dbKey] = defaultValue;
|
|
434
552
|
this.defaultFieldsByFieldName[fieldName] = defaultValue;
|
|
435
|
-
|
|
436
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
|
|
553
|
+
this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
|
|
437
554
|
}
|
|
438
555
|
}
|
|
439
556
|
// if there's data changing, add data
|
|
@@ -459,7 +576,7 @@ class Orchestrator {
|
|
|
459
576
|
// now format and validate...
|
|
460
577
|
if (value === null) {
|
|
461
578
|
if (!field.nullable) {
|
|
462
|
-
|
|
579
|
+
return new Error(`field ${fieldName} set to null for non-nullable field`);
|
|
463
580
|
}
|
|
464
581
|
}
|
|
465
582
|
else if (value === undefined) {
|
|
@@ -470,14 +587,14 @@ class Orchestrator {
|
|
|
470
587
|
// server default allowed
|
|
471
588
|
field.serverDefault === undefined &&
|
|
472
589
|
this.actualOperation === action_1.WriteOperation.Insert) {
|
|
473
|
-
|
|
590
|
+
return new Error(`required field ${fieldName} not set`);
|
|
474
591
|
}
|
|
475
592
|
}
|
|
476
593
|
else if (this.isBuilder(value)) {
|
|
477
594
|
if (field.valid) {
|
|
478
595
|
const valid = await field.valid(value);
|
|
479
596
|
if (!valid) {
|
|
480
|
-
|
|
597
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
481
598
|
}
|
|
482
599
|
}
|
|
483
600
|
// keep track of dependencies to resolve
|
|
@@ -490,66 +607,110 @@ class Orchestrator {
|
|
|
490
607
|
// TODO this could be async. handle this better
|
|
491
608
|
const valid = await field.valid(value);
|
|
492
609
|
if (!valid) {
|
|
493
|
-
|
|
610
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
494
611
|
}
|
|
495
612
|
}
|
|
496
613
|
if (field.format) {
|
|
497
|
-
|
|
498
|
-
value = await Promise.resolve(field.format(value));
|
|
614
|
+
value = await field.format(value);
|
|
499
615
|
}
|
|
500
616
|
}
|
|
501
617
|
return value;
|
|
502
618
|
}
|
|
503
619
|
async formatAndValidateFields(schemaFields, editedFields) {
|
|
620
|
+
const errors = [];
|
|
504
621
|
const op = this.actualOperation;
|
|
505
622
|
if (op === action_1.WriteOperation.Delete) {
|
|
506
|
-
return;
|
|
623
|
+
return [];
|
|
507
624
|
}
|
|
508
625
|
// build up data to be saved...
|
|
509
626
|
let data = {};
|
|
510
627
|
let logValues = {};
|
|
628
|
+
let needsFullDataChecks = [];
|
|
511
629
|
for (const [fieldName, field] of schemaFields) {
|
|
512
630
|
let value = editedFields.get(fieldName);
|
|
631
|
+
if (field.validateWithFullData) {
|
|
632
|
+
needsFullDataChecks.push(fieldName);
|
|
633
|
+
}
|
|
513
634
|
if (value === undefined && op === action_1.WriteOperation.Insert) {
|
|
514
635
|
// null allowed
|
|
515
636
|
value = this.defaultFieldsByFieldName[fieldName];
|
|
516
637
|
}
|
|
517
|
-
let dbKey =
|
|
518
|
-
|
|
638
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
639
|
+
let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
|
|
640
|
+
if (ret instanceof Error) {
|
|
641
|
+
errors.push(ret);
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
value = ret;
|
|
645
|
+
}
|
|
519
646
|
if (value !== undefined) {
|
|
520
647
|
data[dbKey] = value;
|
|
521
648
|
logValues[dbKey] = field.logValue(value);
|
|
522
649
|
}
|
|
523
650
|
}
|
|
651
|
+
for (const fieldName of needsFullDataChecks) {
|
|
652
|
+
const field = schemaFields.get(fieldName);
|
|
653
|
+
let value = editedFields.get(fieldName);
|
|
654
|
+
// @ts-ignore...
|
|
655
|
+
// type hackery because it's hard
|
|
656
|
+
const v = await field.validateWithFullData(value, this.options.builder);
|
|
657
|
+
if (!v) {
|
|
658
|
+
if (value === undefined) {
|
|
659
|
+
errors.push(new Error(`field ${fieldName} set to undefined when it can't be nullable`));
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
errors.push(new Error(`field ${fieldName} set to null when it can't be nullable`));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
524
666
|
// we ignored default values while editing.
|
|
525
667
|
// if we're editing and there's data, add default values
|
|
526
668
|
if (op === action_1.WriteOperation.Edit && this.hasData(data)) {
|
|
527
669
|
for (const fieldName in this.defaultFieldsByFieldName) {
|
|
528
670
|
const defaultValue = this.defaultFieldsByFieldName[fieldName];
|
|
529
671
|
let field = schemaFields.get(fieldName);
|
|
530
|
-
let dbKey =
|
|
672
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
531
673
|
// no value, let's just default
|
|
532
674
|
if (data[dbKey] === undefined) {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
675
|
+
const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
|
|
676
|
+
if (ret instanceof Error) {
|
|
677
|
+
errors.push(ret);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
data[dbKey] = ret;
|
|
681
|
+
logValues[dbKey] = field.logValue(ret);
|
|
682
|
+
}
|
|
536
683
|
}
|
|
537
684
|
}
|
|
538
685
|
}
|
|
539
686
|
this.validatedFields = data;
|
|
540
687
|
this.logValues = logValues;
|
|
688
|
+
return errors;
|
|
541
689
|
}
|
|
542
690
|
async valid() {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
catch (e) {
|
|
547
|
-
(0, logger_1.log)("error", e);
|
|
691
|
+
const errors = await this.validate();
|
|
692
|
+
if (errors.length) {
|
|
693
|
+
errors.map((err) => (0, logger_1.log)("error", err));
|
|
548
694
|
return false;
|
|
549
695
|
}
|
|
550
696
|
return true;
|
|
551
697
|
}
|
|
552
698
|
async validX() {
|
|
699
|
+
const errors = await this.validate();
|
|
700
|
+
if (errors.length) {
|
|
701
|
+
// just throw the first one...
|
|
702
|
+
// TODO we should ideally throw all of them
|
|
703
|
+
throw errors[0];
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* @experimental API that's not guaranteed to remain in the future which returns
|
|
708
|
+
* a list of errors encountered
|
|
709
|
+
* 0 errors indicates valid
|
|
710
|
+
* NOTE that this currently doesn't catch errors returned by validators().
|
|
711
|
+
* If those throws, this still throws and doesn't return them
|
|
712
|
+
*/
|
|
713
|
+
async validWithErrors() {
|
|
553
714
|
return this.validate();
|
|
554
715
|
}
|
|
555
716
|
async build() {
|
|
@@ -557,6 +718,7 @@ class Orchestrator {
|
|
|
557
718
|
await this.validX();
|
|
558
719
|
let ops = [this.buildMainOp()];
|
|
559
720
|
await this.buildEdgeOps(ops);
|
|
721
|
+
// TODO throw if we try and create a new changeset after previously creating one
|
|
560
722
|
return new EntChangeset(this.options.viewer, this.options.builder.placeholderID, this.options.loaderOptions.ent, ops, this.dependencies, this.changesets, this.options);
|
|
561
723
|
}
|
|
562
724
|
async viewerForEntLoad(data) {
|
|
@@ -564,7 +726,7 @@ class Orchestrator {
|
|
|
564
726
|
if (!action || !action.viewerForEntLoad) {
|
|
565
727
|
return this.options.viewer;
|
|
566
728
|
}
|
|
567
|
-
return action.viewerForEntLoad(data);
|
|
729
|
+
return action.viewerForEntLoad(data, action.builder.viewer.context);
|
|
568
730
|
}
|
|
569
731
|
async returnedRow() {
|
|
570
732
|
if (this.mainOp && this.mainOp.returnedRow) {
|
|
@@ -599,6 +761,9 @@ class Orchestrator {
|
|
|
599
761
|
}
|
|
600
762
|
}
|
|
601
763
|
exports.Orchestrator = Orchestrator;
|
|
764
|
+
function randomNum() {
|
|
765
|
+
return Math.random().toString(10).substring(2);
|
|
766
|
+
}
|
|
602
767
|
class EntChangeset {
|
|
603
768
|
constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
|
|
604
769
|
this.viewer = viewer;
|
|
@@ -609,6 +774,9 @@ class EntChangeset {
|
|
|
609
774
|
this.changesets = changesets;
|
|
610
775
|
this.options = options;
|
|
611
776
|
}
|
|
777
|
+
static changesetFrom(builder, ops) {
|
|
778
|
+
return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
|
|
779
|
+
}
|
|
612
780
|
executor() {
|
|
613
781
|
if (this._executor) {
|
|
614
782
|
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 {};
|