UNPKG

13.6 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, Object.assign(Object.assign({ observe: 'response', responseType: 'json', reportProgress: false }, bodyOrParams), req.options));
73};
74const mergeHeaders = (source, destination) => {
75 if (source && destination) {
76 const merged = destination
77 .keys()
78 .reduce((headers, name) => headers.set(name, destination.getAll(name)), source);
79 return merged;
80 }
81 return destination || source;
82};
83function prioritize(...values) {
84 const picked = values.find((val) => typeof val !== 'undefined');
85 if (typeof picked === 'undefined') {
86 return values[values.length - 1];
87 }
88 return picked;
89}
90function createHeadersWithClientAwereness(context) {
91 // `apollographql-client-*` headers are automatically set if a
92 // `clientAwareness` object is found in the context. These headers are
93 // set first, followed by the rest of the headers pulled from
94 // `context.headers`.
95 let headers = context.headers && context.headers instanceof HttpHeaders
96 ? context.headers
97 : new HttpHeaders(context.headers);
98 if (context.clientAwareness) {
99 const { name, version } = context.clientAwareness;
100 // If desired, `apollographql-client-*` headers set by
101 // the `clientAwareness` object can be overridden by
102 // `apollographql-client-*` headers set in `context.headers`.
103 if (name && !headers.has('apollographql-client-name')) {
104 headers = headers.set('apollographql-client-name', name);
105 }
106 if (version && !headers.has('apollographql-client-version')) {
107 headers = headers.set('apollographql-client-version', version);
108 }
109 }
110 return headers;
111}
112
113// XXX find a better name for it
114class HttpLinkHandler extends ApolloLink {
115 constructor(httpClient, options) {
116 super();
117 this.httpClient = httpClient;
118 this.options = options;
119 this.print = print;
120 if (this.options.operationPrinter) {
121 this.print = this.options.operationPrinter;
122 }
123 this.requester = (operation) => new Observable$1((observer) => {
124 const context = operation.getContext();
125 // decides which value to pick, Context, Options or to just use the default
126 const pick = (key, init) => {
127 return prioritize(context[key], this.options[key], init);
128 };
129 let method = pick('method', 'POST');
130 const includeQuery = pick('includeQuery', true);
131 const includeExtensions = pick('includeExtensions', false);
132 const url = pick('uri', 'graphql');
133 const withCredentials = pick('withCredentials');
134 const useMultipart = pick('useMultipart');
135 const useGETForQueries = this.options.useGETForQueries === true;
136 const isQuery = operation.query.definitions.some((def) => def.kind === 'OperationDefinition' && def.operation === 'query');
137 if (useGETForQueries && isQuery) {
138 method = 'GET';
139 }
140 const req = {
141 method,
142 url: typeof url === 'function' ? url(operation) : url,
143 body: {
144 operationName: operation.operationName,
145 variables: operation.variables,
146 },
147 options: {
148 withCredentials,
149 useMultipart,
150 headers: this.options.headers,
151 },
152 };
153 if (includeExtensions) {
154 req.body.extensions = operation.extensions;
155 }
156 if (includeQuery) {
157 req.body.query = this.print(operation.query);
158 }
159 const headers = createHeadersWithClientAwereness(context);
160 req.options.headers = mergeHeaders(req.options.headers, headers);
161 const sub = fetch(req, this.httpClient, this.options.extractFiles).subscribe({
162 next: (response) => {
163 operation.setContext({ response });
164 observer.next(response.body);
165 },
166 error: (err) => observer.error(err),
167 complete: () => observer.complete(),
168 });
169 return () => {
170 if (!sub.closed) {
171 sub.unsubscribe();
172 }
173 };
174 });
175 }
176 request(op) {
177 return this.requester(op);
178 }
179}
180class HttpLink {
181 constructor(httpClient) {
182 this.httpClient = httpClient;
183 }
184 create(options) {
185 return new HttpLinkHandler(this.httpClient, options);
186 }
187}
188HttpLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: HttpLink, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
189HttpLink.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: HttpLink, providedIn: 'root' });
190i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: HttpLink, decorators: [{
191 type: Injectable,
192 args: [{
193 providedIn: 'root',
194 }]
195 }], ctorParameters: function () { return [{ type: i1.HttpClient }]; } });
196
197const defaults = {
198 batchInterval: 10,
199 batchMax: 10,
200 uri: 'graphql',
201 method: 'POST',
202};
203class HttpBatchLinkHandler extends ApolloLink {
204 constructor(httpClient, options) {
205 super();
206 this.httpClient = httpClient;
207 this.options = options;
208 this.print = print;
209 this.batchInterval = options.batchInterval || defaults.batchInterval;
210 this.batchMax = options.batchMax || defaults.batchMax;
211 if (this.options.operationPrinter) {
212 this.print = this.options.operationPrinter;
213 }
214 const batchHandler = (operations) => {
215 return new Observable$1((observer) => {
216 const body = this.createBody(operations);
217 const headers = this.createHeaders(operations);
218 const { method, uri, withCredentials } = this.createOptions(operations);
219 if (typeof uri === 'function') {
220 throw new Error(`Option 'uri' is a function, should be a string`);
221 }
222 const req = {
223 method,
224 url: uri,
225 body: body,
226 options: {
227 withCredentials,
228 headers,
229 },
230 };
231 const sub = fetch(req, this.httpClient, () => {
232 throw new Error('File upload is not available when combined with Batching');
233 }).subscribe({
234 next: (result) => observer.next(result.body),
235 error: (err) => observer.error(err),
236 complete: () => observer.complete(),
237 });
238 return () => {
239 if (!sub.closed) {
240 sub.unsubscribe();
241 }
242 };
243 });
244 };
245 const batchKey = options.batchKey ||
246 ((operation) => {
247 return this.createBatchKey(operation);
248 });
249 this.batcher = new BatchLink({
250 batchInterval: this.batchInterval,
251 batchMax: this.batchMax,
252 batchKey,
253 batchHandler,
254 });
255 }
256 createOptions(operations) {
257 const context = operations[0].getContext();
258 return {
259 method: prioritize(context.method, this.options.method, defaults.method),
260 uri: prioritize(context.uri, this.options.uri, defaults.uri),
261 withCredentials: prioritize(context.withCredentials, this.options.withCredentials),
262 };
263 }
264 createBody(operations) {
265 return operations.map((operation) => {
266 const includeExtensions = prioritize(operation.getContext().includeExtensions, this.options.includeExtensions, false);
267 const includeQuery = prioritize(operation.getContext().includeQuery, this.options.includeQuery, true);
268 const body = {
269 operationName: operation.operationName,
270 variables: operation.variables,
271 };
272 if (includeExtensions) {
273 body.extensions = operation.extensions;
274 }
275 if (includeQuery) {
276 body.query = this.print(operation.query);
277 }
278 return body;
279 });
280 }
281 createHeaders(operations) {
282 var _a, _b;
283 return operations.reduce((headers, operation) => {
284 return mergeHeaders(headers, operation.getContext().headers);
285 }, createHeadersWithClientAwereness({
286 headers: this.options.headers,
287 clientAwareness: (_b = (_a = operations[0]) === null || _a === void 0 ? void 0 : _a.getContext()) === null || _b === void 0 ? void 0 : _b.clientAwareness,
288 }));
289 }
290 createBatchKey(operation) {
291 const context = operation.getContext();
292 if (context.skipBatching) {
293 return Math.random().toString(36).substr(2, 9);
294 }
295 const headers = context.headers &&
296 context.headers.keys().map((k) => context.headers.get(k));
297 const opts = JSON.stringify({
298 includeQuery: context.includeQuery,
299 includeExtensions: context.includeExtensions,
300 headers,
301 });
302 return prioritize(context.uri, this.options.uri) + opts;
303 }
304 request(op) {
305 return this.batcher.request(op);
306 }
307}
308class HttpBatchLink {
309 constructor(httpClient) {
310 this.httpClient = httpClient;
311 }
312 create(options) {
313 return new HttpBatchLinkHandler(this.httpClient, options);
314 }
315}
316HttpBatchLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: HttpBatchLink, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
317HttpBatchLink.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: HttpBatchLink, providedIn: 'root' });
318i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.2", ngImport: i0, type: HttpBatchLink, decorators: [{
319 type: Injectable,
320 args: [{
321 providedIn: 'root',
322 }]
323 }], ctorParameters: function () { return [{ type: i1.HttpClient }]; } });
324
325// http
326
327/**
328 * Generated bundle index. Do not edit.
329 */
330
331export { HttpBatchLink, HttpBatchLinkHandler, HttpLink, HttpLinkHandler };
332//# sourceMappingURL=ngApolloLinkHttp.mjs.map