@snowtop/ent 0.1.0-alpha12 → 0.1.0-alpha121

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 (167) hide show
  1. package/action/action.d.ts +37 -31
  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 +33 -14
  10. package/action/orchestrator.js +251 -54
  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 +60 -37
  18. package/core/base.js +7 -1
  19. package/core/clause.d.ts +84 -40
  20. package/core/clause.js +358 -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 +82 -30
  32. package/core/ent.js +632 -193
  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 +11 -10
  45. package/core/loaders/object_loader.js +70 -60
  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 +15 -7
  71. package/graphql/graphql.js +23 -7
  72. package/graphql/index.d.ts +1 -1
  73. package/graphql/index.js +3 -4
  74. package/graphql/query/connection_type.d.ts +9 -9
  75. package/graphql/query/edge_connection.d.ts +9 -9
  76. package/graphql/query/page_info.d.ts +1 -1
  77. package/graphql/query/shared_assoc_test.js +1 -1
  78. package/graphql/query/shared_edge_connection.js +1 -19
  79. package/graphql/scalars/orderby_direction.d.ts +2 -0
  80. package/graphql/scalars/orderby_direction.js +15 -0
  81. package/imports/index.d.ts +6 -1
  82. package/imports/index.js +19 -4
  83. package/index.d.ts +13 -5
  84. package/index.js +21 -7
  85. package/package.json +17 -16
  86. package/parse_schema/parse.d.ts +31 -9
  87. package/parse_schema/parse.js +152 -12
  88. package/schema/base_schema.d.ts +5 -3
  89. package/schema/base_schema.js +6 -0
  90. package/schema/field.d.ts +78 -21
  91. package/schema/field.js +219 -72
  92. package/schema/index.d.ts +2 -2
  93. package/schema/index.js +5 -1
  94. package/schema/json_field.d.ts +16 -4
  95. package/schema/json_field.js +32 -2
  96. package/schema/schema.d.ts +89 -20
  97. package/schema/schema.js +13 -14
  98. package/schema/struct_field.d.ts +15 -3
  99. package/schema/struct_field.js +71 -22
  100. package/schema/union_field.d.ts +1 -1
  101. package/scripts/custom_compiler.js +10 -6
  102. package/scripts/custom_graphql.js +124 -31
  103. package/scripts/migrate_v0.1.js +36 -0
  104. package/scripts/move_types.js +117 -0
  105. package/scripts/read_schema.js +20 -5
  106. package/testutils/action/complex_schemas.d.ts +69 -0
  107. package/testutils/action/complex_schemas.js +398 -0
  108. package/testutils/builder.d.ts +43 -47
  109. package/testutils/builder.js +76 -49
  110. package/testutils/db/fixture.d.ts +10 -0
  111. package/testutils/db/fixture.js +26 -0
  112. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +24 -8
  113. package/testutils/db/{test_db.js → temp_db.js} +182 -45
  114. package/testutils/db/value.d.ts +7 -0
  115. package/testutils/db/value.js +251 -0
  116. package/testutils/db_mock.d.ts +16 -4
  117. package/testutils/db_mock.js +51 -6
  118. package/testutils/db_time_zone.d.ts +4 -0
  119. package/testutils/db_time_zone.js +41 -0
  120. package/testutils/ent-graphql-tests/index.d.ts +7 -1
  121. package/testutils/ent-graphql-tests/index.js +52 -23
  122. package/testutils/fake_data/const.d.ts +2 -1
  123. package/testutils/fake_data/const.js +3 -0
  124. package/testutils/fake_data/fake_contact.d.ts +8 -4
  125. package/testutils/fake_data/fake_contact.js +15 -8
  126. package/testutils/fake_data/fake_event.d.ts +5 -2
  127. package/testutils/fake_data/fake_event.js +9 -7
  128. package/testutils/fake_data/fake_tag.d.ts +36 -0
  129. package/testutils/fake_data/fake_tag.js +89 -0
  130. package/testutils/fake_data/fake_user.d.ts +10 -7
  131. package/testutils/fake_data/fake_user.js +18 -16
  132. package/testutils/fake_data/index.js +5 -1
  133. package/testutils/fake_data/internal.d.ts +2 -0
  134. package/testutils/fake_data/internal.js +7 -1
  135. package/testutils/fake_data/tag_query.d.ts +13 -0
  136. package/testutils/fake_data/tag_query.js +43 -0
  137. package/testutils/fake_data/test_helpers.d.ts +11 -4
  138. package/testutils/fake_data/test_helpers.js +28 -12
  139. package/testutils/fake_data/user_query.d.ts +13 -6
  140. package/testutils/fake_data/user_query.js +54 -22
  141. package/testutils/fake_log.d.ts +3 -3
  142. package/testutils/parse_sql.d.ts +6 -0
  143. package/testutils/parse_sql.js +16 -2
  144. package/testutils/test_edge_global_schema.d.ts +15 -0
  145. package/testutils/test_edge_global_schema.js +62 -0
  146. package/testutils/write.d.ts +2 -2
  147. package/testutils/write.js +33 -7
  148. package/tsc/ast.d.ts +25 -2
  149. package/tsc/ast.js +141 -17
  150. package/tsc/compilerOptions.js +5 -1
  151. package/tsc/move_generated.d.ts +1 -0
  152. package/tsc/move_generated.js +164 -0
  153. package/tsc/transform.d.ts +22 -0
  154. package/tsc/transform.js +181 -0
  155. package/tsc/transform_action.d.ts +22 -0
  156. package/tsc/transform_action.js +183 -0
  157. package/tsc/transform_ent.d.ts +17 -0
  158. package/tsc/transform_ent.js +60 -0
  159. package/tsc/transform_schema.d.ts +27 -0
  160. package/{scripts → tsc}/transform_schema.js +146 -117
  161. package/graphql/enums.d.ts +0 -3
  162. package/graphql/enums.js +0 -25
  163. package/scripts/move_generated.js +0 -142
  164. package/scripts/transform_code.js +0 -113
  165. package/scripts/transform_schema.d.ts +0 -1
  166. /package/scripts/{move_generated.d.ts → migrate_v0.1.d.ts} +0 -0
  167. /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;
@@ -376,24 +496,30 @@ class Orchestrator {
376
496
  // if action transformations. always do it
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
- let transformed;
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,9 +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) {
547
+ // @ts-ignore
417
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;
418
551
  }
419
552
  }
420
553
  // transforming before doing default fields so that we don't create a new id
@@ -423,6 +556,7 @@ class Orchestrator {
423
556
  let value = editedFields.get(fieldName);
424
557
  let defaultValue = undefined;
425
558
  let dbKey = this.getStorageKey(fieldName);
559
+ let updateOnlyIfOther = field.onlyUpdateIfOtherFieldsBeingSet_BETA;
426
560
  if (value === undefined) {
427
561
  if (this.actualOperation === action_1.WriteOperation.Insert) {
428
562
  if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
@@ -436,11 +570,17 @@ class Orchestrator {
436
570
  if (defaultValue === undefined) {
437
571
  throw new Error(`defaultValueOnCreate() returned undefined for field ${fieldName}`);
438
572
  }
573
+ if (util_1.types.isPromise(defaultValue)) {
574
+ defaultValue = await defaultValue;
575
+ }
439
576
  }
440
577
  }
441
578
  if (field.defaultValueOnEdit &&
442
579
  this.actualOperation === action_1.WriteOperation.Edit) {
443
580
  defaultValue = field.defaultValueOnEdit(builder, input);
581
+ if (util_1.types.isPromise(defaultValue)) {
582
+ defaultValue = await defaultValue;
583
+ }
444
584
  }
445
585
  }
446
586
  if (value !== undefined) {
@@ -448,7 +588,12 @@ class Orchestrator {
448
588
  }
449
589
  if (defaultValue !== undefined) {
450
590
  updateInput = true;
451
- defaultData[dbKey] = defaultValue;
591
+ if (updateOnlyIfOther) {
592
+ defaultData[dbKey] = defaultValue;
593
+ }
594
+ else {
595
+ data[dbKey] = defaultValue;
596
+ }
452
597
  this.defaultFieldsByFieldName[fieldName] = defaultValue;
453
598
  this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
454
599
  }
@@ -476,7 +621,7 @@ class Orchestrator {
476
621
  // now format and validate...
477
622
  if (value === null) {
478
623
  if (!field.nullable) {
479
- 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`);
480
625
  }
481
626
  }
482
627
  else if (value === undefined) {
@@ -487,14 +632,14 @@ class Orchestrator {
487
632
  // server default allowed
488
633
  field.serverDefault === undefined &&
489
634
  this.actualOperation === action_1.WriteOperation.Insert) {
490
- throw new Error(`required field ${fieldName} not set`);
635
+ return new Error(`required field ${fieldName} not set`);
491
636
  }
492
637
  }
493
638
  else if (this.isBuilder(value)) {
494
639
  if (field.valid) {
495
640
  const valid = await field.valid(value);
496
641
  if (!valid) {
497
- throw new Error(`invalid field ${fieldName} with value ${value}`);
642
+ return new Error(`invalid field ${fieldName} with value ${value}`);
498
643
  }
499
644
  }
500
645
  // keep track of dependencies to resolve
@@ -507,7 +652,7 @@ class Orchestrator {
507
652
  // TODO this could be async. handle this better
508
653
  const valid = await field.valid(value);
509
654
  if (!valid) {
510
- throw new Error(`invalid field ${fieldName} with value ${value}`);
655
+ return new Error(`invalid field ${fieldName} with value ${value}`);
511
656
  }
512
657
  }
513
658
  if (field.format) {
@@ -517,26 +662,52 @@ class Orchestrator {
517
662
  return value;
518
663
  }
519
664
  async formatAndValidateFields(schemaFields, editedFields) {
665
+ const errors = [];
520
666
  const op = this.actualOperation;
521
667
  if (op === action_1.WriteOperation.Delete) {
522
- return;
668
+ return [];
523
669
  }
524
670
  // build up data to be saved...
525
671
  let data = {};
526
672
  let logValues = {};
673
+ let needsFullDataChecks = [];
527
674
  for (const [fieldName, field] of schemaFields) {
528
675
  let value = editedFields.get(fieldName);
676
+ if (field.validateWithFullData) {
677
+ needsFullDataChecks.push(fieldName);
678
+ }
529
679
  if (value === undefined && op === action_1.WriteOperation.Insert) {
530
680
  // null allowed
531
681
  value = this.defaultFieldsByFieldName[fieldName];
532
682
  }
533
683
  let dbKey = this.getStorageKey(fieldName);
534
- 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
+ }
535
691
  if (value !== undefined) {
536
692
  data[dbKey] = value;
537
693
  logValues[dbKey] = field.logValue(value);
538
694
  }
539
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
+ }
540
711
  // we ignored default values while editing.
541
712
  // if we're editing and there's data, add default values
542
713
  if (op === action_1.WriteOperation.Edit && this.hasData(data)) {
@@ -546,26 +717,45 @@ class Orchestrator {
546
717
  let dbKey = this.getStorageKey(fieldName);
547
718
  // no value, let's just default
548
719
  if (data[dbKey] === undefined) {
549
- const value = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
550
- data[dbKey] = value;
551
- 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
+ }
552
728
  }
553
729
  }
554
730
  }
555
731
  this.validatedFields = data;
556
732
  this.logValues = logValues;
733
+ return errors;
557
734
  }
558
735
  async valid() {
559
- try {
560
- await this.validate();
561
- }
562
- catch (e) {
563
- (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));
564
739
  return false;
565
740
  }
566
741
  return true;
567
742
  }
568
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() {
569
759
  return this.validate();
570
760
  }
571
761
  async build() {
@@ -573,6 +763,7 @@ class Orchestrator {
573
763
  await this.validX();
574
764
  let ops = [this.buildMainOp()];
575
765
  await this.buildEdgeOps(ops);
766
+ // TODO throw if we try and create a new changeset after previously creating one
576
767
  return new EntChangeset(this.options.viewer, this.options.builder.placeholderID, this.options.loaderOptions.ent, ops, this.dependencies, this.changesets, this.options);
577
768
  }
578
769
  async viewerForEntLoad(data) {
@@ -580,7 +771,7 @@ class Orchestrator {
580
771
  if (!action || !action.viewerForEntLoad) {
581
772
  return this.options.viewer;
582
773
  }
583
- return action.viewerForEntLoad(data);
774
+ return action.viewerForEntLoad(data, action.builder.viewer.context);
584
775
  }
585
776
  async returnedRow() {
586
777
  if (this.mainOp && this.mainOp.returnedRow) {
@@ -615,6 +806,9 @@ class Orchestrator {
615
806
  }
616
807
  }
617
808
  exports.Orchestrator = Orchestrator;
809
+ function randomNum() {
810
+ return Math.random().toString(10).substring(2);
811
+ }
618
812
  class EntChangeset {
619
813
  constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
620
814
  this.viewer = viewer;
@@ -625,6 +819,9 @@ class EntChangeset {
625
819
  this.changesets = changesets;
626
820
  this.options = options;
627
821
  }
822
+ static changesetFrom(builder, ops) {
823
+ return new EntChangeset(builder.viewer, `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, builder.ent, ops);
824
+ }
628
825
  executor() {
629
826
  if (this._executor) {
630
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> | undefined);
5
+ constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
6
6
  apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
7
7
  }
8
8
  export declare class AllowIfBuilder implements PrivacyPolicyRule {
9
9
  private id?;
10
- constructor(id?: ID | Builder<Ent> | undefined);
10
+ constructor(id?: ID | Builder<Ent<Viewer<Ent<any> | null, ID | null>>, any, Ent<Viewer<Ent<any> | null, ID | null>> | null> | undefined);
11
11
  apply(_v: Viewer, _ent: Ent): Promise<PrivacyResult>;
12
12
  }
@@ -0,0 +1,47 @@
1
+ import { Clause } from "../core/clause";
2
+ export interface RelativeFieldValue<T = BigInt | number> {
3
+ delta: T;
4
+ sqlExpression: (col: string) => Clause;
5
+ eval: (curr: T) => T;
6
+ }
7
+ export interface RelativeNumberValue<T> {
8
+ add?: T;
9
+ subtract?: T;
10
+ divide?: T;
11
+ multiply?: T;
12
+ modulo?: T;
13
+ }
14
+ declare function addNumber(delta: number): RelativeFieldValue<number>;
15
+ declare function addNumber(delta: BigInt): RelativeFieldValue<BigInt>;
16
+ declare function subtractNumber(delta: number): RelativeFieldValue<number>;
17
+ declare function subtractNumber(delta: BigInt): RelativeFieldValue<BigInt>;
18
+ declare function multiplyNumber(delta: number): RelativeFieldValue<number>;
19
+ declare function multiplyNumber(delta: BigInt): RelativeFieldValue<BigInt>;
20
+ declare function divideNumber(delta: number): RelativeFieldValue<number>;
21
+ declare function divideNumber(delta: BigInt): RelativeFieldValue<BigInt>;
22
+ declare function moduloNumber(delta: number): RelativeFieldValue<number>;
23
+ declare function moduloNumber(delta: BigInt): RelativeFieldValue<BigInt>;
24
+ export declare const NumberOps: {
25
+ addNumber: typeof addNumber;
26
+ moduloNumber: typeof moduloNumber;
27
+ divideNumber: typeof divideNumber;
28
+ subtractNumber: typeof subtractNumber;
29
+ multiplyNumber: typeof multiplyNumber;
30
+ };
31
+ export declare function convertRelativeInput(rel: RelativeNumberValue<BigInt>, col: string, existing: BigInt): {
32
+ value: BigInt;
33
+ clause: Clause;
34
+ };
35
+ export declare function convertRelativeInput(rel: RelativeNumberValue<number>, col: string, existing: number): {
36
+ value: number;
37
+ clause: Clause;
38
+ };
39
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number>, col: string, existing: number, expressions: Map<string, Clause>): number;
40
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number> | undefined, col: string, existing: number, expressions: Map<string, Clause>): number | undefined;
41
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number> | null, col: string, existing: number | null, expressions: Map<string, Clause>): number | null;
42
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: number | RelativeNumberValue<number> | null | undefined, col: string, existing: number | null, expressions: Map<string, Clause>): number | undefined | null;
43
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt>, col: string, existing: BigInt, expressions: Map<string, Clause>): BigInt;
44
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt> | undefined, col: string, existing: BigInt, expressions: Map<string, Clause>): BigInt | undefined;
45
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt> | null, col: string, existing: BigInt | null, expressions: Map<string, Clause>): BigInt | null;
46
+ export declare function maybeConvertRelativeInputPlusExpressions(rel: BigInt | RelativeNumberValue<BigInt> | null | undefined, col: string, existing: BigInt | null, expressions: Map<string, Clause>): BigInt | null | undefined;
47
+ export {};