Skip to content

Commit e48b25c

Browse files
committed
Adding customization for "events" processing to Http client
1 parent 8c75368 commit e48b25c

File tree

10 files changed

+383
-24
lines changed

10 files changed

+383
-24
lines changed

.changeset/clean-dragons-return.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'apollo-angular': minor
3+
---
4+
5+
Adding additional configurable support for the underlying Angular Http Client

.devcontainer/base.Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# Update the VARIANT arg in devcontainer.json to pick a Node.js version: 14, 12, 10
2-
ARG VARIANT=14
1+
# Update the VARIANT arg in devcontainer.json to pick a Node.js version: 14, 12, 10
2+
ARG VARIANT=18
33
FROM node:${VARIANT}
44

55
# Options for setup scripts
@@ -10,7 +10,7 @@ ARG USER_UID=1000
1010
ARG USER_GID=$USER_UID
1111

1212
ENV NVM_DIR=/usr/local/share/nvm
13-
ENV NVM_SYMLINK_CURRENT=true \
13+
ENV NVM_SYMLINK_CURRENT=true \
1414
PATH=${NVM_DIR}/current/bin:${PATH}
1515

1616
# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies.

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"build": {
44
"dockerfile": "Dockerfile",
55
// Update 'VARIANT' to pick a Node version: 10, 12, 14
6-
"args": { "VARIANT": "14" }
6+
"args": { "VARIANT": "18" }
77
},
88

99
// Set *default* container specific settings.json values on container create.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
]
1010
},
1111
"engines": {
12-
"node": ">=16"
12+
"node": ">=18"
1313
},
1414
"scripts": {
1515
"build": "yarn workspaces run build",

packages/apollo-angular/http/src/http-batch-link.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { print } from 'graphql';
2-
import { HttpClient, HttpHeaders } from '@angular/common/http';
2+
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
33
import { Injectable } from '@angular/core';
44
import {
55
ApolloLink,
@@ -75,7 +75,13 @@ export class HttpBatchLinkHandler extends ApolloLink {
7575
const sub = fetch(req, this.httpClient, () => {
7676
throw new Error('File upload is not available when combined with Batching');
7777
}).subscribe({
78-
next: result => observer.next(result.body),
78+
next: result => {
79+
if (result instanceof HttpResponse) {
80+
observer.next(result.body);
81+
} else {
82+
observer.next(result);
83+
}
84+
},
7985
error: err => observer.error(err),
8086
complete: () => observer.complete(),
8187
});

packages/apollo-angular/http/src/http-link.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { print } from 'graphql';
2-
import { HttpClient } from '@angular/common/http';
2+
import { HttpClient, HttpEvent, HttpEventType, HttpResponse } from '@angular/common/http';
33
import { Injectable } from '@angular/core';
44
import {
55
ApolloLink,
@@ -8,7 +8,7 @@ import {
88
Operation,
99
} from '@apollo/client/core';
1010
import { pick } from './http-batch-link';
11-
import { Body, Context, OperationPrinter, Options, Request } from './types';
11+
import { Body, Context, HttpClientReturn, OperationPrinter, Options, Request } from './types';
1212
import { createHeadersWithClientAwareness, fetch, mergeHeaders } from './utils';
1313

1414
// XXX find a better name for it
@@ -57,6 +57,9 @@ export class HttpLinkHandler extends ApolloLink {
5757
withCredentials,
5858
useMultipart,
5959
headers: this.options.headers,
60+
observe: context.observe,
61+
reportProgress: context.reportProgress,
62+
responseType: context.responseType,
6063
},
6164
};
6265

@@ -73,9 +76,25 @@ export class HttpLinkHandler extends ApolloLink {
7376
req.options.headers = mergeHeaders(req.options.headers, headers);
7477

7578
const sub = fetch(req, this.httpClient, this.options.extractFiles).subscribe({
76-
next: response => {
79+
next: (response: HttpClientReturn) => {
7780
operation.setContext({ response });
78-
observer.next(response.body);
81+
82+
if (
83+
context.responseType === 'blob' ||
84+
context.responseType === 'arraybuffer' ||
85+
context.responseType === 'text'
86+
) {
87+
observer.next(response);
88+
return;
89+
}
90+
91+
if (response instanceof HttpResponse) {
92+
observer.next(response.body);
93+
} else if (this.isHttpEvent(response)) {
94+
this.handleHttpEvent(response, observer);
95+
} else {
96+
observer.next(response);
97+
}
7998
},
8099
error: err => observer.error(err),
81100
complete: () => observer.complete(),
@@ -92,6 +111,42 @@ export class HttpLinkHandler extends ApolloLink {
92111
public request(op: Operation): LinkObservable<FetchResult> | null {
93112
return this.requester(op);
94113
}
114+
115+
private isHttpEvent(response: HttpClientReturn): response is HttpEvent<any> {
116+
return typeof response === 'object' && response !== null && 'type' in response;
117+
}
118+
119+
private handleHttpEvent(event: HttpEvent<any>, observer: any) {
120+
switch (event.type) {
121+
case HttpEventType.Response:
122+
if (event instanceof HttpResponse) {
123+
observer.next(event.body);
124+
}
125+
break;
126+
case HttpEventType.DownloadProgress:
127+
case HttpEventType.UploadProgress:
128+
observer.next({
129+
data: null,
130+
extensions: {
131+
httpEvent: {
132+
type: event.type,
133+
loaded: 'loaded' in event ? event.loaded : undefined,
134+
total: 'total' in event ? event.total : undefined,
135+
},
136+
},
137+
});
138+
break;
139+
default:
140+
observer.next({
141+
data: null,
142+
extensions: {
143+
httpEvent: {
144+
type: event.type,
145+
},
146+
},
147+
});
148+
}
149+
}
95150
}
96151

97152
@Injectable({

packages/apollo-angular/http/src/types.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
import { DocumentNode } from 'graphql';
2-
import { HttpHeaders } from '@angular/common/http';
3-
import { Operation } from '@apollo/client/core';
2+
import { HttpContext, HttpEvent, HttpHeaders, HttpResponse } from '@angular/common/http';
3+
import { FetchResult, Operation } from '@apollo/client/core';
44

55
export type HttpRequestOptions = {
66
headers?: HttpHeaders;
7+
context?: HttpContext;
78
withCredentials?: boolean;
89
useMultipart?: boolean;
10+
observe?: 'body' | 'events' | 'response';
11+
reportProgress?: boolean;
12+
responseType?: 'json' | 'arraybuffer' | 'blob' | 'text';
13+
params?: any;
14+
body?: any;
915
};
1016

17+
export type HttpClientReturn =
18+
| Object
19+
| ArrayBuffer
20+
| Blob
21+
| string
22+
| HttpResponse<Object | ArrayBuffer | Blob | string>
23+
| HttpEvent<Object | ArrayBuffer | Blob | string>;
24+
1125
export type URIFunction = (operation: Operation) => string;
1226

1327
export type FetchOptions = {

packages/apollo-angular/http/src/utils.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
import { Observable } from 'rxjs';
2-
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
3-
import { Body, ExtractedFiles, ExtractFiles, Request } from './types';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
3+
import {
4+
Body,
5+
Context,
6+
ExtractedFiles,
7+
ExtractFiles,
8+
HttpClientReturn,
9+
HttpRequestOptions,
10+
Request,
11+
} from './types';
412

513
export const fetch = (
614
req: Request,
715
httpClient: HttpClient,
816
extractFiles?: ExtractFiles,
9-
): Observable<HttpResponse<Object>> => {
17+
): Observable<HttpClientReturn> => {
18+
const context: Context = req.options || {};
1019
const shouldUseBody = ['POST', 'PUT', 'PATCH'].indexOf(req.method.toUpperCase()) !== -1;
1120
const shouldStringify = (param: string) =>
1221
['variables', 'extensions'].indexOf(param.toLowerCase()) !== -1;
@@ -96,13 +105,21 @@ export const fetch = (
96105
(bodyOrParams as any).body = form;
97106
}
98107

99-
// create a request
100-
return httpClient.request<Object>(req.method, req.url, {
101-
observe: 'response',
102-
responseType: 'json',
103-
reportProgress: false,
108+
const baseOptions: HttpRequestOptions = {
109+
reportProgress: context.reportProgress ?? false,
110+
withCredentials: context.withCredentials,
111+
headers: context.headers,
104112
...bodyOrParams,
105113
...req.options,
114+
};
115+
116+
const observe = context.observe || 'response';
117+
const responseType = context.responseType || 'json';
118+
119+
return httpClient.request(req.method, req.url, {
120+
...baseOptions,
121+
observe,
122+
responseType: responseType as 'json' | 'text' | 'blob' | 'arraybuffer',
106123
});
107124
};
108125

0 commit comments

Comments
 (0)