@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.
Files changed (173) hide show
  1. package/action/action.d.ts +33 -29
  2. package/action/action.js +22 -7
  3. package/action/executor.d.ts +3 -3
  4. package/action/executor.js +8 -3
  5. package/action/experimental_action.d.ts +32 -22
  6. package/action/experimental_action.js +35 -9
  7. package/action/index.d.ts +2 -0
  8. package/action/index.js +7 -1
  9. package/action/orchestrator.d.ts +32 -15
  10. package/action/orchestrator.js +249 -53
  11. package/action/privacy.d.ts +2 -2
  12. package/action/relative_value.d.ts +47 -0
  13. package/action/relative_value.js +125 -0
  14. package/action/transaction.d.ts +10 -0
  15. package/action/transaction.js +23 -0
  16. package/auth/auth.d.ts +1 -1
  17. package/core/base.d.ts +61 -37
  18. package/core/base.js +7 -1
  19. package/core/clause.d.ts +85 -40
  20. package/core/clause.js +375 -64
  21. package/core/config.d.ts +12 -1
  22. package/core/config.js +7 -1
  23. package/core/const.d.ts +3 -0
  24. package/core/const.js +6 -0
  25. package/core/context.d.ts +6 -4
  26. package/core/context.js +20 -2
  27. package/core/convert.d.ts +1 -1
  28. package/core/date.js +1 -5
  29. package/core/db.d.ts +11 -8
  30. package/core/db.js +20 -8
  31. package/core/ent.d.ts +86 -30
  32. package/core/ent.js +626 -197
  33. package/core/global_schema.d.ts +7 -0
  34. package/core/global_schema.js +51 -0
  35. package/core/loaders/assoc_count_loader.d.ts +3 -2
  36. package/core/loaders/assoc_count_loader.js +10 -2
  37. package/core/loaders/assoc_edge_loader.d.ts +2 -2
  38. package/core/loaders/assoc_edge_loader.js +8 -11
  39. package/core/loaders/index.d.ts +1 -1
  40. package/core/loaders/index.js +1 -3
  41. package/core/loaders/index_loader.d.ts +3 -3
  42. package/core/loaders/loader.d.ts +2 -2
  43. package/core/loaders/loader.js +5 -5
  44. package/core/loaders/object_loader.d.ts +32 -11
  45. package/core/loaders/object_loader.js +225 -78
  46. package/core/loaders/query_loader.d.ts +7 -13
  47. package/core/loaders/query_loader.js +52 -11
  48. package/core/loaders/raw_count_loader.d.ts +2 -2
  49. package/core/loaders/raw_count_loader.js +5 -1
  50. package/core/logger.d.ts +1 -1
  51. package/core/logger.js +1 -0
  52. package/core/privacy.d.ts +25 -24
  53. package/core/privacy.js +21 -25
  54. package/core/query/assoc_query.d.ts +7 -6
  55. package/core/query/assoc_query.js +9 -1
  56. package/core/query/custom_clause_query.d.ts +27 -0
  57. package/core/query/custom_clause_query.js +84 -0
  58. package/core/query/custom_query.d.ts +20 -5
  59. package/core/query/custom_query.js +87 -12
  60. package/core/query/index.d.ts +1 -0
  61. package/core/query/index.js +3 -1
  62. package/core/query/query.d.ts +8 -4
  63. package/core/query/query.js +101 -53
  64. package/core/query/shared_assoc_test.d.ts +2 -1
  65. package/core/query/shared_assoc_test.js +35 -45
  66. package/core/query/shared_test.d.ts +8 -1
  67. package/core/query/shared_test.js +470 -236
  68. package/core/viewer.d.ts +3 -3
  69. package/core/viewer.js +1 -1
  70. package/graphql/graphql.d.ts +51 -19
  71. package/graphql/graphql.js +160 -136
  72. package/graphql/graphql_field_helpers.d.ts +7 -1
  73. package/graphql/graphql_field_helpers.js +21 -1
  74. package/graphql/index.d.ts +2 -2
  75. package/graphql/index.js +3 -5
  76. package/graphql/query/connection_type.d.ts +9 -9
  77. package/graphql/query/edge_connection.d.ts +9 -9
  78. package/graphql/query/page_info.d.ts +1 -1
  79. package/graphql/query/shared_assoc_test.js +1 -1
  80. package/graphql/query/shared_edge_connection.js +1 -19
  81. package/graphql/scalars/orderby_direction.d.ts +2 -0
  82. package/graphql/scalars/orderby_direction.js +15 -0
  83. package/imports/dataz/example1/_auth.js +128 -47
  84. package/imports/dataz/example1/_viewer.js +87 -39
  85. package/imports/index.d.ts +6 -1
  86. package/imports/index.js +19 -4
  87. package/index.d.ts +13 -5
  88. package/index.js +21 -7
  89. package/package.json +17 -17
  90. package/parse_schema/parse.d.ts +31 -9
  91. package/parse_schema/parse.js +155 -13
  92. package/schema/base_schema.d.ts +7 -3
  93. package/schema/base_schema.js +10 -0
  94. package/schema/field.d.ts +78 -21
  95. package/schema/field.js +231 -71
  96. package/schema/index.d.ts +2 -2
  97. package/schema/index.js +5 -1
  98. package/schema/json_field.d.ts +16 -4
  99. package/schema/json_field.js +32 -2
  100. package/schema/schema.d.ts +89 -19
  101. package/schema/schema.js +11 -13
  102. package/schema/struct_field.d.ts +15 -3
  103. package/schema/struct_field.js +117 -22
  104. package/schema/union_field.d.ts +1 -1
  105. package/scripts/custom_compiler.js +10 -6
  106. package/scripts/custom_graphql.js +128 -31
  107. package/scripts/migrate_v0.1.js +36 -0
  108. package/scripts/move_types.js +120 -0
  109. package/scripts/read_schema.js +20 -5
  110. package/testutils/action/complex_schemas.d.ts +69 -0
  111. package/testutils/action/complex_schemas.js +398 -0
  112. package/testutils/builder.d.ts +41 -47
  113. package/testutils/builder.js +76 -49
  114. package/testutils/db/fixture.d.ts +10 -0
  115. package/testutils/db/fixture.js +26 -0
  116. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +24 -8
  117. package/testutils/db/{test_db.js → temp_db.js} +182 -45
  118. package/testutils/db/value.d.ts +7 -0
  119. package/testutils/db/value.js +251 -0
  120. package/testutils/db_mock.d.ts +16 -4
  121. package/testutils/db_mock.js +52 -7
  122. package/testutils/db_time_zone.d.ts +4 -0
  123. package/testutils/db_time_zone.js +41 -0
  124. package/testutils/ent-graphql-tests/index.d.ts +7 -1
  125. package/testutils/ent-graphql-tests/index.js +52 -23
  126. package/testutils/fake_comms.js +1 -1
  127. package/testutils/fake_data/const.d.ts +2 -1
  128. package/testutils/fake_data/const.js +3 -0
  129. package/testutils/fake_data/fake_contact.d.ts +7 -3
  130. package/testutils/fake_data/fake_contact.js +13 -7
  131. package/testutils/fake_data/fake_event.d.ts +4 -1
  132. package/testutils/fake_data/fake_event.js +7 -6
  133. package/testutils/fake_data/fake_tag.d.ts +36 -0
  134. package/testutils/fake_data/fake_tag.js +89 -0
  135. package/testutils/fake_data/fake_user.d.ts +8 -5
  136. package/testutils/fake_data/fake_user.js +16 -15
  137. package/testutils/fake_data/index.js +5 -1
  138. package/testutils/fake_data/internal.d.ts +2 -0
  139. package/testutils/fake_data/internal.js +7 -1
  140. package/testutils/fake_data/tag_query.d.ts +13 -0
  141. package/testutils/fake_data/tag_query.js +43 -0
  142. package/testutils/fake_data/test_helpers.d.ts +11 -4
  143. package/testutils/fake_data/test_helpers.js +28 -12
  144. package/testutils/fake_data/user_query.d.ts +13 -6
  145. package/testutils/fake_data/user_query.js +54 -22
  146. package/testutils/fake_log.d.ts +3 -3
  147. package/testutils/fake_log.js +1 -1
  148. package/testutils/parse_sql.d.ts +6 -0
  149. package/testutils/parse_sql.js +16 -2
  150. package/testutils/test_edge_global_schema.d.ts +15 -0
  151. package/testutils/test_edge_global_schema.js +62 -0
  152. package/testutils/write.d.ts +2 -2
  153. package/testutils/write.js +33 -7
  154. package/tsc/ast.d.ts +25 -2
  155. package/tsc/ast.js +141 -17
  156. package/tsc/compilerOptions.js +5 -1
  157. package/tsc/move_generated.d.ts +1 -0
  158. package/tsc/move_generated.js +164 -0
  159. package/tsc/transform.d.ts +22 -0
  160. package/tsc/transform.js +181 -0
  161. package/tsc/transform_action.d.ts +22 -0
  162. package/tsc/transform_action.js +183 -0
  163. package/tsc/transform_ent.d.ts +17 -0
  164. package/tsc/transform_ent.js +60 -0
  165. package/tsc/transform_schema.d.ts +27 -0
  166. package/{scripts → tsc}/transform_schema.js +146 -117
  167. package/graphql/enums.d.ts +0 -3
  168. package/graphql/enums.js +0 -25
  169. package/scripts/move_generated.js +0 -142
  170. package/scripts/transform_code.js +0 -113
  171. package/scripts/transform_schema.d.ts +0 -1
  172. /package/scripts/{move_generated.d.ts → migrate_v0.1.d.ts} +0 -0
  173. /package/scripts/{transform_code.d.ts → move_types.d.ts} +0 -0
@@ -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, editedData);
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
- await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
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
- let triggers = action?.triggers;
321
- if (triggers) {
322
- await this.triggers(action, builder, triggers);
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
- await Promise.all(triggers.map(async (trigger) => {
335
- let ret = await trigger.changeset(builder, action.getInput());
336
- if (Array.isArray(ret)) {
337
- ret = await Promise.all(ret);
338
- }
339
- if (Array.isArray(ret)) {
340
- for (const v of ret) {
341
- if (typeof v === "object") {
342
- this.changesets.push(v);
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 if (ret) {
347
- this.changesets.push(ret);
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
- let promises = [];
353
- validators.forEach((validator) => {
354
- let res = validator.validate(builder, action.getInput());
355
- if (res) {
356
- promises.push(res);
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
- await Promise.all(promises);
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
- viewer: builder.viewer,
383
- op: this.getSQLStatementOperation(),
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
- viewer: builder.viewer,
391
- op: this.getSQLStatementOperation(),
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
- defaultData[dbKey] = defaultValue;
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
- throw new Error(`field ${fieldName} set to null for non-nullable field`);
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
- throw new Error(`required field ${fieldName} not set`);
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
- throw new Error(`invalid field ${fieldName} with value ${value}`);
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
- throw new Error(`invalid field ${fieldName} with value ${value}`);
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
- value = await this.transformFieldValue(fieldName, field, dbKey, value);
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 value = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
551
- data[dbKey] = value;
552
- logValues[dbKey] = field.logValue(value);
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
- try {
561
- await this.validate();
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;
@@ -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 {};