Skip to content

Commit

Permalink
perf(aws): optimized strip base path
Browse files Browse the repository at this point in the history
  • Loading branch information
H4ad committed Jun 7, 2023
1 parent 1fba12c commit f72967a
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 33 deletions.
23 changes: 15 additions & 8 deletions src/adapters/aws/alb.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
OnErrorProps,
} from '../../contracts';
import {
getDefaultIfUndefined,
StripBasePathFn,
buildStripBasePath,
getEventBodyAsBuffer,
getFlattenedHeadersMap,
getMultiValueHeadersMap,
Expand Down Expand Up @@ -56,7 +57,18 @@ export class AlbAdapter
*
* @param options - The options to customize the {@link AlbAdapter}
*/
constructor(protected readonly options?: AlbAdapterOptions) {}
constructor(protected readonly options?: AlbAdapterOptions) {
this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);
}

//#endregion

//#region Protected Properties

/**
* Strip base path function
*/
protected stripPathFn: StripBasePathFn;

//#endregion

Expand Down Expand Up @@ -175,12 +187,7 @@ export class AlbAdapter
* @param event - The event sent by serverless
*/
protected getPathFromEvent(event: ALBEvent): string {
const stripBasePath = getDefaultIfUndefined(
this.options?.stripBasePath,
'',
);
const replaceRegex = new RegExp(`^${stripBasePath}`);
const path = event.path.replace(replaceRegex, '');
const path = this.stripPathFn(event.path);

const queryParams = event.headers
? event.queryStringParameters
Expand Down
34 changes: 19 additions & 15 deletions src/adapters/aws/api-gateway-v1.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
OnErrorProps,
} from '../../contracts';
import {
getDefaultIfUndefined,
StripBasePathFn,
buildStripBasePath,
getEventBodyAsBuffer,
getFlattenedHeadersMap,
getMultiValueHeadersMap,
Expand Down Expand Up @@ -62,7 +63,18 @@ export class ApiGatewayV1Adapter
*
* @param options - The options to customize the {@link ApiGatewayV1Adapter}
*/
constructor(protected readonly options?: ApiGatewayV1Options) {}
constructor(protected readonly options?: ApiGatewayV1Options) {
this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);
}

//#endregion

//#region Protected Properties

/**
* Strip base path function
*/
protected stripPathFn: StripBasePathFn;

//#endregion

Expand Down Expand Up @@ -138,12 +150,10 @@ export class ApiGatewayV1Adapter
}: GetResponseAdapterProps<APIGatewayProxyEvent>): APIGatewayProxyResult {
const multiValueHeaders = getMultiValueHeadersMap(responseHeaders);

const transferEncodingHeader: string[] =
multiValueHeaders['transfer-encoding'];

const hasTransferEncodingChunked =
transferEncodingHeader &&
transferEncodingHeader.some(value => value.includes('chunked'));
const transferEncodingHeader = multiValueHeaders['transfer-encoding'];
const hasTransferEncodingChunked = transferEncodingHeader?.some(value =>
value.includes('chunked'),
);

if (hasTransferEncodingChunked) {
throw new Error(
Expand Down Expand Up @@ -198,13 +208,7 @@ export class ApiGatewayV1Adapter
* @param event - The event sent by serverless
*/
protected getPathFromEvent(event: APIGatewayProxyEvent): string {
const stripBasePath = getDefaultIfUndefined(
this.options?.stripBasePath,
'',
);
const replaceRegex = new RegExp(`^${stripBasePath}`);
const path = event.path.replace(replaceRegex, '');

const path = this.stripPathFn(event.path);
const queryParams = event.queryStringParameters || {};

return getPathWithQueryStringParams(path, queryParams);
Expand Down
24 changes: 15 additions & 9 deletions src/adapters/aws/api-gateway-v2.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
OnErrorProps,
} from '../../contracts';
import {
getDefaultIfUndefined,
StripBasePathFn,
buildStripBasePath,
getEventBodyAsBuffer,
getFlattenedHeadersMap,
getFlattenedHeadersMapAndCookies,
Expand Down Expand Up @@ -65,7 +66,18 @@ export class ApiGatewayV2Adapter
*
* @param options - The options to customize the {@link ApiGatewayV2Adapter}
*/
constructor(protected readonly options?: ApiGatewayV2Options) {}
constructor(protected readonly options?: ApiGatewayV2Options) {
this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);
}

//#endregion

//#region Protected Properties

/**
* Strip base path function
*/
protected stripPathFn: StripBasePathFn;

//#endregion

Expand Down Expand Up @@ -201,13 +213,7 @@ export class ApiGatewayV2Adapter
* @param event - The event sent by serverless
*/
protected getPathFromEvent(event: APIGatewayProxyEventV2): string {
const stripBasePath = getDefaultIfUndefined(
this.options?.stripBasePath,
'',
);
const replaceRegex = new RegExp(`^${stripBasePath}`);
const path = event.rawPath.replace(replaceRegex, '');

const path = this.stripPathFn(event.rawPath);
const queryParams = event.rawQueryString;

return getPathWithQueryStringParams(path, queryParams || {});
Expand Down
33 changes: 33 additions & 0 deletions src/core/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,36 @@ export function getQueryParamsStringFromRecord(

return searchParams.toString();
}

/**
* Type of the function to strip base path
*
* @breadcrumb Core / Path
* @public
*/
export type StripBasePathFn = (path: string) => string;

const NOOPBasePath: StripBasePathFn = (path: string) => path;

/**
* Get the strip base path function
*
* @param basePath - The base path
*
* @breadcrumb Core / Path
* @public
*/
export function buildStripBasePath(
basePath: string | undefined,
): StripBasePathFn {
if (!basePath) return NOOPBasePath;

const length = basePath.length;

return (path: string) => {
if (path.startsWith(basePath))
return path.slice(path.indexOf(basePath) + length, path.length) || '/';

return path;
};
}
2 changes: 1 addition & 1 deletion test/adapters/aws/api-gateway-v2.adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ describe(ApiGatewayV2Adapter.name, () => {
path.replace('/prod', ''),
event.queryStringParameters,
);
expect(result).toHaveProperty('path', resultPath);
expect(result.path).toBe(resultPath);
});
});

Expand Down
22 changes: 22 additions & 0 deletions test/core/path.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import {
buildStripBasePath,
getPathWithQueryStringParams,
getQueryParamsStringFromRecord,
} from '../../src';
Expand Down Expand Up @@ -83,3 +84,24 @@ describe('getQueryParamsStringFromRecord', () => {
expect(getQueryParamsStringFromRecord(queryParams)).toBe(expectedValue);
});
});

describe('buildStripBasePath', () => {
it('should correctly return query string from values', () => {
const options: [
basePath: string | undefined,
path: string,
expectedValue: string,
][] = [
['/prod', '/prod/users', '/users'],
['/v1', '/v1/potato', '/potato'],
['', '/v1/users', '/v1/users'],
[undefined, '/v1/courses', '/v1/courses'],
['/prod', '/prod', '/'],
['/prod', '/ignore-path', '/ignore-path'],
['/v1', '/prod/v1/ignore-path', '/prod/v1/ignore-path'],
];

for (const [basePath, path, expectedValue] of options)
expect(buildStripBasePath(basePath)(path)).toBe(expectedValue);
});
});

0 comments on commit f72967a

Please sign in to comment.