Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/floppy-states-throw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'apollo-angular': patch
---

Allow headers type in DefaultContext to be Record or HttpHeaders
13 changes: 8 additions & 5 deletions packages/apollo-angular/http/src/http-batch-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { BatchLink } from '@apollo/client/link/batch';
import type { HttpLink } from './http-link';
import { Body, Context, OperationPrinter, Request } from './types';
import {
convertHeadersToArray,
convertToHttpHeaders,
createHeadersWithClientAwareness,
fetch,
mergeHeaders,
Expand Down Expand Up @@ -38,7 +40,7 @@ export const defaults = {
*/
export function pick<K extends keyof Omit<typeof defaults, 'batchInterval' | 'batchMax'>>(
context: Context,
options: HttpBatchLink.Options,
options: Omit<HttpBatchLink.Options, 'headers'>,
key: K,
): ReturnType<typeof prioritize<Context[K] | HttpBatchLink.Options[K] | (typeof defaults)[K]>> {
return prioritize(context[key], options[key], defaults[key]);
Expand Down Expand Up @@ -161,7 +163,9 @@ export class HttpBatchLinkHandler extends ApolloLink {
return operations.reduce(
(headers: HttpHeaders, operation: ApolloLink.Operation) => {
const { headers: contextHeaders } = operation.getContext();
return contextHeaders ? mergeHeaders(headers, contextHeaders) : headers;
return contextHeaders
? mergeHeaders(headers, convertToHttpHeaders(contextHeaders))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PowerKiKi , here was already a check/conversion to HttpHeaders missing, which led to a stacktrace in case of using object-type headers. Adding convertToHttpHeaders(contextHeaders) will fix that.

: headers;
},
createHeadersWithClientAwareness({
headers: this.options.headers,
Expand All @@ -187,8 +191,7 @@ export class HttpBatchLinkHandler extends ApolloLink {
return Math.random().toString(36).substring(2, 11);
}

const headers =
context.headers && context.headers.keys().map((k: string) => context.headers!.get(k));
const headers = convertHeadersToArray(context.headers);

const opts = JSON.stringify({
includeQuery: context.includeQuery,
Expand All @@ -199,7 +202,7 @@ export class HttpBatchLinkHandler extends ApolloLink {
return prioritize(context.uri, this.options.uri, '') + opts;
}

public request(
public override request(
op: ApolloLink.Operation,
forward: ApolloLink.ForwardFunction,
): Observable<ApolloLink.Result> {
Expand Down
17 changes: 9 additions & 8 deletions packages/apollo-angular/http/src/http-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import {
Context,
ExtractFiles,
FetchOptions,
HttpRequestOptions,
OperationPrinter,
Request,
RequestOptions,
} from './types';
import { createHeadersWithClientAwareness, fetch, mergeHeaders, mergeHttpContext } from './utils';

export declare namespace HttpLink {
export interface Options extends FetchOptions, HttpRequestOptions {
export interface Options extends FetchOptions, RequestOptions {
operationPrinter?: OperationPrinter;
useGETForQueries?: boolean;
extractFiles?: ExtractFiles;
Expand Down Expand Up @@ -62,6 +62,11 @@ export class HttpLinkHandler extends ApolloLink {
method = 'GET';
}

const headers = mergeHeaders(
this.options.headers,
createHeadersWithClientAwareness(context),
);

const req: Request = {
method,
url: typeof url === 'function' ? url(operation) : url,
Expand All @@ -72,7 +77,7 @@ export class HttpLinkHandler extends ApolloLink {
options: {
withCredentials,
useMultipart,
headers: this.options.headers,
headers,
context: httpContext,
},
};
Expand All @@ -85,10 +90,6 @@ export class HttpLinkHandler extends ApolloLink {
(req.body as Body).query = this.print(operation.query);
}

const headers = createHeadersWithClientAwareness(context);

req.options.headers = mergeHeaders(req.options.headers, headers);

const sub = fetch(req, this.httpClient, this.options.extractFiles).subscribe({
next: response => {
operation.setContext({ response });
Expand All @@ -106,7 +107,7 @@ export class HttpLinkHandler extends ApolloLink {
});
}

public request(op: ApolloLink.Operation): Observable<ApolloLink.Result> {
public override request(op: ApolloLink.Operation): Observable<ApolloLink.Result> {
return this.requester(op);
}
}
Expand Down
10 changes: 6 additions & 4 deletions packages/apollo-angular/http/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ declare module '@apollo/client' {
}

export type HttpRequestOptions = {
headers?: HttpHeaders;
headers?: HttpHeaders | Record<string, string>;
withCredentials?: boolean;
useMultipart?: boolean;
httpContext?: HttpContext;
};

export type RequestOptions = Omit<HttpRequestOptions, 'httpContext'> & {
context?: HttpContext;
export type RequestOptions = Omit<HttpRequestOptions, 'headers'> & {
headers?: HttpHeaders;
};

export type URIFunction = (operation: ApolloLink.Operation) => string;
Expand All @@ -37,11 +37,13 @@ export type Body = {

export interface Context extends FetchOptions, HttpRequestOptions {}

type HttpClientRequestOptions = Omit<RequestOptions, 'httpContext'> & { context: HttpContext };

export type Request = {
method: string;
url: string;
body: Body | Body[];
options: RequestOptions;
options: HttpClientRequestOptions;
};

export type ExtractedFiles = {
Expand Down
20 changes: 13 additions & 7 deletions packages/apollo-angular/http/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,25 @@ export const fetch = (
});
};

export const convertToHttpHeaders = (
headers: HttpHeaders | Record<string, string> | undefined,
): HttpHeaders => (headers instanceof HttpHeaders ? headers : new HttpHeaders(headers));

export const convertHeadersToArray = (
headers: HttpHeaders | Record<string, string> | undefined,
): string[] =>
headers instanceof HttpHeaders
? headers.keys().map((k: string) => headers.get(k)!)
: Object.values(headers ?? {});

export const mergeHeaders = (
source: HttpHeaders | undefined,
destination: HttpHeaders,
): HttpHeaders => {
if (source && destination) {
const merged = destination
return destination
.keys()
.reduce((headers, name) => headers.set(name, destination.getAll(name)!), source);

return merged;
}

return destination || source;
Expand Down Expand Up @@ -146,10 +155,7 @@ export function createHeadersWithClientAwareness(context: Record<string, any>) {
// `clientAwareness` object is found in the context. These headers are
// set first, followed by the rest of the headers pulled from
// `context.headers`.
let headers =
context.headers && context.headers instanceof HttpHeaders
? context.headers
: new HttpHeaders(context.headers);
let headers = convertToHttpHeaders(context.headers);

if (context.clientAwareness) {
const { name, version } = context.clientAwareness;
Expand Down
Loading