@snowtop/ent 0.0.38 → 0.0.39-alpha10

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.
@@ -1,6 +1,7 @@
1
1
  import { Ent, EntConstructor, Viewer, ID, Data, PrivacyPolicy, Context } from "../core/base";
2
2
  import { DataOperation, AssocEdgeInputOptions } from "../core/ent";
3
3
  import { Queryer } from "../core/db";
4
+ import { TransformedUpdateOperation, UpdateOperation } from "../schema";
4
5
  export declare enum WriteOperation {
5
6
  Insert = "insert",
6
7
  Edit = "edit",
@@ -50,6 +51,7 @@ export interface Action<T extends Ent> {
50
51
  observers?: Observer<T>[];
51
52
  validators?: Validator<T>[];
52
53
  getInput(): Data;
54
+ transformWrite?: <T2 extends Ent>(stmt: UpdateOperation<T2>) => Promise<TransformedUpdateOperation<T2>> | TransformedUpdateOperation<T2> | undefined;
53
55
  valid(): Promise<boolean>;
54
56
  validX(): Promise<void>;
55
57
  viewerForEntLoad?(data: Data): Viewer | Promise<Viewer>;
@@ -41,8 +41,12 @@ export declare class Orchestrator<T extends Ent> {
41
41
  viewer: Viewer;
42
42
  private defaultFieldsByFieldName;
43
43
  private defaultFieldsByTSName;
44
+ private actualOperation;
45
+ private existingEnt?;
46
+ private disableTransformations;
44
47
  constructor(options: OrchestratorOptions<T, Data>);
45
48
  private addEdge;
49
+ setDisableTransformations(val: boolean): void;
46
50
  addInboundEdge<T2 extends Ent>(id1: ID | Builder<T2>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
47
51
  addOutboundEdge<T2 extends Ent>(id2: ID | Builder<T2>, edgeType: string, nodeType: string, options?: AssocEdgeInputOptions): void;
48
52
  removeInboundEdge(id1: ID, edgeType: string): void;
@@ -54,6 +58,8 @@ export declare class Orchestrator<T extends Ent> {
54
58
  private buildEdgeOps;
55
59
  private throwError;
56
60
  private getEntForPrivacyPolicy;
61
+ private getSQLStatementOperation;
62
+ private getWriteOpForSQLStamentOp;
57
63
  private validate;
58
64
  private triggers;
59
65
  private validators;
@@ -62,6 +62,8 @@ class Orchestrator {
62
62
  this.defaultFieldsByFieldName = {};
63
63
  this.defaultFieldsByTSName = {};
64
64
  this.viewer = options.viewer;
65
+ this.actualOperation = this.options.operation;
66
+ this.existingEnt = this.options.builder.existingEnt;
65
67
  }
66
68
  addEdge(edge, op) {
67
69
  this.edgeSet.add(edge.edgeType);
@@ -80,6 +82,9 @@ class Orchestrator {
80
82
  m1.set(op, m2);
81
83
  this.edges.set(edge.edgeType, m1);
82
84
  }
85
+ setDisableTransformations(val) {
86
+ this.disableTransformations = val;
87
+ }
83
88
  addInboundEdge(id1, edgeType, nodeType, options) {
84
89
  this.addEdge(new edgeInputData({
85
90
  id: id1,
@@ -135,24 +140,27 @@ class Orchestrator {
135
140
  }
136
141
  buildMainOp() {
137
142
  // this assumes we have validated fields
138
- switch (this.options.operation) {
143
+ switch (this.actualOperation) {
139
144
  case action_1.WriteOperation.Delete:
140
- return new ent_1.DeleteNodeOperation(this.options.builder.existingEnt.id, {
145
+ return new ent_1.DeleteNodeOperation(this.existingEnt.id, {
141
146
  tableName: this.options.tableName,
142
147
  });
143
148
  default:
149
+ if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
150
+ throw new Error(`existing ent required with operation ${this.actualOperation}`);
151
+ }
144
152
  const opts = {
145
153
  fields: this.validatedFields,
146
154
  tableName: this.options.tableName,
147
155
  fieldsToResolve: this.fieldsToResolve,
148
156
  key: this.options.key,
149
- ent: this.options.loaderOptions.ent,
157
+ loadEntOptions: this.options.loaderOptions,
150
158
  placeholderID: this.options.builder.placeholderID,
151
159
  };
152
160
  if (this.logValues) {
153
161
  opts.fieldsToLog = this.logValues;
154
162
  }
155
- this.mainOp = new ent_1.EditNodeOperation(opts, this.options.builder.existingEnt);
163
+ this.mainOp = new ent_1.EditNodeOperation(opts, this.existingEnt);
156
164
  return this.mainOp;
157
165
  }
158
166
  }
@@ -219,35 +227,57 @@ class Orchestrator {
219
227
  if (!privacyPolicy || !action) {
220
228
  throw new Error(`shouldn't get here if no privacyPolicy for action`);
221
229
  }
222
- if (this.options.operation === action_1.WriteOperation.Insert) {
230
+ if (this.actualOperation === action_1.WriteOperation.Insert) {
223
231
  return new EntCannotCreateEntError(privacyPolicy, action);
224
232
  }
225
- else if (this.options.operation === action_1.WriteOperation.Edit) {
226
- return new EntCannotEditEntError(privacyPolicy, action, this.options.builder.existingEnt);
233
+ else if (this.actualOperation === action_1.WriteOperation.Edit) {
234
+ return new EntCannotEditEntError(privacyPolicy, action, this.existingEnt);
227
235
  }
228
- return new EntCannotDeleteEntError(privacyPolicy, action, this.options.builder.existingEnt);
236
+ return new EntCannotDeleteEntError(privacyPolicy, action, this.existingEnt);
229
237
  }
230
238
  getEntForPrivacyPolicy(editedData) {
231
- if (this.options.operation !== action_1.WriteOperation.Insert) {
232
- return this.options.builder.existingEnt;
239
+ if (this.actualOperation !== action_1.WriteOperation.Insert) {
240
+ return this.existingEnt;
233
241
  }
234
242
  // we create an unsafe ent to be used for privacy policies
235
243
  return new this.options.builder.ent(this.options.builder.viewer, editedData);
236
244
  }
245
+ getSQLStatementOperation() {
246
+ switch (this.actualOperation) {
247
+ case action_1.WriteOperation.Edit:
248
+ return schema_1.SQLStatementOperation.Update;
249
+ case action_1.WriteOperation.Insert:
250
+ return schema_1.SQLStatementOperation.Insert;
251
+ case action_1.WriteOperation.Delete:
252
+ return schema_1.SQLStatementOperation.Delete;
253
+ }
254
+ }
255
+ getWriteOpForSQLStamentOp(op) {
256
+ switch (op) {
257
+ case schema_1.SQLStatementOperation.Update:
258
+ return action_1.WriteOperation.Edit;
259
+ case schema_1.SQLStatementOperation.Insert:
260
+ return action_1.WriteOperation.Insert;
261
+ case schema_1.SQLStatementOperation.Update:
262
+ return action_1.WriteOperation.Delete;
263
+ default:
264
+ throw new Error("invalid path");
265
+ }
266
+ }
237
267
  async validate() {
238
268
  // existing ent required for edit or delete operations
239
- switch (this.options.operation) {
269
+ switch (this.actualOperation) {
240
270
  case action_1.WriteOperation.Delete:
241
271
  case action_1.WriteOperation.Edit:
242
- if (!this.options.builder.existingEnt) {
243
- throw new Error("existing ent required with operation");
272
+ if (!this.existingEnt) {
273
+ throw new Error(`existing ent required with operation ${this.actualOperation}`);
244
274
  }
245
275
  }
246
276
  const action = this.options.action;
247
277
  const builder = this.options.builder;
248
278
  // future optimization: can get schemaFields to memoize based on different values
249
279
  const schemaFields = (0, schema_1.getFields)(this.options.schema);
250
- let editedData = this.getFieldsWithDefaultValues(builder, schemaFields, action);
280
+ let editedData = await this.getFieldsWithDefaultValues(builder, schemaFields, action);
251
281
  // this runs in following phases:
252
282
  // * set default fields and pass to builder so the value can be checked by triggers/observers/validators
253
283
  // * privacy policy (use unsafe ent if we have it)
@@ -300,18 +330,66 @@ class Orchestrator {
300
330
  isBuilder(val) {
301
331
  return val.placeholderID !== undefined;
302
332
  }
303
- getFieldsWithDefaultValues(builder, schemaFields, action) {
333
+ async getFieldsWithDefaultValues(builder, schemaFields, action) {
304
334
  const editedFields = this.options.editedFields();
305
335
  let data = {};
306
336
  let defaultData = {};
307
337
  let input = action?.getInput() || {};
308
338
  let updateInput = false;
339
+ // transformations
340
+ // if action transformations. always do it
341
+ // if disable transformations set, don't do schema transform and just do the right thing
342
+ // else apply schema tranformation if it exists
343
+ let transformed;
344
+ if (action?.transformWrite) {
345
+ transformed = await action.transformWrite({
346
+ viewer: builder.viewer,
347
+ op: this.getSQLStatementOperation(),
348
+ data: editedFields,
349
+ existingEnt: this.existingEnt,
350
+ });
351
+ }
352
+ else if (!this.disableTransformations) {
353
+ transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
354
+ viewer: builder.viewer,
355
+ op: this.getSQLStatementOperation(),
356
+ data: editedFields,
357
+ existingEnt: this.existingEnt,
358
+ });
359
+ }
360
+ if (transformed) {
361
+ if (transformed.data) {
362
+ updateInput = true;
363
+ for (const k in transformed.data) {
364
+ let field = schemaFields.get(k);
365
+ if (!field) {
366
+ throw new Error(`tried to transform field with unknown field ${k}`);
367
+ }
368
+ let val = transformed.data[k];
369
+ if (field.format) {
370
+ val = field.format(transformed.data[k]);
371
+ }
372
+ let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(field.name);
373
+ data[dbKey] = val;
374
+ this.defaultFieldsByTSName[(0, camel_case_1.camelCase)(k)] = val;
375
+ // hmm do we need this?
376
+ // TODO how to do this for local tests?
377
+ // this.defaultFieldsByFieldName[k] = val;
378
+ }
379
+ }
380
+ this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
381
+ if (transformed.existingEnt) {
382
+ this.existingEnt = transformed.existingEnt;
383
+ }
384
+ }
385
+ // transforming before doing default fields so that we don't create a new id
386
+ // and anything that depends on the type of operations knows what it is
309
387
  for (const [fieldName, field] of schemaFields) {
310
388
  let value = editedFields.get(fieldName);
311
389
  let defaultValue = undefined;
312
390
  let dbKey = field.storageKey || (0, snake_case_1.snakeCase)(field.name);
313
391
  if (value === undefined) {
314
- if (this.options.operation === action_1.WriteOperation.Insert) {
392
+ if (this.actualOperation === action_1.WriteOperation.Insert) {
315
393
  if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
316
394
  throw new Error(`cannot set both defaultToViewerOnCreate and defaultValueOnCreate`);
317
395
  }
@@ -326,7 +404,7 @@ class Orchestrator {
326
404
  }
327
405
  }
328
406
  if (field.defaultValueOnEdit &&
329
- this.options.operation === action_1.WriteOperation.Edit) {
407
+ this.actualOperation === action_1.WriteOperation.Edit) {
330
408
  defaultValue = field.defaultValueOnEdit(builder, input);
331
409
  }
332
410
  }
@@ -374,7 +452,7 @@ class Orchestrator {
374
452
  // not setting server default as we're depending on the database handling that.
375
453
  // server default allowed
376
454
  field.serverDefault === undefined &&
377
- this.options.operation === action_1.WriteOperation.Insert) {
455
+ this.actualOperation === action_1.WriteOperation.Insert) {
378
456
  throw new Error(`required field ${field.name} not set`);
379
457
  }
380
458
  }
@@ -406,7 +484,7 @@ class Orchestrator {
406
484
  return value;
407
485
  }
408
486
  async formatAndValidateFields(schemaFields) {
409
- const op = this.options.operation;
487
+ const op = this.actualOperation;
410
488
  if (op === action_1.WriteOperation.Delete) {
411
489
  return;
412
490
  }
@@ -494,7 +572,7 @@ class Orchestrator {
494
572
  const viewer = await this.viewerForEntLoad(row);
495
573
  const ent = await (0, ent_1.applyPrivacyPolicyForRow)(viewer, this.options.loaderOptions, row);
496
574
  if (!ent) {
497
- if (this.options.operation == action_1.WriteOperation.Insert) {
575
+ if (this.actualOperation == action_1.WriteOperation.Insert) {
498
576
  throw new Error(`was able to create ent but not load it`);
499
577
  }
500
578
  else {
package/core/base.d.ts CHANGED
@@ -62,6 +62,7 @@ export interface SelectBaseDataOptions extends DataOptions {
62
62
  }
63
63
  export interface SelectDataOptions extends SelectBaseDataOptions {
64
64
  key: string;
65
+ clause?: clause.Clause;
65
66
  }
66
67
  export interface QueryableDataOptions extends SelectBaseDataOptions, QueryDataOptions {
67
68
  }
package/core/clause.d.ts CHANGED
@@ -12,8 +12,10 @@ declare class simpleClause implements Clause {
12
12
  protected col: string;
13
13
  private value;
14
14
  private op;
15
- constructor(col: string, value: any, op: string);
15
+ private handleSqliteNull?;
16
+ constructor(col: string, value: any, op: string, handleSqliteNull?: Clause | undefined);
16
17
  clause(idx: number): string;
18
+ private sqliteNull;
17
19
  values(): any[];
18
20
  logValues(): any[];
19
21
  instanceKey(): string;
@@ -27,14 +29,33 @@ declare class compositeClause implements Clause {
27
29
  logValues(): any[];
28
30
  instanceKey(): string;
29
31
  }
30
- export declare function Eq(col: string, value: any): simpleClause;
31
- export declare function NotEq(col: string, value: any): simpleClause;
32
+ export declare function ArrayEq(col: string, value: any): Clause;
33
+ export declare function ArrayNotEq(col: string, value: any): Clause;
34
+ export declare function ArrayGreater(col: string, value: any): Clause;
35
+ export declare function ArrayLess(col: string, value: any): Clause;
36
+ export declare function ArrayGreaterEq(col: string, value: any): Clause;
37
+ export declare function ArrayLessEq(col: string, value: any): Clause;
38
+ export declare function Eq(col: string, value: any): Clause;
39
+ export declare function NotEq(col: string, value: any): Clause;
32
40
  export declare function Greater(col: string, value: any): simpleClause;
33
41
  export declare function Less(col: string, value: any): simpleClause;
34
42
  export declare function GreaterEq(col: string, value: any): simpleClause;
35
43
  export declare function LessEq(col: string, value: any): simpleClause;
36
44
  export declare function And(...args: Clause[]): compositeClause;
45
+ export declare function AndOptional(...args: (Clause | undefined)[]): Clause;
37
46
  export declare function Or(...args: Clause[]): compositeClause;
38
47
  export declare function In(col: string, ...values: any): Clause;
48
+ interface TsQuery {
49
+ language: "english" | "french" | "german" | "simple";
50
+ value: string;
51
+ }
52
+ export declare function TsQuery(col: string, val: string | TsQuery): Clause;
53
+ export declare function PlainToTsQuery(col: string, val: string | TsQuery): Clause;
54
+ export declare function PhraseToTsQuery(col: string, val: string | TsQuery): Clause;
55
+ export declare function WebsearchToTsQuery(col: string, val: string | TsQuery): Clause;
56
+ export declare function TsVectorColTsQuery(col: string, val: string | TsQuery): Clause;
57
+ export declare function TsVectorPlainToTsQuery(col: string, val: string | TsQuery): Clause;
58
+ export declare function TsVectorPhraseToTsQuery(col: string, val: string | TsQuery): Clause;
59
+ export declare function TsVectorWebsearchToTsQuery(col: string, val: string | TsQuery): Clause;
39
60
  export declare function sensitiveValue(val: any): SensitiveValue;
40
61
  export {};
package/core/clause.js CHANGED
@@ -19,10 +19,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
19
19
  return result;
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.sensitiveValue = exports.In = exports.Or = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = void 0;
22
+ exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.In = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayLessEq = exports.ArrayGreaterEq = exports.ArrayLess = exports.ArrayGreater = exports.ArrayNotEq = exports.ArrayEq = void 0;
23
23
  const db_1 = __importStar(require("./db"));
24
24
  function isSensitive(val) {
25
- return (typeof val === "object" && val.logValue !== undefined);
25
+ return (val !== null &&
26
+ typeof val === "object" &&
27
+ val.logValue !== undefined);
26
28
  }
27
29
  function rawValue(val) {
28
30
  if (isSensitive(val)) {
@@ -31,17 +33,105 @@ function rawValue(val) {
31
33
  return val;
32
34
  }
33
35
  class simpleClause {
34
- constructor(col, value, op) {
36
+ constructor(col, value, op, handleSqliteNull) {
35
37
  this.col = col;
36
38
  this.value = value;
37
39
  this.op = op;
40
+ this.handleSqliteNull = handleSqliteNull;
38
41
  }
39
42
  clause(idx) {
43
+ const sqliteClause = this.sqliteNull();
44
+ if (sqliteClause) {
45
+ return sqliteClause.clause(idx);
46
+ }
40
47
  if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
41
48
  return `${this.col} ${this.op} $${idx}`;
42
49
  }
43
50
  return `${this.col} ${this.op} ?`;
44
51
  }
52
+ sqliteNull() {
53
+ if (!this.handleSqliteNull || this.value !== null) {
54
+ return;
55
+ }
56
+ if (db_1.default.getDialect() !== db_1.Dialect.SQLite) {
57
+ return;
58
+ }
59
+ return this.handleSqliteNull;
60
+ }
61
+ values() {
62
+ const sqliteClause = this.sqliteNull();
63
+ if (sqliteClause) {
64
+ return sqliteClause.values();
65
+ }
66
+ if (isSensitive(this.value)) {
67
+ return [this.value.value()];
68
+ }
69
+ return [this.value];
70
+ }
71
+ logValues() {
72
+ const sqliteClause = this.sqliteNull();
73
+ if (sqliteClause) {
74
+ return sqliteClause.logValues();
75
+ }
76
+ if (isSensitive(this.value)) {
77
+ return [this.value.logValue()];
78
+ }
79
+ return [this.value];
80
+ }
81
+ instanceKey() {
82
+ const sqliteClause = this.sqliteNull();
83
+ if (sqliteClause) {
84
+ return sqliteClause.instanceKey();
85
+ }
86
+ return `${this.col}${this.op}${rawValue(this.value)}`;
87
+ }
88
+ }
89
+ class isNullClause {
90
+ constructor(col) {
91
+ this.col = col;
92
+ }
93
+ clause(idx) {
94
+ return `${this.col} IS NULL`;
95
+ }
96
+ values() {
97
+ return [];
98
+ }
99
+ logValues() {
100
+ return [];
101
+ }
102
+ instanceKey() {
103
+ return `${this.col} IS NULL`;
104
+ }
105
+ }
106
+ class isNotNullClause {
107
+ constructor(col) {
108
+ this.col = col;
109
+ }
110
+ clause(idx) {
111
+ return `${this.col} IS NOT NULL`;
112
+ }
113
+ values() {
114
+ return [];
115
+ }
116
+ logValues() {
117
+ return [];
118
+ }
119
+ instanceKey() {
120
+ return `${this.col} IS NOT NULL`;
121
+ }
122
+ }
123
+ class arraySimpleClause {
124
+ constructor(col, value, op) {
125
+ this.col = col;
126
+ this.value = value;
127
+ this.op = op;
128
+ }
129
+ clause(idx) {
130
+ if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
131
+ return `$${idx} ${this.op} ANY(${this.col})`;
132
+ }
133
+ return `${this.col} ${this.op} ?`;
134
+ }
45
135
  values() {
46
136
  if (isSensitive(this.value)) {
47
137
  return [this.value.value()];
@@ -145,12 +235,100 @@ class compositeClause {
145
235
  return keys.join(this.sep);
146
236
  }
147
237
  }
238
+ class tsQueryClause {
239
+ constructor(col, val, tsVectorCol) {
240
+ this.col = col;
241
+ this.val = val;
242
+ this.tsVectorCol = tsVectorCol;
243
+ }
244
+ isTsQuery(val) {
245
+ return typeof val !== "string";
246
+ }
247
+ getInfo() {
248
+ if (this.isTsQuery(this.val)) {
249
+ return { value: this.val.value, language: this.val.language };
250
+ }
251
+ return {
252
+ language: "english",
253
+ value: this.val,
254
+ };
255
+ }
256
+ clause(idx) {
257
+ const { language } = this.getInfo();
258
+ if (db_1.Dialect.Postgres === db_1.default.getDialect()) {
259
+ if (this.tsVectorCol) {
260
+ return `to_tsvector(${this.col}) @@ ${this.getFunction()}('${language}', $${idx})`;
261
+ }
262
+ return `${this.col} @@ ${this.getFunction()}('${language}', $${idx})`;
263
+ }
264
+ // FYI this doesn't actually work for sqlite since different
265
+ return `${this.col} @@ ${this.getFunction()}('${language}', ?)`;
266
+ }
267
+ values() {
268
+ const { value } = this.getInfo();
269
+ return [value];
270
+ }
271
+ logValues() {
272
+ const { value } = this.getInfo();
273
+ return [value];
274
+ }
275
+ getFunction() {
276
+ return "to_tsquery";
277
+ }
278
+ instanceKey() {
279
+ const { language, value } = this.getInfo();
280
+ if (this.tsVectorCol) {
281
+ return `to_tsvector(${this.col})@@${this.getFunction()}:${language}:${value}`;
282
+ }
283
+ return `${this.col}@@${this.getFunction()}:${language}:${value}`;
284
+ }
285
+ }
286
+ class plainToTsQueryClause extends tsQueryClause {
287
+ getFunction() {
288
+ return "plainto_tsquery";
289
+ }
290
+ }
291
+ class phraseToTsQueryClause extends tsQueryClause {
292
+ getFunction() {
293
+ return "phraseto_tsquery";
294
+ }
295
+ }
296
+ class websearchTosQueryClause extends tsQueryClause {
297
+ getFunction() {
298
+ return "websearch_to_tsquery";
299
+ }
300
+ }
301
+ // TODO we need to check sqlite version...
302
+ function ArrayEq(col, value) {
303
+ return new arraySimpleClause(col, value, "=");
304
+ }
305
+ exports.ArrayEq = ArrayEq;
306
+ function ArrayNotEq(col, value) {
307
+ return new arraySimpleClause(col, value, "!=");
308
+ }
309
+ exports.ArrayNotEq = ArrayNotEq;
310
+ function ArrayGreater(col, value) {
311
+ return new arraySimpleClause(col, value, ">");
312
+ }
313
+ exports.ArrayGreater = ArrayGreater;
314
+ function ArrayLess(col, value) {
315
+ return new arraySimpleClause(col, value, "<");
316
+ }
317
+ exports.ArrayLess = ArrayLess;
318
+ function ArrayGreaterEq(col, value) {
319
+ return new arraySimpleClause(col, value, ">=");
320
+ }
321
+ exports.ArrayGreaterEq = ArrayGreaterEq;
322
+ function ArrayLessEq(col, value) {
323
+ return new arraySimpleClause(col, value, "<=");
324
+ }
325
+ exports.ArrayLessEq = ArrayLessEq;
148
326
  function Eq(col, value) {
149
- return new simpleClause(col, value, "=");
327
+ return new simpleClause(col, value, "=", new isNullClause(col));
150
328
  }
151
329
  exports.Eq = Eq;
152
330
  function NotEq(col, value) {
153
- return new simpleClause(col, value, "!=");
331
+ return new simpleClause(col, value, "!=", new isNotNullClause(col));
154
332
  }
155
333
  exports.NotEq = NotEq;
156
334
  function Greater(col, value) {
@@ -173,6 +351,15 @@ function And(...args) {
173
351
  return new compositeClause(args, " AND ");
174
352
  }
175
353
  exports.And = And;
354
+ function AndOptional(...args) {
355
+ // @ts-ignore
356
+ let filtered = args.filter((v) => v !== undefined);
357
+ if (filtered.length === 1) {
358
+ return filtered[0];
359
+ }
360
+ return And(...filtered);
361
+ }
362
+ exports.AndOptional = AndOptional;
176
363
  function Or(...args) {
177
364
  return new compositeClause(args, " OR ");
178
365
  }
@@ -182,6 +369,58 @@ function In(col, ...values) {
182
369
  return new inClause(col, values);
183
370
  }
184
371
  exports.In = In;
372
+ // if string defaults to english
373
+ // https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES
374
+ // to_tsquery
375
+ // plainto_tsquery
376
+ // phraseto_tsquery;
377
+ // websearch_to_tsquery
378
+ function TsQuery(col, val) {
379
+ return new tsQueryClause(col, val);
380
+ }
381
+ exports.TsQuery = TsQuery;
382
+ function PlainToTsQuery(col, val) {
383
+ return new plainToTsQueryClause(col, val);
384
+ }
385
+ exports.PlainToTsQuery = PlainToTsQuery;
386
+ function PhraseToTsQuery(col, val) {
387
+ return new phraseToTsQueryClause(col, val);
388
+ }
389
+ exports.PhraseToTsQuery = PhraseToTsQuery;
390
+ function WebsearchToTsQuery(col, val) {
391
+ return new websearchTosQueryClause(col, val);
392
+ }
393
+ exports.WebsearchToTsQuery = WebsearchToTsQuery;
394
+ // TsVectorColTsQuery is used when the column is not a tsvector field e.g.
395
+ // when there's an index just on the field and is not a combination of multiple fields
396
+ function TsVectorColTsQuery(col, val) {
397
+ return new tsQueryClause(col, val, true);
398
+ }
399
+ exports.TsVectorColTsQuery = TsVectorColTsQuery;
400
+ // TsVectorPlainToTsQuery is used when the column is not a tsvector field e.g.
401
+ // when there's an index just on the field and is not a combination of multiple fields
402
+ function TsVectorPlainToTsQuery(col, val) {
403
+ return new plainToTsQueryClause(col, val, true);
404
+ }
405
+ exports.TsVectorPlainToTsQuery = TsVectorPlainToTsQuery;
406
+ // TsVectorPhraseToTsQuery is used when the column is not a tsvector field e.g.
407
+ // when there's an index just on the field and is not a combination of multiple fields
408
+ function TsVectorPhraseToTsQuery(col, val) {
409
+ return new phraseToTsQueryClause(col, val, true);
410
+ }
411
+ exports.TsVectorPhraseToTsQuery = TsVectorPhraseToTsQuery;
412
+ // TsVectorWebsearchToTsQuery is used when the column is not a tsvector field e.g.
413
+ // when there's an index just on the field and is not a combination of multiple fields
414
+ function TsVectorWebsearchToTsQuery(col, val) {
415
+ return new websearchTosQueryClause(col, val, true);
416
+ }
417
+ exports.TsVectorWebsearchToTsQuery = TsVectorWebsearchToTsQuery;
418
+ // TODO would be nice to support this with building blocks but not supporting for now
419
+ // AND: foo & bar,
420
+ // OR: foo | bar
421
+ // followed by: foo <-> bar
422
+ // NOT: !foo
423
+ // starts_with: theo:*
185
424
  // wrap a query in the db with this to ensure that it doesn't show up in the logs
186
425
  // e.g. if querying for password, SSN, etc
187
426
  // we'll pass the right fields to query and log something along the lines of `****`
package/core/config.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface Config {
15
15
  db?: Database | DBDict;
16
16
  log?: logType | logType[];
17
17
  codegen?: CodegenConfig;
18
+ customGraphQLJSONPath?: string;
18
19
  }
19
20
  interface CodegenConfig {
20
21
  defaultEntPolicy?: PrivacyConfig;
@@ -27,6 +28,8 @@ interface CodegenConfig {
27
28
  generateRootResolvers?: boolean;
28
29
  defaultGraphQLMutationName?: graphqlMutationName;
29
30
  defaultGraphQLFieldFormat?: graphQLFieldFormat;
31
+ schemaSQLFilePath?: boolean;
32
+ databaseToCompareTo?: string;
30
33
  }
31
34
  interface PrettierConfig {
32
35
  custom?: boolean;
package/core/ent.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Queryer, SyncQueryer } from "./db";
2
- import { Viewer, Ent, ID, LoadRowsOptions, LoadRowOptions, Data, DataOptions, QueryableDataOptions, EditRowOptions, LoadEntOptions, LoadCustomEntOptions, EdgeQueryableDataOptions, Context, SelectBaseDataOptions, CreateRowOptions, QueryDataOptions, EntConstructor } from "./base";
2
+ import { Viewer, Ent, ID, LoadRowsOptions, LoadRowOptions, Data, DataOptions, QueryableDataOptions, EditRowOptions, LoadEntOptions, LoadCustomEntOptions, EdgeQueryableDataOptions, Context, SelectBaseDataOptions, CreateRowOptions, QueryDataOptions } from "./base";
3
3
  import { Executor } from "../action/action";
4
4
  import * as clause from "./clause";
5
5
  import { Builder } from "../action";
@@ -51,7 +51,7 @@ export interface DataOperation<T extends Ent = Ent> {
51
51
  }
52
52
  export interface EditNodeOptions<T extends Ent> extends EditRowOptions {
53
53
  fieldsToResolve: string[];
54
- ent: EntConstructor<T>;
54
+ loadEntOptions: LoadEntOptions<T>;
55
55
  placeholderID?: ID;
56
56
  }
57
57
  export declare class EditNodeOperation<T extends Ent> implements DataOperation {