Skip to content

Commit

Permalink
Fixes handling of non-static controller members #4
Browse files Browse the repository at this point in the history
  • Loading branch information
Jaakko Heusala committed Jan 20, 2024
1 parent 2a6df2c commit ce5243b
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 26 deletions.
20 changes: 19 additions & 1 deletion request/utils/RequestControllerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class RequestControllerUtils {
}

/**
* Find the controller from arbitrary variable.
* Find the controller's static controller from arbitrary variable.
*
* If provided with a class directly, will return the class itself.
*
Expand All @@ -181,6 +181,24 @@ export class RequestControllerUtils {
return undefined;
}

/**
* Find the controller's instance controller from arbitrary variable.
*
* If provided with a class directly, will return the class itself.
*
* If provided with an instance of a class, will return the class instead.
*
* Otherwise, will return `undefined`.
*
* @param target
*/
public static findInstanceController (target : any) : RequestController | undefined {
if ( isObject(target) && isRequestController(target) ) {
return target;
}
return undefined;
}

/**
* This method is used to configure how ModelAttribute is mapped to the
* method's parameter
Expand Down
94 changes: 69 additions & 25 deletions requestServer/RequestRouterImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// Copyright (c) 2020-2021. Sendanor <info@sendanor.fi>. All rights reserved.
// Copyright (c) 2020-2021. Sendanor <info@sendanor.fi>. All rights reserved.

import { LogUtils } from "../LogUtils";
import { getRequestControllerMappingObject, isRequestController, RequestController } from "../request/types/RequestController";
import { parseRequestMethod, RequestMethod } from "../request/types/RequestMethod";
import { filter } from "../functions/filter";
import { forEach } from "../functions/forEach";
import { has } from "../functions/has";
import { RequestControllerUtils } from "../request/utils/RequestControllerUtils";
import { isNull } from "../types/Null";
import { map } from "../functions/map";
import { trim } from "../functions/trim";
Expand Down Expand Up @@ -63,7 +65,18 @@ export class RequestRouterImpl implements RequestRouter {
LOG.setLogLevel(level);
}

private readonly _controllers : RequestController[];
/**
* These are references to controller's static properties
* @private
*/
private readonly _staticControllers : RequestController[];

/**
* These are references to controller's instance properties
* @private
*/
private readonly _instanceControllers : RequestController[];

private _routes : BaseRoutes | undefined;
private _modelAttributeNames : Map<RequestController, ModelAttributeProperty[]> | undefined;
private _requestMappings : readonly RequestControllerMappingObject[] | undefined;
Expand All @@ -76,8 +89,9 @@ export class RequestRouterImpl implements RequestRouter {
private readonly _asyncSynchronizer : Map<string, AsyncSynchronizer>;

protected constructor () {
this._controllers = [];
this._asyncSynchronizer = new Map<string, AsyncSynchronizer>();
this._staticControllers = [];
this._instanceControllers = [];
this._asyncSynchronizer = new Map<string, AsyncSynchronizer>();
this._routes = undefined;
this._requestMappings = undefined;
this._modelAttributeNames = undefined;
Expand All @@ -101,7 +115,17 @@ export class RequestRouterImpl implements RequestRouter {
}

public attachController (controller : RequestController) : void {
this._controllers.push(controller);
const staticController = RequestControllerUtils.findController(controller);
if (!staticController) {
throw new TypeError(`Controller invalid: ${LogUtils.stringifyValue(controller)}`);
}
if (staticController !== controller) {
this._staticControllers.push(staticController);
this._instanceControllers.push(controller);
} else {
this._staticControllers.push(staticController);
this._instanceControllers.push(null);
}
this._routes = undefined;
}

Expand All @@ -111,19 +135,14 @@ export class RequestRouterImpl implements RequestRouter {
parseRequestBody : ParseRequestBodyCallback | undefined = undefined,
requestHeaders : Headers
) : Promise<ResponseEntity<any>> {

try {

const method : RequestMethod = parseRequestMethod(methodString);

const {
pathName,
queryParams
} : RequestContext = parseRequestContextFromPath(urlString);
LOG.debug(`handleRequest: method="${method}", pathName="${pathName}", queryParams=`, queryParams);

const requestPathName : string | undefined = pathName;

const requestQueryParams : RequestQueryParameters = queryParams ?? {};
// LOG.debug('requestQueryParams: ', requestQueryParams);

Expand Down Expand Up @@ -174,30 +193,48 @@ export class RequestRouterImpl implements RequestRouter {

const requestModelAttributes = new Map<RequestController, Map<string, any>>();

function selectController (
staticController : any,
instanceController : any | undefined,
propertyName : string,
) : any | undefined {
const instanceHasProperty : boolean = instanceController && !!instanceController[propertyName];
const staticHasProperty : boolean = staticController && !!staticController[propertyName];
if ( instanceController && staticHasProperty ) {
LOG.warn(`Warning! Identical name for controller's static and instance properties not yet supported. You should use unique names. Using static member for backward compatibility.`);
}
if ( staticHasProperty ) {
return staticController;
} else if ( instanceHasProperty ) {
return instanceController;
}
return undefined;
}

// Handle requests using controllers
await reduce(routes, async (previousPromise, route: RequestRouterMappingPropertyObject) => {

const routeController : any = route.controller;
const staticController : any = route.controller;
const routePropertyName : string = route.propertyName;
const routePropertyParams : readonly (RequestParamObject | null)[] = route.propertyParams;

const routeIndex : number = this._controllers.indexOf(routeController);
const routeIndex : number = staticController ? this._staticControllers.indexOf(staticController) : -2;
const instanceController : any | undefined = (routeIndex >= 0 ? this._instanceControllers[routeIndex] : undefined) ?? undefined;

await previousPromise;

if ( this._modelAttributeNames && this._modelAttributeNames.has(routeController) ) {
if ( this._modelAttributeNames && this._modelAttributeNames.has(staticController) ) {

LOG.debug(`Populating attributes for property "${routePropertyName}"`);

const modelAttributeValues : Map<string, any> = RequestRouterImpl._getOrCreateRequestModelAttributesForController(requestModelAttributes, routeController);
const modelAttributeValues : Map<string, any> = RequestRouterImpl._getOrCreateRequestModelAttributesForController(requestModelAttributes, staticController);

const routeAttributeNames : string[] = map(
filter(routePropertyParams, (item : any) : item is RequestModelAttributeParamObject => isRequestModelAttributeParamObject(item)),
(item : RequestModelAttributeParamObject) : string => item.attributeName
);
LOG.debug('route attributeNames: ', routeAttributeNames);

const allModelAttributeNamesForRouteController = this._modelAttributeNames.get(routeController);
const allModelAttributeNamesForRouteController = this._modelAttributeNames.get(staticController);
LOG.debug('all attributeNamePairs: ', allModelAttributeNamesForRouteController);

const attributeNamePairs : ModelAttributeProperty[] = filter(
Expand Down Expand Up @@ -225,9 +262,13 @@ export class RequestRouterImpl implements RequestRouter {
modelAttributeValues
);

const stepResult : any = await routeController[propertyName](...stepParams);

modelAttributeValues.set(attributeName, stepResult);
const controller = selectController( staticController, instanceController, propertyName );
if (controller) {
const stepResult : any = await controller[propertyName](...stepParams);
modelAttributeValues.set(attributeName, stepResult);
} else {
LOG.warn(`Warning! No property found in the controller for modelAttributeValues: `, propertyName);
}

}, Promise.resolve());

Expand All @@ -239,12 +280,15 @@ export class RequestRouterImpl implements RequestRouter {
routePropertyParams,
requestHeaders,
pathVariables,
requestModelAttributes.get(routeController) ?? new Map<string, any>()
requestModelAttributes.get(staticController) ?? new Map<string, any>()
);
LOG.debug('handleRequest: stepParams 1: ', stepParams);

if (!has(routeController, routePropertyName)) {
LOG.warn(`Warning! No property by name "${routePropertyName}" found in the controller`);
const controller = selectController( staticController, instanceController, routePropertyName );
if ( !controller ) {
LOG.warn(`Warning! No property found in the controller:`, routePropertyName);
LOG.debug(`staticController =`, keys(staticController) );
LOG.debug(`instanceController =`, keys(instanceController) );
responseEntity = ResponseEntity.notFound<JsonObject>().body({error:"404 - Not Found", code: 404});
return;
}
Expand All @@ -270,12 +314,12 @@ export class RequestRouterImpl implements RequestRouter {

stepResult = await synchronizer.run(async () : Promise<any> => {
LOG.debug(`handleRequest: Calling route property by name "${routePropertyName}"`);
return await routeController[routePropertyName](...stepParams);
return await controller[routePropertyName](...stepParams);
});

} else {
LOG.debug(`handleRequest: Calling route property by name "${routePropertyName}"`);
stepResult = await routeController[routePropertyName](...stepParams);
stepResult = await controller[routePropertyName](...stepParams);
}

if (isRequestStatus(stepResult)) {
Expand Down Expand Up @@ -521,12 +565,12 @@ export class RequestRouterImpl implements RequestRouter {
}

private _getRequestMappings () : RequestControllerMappingObject[] {
if (this._controllers.length === 0) {
if (this._staticControllers.length === 0) {
return [];
}
return filter(
map(
this._controllers,
this._staticControllers,
(controller : RequestController) => getRequestControllerMappingObject(controller)
),
(item : RequestControllerMappingObject | undefined) : boolean => !!item
Expand Down

0 comments on commit ce5243b

Please sign in to comment.