@graphql-mesh/transport-rest 0.0.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/cjs/directives/dictionary.js +19 -0
- package/cjs/directives/discriminator.js +9 -0
- package/cjs/directives/flatten.js +16 -0
- package/cjs/directives/getTypeResolverForAbstractType.js +102 -0
- package/cjs/directives/httpOperation.js +404 -0
- package/cjs/directives/isFileUpload.js +7 -0
- package/cjs/directives/jsonApiFields.js +23 -0
- package/cjs/directives/length.js +26 -0
- package/cjs/directives/link.js +67 -0
- package/cjs/directives/process.js +181 -0
- package/cjs/directives/pubsubOperation.js +31 -0
- package/cjs/directives/regexp.js +24 -0
- package/cjs/directives/resolveDataByUnionInputType.js +39 -0
- package/cjs/directives/resolveRoot.js +10 -0
- package/cjs/directives/resolveRootField.js +29 -0
- package/cjs/directives/responseMetadata.js +16 -0
- package/cjs/directives/scalars.js +53 -0
- package/cjs/directives/typescriptAnnotations.js +8 -0
- package/cjs/index.js +20 -0
- package/cjs/package.json +1 -0
- package/esm/directives/dictionary.js +15 -0
- package/esm/directives/discriminator.js +5 -0
- package/esm/directives/flatten.js +12 -0
- package/esm/directives/getTypeResolverForAbstractType.js +98 -0
- package/esm/directives/httpOperation.js +399 -0
- package/esm/directives/isFileUpload.js +3 -0
- package/esm/directives/jsonApiFields.js +18 -0
- package/esm/directives/length.js +22 -0
- package/esm/directives/link.js +63 -0
- package/esm/directives/process.js +177 -0
- package/esm/directives/pubsubOperation.js +27 -0
- package/esm/directives/regexp.js +20 -0
- package/esm/directives/resolveDataByUnionInputType.js +35 -0
- package/esm/directives/resolveRoot.js +6 -0
- package/esm/directives/resolveRootField.js +25 -0
- package/esm/directives/responseMetadata.js +12 -0
- package/esm/directives/scalars.js +48 -0
- package/esm/directives/typescriptAnnotations.js +4 -0
- package/esm/index.js +15 -0
- package/package.json +56 -0
- package/typings/directives/dictionary.d.cts +2 -0
- package/typings/directives/dictionary.d.ts +2 -0
- package/typings/directives/discriminator.d.cts +5 -0
- package/typings/directives/discriminator.d.ts +5 -0
- package/typings/directives/flatten.d.cts +2 -0
- package/typings/directives/flatten.d.ts +2 -0
- package/typings/directives/getTypeResolverForAbstractType.d.cts +7 -0
- package/typings/directives/getTypeResolverForAbstractType.d.ts +7 -0
- package/typings/directives/httpOperation.d.cts +28 -0
- package/typings/directives/httpOperation.d.ts +28 -0
- package/typings/directives/isFileUpload.d.cts +4 -0
- package/typings/directives/isFileUpload.d.ts +4 -0
- package/typings/directives/jsonApiFields.d.cts +2 -0
- package/typings/directives/jsonApiFields.d.ts +2 -0
- package/typings/directives/length.d.cts +5 -0
- package/typings/directives/length.d.ts +5 -0
- package/typings/directives/link.d.cts +2 -0
- package/typings/directives/link.d.ts +2 -0
- package/typings/directives/process.d.cts +12 -0
- package/typings/directives/process.d.ts +12 -0
- package/typings/directives/pubsubOperation.d.cts +10 -0
- package/typings/directives/pubsubOperation.d.ts +10 -0
- package/typings/directives/regexp.d.cts +2 -0
- package/typings/directives/regexp.d.ts +2 -0
- package/typings/directives/resolveDataByUnionInputType.d.cts +2 -0
- package/typings/directives/resolveDataByUnionInputType.d.ts +2 -0
- package/typings/directives/resolveRoot.d.cts +2 -0
- package/typings/directives/resolveRoot.d.ts +2 -0
- package/typings/directives/resolveRootField.d.cts +2 -0
- package/typings/directives/resolveRootField.d.ts +2 -0
- package/typings/directives/responseMetadata.d.cts +2 -0
- package/typings/directives/responseMetadata.d.ts +2 -0
- package/typings/directives/scalars.d.cts +3 -0
- package/typings/directives/scalars.d.ts +3 -0
- package/typings/directives/typescriptAnnotations.d.cts +2 -0
- package/typings/directives/typescriptAnnotations.d.ts +2 -0
- package/typings/index.d.cts +7 -0
- package/typings/index.d.ts +7 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { dset } from 'dset';
|
|
2
|
+
import { getNamedType, isListType, isNonNullType, isScalarType, isUnionType, } from 'graphql';
|
|
3
|
+
import { parse as qsParse, stringify as qsStringify } from 'qs';
|
|
4
|
+
import urlJoin from 'url-join';
|
|
5
|
+
import { process } from '@graphql-mesh/cross-helpers';
|
|
6
|
+
import { stringInterpolator } from '@graphql-mesh/string-interpolation';
|
|
7
|
+
import { DefaultLogger, getHeadersObj } from '@graphql-mesh/utils';
|
|
8
|
+
import { createGraphQLError, memoize1 } from '@graphql-tools/utils';
|
|
9
|
+
import { Blob, File, FormData } from '@whatwg-node/fetch';
|
|
10
|
+
import { isFileUpload } from './isFileUpload.js';
|
|
11
|
+
import { getJsonApiFieldsQuery } from './jsonApiFields.js';
|
|
12
|
+
import { resolveDataByUnionInputType } from './resolveDataByUnionInputType.js';
|
|
13
|
+
const isListTypeOrNonNullListType = memoize1(function isListTypeOrNonNullListType(type) {
|
|
14
|
+
if (isNonNullType(type)) {
|
|
15
|
+
return isListType(type.ofType);
|
|
16
|
+
}
|
|
17
|
+
return isListType(type);
|
|
18
|
+
});
|
|
19
|
+
const defaultQsOptions = {
|
|
20
|
+
indices: false,
|
|
21
|
+
};
|
|
22
|
+
export function addHTTPRootFieldResolver(schema, field, globalLogger = new DefaultLogger('HTTP'), globalFetch, { path, operationSpecificHeaders, httpMethod, isBinary, requestBaseBody, queryParamArgMap, queryStringOptionsByParam, jsonApiFields, }, { sourceName, endpoint, timeout, operationHeaders: globalOperationHeaders, queryStringOptions: globalQueryStringOptions = {}, queryParams: globalQueryParams, }) {
|
|
23
|
+
globalQueryStringOptions = {
|
|
24
|
+
...defaultQsOptions,
|
|
25
|
+
...globalQueryStringOptions,
|
|
26
|
+
};
|
|
27
|
+
const returnNamedGraphQLType = getNamedType(field.type);
|
|
28
|
+
field.resolve = async (root, args, context, info) => {
|
|
29
|
+
if (jsonApiFields) {
|
|
30
|
+
args.fields = undefined;
|
|
31
|
+
}
|
|
32
|
+
const logger = context?.logger || globalLogger;
|
|
33
|
+
const operationLogger = logger.child(`${info.parentType.name}.${info.fieldName}`);
|
|
34
|
+
operationLogger.debug(`=> Resolving`);
|
|
35
|
+
const interpolationData = { root, args, context, env: process.env };
|
|
36
|
+
const interpolatedBaseUrl = stringInterpolator.parse(endpoint, interpolationData);
|
|
37
|
+
const interpolatedPath = stringInterpolator.parse(path, interpolationData);
|
|
38
|
+
let fullPath = urlJoin(interpolatedBaseUrl, interpolatedPath);
|
|
39
|
+
const headers = {};
|
|
40
|
+
for (const headerName in globalOperationHeaders) {
|
|
41
|
+
const nonInterpolatedValue = globalOperationHeaders[headerName];
|
|
42
|
+
const interpolatedValue = stringInterpolator.parse(nonInterpolatedValue, interpolationData);
|
|
43
|
+
if (interpolatedValue) {
|
|
44
|
+
headers[headerName.toLowerCase()] = interpolatedValue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (operationSpecificHeaders) {
|
|
48
|
+
for (const headerName in operationSpecificHeaders) {
|
|
49
|
+
const nonInterpolatedValue = operationSpecificHeaders[headerName];
|
|
50
|
+
const interpolatedValue = stringInterpolator.parse(nonInterpolatedValue, interpolationData);
|
|
51
|
+
if (interpolatedValue) {
|
|
52
|
+
headers[headerName.toLowerCase()] = interpolatedValue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const requestInit = {
|
|
57
|
+
method: httpMethod,
|
|
58
|
+
headers,
|
|
59
|
+
};
|
|
60
|
+
if (timeout) {
|
|
61
|
+
requestInit.signal = AbortSignal.timeout(timeout);
|
|
62
|
+
}
|
|
63
|
+
// Handle binary data
|
|
64
|
+
if (isBinary) {
|
|
65
|
+
const binaryUpload = await args.input;
|
|
66
|
+
if (isFileUpload(binaryUpload)) {
|
|
67
|
+
const readable = binaryUpload.createReadStream();
|
|
68
|
+
const chunks = [];
|
|
69
|
+
for await (const chunk of readable) {
|
|
70
|
+
for (const byte of chunk) {
|
|
71
|
+
chunks.push(byte);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
requestInit.body = new Uint8Array(chunks);
|
|
75
|
+
const [, contentType] = Object.entries(headers).find(([key]) => key.toLowerCase() === 'content-type') || [];
|
|
76
|
+
if (!contentType) {
|
|
77
|
+
headers['content-type'] = binaryUpload.mimetype;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
requestInit.body = binaryUpload;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
if (requestBaseBody != null) {
|
|
84
|
+
args.input = args.input || {};
|
|
85
|
+
for (const key in requestBaseBody) {
|
|
86
|
+
const configValue = requestBaseBody[key];
|
|
87
|
+
if (typeof configValue === 'string') {
|
|
88
|
+
const value = stringInterpolator.parse(configValue, interpolationData);
|
|
89
|
+
dset(args.input, key, value);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
args.input[key] = configValue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Resolve union input
|
|
97
|
+
const input = (args.input = resolveDataByUnionInputType(args.input, field.args?.find(arg => arg.name === 'input')?.type, schema));
|
|
98
|
+
if (input != null) {
|
|
99
|
+
const [, contentType] = Object.entries(headers).find(([key]) => key.toLowerCase() === 'content-type') || [];
|
|
100
|
+
if (contentType?.startsWith('application/x-www-form-urlencoded')) {
|
|
101
|
+
requestInit.body = qsStringify(input, globalQueryStringOptions);
|
|
102
|
+
}
|
|
103
|
+
else if (contentType?.startsWith('multipart/form-data')) {
|
|
104
|
+
delete headers['content-type'];
|
|
105
|
+
delete headers['Content-Type'];
|
|
106
|
+
const formData = new FormData();
|
|
107
|
+
for (const key in input) {
|
|
108
|
+
const inputValue = input[key];
|
|
109
|
+
if (inputValue != null) {
|
|
110
|
+
let formDataValue;
|
|
111
|
+
if (typeof inputValue === 'object') {
|
|
112
|
+
if (inputValue instanceof File) {
|
|
113
|
+
formDataValue = inputValue;
|
|
114
|
+
}
|
|
115
|
+
else if (inputValue.name && inputValue instanceof Blob) {
|
|
116
|
+
formDataValue = new File([inputValue], inputValue.name, {
|
|
117
|
+
type: inputValue.type,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else if (inputValue.arrayBuffer) {
|
|
121
|
+
const arrayBuffer = await inputValue.arrayBuffer();
|
|
122
|
+
if (inputValue.name) {
|
|
123
|
+
formDataValue = new File([arrayBuffer], inputValue.name, {
|
|
124
|
+
type: inputValue.type,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
formDataValue = new Blob([arrayBuffer], { type: inputValue.type });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
formDataValue = JSON.stringify(inputValue);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
formDataValue = inputValue.toString();
|
|
137
|
+
}
|
|
138
|
+
formData.append(key, formDataValue);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
requestInit.body = formData;
|
|
142
|
+
}
|
|
143
|
+
else if (contentType?.startsWith('text/plain')) {
|
|
144
|
+
requestInit.body = input;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
requestInit.body = JSON.stringify(input);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (globalQueryParams) {
|
|
152
|
+
for (const queryParamName in globalQueryParams) {
|
|
153
|
+
if (queryParamArgMap != null &&
|
|
154
|
+
queryParamName in queryParamArgMap &&
|
|
155
|
+
queryParamArgMap[queryParamName] in args) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const interpolatedQueryParam = stringInterpolator.parse(globalQueryParams[queryParamName].toString(), interpolationData);
|
|
159
|
+
const queryParamsString = qsStringify({
|
|
160
|
+
[queryParamName]: interpolatedQueryParam,
|
|
161
|
+
}, {
|
|
162
|
+
...globalQueryStringOptions,
|
|
163
|
+
...queryStringOptionsByParam?.[queryParamName],
|
|
164
|
+
});
|
|
165
|
+
fullPath += fullPath.includes('?') ? '&' : '?';
|
|
166
|
+
fullPath += queryParamsString;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (queryParamArgMap) {
|
|
170
|
+
for (const queryParamName in queryParamArgMap) {
|
|
171
|
+
const argName = queryParamArgMap[queryParamName];
|
|
172
|
+
let argValue = args[argName];
|
|
173
|
+
if (argValue != null) {
|
|
174
|
+
// Somehow it doesn't serialize URLs so we need to do it manually.
|
|
175
|
+
if (argValue instanceof URL) {
|
|
176
|
+
argValue = argValue.toString();
|
|
177
|
+
}
|
|
178
|
+
const opts = {
|
|
179
|
+
...globalQueryStringOptions,
|
|
180
|
+
...queryStringOptionsByParam?.[queryParamName],
|
|
181
|
+
};
|
|
182
|
+
let queryParamObj = argValue;
|
|
183
|
+
if (Array.isArray(argValue) || !(typeof argValue === 'object' && opts.destructObject)) {
|
|
184
|
+
queryParamObj = {
|
|
185
|
+
[queryParamName]: argValue,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
queryParamObj = resolveDataByUnionInputType(queryParamObj, field.args?.find(arg => arg.name === argName)?.type, schema);
|
|
190
|
+
}
|
|
191
|
+
const queryParamsString = qsStringify(queryParamObj, opts);
|
|
192
|
+
fullPath += fullPath.includes('?') ? '&' : '?';
|
|
193
|
+
fullPath += queryParamsString;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (jsonApiFields) {
|
|
198
|
+
fullPath += fullPath.includes('?') ? '&' : '?';
|
|
199
|
+
fullPath += `fields=${getJsonApiFieldsQuery(info)}`;
|
|
200
|
+
}
|
|
201
|
+
operationLogger.debug(`=> Fetching `, fullPath, `=>`, requestInit);
|
|
202
|
+
const fetch = context?.fetch || globalFetch;
|
|
203
|
+
if (!fetch) {
|
|
204
|
+
return createGraphQLError(`You should have fetch defined in either the config or the context!`, {
|
|
205
|
+
extensions: {
|
|
206
|
+
request: {
|
|
207
|
+
url: fullPath,
|
|
208
|
+
method: httpMethod,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// Trick to pass `sourceName` to the `fetch` function for tracing
|
|
214
|
+
const response = await fetch(fullPath, requestInit, context, {
|
|
215
|
+
...info,
|
|
216
|
+
sourceName,
|
|
217
|
+
});
|
|
218
|
+
// If return type is a file
|
|
219
|
+
if (returnNamedGraphQLType.name === 'File') {
|
|
220
|
+
return response.blob();
|
|
221
|
+
}
|
|
222
|
+
const responseText = await response.text();
|
|
223
|
+
operationLogger.debug(`=> Received`, {
|
|
224
|
+
headers: response.headers,
|
|
225
|
+
text: responseText,
|
|
226
|
+
});
|
|
227
|
+
let responseJson;
|
|
228
|
+
try {
|
|
229
|
+
responseJson = JSON.parse(responseText);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
// The result might be defined as scalar
|
|
233
|
+
if (isScalarType(returnNamedGraphQLType)) {
|
|
234
|
+
operationLogger.debug(` => Return type is not a JSON so returning ${responseText}`);
|
|
235
|
+
return responseText;
|
|
236
|
+
}
|
|
237
|
+
else if (response.status === 204 || (response.status === 200 && responseText === '')) {
|
|
238
|
+
responseJson = {};
|
|
239
|
+
}
|
|
240
|
+
else if (response.status.toString().startsWith('2')) {
|
|
241
|
+
logger.debug(`Unexpected response in ${field.name};\n\t${responseText}`);
|
|
242
|
+
return createGraphQLError(`Unexpected response in ${field.name}`, {
|
|
243
|
+
extensions: {
|
|
244
|
+
http: {
|
|
245
|
+
status: response.status,
|
|
246
|
+
statusText: response.statusText,
|
|
247
|
+
headers: getHeadersObj(response.headers),
|
|
248
|
+
},
|
|
249
|
+
request: {
|
|
250
|
+
url: fullPath,
|
|
251
|
+
method: httpMethod,
|
|
252
|
+
},
|
|
253
|
+
responseText,
|
|
254
|
+
originalError: {
|
|
255
|
+
message: error.message,
|
|
256
|
+
stack: error.stack,
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
return createGraphQLError(`HTTP Error: ${response.status}, Could not invoke operation ${httpMethod} ${path}`, {
|
|
263
|
+
extensions: {
|
|
264
|
+
request: {
|
|
265
|
+
url: fullPath,
|
|
266
|
+
method: httpMethod,
|
|
267
|
+
},
|
|
268
|
+
responseText,
|
|
269
|
+
responseStatus: response.status,
|
|
270
|
+
responseStatusText: response.statusText,
|
|
271
|
+
responseHeaders: getHeadersObj(response.headers),
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (!response.status.toString().startsWith('2')) {
|
|
277
|
+
if (!isUnionType(returnNamedGraphQLType)) {
|
|
278
|
+
return createGraphQLError(`HTTP Error: ${response.status}, Could not invoke operation ${httpMethod} ${path}`, {
|
|
279
|
+
extensions: {
|
|
280
|
+
http: {
|
|
281
|
+
status: response.status,
|
|
282
|
+
statusText: response.statusText,
|
|
283
|
+
headers: getHeadersObj(response.headers),
|
|
284
|
+
},
|
|
285
|
+
request: {
|
|
286
|
+
url: fullPath,
|
|
287
|
+
method: httpMethod,
|
|
288
|
+
},
|
|
289
|
+
responseJson,
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
operationLogger.debug(`Returning `, responseJson);
|
|
295
|
+
// Sometimes API returns an array but the return type is not an array
|
|
296
|
+
const isListReturnType = isListTypeOrNonNullListType(field.type);
|
|
297
|
+
const isArrayResponse = Array.isArray(responseJson);
|
|
298
|
+
if (isListReturnType && !isArrayResponse) {
|
|
299
|
+
operationLogger.debug(`Response is not array but return type is list. Normalizing the response`);
|
|
300
|
+
responseJson = [responseJson];
|
|
301
|
+
}
|
|
302
|
+
if (!isListReturnType && isArrayResponse) {
|
|
303
|
+
operationLogger.debug(`Response is array but return type is not list. Normalizing the response`);
|
|
304
|
+
responseJson = responseJson[0];
|
|
305
|
+
}
|
|
306
|
+
const addResponseMetadata = (obj) => {
|
|
307
|
+
if (typeof obj !== 'object') {
|
|
308
|
+
return obj;
|
|
309
|
+
}
|
|
310
|
+
Object.defineProperties(obj, {
|
|
311
|
+
$field: {
|
|
312
|
+
get() {
|
|
313
|
+
return field.name;
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
$url: {
|
|
317
|
+
get() {
|
|
318
|
+
return fullPath.split('?')[0];
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
$method: {
|
|
322
|
+
get() {
|
|
323
|
+
return httpMethod;
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
$statusCode: {
|
|
327
|
+
get() {
|
|
328
|
+
return response.status;
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
$statusText: {
|
|
332
|
+
get() {
|
|
333
|
+
return response.statusText;
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
$headers: {
|
|
337
|
+
get() {
|
|
338
|
+
return requestInit.headers;
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
$request: {
|
|
342
|
+
get() {
|
|
343
|
+
return new Proxy({}, {
|
|
344
|
+
get(_, requestProp) {
|
|
345
|
+
switch (requestProp) {
|
|
346
|
+
case 'query':
|
|
347
|
+
return qsParse(fullPath.split('?')[1]);
|
|
348
|
+
case 'path':
|
|
349
|
+
return new Proxy(args, {
|
|
350
|
+
get(_, prop) {
|
|
351
|
+
return args[prop] || args.input?.[prop] || obj?.[prop];
|
|
352
|
+
},
|
|
353
|
+
has(_, prop) {
|
|
354
|
+
return prop in args || (args.input && prop in args.input) || obj?.[prop];
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
case 'header':
|
|
358
|
+
return requestInit.headers;
|
|
359
|
+
case 'body':
|
|
360
|
+
return requestInit.body;
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
$response: {
|
|
367
|
+
get() {
|
|
368
|
+
return new Proxy({}, {
|
|
369
|
+
get(_, responseProp) {
|
|
370
|
+
switch (responseProp) {
|
|
371
|
+
case 'header':
|
|
372
|
+
return getHeadersObj(response.headers);
|
|
373
|
+
case 'body':
|
|
374
|
+
return obj;
|
|
375
|
+
case 'query':
|
|
376
|
+
return qsParse(fullPath.split('?')[1]);
|
|
377
|
+
case 'path':
|
|
378
|
+
return new Proxy(args, {
|
|
379
|
+
get(_, prop) {
|
|
380
|
+
return args[prop] || args.input?.[prop] || obj?.[prop];
|
|
381
|
+
},
|
|
382
|
+
has(_, prop) {
|
|
383
|
+
return prop in args || (args.input && prop in args.input) || obj?.[prop];
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
return obj;
|
|
393
|
+
};
|
|
394
|
+
operationLogger.debug(`Adding response metadata to the response object`);
|
|
395
|
+
return Array.isArray(responseJson)
|
|
396
|
+
? responseJson.map(obj => addResponseMetadata(obj))
|
|
397
|
+
: addResponseMetadata(responseJson);
|
|
398
|
+
};
|
|
399
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import graphqlFields from 'graphql-fields';
|
|
2
|
+
function handleFields(fields) {
|
|
3
|
+
const fieldsStrsArray = [];
|
|
4
|
+
for (const fieldName in fields) {
|
|
5
|
+
const subFields = fields[fieldName];
|
|
6
|
+
const subFieldsStr = handleFields(subFields);
|
|
7
|
+
let fieldStr = fieldName;
|
|
8
|
+
if (subFieldsStr) {
|
|
9
|
+
fieldStr += `(${subFieldsStr})`;
|
|
10
|
+
}
|
|
11
|
+
fieldsStrsArray.push(fieldStr);
|
|
12
|
+
}
|
|
13
|
+
return fieldsStrsArray.join(',');
|
|
14
|
+
}
|
|
15
|
+
export function getJsonApiFieldsQuery(info) {
|
|
16
|
+
const fields = graphqlFields(info);
|
|
17
|
+
return handleFields(fields);
|
|
18
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function processLengthAnnotations(scalar, { min: minLength, max: maxLength, }) {
|
|
2
|
+
function coerceString(value) {
|
|
3
|
+
if (value != null) {
|
|
4
|
+
const vStr = value.toString();
|
|
5
|
+
if (typeof minLength !== 'undefined' && vStr.length < minLength) {
|
|
6
|
+
throw new Error(`${scalar.name} cannot be less than ${minLength} but given ${vStr}`);
|
|
7
|
+
}
|
|
8
|
+
if (typeof maxLength !== 'undefined' && vStr.length > maxLength) {
|
|
9
|
+
throw new Error(`${scalar.name} cannot be more than ${maxLength} but given ${vStr}`);
|
|
10
|
+
}
|
|
11
|
+
return vStr;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
scalar.serialize = coerceString;
|
|
15
|
+
scalar.parseValue = coerceString;
|
|
16
|
+
scalar.parseLiteral = ast => {
|
|
17
|
+
if ('value' in ast) {
|
|
18
|
+
return coerceString(ast.value);
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { dset } from 'dset';
|
|
2
|
+
import { stringInterpolator } from '@graphql-mesh/string-interpolation';
|
|
3
|
+
import { getDirective } from '@graphql-tools/utils';
|
|
4
|
+
export function processLinkFieldAnnotations(field, defaultRootTypeName, defaultFieldName) {
|
|
5
|
+
field.resolve = function linkDirectiveHandler(root, args, context, info) {
|
|
6
|
+
const linkResolverMap = findLinkResolverMap({
|
|
7
|
+
schema: info.schema,
|
|
8
|
+
defaultRootTypeName,
|
|
9
|
+
defaultFieldName,
|
|
10
|
+
parentFieldName: root.$field,
|
|
11
|
+
operationType: info.operation.operation,
|
|
12
|
+
});
|
|
13
|
+
const linkResolverOpts = linkResolverMap[field.name];
|
|
14
|
+
return linkResolver(linkResolverOpts, { root, args, context, info, env: process.env });
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function findLinkResolverMap({ schema, operationType, defaultRootTypeName, defaultFieldName, }) {
|
|
18
|
+
const parentType = schema.getRootType(operationType);
|
|
19
|
+
const parentField = parentType.getFields()[operationType];
|
|
20
|
+
if (parentField) {
|
|
21
|
+
const linkResolverMap = getLinkResolverMap(schema, parentField);
|
|
22
|
+
if (linkResolverMap) {
|
|
23
|
+
return linkResolverMap;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const defaultRootType = schema.getType(defaultRootTypeName);
|
|
27
|
+
if (defaultRootType) {
|
|
28
|
+
const defaultField = defaultRootType.getFields()[defaultFieldName];
|
|
29
|
+
if (defaultField) {
|
|
30
|
+
const linkResolverMap = getLinkResolverMap(schema, defaultField);
|
|
31
|
+
if (linkResolverMap) {
|
|
32
|
+
return linkResolverMap;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getLinkResolverMap(schema, field) {
|
|
38
|
+
const parentFieldLinkResolverDirectives = getDirective(schema, field, 'linkResolver');
|
|
39
|
+
if (parentFieldLinkResolverDirectives?.length) {
|
|
40
|
+
const linkResolverMap = parentFieldLinkResolverDirectives[0].linkResolverMap;
|
|
41
|
+
if (linkResolverMap) {
|
|
42
|
+
return linkResolverMap;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function linkResolver({ linkObjArgs, targetTypeName, targetFieldName }, { root, args, context, info, env }) {
|
|
47
|
+
for (const argKey in linkObjArgs) {
|
|
48
|
+
const argInterpolation = linkObjArgs[argKey];
|
|
49
|
+
const actualValue = typeof argInterpolation === 'string'
|
|
50
|
+
? stringInterpolator.parse(argInterpolation, {
|
|
51
|
+
root,
|
|
52
|
+
args,
|
|
53
|
+
context,
|
|
54
|
+
info,
|
|
55
|
+
env,
|
|
56
|
+
})
|
|
57
|
+
: argInterpolation;
|
|
58
|
+
dset(args, argKey, actualValue);
|
|
59
|
+
}
|
|
60
|
+
const type = info.schema.getType(targetTypeName);
|
|
61
|
+
const field = type.getFields()[targetFieldName];
|
|
62
|
+
return field.resolve(root, args, context, info);
|
|
63
|
+
}
|