Skip to content

Commit

Permalink
anthill configuration for handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoineLep committed Sep 14, 2023
1 parent 2ab879a commit b555cd6
Show file tree
Hide file tree
Showing 22 changed files with 304 additions and 150 deletions.
20 changes: 3 additions & 17 deletions src/core/abstract-handler.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
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/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";

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

protected name: string;
protected callable: AHCallable<T, U>;
protected options: AHHandlerOptions = AHAbstractHandler.defaultOptions;
protected options: AHHandlerOptions = { 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`,
Expand All @@ -29,17 +26,6 @@ export abstract class AHAbstractHandler<T, U> {
}
}

/**
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AHCallable } from "../../framework/models/handler/callable";
import { AHHandlerOptions } from "../../framework/models/handler/handler-options";


export interface AHAbstractHandlerParams<T, U> {
export interface AHAbstractHandlerConfig<T, U> {
name: string;
callable: AHCallable<T, U>;
options?: AHHandlerOptions;
Expand Down
6 changes: 6 additions & 0 deletions src/core/models/abstract-handler-overridable-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AHHandlerOptions } from "../../framework/models/handler/handler-options";


export interface AHAbstractHandlerOverridableConfig {
options?: AHHandlerOptions;
}
6 changes: 3 additions & 3 deletions src/framework/decorators/restHandler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AHRestHandler } from "../features/handler/rest-handler";
import { Anthill } from "../features/anthill";
import { AHException } from "../features/anthill-exception";
import { AHRestHandlerParams } from "../models/handler/rest-handler-params";
import { AHRestHandlerConfig } from "../models/handler/rest-handler-config";


export function RestHandler(restHandlerOptions: Partial<AHRestHandlerParams>): MethodDecorator {
export function RestHandler(restHandlerOptions: Partial<AHRestHandlerConfig>): MethodDecorator {
if (!restHandlerOptions.method) {
throw new AHException("@RestHandler Missing rest handler method");
}
Expand All @@ -14,7 +14,7 @@ export function RestHandler(restHandlerOptions: Partial<AHRestHandlerParams>): M
restHandlerOptions.name = String(propertyKey);
}

const _restHandlerOptions: AHRestHandlerParams = {
const _restHandlerOptions: AHRestHandlerConfig = {
name: restHandlerOptions.name,
method: restHandlerOptions.method,
callable: descriptor.value,
Expand Down
61 changes: 55 additions & 6 deletions src/framework/features/anthill.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
import { AHAwsContext } from "../models/aws/aws-context";
import { AHException } from "./anthill-exception";
import { AHCallable } from "../..";
import { AHAnthillConfig } from "../models/anthill-config";
import { AHAbstractHandler } from "../../core/abstract-handler";
import { AHCallable } from "../models/handler/callable";
import { AHCacheConfig } from "../models/cache-config";
import { AHHandlerOptions } from "../models/handler/handler-options";

export class Anthill {

private static defaultCacheConfig: AHCacheConfig = {
cachable: false,
ttl: 120,
maxCacheSize: 1000000,
};

private static handlerDefaultOptions: AHHandlerOptions = {
displayPerformanceMetrics: false,
};

private static instance: Anthill;
private static handlers: Array<AHAbstractHandler<any, any>>;
private handlers: Array<AHAbstractHandler<any, any>>;

// Shouldn't be set directly by user
_configuration: AHAnthillConfig;

private constructor() {
Anthill.handlers = [];
this.handlers = [];

// Default configuration if configure isn't called
this._configuration = {
restHandlerConfig: {
middlewares: [],
cacheConfig: Anthill.defaultCacheConfig,
options: Anthill.handlerDefaultOptions
},
lambdaHandlerConfig: {
options: Anthill.handlerDefaultOptions,
}
};
}

/**
Expand All @@ -23,18 +52,38 @@ export class Anthill {
return Anthill.instance;
}

/**
* Configure the anthill application
* @param anthillConfig The configuration object for configuring an anthill application
*/
configure(anthillConfig?: AHAnthillConfig): void {
anthillConfig = { ...anthillConfig }; // Will be an empty object even if anthillConfig is null

// Apply configuration on top of default configuration
this._configuration = {
restHandlerConfig: {
cacheConfig: { ...this._configuration.restHandlerConfig.cacheConfig, ...anthillConfig?.restHandlerConfig?.cacheConfig },
middlewares: anthillConfig?.restHandlerConfig?.middlewares || [],
options: { ...this._configuration.restHandlerConfig.options, ...anthillConfig?.restHandlerConfig?.options },
},
lambdaHandlerConfig: {
options: { ...this._configuration.lambdaHandlerConfig.options, ...anthillConfig?.lambdaHandlerConfig?.options },
}
};
}

/**
* Register a rest handler
* @param handler The handler to register
*/
registerHandler(handler: AHAbstractHandler<any, any>): void {
if (Anthill.handlers.map(h => h.getName()).includes(handler.getName())) {
if (this.handlers.map(h => h.getName()).includes(handler.getName())) {
throw new AHException(
`Duplicate handler with name ${handler.getName()}. Handler names must be unique within the application`,
);
}

Anthill.handlers.push(handler);
this.handlers.push(handler);
}

/**
Expand All @@ -45,7 +94,7 @@ export class Anthill {
const exportObject = {};

// Expose rest handlers
for (const handler of Anthill.handlers) {
for (const handler of this.handlers) {
// Export a method that call the handler
exportObject[handler.getName()] = async (event: any, context: AHAwsContext) =>
await handler.handleRequest(event, context);
Expand Down
7 changes: 5 additions & 2 deletions src/framework/features/handler/lambda-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { AHLogger } from "../logger";
import { AHTimeTracker } from "../time-tracker";
import { AHAbstractHandler } from "../../../core/abstract-handler";
import { AHAwsContext } from "../../models/aws/aws-context";
import { AHLambdaHandlerParams } from "../../models/handler/lambda-handler-params";
import { AHLambdaHandlerConfig } from "../../models/handler/lambda-handler-config";
import { Anthill } from "../anthill";


export class AHLambdaHandler<T, U> extends AHAbstractHandler<T, U> {

constructor(params: AHLambdaHandlerParams<T, U>) {
constructor(params: AHLambdaHandlerConfig<T, U>) {
// Apply lambdaHandlerConfig options
params.options = { ...Anthill.getInstance()._configuration.lambdaHandlerConfig.options, ...params.options };
super(params);
}

Expand Down
43 changes: 16 additions & 27 deletions src/framework/features/handler/rest-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,23 @@ import { AHRestMethodEnum } from "../../models/enums/rest-method-enum";
import { AHHttpResponse } from "../http-response";
import { AHLogger } from "../logger";
import { AHMiddleware } from "../middleware/middleware";
import { AHRestHandlerParams } from "../../models/handler/rest-handler-params";
import { AHRestHandlerConfig } from "../../models/handler/rest-handler-config";
import { AHTimeTracker } from "../time-tracker";
import { AHAbstractHandler } from "../../../core/abstract-handler";
import { AHAwsContext } from "../../models/aws/aws-context";
import { Anthill } from "../anthill";


export class AHRestHandler extends AHAbstractHandler<AHAwsEvent, AHHttpResponse> {

private static defaultCacheConfig: AHCacheConfig = {
cachable: false,
ttl: 120,
maxCacheSize: 1000000,
};

private cacheConfig: AHCacheConfig = Anthill.getInstance()._configuration.restHandlerConfig.cacheConfig;
private httpCache: AHHttpRequestCache = new AHHttpRequestCache();
private method: AHRestMethodEnum;
private middlewares: Array<AHMiddleware<any>> = [];
private cacheConfig: AHCacheConfig = AHRestHandler.defaultCacheConfig;
private httpCache: AHHttpRequestCache = new AHHttpRequestCache();

constructor(params: AHRestHandlerParams) {
constructor(params: AHRestHandlerConfig) {
// Apply restHandlerConfig options
params.options = { ...Anthill.getInstance()._configuration.restHandlerConfig.options, ...params.options };
super(params);

this.method = params.method;
Expand All @@ -34,17 +31,6 @@ export class AHRestHandler extends AHAbstractHandler<AHAwsEvent, AHHttpResponse>
if (params.cacheConfig) { this.cacheConfig = { ...this.cacheConfig, ...params.cacheConfig }; }
}

/**
* Set the default cache config
* @param cacheConfig The cache config (override) to set by default
*/
static setDefaultCacheConfig(cacheConfig: AHCacheConfig): void {
AHRestHandler.defaultCacheConfig = {
...AHRestHandler.defaultCacheConfig,
...cacheConfig
}
}

/**
* Set a new cache config for the handler
* @param cacheConfig The cache config to be set
Expand Down Expand Up @@ -97,11 +83,14 @@ export class AHRestHandler extends AHAbstractHandler<AHAwsEvent, AHHttpResponse>

tracker.startSegment(`middleware-runBefore`);

// Add anthill configuration middlewares to middleware list
const middlewares = [...Anthill.getInstance()._configuration.restHandlerConfig.middlewares, ...this.middlewares];

// Run all the middlewares runBefore one by one
for (let i = 0; i < this.middlewares.length; i++) {
AHLogger.getInstance().debug(`Running runBefore for middleware ${i + 1} of ${this.middlewares.length}`);
for (let i = 0; i < middlewares.length; i++) {
AHLogger.getInstance().debug(`Running runBefore for middleware ${i + 1} of ${middlewares.length}`);

const middlewareResult = await this.middlewares[i].runBefore(ev, context);
const middlewareResult = await middlewares[i].runBefore(ev, context);

// The middleware returned an AHAwsEvent
if (middlewareResult instanceof AHAwsEvent) {
Expand Down Expand Up @@ -163,9 +152,9 @@ export class AHRestHandler extends AHAbstractHandler<AHAwsEvent, AHHttpResponse>
tracker.startSegment(`middleware-runAfter`);

// Run all the middlewares runAfter one by one
for (let i = 0; i < this.middlewares.length; i++) {
AHLogger.getInstance().debug(`Running runAfter for middleware ${i + 1} of ${this.middlewares.length}`);
response = await this.middlewares[i].runAfter(response, ev, context);
for (let i = 0; i < middlewares.length; i++) {
AHLogger.getInstance().debug(`Running runAfter for middleware ${i + 1} of ${middlewares.length}`);
response = await middlewares[i].runAfter(response, ev, context);
}

tracker.stopSegment(`middleware-runAfter`);
Expand Down
49 changes: 29 additions & 20 deletions src/framework/helpers/object-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,41 @@ const ECMA_SIZES = {
export class AHObjectHelper {
/**
* Compare an object with another and returns if the objects are equivalent
* @param a First object to compare
* @param b Second object to compare
* @returns True is a object is equivalent to b object, false otherwise
* @param obj1 First object to compare
* @param obj2 Second object to compare
* @returns True is obj1 object is equivalent to obj2 object, false otherwise
*/
static isEquivalentObj(a: any, b: any) {
if (!a && !b) {
return true;
} else if (a && b) {
// Create arrays of property names
const aProps = Object.keys(a);
const bProps = Object.keys(b);
static isEquivalentObj(obj1: any, obj2: any): boolean {
if (!obj1 || !obj2) {
return obj1 === obj2;
}

// If number of properties is different, objects are not equivalent
if (aProps.length !== bProps.length) {
return false;
}
const props1 = Object.getOwnPropertyNames(obj1);
const props2 = Object.getOwnPropertyNames(obj2);

if (props1.length != props2.length) {
return false;
}

// Verify if all the keys are in both aProps and bProps
// && If values of same property are not equal, objects are not equivalent
if (!aProps.every((p) => bProps.includes(p) && a[p] === b[p])) {
for (let i = 0; i < props1.length; i++) {
let val1 = obj1[props1[i]];
let val2 = obj2[props1[i]];
let isObjects = AHObjectHelper.isObject(val1) && AHObjectHelper.isObject(val2);

if ((isObjects && !AHObjectHelper.isEquivalentObj(val1, val2)) || (!isObjects && val1 !== val2)) {
return false;
}
}
return true;
}

// Objects are equivalent
return true;
} else return false;
/**
* Check that a given value is an object
* @param object The given value
* @returns True if the given value is an object, false otherwise
*/
static isObject(object: any): boolean {
return object != null && typeof object === "object";
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/framework/models/anthill-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AHLambdaHandlerOverridableConfig } from "./handler/lambda-handler-overridable-config";
import { AHRestHandlerOverridableConfig } from "./handler/rest-handler-overridable-config";


export interface AHAnthillConfig {
restHandlerConfig?: AHRestHandlerOverridableConfig;
lambdaHandlerConfig?: AHLambdaHandlerOverridableConfig;
}
6 changes: 6 additions & 0 deletions src/framework/models/handler/lambda-handler-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AHAbstractHandlerConfig } from "../../../core/models/abstract-handler-config";


export interface AHLambdaHandlerConfig<T, U> extends AHAbstractHandlerConfig<T, U> {
/* no more config than what is in the extended class for the moment */
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AHAbstractHandlerOverridableConfig } from "../../../core/models/abstract-handler-overridable-config";


export interface AHLambdaHandlerOverridableConfig extends AHAbstractHandlerOverridableConfig {
/* no more config than what is in the extended class for the moment */
}
4 changes: 0 additions & 4 deletions src/framework/models/handler/lambda-handler-params.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import { AHCacheConfig } from "../cache-config";
import { AHMiddleware } from "../../features/middleware/middleware";
import { AHRestMethodEnum } from "../enums/rest-method-enum";
import { AHAbstractHandlerParams } from "../../../core/models/abstract-handler-params";
import { AHAbstractHandlerConfig } from "../../../core/models/abstract-handler-config";
import { AHAwsEvent } from "../aws/event/aws-event";
import { AHHttpResponse } from "../../features/http-response";


export interface AHRestHandlerParams extends AHAbstractHandlerParams<AHAwsEvent, AHHttpResponse> {
export interface AHRestHandlerConfig extends AHAbstractHandlerConfig<AHAwsEvent, AHHttpResponse> {
method: AHRestMethodEnum;
middlewares?: Array<AHMiddleware<any>>;
cacheConfig?: AHCacheConfig;
Expand Down
11 changes: 11 additions & 0 deletions src/framework/models/handler/rest-handler-overridable-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

import { AHCacheConfig } from "../cache-config";
import { AHMiddleware } from "../../features/middleware/middleware";
import { AHHandlerOptions } from "./handler-options";


export interface AHRestHandlerOverridableConfig {
middlewares?: Array<AHMiddleware<any>>;
cacheConfig?: AHCacheConfig;
options?: AHHandlerOptions;
}
Loading

0 comments on commit b555cd6

Please sign in to comment.