@snowtop/ent 0.1.0-alpha8 → 0.1.0-alpha80
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 +27 -25
- package/action/executor.d.ts +3 -3
- package/action/executor.js +2 -2
- package/action/experimental_action.d.ts +29 -22
- package/action/experimental_action.js +29 -6
- package/action/orchestrator.d.ts +37 -16
- package/action/orchestrator.js +184 -54
- package/action/privacy.d.ts +2 -2
- package/core/base.d.ts +38 -22
- package/core/clause.d.ts +65 -7
- package/core/clause.js +261 -54
- package/core/config.d.ts +8 -0
- package/core/context.d.ts +5 -3
- package/core/context.js +7 -2
- package/core/convert.d.ts +1 -1
- package/core/db.d.ts +1 -2
- package/core/db.js +1 -1
- package/core/ent.d.ts +76 -24
- package/core/ent.js +459 -143
- package/core/loaders/assoc_count_loader.d.ts +2 -2
- package/core/loaders/assoc_count_loader.js +6 -1
- package/core/loaders/assoc_edge_loader.d.ts +2 -2
- package/core/loaders/loader.js +5 -5
- package/core/loaders/object_loader.d.ts +4 -3
- package/core/loaders/object_loader.js +22 -5
- package/core/loaders/query_loader.d.ts +2 -2
- package/core/loaders/raw_count_loader.d.ts +2 -2
- package/core/logger.d.ts +1 -1
- package/core/logger.js +1 -0
- package/core/privacy.d.ts +26 -25
- package/core/privacy.js +21 -25
- package/core/query/assoc_query.d.ts +6 -6
- package/core/query/custom_query.d.ts +5 -5
- package/core/query/query.d.ts +1 -1
- package/core/query/shared_assoc_test.d.ts +1 -1
- package/core/query/shared_assoc_test.js +17 -5
- package/core/query/shared_test.d.ts +3 -0
- package/core/query/shared_test.js +95 -17
- package/core/viewer.d.ts +3 -3
- package/core/viewer.js +1 -1
- package/graphql/builtins/connection.js +3 -3
- package/graphql/builtins/edge.js +2 -2
- package/graphql/builtins/node.js +1 -1
- package/graphql/graphql.js +8 -2
- package/graphql/query/connection_type.js +6 -6
- package/graphql/query/edge_connection.d.ts +9 -9
- package/graphql/query/page_info.d.ts +1 -1
- package/graphql/query/page_info.js +4 -4
- package/graphql/query/shared_assoc_test.js +2 -2
- package/graphql/scalars/time.d.ts +1 -1
- package/index.d.ts +10 -5
- package/index.js +13 -6
- package/package.json +2 -2
- package/parse_schema/parse.d.ts +14 -2
- package/parse_schema/parse.js +40 -2
- package/schema/base_schema.d.ts +1 -1
- package/schema/base_schema.js +3 -0
- package/schema/field.d.ts +36 -7
- package/schema/field.js +70 -2
- package/schema/index.d.ts +2 -2
- package/schema/json_field.d.ts +13 -1
- package/schema/json_field.js +28 -1
- package/schema/schema.d.ts +61 -10
- package/schema/schema.js +18 -4
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +43 -4
- package/scripts/custom_graphql.js +5 -1
- package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
- package/scripts/migrate_v0.1.js +36 -0
- package/scripts/read_schema.js +25 -2
- package/testutils/builder.d.ts +31 -21
- package/testutils/builder.js +82 -29
- 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} +15 -3
- package/testutils/db/{test_db.js → temp_db.js} +63 -15
- package/testutils/db/value.d.ts +6 -0
- package/testutils/db/value.js +251 -0
- package/testutils/db_time_zone.d.ts +4 -0
- package/testutils/db_time_zone.js +41 -0
- package/testutils/ent-graphql-tests/index.js +19 -12
- package/testutils/fake_data/fake_contact.d.ts +3 -3
- package/testutils/fake_data/fake_contact.js +9 -6
- package/testutils/fake_data/fake_event.d.ts +3 -3
- package/testutils/fake_data/fake_event.js +8 -5
- package/testutils/fake_data/fake_user.d.ts +4 -4
- package/testutils/fake_data/fake_user.js +16 -13
- package/testutils/fake_data/test_helpers.d.ts +2 -2
- package/testutils/fake_data/test_helpers.js +5 -5
- package/testutils/fake_data/user_query.d.ts +2 -2
- 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 +58 -0
- package/testutils/write.d.ts +2 -2
- package/testutils/write.js +29 -7
- package/tsc/ast.d.ts +44 -0
- package/tsc/ast.js +267 -0
- package/tsc/compilerOptions.d.ts +6 -0
- package/tsc/compilerOptions.js +40 -1
- package/tsc/move_generated.d.ts +1 -0
- package/tsc/move_generated.js +160 -0
- package/tsc/transform.d.ts +21 -0
- package/tsc/transform.js +167 -0
- package/tsc/transform_action.d.ts +22 -0
- package/tsc/transform_action.js +179 -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/tsc/transform_schema.js +379 -0
- package/scripts/transform_schema.js +0 -437
package/action/orchestrator.js
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
2
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
25
|
exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
|
|
4
26
|
const ent_1 = require("../core/ent");
|
|
5
27
|
const schema_1 = require("../schema/schema");
|
|
6
28
|
const action_1 = require("../action");
|
|
7
|
-
const camel_case_1 = require("camel-case");
|
|
8
29
|
const privacy_1 = require("../core/privacy");
|
|
9
30
|
const executor_1 = require("./executor");
|
|
10
31
|
const logger_1 = require("../core/logger");
|
|
32
|
+
const memoizee_1 = __importDefault(require("memoizee"));
|
|
33
|
+
const clause = __importStar(require("../core/clause"));
|
|
11
34
|
var edgeDirection;
|
|
12
35
|
(function (edgeDirection) {
|
|
13
36
|
edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
|
|
@@ -63,6 +86,7 @@ class Orchestrator {
|
|
|
63
86
|
this.viewer = options.viewer;
|
|
64
87
|
this.actualOperation = this.options.operation;
|
|
65
88
|
this.existingEnt = this.options.builder.existingEnt;
|
|
89
|
+
this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
|
|
66
90
|
}
|
|
67
91
|
addEdge(edge, op) {
|
|
68
92
|
this.edgeSet.add(edge.edgeType);
|
|
@@ -155,6 +179,7 @@ class Orchestrator {
|
|
|
155
179
|
key: this.options.key,
|
|
156
180
|
loadEntOptions: this.options.loaderOptions,
|
|
157
181
|
placeholderID: this.options.builder.placeholderID,
|
|
182
|
+
whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
|
|
158
183
|
};
|
|
159
184
|
if (this.logValues) {
|
|
160
185
|
opts.fieldsToLog = this.logValues;
|
|
@@ -269,9 +294,28 @@ class Orchestrator {
|
|
|
269
294
|
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
270
295
|
return this.existingEnt;
|
|
271
296
|
}
|
|
272
|
-
const { editedData } = await this.
|
|
297
|
+
const { editedData } = await this.memoizedGetFields();
|
|
273
298
|
return this.getEntForPrivacyPolicyImpl(editedData);
|
|
274
299
|
}
|
|
300
|
+
// this gets the fields that were explicitly set plus any default or transformed values
|
|
301
|
+
// mainly exists to get default fields e.g. default id to be used in triggers
|
|
302
|
+
// NOTE: this API may change in the future
|
|
303
|
+
// doesn't work to get ids for autoincrement keys
|
|
304
|
+
async getEditedData() {
|
|
305
|
+
const { editedData } = await this.memoizedGetFields();
|
|
306
|
+
return editedData;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* @returns validated and formatted fields that would be written to the db
|
|
310
|
+
* throws an error if called before valid() or validX() has been called
|
|
311
|
+
*/
|
|
312
|
+
getValidatedFields() {
|
|
313
|
+
if (this.validatedFields === null) {
|
|
314
|
+
throw new Error(`trying to call getValidatedFields before validating fields`);
|
|
315
|
+
}
|
|
316
|
+
return this.validatedFields;
|
|
317
|
+
}
|
|
318
|
+
// Note: this is memoized. call memoizedGetFields instead
|
|
275
319
|
async getFieldsInfo() {
|
|
276
320
|
const action = this.options.action;
|
|
277
321
|
const builder = this.options.builder;
|
|
@@ -290,7 +334,7 @@ class Orchestrator {
|
|
|
290
334
|
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
291
335
|
}
|
|
292
336
|
}
|
|
293
|
-
const { schemaFields, editedData } = await this.
|
|
337
|
+
const { schemaFields, editedData } = await this.memoizedGetFields();
|
|
294
338
|
const action = this.options.action;
|
|
295
339
|
const builder = this.options.builder;
|
|
296
340
|
// this runs in following phases:
|
|
@@ -299,43 +343,81 @@ class Orchestrator {
|
|
|
299
343
|
// * triggers
|
|
300
344
|
// * validators
|
|
301
345
|
let privacyPolicy = action?.getPrivacyPolicy();
|
|
346
|
+
let privacyError = null;
|
|
302
347
|
if (privacyPolicy) {
|
|
303
|
-
|
|
348
|
+
try {
|
|
349
|
+
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
privacyError = err;
|
|
353
|
+
}
|
|
304
354
|
}
|
|
305
355
|
// have to run triggers which update fields first before field and other validators
|
|
306
356
|
// so running this first to build things up
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
357
|
+
if (action?.getTriggers) {
|
|
358
|
+
await this.triggers(action, builder, action.getTriggers());
|
|
359
|
+
}
|
|
360
|
+
let validators = [];
|
|
361
|
+
if (action?.getValidators) {
|
|
362
|
+
validators = action.getValidators();
|
|
310
363
|
}
|
|
311
|
-
let validators = action?.validators || [];
|
|
312
364
|
// not ideal we're calling this twice. fix...
|
|
313
365
|
// needed for now. may need to rewrite some of this?
|
|
314
366
|
const editedFields2 = await this.options.editedFields();
|
|
315
|
-
await Promise.all([
|
|
367
|
+
const [errors, _] = await Promise.all([
|
|
316
368
|
this.formatAndValidateFields(schemaFields, editedFields2),
|
|
317
369
|
this.validators(validators, action, builder),
|
|
318
370
|
]);
|
|
371
|
+
if (privacyError !== null) {
|
|
372
|
+
errors.unshift(privacyError);
|
|
373
|
+
}
|
|
374
|
+
return errors;
|
|
319
375
|
}
|
|
320
376
|
async triggers(action, builder, triggers) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (Array.isArray(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
377
|
+
let groups = [];
|
|
378
|
+
let lastArray = 0;
|
|
379
|
+
let prevWasArray = false;
|
|
380
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
381
|
+
let t = triggers[i];
|
|
382
|
+
if (Array.isArray(t)) {
|
|
383
|
+
if (!prevWasArray) {
|
|
384
|
+
// @ts-ignore
|
|
385
|
+
groups.push(triggers.slice(lastArray, i));
|
|
331
386
|
}
|
|
387
|
+
groups.push(t);
|
|
388
|
+
prevWasArray = true;
|
|
389
|
+
lastArray++;
|
|
332
390
|
}
|
|
333
|
-
else
|
|
334
|
-
|
|
391
|
+
else {
|
|
392
|
+
if (i === triggers.length - 1) {
|
|
393
|
+
// @ts-ignore
|
|
394
|
+
groups.push(triggers.slice(lastArray, i + 1));
|
|
395
|
+
}
|
|
396
|
+
prevWasArray = false;
|
|
335
397
|
}
|
|
336
|
-
}
|
|
398
|
+
}
|
|
399
|
+
for (const triggers of groups) {
|
|
400
|
+
await Promise.all(triggers.map(async (trigger) => {
|
|
401
|
+
let ret = await trigger.changeset(builder, action.getInput());
|
|
402
|
+
if (Array.isArray(ret)) {
|
|
403
|
+
ret = await Promise.all(ret);
|
|
404
|
+
}
|
|
405
|
+
if (Array.isArray(ret)) {
|
|
406
|
+
for (const v of ret) {
|
|
407
|
+
if (typeof v === "object") {
|
|
408
|
+
this.changesets.push(v);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else if (ret) {
|
|
413
|
+
this.changesets.push(ret);
|
|
414
|
+
}
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
337
417
|
}
|
|
338
418
|
async validators(validators, action, builder) {
|
|
419
|
+
// TODO need to catch errors and return it...
|
|
420
|
+
// don't need it initially since what we need this for doesn't have the errors
|
|
339
421
|
let promises = [];
|
|
340
422
|
validators.forEach((validator) => {
|
|
341
423
|
let res = validator.validate(builder, action.getInput());
|
|
@@ -348,6 +430,12 @@ class Orchestrator {
|
|
|
348
430
|
isBuilder(val) {
|
|
349
431
|
return val.placeholderID !== undefined;
|
|
350
432
|
}
|
|
433
|
+
getInputKey(k) {
|
|
434
|
+
return this.options.fieldInfo[k].inputKey;
|
|
435
|
+
}
|
|
436
|
+
getStorageKey(k) {
|
|
437
|
+
return this.options.fieldInfo[k].dbCol;
|
|
438
|
+
}
|
|
351
439
|
async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
|
|
352
440
|
let data = {};
|
|
353
441
|
let defaultData = {};
|
|
@@ -357,24 +445,30 @@ class Orchestrator {
|
|
|
357
445
|
// if action transformations. always do it
|
|
358
446
|
// if disable transformations set, don't do schema transform and just do the right thing
|
|
359
447
|
// else apply schema tranformation if it exists
|
|
360
|
-
let transformed;
|
|
448
|
+
let transformed = null;
|
|
449
|
+
const sqlOp = this.getSQLStatementOperation();
|
|
361
450
|
if (action?.transformWrite) {
|
|
362
451
|
transformed = await action.transformWrite({
|
|
363
|
-
|
|
364
|
-
|
|
452
|
+
builder,
|
|
453
|
+
input,
|
|
454
|
+
op: sqlOp,
|
|
365
455
|
data: editedFields,
|
|
366
|
-
existingEnt: this.existingEnt,
|
|
367
456
|
});
|
|
368
457
|
}
|
|
369
458
|
else if (!this.disableTransformations) {
|
|
370
459
|
transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
|
|
371
|
-
|
|
372
|
-
|
|
460
|
+
builder,
|
|
461
|
+
input,
|
|
462
|
+
op: sqlOp,
|
|
373
463
|
data: editedFields,
|
|
374
|
-
existingEnt: this.existingEnt,
|
|
375
464
|
});
|
|
376
465
|
}
|
|
377
466
|
if (transformed) {
|
|
467
|
+
if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
|
|
468
|
+
if (!transformed.existingEnt) {
|
|
469
|
+
throw new Error(`cannot transform an insert operation without providing an existing ent`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
378
472
|
if (transformed.data) {
|
|
379
473
|
updateInput = true;
|
|
380
474
|
for (const k in transformed.data) {
|
|
@@ -386,17 +480,23 @@ class Orchestrator {
|
|
|
386
480
|
if (field.format) {
|
|
387
481
|
val = field.format(transformed.data[k]);
|
|
388
482
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(k)] = val;
|
|
483
|
+
data[this.getStorageKey(k)] = val;
|
|
484
|
+
this.defaultFieldsByTSName[this.getInputKey(k)] = val;
|
|
392
485
|
// hmm do we need this?
|
|
393
486
|
// TODO how to do this for local tests?
|
|
394
487
|
// this.defaultFieldsByFieldName[k] = val;
|
|
395
488
|
}
|
|
396
489
|
}
|
|
490
|
+
if (transformed.changeset) {
|
|
491
|
+
const ct = await transformed.changeset();
|
|
492
|
+
this.changesets.push(ct);
|
|
493
|
+
}
|
|
397
494
|
this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
|
|
398
495
|
if (transformed.existingEnt) {
|
|
496
|
+
// @ts-ignore
|
|
399
497
|
this.existingEnt = transformed.existingEnt;
|
|
498
|
+
// modify existing ent in builder. it's readonly in generated ents but doesn't apply here
|
|
499
|
+
builder.existingEnt = transformed.existingEnt;
|
|
400
500
|
}
|
|
401
501
|
}
|
|
402
502
|
// transforming before doing default fields so that we don't create a new id
|
|
@@ -404,7 +504,7 @@ class Orchestrator {
|
|
|
404
504
|
for (const [fieldName, field] of schemaFields) {
|
|
405
505
|
let value = editedFields.get(fieldName);
|
|
406
506
|
let defaultValue = undefined;
|
|
407
|
-
let dbKey =
|
|
507
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
408
508
|
if (value === undefined) {
|
|
409
509
|
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
410
510
|
if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
|
|
@@ -432,8 +532,7 @@ class Orchestrator {
|
|
|
432
532
|
updateInput = true;
|
|
433
533
|
defaultData[dbKey] = defaultValue;
|
|
434
534
|
this.defaultFieldsByFieldName[fieldName] = defaultValue;
|
|
435
|
-
|
|
436
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
|
|
535
|
+
this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
|
|
437
536
|
}
|
|
438
537
|
}
|
|
439
538
|
// if there's data changing, add data
|
|
@@ -459,7 +558,7 @@ class Orchestrator {
|
|
|
459
558
|
// now format and validate...
|
|
460
559
|
if (value === null) {
|
|
461
560
|
if (!field.nullable) {
|
|
462
|
-
|
|
561
|
+
return new Error(`field ${fieldName} set to null for non-nullable field`);
|
|
463
562
|
}
|
|
464
563
|
}
|
|
465
564
|
else if (value === undefined) {
|
|
@@ -470,14 +569,14 @@ class Orchestrator {
|
|
|
470
569
|
// server default allowed
|
|
471
570
|
field.serverDefault === undefined &&
|
|
472
571
|
this.actualOperation === action_1.WriteOperation.Insert) {
|
|
473
|
-
|
|
572
|
+
return new Error(`required field ${fieldName} not set`);
|
|
474
573
|
}
|
|
475
574
|
}
|
|
476
575
|
else if (this.isBuilder(value)) {
|
|
477
576
|
if (field.valid) {
|
|
478
577
|
const valid = await field.valid(value);
|
|
479
578
|
if (!valid) {
|
|
480
|
-
|
|
579
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
481
580
|
}
|
|
482
581
|
}
|
|
483
582
|
// keep track of dependencies to resolve
|
|
@@ -490,20 +589,20 @@ class Orchestrator {
|
|
|
490
589
|
// TODO this could be async. handle this better
|
|
491
590
|
const valid = await field.valid(value);
|
|
492
591
|
if (!valid) {
|
|
493
|
-
|
|
592
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
494
593
|
}
|
|
495
594
|
}
|
|
496
595
|
if (field.format) {
|
|
497
|
-
|
|
498
|
-
value = await Promise.resolve(field.format(value));
|
|
596
|
+
value = await field.format(value);
|
|
499
597
|
}
|
|
500
598
|
}
|
|
501
599
|
return value;
|
|
502
600
|
}
|
|
503
601
|
async formatAndValidateFields(schemaFields, editedFields) {
|
|
602
|
+
const errors = [];
|
|
504
603
|
const op = this.actualOperation;
|
|
505
604
|
if (op === action_1.WriteOperation.Delete) {
|
|
506
|
-
return;
|
|
605
|
+
return [];
|
|
507
606
|
}
|
|
508
607
|
// build up data to be saved...
|
|
509
608
|
let data = {};
|
|
@@ -514,8 +613,14 @@ class Orchestrator {
|
|
|
514
613
|
// null allowed
|
|
515
614
|
value = this.defaultFieldsByFieldName[fieldName];
|
|
516
615
|
}
|
|
517
|
-
let dbKey =
|
|
518
|
-
|
|
616
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
617
|
+
let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
|
|
618
|
+
if (ret instanceof Error) {
|
|
619
|
+
errors.push(ret);
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
value = ret;
|
|
623
|
+
}
|
|
519
624
|
if (value !== undefined) {
|
|
520
625
|
data[dbKey] = value;
|
|
521
626
|
logValues[dbKey] = field.logValue(value);
|
|
@@ -527,29 +632,48 @@ class Orchestrator {
|
|
|
527
632
|
for (const fieldName in this.defaultFieldsByFieldName) {
|
|
528
633
|
const defaultValue = this.defaultFieldsByFieldName[fieldName];
|
|
529
634
|
let field = schemaFields.get(fieldName);
|
|
530
|
-
let dbKey =
|
|
635
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
531
636
|
// no value, let's just default
|
|
532
637
|
if (data[dbKey] === undefined) {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
638
|
+
const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
|
|
639
|
+
if (ret instanceof Error) {
|
|
640
|
+
errors.push(ret);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
data[dbKey] = ret;
|
|
644
|
+
logValues[dbKey] = field.logValue(ret);
|
|
645
|
+
}
|
|
536
646
|
}
|
|
537
647
|
}
|
|
538
648
|
}
|
|
539
649
|
this.validatedFields = data;
|
|
540
650
|
this.logValues = logValues;
|
|
651
|
+
return errors;
|
|
541
652
|
}
|
|
542
653
|
async valid() {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
catch (e) {
|
|
547
|
-
(0, logger_1.log)("error", e);
|
|
654
|
+
const errors = await this.validate();
|
|
655
|
+
if (errors.length) {
|
|
656
|
+
errors.map((err) => (0, logger_1.log)("error", err));
|
|
548
657
|
return false;
|
|
549
658
|
}
|
|
550
659
|
return true;
|
|
551
660
|
}
|
|
552
661
|
async validX() {
|
|
662
|
+
const errors = await this.validate();
|
|
663
|
+
if (errors.length) {
|
|
664
|
+
// just throw the first one...
|
|
665
|
+
// TODO we should ideally throw all of them
|
|
666
|
+
throw errors[0];
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* @experimental API that's not guaranteed to remain in the future which returns
|
|
671
|
+
* a list of errors encountered
|
|
672
|
+
* 0 errors indicates valid
|
|
673
|
+
* NOTE that this currently doesn't catch errors returned by validators().
|
|
674
|
+
* If those throws, this still throws and doesn't return them
|
|
675
|
+
*/
|
|
676
|
+
async validWithErrors() {
|
|
553
677
|
return this.validate();
|
|
554
678
|
}
|
|
555
679
|
async build() {
|
|
@@ -564,7 +688,7 @@ class Orchestrator {
|
|
|
564
688
|
if (!action || !action.viewerForEntLoad) {
|
|
565
689
|
return this.options.viewer;
|
|
566
690
|
}
|
|
567
|
-
return action.viewerForEntLoad(data);
|
|
691
|
+
return action.viewerForEntLoad(data, action.builder.viewer.context);
|
|
568
692
|
}
|
|
569
693
|
async returnedRow() {
|
|
570
694
|
if (this.mainOp && this.mainOp.returnedRow) {
|
|
@@ -599,6 +723,9 @@ class Orchestrator {
|
|
|
599
723
|
}
|
|
600
724
|
}
|
|
601
725
|
exports.Orchestrator = Orchestrator;
|
|
726
|
+
function randomNum() {
|
|
727
|
+
return Math.random().toString(10).substring(2);
|
|
728
|
+
}
|
|
602
729
|
class EntChangeset {
|
|
603
730
|
constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
|
|
604
731
|
this.viewer = viewer;
|
|
@@ -609,6 +736,9 @@ class EntChangeset {
|
|
|
609
736
|
this.changesets = changesets;
|
|
610
737
|
this.options = options;
|
|
611
738
|
}
|
|
739
|
+
static changesetFrom(builder, ops) {
|
|
740
|
+
return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
|
|
741
|
+
}
|
|
612
742
|
executor() {
|
|
613
743
|
if (this._executor) {
|
|
614
744
|
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
|
}
|
package/core/base.d.ts
CHANGED
|
@@ -5,16 +5,23 @@ export interface Loader<T, V> {
|
|
|
5
5
|
loadMany?(keys: T[]): Promise<(V | null)[]>;
|
|
6
6
|
clearAll(): any;
|
|
7
7
|
}
|
|
8
|
+
interface LoaderWithLoadMany<T, V> extends Loader<T, V> {
|
|
9
|
+
loadMany(keys: T[]): Promise<(V | null)[]>;
|
|
10
|
+
}
|
|
8
11
|
export interface LoaderFactory<T, V> {
|
|
9
12
|
name: string;
|
|
10
13
|
createLoader(context?: Context): Loader<T, V>;
|
|
11
14
|
}
|
|
15
|
+
interface LoaderFactoryWithLoaderMany<T, V> extends LoaderFactory<T, V> {
|
|
16
|
+
createLoader(context?: Context): LoaderWithLoadMany<T, V>;
|
|
17
|
+
}
|
|
12
18
|
export interface ConfigurableLoaderFactory<T, V> extends LoaderFactory<T, V> {
|
|
13
19
|
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): Loader<T, V>;
|
|
14
20
|
}
|
|
15
21
|
export declare type EdgeQueryableDataOptions = Partial<Pick<QueryableDataOptions, "limit" | "orderby" | "clause">>;
|
|
16
22
|
export interface PrimableLoader<T, V> extends Loader<T, V> {
|
|
17
23
|
prime(d: Data): void;
|
|
24
|
+
primeAll?(d: Data): void;
|
|
18
25
|
}
|
|
19
26
|
interface cache {
|
|
20
27
|
getLoader<T, V>(name: string, create: () => Loader<T, V>): Loader<T, V>;
|
|
@@ -23,6 +30,7 @@ interface cache {
|
|
|
23
30
|
primeCache(options: queryOptions, rows: Data[]): void;
|
|
24
31
|
primeCache(options: queryOptions, rows: Data): void;
|
|
25
32
|
clearCache(): void;
|
|
33
|
+
getEntCache(): Map<string, Ent | Error | null>;
|
|
26
34
|
}
|
|
27
35
|
interface queryOptions {
|
|
28
36
|
fields: string[];
|
|
@@ -30,27 +38,27 @@ interface queryOptions {
|
|
|
30
38
|
clause: clause.Clause;
|
|
31
39
|
orderby?: string;
|
|
32
40
|
}
|
|
33
|
-
export interface Context {
|
|
34
|
-
getViewer():
|
|
41
|
+
export interface Context<TViewer extends Viewer = Viewer> {
|
|
42
|
+
getViewer(): TViewer;
|
|
35
43
|
cache?: cache;
|
|
36
44
|
}
|
|
37
|
-
export interface Viewer {
|
|
38
|
-
viewerID:
|
|
39
|
-
viewer: () => Promise<
|
|
45
|
+
export interface Viewer<TEnt extends any = Ent<any> | null, TID extends any = ID | null> {
|
|
46
|
+
viewerID: TID;
|
|
47
|
+
viewer: () => Promise<TEnt>;
|
|
40
48
|
instanceKey: () => string;
|
|
41
|
-
context?: Context
|
|
49
|
+
context?: Context<any>;
|
|
42
50
|
}
|
|
43
|
-
export interface Ent {
|
|
51
|
+
export interface Ent<TViewer extends Viewer = Viewer> {
|
|
44
52
|
id: ID;
|
|
45
|
-
viewer:
|
|
46
|
-
|
|
53
|
+
viewer: TViewer;
|
|
54
|
+
getPrivacyPolicy(): PrivacyPolicy<this, TViewer>;
|
|
47
55
|
nodeType: string;
|
|
48
56
|
}
|
|
49
57
|
export declare type Data = {
|
|
50
58
|
[key: string]: any;
|
|
51
59
|
};
|
|
52
|
-
export interface EntConstructor<
|
|
53
|
-
new (viewer:
|
|
60
|
+
export interface EntConstructor<TEnt extends Ent, TViewer extends Viewer = Viewer> {
|
|
61
|
+
new (viewer: TViewer, data: Data): TEnt;
|
|
54
62
|
}
|
|
55
63
|
export declare type ID = string | number;
|
|
56
64
|
export interface DataOptions {
|
|
@@ -72,6 +80,7 @@ export interface QueryDataOptions {
|
|
|
72
80
|
orderby?: string;
|
|
73
81
|
groupby?: string;
|
|
74
82
|
limit?: number;
|
|
83
|
+
disableTransformations?: boolean;
|
|
75
84
|
}
|
|
76
85
|
export interface LoadRowOptions extends QueryableDataOptions {
|
|
77
86
|
}
|
|
@@ -82,17 +91,24 @@ export interface CreateRowOptions extends DataOptions {
|
|
|
82
91
|
fieldsToLog?: Data;
|
|
83
92
|
}
|
|
84
93
|
export interface EditRowOptions extends CreateRowOptions {
|
|
85
|
-
|
|
94
|
+
whereClause: clause.Clause;
|
|
86
95
|
}
|
|
87
|
-
interface LoadableEntOptions<
|
|
88
|
-
loaderFactory:
|
|
89
|
-
ent: EntConstructor<
|
|
96
|
+
interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> {
|
|
97
|
+
loaderFactory: LoaderFactoryWithOptions;
|
|
98
|
+
ent: EntConstructor<TEnt, TViewer>;
|
|
99
|
+
}
|
|
100
|
+
interface LoaderFactoryWithOptions extends LoaderFactoryWithLoaderMany<any, Data | null> {
|
|
101
|
+
options?: SelectDataOptions;
|
|
90
102
|
}
|
|
91
|
-
export interface LoadEntOptions<
|
|
103
|
+
export interface LoadEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends LoadableEntOptions<TEnt, TViewer>, SelectBaseDataOptions {
|
|
92
104
|
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
93
105
|
}
|
|
94
|
-
export interface
|
|
95
|
-
|
|
106
|
+
export interface SelectCustomDataOptions extends SelectBaseDataOptions {
|
|
107
|
+
loaderFactory: LoaderFactoryWithOptions;
|
|
108
|
+
prime?: boolean;
|
|
109
|
+
}
|
|
110
|
+
export interface LoadCustomEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> extends SelectCustomDataOptions {
|
|
111
|
+
ent: EntConstructor<TEnt, TViewer>;
|
|
96
112
|
fieldPrivacy?: Map<string, PrivacyPolicy>;
|
|
97
113
|
}
|
|
98
114
|
export interface LoaderInfo {
|
|
@@ -121,10 +137,10 @@ export declare function Allow(): PrivacyResult;
|
|
|
121
137
|
export declare function Skip(): PrivacyResult;
|
|
122
138
|
export declare function Deny(): PrivacyResult;
|
|
123
139
|
export declare function DenyWithReason(e: PrivacyError | string): PrivacyResult;
|
|
124
|
-
export interface PrivacyPolicyRule<TEnt extends Ent = Ent> {
|
|
125
|
-
apply(v:
|
|
140
|
+
export interface PrivacyPolicyRule<TEnt extends Ent = Ent, TViewer = Viewer> {
|
|
141
|
+
apply(v: TViewer, ent?: TEnt): Promise<PrivacyResult>;
|
|
126
142
|
}
|
|
127
|
-
export interface PrivacyPolicy<TEnt extends Ent = Ent> {
|
|
128
|
-
rules: PrivacyPolicyRule<TEnt>[];
|
|
143
|
+
export interface PrivacyPolicy<TEnt extends Ent = Ent, TViewer = Viewer> {
|
|
144
|
+
rules: PrivacyPolicyRule<TEnt, TViewer>[];
|
|
129
145
|
}
|
|
130
146
|
export {};
|