1 | import { ApolloLink, Observable, concat, toPromise, execute } from 'apollo-link';
|
2 | import { createUploadLink, isExtractableFile, formDataAppendFile } from 'apollo-upload-client';
|
3 | import FormData from 'form-data';
|
4 | import { fetch } from 'cross-fetch';
|
5 | import { observableToAsyncIterable } from '@graphql-tools/utils';
|
6 | import { GraphQLScalarType, GraphQLError } from 'graphql';
|
7 | import isPromise from 'is-promise';
|
8 |
|
9 | function getFinalPromise(object) {
|
10 | return Promise.resolve(object).then(resolvedObject => {
|
11 | if (resolvedObject == null) {
|
12 | return resolvedObject;
|
13 | }
|
14 | if (Array.isArray(resolvedObject)) {
|
15 | return Promise.all(resolvedObject.map(o => getFinalPromise(o)));
|
16 | }
|
17 | else if (typeof resolvedObject === 'object') {
|
18 | const keys = Object.keys(resolvedObject);
|
19 | return Promise.all(keys.map(key => getFinalPromise(resolvedObject[key]))).then(awaitedValues => {
|
20 | for (let i = 0; i < keys.length; i++) {
|
21 | resolvedObject[keys[i]] = awaitedValues[i];
|
22 | }
|
23 | return resolvedObject;
|
24 | });
|
25 | }
|
26 | return resolvedObject;
|
27 | });
|
28 | }
|
29 | class AwaitVariablesLink extends ApolloLink {
|
30 | request(operation, forward) {
|
31 | return new Observable(observer => {
|
32 | let subscription;
|
33 | getFinalPromise(operation.variables)
|
34 | .then(resolvedVariables => {
|
35 | operation.variables = resolvedVariables;
|
36 | subscription = forward(operation).subscribe({
|
37 | next: observer.next.bind(observer),
|
38 | error: observer.error.bind(observer),
|
39 | complete: observer.complete.bind(observer),
|
40 | });
|
41 | })
|
42 | .catch(observer.error.bind(observer));
|
43 | return () => {
|
44 | if (subscription != null) {
|
45 | subscription.unsubscribe();
|
46 | }
|
47 | };
|
48 | });
|
49 | }
|
50 | }
|
51 |
|
52 | class FormDataWithStreamSupport extends FormData {
|
53 | constructor(options) {
|
54 | super(options);
|
55 | this.hasUnknowableLength = false;
|
56 | }
|
57 | append(key, value, optionsOrFilename = {}) {
|
58 |
|
59 | const options = typeof optionsOrFilename === 'string' ? { filename: optionsOrFilename } : optionsOrFilename;
|
60 |
|
61 | if (!options.knownLength &&
|
62 | !Buffer.isBuffer(value) &&
|
63 | typeof value !== 'string' &&
|
64 | !value.path &&
|
65 | !(value.readable && 'httpVersion' in value)) {
|
66 | this.hasUnknowableLength = true;
|
67 | }
|
68 | super.append(key, value, options);
|
69 | }
|
70 | getLength(callback) {
|
71 | if (this.hasUnknowableLength) {
|
72 | return null;
|
73 | }
|
74 | return super.getLength(callback);
|
75 | }
|
76 | getLengthSync() {
|
77 | if (this.hasUnknowableLength) {
|
78 | return null;
|
79 | }
|
80 |
|
81 | return super.getLengthSync();
|
82 | }
|
83 | }
|
84 | const createServerHttpLink = (options) => concat(new AwaitVariablesLink(), createUploadLink({
|
85 | ...options,
|
86 | fetch,
|
87 | FormData: FormDataWithStreamSupport,
|
88 | isExtractableFile: (value) => isExtractableFile(value) || (value === null || value === void 0 ? void 0 : value.createReadStream),
|
89 | formDataAppendFile: (form, index, file) => {
|
90 | if (file.createReadStream != null) {
|
91 | form.append(index, file.createReadStream(), {
|
92 | filename: file.filename,
|
93 | contentType: file.mimetype,
|
94 | });
|
95 | }
|
96 | else {
|
97 | formDataAppendFile(form, index, file);
|
98 | }
|
99 | },
|
100 | }));
|
101 |
|
102 | const linkToExecutor = (link) => ({ document, variables, context, info, }) => toPromise(execute(link, {
|
103 | query: document,
|
104 | variables,
|
105 | context: {
|
106 | graphqlContext: context,
|
107 | graphqlResolveInfo: info,
|
108 | clientAwareness: {},
|
109 | },
|
110 | }));
|
111 |
|
112 | const linkToSubscriber = (link) => async ({ document, variables, context, info, }) => observableToAsyncIterable(execute(link, {
|
113 | query: document,
|
114 | variables,
|
115 | context: {
|
116 | graphqlContext: context,
|
117 | graphqlResolveInfo: info,
|
118 | clientAwareness: {},
|
119 | },
|
120 | }));
|
121 |
|
122 | const GraphQLUpload = new GraphQLScalarType({
|
123 | name: 'Upload',
|
124 | description: 'The `Upload` scalar type represents a file upload.',
|
125 | parseValue: value => {
|
126 | if (value != null && isPromise(value.promise)) {
|
127 |
|
128 | return value.promise;
|
129 | }
|
130 | else if (isPromise(value)) {
|
131 |
|
132 | return value;
|
133 | }
|
134 | throw new GraphQLError('Upload value invalid.');
|
135 | },
|
136 |
|
137 | serialize: value => value,
|
138 | parseLiteral: ast => {
|
139 | throw new GraphQLError('Upload literal unsupported.', ast);
|
140 | },
|
141 | });
|
142 |
|
143 | export { AwaitVariablesLink, GraphQLUpload, createServerHttpLink, linkToExecutor, linkToSubscriber };
|
144 |
|