@coderich/autograph 0.13.11 → 0.13.13

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coderich/autograph",
3
3
  "main": "index.js",
4
- "version": "0.13.11",
4
+ "version": "0.13.13",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -78,8 +78,9 @@ class Emitter extends EventEmitter {
78
78
  } : (event, next) => {
79
79
  if (arr.includes(`${event.query[prop]}`)) {
80
80
  if (once) this.removeListener(eventName, wrapper);
81
- next(listener(event, next));
81
+ return next(listener(event, next));
82
82
  }
83
+ return next();
83
84
  };
84
85
 
85
86
  return this.on(eventName, wrapper);
@@ -279,6 +279,10 @@ module.exports = class Resolver {
279
279
  const type = query.isMutation ? 'Mutation' : 'Query';
280
280
  const event = { schema: this.#schema, context: this.#context, resolver: this, query };
281
281
 
282
+ // Backwards compat
283
+ query.match = { ...query.where };
284
+ query.toObject = () => query;
285
+
282
286
  return Emitter.emit(`pre${type}`, event).then(async (resultEarly) => {
283
287
  if (resultEarly !== undefined) return resultEarly;
284
288
  if (Util.isEqual(query.changeset, { added: {}, updated: {}, deleted: {} })) return query.doc;
@@ -8,9 +8,12 @@ const Pipeline = require('../data/Pipeline');
8
8
  const Emitter = require('../data/Emitter');
9
9
 
10
10
  const operations = ['Query', 'Mutation', 'Subscription'];
11
- // const interfaceKinds = [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
12
- const modelKinds = [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION, Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
13
- const allowedKinds = modelKinds.concat(Kind.DOCUMENT, Kind.FIELD_DEFINITION, Kind.NON_NULL_TYPE, Kind.NAMED_TYPE, Kind.LIST_TYPE, Kind.DIRECTIVE);
11
+ const interfaceKinds = [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
12
+ // const unionKinds = [Kind.UNION_TYPE_DEFINITION, Kind.UNION_TYPE_EXTENSION];
13
+ const scalarKinds = [Kind.SCALAR_TYPE_DEFINITION, Kind.SCALAR_TYPE_EXTENSION, Kind.ENUM_TYPE_DEFINITION, Kind.ENUM_TYPE_EXTENSION];
14
+ const fieldKinds = [Kind.FIELD_DEFINITION].concat(scalarKinds);
15
+ const modelKinds = [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].concat(interfaceKinds);
16
+ const allowedKinds = modelKinds.concat(fieldKinds).concat(Kind.DOCUMENT, Kind.NON_NULL_TYPE, Kind.NAMED_TYPE, Kind.LIST_TYPE, Kind.DIRECTIVE).concat(scalarKinds);
14
17
  const pipelines = ['finalize', 'construct', 'restruct', 'instruct', 'normalize', 'serialize'];
15
18
  const inputPipelines = ['finalize', 'construct', 'instruct', 'normalize', 'serialize'];
16
19
  const scalars = ['ID', 'String', 'Float', 'Int', 'Boolean'];
@@ -110,7 +113,6 @@ module.exports = class Schema {
110
113
  parse() {
111
114
  if (this.#schema) return this.#schema;
112
115
 
113
- // const schema = buildASTSchema(this.#typeDefs);
114
116
  const { directives, namespace } = this.#config;
115
117
  this.#schema = { types: {}, models: {}, indexes: [], namespace };
116
118
  let model, field, isField, isList;
@@ -119,21 +121,21 @@ module.exports = class Schema {
119
121
  // Deprecate
120
122
  this.#schema.getModel = name => this.#schema.models[`${name}`];
121
123
 
122
- // Parse AST
124
+ // Parse AST (build/defined this.#schema)
123
125
  visit(this.#typeDefs, {
124
126
  enter: (node) => {
125
127
  const name = node.name?.value;
126
- if (!allowedKinds.includes(node.kind)) return false;
128
+ if (!allowedKinds.includes(node.kind) || operations.includes(name)) return false;
127
129
 
128
- if (modelKinds.includes(node.kind) && !operations.includes(name)) {
130
+ if (modelKinds.includes(node.kind)) {
129
131
  // this.#schema.types[name] = schema.getType(name);
130
132
 
131
133
  model = this.#schema.models[name] = {
132
134
  name,
133
135
  key: name,
134
136
  fields: {},
135
- crud: 'crud',
136
- scope: 'crud',
137
+ crud: 'crud', // For use when creating API Queries and Mutations
138
+ scope: 'crud', // For use when defining types (how it's field.model reference can be used)
137
139
  idField: 'id',
138
140
  isPersistable: true,
139
141
  source: this.#config.dataSources?.default,
@@ -141,16 +143,20 @@ module.exports = class Schema {
141
143
  directives: {},
142
144
  toString: () => name,
143
145
  };
144
- } else if (node.kind === Kind.FIELD_DEFINITION) {
146
+ } else if (fieldKinds.includes(node.kind)) {
145
147
  isField = true;
146
- field = model.fields[name] = {
148
+ field = {
147
149
  name,
148
150
  key: name,
149
- crud: 'crud',
150
151
  pipelines: pipelines.reduce((prev, key) => Object.assign(prev, { [key]: [] }), {}),
151
152
  directives: {},
152
153
  toString: () => name,
153
154
  };
155
+ if (model) model.fields[name] = field;
156
+ }
157
+
158
+ if (scalarKinds.includes(node.kind)) {
159
+ scalars.push(node.name.value);
154
160
  } else if (node.kind === Kind.NON_NULL_TYPE) {
155
161
  field[isList ? 'isArrayRequired' : 'isRequired'] = true;
156
162
  } else if (node.kind === Kind.NAMED_TYPE) {
@@ -167,8 +173,7 @@ module.exports = class Schema {
167
173
 
168
174
  node.arguments.forEach((arg) => {
169
175
  const key = arg.name.value;
170
- const { value: val, values } = arg.value;
171
- const value = values ? values.map(n => n.value) : val;
176
+ const value = Schema.#resolveNodeValue(arg.value);
172
177
  target.directives[name][key] = value;
173
178
 
174
179
  if (name === directives.index) this.#schema.indexes[this.#schema.indexes.length - 1][key] = value;
@@ -227,7 +232,6 @@ module.exports = class Schema {
227
232
  case 'model-gqlScope': { model.crud = value; break; }
228
233
  case 'model-fieldScope': { model.scope = value; break; }
229
234
  case 'field-gqlScope': { field.crud = value; break; }
230
- case 'field-fieldScope': { field.scope = value; break; }
231
235
 
232
236
  // Pipelines
233
237
  default: {
@@ -243,7 +247,7 @@ module.exports = class Schema {
243
247
  return undefined; // Continue
244
248
  },
245
249
  leave: (node) => {
246
- if (modelKinds.includes(node.kind) && !operations.includes(node.name.value)) {
250
+ if (modelKinds.includes(node.kind)) {
247
251
  const $model = model;
248
252
  // const idField = $model.fields[$model.idField];
249
253
  // $model.primaryKey = Util.nvl(idField?.key, idField?.name, 'id');
@@ -308,11 +312,12 @@ module.exports = class Schema {
308
312
  // Field resolution comes first (unshift)
309
313
  thunks.unshift(($schema) => {
310
314
  $field.model = $schema.models[$field.type];
311
- $field.linkBy = $field.linkBy || $field.model?.idField;
315
+ $field.crud = Util.uvl($field.crud, $field.model?.scope, 'crud');
316
+ $field.linkBy ??= $field.model?.idField;
312
317
  $field.linkField = $field.isVirtual ? $model.fields[$model.idField] : $field;
313
318
  $field.isFKReference = !$field.isPrimaryKey && $field.model?.isMarkedModel && !$field.model?.isEmbedded;
314
319
  $field.isEmbedded = Boolean($field.model && !$field.isFKReference && !$field.isPrimaryKey);
315
- $field.isScalar = Boolean(!$field.model || scalars.includes($field.type));
320
+ $field.isScalar = scalars.includes($field.type);
316
321
 
317
322
  if ($field.isArray) $field.pipelines.normalize.unshift('toArray');
318
323
  if ($field.isPrimaryKey) $field.pipelines.serialize.unshift('$pk'); // Will create/convert to FK type always
@@ -360,6 +365,23 @@ module.exports = class Schema {
360
365
  return fieldKeys.reduce((parent, key) => Object.values(parent.fields || parent.model.fields).find(el => el[prop] === key) || parent, $model);
361
366
  };
362
367
 
368
+ // Mutate typeDefs
369
+ let $model;
370
+ this.#typeDefs = visit(this.#typeDefs, {
371
+ enter: (node) => {
372
+ const name = node.name?.value;
373
+ if (!allowedKinds.includes(node.kind) || operations.includes(name)) return false;
374
+
375
+ if (modelKinds.includes(node.kind)) {
376
+ $model = this.#schema.models[name];
377
+ } else if (fieldKinds.includes(node.kind)) {
378
+ if (!Util.uvl($model?.fields[name]?.crud, 'crud')?.includes('r')) return null;
379
+ }
380
+
381
+ return undefined;
382
+ },
383
+ });
384
+
363
385
  // Return schema
364
386
  return this.#schema;
365
387
  }
@@ -388,6 +410,15 @@ module.exports = class Schema {
388
410
  return this.#config.makeExecutableSchema(this.toObject());
389
411
  }
390
412
 
413
+ static #resolveNodeValue(node) {
414
+ switch (node.kind) {
415
+ case 'NullValue': return null;
416
+ case 'ListValue': return node.values.map(Schema.#resolveNodeValue);
417
+ case 'ObjectValue': return node.fields.reduce((prev, field) => Object.assign(prev, { [field.name.value]: Schema.#resolveNodeValue(field.value) }), {});
418
+ default: return node.value ?? node;
419
+ }
420
+ }
421
+
391
422
  static #framework(directives) {
392
423
  const { model, field, link, index } = directives;
393
424
 
@@ -444,7 +475,6 @@ module.exports = class Schema {
444
475
  ref: AutoGraphMixed # Specify the modelRef field's name (overrides isEmbedded)
445
476
  gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
446
477
  dalScope: AutoGraphMixed # Dictate how the DAL behaves
447
- fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
448
478
  destruct: [AutoGraphPipelineEnum!]
449
479
  transform: [AutoGraphPipelineEnum!]
450
480
  deserialize: [AutoGraphPipelineEnum!]
@@ -466,9 +496,9 @@ module.exports = class Schema {
466
496
 
467
497
  static #api(schema) {
468
498
  // These models are for creating types
469
- const readModels = Object.values(schema.models).filter(model => model.crud?.includes('r'));
470
- const createModels = Object.values(schema.models).filter(model => model.crud?.includes('c'));
471
- const updateModels = Object.values(schema.models).filter(model => model.crud?.includes('u'));
499
+ const readModels = Object.values(schema.models).filter(model => [model.crud, model.scope].join()?.includes('r'));
500
+ const createModels = Object.values(schema.models).filter(model => [model.crud, model.scope].join()?.includes('c'));
501
+ const updateModels = Object.values(schema.models).filter(model => [model.crud, model.scope].join()?.includes('u'));
472
502
 
473
503
  // These are for defining schema queries/mutations
474
504
  const entityModels = Object.values(schema.models).filter(model => model.isEntity);
@@ -479,6 +509,7 @@ module.exports = class Schema {
479
509
  return {
480
510
  typeDefs: `
481
511
  scalar AutoGraphMixed
512
+ scalar AutoGraphDateTime
482
513
 
483
514
  interface Node { id: ID! }
484
515
 
@@ -505,10 +536,10 @@ module.exports = class Schema {
505
536
 
506
537
  return `
507
538
  input ${model}InputWhere {
508
- ${fields.map(field => `${field}: ${field.model?.isEntity ? `${field.model}InputWhere` : 'AutoGraphMixed'}`)}
539
+ ${fields.map(field => `${field}: ${field.model ? `${field.model}InputWhere` : 'AutoGraphMixed'}`)}
509
540
  }
510
541
  input ${model}InputSort {
511
- ${fields.map(field => `${field}: ${field.model?.isEntity ? `${field.model}InputSort` : 'SortOrderEnum'}`)}
542
+ ${fields.map(field => `${field}: ${field.model ? `${field.model}InputSort` : 'SortOrderEnum'}`)}
512
543
  }
513
544
  type ${model}Connection {
514
545
  count: Int!
@@ -586,6 +617,47 @@ module.exports = class Schema {
586
617
  ): ${model}SubscriptionPayload!
587
618
  `)}
588
619
  }
620
+
621
+ ${subscriptionModels.map((model) => {
622
+ const fields = Object.values(model.fields).filter(field => field.crud?.includes('r'));
623
+
624
+ return `
625
+ input ${model}SubscriptionInputFilter {
626
+ when: [SubscriptionWhenEnum!]! = [preEvent, postEvent]
627
+ where: ${model}SubscriptionInputWhere! = {}
628
+ }
629
+
630
+ input ${model}SubscriptionInputWhere {
631
+ ${fields.map(field => `${field}: ${field.model ? `${field.model}InputWhere` : 'AutoGraphMixed'}`)}
632
+ }
633
+
634
+ type ${model}SubscriptionPayload {
635
+ event: ${model}SubscriptionPayloadEvent
636
+ query: ${model}SubscriptionQuery
637
+ }
638
+
639
+ type ${model}SubscriptionPayloadEvent {
640
+ crud: SubscriptionCrudEnum!
641
+ data: ${model}SubscriptionPayloadEventData!
642
+ }
643
+
644
+ type ${model}SubscriptionPayloadEventData {
645
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
646
+ }
647
+
648
+ interface ${model}SubscriptionQuery {
649
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
650
+ }
651
+
652
+ type ${model}Create implements ${model}SubscriptionQuery {
653
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
654
+ }
655
+
656
+ type ${model}Update implements ${model}SubscriptionQuery {
657
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
658
+ }
659
+ `;
660
+ })}
589
661
  ` : ''}
590
662
  `,
591
663
  resolvers: {
@@ -649,12 +721,12 @@ module.exports = class Schema {
649
721
 
650
722
  static #getGQLType(field, suffix) {
651
723
  let { type } = field;
652
- const { isEmbedded, isRequired, isScalar, isArray, isArrayRequired, defaultValue } = field;
724
+ const { isEmbedded, isRequired, isScalar, isArray, isArrayRequired, isPrimaryKey, defaultValue } = field;
653
725
  const modelType = `${type}${suffix}`;
654
726
  if (suffix && !isScalar) type = isEmbedded ? modelType : 'ID';
655
727
  type = isArray ? `[${type}${isArrayRequired ? '!' : ''}]` : type;
656
728
  if (!suffix && isRequired) type += '!';
657
- if (suffix === 'InputCreate' && isRequired && defaultValue != null) type += '!';
729
+ if (suffix === 'InputCreate' && !isPrimaryKey && isRequired && defaultValue == null) type += '!';
658
730
  return type;
659
731
  }
660
732