Create templates for Route, Control, Service, Repository layers and DI by yarn command
Relationship diagram between object types and layers
import { IsNotEmpty, IsString } from "class-validator";
import { ParsedQs } from "qs";
export class GetInspectionLotQueryDTO {
@IsNotEmpty()
@IsString()
lotID: string;
constructor(query: ParsedQs) {
this.lotID = query.lotID as string;
}
}
export class GetInspectionLotResDTO {
id: string;
plant: string;
constructor(data: {
id: string;
plant: string;
}) {
this.id = data.id;
this.plant = data.plant;
}
}
import { Exclude, Expose, Transform, Type } from "class-transformer";
@Exclude()
export class InspectionLot {
@Expose({ name: "InspectionLot" })
id: string;
@Expose({ name: "Plant" })
plant: string;
}
export interface InspectionLotResDAO {
InspectionLot: string;
InspectionLotObjectText: string;
InspectionLotActualQuantity: string;
InspectionLotQuantityUnit: string;
InspectionLotStartDate: string;
InspectionLotEndDate: string;
to_InspectionLotWithStatus: { InspLotStatusInspCompleted: string };
}
import { injectable } from "inversify";
import { InspectionLotResDAO } from "~/s4/dao/inspectionLotResDAO";
import { UsageDecisionReqDAO } from "~/s4/dao/usageDecisionReqDAO";
import { InspectionLot } from "~/s4/model/inspectionLot";
import { UsageDecision } from "~/s4/model/usageDecision";
import { createAPIClientDefault } from "~/s4/repository/index";
import { InspectionRepository } from "~/s4/service/inspectionRepository";
import { AuthData } from "~/type/authData";
import { fromDAO2Model } from "~/utils/fromDAO2Model";
const client = createAPIClientDefault(
"/sap/opu/odata/sap/API_INSPECTIONLOT_SRV"
);
@injectable()
export class InspectionRepositoryImpl implements InspectionRepository {
public async findInspectionLotByLotID(lotID: string): Promise<InspectionLot> {
const res = await client.get(
`/A_InspectionLot('${lotID}')?$expand=to_InspectionLotWithStatus`
);
const dao: InspectionLotResDAO = res.data.d;
return fromDAO2Model(InspectionLot, dao);
}
public async saveUsageDecision(
authData: AuthData,
usageDecision: UsageDecision
😞 Promise<void> {
const body: UsageDecisionReqDAO = {
d: {
InspectionLot: usageDecision.lotID,
InspLotUsageDecisionLevel: usageDecision.usageDecisionLevel,
InspectionLotQualityScore: usageDecision.qualityScore,
InspLotUsageDecisionCatalog: usageDecision.catalogID,
SelectedCodeSetPlant: usageDecision.plant,
InspLotUsgeDcsnSelectedSet: usageDecision.usageDecisionSelectedSet,
InspLotUsageDecisionCodeGroup: usageDecision.catalogName,
InspectionLotUsageDecisionCode: usageDecision.usageDecisionCode,
ChangedDateTime: usageDecision.changedDate,
},
};
await client.post(`/A_InspLotUsageDecision`, body, {
headers: {
"Content-Type": "application/json",
"x-csrf-token": authData.xCSRFToken,
Cookie: authData.cookie,
},
});
}
}
import { inject, injectable } from "inversify";
import { TYPES } from "~/di/types";
import { PostUsageDecisionBodyDTO } from "~/s4/dto/postUsageDecisionBodyDTO";
import { InspectionLot } from "~/s4/model/inspectionLot";
import { UsageDecision } from "~/s4/model/usageDecision";
import { CatalogRepository } from "~/s4/service/catalogRepository";
import { InspectionRepository } from "~/s4/service/inspectionRepository";
import { InspectionService } from "~/s4/service/inspectionService";
@injectable()
export class InspectionServiceImpl implements InspectionService {
private inspectionRepo: InspectionRepository;
private catalogRepo: CatalogRepository;
constructor(
@inject(TYPES.InspectionRepository)
inspectionRepo: InspectionRepository,
@inject(TYPES.CatalogRepository)
catalogRepo: CatalogRepository
) {
this.inspectionRepo = inspectionRepo;
this.catalogRepo = catalogRepo;
}
public async getInspectionLot(lotID: string): Promise<InspectionLot> {
return await this.inspectionRepo.findInspectionLotByLotID(lotID);
}
public async postUsageDecision(
bodyDTO: PostUsageDecisionBodyDTO
😞 Promise<void> {
const authData = await this.inspectionRepo.findAuthData();
const usageDecision = new UsageDecision({ ...bodyDTO });
const inspectionResults =
await this.inspectionRepo.findInspectionResultsByLotID(bodyDTO.lotID);
usageDecision.qualityScore =
usageDecision.calcQualityScore(inspectionResults);
usageDecision.usageDecisionCode = usageDecision.addUsageDecision();
await this.inspectionRepo.saveUsageDecision(authData, usageDecision);
}
}
import { inject, injectable } from "inversify";
import { TYPES } from "~/di/types";
import { InspectionController } from "~/s4/controller/inspectionController";
import { GetInspectionLotQueryDTO } from "~/s4/dto/getInspectionLotQueryDTO";
import { GetInspectionLotResDTO } from "~/s4/dto/getInspectionLotResDTO";
import { UsageDecision } from "~/s4/model/usageDecision";
import { InspectionService } from "~/s4/service/inspectionService";
import { ControllerMethod } from "~/type/controllerMethod";
import { HttpStatusCode } from "~/type/httpStatusCode";
import { validateDTO } from "~/utils/validateDTO";
@injectable()
export class InspectionControllerImpl implements InspectionController {
private service: InspectionService;
constructor(
@inject(TYPES.InspectionService) inspectionService: InspectionService
) {
this.service = inspectionService;
}
public getInspectionLot: ControllerMethod = async (req, res) => {
const queryDTO = new GetInspectionLotQueryDTO(req.query);
await validateDTO(queryDTO);
const lot = await this.service.getInspectionLot(queryDTO.lotID);
const resDTO = new GetInspectionLotResDTO(lot);
res.send(resDTO);
};
public postUsageDecision: ControllerMethod = async (req, res) => {
const bodyDTO = new UsageDecision(req.body);
await validateDTO(bodyDTO);
await this.service.postUsageDecision(bodyDTO);
res.status(HttpStatusCode.CREATED).send({ message: "Post success" });
};
}
import { validate } from "class-validator";
import { HTTP400Error } from "~/utils/errors";
export const validateDTO = async (object: object): Promise<void> =>
validate(object).then((res) => {
if (res.length > 0) {
throw new HTTP400Error(
res[0].constraints[Object.keys(res[0].constraints)[0]]
);
}
});
import { Router } from "express";
import { myContainer } from "~/di/inversify.config";
import { TYPES } from "~/di/types";
import { InspectionController } from "~/s4/controller/inspectionController";
import { asyncWrapper } from "~/utils/asyncWrapper";
const controller = myContainer.get<InspectionController>(
TYPES.InspectionController
);
const inspectionRoute = Router();
inspectionRoute.get(
"/lot",
asyncWrapper(async (req, res) => await controller.getInspectionLot(req, res))
);
inspectionRoute.post(
"/usage-decision",
asyncWrapper(async (req, res) => await controller.postUsageDecision(req, res))
);
export { inspectionRoute };
import { NextFunction, Request, Response } from "express";
type RouteHandler = (
req: Request,
res: Response,
next: NextFunction
) => Promise<void>;
type AsyncWrapper = (handler: RouteHandler) => RouteHandler;
export const asyncWrapper: AsyncWrapper =
(handler) => async (req, res, next) => {
try {
await handler(req, res, next);
} catch (error) {
next(error);
}
};
import { AxiosError } from "axios";
import { Application, NextFunction, Request, Response } from "express";
import { HttpStatusCode } from "~/type/httpStatusCode";
import { HTTP400Error } from "~/utils/errors";
interface ErrorResponse {
name: string;
httpCode: number;
isOperational: boolean;
message: string;
stack: string | undefined;
}
export const configureErrorHandler = (app: Application): void => {
app.use(
(error: Error, _: Request, res: Response, next: NextFunction): void => {
if (res.headersSent) {
return next(error);
}
if (error instanceof AxiosError) {
const httpCode =
error.response?.status || HttpStatusCode.INTERNAL_SERVER;
const axiosError: ErrorResponse = {
name: error.code || "AxiosError",
httpCode: httpCode,
isOperational: false,
message:
error.response?.data?.error?.message?.value ||
"An error occurred while processing the request.",
stack: error.stack,
};
res.status(httpCode).json(axiosError);
} else if (error instanceof HTTP400Error) {
const errorJson: ErrorResponse = createErrorResponse(
error,
error.httpCode
);
res.status(error.httpCode).json(errorJson);
} else {
const errorJson: ErrorResponse = createErrorResponse(
error,
HttpStatusCode.INTERNAL_SERVER
);
res.status(HttpStatusCode.INTERNAL_SERVER).json(errorJson);
}
}
);
};
import { Application } from "express";
import { userRoute } from "~/externalPlatform1/route/userRoute";
import { inspectionRoute } from "~/s4/route/inspectionRoute";
export const configureRoute = (app: Application): void => {
app.use("/external-platform1/user", userRoute);
app.use("/s4/inspection", inspectionRoute);
};
import express, { Application } from "express";
import http from "http";
import "reflect-metadata";
import { configureErrorHandler } from "~/server-config/errorHandler";
import { configureLogging } from "~/server-config/logging";
import { configureRequestParser } from "~/server-config/requestParser";
import { configureRoute } from "~/server-config/route";
import { configureSecurity } from "~/server-config/security";
const app: Application = express();
const port: number = 3000;
configureLogging(app)
configureRequestParser(app)
configureRoute(app);
configureSecurity(app);
configureErrorHandler(app);
const server = http.createServer(app);
server.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Inter-layer relationships
export class InspectionServiceImpl implements InspectionService {
private inspectionRepo: InspectionRepository;
private catalogRepo: CatalogRepository;
constructor() {
this.inspectionRepo = new InspectionRepositoryImpl();
this.catalogRepo = new CatalogRepositoryImpl();
}
}
export class InspectionServiceImpl implements InspectionService {
private inspectionRepo: InspectionRepository;
private catalogRepo: CatalogRepository;
constructor(
inspectionRepo: InspectionRepository,
catalogRepo: CatalogRepository
) {
this.inspectionRepo = inspectionRepo();
this.catalogRepo = catalogRepo();
}
}
import { GetInspectionLotsQueryDTO } from "~/s4/dto/getInspectionLotsQueryDTO";
import { PostInspectionQlResultBodyDTO } from "~/s4/dto/postInspectionQlResultBodyDTO";
import { PostInspectionQnResultBodyDTO } from "~/s4/dto/postInspectionQnResultBodyDTO";
import { PostUsageDecisionBodyDTO } from "~/s4/dto/postUsageDecisionBodyDTO";
import { CharacteristicCode } from "~/s4/model/characteristicCode";
import { InspectionCharacteristic } from "~/s4/model/inspectionCharacteristic";
import { InspectionLot } from "~/s4/model/inspectionLot";
export interface InspectionService {
getInspectionLot(lotID: string): Promise<InspectionLot>;
getInspectionLots(
queryDTO: GetInspectionLotsQueryDTO
😞 Promise<InspectionLot[]>;
getInspectionCharacteristics(
lotID: string
😞 Promise<InspectionCharacteristic[]>;
getCharacteristicCodes(catalogName: string): Promise<CharacteristicCode[]>;
postInspectionQnResult(bodyDTO: PostInspectionQnResultBodyDTO): Promise<void>;
postInspectionQlResult(bodyDTO: PostInspectionQlResultBodyDTO): Promise<void>;
postUsageDecision(bodyDTO: PostUsageDecisionBodyDTO): Promise<void>;
}
const controller = new InspectionControllerImpl(
new InspectionServiceImpl(
new InspectionRepositoryImpl(),
new CatalogRepositoryImpl()
)
);
const inspectionRoute = Router();
inspectionRoute.get(
"/lot",
asyncWrapper(async (req, res) => await controller.getInspectionLot(req, res))
);
import { inject, injectable } from "inversify";
import { TYPES } from "~/di/types";
import { CatalogRepository } from "~/s4/service/catalogRepository";
import { InspectionRepository } from "~/s4/service/inspectionRepository";
import { InspectionService } from "~/s4/service/inspectionService";
@injectable()
export class InspectionServiceImpl implements InspectionService {
private inspectionRepo: InspectionRepository;
private catalogRepo: CatalogRepository;
constructor(
@inject(TYPES.InspectionRepository)
inspectionRepo: InspectionRepository,
@inject(TYPES.CatalogRepository)
catalogRepo: CatalogRepository
) {
this.inspectionRepo = inspectionRepo;
this.catalogRepo = catalogRepo;
}
}
const TYPES = {
//Controller
InspectionController: Symbol.for("InspectionController"),
UserController: Symbol.for("UserController"),
//Service
InspectionService: Symbol.for("InspectionService"),
UserService: Symbol.for("UserService"),
//Repository
InspectionRepository: Symbol.for("InspectionRepository"),
CatalogRepository: Symbol.for("CatalogRepository"),
UserRepository: Symbol.for("UserRepository"),
};
export { TYPES };
import { Container } from "inversify";
import { TYPES } from "~/di/types";
import { UserController } from "~/externalPlatform1/controller/userController";
import { UserControllerImpl } from "~/externalPlatform1/controller/userControllerImpl";
import { UserRepositoryImpl } from "~/externalPlatform1/repository/userRepositoryImpl";
import { UserRepository } from "~/externalPlatform1/service/userRepository";
import { UserService } from "~/externalPlatform1/service/userService";
import { UserServiceImpl } from "~/externalPlatform1/service/userServiceImpl";
import { InspectionController } from "~/s4/controller/inspectionController";
import { InspectionControllerImpl } from "~/s4/controller/inspectionControllerImpl";
import { CatalogRepositoryImpl } from "~/s4/repository/catalogRepositoryImpl";
import { InspectionRepositoryImpl } from "~/s4/repository/inspectionRepositoryImpl";
import { CatalogRepository } from "~/s4/service/catalogRepository";
import { InspectionRepository } from "~/s4/service/inspectionRepository";
import { InspectionService } from "~/s4/service/inspectionService";
import { InspectionServiceImpl } from "~/s4/service/inspectionServiceImpl";
const myContainer = new Container();
//Controller
myContainer
.bind<InspectionController>(TYPES.InspectionController)
.to(InspectionControllerImpl);
myContainer.bind<UserController>(TYPES.UserController).to(UserControllerImpl);
//Service
myContainer
.bind<InspectionService>(TYPES.InspectionService)
.to(InspectionServiceImpl);
myContainer.bind<UserService>(TYPES.UserService).to(UserServiceImpl);
//Repository
myContainer
.bind<InspectionRepository>(TYPES.InspectionRepository)
.to(InspectionRepositoryImpl);
myContainer
.bind<CatalogRepository>(TYPES.CatalogRepository)
.to(CatalogRepositoryImpl);
myContainer.bind<UserRepository>(TYPES.UserRepository).to(UserRepositoryImpl);
export { myContainer };
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
11 | |
10 | |
9 | |
8 | |
7 | |
7 | |
6 | |
5 | |
4 | |
4 |