Skip to content

Commit

Permalink
fix: header parsing correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
bddvlpr committed Mar 28, 2024
1 parent 26d22df commit 8217304
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 21 deletions.
2 changes: 1 addition & 1 deletion projects/openapi-fetch-angular/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "openapi-fetch-angular",
"description": "Fast, typesafe fetch client for your OpenAPI schema. Works with Angular HTTPClient API.",
"version": "0.1.1",
"version": "0.1.2",
"license": "MIT",
"author": "Luna Simons <luna@bddvlpr.com> (https://bddvlpr.com)",
"contributors": [
Expand Down
125 changes: 125 additions & 0 deletions projects/openapi-fetch-angular/src/lib/openapi-client.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,131 @@ describe('OpenAPIClientService', () => {
expect(httpMock.expectOne('/query-params?string=10')).toBeDefined();
});
});

describe('headers', () => {
it('should correctly type the headers', () => {
service
// @ts-expect-error - missing required parameter
.get('/header-params')
.subscribe(({ response }) => expect(response).toBeDefined());
let req = httpMock.expectOne('/header-params');
expect(req).toBeDefined();

service
.get('/header-params', {
// @ts-expect-error - empty object
params: {},
})
.subscribe(({ response }) => expect(response).toBeDefined());
req = httpMock.expectOne('/header-params');
expect(req).toBeDefined();

service
.get('/header-params', {
params: {
header: {
// @ts-expect-error - wrong type
'x-required-header': 123,
},
},
})
.subscribe(({ response }) => expect(response).toBeDefined());
req = httpMock.expectOne('/header-params');
expect(req).toBeDefined();

service
.get('/header-params', {
params: {
header: {
'x-required-header': '123',
},
},
})
.subscribe(({ response }) => expect(response).toBeDefined());
req = httpMock.expectOne('/header-params');
expect(req).toBeDefined();
});

it('should correctly send headers', () => {
service
.get('/header-params', {
params: {
header: {
'x-required-header': '123',
},
},
})
.subscribe(({ response }) =>
expect(response.headers.get('x-required-header-server')).toEqual(
'123',
),
);
const req = httpMock.expectOne('/header-params');

expect(req.request.headers.get('Content-Type')).toEqual(
'application/json',
);
expect(req.request.headers.get('x-required-header')).toEqual('123');

req.flush(null, { headers: { 'x-required-header-server': '123' } });
});
});

describe('headers (promise)', () => {
it('should correctly type the headers', () => {
// @ts-expect-error - missing required parameter
service.getPromise('/header-params');
expect(httpMock.expectOne('/header-params')).toBeDefined();

service.getPromise('/header-params', {
// @ts-expect-error - empty object
params: {},
});
expect(httpMock.expectOne('/header-params')).toBeDefined();

service.getPromise('/header-params', {
params: {
header: {
// @ts-expect-error - wrong type
'x-required-header': 123,
},
},
});
expect(httpMock.expectOne('/header-params')).toBeDefined();

service.getPromise('/header-params', {
params: {
header: {
'x-required-header': '123',
},
},
});
expect(httpMock.expectOne('/header-params')).toBeDefined();
});

it('should correctly send headers', () => {
service
.getPromise('/header-params', {
params: {
header: {
'x-required-header': '123',
},
},
})
.then(({ response }) =>
expect(response.headers.get('x-required-header-server')).toEqual(
'123',
),
);
const req = httpMock.expectOne('/header-params');

expect(req.request.headers.get('Content-Type')).toEqual(
'application/json',
);
expect(req.request.headers.get('x-required-header')).toEqual('123');
req.flush(null, { headers: { 'x-required-header-server': '123' } });
});
});
});

describe('body', () => {
Expand Down
14 changes: 9 additions & 5 deletions projects/openapi-fetch-angular/src/lib/openapi-client.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
HttpRequest,
HttpResponse,
} from '@angular/common/http';
Expand All @@ -13,15 +12,16 @@ import {
FetchOptions,
} from './openapi-client.types';
import {
convertHeaders,
createFinalURL,
createQuerySerializer,
mergeHeaders,
} from './openapi-serializer';
import { catchError, filter, lastValueFrom, map, of } from 'rxjs';

export const DEFAULT_HEADERS = new HttpHeaders({
export const DEFAULT_HEADERS = {
'Content-Type': 'application/json',
});
};

@Injectable({
providedIn: 'root',
Expand Down Expand Up @@ -92,7 +92,9 @@ export abstract class OpenAPIClientService<Paths extends {}> {
const requestInit = {
...baseOptions,
...init,
headers: mergeHeaders(baseHeaders, headers, params.headers),
headers: convertHeaders(
mergeHeaders(baseHeaders, headers, params.header),
),
};

const request = new HttpRequest(
Expand Down Expand Up @@ -160,7 +162,9 @@ export abstract class OpenAPIClientService<Paths extends {}> {
const requestInit = {
...baseOptions,
...init,
headers: mergeHeaders(baseHeaders, headers, params.headers),
headers: convertHeaders(
mergeHeaders(baseHeaders, headers, params.header),
),
};

const request = new HttpRequest(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HttpHeaders, HttpRequest, HttpResponse } from '@angular/common/http';
import { HttpRequest, HttpResponse } from '@angular/common/http';
import {
ErrorResponse,
FilterKeys,
Expand Down Expand Up @@ -35,7 +35,10 @@ export interface ClientOptions
headers?: HeadersOptions;
}

export type HeadersOptions = HttpHeaders;
export type HeadersOptions = Record<
string,
string | number | boolean | (string | number | boolean)[] | null | undefined
>;

export type ParamsOption<T> = T extends {
parameters: any;
Expand Down Expand Up @@ -67,7 +70,7 @@ export type RequestBodyOption<T> =
export type RequestOptions<T> = ParamsOption<T> &
RequestBodyOption<T> & {
querySerializer?: QuerySerializer<T> | QuerySerializerOptions;
headers?: HttpHeaders;
headers?: HeadersOptions;
};

export type FetchOptions<T> = RequestOptions<T> &
Expand Down
31 changes: 19 additions & 12 deletions projects/openapi-fetch-angular/src/lib/openapi-serializer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HttpHeaders } from '@angular/common/http';
import {
HeadersOptions,
QuerySerializer,
QuerySerializerOptions,
} from './openapi-client.types';
Expand Down Expand Up @@ -201,20 +202,26 @@ export const serializeObjectParam = (
};

export const mergeHeaders = (
...allHeaders: (HttpHeaders | undefined)[]
): HttpHeaders => {
let headers = new HttpHeaders();
for (const h of allHeaders) {
if (h) {
for (const key of h.keys()) {
const v = h.get(key);
if (v) {
headers = headers.append(key, v);
}
}
...allHeaders: (HeadersOptions | undefined)[]
): HeadersOptions => {
return allHeaders.reduce<HeadersOptions>((acc, headers) => {
if (!headers) {
return acc;
}
for (const key in headers) {
acc[key] = headers[key];
}
return acc;
}, {});
};

export const convertHeaders = (headers?: HeadersOptions): HttpHeaders => {
let httpHeaders = new HttpHeaders();
for (const key in headers) {
if (!headers[key]) continue;
httpHeaders = httpHeaders.set(key, headers[key] as string);
}
return headers;
return httpHeaders;
};

export const defaultPathSerializer = (
Expand Down

0 comments on commit 8217304

Please sign in to comment.