@snowtop/ent 0.1.0-alpha56 → 0.1.0-alpha58

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 (38) hide show
  1. package/action/orchestrator.js +21 -0
  2. package/core/base.d.ts +1 -1
  3. package/core/clause.d.ts +6 -3
  4. package/core/clause.js +39 -14
  5. package/core/config.d.ts +5 -2
  6. package/core/ent.d.ts +16 -3
  7. package/core/ent.js +147 -35
  8. package/core/loaders/assoc_count_loader.js +6 -1
  9. package/core/query/shared_assoc_test.d.ts +1 -1
  10. package/core/query/shared_assoc_test.js +17 -5
  11. package/core/query/shared_test.d.ts +3 -0
  12. package/core/query/shared_test.js +95 -17
  13. package/index.d.ts +1 -1
  14. package/index.js +3 -2
  15. package/package.json +1 -1
  16. package/parse_schema/parse.d.ts +8 -2
  17. package/parse_schema/parse.js +22 -2
  18. package/schema/index.d.ts +1 -1
  19. package/schema/schema.d.ts +15 -0
  20. package/scripts/custom_graphql.js +1 -1
  21. package/scripts/read_schema.js +10 -1
  22. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +4 -2
  23. package/testutils/db/{test_db.js → temp_db.js} +16 -5
  24. package/testutils/fake_data/fake_contact.d.ts +1 -1
  25. package/testutils/fake_data/fake_contact.js +2 -2
  26. package/testutils/fake_data/fake_event.d.ts +1 -1
  27. package/testutils/fake_data/fake_event.js +3 -3
  28. package/testutils/fake_data/fake_user.d.ts +1 -1
  29. package/testutils/fake_data/fake_user.js +2 -2
  30. package/testutils/fake_data/test_helpers.d.ts +2 -2
  31. package/testutils/fake_data/test_helpers.js +5 -5
  32. package/testutils/parse_sql.js +4 -0
  33. package/testutils/test_edge_global_schema.d.ts +15 -0
  34. package/testutils/test_edge_global_schema.js +58 -0
  35. package/testutils/write.d.ts +2 -2
  36. package/testutils/write.js +3 -3
  37. package/tsc/ast.d.ts +1 -0
  38. package/tsc/ast.js +3 -0
@@ -1,4 +1,23 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
@@ -11,6 +30,7 @@ const privacy_1 = require("../core/privacy");
11
30
  const executor_1 = require("./executor");
12
31
  const logger_1 = require("../core/logger");
13
32
  const memoizee_1 = __importDefault(require("memoizee"));
33
+ const clause = __importStar(require("../core/clause"));
14
34
  var edgeDirection;
15
35
  (function (edgeDirection) {
16
36
  edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
@@ -159,6 +179,7 @@ class Orchestrator {
159
179
  key: this.options.key,
160
180
  loadEntOptions: this.options.loaderOptions,
161
181
  placeholderID: this.options.builder.placeholderID,
182
+ whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
162
183
  };
163
184
  if (this.logValues) {
164
185
  opts.fieldsToLog = this.logValues;
package/core/base.d.ts CHANGED
@@ -82,7 +82,7 @@ export interface CreateRowOptions extends DataOptions {
82
82
  fieldsToLog?: Data;
83
83
  }
84
84
  export interface EditRowOptions extends CreateRowOptions {
85
- key: string;
85
+ whereClause: clause.Clause;
86
86
  }
87
87
  interface LoadableEntOptions<TEnt extends Ent, TViewer extends Viewer = Viewer> {
88
88
  loaderFactory: LoaderFactory<any, Data | null>;
package/core/clause.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export interface Clause {
2
2
  clause(idx: number): string;
3
+ columns(): string[];
3
4
  values(): any[];
4
5
  instanceKey(): string;
5
6
  logValues(): any[];
@@ -12,10 +13,11 @@ declare class simpleClause implements Clause {
12
13
  protected col: string;
13
14
  private value;
14
15
  private op;
15
- private handleSqliteNull?;
16
- constructor(col: string, value: any, op: string, handleSqliteNull?: Clause | undefined);
16
+ private handleNull?;
17
+ constructor(col: string, value: any, op: string, handleNull?: Clause | undefined);
17
18
  clause(idx: number): string;
18
- private sqliteNull;
19
+ private nullClause;
20
+ columns(): string[];
19
21
  values(): any[];
20
22
  logValues(): any[];
21
23
  instanceKey(): string;
@@ -25,6 +27,7 @@ declare class compositeClause implements Clause {
25
27
  private sep;
26
28
  constructor(clauses: Clause[], sep: string);
27
29
  clause(idx: number): string;
30
+ columns(): string[];
28
31
  values(): any[];
29
32
  logValues(): any[];
30
33
  instanceKey(): string;
package/core/clause.js CHANGED
@@ -33,33 +33,33 @@ function rawValue(val) {
33
33
  return val;
34
34
  }
35
35
  class simpleClause {
36
- constructor(col, value, op, handleSqliteNull) {
36
+ constructor(col, value, op, handleNull) {
37
37
  this.col = col;
38
38
  this.value = value;
39
39
  this.op = op;
40
- this.handleSqliteNull = handleSqliteNull;
40
+ this.handleNull = handleNull;
41
41
  }
42
42
  clause(idx) {
43
- const sqliteClause = this.sqliteNull();
44
- if (sqliteClause) {
45
- return sqliteClause.clause(idx);
43
+ const nullClause = this.nullClause();
44
+ if (nullClause) {
45
+ return nullClause.clause(idx);
46
46
  }
47
47
  if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
48
48
  return `${this.col} ${this.op} $${idx}`;
49
49
  }
50
50
  return `${this.col} ${this.op} ?`;
51
51
  }
52
- sqliteNull() {
53
- if (!this.handleSqliteNull || this.value !== null) {
54
- return;
55
- }
56
- if (db_1.default.getDialect() !== db_1.Dialect.SQLite) {
52
+ nullClause() {
53
+ if (!this.handleNull || this.value !== null) {
57
54
  return;
58
55
  }
59
- return this.handleSqliteNull;
56
+ return this.handleNull;
57
+ }
58
+ columns() {
59
+ return [this.col];
60
60
  }
61
61
  values() {
62
- const sqliteClause = this.sqliteNull();
62
+ const sqliteClause = this.nullClause();
63
63
  if (sqliteClause) {
64
64
  return sqliteClause.values();
65
65
  }
@@ -69,7 +69,7 @@ class simpleClause {
69
69
  return [this.value];
70
70
  }
71
71
  logValues() {
72
- const sqliteClause = this.sqliteNull();
72
+ const sqliteClause = this.nullClause();
73
73
  if (sqliteClause) {
74
74
  return sqliteClause.logValues();
75
75
  }
@@ -79,7 +79,7 @@ class simpleClause {
79
79
  return [this.value];
80
80
  }
81
81
  instanceKey() {
82
- const sqliteClause = this.sqliteNull();
82
+ const sqliteClause = this.nullClause();
83
83
  if (sqliteClause) {
84
84
  return sqliteClause.instanceKey();
85
85
  }
@@ -93,6 +93,9 @@ class isNullClause {
93
93
  clause(idx) {
94
94
  return `${this.col} IS NULL`;
95
95
  }
96
+ columns() {
97
+ return [];
98
+ }
96
99
  values() {
97
100
  return [];
98
101
  }
@@ -110,6 +113,9 @@ class isNotNullClause {
110
113
  clause(idx) {
111
114
  return `${this.col} IS NOT NULL`;
112
115
  }
116
+ columns() {
117
+ return [];
118
+ }
113
119
  values() {
114
120
  return [];
115
121
  }
@@ -132,6 +138,9 @@ class arraySimpleClause {
132
138
  }
133
139
  return `${this.col} ${this.op} ?`;
134
140
  }
141
+ columns() {
142
+ return [this.col];
143
+ }
135
144
  values() {
136
145
  if (isSensitive(this.value)) {
137
146
  return [this.value.value()];
@@ -163,6 +172,9 @@ class postgresArrayContains {
163
172
  }
164
173
  throw new Error(`not supported`);
165
174
  }
175
+ columns() {
176
+ return [this.col];
177
+ }
166
178
  values() {
167
179
  if (isSensitive(this.value)) {
168
180
  return [`{${this.value.value()}}`];
@@ -225,6 +237,9 @@ class inClause {
225
237
  // or change to a sqlx.Rebind format
226
238
  // here's what sqlx does: https://play.golang.org/p/vPzvYqeAcP0
227
239
  }
240
+ columns() {
241
+ return [this.col];
242
+ }
228
243
  values() {
229
244
  const result = [];
230
245
  for (const value of this.value) {
@@ -266,6 +281,13 @@ class compositeClause {
266
281
  }
267
282
  return clauses.join(this.sep);
268
283
  }
284
+ columns() {
285
+ const ret = [];
286
+ for (const cls of this.clauses) {
287
+ ret.push(...cls.columns());
288
+ }
289
+ return ret;
290
+ }
269
291
  values() {
270
292
  let result = [];
271
293
  for (const clause of this.clauses) {
@@ -315,6 +337,9 @@ class tsQueryClause {
315
337
  // FYI this doesn't actually work for sqlite since different
316
338
  return `${this.col} @@ ${this.getFunction()}('${language}', ?)`;
317
339
  }
340
+ columns() {
341
+ return [this.col];
342
+ }
318
343
  values() {
319
344
  const { value } = this.getInfo();
320
345
  return [value];
package/core/config.d.ts CHANGED
@@ -20,6 +20,7 @@ export interface Config {
20
20
  log?: logType | logType[];
21
21
  codegen?: CodegenConfig;
22
22
  customGraphQLJSONPath?: string;
23
+ globalSchemaPath?: string;
23
24
  }
24
25
  interface CodegenConfig {
25
26
  defaultEntPolicy?: PrivacyConfig;
@@ -35,7 +36,8 @@ interface CodegenConfig {
35
36
  schemaSQLFilePath?: boolean;
36
37
  databaseToCompareTo?: string;
37
38
  fieldPrivacyEvaluated?: fieldPrivacyEvaluated;
38
- templatizedViewer?: templatizedViewer;
39
+ templatizedViewer?: importedObject;
40
+ customAssocEdgePath?: importedObject;
39
41
  }
40
42
  interface PrettierConfig {
41
43
  custom?: boolean;
@@ -46,9 +48,10 @@ interface PrivacyConfig {
46
48
  policyName: string;
47
49
  class?: boolean;
48
50
  }
49
- interface templatizedViewer {
51
+ interface importedObject {
50
52
  path: string;
51
53
  name: string;
54
+ alias?: string;
52
55
  }
53
56
  export declare function loadConfig(file?: string | Buffer | Config): void;
54
57
  export {};
package/core/ent.d.ts CHANGED
@@ -4,6 +4,7 @@ import { Executor } from "../action/action";
4
4
  import * as clause from "./clause";
5
5
  import { Builder } from "../action";
6
6
  import DataLoader from "dataloader";
7
+ import { GlobalSchema } from "../schema/";
7
8
  export declare function loadEnt<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
8
9
  export declare function loadEntViaKey<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, key: any, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
9
10
  export declare function loadEntX<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id: ID, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt>;
@@ -52,6 +53,7 @@ export interface EditNodeOptions<T extends Ent> extends EditRowOptions {
52
53
  fieldsToResolve: string[];
53
54
  loadEntOptions: LoadEntOptions<T>;
54
55
  placeholderID?: ID;
56
+ key: string;
55
57
  }
56
58
  export declare class EditNodeOperation<T extends Ent> implements DataOperation {
57
59
  options: EditNodeOptions<T>;
@@ -67,7 +69,11 @@ export declare class EditNodeOperation<T extends Ent> implements DataOperation {
67
69
  returnedRow(): Data | null;
68
70
  createdEnt(viewer: Viewer): T | null;
69
71
  }
72
+ export declare function setGlobalSchema(val: GlobalSchema): void;
73
+ export declare function clearGlobalSchema(): void;
74
+ export declare function __hasGlobalSchema(): boolean;
70
75
  export declare class EdgeOperation implements DataOperation {
76
+ private builder;
71
77
  edgeInput: AssocEdgeInput;
72
78
  private options;
73
79
  private edgeData;
@@ -96,9 +102,9 @@ export declare class EdgeOperation implements DataOperation {
96
102
  export declare function buildInsertQuery(options: CreateRowOptions, suffix?: string): [string, string[], string[]];
97
103
  export declare function createRow(queryer: Queryer, options: CreateRowOptions, suffix: string): Promise<Data | null>;
98
104
  export declare function createRowSync(queryer: SyncQueryer, options: CreateRowOptions, suffix: string): Data | null;
99
- export declare function buildUpdateQuery(options: EditRowOptions, id: ID, suffix?: string): [string, any[], any[]];
100
- export declare function editRow(queryer: Queryer, options: EditRowOptions, id: ID, suffix?: string): Promise<Data | null>;
101
- export declare function editRowSync(queryer: SyncQueryer, options: EditRowOptions, id: ID, suffix?: string): Data | null;
105
+ export declare function buildUpdateQuery(options: EditRowOptions, suffix?: string): [string, any[], any[]];
106
+ export declare function editRow(queryer: Queryer, options: EditRowOptions, suffix?: string): Promise<Data | null>;
107
+ export declare function editRowSync(queryer: SyncQueryer, options: EditRowOptions, suffix?: string): Data | null;
102
108
  export declare function deleteRows(queryer: Queryer, options: DataOptions, cls: clause.Clause): Promise<void>;
103
109
  export declare function deleteRowsSync(queryer: SyncQueryer, options: DataOptions, cls: clause.Clause): void;
104
110
  export declare class DeleteNodeOperation implements DataOperation {
@@ -116,7 +122,9 @@ export declare class AssocEdge {
116
122
  id2Type: string;
117
123
  time: Date;
118
124
  data?: string | null;
125
+ private rawData;
119
126
  constructor(data: Data);
127
+ __getRawData(): Data;
120
128
  getCursor(): string;
121
129
  }
122
130
  interface cursorOptions {
@@ -156,6 +164,7 @@ interface loadEdgesOptions {
156
164
  edgeType: string;
157
165
  context?: Context;
158
166
  queryOptions?: EdgeQueryableDataOptions;
167
+ disableTransformations?: boolean;
159
168
  }
160
169
  interface loadCustomEdgesOptions<T extends AssocEdge> extends loadEdgesOptions {
161
170
  ctr: AssocEdgeConstructor<T>;
@@ -163,6 +172,10 @@ interface loadCustomEdgesOptions<T extends AssocEdge> extends loadEdgesOptions {
163
172
  export declare const DefaultLimit = 1000;
164
173
  export declare function defaultEdgeQueryOptions(id1: ID, edgeType: string): EdgeQueryableDataOptions;
165
174
  export declare function loadEdges(options: loadEdgesOptions): Promise<AssocEdge[]>;
175
+ export declare function getEdgeClauseAndFields(cls: clause.Clause, options: loadEdgesOptions): {
176
+ cls: clause.Clause;
177
+ fields: string[];
178
+ };
166
179
  export declare function loadCustomEdges<T extends AssocEdge>(options: loadCustomEdgesOptions<T>): Promise<T[]>;
167
180
  export declare function loadUniqueEdge(options: loadEdgesOptions): Promise<AssocEdge | null>;
168
181
  export declare function loadUniqueNode<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, id1: ID, edgeType: string, options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt | null>;
package/core/ent.js CHANGED
@@ -22,13 +22,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
25
+ exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
26
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = void 0;
26
27
  const db_1 = __importStar(require("./db"));
27
28
  const privacy_1 = require("./privacy");
28
29
  const clause = __importStar(require("./clause"));
29
30
  const action_1 = require("../action");
30
31
  const logger_1 = require("./logger");
31
32
  const dataloader_1 = __importDefault(require("dataloader"));
33
+ const schema_1 = require("../schema/");
32
34
  // TODO kill this and createDataLoader
33
35
  class cacheMap {
34
36
  constructor(options) {
@@ -473,7 +475,7 @@ class EditNodeOperation {
473
475
  if (this.hasData(options.fields)) {
474
476
  // even this with returning * may not always work if transformed...
475
477
  // we can have a transformed flag to see if it should be returned?
476
- this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
478
+ this.row = await editRow(queryer, options, "RETURNING *");
477
479
  }
478
480
  else {
479
481
  // @ts-ignore
@@ -523,7 +525,7 @@ class EditNodeOperation {
523
525
  };
524
526
  if (this.existingEnt) {
525
527
  if (this.hasData(this.options.fields)) {
526
- editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
528
+ editRowSync(queryer, options, "RETURNING *");
527
529
  this.reloadRow(queryer, this.existingEnt.id, options);
528
530
  }
529
531
  else {
@@ -548,8 +550,23 @@ class EditNodeOperation {
548
550
  }
549
551
  }
550
552
  exports.EditNodeOperation = EditNodeOperation;
553
+ let globalSchema;
554
+ function setGlobalSchema(val) {
555
+ globalSchema = val;
556
+ }
557
+ exports.setGlobalSchema = setGlobalSchema;
558
+ function clearGlobalSchema() {
559
+ globalSchema = undefined;
560
+ }
561
+ exports.clearGlobalSchema = clearGlobalSchema;
562
+ // used by tests. no guarantee will always exist
563
+ function __hasGlobalSchema() {
564
+ return globalSchema !== undefined;
565
+ }
566
+ exports.__hasGlobalSchema = __hasGlobalSchema;
551
567
  class EdgeOperation {
552
- constructor(edgeInput, options) {
568
+ constructor(builder, edgeInput, options) {
569
+ this.builder = builder;
553
570
  this.edgeInput = edgeInput;
554
571
  this.options = options;
555
572
  }
@@ -585,7 +602,31 @@ class EdgeOperation {
585
602
  }
586
603
  }
587
604
  getDeleteRowParams(edgeData, edge, context) {
605
+ let transformed = null;
606
+ let op = schema_1.SQLStatementOperation.Delete;
607
+ let updateData = null;
608
+ // TODO respect disableTransformations
609
+ if (globalSchema?.transformEdgeWrite) {
610
+ transformed = globalSchema.transformEdgeWrite({
611
+ op: schema_1.SQLStatementOperation.Delete,
612
+ edge,
613
+ });
614
+ if (transformed) {
615
+ op = transformed.op;
616
+ if (transformed.op === schema_1.SQLStatementOperation.Insert) {
617
+ throw new Error(`cannot currently transform a delete into an insert`);
618
+ }
619
+ if (transformed.op === schema_1.SQLStatementOperation.Update) {
620
+ if (!transformed.data) {
621
+ throw new Error(`cannot transform a delete into an update without providing data`);
622
+ }
623
+ updateData = transformed.data;
624
+ }
625
+ }
626
+ }
588
627
  return {
628
+ op,
629
+ updateData,
589
630
  options: {
590
631
  tableName: edgeData.edgeTable,
591
632
  context,
@@ -595,11 +636,35 @@ class EdgeOperation {
595
636
  }
596
637
  async performDeleteWrite(q, edgeData, edge, context) {
597
638
  const params = this.getDeleteRowParams(edgeData, edge, context);
598
- return deleteRows(q, params.options, params.clause);
639
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
640
+ return deleteRows(q, params.options, params.clause);
641
+ }
642
+ else {
643
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
644
+ throw new Error(`invalid operation ${params.op}`);
645
+ }
646
+ await editRow(q, {
647
+ tableName: params.options.tableName,
648
+ whereClause: params.clause,
649
+ fields: params.updateData,
650
+ });
651
+ }
599
652
  }
600
653
  performDeleteWriteSync(q, edgeData, edge, context) {
601
654
  const params = this.getDeleteRowParams(edgeData, edge, context);
602
- return deleteRowsSync(q, params.options, params.clause);
655
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
656
+ return deleteRowsSync(q, params.options, params.clause);
657
+ }
658
+ else {
659
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
660
+ throw new Error(`invalid operation ${params.op}`);
661
+ }
662
+ editRowSync(q, {
663
+ tableName: params.options.tableName,
664
+ whereClause: params.clause,
665
+ fields: params.updateData,
666
+ });
667
+ }
603
668
  }
604
669
  getInsertRowParams(edgeData, edge, context) {
605
670
  const fields = {
@@ -618,6 +683,30 @@ class EdgeOperation {
618
683
  // maybe when actions exist?
619
684
  fields["time"] = new Date().toISOString();
620
685
  }
686
+ const onConflictFields = ["data"];
687
+ if (globalSchema?.extraEdgeFields) {
688
+ for (const name in globalSchema.extraEdgeFields) {
689
+ const f = globalSchema.extraEdgeFields[name];
690
+ if (f.defaultValueOnCreate) {
691
+ const storageKey = (0, schema_1.getStorageKey)(f, name);
692
+ fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
693
+ // onconflict make sure we override the default values
694
+ // e.g. setting deleted_at = null for soft delete
695
+ onConflictFields.push(storageKey);
696
+ }
697
+ }
698
+ }
699
+ // TODO respect disableTransformations
700
+ let transformed = null;
701
+ if (globalSchema?.transformEdgeWrite) {
702
+ transformed = globalSchema.transformEdgeWrite({
703
+ op: schema_1.SQLStatementOperation.Insert,
704
+ edge,
705
+ });
706
+ if (transformed) {
707
+ throw new Error(`transforming an insert edge not currently supported`);
708
+ }
709
+ }
621
710
  return [
622
711
  {
623
712
  tableName: edgeData.edgeTable,
@@ -625,7 +714,9 @@ class EdgeOperation {
625
714
  fieldsToLog: fields,
626
715
  context,
627
716
  },
628
- "ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
717
+ `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
718
+ .map((f) => `${f} = EXCLUDED.${f}`)
719
+ .join(", ")}`,
629
720
  ];
630
721
  }
631
722
  async performInsertWrite(q, edgeData, edge, context) {
@@ -662,7 +753,7 @@ class EdgeOperation {
662
753
  }
663
754
  }
664
755
  symmetricEdge() {
665
- return new EdgeOperation({
756
+ return new EdgeOperation(this.builder, {
666
757
  id1: this.edgeInput.id2,
667
758
  id1Type: this.edgeInput.id2Type,
668
759
  id2: this.edgeInput.id1,
@@ -678,7 +769,7 @@ class EdgeOperation {
678
769
  });
679
770
  }
680
771
  inverseEdge(edgeData) {
681
- return new EdgeOperation({
772
+ return new EdgeOperation(this.builder, {
682
773
  id1: this.edgeInput.id2,
683
774
  id1Type: this.edgeInput.id2Type,
684
775
  id2: this.edgeInput.id1,
@@ -746,7 +837,7 @@ class EdgeOperation {
746
837
  if (data) {
747
838
  edge.data = data;
748
839
  }
749
- return new EdgeOperation(edge, {
840
+ return new EdgeOperation(builder, edge, {
750
841
  operation: action_1.WriteOperation.Insert,
751
842
  id2Placeholder,
752
843
  id1Placeholder,
@@ -767,7 +858,7 @@ class EdgeOperation {
767
858
  if (data) {
768
859
  edge.data = data;
769
860
  }
770
- return new EdgeOperation(edge, {
861
+ return new EdgeOperation(builder, edge, {
771
862
  operation: action_1.WriteOperation.Insert,
772
863
  id1Placeholder,
773
864
  id2Placeholder,
@@ -785,7 +876,7 @@ class EdgeOperation {
785
876
  id2Type: "",
786
877
  id1Type: "",
787
878
  };
788
- return new EdgeOperation(edge, {
879
+ return new EdgeOperation(builder, edge, {
789
880
  operation: action_1.WriteOperation.Delete,
790
881
  });
791
882
  }
@@ -800,7 +891,7 @@ class EdgeOperation {
800
891
  id2Type: "",
801
892
  id1Type: "",
802
893
  };
803
- return new EdgeOperation(edge, {
894
+ return new EdgeOperation(builder, edge, {
804
895
  operation: action_1.WriteOperation.Delete,
805
896
  });
806
897
  }
@@ -897,43 +988,42 @@ function createRowSync(queryer, options, suffix) {
897
988
  return null;
898
989
  }
899
990
  exports.createRowSync = createRowSync;
900
- function buildUpdateQuery(options, id, suffix) {
991
+ function buildUpdateQuery(options, suffix) {
901
992
  let valsString = [];
902
993
  let values = [];
903
994
  let logValues = [];
904
995
  const dialect = db_1.default.getDialect();
905
996
  let idx = 1;
906
997
  for (const key in options.fields) {
907
- values.push(options.fields[key]);
998
+ const val = options.fields[key];
999
+ values.push(val);
908
1000
  if (options.fieldsToLog) {
909
1001
  logValues.push(options.fieldsToLog[key]);
910
1002
  }
1003
+ // TODO would be nice to use clause here. need update version of the queries so that
1004
+ // we don't have to handle dialect specifics here
1005
+ // can't use clause because of IS NULL
1006
+ // valsString.push(clause.Eq(key, val).clause(idx));
911
1007
  if (dialect === db_1.Dialect.Postgres) {
912
1008
  valsString.push(`${key} = $${idx}`);
913
- idx++;
914
1009
  }
915
1010
  else {
916
1011
  valsString.push(`${key} = ?`);
917
1012
  }
1013
+ idx++;
918
1014
  }
919
1015
  const vals = valsString.join(", ");
920
1016
  let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
921
- if (dialect === db_1.Dialect.Postgres) {
922
- query = query + `${options.key} = $${idx}`;
923
- }
924
- else {
925
- query = query + `${options.key} = ?`;
926
- }
1017
+ query = query + options.whereClause.clause(idx);
1018
+ values.push(...options.whereClause.values());
927
1019
  if (suffix) {
928
1020
  query = query + " " + suffix;
929
1021
  }
930
1022
  return [query, values, logValues];
931
1023
  }
932
1024
  exports.buildUpdateQuery = buildUpdateQuery;
933
- async function editRow(queryer, options, id, suffix) {
934
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
935
- // add id as value to prepared query
936
- values.push(id);
1025
+ async function editRow(queryer, options, suffix) {
1026
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
937
1027
  const res = await mutateRow(queryer, query, values, logValues, options);
938
1028
  if (res?.rowCount == 1) {
939
1029
  // for now assume id primary key
@@ -944,10 +1034,8 @@ async function editRow(queryer, options, id, suffix) {
944
1034
  return null;
945
1035
  }
946
1036
  exports.editRow = editRow;
947
- function editRowSync(queryer, options, id, suffix) {
948
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
949
- // add id as value to prepared query
950
- values.push(id);
1037
+ function editRowSync(queryer, options, suffix) {
1038
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
951
1039
  const res = mutateRowSync(queryer, query, values, logValues, options);
952
1040
  if (res?.rowCount == 1) {
953
1041
  // for now assume id primary key
@@ -998,6 +1086,12 @@ class AssocEdge {
998
1086
  this.edgeType = data.edge_type;
999
1087
  this.time = data.time;
1000
1088
  this.data = data.data;
1089
+ this.rawData = data;
1090
+ }
1091
+ __getRawData() {
1092
+ // incase there's extra db fields. useful for tests
1093
+ // in production, a subclass of this should be in use so we won't need this...
1094
+ return this.rawData;
1001
1095
  }
1002
1096
  getCursor() {
1003
1097
  return getCursor({
@@ -1104,6 +1198,21 @@ async function loadEdges(options) {
1104
1198
  return loadCustomEdges({ ...options, ctr: AssocEdge });
1105
1199
  }
1106
1200
  exports.loadEdges = loadEdges;
1201
+ function getEdgeClauseAndFields(cls, options) {
1202
+ let fields = edgeFields;
1203
+ if (globalSchema?.transformEdgeRead) {
1204
+ const transformClause = globalSchema.transformEdgeRead();
1205
+ if (!options.disableTransformations) {
1206
+ cls = clause.And(cls, transformClause);
1207
+ }
1208
+ fields = edgeFields.concat(transformClause.columns());
1209
+ }
1210
+ return {
1211
+ cls,
1212
+ fields,
1213
+ };
1214
+ }
1215
+ exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
1107
1216
  async function loadCustomEdges(options) {
1108
1217
  const { id1, edgeType, context } = options;
1109
1218
  const edgeData = await loadEdgeData(edgeType);
@@ -1115,10 +1224,11 @@ async function loadCustomEdges(options) {
1115
1224
  if (options.queryOptions?.clause) {
1116
1225
  cls = clause.And(cls, options.queryOptions.clause);
1117
1226
  }
1227
+ const { cls: actualClause, fields } = getEdgeClauseAndFields(cls, options);
1118
1228
  const rows = await loadRows({
1119
1229
  tableName: edgeData.edgeTable,
1120
- fields: edgeFields,
1121
- clause: cls,
1230
+ fields: fields,
1231
+ clause: actualClause,
1122
1232
  orderby: options.queryOptions?.orderby || defaultOptions.orderby,
1123
1233
  limit: options.queryOptions?.limit || defaultOptions.limit,
1124
1234
  context,
@@ -1134,10 +1244,11 @@ async function loadUniqueEdge(options) {
1134
1244
  if (!edgeData) {
1135
1245
  throw new Error(`error loading edge data for ${edgeType}`);
1136
1246
  }
1247
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1137
1248
  const row = await loadRow({
1138
1249
  tableName: edgeData.edgeTable,
1139
- fields: edgeFields,
1140
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1250
+ fields: fields,
1251
+ clause: cls,
1141
1252
  context,
1142
1253
  });
1143
1254
  if (!row) {
@@ -1164,11 +1275,12 @@ async function loadRawEdgeCountX(options) {
1164
1275
  if (!edgeData) {
1165
1276
  throw new Error(`error loading edge data for ${edgeType}`);
1166
1277
  }
1278
+ const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1167
1279
  const row = await loadRowX({
1168
1280
  tableName: edgeData.edgeTable,
1169
1281
  // sqlite needs as count otherwise it returns count(1)
1170
1282
  fields: ["count(1) as count"],
1171
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1283
+ clause: cls,
1172
1284
  context,
1173
1285
  });
1174
1286
  return parseInt(row["count"], 10) || 0;