Skip to content
Merged
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
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './validator';
export * from './config/service';
export * from './foundation';
export { registerAs } from '@nestjs/config';
export * from './reflections';
2 changes: 2 additions & 0 deletions lib/reflections/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './reflector';
export * from './setMetadata';
117 changes: 117 additions & 0 deletions lib/reflections/reflector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* eslint-disable @typescript-eslint/ban-types */
import 'reflect-metadata';
import { ulid } from 'ulid';
import { Obj } from '../utils';

/**
* Reflector is a class to easily fetch metadata from a class and request handler method
* at the runtime.
*/
export class Reflector {
constructor(
private cls: any,
private handler?: Function,
) {}

/**
* Gets the metadata from the `handler` method and `handler's class`,
* if the metadata from the handler is not empty, then return the handler data.
* Otherwise, return the class' metadata.
*
* @param keyOrDecorator
* @returns
*/
allAndOverride<T = any>(keyOrDecorator: string | Object): T {
const decoratorKey =
typeof keyOrDecorator === 'function'
? keyOrDecorator['KEY']
: keyOrDecorator;

const dataFromHandler = Reflect.getMetadata(decoratorKey, this.handler);
const dataFromClass = Reflect.getMetadata(decoratorKey, this.cls);
return dataFromHandler || dataFromClass;
}

/**
* Gets the metadata from the `handler` method and `handler's class`
* and merges it. It can currently merge the array and objects.
*
* @param keyOrDecorator string | Object
* @returns
*/
allAndMerge<T = any>(keyOrDecorator: string | Object): T {
const decoratorKey =
typeof keyOrDecorator === 'function'
? keyOrDecorator['KEY']
: keyOrDecorator;

const dataFromHandler = Reflect.getMetadata(decoratorKey, this.handler);
const dataFromClass = Reflect.getMetadata(decoratorKey, this.cls);

if (Array.isArray(dataFromHandler) && Array.isArray(dataFromClass)) {
return dataFromHandler.concat(dataFromClass) as T;
}

if (Obj.isObj(dataFromHandler) && Obj.isObj(dataFromClass)) {
return { ...dataFromClass, ...dataFromHandler } as T;
}

return [dataFromClass, dataFromHandler] as T;
}

/**
* Gets the metadata from the `handler's class` and returns it.
* @param keyOrDecorator
* @param defaultValue
* @returns
*/
getFromClass<T = any>(keyOrDecorator: string | Object, defaultValue?: T): T {
const key =
typeof keyOrDecorator === 'function'
? keyOrDecorator['KEY']
: keyOrDecorator;

const data = Reflect.getMetadata(key, this.cls);
return data || defaultValue;
}

/**
* Gets the metadata from the `handler` method and returns it.
* @param keyOrDecorator string
* @param defaultValue T
* @returns
*/
getFromMethod<T = any>(keyOrDecorator: string | Object, defaultValue?: T): T {
const key =
typeof keyOrDecorator === 'function'
? keyOrDecorator['KEY']
: keyOrDecorator;

const data = Reflect.getMetadata(key, this.handler);
return data || defaultValue;
}

/**
* A shorthand to create a decorator for quickly setting metadata
* on controllers and methods.
* @param decoratorKey string
* @returns
*/
public static createDecorator<TParam>(decoratorKey?: string) {
const metadataKey = decoratorKey || ulid();
const decoratorFn =
(metadataValue: TParam) =>
(target: object | Function, key?: string | symbol, descriptor?: any) => {
if (descriptor) {
Reflect.defineMetadata(metadataKey, metadataValue, descriptor.value);
return descriptor;
}

Reflect.defineMetadata(metadataKey, metadataValue, target);
return target;
};

decoratorFn.KEY = metadataKey;
return decoratorFn;
}
}
22 changes: 22 additions & 0 deletions lib/reflections/setMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* eslint-disable @typescript-eslint/ban-types */
export const SetMetadata = <K = string, V = any>(
metadataKey: K,
metadataValue: V,
) => {
const decoratorFn = (
target: object | Function,
key?: string | symbol,
descriptor?: any,
) => {
if (descriptor) {
Reflect.defineMetadata(metadataKey, metadataValue, descriptor.value);
return descriptor;
}

Reflect.defineMetadata(metadataKey, metadataValue, target);
return target;
};

decoratorFn.KEY = metadataKey;
return decoratorFn;
};
29 changes: 22 additions & 7 deletions lib/rest/foundation/guards/baseGuard.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import { CanActivate, ExecutionContext, Inject } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { Request, Response } from '../interface';
import { Reflector } from '../../../reflections';

export abstract class IntentGuard implements CanActivate {
@Inject()
private reflector: Reflector;

async canActivate(context: ExecutionContext): Promise<boolean> {
/**
* Get Express Request Object
*/
const expressRequest = context.switchToHttp().getRequest();
return this.boot(expressRequest, expressRequest);

/**
* Get Express Response Object
*/
const expressResponse = context.switchToHttp().getResponse();

/**
* Initialise a new Reflector class.
*/
const reflector = new Reflector(context.getClass(), context.getHandler());

return this.guard(expressRequest, expressResponse, reflector);
}

abstract boot(req: Request, res: Response): boolean | Promise<boolean>;
abstract guard(
req: Request,
res: Response,
reflector: Reflector,
): boolean | Promise<boolean>;
}