Skip to content

Commit

Permalink
feat(Router): route params added to Request.
Browse files Browse the repository at this point in the history
fix(core): core freeze when loadConfig method didn't run.
fix(types): Request and Response code suggestion didn't work for Javascript.
chore(core): some changes in request handling structure.
  • Loading branch information
MR-MKZ committed Sep 12, 2024
1 parent 5799cb1 commit f4731e4
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 69 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ AxonJs has some types which can help you in developing your applications for aut
- `AxonCoreConfig`: Type of core config object for configuration Axon core as you want.
- `Request`: Type of controller request param. (IncomingMessage)
- `Response`: Type of controller response param. (ServerResponse)
- `Headers`: Type of response headers. (OutgoingHeaders)

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


start_time = time.time()
duration = 1
duration = 10
requests_count = 0
response_time = []

Expand Down
31 changes: 21 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,19 @@
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/node": "^22.1.0",
"jest": "^29.7.0",
"nodemon": "^3.1.4",
"path-to-regexp": "^8.1.0",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"tsup": "^8.2.4",
"typescript": "^5.5.4"
},
"dependencies": {
"@spacingbat3/kolor": "^4.0.0",
"moment": "^2.30.1",
"pino": "^9.4.0",
"pino-pretty": "^11.2.2",
"moment": "^2.30.1"
"@types/node": "^22.5.4"
}
}
20 changes: 19 additions & 1 deletion src/core/coreTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,26 @@ interface AxonCoreConfig {
DEBUG?: boolean;
LOGGER?: boolean;
LOGGER_VERBOSE?: boolean;
RESPONSE_MESSAGES?: AxonResponseMessage;
}

interface AxonResponseMessage {
/**
* response error message for 404 not found response from core
*/
notFound?: string;
/**
* response error message for 500 internal server error response from core
*/
serverError?: string;
/**
* response error message for 405 method not allowed response from core
*/
methodNotAllowed?: string;
[key: string]: string | undefined;
}

export {
AxonCoreConfig
AxonCoreConfig,
AxonResponseMessage
}
129 changes: 97 additions & 32 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import Router from "../Router";
import { HttpMethods, JsonResponse, CoreReq } from "../types"
import { HttpMethods, JsonResponse } from "../types"
import * as http from "http";
import { routeDuplicateException } from "./CoreExceptions";
import addRoutePrefix from "./utils/routePrefixHandler";
import { AxonCoreConfig } from "./coreTypes";
import { logger } from "./utils/coreLogger";
import { colors } from "@spacingbat3/kolor"
import getRequestBody from "./utils/getRequestBody";
import { Key, pathToRegexp, Keys } from "path-to-regexp";
import { Request, Response, Headers } from "..";

const defaultResponses = {
notFound: "Not found",
serverError: "Internal server error",
methodNotAllowed: "Method not allowed"
}

export default class AxonCore {
private routes: HttpMethods;
private config: AxonCoreConfig;
private configsLoaded: boolean;
private passConfig: boolean;
private routesLoaded: boolean;

constructor() {
Expand All @@ -27,10 +36,12 @@ export default class AxonCore {
this.config = {
DEBUG: false,
LOGGER: true,
LOGGER_VERBOSE: false
LOGGER_VERBOSE: false,
RESPONSE_MESSAGES: defaultResponses
};

this.configsLoaded = false;
this.passConfig = true;
this.routesLoaded = false;
}

Expand All @@ -41,9 +52,11 @@ export default class AxonCore {
* @param config core config object
*/
loadConfig(config: AxonCoreConfig) {
this.passConfig = false;
this.config.DEBUG = config.DEBUG || false
this.config.LOGGER = config.LOGGER || true
this.config.LOGGER_VERBOSE = config.LOGGER_VERBOSE || false
this.config.RESPONSE_MESSAGES = { ...config.RESPONSE_MESSAGES }

if (this.config.DEBUG) {
logger.level = "debug"
Expand Down Expand Up @@ -89,7 +102,7 @@ export default class AxonCore {
* @param res server response
* @returns
*/
async #handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
async #handleRequest(req: Request, res: Response) {
// log incoming requests
if (this.config.LOGGER_VERBOSE) {
logger.request({
Expand All @@ -107,7 +120,7 @@ export default class AxonCore {
res.statusCode = 405

res.write(JSON.stringify({
msg: `Method ${req.method} not allowed`
message: this.config.RESPONSE_MESSAGES?.methodNotAllowed || defaultResponses.methodNotAllowed
}));

return res.end();
Expand All @@ -118,50 +131,93 @@ export default class AxonCore {
(Object.keys(this.routes) as Array<keyof HttpMethods>).forEach(async (method) => {
if (method == req.method) {
if (req.url) {
try {
controller = await this.routes[method][req.url]["controller"](req, res)

if (controller.responseMessage) {
res.statusMessage = controller.responseMessage
}
let findedRoute = false;

if (Object.keys(this.routes[method]).length === 0) {
res.statusCode = 404
res.write(JSON.stringify({
message: this.config.RESPONSE_MESSAGES?.notFound || defaultResponses.notFound
}))

return res.end()
}

return Object.keys(this.routes[method]).forEach(async (route, index) => {
let keys: Keys;
const regexp = pathToRegexp(route);
keys = regexp.keys
const match: RegExpExecArray | null = regexp.regexp.exec(req.url as string);

if (match) {
try {
if (!findedRoute) {
findedRoute = true

const params: Record<string, string | undefined> = {};

keys.forEach((key: Key, index: number) => {
params[key.name] = match[index + 1];
});

req.params = params;

controller = await this.routes[method][route]["controller"](req, res)

if (controller.responseMessage) {
res.statusMessage = controller.responseMessage
}

res.statusCode = controller.responseCode
if (typeof controller.body !== "object") {
throw new TypeError(`Response body can't be ${typeof controller.body}`)
}

if (controller.headers) {
for (const key in controller.headers) {
if (controller.headers[key]) {
res.setHeader(key, controller.headers[key])
if (typeof controller.responseCode !== "number") {
throw new TypeError(`Response code can't be ${typeof controller.responseCode}`);
}

res.statusCode = controller.responseCode

if (controller.headers) {
for (const key in controller.headers) {
if (controller.headers[key]) {
res.setHeader(key, controller.headers[key])
}
}
}

res.write(JSON.stringify(controller.body))

return res.end()
} else {
return;
}
} catch (error) {
logger.error(error)

res.statusCode = 500
res.write(JSON.stringify({
message: this.config.RESPONSE_MESSAGES?.serverError || defaultResponses.serverError
}))
return res.end()
}
}

res.write(JSON.stringify(controller.body))

return res.end()
} catch (error) {
if (error instanceof TypeError) {
if (!findedRoute && (Object.keys(this.routes[method]).length == (index + 1))) {
res.statusCode = 404
res.write(JSON.stringify({
message: "Not found"
message: this.config.RESPONSE_MESSAGES?.notFound || defaultResponses.notFound
}))

return res.end()
}
res.statusCode = 500
res.write(JSON.stringify({
message: "Internal Server Error"
}))

return res.end()
}
})
}
}
})
}

async #responseHandler(req: CoreReq, res: http.ServerResponse) { }


/**
* Start listening to http incoming requests
* @param {string} host server host address
Expand All @@ -174,9 +230,18 @@ export default class AxonCore {
const corePreloader = async (): Promise<void> => {
return new Promise((resolve) => {
const interval = setInterval(() => {
if (this.routesLoaded && this.configsLoaded) {
clearInterval(interval);
resolve();
if (this.passConfig) {
if (this.routesLoaded) {
logger.info("all routes loaded!");
clearInterval(interval);
resolve();
}
} else {
if (this.routesLoaded && this.configsLoaded) {
logger.info("all configs and routes loaded!");
clearInterval(interval);
resolve();
}
}
}, 100);
});
Expand Down
Loading

0 comments on commit f4731e4

Please sign in to comment.