-
Notifications
You must be signed in to change notification settings - Fork 2
/
execute.js
123 lines (114 loc) · 3.09 KB
/
execute.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import isArray from './is-array.js';
import isFunction from './is-function.js';
import isObject from './is-object.js';
import PaveError from './pave-error.js';
import validateArgs from './validate-args.js';
const execute = async ({
context,
obj,
path = [],
query,
schema,
type,
value
}) => {
const fail = (code, extra) => {
throw new PaveError(code, {
context,
obj,
path,
query,
schema,
type,
value,
...extra
});
};
let isNullable = true;
do {
if (isFunction(value)) value = await value();
else if (type == null) {
if (value != null) return value;
if (!isNullable) fail('expectedNonNull');
return null;
} else if (!isObject(type)) {
if (schema[type]) {
obj = null;
type = schema[type];
} else fail('unknownType');
} else if (value === undefined && type.defaultValue !== undefined) {
value = type.defaultValue;
} else if (type.nonNull) {
if (value == null) fail('expectedNonNull');
type = type.nonNull;
isNullable = false;
} else if (obj == null && value == null) return null;
else if (type.arrayOf) {
if (!isArray(value)) fail('expectedArray');
return Promise.all(
value.map((value, i) =>
execute({
context,
obj,
path: path[i],
query,
schema,
type: type.arrayOf,
value
})
)
);
} else if (type.oneOf) type = type.resolveType(value);
else if (type.fields) {
const merged = {};
const onKey = `_on${type.name}`;
for (const key in query) {
if (key === onKey) Object.assign(merged, query[key]);
else if (!key.startsWith('_on')) merged[key] = query[key];
}
return Object.fromEntries(
await Promise.all(
Object.entries(merged).map(async ([alias, query]) => {
const { _field, ..._query } = query;
const field = _field || alias;
let _type = type.fields[field];
if (!_type) {
if (field === '_type') _type = { resolve: type.name };
else fail('unknownField', { alias, field });
}
return [
alias,
await execute({
context,
obj: value,
path: path.concat(alias),
query: _query,
schema,
type: _type,
value: value[field]
})
];
})
)
);
} else {
const { _args, ..._query } = query;
let _value = 'resolve' in type ? type.resolve : value;
if (isFunction(_value)) {
const args = validateArgs({
context,
path: path.concat('_args'),
schema,
type,
value: _args
});
_value = await _value({ args, context, obj, query, value });
}
if (type.typeArgs) _query._args = type.typeArgs;
query = _query;
type = type.type;
value = _value;
}
} while (true);
};
export default execute;