Skip to content
Snippets Groups Projects
Unverified Commit 02ec4b11 authored by Ajay Nishad's avatar Ajay Nishad Committed by GitHub
Browse files

Merge pull request #10 from beckn/feat/ajay/create_arch

Add new arch
parents 625bd652 cabf05e0
No related branches found
No related tags found
No related merge requests found
Showing with 239 additions and 310 deletions
PORT=3009 APP_NAME="Generic Client Layer"
BAP_ID=ps-bap-network.becknprotocol.io APP_ENV=local
BAP_URI=https://ps-bap-network.becknprotocol.io APP_KEY=
BAP_CLIENT_URI=https://ps-bap-client.becknprotocol.io APP_DEBUG=true
CITY_NAME=Bangalore APP_PORT=3002
CITY_CODE=std:080 APP_URL=http://localhost
COUNTRY_NAME=India
COUNTRY_CODE=IND LOG_CHANNEL=stack
\ No newline at end of file LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
PS_BASE_URI=https://ps-bap-client.becknprotocol.io
PS_BAP_ID=ps-bap-network.becknprotocol.io
PS_BAP_URI=https://ps-bap-network.becknprotocol.io
PS_CITY_NAME=Bangalore
PS_CITY_CODE=std:080
PS_COUNTRY_NAME=India
PS_COUNTRY_CODE=IND
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_PORT=3002
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
PS_BASE_URI=https://ps-bap-client.becknprotocol.io
PS_BAP_ID=ps-bap-network.becknprotocol.io
PS_BAP_URI=https://ps-bap-network.becknprotocol.io
PS_CITY_NAME=Bangalore
PS_CITY_CODE=std:080
PS_COUNTRY_NAME=India
PS_COUNTRY_CODE=IND
...@@ -3,3 +3,4 @@ dist/ ...@@ -3,3 +3,4 @@ dist/
package-lock.json package-lock.json
.DS_Store .DS_Store
coverage/ coverage/
logs/
{
"domain": domain,
"bpp_id": bpp_id,
"bpp_uri": bpp_uri,
"bap_id": $env.PS_BAP_ID,
"action": $action,
"bap_uri": $env.PS_BAP_URI,
"version": "1.1.0",
"transaction_id": transaction_id ? transaction_id : $uuid(),
"message_id": message_id ? message_id : $uuid(),
"location": {
"country": {
"name": $env.PS_COUNTRY_NAME,
"code": $env.PS_COUNTRY_CODE
},
"city": {
"name": $env.PS_CITY_NAME,
"code": $env.PS_CITY_CODE
}
},
"ttl": "PT10M",
"key": key,
"timestamp": $moment().toISOString()
}
$.data.message.orders.{
"context": $context(%.%.context, $action),
"message": {
"order": $
}
}[]
$.responses.{
"context":context,
"message": {
"order": {
"type": message.order.type,
"provider": {
"id": message.order.provider.id,
"name": message.order.provider.descriptor.name,
"short_desc": message.order.provider.descriptor.short_desc,
"long_desc": message.order.provider.descriptor.long_desc,
"rating": message.order.provider.rating,
"images": message.order.provider.descriptor.images.{
"url": url,
"size_type": size_type
},
"media": message.order.provider.descriptor.media.{
"url": url
}
},
"items": message.order.items.{
"id": id,
"name": descriptor.name,
"short_desc": descriptor.short_desc,
"long_desc": descriptor.long_desc,
"price": price,
"rating": rating,
"rateable": rateable,
"time": time,
"quantity": quantity,
"categories": $map(
$filter(%.provider.categories, function($category) { $boolean($category.id in category_ids)}),
function($category) {
{ "id": $category.id, "name": $category.descriptor.name, "code": $category.descriptor.code }
}
)[],
"locations": $map(
$filter(%.provider.locations, function($location) { $boolean($location.id in location_ids)}),
function($location) {
{
"id": $location.id,
"city": $location.city.name,
"state": $location.state.name,
"country": $location.country.name
}
}
)[],
"tags": tags.{
"code": descriptor.code,
"name": descriptor.name,
"display": display,
"list": list.{ "code": descriptor.code, "name": descriptor.name, "value": value }[]
}[]
},
"fulfillments": message.order.fulfillments,
"quote": message.order.quote,
"billing": message.order.billing,
"payments": message.order.payments,
"cancellation_terms": message.order.cancellation_terms
}
}
}[]
{ $.responses.{
"data": responses.{ "context": context,
"message": {
"order": { "order": {
"type": message.order.type, "type": message.order.type,
"quote": message.order.quote, "quote": message.order.quote,
...@@ -52,5 +53,5 @@ ...@@ -52,5 +53,5 @@
}[] }[]
} }
} }
}[] }
} }[]
{ {
"context":context, "context": $context(context, $action),
"message":{ "message":{
"intent":{ "intent":{
"item":{ "item":{
...@@ -26,7 +26,8 @@ ...@@ -26,7 +26,8 @@
"name":provider.providerName "name":provider.providerName
}, },
"locations":$map( provider.providerCity, function($location) { "locations":$map( provider.providerCity, function($location) {
{"city":{ {
"city":{
"name":$location "name":$location
}} }}
})[], })[],
...@@ -51,4 +52,4 @@ ...@@ -51,4 +52,4 @@
} }
} }
} }
} }
\ No newline at end of file
$.data.message.orders.{
"context": $context(%.%.context, $action),
"message": {
"order": $
}
}[]
{ {
"name": "generic-client-layer", "name": "gcl",
"version": "1.0.0", "version": "1.0.0",
"description": "Beckn Generic Client Layer", "description": "",
"main": "index.ts", "main": "index.js",
"scripts": { "scripts": {
"test": "node dist/index.js", "test": "echo \"Error: no test specified\" && exit 1",
"start": "node dist/index.js", "start": "nodemon src/app.ts"
"start:dev": "concurrently \"npx tsc --watch\" \"nodemon --delay 1000ms -q dist/index.js\"",
"dev": "nodemon src/index.ts",
"build": "npx tsc"
}, },
"repository": { "keywords": [],
"type": "git", "author": "",
"url": "git+https://github.com/beckn/generic-client-layer.git"
},
"author": "beckn",
"license": "ISC", "license": "ISC",
"bugs": {
"url": "https://github.com/beckn/generic-client-layer/issues"
},
"homepage": "https://github.com/beckn/generic-client-layer#readme",
"dependencies": { "dependencies": {
"app-root-path": "^3.1.0", "app-root-path": "^3.1.0",
"axios": "^1.6.1", "axios": "^1.6.2",
"class-transformer": "^0.5.1",
"concurrently": "^8.2.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"express": "^4.18.2", "express": "^4.18.2",
"joi": "^17.11.0", "ini": "^4.1.1",
"inversify": "^6.0.2",
"inversify-express-utils": "^6.4.6",
"jsonata": "^2.0.3", "jsonata": "^2.0.3",
"lodash": "^4.17.21",
"moment": "^2.29.4", "moment": "^2.29.4",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"uuid": "^9.0.1" "uuid": "^9.0.1",
"winston": "^3.11.0",
"winston-express": "^0.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.15", "@types/express": "^4.17.21",
"@types/express": "^4.17.20", "@types/ini": "^1.3.33",
"@types/lodash": "^4.14.200", "@types/uuid": "^9.0.7",
"@types/node": "^20.8.7", "nodemon": "^3.0.1"
"@types/uuid": "^9.0.6",
"nodemon": "^3.0.1",
"typescript": "^5.2.2"
} }
} }
import express, { Application } from 'express';
import { container, server } from './inversify/inversify.config';
import './gcl/gcl.controller';
import { ConfigService } from './config/config.service';
import { AppLogger } from './app/app.logger';
import { ErrorHandlerMiddleware } from './middleware/errorhandler.middleware';
class App {
public app: Application;
constructor() {
this.app = express();
this.config();
this.setupMiddlewares();
}
private config(): void {
server.setConfig((app) => {
app.use(express.json());
app.use(express.urlencoded({ extended: true }))
});
this.app.use(server.build());
}
private setupMiddlewares() {
const errorHandlerMiddleware = container.get<ErrorHandlerMiddleware>(ErrorHandlerMiddleware);
this.app.use(errorHandlerMiddleware.handleError.bind(errorHandlerMiddleware));
}
}
const configService = container.resolve<ConfigService>(ConfigService);
const logger = container.resolve<AppLogger>(AppLogger);
const app = new App().app;
const port = configService.getAppPort();
app.listen(port, () => {
logger.info(`Server is running on http://localhost:${port}`);
});
import { injectable } from "inversify";
import winston, { Logger, format } from "winston";
import { format as printf } from 'util';
@injectable()
export class AppLogger {
private logger: Logger;
constructor() {
const logFormat = format.printf(info => `\n${info.timestamp} [${info.level}]: ${info.message}`)
this.logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), // Add timestamp to the log message
winston.format.simple()
),
transports: [
new winston.transports.Console({
format: format.combine(format.colorize(), logFormat, format.splat()),
}),
new winston.transports.File({ filename: 'logs/error.log', level: 'error', format: format.combine(format.colorize(), format.json()) }),
new winston.transports.File({ filename: 'logs/combined.log', format: format.combine(format.colorize(), format.json()) }),
],
exitOnError: false
});
}
public info(message: string, ...args: any[]): void {
this.logger.info(this.formatMessage(message, args));
}
public error(message: string, ...args: any[]): void {
this.logger.error(this.formatMessage(message, args));
}
private formatMessage(message: string, args: any[]): string {
return args.length > 0 ? printf(message, ...args) : message;
}
}
import express, { Express, Router, Request, Response } from "express";
import cors from "cors";
import dotenv from "dotenv";
import { clientLayerRoutes } from "./routes";
interface InitAppParams {
app: Express;
}
const initApp = ({ app }: InitAppParams) => {
const router: Router = express.Router();
dotenv.config();
app.options(
"*",
cors<Request>({
origin: process.env.NODE_ENV === "*",
optionsSuccessStatus: 200,
credentials: true,
methods: ["GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS"]
})
);
app.use(
cors({
origin: process.env.NODE_ENV === "*",
methods: ["GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS"]
})
);
app.set("trust proxy", true);
app.use(express.urlencoded({ extended: true, limit: "200mb" }));
app.use(express.json({ limit: "200mb" }));
app.use(router);
router.use("/ping", (req: Request, res: Response) => {
res.json({
status: 200,
message: "Generic Client Layer Started"
});
});
router.use(clientLayerRoutes());
return app;
};
export { initApp };
export * from "./routes";
import express, { Router } from "express";
import { validateRequest } from "../common";
import { searchController } from "../modules/search/controller";
import { selectController } from "../modules/select/controller";
const router: Router = express.Router();
export const clientLayerRoutes = () => {
router.post("/search", validateRequest, searchController);
router.post("/select", validateRequest, selectController);
router.post("/init", validateRequest, () => {});
router.post("/confirm", validateRequest, () => {});
router.post("/update", validateRequest, () => {});
router.post("/status", validateRequest, () => {});
router.post("/cancel", validateRequest, () => {});
router.post("/track", validateRequest, () => {});
router.post("/support", validateRequest, () => {});
router.post("/rating", validateRequest, () => {});
return router;
};
import { initApp } from "./app";
import { Express } from "express";
export const startServer = async (app: Express) => {
const serverApp = initApp({ app });
const PORT: string = process.env.PORT || "3009";
serverApp.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
};
import { IRequestContext, requestContextSchema } from "./schemaValidator";
import { v4 as uuid } from "uuid";
import moment from "moment";
export interface IRequestContextStructure_ver_1_1_0 {
domain: string;
location: {
id?: string;
descriptor?: {
name?: string;
code?: string;
short_desc?: string;
long_desc?: string;
additional_desc?: {
url?: string;
content_type?: "text/plain";
};
media?: [
{
mimetype?: string;
url?: string;
signature?: string;
dsa?: string;
}
];
images?: [
{
url?: string;
size_type?: string;
width?: string;
height?: string;
}
];
};
map_url?: string;
gps?: string;
address?: string;
city?: {
name?: string;
code?: string;
};
district?: string;
state?: {
name?: string;
code?: string;
};
country?: {
name?: string;
code?: string;
};
area_code?: string;
circle?: {
gps?: string;
radius?: {
type?: string;
value?: string;
estimated_value?: string;
computed_value?: string;
range?: {
min?: string;
max?: string;
};
unit?: string;
};
};
polygon?: string;
rating?: string;
};
action: string;
version: string;
bap_id: string;
bap_uri: string;
bpp_id?: string;
bpp_uri?: string;
transaction_id: string;
message_id: string;
timestamp?: string;
key?: string;
ttl?: string;
}
interface IResponseContextStructure_ver_1_1_0 {
bapId?: string;
messageId?: string;
transactionId?: string;
bapUri?: string;
bppId?: string;
bppUri?: string;
domain?: string;
}
export const buildRequestContextVer1_1_0 = (
input: IRequestContext,
action: string
) => {
const {
bppId,
bppUri,
domain,
messageId,
transactionId,
bapId,
bapUri,
key
} = input;
const context: IRequestContextStructure_ver_1_1_0 = {
domain: domain,
bpp_id: bppId,
bpp_uri: bppUri,
bap_id: bapId || `${process.env.BAP_ID}`,
action: action,
bap_uri: bapUri || `${process.env.BAP_URI}`,
version: "1.1.0",
transaction_id: transactionId || uuid(),
message_id: messageId || uuid(),
location: {
country: {
name: `${process.env.COUNTRY_NAME}`,
code: `${process.env.COUNTRY_CODE}`
},
city: {
name: `${process.env.CITY_NAME}`,
code: `${process.env.CITY_CODE}`
}
},
ttl: "PT10M",
key: key,
timestamp: moment().toISOString()
};
return context;
};
export const responseContextBuilderVer1_1_0 = (
context: IRequestContextStructure_ver_1_1_0
): IResponseContextStructure_ver_1_1_0 => {
return {
messageId: context?.message_id,
transactionId: context?.transaction_id,
bppId: context?.bpp_id,
bppUri: context?.bpp_uri,
domain: context?.domain
};
};
export * from "./context";
export * from "./schemaValidator";
export * from "./schemaObjectGenerator";
import fs from "fs";
import path from "path";
import appRootPath from 'app-root-path';
import jsonata from 'jsonata';
import { removeEmptyObjectKeys } from './utils'
export const map = async (data: any, action?: string) => {
const expression = jsonata(fs.readFileSync(path.join(appRootPath.toString(), `/mappings/${action}.jsonata`), "utf8"));
let mapped = await expression.evaluate(data);
mapped = removeEmptyObjectKeys(mapped);
return mapped;
};
{
"searchString": {
"path": "message.intent.item.descriptor.name"
},
"itemId": { "path": "message.intent.item.id" },
"fulfillment.agentName": {
"path": "message.intent.fulfillment.agent.person.name"
},
"fulfillment.customerGender": {
"path": "message.intent.fulfillment.customer.person.gender"
},
"provider.providerName": {
"path": "message.intent.provider.descriptor.name"
},
"provider.providerCity": {
"path": "message.intent.provider.locations[0].city.name"
},
"provider.providerId": { "path": "message.intent.provider.id" },
"category.categoryCode": {
"path": "message.intent.category.descriptor.code"
},
"category.categoryName": {
"path": "message.intent.category.descriptor.name"
},
"category.categoryId": {
"path": "message.intent.category.id"
},
"location": { "path": "message.intent.location.circle.gps" }
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment