UNPKG

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