@snowtop/ent 0.1.0-alpha9 → 0.1.0-alpha91
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 +36 -31
- package/action/action.js +2 -6
- 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 +38 -16
- package/action/orchestrator.js +223 -61
- package/action/privacy.d.ts +2 -2
- package/core/base.d.ts +45 -24
- package/core/base.js +7 -1
- package/core/clause.d.ts +83 -7
- package/core/clause.js +334 -63
- package/core/config.d.ts +8 -0
- package/core/config.js +5 -1
- package/core/context.d.ts +5 -3
- package/core/context.js +20 -2
- package/core/convert.d.ts +1 -1
- package/core/db.d.ts +2 -2
- package/core/db.js +6 -2
- package/core/ent.d.ts +79 -24
- package/core/ent.js +527 -176
- package/core/loaders/assoc_count_loader.d.ts +3 -2
- package/core/loaders/assoc_count_loader.js +14 -2
- package/core/loaders/assoc_edge_loader.d.ts +2 -2
- package/core/loaders/assoc_edge_loader.js +5 -1
- 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 +26 -25
- 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 +34 -43
- 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.js +16 -6
- package/graphql/query/edge_connection.d.ts +9 -9
- package/graphql/query/page_info.d.ts +1 -1
- package/graphql/query/shared_edge_connection.js +1 -15
- package/imports/index.js +5 -1
- package/index.d.ts +11 -5
- package/index.js +20 -7
- package/package.json +1 -1
- package/parse_schema/parse.d.ts +12 -3
- package/parse_schema/parse.js +70 -11
- package/schema/base_schema.js +3 -0
- package/schema/field.d.ts +44 -8
- package/schema/field.js +136 -10
- 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 +66 -11
- package/schema/schema.js +18 -4
- package/schema/struct_field.d.ts +11 -1
- package/schema/struct_field.js +44 -5
- package/scripts/custom_compiler.js +10 -6
- package/scripts/custom_graphql.js +13 -4
- 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 +20 -5
- package/testutils/builder.d.ts +31 -21
- package/testutils/builder.js +83 -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} +20 -7
- package/testutils/db/{test_db.js → temp_db.js} +102 -36
- package/testutils/db/value.d.ts +6 -0
- package/testutils/db/value.js +251 -0
- package/testutils/db_mock.js +3 -1
- package/testutils/db_time_zone.d.ts +4 -0
- package/testutils/db_time_zone.js +41 -0
- package/testutils/ent-graphql-tests/index.js +8 -1
- 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 -4
- package/testutils/fake_data/fake_contact.js +14 -6
- package/testutils/fake_data/fake_event.d.ts +5 -3
- package/testutils/fake_data/fake_event.js +8 -5
- package/testutils/fake_data/fake_tag.d.ts +35 -0
- package/testutils/fake_data/fake_tag.js +88 -0
- package/testutils/fake_data/fake_user.d.ts +6 -4
- package/testutils/fake_data/fake_user.js +16 -13
- 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 +44 -0
- package/tsc/ast.js +271 -0
- package/tsc/compilerOptions.d.ts +6 -0
- package/tsc/compilerOptions.js +45 -2
- package/tsc/move_generated.d.ts +1 -0
- package/tsc/move_generated.js +164 -0
- package/tsc/transform.d.ts +21 -0
- package/tsc/transform.js +171 -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/tsc/transform_schema.js +383 -0
- package/scripts/transform_schema.js +0 -445
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);
|
|
@@ -155,6 +187,7 @@ class Orchestrator {
|
|
|
155
187
|
key: this.options.key,
|
|
156
188
|
loadEntOptions: this.options.loaderOptions,
|
|
157
189
|
placeholderID: this.options.builder.placeholderID,
|
|
190
|
+
whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
|
|
158
191
|
};
|
|
159
192
|
if (this.logValues) {
|
|
160
193
|
opts.fieldsToLog = this.logValues;
|
|
@@ -269,9 +302,28 @@ class Orchestrator {
|
|
|
269
302
|
if (this.actualOperation !== action_1.WriteOperation.Insert) {
|
|
270
303
|
return this.existingEnt;
|
|
271
304
|
}
|
|
272
|
-
const { editedData } = await this.
|
|
305
|
+
const { editedData } = await this.memoizedGetFields();
|
|
273
306
|
return this.getEntForPrivacyPolicyImpl(editedData);
|
|
274
307
|
}
|
|
308
|
+
// this gets the fields that were explicitly set plus any default or transformed values
|
|
309
|
+
// mainly exists to get default fields e.g. default id to be used in triggers
|
|
310
|
+
// NOTE: this API may change in the future
|
|
311
|
+
// doesn't work to get ids for autoincrement keys
|
|
312
|
+
async getEditedData() {
|
|
313
|
+
const { editedData } = await this.memoizedGetFields();
|
|
314
|
+
return editedData;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* @returns validated and formatted fields that would be written to the db
|
|
318
|
+
* throws an error if called before valid() or validX() has been called
|
|
319
|
+
*/
|
|
320
|
+
getValidatedFields() {
|
|
321
|
+
if (this.validatedFields === null) {
|
|
322
|
+
throw new Error(`trying to call getValidatedFields before validating fields`);
|
|
323
|
+
}
|
|
324
|
+
return this.validatedFields;
|
|
325
|
+
}
|
|
326
|
+
// Note: this is memoized. call memoizedGetFields instead
|
|
275
327
|
async getFieldsInfo() {
|
|
276
328
|
const action = this.options.action;
|
|
277
329
|
const builder = this.options.builder;
|
|
@@ -290,7 +342,7 @@ class Orchestrator {
|
|
|
290
342
|
throw new Error(`existing ent required with operation ${this.actualOperation}`);
|
|
291
343
|
}
|
|
292
344
|
}
|
|
293
|
-
const { schemaFields, editedData } = await this.
|
|
345
|
+
const { schemaFields, editedData } = await this.memoizedGetFields();
|
|
294
346
|
const action = this.options.action;
|
|
295
347
|
const builder = this.options.builder;
|
|
296
348
|
// this runs in following phases:
|
|
@@ -299,55 +351,104 @@ class Orchestrator {
|
|
|
299
351
|
// * triggers
|
|
300
352
|
// * validators
|
|
301
353
|
let privacyPolicy = action?.getPrivacyPolicy();
|
|
354
|
+
let privacyError = null;
|
|
302
355
|
if (privacyPolicy) {
|
|
303
|
-
|
|
356
|
+
try {
|
|
357
|
+
await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
privacyError = err;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// privacyError should return first since it's less confusing
|
|
364
|
+
if (privacyError !== null) {
|
|
365
|
+
return [privacyError];
|
|
304
366
|
}
|
|
305
367
|
// have to run triggers which update fields first before field and other validators
|
|
306
368
|
// so running this first to build things up
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
369
|
+
if (action?.getTriggers) {
|
|
370
|
+
await this.triggers(action, builder, action.getTriggers());
|
|
371
|
+
}
|
|
372
|
+
let validators = [];
|
|
373
|
+
if (action?.getValidators) {
|
|
374
|
+
validators = action.getValidators();
|
|
310
375
|
}
|
|
311
|
-
let validators = action?.validators || [];
|
|
312
376
|
// not ideal we're calling this twice. fix...
|
|
313
377
|
// needed for now. may need to rewrite some of this?
|
|
314
378
|
const editedFields2 = await this.options.editedFields();
|
|
315
|
-
await Promise.all([
|
|
379
|
+
const [errors, errs2] = await Promise.all([
|
|
316
380
|
this.formatAndValidateFields(schemaFields, editedFields2),
|
|
317
381
|
this.validators(validators, action, builder),
|
|
318
382
|
]);
|
|
383
|
+
errors.push(...errs2);
|
|
384
|
+
return errors;
|
|
319
385
|
}
|
|
320
386
|
async triggers(action, builder, triggers) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (Array.isArray(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
387
|
+
let groups = [];
|
|
388
|
+
let lastArray = 0;
|
|
389
|
+
let prevWasArray = false;
|
|
390
|
+
for (let i = 0; i < triggers.length; i++) {
|
|
391
|
+
let t = triggers[i];
|
|
392
|
+
if (Array.isArray(t)) {
|
|
393
|
+
if (!prevWasArray) {
|
|
394
|
+
// @ts-ignore
|
|
395
|
+
groups.push(triggers.slice(lastArray, i));
|
|
331
396
|
}
|
|
397
|
+
groups.push(t);
|
|
398
|
+
prevWasArray = true;
|
|
399
|
+
lastArray++;
|
|
332
400
|
}
|
|
333
|
-
else
|
|
334
|
-
|
|
401
|
+
else {
|
|
402
|
+
if (i === triggers.length - 1) {
|
|
403
|
+
// @ts-ignore
|
|
404
|
+
groups.push(triggers.slice(lastArray, i + 1));
|
|
405
|
+
}
|
|
406
|
+
prevWasArray = false;
|
|
335
407
|
}
|
|
336
|
-
}
|
|
408
|
+
}
|
|
409
|
+
for (const triggers of groups) {
|
|
410
|
+
await Promise.all(triggers.map(async (trigger) => {
|
|
411
|
+
let ret = await trigger.changeset(builder, action.getInput());
|
|
412
|
+
if (Array.isArray(ret)) {
|
|
413
|
+
ret = await Promise.all(ret);
|
|
414
|
+
}
|
|
415
|
+
if (Array.isArray(ret)) {
|
|
416
|
+
for (const v of ret) {
|
|
417
|
+
if (typeof v === "object") {
|
|
418
|
+
this.changesets.push(v);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else if (ret) {
|
|
423
|
+
this.changesets.push(ret);
|
|
424
|
+
}
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
337
427
|
}
|
|
338
428
|
async validators(validators, action, builder) {
|
|
339
|
-
|
|
340
|
-
validators.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
429
|
+
const errors = [];
|
|
430
|
+
await Promise.all(validators.map(async (v) => {
|
|
431
|
+
try {
|
|
432
|
+
const r = await v.validate(builder, action.getInput());
|
|
433
|
+
if (r instanceof Error) {
|
|
434
|
+
errors.push(r);
|
|
435
|
+
}
|
|
344
436
|
}
|
|
345
|
-
|
|
346
|
-
|
|
437
|
+
catch (err) {
|
|
438
|
+
errors.push(err);
|
|
439
|
+
}
|
|
440
|
+
}));
|
|
441
|
+
return errors;
|
|
347
442
|
}
|
|
348
443
|
isBuilder(val) {
|
|
349
444
|
return val.placeholderID !== undefined;
|
|
350
445
|
}
|
|
446
|
+
getInputKey(k) {
|
|
447
|
+
return this.options.fieldInfo[k].inputKey;
|
|
448
|
+
}
|
|
449
|
+
getStorageKey(k) {
|
|
450
|
+
return this.options.fieldInfo[k].dbCol;
|
|
451
|
+
}
|
|
351
452
|
async getFieldsWithDefaultValues(builder, schemaFields, editedFields, action) {
|
|
352
453
|
let data = {};
|
|
353
454
|
let defaultData = {};
|
|
@@ -357,24 +458,30 @@ class Orchestrator {
|
|
|
357
458
|
// if action transformations. always do it
|
|
358
459
|
// if disable transformations set, don't do schema transform and just do the right thing
|
|
359
460
|
// else apply schema tranformation if it exists
|
|
360
|
-
let transformed;
|
|
461
|
+
let transformed = null;
|
|
462
|
+
const sqlOp = this.getSQLStatementOperation();
|
|
361
463
|
if (action?.transformWrite) {
|
|
362
464
|
transformed = await action.transformWrite({
|
|
363
|
-
|
|
364
|
-
|
|
465
|
+
builder,
|
|
466
|
+
input,
|
|
467
|
+
op: sqlOp,
|
|
365
468
|
data: editedFields,
|
|
366
|
-
existingEnt: this.existingEnt,
|
|
367
469
|
});
|
|
368
470
|
}
|
|
369
471
|
else if (!this.disableTransformations) {
|
|
370
472
|
transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
|
|
371
|
-
|
|
372
|
-
|
|
473
|
+
builder,
|
|
474
|
+
input,
|
|
475
|
+
op: sqlOp,
|
|
373
476
|
data: editedFields,
|
|
374
|
-
existingEnt: this.existingEnt,
|
|
375
477
|
});
|
|
376
478
|
}
|
|
377
479
|
if (transformed) {
|
|
480
|
+
if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
|
|
481
|
+
if (!transformed.existingEnt) {
|
|
482
|
+
throw new Error(`cannot transform an insert operation without providing an existing ent`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
378
485
|
if (transformed.data) {
|
|
379
486
|
updateInput = true;
|
|
380
487
|
for (const k in transformed.data) {
|
|
@@ -386,17 +493,23 @@ class Orchestrator {
|
|
|
386
493
|
if (field.format) {
|
|
387
494
|
val = field.format(transformed.data[k]);
|
|
388
495
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(k)] = val;
|
|
496
|
+
data[this.getStorageKey(k)] = val;
|
|
497
|
+
this.defaultFieldsByTSName[this.getInputKey(k)] = val;
|
|
392
498
|
// hmm do we need this?
|
|
393
499
|
// TODO how to do this for local tests?
|
|
394
500
|
// this.defaultFieldsByFieldName[k] = val;
|
|
395
501
|
}
|
|
396
502
|
}
|
|
503
|
+
if (transformed.changeset) {
|
|
504
|
+
const ct = await transformed.changeset();
|
|
505
|
+
this.changesets.push(ct);
|
|
506
|
+
}
|
|
397
507
|
this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
|
|
398
508
|
if (transformed.existingEnt) {
|
|
509
|
+
// @ts-ignore
|
|
399
510
|
this.existingEnt = transformed.existingEnt;
|
|
511
|
+
// modify existing ent in builder. it's readonly in generated ents but doesn't apply here
|
|
512
|
+
builder.existingEnt = transformed.existingEnt;
|
|
400
513
|
}
|
|
401
514
|
}
|
|
402
515
|
// transforming before doing default fields so that we don't create a new id
|
|
@@ -404,7 +517,7 @@ class Orchestrator {
|
|
|
404
517
|
for (const [fieldName, field] of schemaFields) {
|
|
405
518
|
let value = editedFields.get(fieldName);
|
|
406
519
|
let defaultValue = undefined;
|
|
407
|
-
let dbKey =
|
|
520
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
408
521
|
if (value === undefined) {
|
|
409
522
|
if (this.actualOperation === action_1.WriteOperation.Insert) {
|
|
410
523
|
if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
|
|
@@ -432,8 +545,7 @@ class Orchestrator {
|
|
|
432
545
|
updateInput = true;
|
|
433
546
|
defaultData[dbKey] = defaultValue;
|
|
434
547
|
this.defaultFieldsByFieldName[fieldName] = defaultValue;
|
|
435
|
-
|
|
436
|
-
this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(fieldName)] = defaultValue;
|
|
548
|
+
this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
|
|
437
549
|
}
|
|
438
550
|
}
|
|
439
551
|
// if there's data changing, add data
|
|
@@ -459,7 +571,7 @@ class Orchestrator {
|
|
|
459
571
|
// now format and validate...
|
|
460
572
|
if (value === null) {
|
|
461
573
|
if (!field.nullable) {
|
|
462
|
-
|
|
574
|
+
return new Error(`field ${fieldName} set to null for non-nullable field`);
|
|
463
575
|
}
|
|
464
576
|
}
|
|
465
577
|
else if (value === undefined) {
|
|
@@ -470,14 +582,14 @@ class Orchestrator {
|
|
|
470
582
|
// server default allowed
|
|
471
583
|
field.serverDefault === undefined &&
|
|
472
584
|
this.actualOperation === action_1.WriteOperation.Insert) {
|
|
473
|
-
|
|
585
|
+
return new Error(`required field ${fieldName} not set`);
|
|
474
586
|
}
|
|
475
587
|
}
|
|
476
588
|
else if (this.isBuilder(value)) {
|
|
477
589
|
if (field.valid) {
|
|
478
590
|
const valid = await field.valid(value);
|
|
479
591
|
if (!valid) {
|
|
480
|
-
|
|
592
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
481
593
|
}
|
|
482
594
|
}
|
|
483
595
|
// keep track of dependencies to resolve
|
|
@@ -490,66 +602,110 @@ class Orchestrator {
|
|
|
490
602
|
// TODO this could be async. handle this better
|
|
491
603
|
const valid = await field.valid(value);
|
|
492
604
|
if (!valid) {
|
|
493
|
-
|
|
605
|
+
return new Error(`invalid field ${fieldName} with value ${value}`);
|
|
494
606
|
}
|
|
495
607
|
}
|
|
496
608
|
if (field.format) {
|
|
497
|
-
|
|
498
|
-
value = await Promise.resolve(field.format(value));
|
|
609
|
+
value = await field.format(value);
|
|
499
610
|
}
|
|
500
611
|
}
|
|
501
612
|
return value;
|
|
502
613
|
}
|
|
503
614
|
async formatAndValidateFields(schemaFields, editedFields) {
|
|
615
|
+
const errors = [];
|
|
504
616
|
const op = this.actualOperation;
|
|
505
617
|
if (op === action_1.WriteOperation.Delete) {
|
|
506
|
-
return;
|
|
618
|
+
return [];
|
|
507
619
|
}
|
|
508
620
|
// build up data to be saved...
|
|
509
621
|
let data = {};
|
|
510
622
|
let logValues = {};
|
|
623
|
+
let needsFullDataChecks = [];
|
|
511
624
|
for (const [fieldName, field] of schemaFields) {
|
|
512
625
|
let value = editedFields.get(fieldName);
|
|
626
|
+
if (field.validateWithFullData) {
|
|
627
|
+
needsFullDataChecks.push(fieldName);
|
|
628
|
+
}
|
|
513
629
|
if (value === undefined && op === action_1.WriteOperation.Insert) {
|
|
514
630
|
// null allowed
|
|
515
631
|
value = this.defaultFieldsByFieldName[fieldName];
|
|
516
632
|
}
|
|
517
|
-
let dbKey =
|
|
518
|
-
|
|
633
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
634
|
+
let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
|
|
635
|
+
if (ret instanceof Error) {
|
|
636
|
+
errors.push(ret);
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
value = ret;
|
|
640
|
+
}
|
|
519
641
|
if (value !== undefined) {
|
|
520
642
|
data[dbKey] = value;
|
|
521
643
|
logValues[dbKey] = field.logValue(value);
|
|
522
644
|
}
|
|
523
645
|
}
|
|
646
|
+
for (const fieldName of needsFullDataChecks) {
|
|
647
|
+
const field = schemaFields.get(fieldName);
|
|
648
|
+
let value = editedFields.get(fieldName);
|
|
649
|
+
// @ts-ignore...
|
|
650
|
+
// type hackery because it's hard
|
|
651
|
+
const v = await field.validateWithFullData(value, this.options.builder);
|
|
652
|
+
if (!v) {
|
|
653
|
+
if (value === undefined) {
|
|
654
|
+
errors.push(new Error(`field ${fieldName} set to undefined when it can't be nullable`));
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
errors.push(new Error(`field ${fieldName} set to null when it can't be nullable`));
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
524
661
|
// we ignored default values while editing.
|
|
525
662
|
// if we're editing and there's data, add default values
|
|
526
663
|
if (op === action_1.WriteOperation.Edit && this.hasData(data)) {
|
|
527
664
|
for (const fieldName in this.defaultFieldsByFieldName) {
|
|
528
665
|
const defaultValue = this.defaultFieldsByFieldName[fieldName];
|
|
529
666
|
let field = schemaFields.get(fieldName);
|
|
530
|
-
let dbKey =
|
|
667
|
+
let dbKey = this.getStorageKey(fieldName);
|
|
531
668
|
// no value, let's just default
|
|
532
669
|
if (data[dbKey] === undefined) {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
670
|
+
const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
|
|
671
|
+
if (ret instanceof Error) {
|
|
672
|
+
errors.push(ret);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
data[dbKey] = ret;
|
|
676
|
+
logValues[dbKey] = field.logValue(ret);
|
|
677
|
+
}
|
|
536
678
|
}
|
|
537
679
|
}
|
|
538
680
|
}
|
|
539
681
|
this.validatedFields = data;
|
|
540
682
|
this.logValues = logValues;
|
|
683
|
+
return errors;
|
|
541
684
|
}
|
|
542
685
|
async valid() {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
catch (e) {
|
|
547
|
-
(0, logger_1.log)("error", e);
|
|
686
|
+
const errors = await this.validate();
|
|
687
|
+
if (errors.length) {
|
|
688
|
+
errors.map((err) => (0, logger_1.log)("error", err));
|
|
548
689
|
return false;
|
|
549
690
|
}
|
|
550
691
|
return true;
|
|
551
692
|
}
|
|
552
693
|
async validX() {
|
|
694
|
+
const errors = await this.validate();
|
|
695
|
+
if (errors.length) {
|
|
696
|
+
// just throw the first one...
|
|
697
|
+
// TODO we should ideally throw all of them
|
|
698
|
+
throw errors[0];
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* @experimental API that's not guaranteed to remain in the future which returns
|
|
703
|
+
* a list of errors encountered
|
|
704
|
+
* 0 errors indicates valid
|
|
705
|
+
* NOTE that this currently doesn't catch errors returned by validators().
|
|
706
|
+
* If those throws, this still throws and doesn't return them
|
|
707
|
+
*/
|
|
708
|
+
async validWithErrors() {
|
|
553
709
|
return this.validate();
|
|
554
710
|
}
|
|
555
711
|
async build() {
|
|
@@ -564,7 +720,7 @@ class Orchestrator {
|
|
|
564
720
|
if (!action || !action.viewerForEntLoad) {
|
|
565
721
|
return this.options.viewer;
|
|
566
722
|
}
|
|
567
|
-
return action.viewerForEntLoad(data);
|
|
723
|
+
return action.viewerForEntLoad(data, action.builder.viewer.context);
|
|
568
724
|
}
|
|
569
725
|
async returnedRow() {
|
|
570
726
|
if (this.mainOp && this.mainOp.returnedRow) {
|
|
@@ -599,6 +755,9 @@ class Orchestrator {
|
|
|
599
755
|
}
|
|
600
756
|
}
|
|
601
757
|
exports.Orchestrator = Orchestrator;
|
|
758
|
+
function randomNum() {
|
|
759
|
+
return Math.random().toString(10).substring(2);
|
|
760
|
+
}
|
|
602
761
|
class EntChangeset {
|
|
603
762
|
constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
|
|
604
763
|
this.viewer = viewer;
|
|
@@ -609,6 +768,9 @@ class EntChangeset {
|
|
|
609
768
|
this.changesets = changesets;
|
|
610
769
|
this.options = options;
|
|
611
770
|
}
|
|
771
|
+
static changesetFrom(builder, ops) {
|
|
772
|
+
return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
|
|
773
|
+
}
|
|
612
774
|
executor() {
|
|
613
775
|
if (this._executor) {
|
|
614
776
|
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,9 +5,15 @@ export interface Loader<T, V> {
|
|
|
5
5
|
loadMany?(keys: T[]): Promise<(V | null)[]>;
|
|
6
6
|
clearAll(): any;
|
|
7
7
|
}
|
|
8
|
-
export interface
|
|
8
|
+
export interface LoaderWithLoadMany<T, V> extends Loader<T, V> {
|
|
9
|
+
loadMany(keys: T[]): Promise<V[]>;
|
|
10
|
+
}
|
|
11
|
+
export interface LoaderFactory<K, V> {
|
|
9
12
|
name: string;
|
|
10
|
-
createLoader(context?: Context): Loader<
|
|
13
|
+
createLoader(context?: Context): Loader<K, V>;
|
|
14
|
+
}
|
|
15
|
+
interface LoaderFactoryWithLoaderMany<T, V> extends LoaderFactory<T, V> {
|
|
16
|
+
createLoader(context?: Context): LoaderWithLoadMany<T, V>;
|
|
11
17
|
}
|
|
12
18
|
export interface ConfigurableLoaderFactory<T, V> extends LoaderFactory<T, V> {
|
|
13
19
|
createConfigurableLoader(options: EdgeQueryableDataOptions, context?: Context): Loader<T, V>;
|
|
@@ -15,9 +21,11 @@ export interface ConfigurableLoaderFactory<T, V> extends LoaderFactory<T, V> {
|
|
|
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>;
|
|
28
|
+
getLoaderWithLoadMany<T, V>(name: string, create: () => LoaderWithLoadMany<T, V>): LoaderWithLoadMany<T, V>;
|
|
21
29
|
getCachedRows(options: queryOptions): Data[] | null;
|
|
22
30
|
getCachedRow(options: queryOptions): Data | null;
|
|
23
31
|
primeCache(options: queryOptions, rows: Data[]): void;
|
|
@@ -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
|
+
export 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,15 @@ 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>;
|
|
142
|
+
}
|
|
143
|
+
export interface PrivacyPolicy<TEnt extends Ent = Ent, TViewer = Viewer> {
|
|
144
|
+
rules: PrivacyPolicyRule<TEnt, TViewer>[];
|
|
126
145
|
}
|
|
127
|
-
export
|
|
128
|
-
|
|
146
|
+
export declare enum WriteOperation {
|
|
147
|
+
Insert = "insert",
|
|
148
|
+
Edit = "edit",
|
|
149
|
+
Delete = "delete"
|
|
129
150
|
}
|
|
130
151
|
export {};
|
package/core/base.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DenyWithReason = exports.Deny = exports.Skip = exports.Allow = void 0;
|
|
3
|
+
exports.WriteOperation = exports.DenyWithReason = exports.Deny = exports.Skip = exports.Allow = void 0;
|
|
4
4
|
// Privacy
|
|
5
5
|
var privacyResult;
|
|
6
6
|
(function (privacyResult) {
|
|
@@ -53,3 +53,9 @@ function DenyWithReason(e) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
exports.DenyWithReason = DenyWithReason;
|
|
56
|
+
var WriteOperation;
|
|
57
|
+
(function (WriteOperation) {
|
|
58
|
+
WriteOperation["Insert"] = "insert";
|
|
59
|
+
WriteOperation["Edit"] = "edit";
|
|
60
|
+
WriteOperation["Delete"] = "delete";
|
|
61
|
+
})(WriteOperation = exports.WriteOperation || (exports.WriteOperation = {}));
|