@coderich/autograph 0.9.16 → 0.10.2
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/CHANGELOG.md +24 -0
- package/index.js +2 -6
- package/package.json +10 -10
- package/src/.DS_Store +0 -0
- package/src/core/EventEmitter.js +2 -4
- package/src/core/Resolver.js +35 -58
- package/src/core/Schema.js +5 -38
- package/src/core/ServerResolver.js +7 -93
- package/src/data/DataLoader.js +68 -27
- package/src/data/DataService.js +59 -58
- package/src/data/Field.js +71 -96
- package/src/data/Model.js +95 -113
- package/src/data/Pipeline.js +174 -0
- package/src/data/Type.js +19 -60
- package/src/driver/MongoDriver.js +53 -28
- package/src/graphql/ast/Field.js +44 -26
- package/src/graphql/ast/Model.js +5 -16
- package/src/graphql/ast/Node.js +0 -32
- package/src/graphql/ast/Schema.js +109 -112
- package/src/graphql/extension/api.js +22 -35
- package/src/graphql/extension/framework.js +25 -33
- package/src/graphql/extension/type.js +2 -2
- package/src/query/Query.js +73 -15
- package/src/query/QueryBuilder.js +37 -28
- package/src/query/QueryBuilderTransaction.js +3 -3
- package/src/query/QueryResolver.js +93 -44
- package/src/query/QueryService.js +31 -34
- package/src/service/app.service.js +56 -35
- package/src/service/decorator.service.js +21 -288
- package/src/service/event.service.js +5 -79
- package/src/service/graphql.service.js +0 -9
- package/src/service/schema.service.js +5 -3
- package/src/core/Rule.js +0 -107
- package/src/core/SchemaDecorator.js +0 -46
- package/src/core/Transformer.js +0 -68
- package/src/data/Memoizer.js +0 -39
- package/src/data/ResultSet.js +0 -246
- package/src/graphql/ast/SchemaDecorator.js +0 -138
- package/src/graphql/directive/authz.directive.js +0 -84
|
@@ -1,141 +1,138 @@
|
|
|
1
1
|
const FS = require('fs');
|
|
2
2
|
const Glob = require('glob');
|
|
3
3
|
const Merge = require('deepmerge');
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
4
|
+
const { Kind, print, parse, visit } = require('graphql');
|
|
5
|
+
const { mergeASTArray } = require('../../service/graphql.service');
|
|
6
|
+
const { deleteKeys } = require('../../service/app.service');
|
|
7
|
+
const frameworkExt = require('../extension/framework');
|
|
8
|
+
const typeExt = require('../extension/type');
|
|
9
|
+
const apiExt = require('../extension/api');
|
|
10
|
+
const TypeDefApi = require('./TypeDefApi');
|
|
6
11
|
const Node = require('./Node');
|
|
7
|
-
const Model = require('./Model');
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Schema
|
|
15
|
+
*
|
|
16
|
+
* This class helps facilitate dynamic modification of a schema before it is passed to makeExecutableSchema(). It allows
|
|
17
|
+
* for "intelligent" merging of schemas and exposes an API wrapper for typeDefs.
|
|
18
|
+
*
|
|
19
|
+
* A "schema" is defined by the following object attributes:
|
|
20
|
+
*
|
|
21
|
+
* typeDefs <String|Object> - GQL String or AST Object (also supports a mixed array of both)
|
|
22
|
+
* resolvers <Object> - GraphQL resolvers
|
|
23
|
+
* schemaDirectives <Object> - GraphQL directives
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
module.exports = class Schema extends TypeDefApi {
|
|
27
|
+
constructor(schema, toExecutableSchema) {
|
|
28
|
+
super();
|
|
29
|
+
this.toExecutableSchema = toExecutableSchema;
|
|
30
|
+
this.schema = { typeDefs: [], resolvers: {}, schemaDirectives: {} };
|
|
31
|
+
if (schema) this.mergeSchema(schema);
|
|
23
32
|
}
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Synchronously merge a schema
|
|
36
|
+
*/
|
|
37
|
+
mergeSchema(schema, options = {}) {
|
|
38
|
+
// Ensure this is a schema of sorts otherwise skip it
|
|
39
|
+
if (typeof schema !== 'string' && ['typeDefs', 'resolvers', 'schemaDirectives'].every(key => !schema[key])) return this;
|
|
40
|
+
|
|
41
|
+
// Here we want to normalize the schema into the shape { typeDefs, resolvers, schemaDirectives }
|
|
42
|
+
// We do NOT want to modify the schema object because that may cause unwanted side-effects.
|
|
43
|
+
const normalizedSchema = { ...schema };
|
|
44
|
+
if (typeof schema === 'string') normalizedSchema.typeDefs = [schema];
|
|
45
|
+
else if (schema.typeDefs && !Array.isArray(schema.typeDefs)) normalizedSchema.typeDefs = [schema.typeDefs];
|
|
46
|
+
|
|
47
|
+
// For typeDefs we want the AST so that it can be intelligently merged. Here we convert
|
|
48
|
+
// GQL strings to AST objects and also filter out anything that does not parse to AST.
|
|
49
|
+
if (normalizedSchema.typeDefs && normalizedSchema.typeDefs.length) {
|
|
50
|
+
normalizedSchema.typeDefs = deleteKeys(normalizedSchema.typeDefs.map((td) => {
|
|
51
|
+
try {
|
|
52
|
+
const ast = typeof td === 'object' ? td : parse(td);
|
|
53
|
+
return ast.definitions;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}), ['loc']).filter(Boolean).flat();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Now we're ready to merge the schema
|
|
61
|
+
const [left, right] = options.passive ? [normalizedSchema, this.schema] : [this.schema, normalizedSchema];
|
|
62
|
+
if (normalizedSchema.typeDefs && normalizedSchema.typeDefs.length) this.schema.typeDefs = mergeASTArray(left.typeDefs.concat(right.typeDefs));
|
|
63
|
+
if (normalizedSchema.resolvers) this.schema.resolvers = Merge(left.resolvers, right.resolvers);
|
|
64
|
+
if (normalizedSchema.schemaDirectives) this.schema.schemaDirectives = Merge(left.schemaDirectives, right.schemaDirectives);
|
|
65
|
+
|
|
66
|
+
// Chaining
|
|
67
|
+
return this;
|
|
33
68
|
}
|
|
34
69
|
|
|
35
70
|
/**
|
|
36
|
-
*
|
|
37
|
-
* Here I'm making last-minute modifications to the schema that is exposed to the API.
|
|
71
|
+
* Asynchronously load files from a given glob pattern and merge each schema
|
|
38
72
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return
|
|
73
|
+
mergeSchemaFromFiles(globPattern, options) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
Glob(globPattern, options, (err, files) => {
|
|
76
|
+
if (err) return reject(err);
|
|
77
|
+
|
|
78
|
+
return Promise.all(files.map((file) => {
|
|
79
|
+
return new Promise((res) => {
|
|
80
|
+
if (file.endsWith('.js')) res(require(file)); // eslint-disable-line global-require,import/no-dynamic-require
|
|
81
|
+
else res(FS.readFileSync(file, 'utf8'));
|
|
82
|
+
}).then(schema => this.mergeSchema(schema, options));
|
|
83
|
+
})).then(() => resolve(this)).catch(e => reject(e));
|
|
45
84
|
});
|
|
46
|
-
|
|
47
|
-
return definition;
|
|
48
85
|
});
|
|
49
|
-
|
|
50
|
-
const ast = Object.assign({}, this.ast, { definitions });
|
|
51
|
-
const schema = Object.assign({}, this.schema, { typeDefs: ast });
|
|
52
|
-
validateSchema(schema);
|
|
53
|
-
return schema;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
getModel(name) {
|
|
57
|
-
return this.modelsByName[name] || this.modelsByKey[name];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
getModels() {
|
|
61
|
-
return this.models;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getModelNames() {
|
|
65
|
-
return this.getModels().map(model => model.getName());
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
getModelMap() {
|
|
69
|
-
return this.getModels().reduce((prev, model) => Object.assign(prev, { [model.getName()]: model }), {});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
getInput(name) {
|
|
73
|
-
return this.getInputs().find(input => input.getName() === name);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
getInputs() {
|
|
77
|
-
return this.inputs;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
getScalar(name) {
|
|
81
|
-
return this.getScalars().find(scalar => scalar.getName() === name);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
getScalars() {
|
|
85
|
-
return this.scalars;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
getMarkedModels() {
|
|
97
|
-
return this.getModels().filter(model => model.isMarkedModel());
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
getEntityModels() {
|
|
101
|
-
return this.getModels().filter(model => model.isEntity());
|
|
88
|
+
/**
|
|
89
|
+
* Traverses the current schema's typeDefs in order to keep the TypeDefApi in sync. This operation
|
|
90
|
+
* only needs to be called when typeDefs have been changed and you want to keep the data model in sync.
|
|
91
|
+
*/
|
|
92
|
+
initialize() {
|
|
93
|
+
super.initialize(this.schema.typeDefs);
|
|
94
|
+
this.getModels().forEach(model => model.initialize());
|
|
95
|
+
return this;
|
|
102
96
|
}
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Decorate the schema with Autograph's default api/definitions
|
|
100
|
+
*/
|
|
101
|
+
decorate() {
|
|
102
|
+
this.initialize();
|
|
103
|
+
this.mergeSchema(frameworkExt(this), { passive: true });
|
|
104
|
+
this.mergeSchema(typeExt(this), { passive: true });
|
|
105
|
+
this.initialize();
|
|
106
|
+
this.mergeSchema(apiExt(this), { passive: true });
|
|
107
|
+
this.finalize();
|
|
108
|
+
return this;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
resolvers: {},
|
|
119
|
-
schemaDirectives: {},
|
|
111
|
+
/**
|
|
112
|
+
* This should be called once before passing to makeExecutableSchema()
|
|
113
|
+
*/
|
|
114
|
+
finalize() {
|
|
115
|
+
const definitions = visit(this.schema.typeDefs, {
|
|
116
|
+
[Kind.FIELD_DEFINITION]: (node) => {
|
|
117
|
+
const scope = new Node(node, 'field').getDirectiveArg('field', 'gqlScope', 'crud');
|
|
118
|
+
if (scope === null || scope.indexOf('r') === -1) return null; // Delete node
|
|
119
|
+
return false; // Stop traversing this node
|
|
120
|
+
},
|
|
120
121
|
});
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
this.schema.typeDefs = { kind: Kind.DOCUMENT, definitions };
|
|
124
|
+
return this;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.ast.definitions = mergeASTArray(this.ast.definitions.concat(...definitions));
|
|
128
|
-
this.schema.resolvers = Merge(schemas.reduce((prev, schema) => Merge(prev, schema.resolvers || {}), {}), this.schema.resolvers);
|
|
129
|
-
return this;
|
|
127
|
+
makeExecutableSchema() {
|
|
128
|
+
return this.toExecutableSchema(this.schema);
|
|
130
129
|
}
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
this.initialize();
|
|
135
|
-
return this;
|
|
131
|
+
toObject() {
|
|
132
|
+
return this.schema;
|
|
136
133
|
}
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
return
|
|
135
|
+
toString() {
|
|
136
|
+
return print(this.typeDefs);
|
|
140
137
|
}
|
|
141
138
|
};
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
const { get } = require('lodash');
|
|
2
2
|
const { Kind } = require('graphql');
|
|
3
3
|
const ServerResolver = require('../../core/ServerResolver');
|
|
4
|
-
const { ucFirst, fromGUID } = require('../../service/app.service');
|
|
4
|
+
const { ucFirst, toGUID, fromGUID } = require('../../service/app.service');
|
|
5
5
|
const { findGQLModels } = require('../../service/schema.service');
|
|
6
|
-
const { makeCreateAPI, makeReadAPI, makeUpdateAPI, makeDeleteAPI, makeSubscriptionAPI,
|
|
6
|
+
const { makeCreateAPI, makeReadAPI, makeUpdateAPI, makeDeleteAPI, makeSubscriptionAPI, makeQueryResolver, makeMutationResolver } = require('../../service/decorator.service');
|
|
7
7
|
|
|
8
8
|
const interfaceKinds = [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
|
|
9
9
|
|
|
10
10
|
const getGQLWhereFields = (model) => {
|
|
11
11
|
return model.getFields().filter((field) => {
|
|
12
12
|
if (!field.hasGQLScope('r')) return false;
|
|
13
|
-
if (field.hasBoundValue() && !field.getDirectiveArg('value', 'passive')) return false;
|
|
14
13
|
const modelRef = field.getModelRef();
|
|
15
14
|
if (modelRef && !modelRef.isEmbedded() && !modelRef.isEntity()) return false;
|
|
16
15
|
return true;
|
|
@@ -25,8 +24,6 @@ module.exports = (schema) => {
|
|
|
25
24
|
const createModels = findGQLModels('c', markedModels, allModels);
|
|
26
25
|
const readModels = findGQLModels('r', markedModels, allModels);
|
|
27
26
|
const updateModels = findGQLModels('u', markedModels, allModels);
|
|
28
|
-
const deleteModels = findGQLModels('d', markedModels, allModels);
|
|
29
|
-
const spliceModels = [...new Set([...createModels, ...updateModels, ...deleteModels])];
|
|
30
27
|
|
|
31
28
|
return ({
|
|
32
29
|
typeDefs: [
|
|
@@ -40,7 +37,6 @@ module.exports = (schema) => {
|
|
|
40
37
|
input ${model.getName()}InputUpdate {
|
|
41
38
|
${model.getFields().filter(field => field.hasGQLScope('u') && !field.isVirtual()).map(field => `${field.getName()}: ${field.getGQLType('InputUpdate')}`)}
|
|
42
39
|
}
|
|
43
|
-
# ${makeInputSplice(model)}
|
|
44
40
|
`),
|
|
45
41
|
|
|
46
42
|
...readModels.map(model => `
|
|
@@ -50,19 +46,14 @@ module.exports = (schema) => {
|
|
|
50
46
|
input ${model.getName()}InputSort {
|
|
51
47
|
${getGQLWhereFields(model).map(field => `${field.getName()}: ${field.getModelRef() ? `${ucFirst(field.getDataRef())}InputSort` : 'SortOrderEnum'}`)}
|
|
52
48
|
}
|
|
53
|
-
`),
|
|
54
|
-
|
|
55
|
-
...readModels.map(model => `
|
|
56
49
|
extend ${interfaceKinds.indexOf(model.getKind()) > -1 ? 'interface' : 'type'} ${model.getName()} {
|
|
57
50
|
${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}${field.getExtendArgs()}: ${field.getPayloadType()}`)}
|
|
58
51
|
}
|
|
59
|
-
|
|
60
52
|
type ${model.getName()}Connection {
|
|
53
|
+
count: Int!
|
|
61
54
|
pageInfo: PageInfo!
|
|
62
55
|
edges: [${model.getName()}Edge]
|
|
63
|
-
count: Int!
|
|
64
56
|
}
|
|
65
|
-
|
|
66
57
|
type ${model.getName()}Edge {
|
|
67
58
|
node: ${model.getName()}
|
|
68
59
|
cursor: String!
|
|
@@ -90,7 +81,7 @@ module.exports = (schema) => {
|
|
|
90
81
|
}
|
|
91
82
|
|
|
92
83
|
type ${model.getName()}SubscriptionPayloadEventData {
|
|
93
|
-
${model
|
|
84
|
+
${getGQLWhereFields(model).map(field => `${field.getName()}: ${field.getSubscriptionType()}`)}
|
|
94
85
|
}
|
|
95
86
|
|
|
96
87
|
interface ${model.getName()}SubscriptionQuery {
|
|
@@ -105,13 +96,6 @@ module.exports = (schema) => {
|
|
|
105
96
|
${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}: ${field.getPayloadType()}`)}
|
|
106
97
|
}
|
|
107
98
|
`),
|
|
108
|
-
|
|
109
|
-
...spliceModels.map(model => `
|
|
110
|
-
#input ${model.getName()}InputSplice {
|
|
111
|
-
# with: ${model}InputWhere
|
|
112
|
-
# put: ${model}InputUpdate
|
|
113
|
-
#}
|
|
114
|
-
`),
|
|
115
99
|
].concat([
|
|
116
100
|
`type PageInfo {
|
|
117
101
|
startCursor: String!
|
|
@@ -148,21 +132,20 @@ module.exports = (schema) => {
|
|
|
148
132
|
const isConnection = field.isConnection();
|
|
149
133
|
|
|
150
134
|
return Object.assign(def, {
|
|
151
|
-
[fieldName]: (
|
|
152
|
-
if (fieldName === 'id') return autograph.legacyMode ?
|
|
153
|
-
|
|
154
|
-
const $fieldName = `$${fieldName}`;
|
|
135
|
+
[fieldName]: (doc, args, { autograph }, info) => {
|
|
136
|
+
if (fieldName === 'id') return autograph.legacyMode ? doc.id : toGUID(modelName, doc.id);
|
|
155
137
|
|
|
138
|
+
// If this field is a connection we return thunks in order to delay query
|
|
139
|
+
// until the "Connection" resolver (below) is run
|
|
156
140
|
if (isConnection) {
|
|
157
141
|
return {
|
|
158
|
-
args,
|
|
159
|
-
edges:
|
|
160
|
-
pageInfo:
|
|
161
|
-
count: root[`${$fieldName}:count`], // Thunk to $$count
|
|
142
|
+
count: () => field.count(autograph.resolver, doc, args),
|
|
143
|
+
edges: () => field.resolve(autograph.resolver, doc, args),
|
|
144
|
+
pageInfo: () => field.resolve(autograph.resolver, doc, args),
|
|
162
145
|
};
|
|
163
146
|
}
|
|
164
147
|
|
|
165
|
-
return
|
|
148
|
+
return field.resolve(autograph.resolver, doc, args);
|
|
166
149
|
},
|
|
167
150
|
});
|
|
168
151
|
}, {});
|
|
@@ -179,24 +162,28 @@ module.exports = (schema) => {
|
|
|
179
162
|
return Object.assign(prev, {
|
|
180
163
|
[modelName]: fieldResolvers,
|
|
181
164
|
[`${modelName}Connection`]: {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
165
|
+
count: ({ count }) => count(),
|
|
166
|
+
edges: ({ edges }) => edges().then(rs => rs.map(node => ({ cursor: get(node, '$$cursor'), node }))),
|
|
167
|
+
pageInfo: ({ pageInfo }) => pageInfo().then(rs => get(rs, '$$pageInfo')),
|
|
185
168
|
},
|
|
186
169
|
});
|
|
187
170
|
}, {
|
|
188
171
|
Node: {
|
|
189
|
-
__resolveType: (
|
|
172
|
+
__resolveType: (doc, args, context, info) => doc.__typename, // eslint-disable-line no-underscore-dangle
|
|
190
173
|
},
|
|
191
174
|
|
|
192
175
|
Query: entityModels.reduce((prev, model) => {
|
|
193
176
|
return Object.assign(prev, makeQueryResolver(model.getName(), model, resolver));
|
|
194
177
|
}, {
|
|
195
|
-
node: (
|
|
178
|
+
node: (doc, args, context, info) => {
|
|
196
179
|
const { id } = args;
|
|
197
180
|
const [modelName] = fromGUID(id);
|
|
198
181
|
const model = schema.getModel(modelName);
|
|
199
|
-
return resolver.get(context, model, args, false, info)
|
|
182
|
+
return resolver.get(context, model, args, false, info).then((result) => {
|
|
183
|
+
if (result == null) return result;
|
|
184
|
+
result.__typename = modelName; // eslint-disable-line no-underscore-dangle
|
|
185
|
+
return result;
|
|
186
|
+
});
|
|
200
187
|
},
|
|
201
188
|
}),
|
|
202
189
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const Pipeline = require('../../data/Pipeline');
|
|
2
|
+
|
|
3
|
+
Pipeline.createPresets();
|
|
3
4
|
|
|
4
5
|
module.exports = (schema) => {
|
|
5
6
|
return {
|
|
@@ -7,51 +8,49 @@ module.exports = (schema) => {
|
|
|
7
8
|
scalar AutoGraphMixed
|
|
8
9
|
scalar AutoGraphDriver
|
|
9
10
|
scalar AutoGraphDateTime @field(transform: toDate)
|
|
10
|
-
enum
|
|
11
|
-
enum AutoGraphTransformEnum { ${Object.keys(Transformer.getInstances()).join(' ')} }
|
|
11
|
+
enum AutoGraphPipelineEnum { ${Object.keys(Pipeline).join(' ')} }
|
|
12
12
|
enum AutoGraphAuthzEnum { private protected public }
|
|
13
|
-
enum AutoGraphValueScopeEnum { self context }
|
|
14
13
|
enum AutoGraphOnDeleteEnum { cascade nullify restrict defer }
|
|
15
14
|
enum AutoGraphIndexEnum { unique }
|
|
16
15
|
|
|
17
16
|
directive @model(
|
|
18
|
-
|
|
17
|
+
id: String # Specify db key (default "id")
|
|
18
|
+
key: String # Specify db table/collection name
|
|
19
|
+
createdAt: String # Specify db key (default "createdAt")
|
|
20
|
+
updatedAt: String # Specify db key (default "updatedAt")
|
|
21
|
+
meta: AutoGraphMixed # Custom input "meta" field for mutations
|
|
22
|
+
embed: Boolean # Mark this an embedded model (default false)
|
|
23
|
+
persist: Boolean # Persist this model (default true)
|
|
19
24
|
gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
|
|
20
25
|
dalScope: AutoGraphMixed # Dictate how the DAL behaves
|
|
21
26
|
fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
|
|
22
|
-
meta: AutoGraphMixed # Custom input 'meta' field for mutations
|
|
23
|
-
embed: Boolean # Mark this an embedded model (default false)
|
|
24
|
-
persist: Boolean # Persist this model (default true)
|
|
25
27
|
driver: AutoGraphDriver # External data driver
|
|
26
28
|
authz: AutoGraphAuthzEnum # Access level used for authorization (default: private)
|
|
27
29
|
namespace: String # Logical grouping of models that can be globbed (useful for authz)
|
|
28
|
-
|
|
29
|
-
# Override auto-gen
|
|
30
|
-
id: String
|
|
31
|
-
createdAt: String
|
|
32
|
-
updatedAt: String
|
|
33
30
|
) on OBJECT | INTERFACE
|
|
34
31
|
|
|
35
32
|
directive @field(
|
|
36
|
-
|
|
33
|
+
id: String # Specify the ModelRef this field FK References
|
|
34
|
+
key: String # Specify db key
|
|
35
|
+
persist: Boolean # Persist this field (default true)
|
|
36
|
+
connection: Boolean # Treat this field as a connection type (default false - rolling this out slowly)
|
|
37
|
+
default: AutoGraphMixed # Define a default value
|
|
37
38
|
ref: AutoGraphMixed # Specify the modelRef field's name (overrides isEmbedded)
|
|
38
39
|
gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
|
|
39
40
|
dalScope: AutoGraphMixed # Dictate how the DAL behaves
|
|
40
41
|
fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
|
|
41
|
-
|
|
42
|
-
default: AutoGraphMixed # Define a default value
|
|
43
|
-
embedApi: Boolean # Should we also create an embedded API from this (default false)
|
|
44
|
-
connection: Boolean # Treat this field as a connection type (default false - rolling this out slowly)
|
|
45
|
-
|
|
46
|
-
noRepeat: Boolean
|
|
42
|
+
onDelete: AutoGraphOnDeleteEnum # onDelete behavior
|
|
47
43
|
|
|
48
44
|
authz: AutoGraphAuthzEnum # Access level used for authorization (default: private)
|
|
49
|
-
onDelete: AutoGraphOnDeleteEnum
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
# Pipeline Structure
|
|
47
|
+
instruct: [AutoGraphPipelineEnum!]
|
|
48
|
+
destruct: [AutoGraphPipelineEnum!]
|
|
49
|
+
restruct: [AutoGraphPipelineEnum!]
|
|
50
|
+
construct: [AutoGraphPipelineEnum!]
|
|
51
|
+
serialize: [AutoGraphPipelineEnum!]
|
|
52
|
+
deserialize: [AutoGraphPipelineEnum!]
|
|
53
|
+
transform: [AutoGraphPipelineEnum!]
|
|
55
54
|
) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
|
|
56
55
|
|
|
57
56
|
directive @link(
|
|
@@ -60,13 +59,6 @@ module.exports = (schema) => {
|
|
|
60
59
|
use: AutoGraphMixed # The VALUE to use (default's to @link'd value); useful for many-to-many relationships
|
|
61
60
|
) on FIELD_DEFINITION
|
|
62
61
|
|
|
63
|
-
directive @value(
|
|
64
|
-
path: String! # The path to the data
|
|
65
|
-
# merge: Boolean # Deep merge the data? (default false - overwrite) [does not even look supported at the moment]
|
|
66
|
-
passive: Boolean # If value exists leave it alone (default false)
|
|
67
|
-
scope: AutoGraphValueScopeEnum # Where to look for the data (default self)
|
|
68
|
-
) on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
|
|
69
|
-
|
|
70
62
|
directive @index(
|
|
71
63
|
name: String
|
|
72
64
|
on: [AutoGraphMixed!]!
|
|
@@ -17,8 +17,8 @@ module.exports = (schema) => {
|
|
|
17
17
|
return `
|
|
18
18
|
extend type ${modelName}${interfacesGQL} {
|
|
19
19
|
${id ? `id: ID! @field(key: "${id}", gqlScope: r)` : ''}
|
|
20
|
-
${createdAt ? `createdAt: AutoGraphDateTime @field(key: "${createdAt}", gqlScope: r)` : ''}
|
|
21
|
-
${updatedAt ? `updatedAt: AutoGraphDateTime @field(key: "${updatedAt}", gqlScope: r)` : ''}
|
|
20
|
+
${createdAt ? `createdAt: AutoGraphDateTime @field(key: "${createdAt}", serialize: createdAt, gqlScope: r)` : ''}
|
|
21
|
+
${updatedAt ? `updatedAt: AutoGraphDateTime @field(key: "${updatedAt}", serialize: timestamp, gqlScope: r)` : ''}
|
|
22
22
|
}
|
|
23
23
|
`;
|
|
24
24
|
}
|