@coderich/autograph 0.11.0 → 0.12.0
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 +41 -0
- package/package.json +4 -7
- package/src/.DS_Store +0 -0
- package/src/core/.DS_Store +0 -0
- package/src/data/.DS_Store +0 -0
- package/src/data/Model.js +14 -8
- package/src/driver/.DS_Store +0 -0
- package/src/driver/MongoDriver.js +2 -2
- package/src/graphql/.DS_Store +0 -0
- package/src/graphql/ast/.DS_Store +0 -0
- package/src/graphql/ast/Node.js +2 -2
- package/src/graphql/ast/Schema.js +1 -1
- package/src/graphql/extension/.DS_Store +0 -0
- package/src/graphql/extension/api.js +0 -2
- package/src/graphql/extension/framework.js +3 -4
- package/src/query/.DS_Store +0 -0
- package/src/query/QueryBuilder.js +4 -4
- package/src/query/QueryResolver.js +1 -3
- package/src/service/.DS_Store +0 -0
- package/src/service/app.service.js +1 -21
- package/src/service/event.service.js +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## v0.10.x
|
|
4
|
+
- Replaced ResultSet -> POJOs
|
|
5
|
+
- Removed all $field methods (auto populated)
|
|
6
|
+
- Removed .toObject()
|
|
7
|
+
- $model $save remove $delete $lookup $cursor $pageInfo
|
|
8
|
+
- Removed embedded API completely
|
|
9
|
+
- Removed Directives
|
|
10
|
+
- embedApi -> no replacement
|
|
11
|
+
- enforce -> use pipeline methods
|
|
12
|
+
- resolve -> use graphql resolvers
|
|
13
|
+
- @value -> use @field.instruct directive
|
|
14
|
+
- Removed Model.tform() -> use Model.shapeObject(shape, data)
|
|
15
|
+
- Removed Transformer + Rule -> use Pipeline
|
|
16
|
+
- Removed many pre-defined rules + transformers
|
|
17
|
+
- Moved "validator" to dev dependency -> isEmail
|
|
18
|
+
- Added QueryBuilder.resolve() terminal command
|
|
19
|
+
- Exported SchemaDecorator -> Schema
|
|
20
|
+
- Removed embedded schema SystemEvents (internal emitter also removed)
|
|
21
|
+
- Removed spread of arguments in QueryBuilder terminal commands (must pass in array)
|
|
22
|
+
- Mutate "merged" instead of "input"
|
|
23
|
+
- Validate "payload"
|
|
24
|
+
|
|
25
|
+
## v0.9.x
|
|
26
|
+
- Subscriptions API
|
|
27
|
+
- postMutation no longer mutates "doc" and adds "result"
|
|
28
|
+
- Added onDelete defer option
|
|
29
|
+
|
|
30
|
+
## v0.8.x
|
|
31
|
+
- Engine 14+
|
|
32
|
+
|
|
33
|
+
## v0.7.x
|
|
34
|
+
- Complete overhaul of Query to Mongo Driver (pagination, sorting, counts, etc)
|
|
35
|
+
- Removed countModel Queries from the API (now available as `count` property on `Connetion` types)
|
|
36
|
+
- Dropped Neo4J (temporarily)
|
|
37
|
+
|
|
38
|
+
## v0.6.x
|
|
39
|
+
- Mongo driver no longer checks for `version` directive
|
|
40
|
+
- Models no longer share a Connection type; removing the need to use `... on Model` for GraphQL queries
|
|
41
|
+
- Added `@field(connection: Boolean)` parameter to specifically indicate fields that should return a Connection type
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderich/autograph",
|
|
3
3
|
"author": "Richard Livolsi (coderich)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"description": "AutoGraph",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"graphql",
|
|
@@ -34,22 +34,19 @@
|
|
|
34
34
|
"dataloader": "^2.0.0",
|
|
35
35
|
"deepmerge": "^4.2.2",
|
|
36
36
|
"fill-range": "^7.0.1",
|
|
37
|
+
"flat": "^5.0.2",
|
|
37
38
|
"glob": "^7.1.6",
|
|
38
39
|
"graphql-fields": "^2.0.3",
|
|
39
40
|
"lodash": "^4.17.21",
|
|
40
|
-
"mongodb": "
|
|
41
|
+
"mongodb": "4.8.1",
|
|
41
42
|
"object-hash": "^2.0.1",
|
|
42
43
|
"picomatch": "^2.1.1"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
|
-
"@coderich/ratchet": "^1.5.
|
|
46
|
+
"@coderich/ratchet": "^1.5.8",
|
|
46
47
|
"@graphql-tools/schema": "^9.0.1",
|
|
47
48
|
"graphql": "^15.5.0",
|
|
48
49
|
"mongodb-memory-server": "^8.7.2",
|
|
49
|
-
"neo4j-driver": "^4.0.0",
|
|
50
|
-
"neodb": "^3.0.0",
|
|
51
|
-
"redis": "^2.8.0",
|
|
52
|
-
"redis-mock": "^0.47.0",
|
|
53
50
|
"validator": "^13.7.0"
|
|
54
51
|
},
|
|
55
52
|
"peerDependencies": {
|
package/src/.DS_Store
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/data/Model.js
CHANGED
|
@@ -135,7 +135,7 @@ module.exports = class extends Model {
|
|
|
135
135
|
return shape;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
shapeObject(shape, obj, query, root) {
|
|
138
|
+
shapeObject(shape, obj, query, root, base) {
|
|
139
139
|
const { serdes, model } = shape;
|
|
140
140
|
const { context, resolver, doc = {}, flags = {} } = query.toObject();
|
|
141
141
|
const { pipeline } = flags;
|
|
@@ -143,8 +143,11 @@ module.exports = class extends Model {
|
|
|
143
143
|
if (!pipeline) return obj;
|
|
144
144
|
// const filters = pipeline === true ? [] : Object.entries(pipeline).map(([k, v]) => (v === false ? k : null)).filter(Boolean);
|
|
145
145
|
|
|
146
|
+
// base is the base model
|
|
147
|
+
base = base || model;
|
|
148
|
+
|
|
146
149
|
return map(obj, (parent) => {
|
|
147
|
-
//
|
|
150
|
+
// root is the base data object
|
|
148
151
|
root = root || parent;
|
|
149
152
|
|
|
150
153
|
// Lookup helper functions
|
|
@@ -158,7 +161,7 @@ module.exports = class extends Model {
|
|
|
158
161
|
|
|
159
162
|
// Transform value
|
|
160
163
|
const transformedValue = transformers.reduce((value, t) => {
|
|
161
|
-
const v = t({ model, field, path, docPath, rootPath, parentPath, startValue, value, resolver, context });
|
|
164
|
+
const v = t({ base, model, field, path, docPath, rootPath, parentPath, startValue, value, resolver, context });
|
|
162
165
|
return v === undefined ? value : v;
|
|
163
166
|
}, startValue);
|
|
164
167
|
|
|
@@ -167,22 +170,25 @@ module.exports = class extends Model {
|
|
|
167
170
|
if (!instructed && subShape && typeof transformedValue !== 'object') return prev;
|
|
168
171
|
|
|
169
172
|
// Rename key & assign value
|
|
170
|
-
prev[to] = (!subShape || transformedValue == null) ? transformedValue : this.shapeObject(subShape, transformedValue, query, root);
|
|
173
|
+
prev[to] = (!subShape || transformedValue == null) ? transformedValue : this.shapeObject(subShape, transformedValue, query, root, base);
|
|
171
174
|
|
|
172
175
|
return prev;
|
|
173
176
|
}, {});
|
|
174
177
|
});
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
validateObject(shape, obj, query, root, silent = false) {
|
|
180
|
+
validateObject(shape, obj, query, root, base, silent = false) {
|
|
178
181
|
const { model } = shape;
|
|
179
182
|
const { context, resolver, doc = {}, flags = {} } = query.toObject();
|
|
180
183
|
const { validate = true } = flags;
|
|
181
184
|
|
|
182
185
|
if (!validate) return Promise.resolve();
|
|
183
186
|
|
|
187
|
+
// base is the base model
|
|
188
|
+
base = base || model;
|
|
189
|
+
|
|
184
190
|
return mapPromise(obj, (parent) => {
|
|
185
|
-
//
|
|
191
|
+
// root is the base data object
|
|
186
192
|
root = root || parent;
|
|
187
193
|
|
|
188
194
|
// Lookup helper functions
|
|
@@ -195,10 +201,10 @@ module.exports = class extends Model {
|
|
|
195
201
|
|
|
196
202
|
return Promise.all(validators.map((v) => {
|
|
197
203
|
return new Promise((resolve, reject) => {
|
|
198
|
-
return Promise.resolve(v({ model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context })).then(resolve).catch(reject);
|
|
204
|
+
return Promise.resolve(v({ base, model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context })).then(resolve).catch(reject);
|
|
199
205
|
});
|
|
200
206
|
})).then(() => {
|
|
201
|
-
return subShape ? this.validateObject(subShape, value, query, root, true) : Promise.resolve();
|
|
207
|
+
return subShape ? this.validateObject(subShape, value, query, root, base, true) : Promise.resolve();
|
|
202
208
|
});
|
|
203
209
|
}));
|
|
204
210
|
}).then(() => {
|
|
Binary file
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const Util = require('util');
|
|
2
|
+
const Flat = require('flat');
|
|
2
3
|
const { get } = require('lodash');
|
|
3
4
|
const { MongoClient, ObjectId } = require('mongodb');
|
|
4
5
|
const { map, ensureArray, proxyDeep, toKeyObj, globToRegex, proxyPromise, isScalarDataType, promiseRetry } = require('../service/app.service');
|
|
@@ -64,12 +65,11 @@ module.exports = class MongoDriver {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
createOne({ model, input, options, flags }) {
|
|
67
|
-
// console.log(JSON.stringify(input, null, 2));
|
|
68
68
|
return this.query(model, 'insertOne', input, options, flags).then(result => Object.assign(input, { id: result.insertedId }));
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
updateOne({ model, where, $doc, options, flags }) {
|
|
72
|
-
const $update = { $set: $doc };
|
|
72
|
+
const $update = { $set: Flat.flatten($doc, { safe: true }) };
|
|
73
73
|
return this.query(model, 'updateOne', where, $update, options, flags).then(() => $doc);
|
|
74
74
|
}
|
|
75
75
|
|
|
Binary file
|
|
Binary file
|
package/src/graphql/ast/Node.js
CHANGED
|
@@ -117,7 +117,7 @@ module.exports = class Node {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
getVirtualRef() {
|
|
120
|
-
return this.getDirectiveArg('
|
|
120
|
+
return this.getDirectiveArg('join', 'by');
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
getAuthz() {
|
|
@@ -153,7 +153,7 @@ module.exports = class Node {
|
|
|
153
153
|
* Is the field virtual; does it's value come from another model
|
|
154
154
|
*/
|
|
155
155
|
isVirtual() {
|
|
156
|
-
return Boolean(this.getDirectiveArg('
|
|
156
|
+
return Boolean(this.getDirectiveArg('join', 'by'));
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/**
|
|
Binary file
|
|
@@ -107,8 +107,6 @@ module.exports = (schema) => {
|
|
|
107
107
|
`type Query {
|
|
108
108
|
node(id: ID!): Node
|
|
109
109
|
${entityModels.map(model => makeReadAPI(model.getName(), model))}
|
|
110
|
-
${entityModels.map(model => makeReadAPI(`${model.getName()}Create`, model))}
|
|
111
|
-
${entityModels.map(model => makeReadAPI(`${model.getName()}Update`, model))}
|
|
112
110
|
}`,
|
|
113
111
|
|
|
114
112
|
`type Mutation {
|
|
@@ -8,7 +8,6 @@ module.exports = (schema) => {
|
|
|
8
8
|
scalar AutoGraphMixed
|
|
9
9
|
scalar AutoGraphDriver
|
|
10
10
|
scalar AutoGraphDateTime @field(transform: toDate)
|
|
11
|
-
enum AutoGraphSchemaEnum { ${schema.getModels().map(m => m.getName()).join(' ')} }
|
|
12
11
|
enum AutoGraphPipelineEnum { ${Object.keys(Pipeline).join(' ')} }
|
|
13
12
|
enum AutoGraphAuthzEnum { private protected public }
|
|
14
13
|
enum AutoGraphOnDeleteEnum { cascade nullify restrict defer }
|
|
@@ -56,10 +55,10 @@ module.exports = (schema) => {
|
|
|
56
55
|
deserialize: [AutoGraphPipelineEnum!]
|
|
57
56
|
) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
|
|
58
57
|
|
|
59
|
-
directive @
|
|
60
|
-
to: AutoGraphMixed # The MODEL to
|
|
58
|
+
directive @join(
|
|
59
|
+
to: AutoGraphMixed # The MODEL to join to (default's to modelRef)
|
|
61
60
|
by: AutoGraphMixed! # The FIELD to match yourself by
|
|
62
|
-
use: AutoGraphMixed # The VALUE to use (default's to @
|
|
61
|
+
use: AutoGraphMixed # The VALUE to use (default's to @join'd value); useful for many-to-many relationships
|
|
63
62
|
) on FIELD_DEFINITION
|
|
64
63
|
|
|
65
64
|
directive @index(
|
|
Binary file
|
|
@@ -10,7 +10,7 @@ const { unravelObject } = require('../service/app.service');
|
|
|
10
10
|
*/
|
|
11
11
|
module.exports = class QueryBuilder {
|
|
12
12
|
constructor(resolver, model) {
|
|
13
|
-
this.terminated = false; // Prevent accidental re-use of the QueryBuilder
|
|
13
|
+
// this.terminated = false; // Prevent accidental re-use of the QueryBuilder
|
|
14
14
|
this.query = new Query({ model, resolver });
|
|
15
15
|
|
|
16
16
|
// Chainable commands
|
|
@@ -58,10 +58,10 @@ module.exports = class QueryBuilder {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
execute(cmd, args) {
|
|
61
|
-
// Do not allow re-use
|
|
62
|
-
if (this.terminated) return Promise.reject(new Error('This query has already been executed'));
|
|
61
|
+
// // Do not allow re-use
|
|
62
|
+
// if (this.terminated) return Promise.reject(new Error('This query has already been executed'));
|
|
63
|
+
// this.terminated = true;
|
|
63
64
|
|
|
64
|
-
this.terminated = true;
|
|
65
65
|
let method, crud, input, flags = {};
|
|
66
66
|
const { id, where } = this.query.toObject();
|
|
67
67
|
|
|
@@ -3,7 +3,7 @@ const Boom = require('../core/Boom');
|
|
|
3
3
|
const QueryService = require('./QueryService');
|
|
4
4
|
const DataService = require('../data/DataService');
|
|
5
5
|
const { createSystemEvent } = require('../service/event.service');
|
|
6
|
-
const {
|
|
6
|
+
const { mergeDeep, getGQLReturnType } = require('../service/app.service');
|
|
7
7
|
|
|
8
8
|
module.exports = class QueryResolver {
|
|
9
9
|
constructor(query) {
|
|
@@ -81,8 +81,6 @@ module.exports = class QueryResolver {
|
|
|
81
81
|
const docShape = model.getShape('update', 'doc');
|
|
82
82
|
|
|
83
83
|
return this.resolver.match(model).match(match).one({ required: true }).then((doc) => {
|
|
84
|
-
if (objectIntersectionEqual(doc, input)) return doc; // If no changes do not perform query
|
|
85
|
-
|
|
86
84
|
const merged = mergeDeep(doc, input);
|
|
87
85
|
|
|
88
86
|
return createSystemEvent('Mutation', { query: query.doc(doc).merged(merged) }, async () => {
|
|
Binary file
|
|
@@ -130,9 +130,6 @@ exports.castCmp = (type, value) => {
|
|
|
130
130
|
}
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
-
/**
|
|
134
|
-
* Returns true if b is a subset of a
|
|
135
|
-
*/
|
|
136
133
|
exports.objectContaining = (a, b) => {
|
|
137
134
|
if (a === b) return true;
|
|
138
135
|
|
|
@@ -140,7 +137,7 @@ exports.objectContaining = (a, b) => {
|
|
|
140
137
|
return exports.keyPathLeafs(b).every((leaf) => {
|
|
141
138
|
const $a = _.get(a, leaf, { a: 'a' });
|
|
142
139
|
const $b = _.get(b, leaf, { b: 'b' });
|
|
143
|
-
if (Array.isArray($b)) return $b.
|
|
140
|
+
if (Array.isArray($b)) return $b.some(bb => exports.ensureArray($a).some(aa => exports.objectContaining(aa, bb)));
|
|
144
141
|
if (exports.isScalarValue($a) && exports.isScalarValue($b)) return PicoMatch.isMatch(`${$a}`, `${$b}`, { nocase: true });
|
|
145
142
|
return exports.hashObject($a) === exports.hashObject($b);
|
|
146
143
|
});
|
|
@@ -149,23 +146,6 @@ exports.objectContaining = (a, b) => {
|
|
|
149
146
|
return exports.hashObject(a) === exports.hashObject(b);
|
|
150
147
|
};
|
|
151
148
|
|
|
152
|
-
/**
|
|
153
|
-
* Returns true if (b intersection a) are equal
|
|
154
|
-
*/
|
|
155
|
-
exports.objectIntersectionEqual = (a, b) => {
|
|
156
|
-
if (a === b) return true;
|
|
157
|
-
|
|
158
|
-
if (exports.isPlainObject(b)) {
|
|
159
|
-
return exports.keyPathLeafs(b).every((leaf) => {
|
|
160
|
-
const $a = _.get(a, leaf, { a: 'a' });
|
|
161
|
-
const $b = _.get(b, leaf, { b: 'b' });
|
|
162
|
-
return exports.hashObject($a) === exports.hashObject($b);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return exports.hashObject(a) === exports.hashObject(b);
|
|
167
|
-
};
|
|
168
|
-
|
|
169
149
|
/**
|
|
170
150
|
* Transform an object with dot.notation keys into an expanded object.
|
|
171
151
|
* eg. { 'user.name': 'richard' } => { user: { name: 'richard' } }
|