Skip to content

Commit

Permalink
Merge pull request #14 from mojaloop/feat/code-quality-plugin
Browse files Browse the repository at this point in the history
feat: added logging plugin; removed ts-ignores
  • Loading branch information
geka-evk authored Mar 21, 2024
2 parents 66fbbc8 + 6b1e983 commit 95823bf
Show file tree
Hide file tree
Showing 15 changed files with 179 additions and 232 deletions.
14 changes: 14 additions & 0 deletions src/api-spec/common.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
components:
schemas:
IdType:
type: string
enum:
- MSISDN
- ACCOUNT_NO
- EMAIL
- PERSONAL_ID
- BUSINESS
- DEVICE
- ACCOUNT_ID
- IBAN
- ALIAS
5 changes: 2 additions & 3 deletions src/api-spec/payment-token-adapter-spec-externalPortal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ paths:
type: string
example: '256781466410'
payeeIdType:
type: string
example: MSISDN
$ref: 'common.yaml#/components/schemas/IdType'
paymentToken:
type: string
example: CM2903E3E0WE
Expand All @@ -40,7 +39,7 @@ paths:
payeeIdType: MSISDN
paymentToken: CM2903E3E0WE
responses:
'200':
'201':
description: 'Successful storage of the Payment Token Mapping'


Expand Down
12 changes: 1 addition & 11 deletions src/api-spec/payment-token-adapter-spec-sdk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,7 @@ paths:
in: path
required: true
schema:
type: string
# enum:
# - MSISDN
# - ACCOUNT_NO
# - EMAIL
# - PERSONAL_ID
# - BUSINESS
# - DEVICE
# - ACCOUNT_ID
# - IBAN
# - ALIAS
$ref: 'common.yaml#/components/schemas/IdType'
- name: ID
in: path
required: true
Expand Down
9 changes: 3 additions & 6 deletions src/domain/interfaces/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@

'use strict';

import { ServerRoute } from 'hapi';
import { ReqRefDefaults } from '@hapi/hapi';
import { ServerRoute } from '@hapi/hapi';
import {SDKSchemeAdapter} from '@mojaloop/api-snippets';

export interface IPaymentTokenMapping {
Expand Down Expand Up @@ -91,17 +90,15 @@ export enum PayeeIdType {
}

export interface IRoutes {
//@ts-expect-error ReqRefDefaults not found
getRoutes(): ServerRoute<ReqRefDefaults>[];
getRoutes(): ServerRoute[];
}

export type TQuoteRequest = SDKSchemeAdapter.V2_0_0.Backend.Types.quoteRequest;

export type TTransferRequest = SDKSchemeAdapter.V2_0_0.Backend.Types.transferRequest;

export interface ISDKBackendClient {
lookupPartyInfo(idType: string, id: string): Promise<Payee | null>;
// todo: make idType as PayeeIdType
lookupPartyInfo(idType: PayeeIdType, id: string): Promise<Payee | null>;
calculateQuote(payload: TQuoteRequest): Promise<Quote | null>;
createTransfer(payload: TTransferRequest): Promise<Transfer | null>; // or performTransfer?
}
Expand Down
17 changes: 8 additions & 9 deletions src/domain/sdkAggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,29 @@ export class SDKAggregate {
return true;
}


// todo: avoid returning string
async getParties(ID: string, Type: string): Promise<Payee | string> {
if (Type == PayeeIdType.ALIAS) {
const tokenMapping = await this.aliasMappingRepo.getMapping(ID);
async getParties(idType: PayeeIdType, id: string): Promise<Payee | string> {
if (idType == PayeeIdType.ALIAS) {
const tokenMapping = await this.aliasMappingRepo.getMapping(id);
if (!tokenMapping) {
this.logger.warn('no tokenMapping', { ID, Type });
this.logger.warn('no tokenMapping', { id, idType });
return '';
}

const res = await this.sdkClient.lookupPartyInfo(tokenMapping.payeeIdType, tokenMapping.payeeId);

if (!res) {
this.logger.error('no lookupPartyInfo results', { ID, Type });
this.logger.error('no lookupPartyInfo results', { id, idType });
return 'Http Request Error';
// todo: improve error handling
}
res.idValue = ID;
res.idType = Type;
res.idValue = id;
res.idType = idType;

return res;
}

const res = await this.sdkClient.lookupPartyInfo(Type, ID);
const res = await this.sdkClient.lookupPartyInfo(idType, id);
if (!res) {
return 'Http Request Error';
}
Expand Down
1 change: 1 addition & 0 deletions src/infra/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './httpClient';
export * from './Logger';
export * from './repo';
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

'use strict';

import { ITokenMappingStorageRepo, IPaymentTokenMapping } from '../domain/interfaces';
import { ITokenMappingStorageRepo, IPaymentTokenMapping } from '../../domain';

export class MemoryTokenMappingStorageRepo implements ITokenMappingStorageRepo {
private mappings = new Map<string, IPaymentTokenMapping>();
Expand Down
2 changes: 1 addition & 1 deletion src/implementations/index.ts → src/infra/repo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@

'use strict';

export * from './implementations';
export * from './MemoryTokenMappingStorageRepo';
2 changes: 2 additions & 0 deletions src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './loggingPlugin';
export * from './types';
47 changes: 47 additions & 0 deletions src/plugins/loggingPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Plugin, Request, ResponseToolkit } from '@hapi/hapi';
import { ILogger } from '../domain';
import { ReqAppState } from './types';

type PluginOptions = {
logger: ILogger;
}

export const loggingPlugin: Plugin<PluginOptions> = {
name: 'loggingPlugin',
version: '1.0.0',
once: true,
register: async (server, options) => {
const { logger } = options;

server.ext({
type: 'onPreHandler',
method: (req: Request, h: ResponseToolkit) => {
const { path, method, info} = req;
const { id, remoteAddress, received} = info;
const context = {
id, remoteAddress, path, method, received,
};
Object.assign(req.app, { context });
logger.info(`[--> req] ${method.toUpperCase()} ${path}`, context);

return h.continue;
},
});

server.ext({
type: 'onPreResponse',
method: (req: Request, h: ResponseToolkit) => {
const { context } = req.app as ReqAppState; // todo: think, how to specify req.app type
const { path, method, response } = req;
const responseTimeSec = ((Date.now() - context.received) / 1000).toFixed(3);

const statusCode = response instanceof Error
? response.output.statusCode
: response.statusCode;
logger.info(`[<-- ${statusCode}][${responseTimeSec} s] ${method.toUpperCase()} ${path}`, context);

return h.continue;
},
});
},
};
9 changes: 9 additions & 0 deletions src/plugins/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type ReqAppState = {
context: {
id: string;
remoteAddress: string;
path: string;
method: string;
received: number;
};
};
103 changes: 24 additions & 79 deletions src/token-adapter-svc/externalPortalRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@

'use strict';

import OpenAPIBackend from 'openapi-backend';
import { ReqRefDefaults } from '@hapi/hapi';
import { ServerRoute } from 'hapi';
import { ExternalPortalAggregate, IRoutes, PayeeIdType, ILogger } from '../domain';
import OpenAPIBackend, { Context } from 'openapi-backend';
import { Request, ResponseToolkit, ServerRoute, } from '@hapi/hapi';
import { ExternalPortalAggregate, IRoutes, ILogger, IPaymentTokenMapping } from '../domain';

const API_SPEC_FILE = './src/api-spec/payment-token-adapter-spec-externalPortal.yaml';

export class ExternalPortalRoutes implements IRoutes {
//@ts-expect-error ReqRefDefaults not found
private readonly routes: ServerRoute<ReqRefDefaults>[] = [];
private readonly routes: ServerRoute[] = [];
private readonly coreConnectorAggregate: ExternalPortalAggregate;
private readonly logger: ILogger;

Expand All @@ -44,11 +44,11 @@ export class ExternalPortalRoutes implements IRoutes {

// initialise openapi backend with validation
const api = new OpenAPIBackend({
definition: './src/api-spec/payment-token-adapter-spec-externalPortal.yaml',
definition: API_SPEC_FILE,
handlers: {
registerToken: this.registerTokenMapping.bind(this),
validationFail: async (context, req, h) => h.response({ err: context.validation.errors }).code(400),
notFound: async (context, req, h) => h.response({ context, err: 'not found' }).code(404),
validationFail: async (context, req, h) => h.response({ error: context.validation.errors }).code(400),
notFound: async (context, req, h) => h.response({ error: 'Not found' }).code(404),
},
});

Expand All @@ -57,83 +57,28 @@ export class ExternalPortalRoutes implements IRoutes {
this.routes.push({
method: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
path: '/{path*}',
//@ts-expect-error h has no type
handler: (req, h) =>
api.handleRequest(
{
method: req.method,
path: req.path,
body: req.payload,
query: req.query,
headers: req.headers,
},
req,
h,
),
});

// get Token - this route was implemented for convenience.
// it was not part of the api specification for external portal server so it does not need validation
this.getTokenMapping = this.getTokenMapping.bind(this);
this.routes.push({
method: 'GET',
path: '/parties/{payeeIdType}/{payeeId}',
handler: this.getTokenMapping,
handler: (req, h) => api.handleRequest( {
method: req.method,
path: req.path,
body: req.payload,
query: req.query,
headers: req.headers,
}, req, h),
});
}

getRoutes() {
getRoutes(): ServerRoute[] {
return this.routes;
}

//@ts-expect-error h has no type
private async registerTokenMapping(context, request: Hapi.Request, h: Hapi.ResponseToolkit) {
this.logger.info('Received registerTokenMapping request');
const payload = request.payload;
private async registerTokenMapping(context: Context, req: Request, h: ResponseToolkit) {
const { paymentToken, payeeIdType, payeeId} = req.payload as IPaymentTokenMapping;

await this.coreConnectorAggregate.createMapping({
paymentToken: payload.paymentToken,
// todo: refactor it!
payeeIdType:
payload.payeeIdType == 'IBAN'
? PayeeIdType.IBAN
: payload.payeeIdType == 'MSISDN'
? PayeeIdType.MSISDN
: payload.payeeIdType == 'ACCOUNT_NO'
? PayeeIdType.ACCOUNT_NO
: payload.payeeIdType == 'EMAIL'
? PayeeIdType.EMAIL
: payload.payeeIdType == 'PERSONAL_ID'
? PayeeIdType.PERSONAL_ID
: payload.payeeIdType == 'BUSINESS'
? PayeeIdType.BUSINESS
: payload.payeeIdType == 'DEVICE'
? PayeeIdType.DEVICE
: PayeeIdType.ACCOUNT_ID,
payeeId: payload.payeeId,
paymentToken,
payeeIdType,
payeeId,
});
return h.response('OK').code(200);
}

//@ts-expect-error h has no type
private async getTokenMapping(request, h) {
// todo: remove any testing functionality !!
// this function is for testing not part of the api spec
const params = request.params;

if (!params.payeeId) {
h.response('Bad Request. Please specify Payment Token').code(400);
}

const tokenMapping = await this.coreConnectorAggregate.getMapping(params.payeeId);
if (!tokenMapping) {
return h
.response({
statusCode: '4001',
message: 'Party Not Found',
})
.code(404);
} else {
return h.response(tokenMapping).code(200);
}
return h.response('OK').code(201);
}
}
Loading

0 comments on commit 95823bf

Please sign in to comment.