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