-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #621 from hngprojects/feat/api-status
feat: api status updates
- Loading branch information
Showing
10 changed files
with
170 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Request, Response } from "express"; | ||
import { asyncHandler } from "../middleware/asyncHandler"; | ||
import { parseJsonResponse } from "../services/api-status.services"; | ||
import { sendJsonResponse } from "../utils/sendJsonResponse"; | ||
|
||
export const createApiStatus = asyncHandler( | ||
async (req: Request, res: Response) => { | ||
const resultJson = req.body; | ||
|
||
await parseJsonResponse(resultJson); | ||
sendJsonResponse(res, 201, "API status updated successfully"); | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { NextFunction, Request, Response } from "express"; | ||
|
||
/** | ||
* Async handler to wrap the API routes, this allows for async error handling. | ||
* @param fn Function to call for the API endpoint | ||
* @returns Promise with a catch statement | ||
*/ | ||
const asyncHandler = | ||
(fn: (req: Request, res: Response, next: NextFunction) => void) => | ||
(req: Request, res: Response, next: NextFunction) => { | ||
return Promise.resolve(fn(req, res, next)).catch(next); | ||
}; | ||
|
||
export { asyncHandler }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { | ||
Column, | ||
CreateDateColumn, | ||
DeleteDateColumn, | ||
Entity, | ||
PrimaryGeneratedColumn, | ||
UpdateDateColumn, | ||
} from "typeorm"; | ||
|
||
export enum API_STATUS { | ||
OPERATIONAL = "operational", | ||
DEGRADED = "degraded", | ||
DOWN = "down", | ||
} | ||
|
||
@Entity({ name: "api_status" }) | ||
export class ApiStatus { | ||
@PrimaryGeneratedColumn("uuid") | ||
id: string; | ||
|
||
@Column({ name: "api_group" }) | ||
api_group: string; | ||
|
||
@Column({ name: "api_name" }) | ||
api_name: string; | ||
|
||
@Column({ name: "status", type: "enum", enum: API_STATUS }) | ||
status: API_STATUS; | ||
|
||
@Column("text", { nullable: true }) | ||
details: string; | ||
|
||
@Column({ name: "response_time", type: "int", nullable: true }) | ||
response_time: string; | ||
|
||
@CreateDateColumn({ name: "created_at" }) | ||
created_at: Date; | ||
|
||
@UpdateDateColumn({ name: "updated_at" }) | ||
updated_at: Date; | ||
|
||
@DeleteDateColumn({ nullable: true }) | ||
deleted_at: Date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Router } from "express"; | ||
import { createApiStatus } from "../controllers/api-status.controller"; | ||
|
||
const apiStatusRouter = Router(); | ||
|
||
apiStatusRouter.post("/api-status", createApiStatus); | ||
|
||
export { apiStatusRouter }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import AppDataSource from "../data-source"; | ||
import { API_STATUS, ApiStatus } from "../models/api-model"; | ||
|
||
const apiStatusRepository = AppDataSource.getRepository(ApiStatus); | ||
const MAX_ALLOWED_RESPONSE_TIME = 2000; | ||
|
||
const determineStatus = ( | ||
statusCode: number, | ||
responseTime: number, | ||
): API_STATUS => { | ||
if (statusCode >= 200 && statusCode < 300) { | ||
if (responseTime && responseTime > MAX_ALLOWED_RESPONSE_TIME) { | ||
return API_STATUS.DEGRADED; | ||
} | ||
return API_STATUS.OPERATIONAL; | ||
} else if (statusCode >= 500) { | ||
return API_STATUS.DOWN; | ||
} | ||
return API_STATUS.DEGRADED; | ||
}; | ||
|
||
const parseJsonResponse = async (resultJson: any): Promise<void> => { | ||
const apiGroups = resultJson.collection.item; | ||
|
||
for (const apiGroup of apiGroups) { | ||
for (const api of apiGroup.item) { | ||
let status = API_STATUS.DEGRADED; | ||
let responseTime = null; | ||
|
||
if (api.response && api.response.length > 0) { | ||
const response = api.response[0]; | ||
responseTime = response.responseTime || null; | ||
status = determineStatus(response.code, responseTime); | ||
} | ||
|
||
const apiStatus = apiStatusRepository.create({ | ||
api_group: apiGroup.name, | ||
api_name: api.name, | ||
status, | ||
response_time: responseTime, | ||
details: responseTime ? `Response time: ${responseTime}ms` : null, | ||
}); | ||
|
||
await apiStatusRepository.save(apiStatus); | ||
} | ||
} | ||
}; | ||
|
||
export { parseJsonResponse }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import { Response } from "express"; | ||
|
||
/** | ||
* Sends a JSON response with a standard structure. | ||
* | ||
* @param res - The Express response object. | ||
* @param statusCode - The HTTP status code to send. | ||
* @param message - The message to include in the response. | ||
* @param data - The data to include in the response. Can be any type. | ||
* @param accessToken - Optional access token to include in the response. | ||
*/ | ||
const sendJsonResponse = ( | ||
res: Response, | ||
statusCode: number, | ||
message: string, | ||
data?: any, | ||
accessToken?: string, | ||
) => { | ||
const responsePayload: any = { | ||
status: "success", | ||
message, | ||
status_code: statusCode, | ||
data, | ||
}; | ||
|
||
if (accessToken) { | ||
responsePayload.access_token = accessToken; | ||
} | ||
|
||
res.status(statusCode).json(responsePayload); | ||
}; | ||
|
||
export { sendJsonResponse }; |