@snowtop/ent 0.0.18 → 0.0.22

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.
@@ -224,55 +224,44 @@ class Orchestrator {
224
224
  }
225
225
  async validate() {
226
226
  const action = this.options.action;
227
- let privacyPolicy = action?.getPrivacyPolicy();
228
227
  const builder = this.options.builder;
229
- let promises = [];
228
+ // this runs in 3 phases:
229
+ // * privacy policy
230
+ // * triggers
231
+ // * validators
232
+ let privacyPolicy = action?.getPrivacyPolicy();
230
233
  if (privacyPolicy) {
231
- promises.push((0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, builder.existingEnt, this.throwError.bind(this)));
234
+ await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, builder.existingEnt, this.throwError.bind(this));
232
235
  }
233
236
  // have to run triggers which update fields first before field and other validators
234
237
  // so running this first to build things up
235
238
  let triggers = action?.triggers;
236
239
  if (triggers) {
237
- let triggerPromises = [];
238
- triggers.forEach((trigger) => {
239
- let c = trigger.changeset(builder, action.getInput());
240
- if (c) {
241
- triggerPromises.push(c);
242
- }
243
- });
244
- // TODO right now trying to parallelize this with validateFields below
245
- // may need to run triggers first to be deterministic
246
- // TODO: see https://github.com/lolopinto/ent/pull/50
247
- promises.push(this.triggers(triggerPromises));
240
+ await this.triggers(action, builder, triggers);
248
241
  }
249
- promises.push(this.validateFields(builder, action));
250
242
  let validators = action?.validators || [];
251
- if (validators) {
252
- promises.push(this.validators(validators, action, builder));
253
- }
254
- await Promise.all(promises);
255
- }
256
- async triggers(triggerPromises) {
257
- // keep changesets to use later
258
- let changesets = await Promise.all(triggerPromises.map(async (promise) => {
259
- if (Array.isArray(promise)) {
260
- return await Promise.all(promise);
243
+ await Promise.all([
244
+ this.validateFields(builder, action),
245
+ this.validators(validators, action, builder),
246
+ ]);
247
+ }
248
+ async triggers(action, builder, triggers) {
249
+ await Promise.all(triggers.map(async (trigger) => {
250
+ let ret = await trigger.changeset(builder, action.getInput());
251
+ if (Array.isArray(ret)) {
252
+ ret = await Promise.all(ret);
261
253
  }
262
- return await promise;
263
- }));
264
- changesets.forEach((c) => {
265
- if (Array.isArray(c)) {
266
- for (const v of c) {
254
+ if (Array.isArray(ret)) {
255
+ for (const v of ret) {
267
256
  if (typeof v === "object") {
268
257
  this.changesets.push(v);
269
258
  }
270
259
  }
271
260
  }
272
- else if (c) {
273
- this.changesets.push(c);
261
+ else if (ret) {
262
+ this.changesets.push(ret);
274
263
  }
275
- });
264
+ }));
276
265
  }
277
266
  async validators(validators, action, builder) {
278
267
  let promises = [];
package/core/convert.d.ts CHANGED
@@ -10,3 +10,5 @@ export declare function convertBoolList(val: any): boolean[];
10
10
  export declare function convertNullableBoolList(val: any): boolean[] | null;
11
11
  export declare function convertJSON(val: any): any;
12
12
  export declare function convertNullableJSON(val: any): any | null;
13
+ export declare function convertJSONList(val: any): boolean[];
14
+ export declare function convertNullableJSONList(val: any): any[] | null;
package/core/convert.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.convertNullableJSON = exports.convertJSON = exports.convertNullableBoolList = exports.convertBoolList = exports.convertNullableDateList = exports.convertDateList = exports.convertNullableList = exports.convertList = exports.convertNullableBool = exports.convertBool = exports.convertNullableDate = exports.convertDate = void 0;
3
+ exports.convertNullableJSONList = exports.convertJSONList = exports.convertNullableJSON = exports.convertJSON = exports.convertNullableBoolList = exports.convertBoolList = exports.convertNullableDateList = exports.convertDateList = exports.convertNullableList = exports.convertList = exports.convertNullableBool = exports.convertBool = exports.convertNullableDate = exports.convertDate = void 0;
4
4
  const luxon_1 = require("luxon");
5
5
  // these are needed to deal with SQLite having different types stored in the db vs the representation
6
6
  // gotten back from the db/needed in ent land
@@ -95,3 +95,11 @@ function convertNullableJSON(val) {
95
95
  return convertJSON(val);
96
96
  }
97
97
  exports.convertNullableJSON = convertNullableJSON;
98
+ function convertJSONList(val) {
99
+ return convertList(val, convertJSON);
100
+ }
101
+ exports.convertJSONList = convertJSONList;
102
+ function convertNullableJSONList(val) {
103
+ return convertNullableList(val, convertJSON);
104
+ }
105
+ exports.convertNullableJSONList = convertNullableJSONList;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snowtop/ent",
3
- "version": "0.0.18",
3
+ "version": "0.0.22",
4
4
  "description": "snowtop ent framework",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/schema/field.d.ts CHANGED
@@ -101,8 +101,12 @@ export declare class DateField extends BaseField implements Field {
101
101
  format(val: any): any;
102
102
  }
103
103
  export declare function DateType(options: FieldOptions): DateField;
104
+ declare type EnumMap = {
105
+ [key: string]: string;
106
+ };
104
107
  export interface EnumOptions extends FieldOptions {
105
108
  values?: string[];
109
+ map?: EnumMap;
106
110
  tsType?: string;
107
111
  graphQLType?: string;
108
112
  createEnumType?: boolean;
@@ -110,6 +114,7 @@ export interface EnumOptions extends FieldOptions {
110
114
  export declare class EnumField extends BaseField implements Field {
111
115
  type: Type;
112
116
  private values?;
117
+ private map?;
113
118
  constructor(options: EnumOptions);
114
119
  convertForGQL(value: string): string;
115
120
  valid(val: any): boolean;
@@ -119,13 +124,22 @@ export declare function EnumType(options: EnumOptions): EnumField;
119
124
  export declare class ListField extends BaseField {
120
125
  private field;
121
126
  type: Type;
127
+ private validators;
122
128
  constructor(field: Field, options: FieldOptions);
129
+ validate(validator: (val: any[]) => boolean): this;
123
130
  valid(val: any): boolean;
131
+ private postgresVal;
124
132
  format(val: any): any;
133
+ minLen(l: number): this;
134
+ maxLen(l: number): this;
135
+ length(l: number): this;
136
+ range(start: any, stop: any): this;
125
137
  }
126
138
  export declare function StringListType(options: StringOptions): ListField;
127
139
  export declare function IntListType(options: FieldOptions): ListField;
140
+ export declare function IntegerListType(options: FieldOptions): ListField;
128
141
  export declare function FloatListType(options: FieldOptions): ListField;
142
+ export declare function BigIntegerListType(options: FieldOptions): ListField;
129
143
  export declare function BooleanListType(options: FieldOptions): ListField;
130
144
  export declare function TimestampListType(options: TimestampOptions): ListField;
131
145
  export declare function TimestamptzListType(options: TimestampOptions): ListField;
@@ -134,3 +148,4 @@ export declare function TimetzListType(options: TimeOptions): ListField;
134
148
  export declare function DateListType(options: FieldOptions): ListField;
135
149
  export declare function EnumListType(options: EnumOptions): ListField;
136
150
  export declare function UUIDListType(options: FieldOptions): ListField;
151
+ export {};
package/schema/field.js CHANGED
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
19
19
  return result;
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.UUIDListType = exports.EnumListType = exports.DateListType = exports.TimetzListType = exports.TimeListType = exports.TimestamptzListType = exports.TimestampListType = exports.BooleanListType = exports.FloatListType = exports.IntListType = exports.StringListType = exports.ListField = exports.EnumType = exports.EnumField = exports.DateType = exports.DateField = exports.TimetzType = exports.TimeType = exports.TimeField = exports.leftPad = exports.TimestamptzType = exports.TimestampType = exports.TimestampField = exports.StringType = exports.StringField = exports.BooleanType = exports.BooleanField = exports.FloatType = exports.FloatField = exports.BigIntegerType = exports.BigIntegerField = exports.IntegerType = exports.IntegerField = exports.UUIDType = exports.UUIDField = exports.BaseField = void 0;
22
+ exports.UUIDListType = exports.EnumListType = exports.DateListType = exports.TimetzListType = exports.TimeListType = exports.TimestamptzListType = exports.TimestampListType = exports.BooleanListType = exports.BigIntegerListType = exports.FloatListType = exports.IntegerListType = exports.IntListType = exports.StringListType = exports.ListField = exports.EnumType = exports.EnumField = exports.DateType = exports.DateField = exports.TimetzType = exports.TimeType = exports.TimeField = exports.leftPad = exports.TimestamptzType = exports.TimestampType = exports.TimestampField = exports.StringType = exports.StringField = exports.BooleanType = exports.BooleanField = exports.FloatType = exports.FloatField = exports.BigIntegerType = exports.BigIntegerField = exports.IntegerType = exports.IntegerField = exports.UUIDType = exports.UUIDField = exports.BaseField = void 0;
23
23
  const schema_1 = require("./schema");
24
24
  const snake_case_1 = require("snake-case");
25
25
  const luxon_1 = require("luxon");
@@ -64,6 +64,7 @@ class UUIDField extends BaseField {
64
64
  values: polymorphic.types,
65
65
  hideFromGraphQL: true,
66
66
  derivedWhenEmbedded: true,
67
+ nullable: options.nullable,
67
68
  }),
68
69
  ];
69
70
  }
@@ -74,6 +75,7 @@ class UUIDField extends BaseField {
74
75
  name,
75
76
  hideFromGraphQL: true,
76
77
  derivedWhenEmbedded: true,
78
+ nullable: options.nullable,
77
79
  }),
78
80
  ];
79
81
  }
@@ -382,20 +384,33 @@ class EnumField extends BaseField {
382
384
  // if createEnumType boolean, we create postgres enum otherwise we use a string for it
383
385
  dbType: options.createEnumType ? schema_1.DBType.Enum : schema_1.DBType.StringEnum,
384
386
  values: options.values,
387
+ enumMap: options.map,
385
388
  type: options.tsType || options.name,
386
389
  graphQLType: options.graphQLType || options.name,
387
390
  };
388
391
  if (!options.foreignKey) {
389
- if (!options.values) {
390
- throw new Error("values required if not look up table enum. Look-up table enum indicated by foreignKey field");
392
+ if (!options.values && !options.map) {
393
+ throw new Error("values or map required if not look up table enum. Look-up table enum indicated by foreignKey field");
391
394
  }
392
- if (!options.values.length) {
393
- throw new Error("need at least one value in enum type");
395
+ if (options.values) {
396
+ if (!options.values.length) {
397
+ throw new Error("need at least one value in enum type");
398
+ }
399
+ }
400
+ if (options.map) {
401
+ let count = 0;
402
+ for (const k in options.map) {
403
+ count++;
404
+ break;
405
+ }
406
+ if (!count) {
407
+ throw new Error("need at least one entry in enum map");
408
+ }
394
409
  }
395
410
  }
396
411
  else {
397
- if (options.values) {
398
- throw new Error("cannot specify values and foreign key for lookup table enum type");
412
+ if (options.values || options.map) {
413
+ throw new Error("cannot specify values or map and foreign key for lookup table enum type");
399
414
  }
400
415
  if (options.createEnumType) {
401
416
  throw new Error("cannot specify createEnumType without specifying values");
@@ -408,30 +423,55 @@ class EnumField extends BaseField {
408
423
  }
409
424
  }
410
425
  this.values = options.values;
426
+ this.map = options.map;
411
427
  }
428
+ // TODO need to update this for map
412
429
  convertForGQL(value) {
413
430
  return (0, snake_case_1.snakeCase)(value).toUpperCase();
414
431
  }
415
432
  valid(val) {
416
433
  // lookup table enum and indicated via presence of foreignKey
417
- if (!this.values) {
434
+ if (!this.values && !this.map) {
418
435
  return true;
419
436
  }
420
- let str = String(val);
421
- return this.values.some((value) => value === str || this.convertForGQL(value) === str);
437
+ if (this.values) {
438
+ let str = String(val);
439
+ return this.values.some((value) => value === str || this.convertForGQL(value) === str);
440
+ }
441
+ for (const k in this.map) {
442
+ const v = this.map[k];
443
+ if (v === val || this.convertForGQL(k) === val) {
444
+ // TODO decide on behavior for GQL since GQL only supports one type
445
+ return true;
446
+ }
447
+ }
448
+ return false;
422
449
  }
423
450
  format(val) {
424
451
  // TODO need to format correctly for graphql purposes...
425
452
  // how to best get the values in the db...
426
- if (!this.values) {
453
+ if (!this.values && !this.map) {
427
454
  return val;
428
455
  }
429
456
  let str = String(val);
430
- for (let i = 0; i < this.values.length; i++) {
431
- let value = this.values[i];
432
- // store the format that maps to the given value in the db instead of saving the upper case value
433
- if (str === value || str === this.convertForGQL(value)) {
434
- return value;
457
+ if (this.values) {
458
+ for (let i = 0; i < this.values.length; i++) {
459
+ let value = this.values[i];
460
+ // store the format that maps to the given value in the db instead of saving the upper case value
461
+ if (str === value || str === this.convertForGQL(value)) {
462
+ return value;
463
+ }
464
+ }
465
+ }
466
+ if (this.map) {
467
+ for (const k in this.map) {
468
+ const v = this.map[k];
469
+ if (str === v) {
470
+ return v;
471
+ }
472
+ if (str === this.convertForGQL(k)) {
473
+ return v;
474
+ }
435
475
  }
436
476
  }
437
477
  // whelp, just return what's passed
@@ -448,6 +488,7 @@ class ListField extends BaseField {
448
488
  constructor(field, options) {
449
489
  super();
450
490
  this.field = field;
491
+ this.validators = [];
451
492
  if (field.type.dbType === schema_1.DBType.List) {
452
493
  throw new Error(`nested lists not currently supported`);
453
494
  }
@@ -457,10 +498,19 @@ class ListField extends BaseField {
457
498
  };
458
499
  Object.assign(this, options);
459
500
  }
501
+ validate(validator) {
502
+ this.validators.push(validator);
503
+ return this;
504
+ }
460
505
  valid(val) {
461
506
  if (!Array.isArray(val)) {
462
507
  return false;
463
508
  }
509
+ for (const validator of this.validators) {
510
+ if (!validator(val)) {
511
+ return false;
512
+ }
513
+ }
464
514
  if (!this.field.valid) {
465
515
  return true;
466
516
  }
@@ -471,22 +521,66 @@ class ListField extends BaseField {
471
521
  }
472
522
  return true;
473
523
  }
524
+ postgresVal(val, jsonType) {
525
+ if (!jsonType) {
526
+ return val;
527
+ }
528
+ return JSON.stringify(val);
529
+ }
474
530
  format(val) {
475
531
  if (!Array.isArray(val)) {
476
532
  throw new Error(`need an array to format`);
477
533
  }
478
- if (this.field.format) {
479
- for (let i = 0; i < val.length; i++) {
480
- val[i] = this.field.format(val[i]);
534
+ const elemDBType = this.type.listElemType.dbType;
535
+ const jsonType = elemDBType === "JSON" || elemDBType === "JSONB";
536
+ const postgres = db_1.default.getDialect() === db_1.Dialect.Postgres;
537
+ if (!postgres && !this.field.format) {
538
+ return JSON.stringify(val);
539
+ }
540
+ let ret = [];
541
+ let postgresRet = "{";
542
+ for (let i = 0; i < val.length; i++) {
543
+ let formatted = val[i];
544
+ if (this.field.format) {
545
+ formatted = this.field.format(val[i]);
546
+ }
547
+ // postgres supports arrays natively so we
548
+ // structure it in the expected format
549
+ if (postgres) {
550
+ postgresRet += this.postgresVal(formatted, jsonType);
551
+ if (i !== val.length - 1) {
552
+ postgresRet += ",";
553
+ }
554
+ }
555
+ else {
556
+ ret[i] = formatted;
481
557
  }
482
558
  }
483
- // postgres supports arrays natively so we
484
- // structure it in the expected format
485
- if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
486
- return `{${val.join(",")}}`;
559
+ if (postgres) {
560
+ return postgresRet + "}";
487
561
  }
488
- // For SQLite, we store a JSON string
489
- return JSON.stringify(val);
562
+ return JSON.stringify(ret);
563
+ }
564
+ minLen(l) {
565
+ return this.validate((val) => val.length >= l);
566
+ }
567
+ maxLen(l) {
568
+ return this.validate((val) => val.length <= l);
569
+ }
570
+ length(l) {
571
+ return this.validate((val) => val.length === l);
572
+ }
573
+ // like python's range() function
574
+ // start is where to start and stop is the number to stop (not inclusive in the range)
575
+ range(start, stop) {
576
+ return this.validate((val) => {
577
+ for (const v of val) {
578
+ if (v < start || v >= stop) {
579
+ return false;
580
+ }
581
+ }
582
+ return true;
583
+ });
490
584
  }
491
585
  }
492
586
  exports.ListField = ListField;
@@ -498,10 +592,18 @@ function IntListType(options) {
498
592
  return new ListField(IntegerType(options), options);
499
593
  }
500
594
  exports.IntListType = IntListType;
595
+ function IntegerListType(options) {
596
+ return new ListField(IntegerType(options), options);
597
+ }
598
+ exports.IntegerListType = IntegerListType;
501
599
  function FloatListType(options) {
502
600
  return new ListField(FloatType(options), options);
503
601
  }
504
602
  exports.FloatListType = FloatListType;
603
+ function BigIntegerListType(options) {
604
+ return new ListField(BigIntegerType(options), options);
605
+ }
606
+ exports.BigIntegerListType = BigIntegerListType;
505
607
  function BooleanListType(options) {
506
608
  return new ListField(BooleanType(options), options);
507
609
  }
@@ -1,5 +1,5 @@
1
1
  import { FieldOptions, Type, Field, ImportType } from "./schema";
2
- import { BaseField } from "./field";
2
+ import { BaseField, ListField } from "./field";
3
3
  export interface JSONOptions extends FieldOptions {
4
4
  validator?: (val: any) => boolean;
5
5
  importType?: ImportType;
@@ -13,3 +13,5 @@ export declare class JSONField extends BaseField implements Field {
13
13
  }
14
14
  export declare function JSONType(options: JSONOptions): JSONField;
15
15
  export declare function JSONBType(options: JSONOptions): JSONField;
16
+ export declare function JSONBListType(options: JSONOptions): ListField;
17
+ export declare function JSONListType(options: JSONOptions): ListField;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JSONBType = exports.JSONType = exports.JSONField = void 0;
3
+ exports.JSONListType = exports.JSONBListType = exports.JSONBType = exports.JSONType = exports.JSONField = void 0;
4
4
  const schema_1 = require("./schema");
5
5
  const field_1 = require("./field");
6
6
  class JSONField extends field_1.BaseField {
@@ -38,3 +38,11 @@ function JSONBType(options) {
38
38
  return Object.assign(result, options);
39
39
  }
40
40
  exports.JSONBType = JSONBType;
41
+ function JSONBListType(options) {
42
+ return new field_1.ListField(JSONBType(options), options);
43
+ }
44
+ exports.JSONBListType = JSONBListType;
45
+ function JSONListType(options) {
46
+ return new field_1.ListField(JSONType(options), options);
47
+ }
48
+ exports.JSONListType = JSONListType;
@@ -83,12 +83,16 @@ export interface ImportType {
83
83
  path: string;
84
84
  type: string;
85
85
  }
86
+ declare type EnumMap = {
87
+ [key: string]: string;
88
+ };
86
89
  export interface Type {
87
90
  dbType: DBType;
88
91
  listElemType?: Type;
89
92
  type?: string;
90
93
  graphQLType?: string;
91
94
  values?: string[];
95
+ enumMap?: EnumMap;
92
96
  importType?: ImportType;
93
97
  }
94
98
  export interface ForeignKey {