Skip to content

Commit

Permalink
Merge 99254ee into 51294f4
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineLep committed Sep 25, 2023
2 parents 51294f4 + 99254ee commit 8c6d3c8
Show file tree
Hide file tree
Showing 82 changed files with 1,603 additions and 636 deletions.
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# anthill-framework Code Of Conduct
# anthill-framework Code Of Conduct
11 changes: 11 additions & 0 deletions SNIPPET.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# anthill-framework useful snippets

## Run one test
```bash
node ./node_modules/jest/bin/jest.js -i ./src/tests/rest-handler-decorator.test.ts -c ./jest.config.ts -t "decorator add handler to anthill"
```

## Run a test file
```bash
node ./node_modules/jest/bin/jest.js -i ./src/tests/rest-handler-decorator.test.ts -c ./jest.config.ts
```
67 changes: 44 additions & 23 deletions src/core/abstract-handler.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
import { AHCallable } from "../framework/models/handler/callable";
import { AHException } from "../framework/features/anthill-exception";
import { AHAbstractHandlerParams } from "./models/abstract-handler-params";
import { AHAbstractHandlerConfig } from "./models/handler/abstract-handler-config";
import { AHAwsContext } from "../framework/models/aws/aws-context";
import { AHHandlerOptions } from "../framework/models/handler/handler-options";
import { AHTimeTracker } from "../framework/features/time-tracker";
import { AHMultiLevelHandlerConfig } from "./models/handler/multi-level-handler-config";
import { AHHandlerConfigLevelEnum } from "../framework/models/enums/handler-config-level-enum";
import { AHAwsCallback } from "../framework/models/aws/aws-callback";
import { AHPromiseHelper } from "../framework/helpers/promise-helper";
import { Anthill } from "../framework/features/anthill";

export abstract class AHAbstractHandler<T, U> {
private static defaultOptions: AHHandlerOptions = {
displayPerformanceMetrics: false,
};

protected controllerName: Promise<string>;
protected name: string;
protected callable: AHCallable<T, U>;
protected options: AHHandlerOptions = AHAbstractHandler.defaultOptions;
protected options: AHMultiLevelHandlerConfig<AHHandlerOptions> = {
anthill: { displayPerformanceMetrics: false },
controller: { displayPerformanceMetrics: false },
handler: { displayPerformanceMetrics: false },
};

constructor(params: AHAbstractHandlerParams<T, U>) {
constructor(params: AHAbstractHandlerConfig<T, U>) {
if (!/^[a-zA-z_]+[a-zA-z0-9]*$/.test(params.name)) {
throw new AHException(
`Invalid handler name: ${params.name}. Handler name must respect typescript var naming convention`,
);
}

this.controllerName = params.controllerName;
this.name = params.name;
this.callable = params.callable;

if (params.options) {
this.options = { ...this.options, ...params.options };
this.setOptions(params.options, AHHandlerConfigLevelEnum.Handler);
}
}

/**
* Set the default options
* @param options The options (override) to set by default
*/
static setDefaultOptions(options: AHHandlerOptions): void {
AHAbstractHandler.defaultOptions = {
...AHAbstractHandler.defaultOptions,
...options,
};
}

/**
* Set new options for the handler
* @param options The options to be set
* @param configLevel The config level that should be applied for the given config
*/
setOptions(options: AHHandlerOptions): void {
this.options = { ...this.options, ...options };
setOptions(
options: AHHandlerOptions,
configLevel: AHHandlerConfigLevelEnum = AHHandlerConfigLevelEnum.Handler,
): void {
this.options[configLevel] = { ...this.options[configLevel], ...options };
}

/**
Expand All @@ -61,16 +61,37 @@ export abstract class AHAbstractHandler<T, U> {
* @param tracker Tracker to print metrics for
*/
displayPerformanceMetrics(tracker: AHTimeTracker) {
if (this.options.displayPerformanceMetrics) {
if (this.computeOptions().displayPerformanceMetrics) {
tracker.logTrackingSession();
}
}

private computeOptions(): AHHandlerOptions {
return {
...this.options.anthill,
...this.options.controller,
...this.options.handler,
};
}

protected async getControllerInstance<T>(): Promise<T> {
const controllerName = await AHPromiseHelper.timeout(
this.controllerName,
100,
new AHException(
`Can't resolve controller name for handler ${this.name}. A handler method must be inside a class decorated with a controller decorator`,
),
);

return Anthill.getInstance()._dependencyContainer.resolve<T>(controllerName);
}

/**
* Handle the request
* @param event The request event
* @param context This object provides methods and properties that provide information about the invocation, function, and execution environment
* @param callback Callback method to respond the lambda call (pref not to use it)
* @returns The request response
*/
abstract handleRequest(event: T, context?: AHAwsContext): Promise<U>;
abstract handleRequest(event: T, context?: AHAwsContext, callback?: AHAwsCallback): Promise<U>;
}
3 changes: 1 addition & 2 deletions src/core/cache/cache.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { AHObjectHelper } from "../../framework/helpers/object-helper";
import { AHLogger } from "../../framework/features/logger";
import { AHCacheConfig } from "../../framework/models/cache-config";
import { AHCacheConfig } from "../models/cache/cache-config";
import { AHCacheData } from "../models/cache/cache-data";


export abstract class AHCache<T, U> {
data: Array<AHCacheData<T, U>> = [];
currentCacheSize: number = 0;
Expand Down
13 changes: 9 additions & 4 deletions src/core/cache/http-request-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import { AHCacheData } from "../models/cache/cache-data";
import { AHHttpResponse } from "../../framework/features/http-response";
import { AHCache } from "./cache";
import { AHHttpRequestParameters } from "../models/cache/http-request-parameters";

import { AHHttpRequestHelper } from "../../framework/helpers/http-request-helper";

export class AHHttpRequestCache extends AHCache<AHHttpRequestParameters, AHHttpResponse> {

/**
* Get a cache item with its id
* @param id The Id of the item to find inside the cache
Expand All @@ -18,21 +17,27 @@ export class AHHttpRequestCache extends AHCache<AHHttpRequestParameters, AHHttpR
return (
data.id.path === id.path &&
AHObjectHelper.isEquivalentObj(data.id.pathParameters, id.pathParameters) &&
AHObjectHelper.isEquivalentObj(data.id.queryStringParameters, id.queryStringParameters)
AHObjectHelper.isEquivalentObj(data.id.queryStringParameters, id.queryStringParameters) &&
AHObjectHelper.isEquivalentObj(data.id.headersIncluded, id.headersIncluded)
);
});
}

/**
* Build the http cache request based on the event
* @param event The event to build the cache request with
* @param headersToInclude Headers that have to be taken into an account for request identification
* @returns The cache request parameters
*/
static buildCacheRequestParameters(event: AHAwsEvent): AHHttpRequestParameters {
static buildCacheRequestParameters(event: AHAwsEvent, headersToInclude: Array<string> = []): AHHttpRequestParameters {
return {
path: event.path,
pathParameters: event.pathParameters,
queryStringParameters: event.queryStringParameters,
headersIncluded: headersToInclude?.reduce((acc, header) => {
acc[header] = AHHttpRequestHelper.getHeaderValue(header, event.headers);
return acc;
}, {}),
};
}
}
37 changes: 37 additions & 0 deletions src/core/dependency-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { AHException } from "../framework/features/anthill-exception";
import { AHDependencyContainerMap } from "./models/dependency-container-map";

export class AHDependencyContainer {
private container: AHDependencyContainerMap = {};

/**
* Register a contructor in order to be able to instanciate it on demand later
* @param identifier The constructor (i.e. class) identifier
* @param constructor The constructor function
*/
register<T>(identifier: string, constructor: new () => T): void {
if (!Object.keys(this.container).includes(identifier)) {
this.container[identifier] = {
constructor: constructor,
instance: null,
};
}
}

/**
* Resolve a given constructor (i.e. class) identifier to the matching instance
* @param identifier The constructor (i.e. class) identifier
* @returns The instance matching with constructor identifier
*/
resolve<T>(identifier: string): T {
if (!Object.keys(this.container).includes(identifier)) {
throw new AHException(`Dependency ${identifier} has never been registered`);
}

if (!this.container[identifier].instance) {
this.container[identifier].instance = new this.container[identifier].constructor();
}

return this.container[identifier].instance as T;
}
}
9 changes: 0 additions & 9 deletions src/core/models/abstract-handler-params.ts

This file was deleted.

File renamed without changes.
1 change: 1 addition & 0 deletions src/core/models/cache/http-request-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface AHHttpRequestParameters {
path: string;
queryStringParameters?: { [key: string]: string };
pathParameters?: { [key: string]: string };
headersIncluded?: { [key: string]: string };
}
1 change: 1 addition & 0 deletions src/core/models/constructible.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type AHConstructible<T> = { new (...args: any[]): T };
6 changes: 6 additions & 0 deletions src/core/models/controller-class/lambda-controller-class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AHLambdaHandlerOverridableConfig } from "../../../framework/models/handler/lambda-handler-overridable-config";
import { AHConstructible } from "../constructible";

export type AHLambdaControllerClass<T> = AHConstructible<T> & {
_lambdaHandlerConfig: AHLambdaHandlerOverridableConfig;
}
6 changes: 6 additions & 0 deletions src/core/models/controller-class/rest-controller-class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AHRestHandlerOverridableConfig } from "../../../framework/models/handler/rest-handler-overridable-config";
import { AHConstructible } from "../constructible";

export type AHRestControllerClass<T> = AHConstructible<T> & {
_restHandlerConfig: AHRestHandlerOverridableConfig;
}
6 changes: 6 additions & 0 deletions src/core/models/dependency-container-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface AHDependencyContainerMap {
[key: string]: {
constructor: new () => any;
instance: any;
};
}
2 changes: 1 addition & 1 deletion src/core/models/enums/time-tracker-state-enum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export enum AHTimeTrackerStateEnum {
Started,
Stopped,
}
}
9 changes: 9 additions & 0 deletions src/core/models/handler/abstract-handler-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AHCallable } from "../../../framework/models/handler/callable";
import { AHHandlerOptions } from "../../../framework/models/handler/handler-options";

export interface AHAbstractHandlerConfig<T, U> {
controllerName: Promise<string>;
name: string;
callable: AHCallable<T, U>;
options?: AHHandlerOptions;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AHHandlerOptions } from "../../../framework/models/handler/handler-options";

export interface AHAbstractHandlerOverridableConfig {
options?: AHHandlerOptions;
}
5 changes: 5 additions & 0 deletions src/core/models/handler/multi-level-handler-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface AHMultiLevelHandlerConfig<T> {
anthill?: T;
controller?: T;
handler?: T;
}
17 changes: 17 additions & 0 deletions src/framework/decorators/lambda-controller-decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { AHLambdaHandlerOverridableConfig } from "../models/handler/lambda-handler-overridable-config";
import { AHLambdaControllerClass } from "../../core/models/controller-class/lambda-controller-class";
import { Anthill } from "../features/anthill";

/**
* Lambda controller decorator used to decorate controller classes
* @param lambdaControllerOptions Lambda handler options that will be applied to all handlers inside the controller class
* @returns The lambda controller decorator
*/
export function LambdaController(lambdaControllerOptions: AHLambdaHandlerOverridableConfig = {}): any {
return <T extends AHLambdaControllerClass<T>>(target: T, context: ClassDecoratorContext<T>) => {
Anthill.getInstance()._dependencyContainer.register<T>(context.name, target);
Anthill.getInstance()._dependencyContainer.resolve<T>(context.name)._lambdaHandlerConfig = lambdaControllerOptions;

return target;
};
}
42 changes: 42 additions & 0 deletions src/framework/decorators/lambda-handler-decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Anthill } from "../features/anthill";
import { AHLambdaHandler } from "../features/handler/lambda-handler";
import { AHLambdaHandlerConfig } from "../models/handler/lambda-handler-config";

/**
* Lambda handler decorator used to decorate handler method inside controller classes
* @param lambdaHandlerOptions Lambda handler options that will be applied to this handler
* @returns The lambda handler decorator
*/
export function LambdaHandler<T, A extends [any, ...undefined[]], R extends Promise<any>>(
lambdaHandlerOptions: Partial<AHLambdaHandlerConfig<any, any>> = {},
) {

return (target: (this: T, ...args: A) => R, context: ClassMethodDecoratorContext<T, (this: T, ...args: A) => R>) => {
if (!lambdaHandlerOptions.name) {
lambdaHandlerOptions.name = String(context.name);
}

const controllerNamePromise = new Promise<any>((resolve) => {
context.addInitializer(function () {
if (typeof this === "object") {
// Instance method
return resolve(this.constructor.name);
}

// static method
return resolve((this as any).name);
});
});

const _lambdaHandlerOptions: AHLambdaHandlerConfig<any, any> = {
controllerName: controllerNamePromise,
name: lambdaHandlerOptions.name,
callable: target as any,
...lambdaHandlerOptions,
};

const lambdaHandler = new AHLambdaHandler(_lambdaHandlerOptions);
Anthill.getInstance()._registerHandler(lambdaHandler);
return target;
};
}
17 changes: 17 additions & 0 deletions src/framework/decorators/rest-controller-decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { AHRestControllerClass } from "../../core/models/controller-class/rest-controller-class";
import { Anthill } from "../features/anthill";
import { AHRestHandlerOverridableConfig } from "../models/handler/rest-handler-overridable-config";

/**
* REST controller decorator used to decorate controller classes
* @param restControllerOptions REST handler options that will be applied to all handlers inside the controller class
* @returns The REST controller decorator
*/
export function RestController(restControllerOptions: AHRestHandlerOverridableConfig = {}): any {
return <T extends AHRestControllerClass<T>>(target: T, context: ClassDecoratorContext<T>) => {
Anthill.getInstance()._dependencyContainer.register<T>(context.name, target);
Anthill.getInstance()._dependencyContainer.resolve<T>(context.name)._restHandlerConfig = restControllerOptions;

return target;
};
}
Loading

0 comments on commit 8c6d3c8

Please sign in to comment.