UNPKG

13.5 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { Injectable } from '@angular/core';
3import { ApolloLink, Observable as Observable$1 } from '@apollo/client/core';
4import { print } from 'graphql';
5import * as i1 from '@angular/common/http';
6import { HttpHeaders } from '@angular/common/http';
7import { Observable } from 'rxjs';
8import { BatchLink } from '@apollo/client/link/batch';
9
10const fetch = (req, httpClient, extractFiles) => {
11 const shouldUseBody = ['POST', 'PUT', 'PATCH'].indexOf(req.method.toUpperCase()) !== -1;
12 const shouldStringify = (param) => ['variables', 'extensions'].indexOf(param.toLowerCase()) !== -1;
13 const isBatching = req.body.length;
14 let shouldUseMultipart = req.options && req.options.useMultipart;
15 let multipartInfo;
16 if (shouldUseMultipart) {
17 if (isBatching) {
18 return new Observable((observer) => observer.error(new Error('File upload is not available when combined with Batching')));
19 }
20 if (!shouldUseBody) {
21 return new Observable((observer) => observer.error(new Error('File upload is not available when GET is used')));
22 }
23 if (!extractFiles) {
24 return new Observable((observer) => observer.error(new Error(`To use File upload you need to pass "extractFiles" function from "extract-files" library to HttpLink's options`)));
25 }
26 multipartInfo = extractFiles(req.body);
27 shouldUseMultipart = !!multipartInfo.files.size;
28 }
29 // `body` for some, `params` for others
30 let bodyOrParams = {};
31 if (isBatching) {
32 if (!shouldUseBody) {
33 return new Observable((observer) => observer.error(new Error('Batching is not available for GET requests')));
34 }
35 bodyOrParams = {
36 body: req.body,
37 };
38 }
39 else {
40 const body = shouldUseMultipart ? multipartInfo.clone : req.body;
41 if (shouldUseBody) {
42 bodyOrParams = {
43 body,
44 };
45 }
46 else {
47 const params = Object.keys(req.body).reduce((obj, param) => {
48 const value = req.body[param];
49 obj[param] = shouldStringify(param) ? JSON.stringify(value) : value;
50 return obj;
51 }, {});
52 bodyOrParams = { params: params };
53 }
54 }
55 if (shouldUseMultipart && shouldUseBody) {
56 const form = new FormData();
57 form.append('operations', JSON.stringify(bodyOrParams.body));
58 const map = {};
59 const files = multipartInfo.files;
60 let i = 0;
61 files.forEach((paths) => {
62 map[++i] = paths;
63 });
64 form.append('map', JSON.stringify(map));
65 i = 0;
66 files.forEach((_, file) => {
67 form.append(++i + '', file, file.name);
68 });
69 bodyOrParams.body = form;
70 }
71 // create a request
72 return httpClient.request(req.method, req.url, {
73 observe: 'response',
74 responseType: 'json',
75 reportProgress: false,
76 ...bodyOrParams,
77 ...req.options,
78 });
79};
80const mergeHeaders = (source, destination) => {
81 if (source && destination) {
82 const merged = destination
83 .keys()
84 .reduce((headers, name) => headers.set(name, destination.getAll(name)), source);
85 return merged;
86 }
87 return destination || source;
88};
89function prioritize(...values) {
90 const picked = values.find((val) => typeof val !== 'undefined');
91 if (typeof picked === 'undefined') {
92 return values[values.length - 1];
93 }
94 return picked;
95}
96function createHeadersWithClientAwereness(context) {
97 // `apollographql-client-*` headers are automatically set if a
98 // `clientAwareness` object is found in the context. These headers are
99 // set first, followed by the rest of the headers pulled from
100 // `context.headers`.
101 let headers = context.headers && context.headers instanceof HttpHeaders
102 ? context.headers
103 : new HttpHeaders(context.headers);
104 if (context.clientAwareness) {
105 const { name, version } = context.clientAwareness;
106 // If desired, `apollographql-client-*` headers set by
107 // the `clientAwareness` object can be overridden by
108 // `apollographql-client-*` headers set in `context.headers`.
109 if (name && !headers.has('apollographql-client-name')) {
110 headers = headers.set('apollographql-client-name', name);
111 }
112 if (version && !headers.has('apollographql-client-version')) {
113 headers = headers.set('apollographql-client-version', version);
114 }
115 }
116 return headers;
117}
118
119// XXX find a better name for it
120class HttpLinkHandler extends ApolloLink {
121 constructor(httpClient, options) {
122 super();
123 this.httpClient = httpClient;
124 this.options = options;
125 this.print = print;
126 if (this.options.operationPrinter) {
127 this.print = this.options.operationPrinter;
128 }
129 this.requester = (operation) => new Observable$1((observer) => {
130 const context = operation.getContext();
131 // decides which value to pick, Context, Options or to just use the default
132 const pick = (key, init) => {
133 return prioritize(context[key], this.options[key], init);
134 };
135 let method = pick('method', 'POST');
136 const includeQuery = pick('includeQuery', true);
137 const includeExtensions = pick('includeExtensions', false);
138 const url = pick('uri', 'graphql');
139 const withCredentials = pick('withCredentials');
140 const useMultipart = pick('useMultipart');
141 const useGETForQueries = this.options.useGETForQueries === true;
142 const isQuery = operation.query.definitions.some((def) => def.kind === 'OperationDefinition' && def.operation === 'query');
143 if (useGETForQueries && isQuery) {
144 method = 'GET';
145 }
146 const req = {
147 method,
148 url: typeof url === 'function' ? url(operation) : url,
149 body: {
150 operationName: operation.operationName,
151 variables: operation.variables,
152 },
153 options: {
154 withCredentials,
155 useMultipart,
156 headers: this.options.headers,
157 },
158 };
159 if (includeExtensions) {
160 req.body.extensions = operation.extensions;
161 }
162 if (includeQuery) {
163 req.body.query = this.print(operation.query);
164 }
165 const headers = createHeadersWithClientAwereness(context);
166 req.options.headers = mergeHeaders(req.options.headers, headers);
167 const sub = fetch(req, this.httpClient, this.options.extractFiles).subscribe({
168 next: (response) => {
169 operation.setContext({ response });
170 observer.next(response.body);
171 },
172 error: (err) => observer.error(err),
173 complete: () => observer.complete(),
174 });
175 return () => {
176 if (!sub.closed) {
177 sub.unsubscribe();
178 }
179 };
180 });
181 }
182 request(op) {
183 return this.requester(op);
184 }
185}
186class HttpLink {
187 constructor(httpClient) {
188 this.httpClient = httpClient;
189 }
190 create(options) {
191 return new HttpLinkHandler(this.httpClient, options);
192 }
193}
194HttpLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: HttpLink, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
195HttpLink.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: HttpLink, providedIn: 'root' });
196i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: HttpLink, decorators: [{
197 type: Injectable,
198 args: [{
199 providedIn: 'root',
200 }]
201 }], ctorParameters: function () { return [{ type: i1.HttpClient }]; } });
202
203const defaults = {
204 batchInterval: 10,
205 batchMax: 10,
206 uri: 'graphql',
207 method: 'POST',
208};
209class HttpBatchLinkHandler extends ApolloLink {
210 constructor(httpClient, options) {
211 super();
212 this.httpClient = httpClient;
213 this.options = options;
214 this.print = print;
215 this.batchInterval = options.batchInterval || defaults.batchInterval;
216 this.batchMax = options.batchMax || defaults.batchMax;
217 if (this.options.operationPrinter) {
218 this.print = this.options.operationPrinter;
219 }
220 const batchHandler = (operations) => {
221 return new Observable$1((observer) => {
222 const body = this.createBody(operations);
223 const headers = this.createHeaders(operations);
224 const { method, uri, withCredentials } = this.createOptions(operations);
225 if (typeof uri === 'function') {
226 throw new Error(`Option 'uri' is a function, should be a string`);
227 }
228 const req = {
229 method,
230 url: uri,
231 body: body,
232 options: {
233 withCredentials,
234 headers,
235 },
236 };
237 const sub = fetch(req, this.httpClient, () => {
238 throw new Error('File upload is not available when combined with Batching');
239 }).subscribe({
240 next: (result) => observer.next(result.body),
241 error: (err) => observer.error(err),
242 complete: () => observer.complete(),
243 });
244 return () => {
245 if (!sub.closed) {
246 sub.unsubscribe();
247 }
248 };
249 });
250 };
251 const batchKey = options.batchKey ||
252 ((operation) => {
253 return this.createBatchKey(operation);
254 });
255 this.batcher = new BatchLink({
256 batchInterval: this.batchInterval,
257 batchMax: this.batchMax,
258 batchKey,
259 batchHandler,
260 });
261 }
262 createOptions(operations) {
263 const context = operations[0].getContext();
264 return {
265 method: prioritize(context.method, this.options.method, defaults.method),
266 uri: prioritize(context.uri, this.options.uri, defaults.uri),
267 withCredentials: prioritize(context.withCredentials, this.options.withCredentials),
268 };
269 }
270 createBody(operations) {
271 return operations.map((operation) => {
272 const includeExtensions = prioritize(operation.getContext().includeExtensions, this.options.includeExtensions, false);
273 const includeQuery = prioritize(operation.getContext().includeQuery, this.options.includeQuery, true);
274 const body = {
275 operationName: operation.operationName,
276 variables: operation.variables,
277 };
278 if (includeExtensions) {
279 body.extensions = operation.extensions;
280 }
281 if (includeQuery) {
282 body.query = this.print(operation.query);
283 }
284 return body;
285 });
286 }
287 createHeaders(operations) {
288 return operations.reduce((headers, operation) => {
289 return mergeHeaders(headers, operation.getContext().headers);
290 }, createHeadersWithClientAwereness({
291 headers: this.options.headers,
292 clientAwareness: operations[0]?.getContext()?.clientAwareness,
293 }));
294 }
295 createBatchKey(operation) {
296 const context = operation.getContext();
297 if (context.skipBatching) {
298 return Math.random().toString(36).substr(2, 9);
299 }
300 const headers = context.headers &&
301 context.headers.keys().map((k) => context.headers.get(k));
302 const opts = JSON.stringify({
303 includeQuery: context.includeQuery,
304 includeExtensions: context.includeExtensions,
305 headers,
306 });
307 return prioritize(context.uri, this.options.uri) + opts;
308 }
309 request(op) {
310 return this.batcher.request(op);
311 }
312}
313class HttpBatchLink {
314 constructor(httpClient) {
315 this.httpClient = httpClient;
316 }
317 create(options) {
318 return new HttpBatchLinkHandler(this.httpClient, options);
319 }
320}
321HttpBatchLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: HttpBatchLink, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
322HttpBatchLink.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: HttpBatchLink, providedIn: 'root' });
323i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: HttpBatchLink, decorators: [{
324 type: Injectable,
325 args: [{
326 providedIn: 'root',
327 }]
328 }], ctorParameters: function () { return [{ type: i1.HttpClient }]; } });
329
330// http
331
332/**
333 * Generated bundle index. Do not edit.
334 */
335
336export { HttpBatchLink, HttpBatchLinkHandler, HttpLink, HttpLinkHandler };
337//# sourceMappingURL=ngApolloLinkHttp.mjs.map