separate flight class api and transportation class api into different module
This commit is contained in:
@@ -17,6 +17,8 @@ import { CityController } from "@/modules/city/city.controller";
|
||||
import { CityMapper } from "@/modules/city/city.mapper";
|
||||
import { CountryController } from "@/modules/country/country.controller";
|
||||
import { CountryMapper } from "@/modules/country/country.mapper";
|
||||
import { FlightClassController } from "@/modules/flight-class/flight-class.controller";
|
||||
import { FlightClassMapper } from "@/modules/flight-class/flight-class.mapper";
|
||||
import { FlightController } from "@/modules/flight/flight.controller";
|
||||
import { FlightMapper } from "@/modules/flight/flight.mapper";
|
||||
import { HotelFacilityController } from "@/modules/hotel-facility/hotel-facility.controller";
|
||||
@@ -30,6 +32,8 @@ import { PackageMapper } from "@/modules/package/package.mapper";
|
||||
import { PartnerController } from "@/modules/partner/partner.controller";
|
||||
import { PartnerMapper } from "@/modules/partner/partner.mapper";
|
||||
import { StaticController } from "@/modules/static/static.controller";
|
||||
import { TransportationClassController } from "@/modules/transportation-class/transportation-class.controller";
|
||||
import { TransportationClassMapper } from "@/modules/transportation-class/transportation-class.mapper";
|
||||
import { TransportationController } from "@/modules/transportation/transportation.controller";
|
||||
import { TransportationMapper } from "@/modules/transportation/transportation.mapper";
|
||||
import compression from "compression";
|
||||
@@ -71,9 +75,13 @@ export class Application {
|
||||
const airlineMapper = new AirlineMapper();
|
||||
const airportMapper = new AirportMapper(cityMapper);
|
||||
const flightMapper = new FlightMapper(airlineMapper, airportMapper);
|
||||
const flightClassMapper = new FlightClassMapper(flightMapper);
|
||||
const hotelFacilityMapper = new HotelFacilityMapper();
|
||||
const hotelMapper = new HotelMapper(cityMapper, hotelFacilityMapper);
|
||||
const transportationMapper = new TransportationMapper();
|
||||
const transportationClassMapper = new TransportationClassMapper(
|
||||
transportationMapper,
|
||||
);
|
||||
const packageMapper = new PackageMapper(
|
||||
flightMapper,
|
||||
hotelMapper,
|
||||
@@ -104,6 +112,9 @@ export class Application {
|
||||
flightMapper,
|
||||
this._jwtService,
|
||||
).buildRouter();
|
||||
const flightClassRouter = new FlightClassController(
|
||||
flightClassMapper,
|
||||
).buildRouter();
|
||||
const hotelFacilityRouter = new HotelFacilityController(
|
||||
hotelFacilityMapper,
|
||||
this._jwtService,
|
||||
@@ -118,6 +129,9 @@ export class Application {
|
||||
this._fileStorage,
|
||||
this._jwtService,
|
||||
).buildRouter();
|
||||
const transportationClassRouter = new TransportationClassController(
|
||||
transportationClassMapper,
|
||||
).buildRouter();
|
||||
const packageRouter = new PackageController(
|
||||
packageMapper,
|
||||
this._fileStorage,
|
||||
@@ -147,9 +161,11 @@ export class Application {
|
||||
this._app.use("/airlines", airlineRouter);
|
||||
this._app.use("/airports", airportRouter);
|
||||
this._app.use("/flights", flightRouter);
|
||||
this._app.use("/flight-classes", flightClassRouter);
|
||||
this._app.use("/hotel-facilities", hotelFacilityRouter);
|
||||
this._app.use("/hotels", hotelRouter);
|
||||
this._app.use("/transportations", transportationRouter);
|
||||
this._app.use("/transportation-classes", transportationClassRouter);
|
||||
this._app.use("/packages", packageRouter);
|
||||
this._app.use("/admins", adminRouter);
|
||||
this._app.use("/partners", partnerRouter);
|
||||
|
||||
57
src/modules/flight-class/flight-class.controller.ts
Normal file
57
src/modules/flight-class/flight-class.controller.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Controller } from "@/common/controller";
|
||||
import { createOrmContextMiddleware } from "@/common/middlewares/create-orm-context.middleware";
|
||||
import { paginationQuerySchema } from "@/common/schemas";
|
||||
import type { ListResponse } from "@/common/types";
|
||||
import { FlightClass } from "@/database/entities/flight-class.entity";
|
||||
import { Flight } from "@/database/entities/flight.entity";
|
||||
import { orm } from "@/database/orm";
|
||||
import type { FlightClassMapper } from "@/modules/flight-class/flight-class.mapper";
|
||||
import type { FlightClassResponse } from "@/modules/flight-class/flight-class.types";
|
||||
import { Router, type Request, type Response } from "express";
|
||||
|
||||
export class FlightClassController extends Controller {
|
||||
public constructor(private readonly mapper: FlightClassMapper) {
|
||||
super();
|
||||
}
|
||||
|
||||
async list(req: Request, res: Response) {
|
||||
const parseQueryResult = paginationQuerySchema.safeParse(req.query);
|
||||
if (!parseQueryResult.success) {
|
||||
return this.handleZodError(parseQueryResult.error, res, "query");
|
||||
}
|
||||
const query = parseQueryResult.data;
|
||||
|
||||
const count = await orm.em.count(Flight);
|
||||
|
||||
const flightClasses = await orm.em.find(
|
||||
FlightClass,
|
||||
{},
|
||||
{
|
||||
limit: query.per_page,
|
||||
offset: (query.page - 1) * query.per_page,
|
||||
orderBy: { createdAt: "DESC" },
|
||||
populate: ["*"],
|
||||
},
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
data: flightClasses.map(
|
||||
this.mapper.mapEntityToResponse.bind(this.mapper),
|
||||
),
|
||||
errors: null,
|
||||
meta: {
|
||||
page: query.page,
|
||||
per_page: query.per_page,
|
||||
total_pages: Math.ceil(count / query.per_page),
|
||||
total_items: count,
|
||||
},
|
||||
} satisfies ListResponse<FlightClassResponse>);
|
||||
}
|
||||
|
||||
public buildRouter(): Router {
|
||||
const router = Router();
|
||||
router.get("/", createOrmContextMiddleware, this.list.bind(this));
|
||||
|
||||
return router;
|
||||
}
|
||||
}
|
||||
20
src/modules/flight-class/flight-class.mapper.ts
Normal file
20
src/modules/flight-class/flight-class.mapper.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { FlightClass } from "@/database/entities/flight-class.entity";
|
||||
import type { FlightMapper } from "@/modules/flight/flight.mapper";
|
||||
import type { FlightClassResponse } from "@/modules/flight/flight.types";
|
||||
|
||||
export class FlightClassMapper {
|
||||
public constructor(private readonly flightMapper: FlightMapper) {}
|
||||
|
||||
public mapEntityToResponse(flightClass: FlightClass): FlightClassResponse {
|
||||
return {
|
||||
id: flightClass.id,
|
||||
flight: this.flightMapper.mapEntityToResponse(flightClass.flight),
|
||||
class: flightClass.class,
|
||||
seat_layout: flightClass.seatLayout,
|
||||
baggage: flightClass.baggage,
|
||||
cabin_baggage: flightClass.cabinBaggage,
|
||||
created_at: flightClass.createdAt,
|
||||
updated_at: flightClass.updatedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
27
src/modules/flight-class/flight-class.schemas.ts
Normal file
27
src/modules/flight-class/flight-class.schemas.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import z from "zod";
|
||||
|
||||
export const flightClassRequestSchema = z.object({
|
||||
class: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
seat_layout: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(10, "Max 10 characters."),
|
||||
baggage: z
|
||||
.number("Must be number.")
|
||||
.int("Must be integer.")
|
||||
.positive("Must be positive."),
|
||||
cabin_baggage: z
|
||||
.number("Must be number.")
|
||||
.int("Must be integer.")
|
||||
.positive("Must be positive."),
|
||||
});
|
||||
|
||||
export const flightClassParamsSchema = z.object({
|
||||
id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(30, "Max 30 characters."),
|
||||
});
|
||||
21
src/modules/flight-class/flight-class.types.ts
Normal file
21
src/modules/flight-class/flight-class.types.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type {
|
||||
flightClassParamsSchema,
|
||||
flightClassRequestSchema,
|
||||
} from "@/modules/flight-class/flight-class.schemas";
|
||||
import type { FlightResponse } from "@/modules/flight/flight.types";
|
||||
import z from "zod";
|
||||
|
||||
export type FlightClassRequest = z.infer<typeof flightClassRequestSchema>;
|
||||
|
||||
export type FlightClassQuery = z.infer<typeof flightClassParamsSchema>;
|
||||
|
||||
export type FlightClassResponse = {
|
||||
id: string;
|
||||
flight: FlightResponse;
|
||||
class: string;
|
||||
seat_layout: string;
|
||||
baggage: number;
|
||||
cabin_baggage: number;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
};
|
||||
@@ -397,38 +397,6 @@ export class FlightController extends Controller {
|
||||
} satisfies SingleResponse<FlightClassResponse>);
|
||||
}
|
||||
|
||||
async listAllClasses(req: Request, res: Response) {
|
||||
const parseQueryResult = paginationQuerySchema.safeParse(req.query);
|
||||
if (!parseQueryResult.success) {
|
||||
return this.handleZodError(parseQueryResult.error, res, "query");
|
||||
}
|
||||
const query = parseQueryResult.data;
|
||||
|
||||
const count = await orm.em.count(FlightClass);
|
||||
|
||||
const classes = await orm.em.find(
|
||||
FlightClass,
|
||||
{},
|
||||
{
|
||||
limit: query.per_page,
|
||||
offset: (query.page - 1) * query.per_page,
|
||||
orderBy: { createdAt: "DESC" },
|
||||
populate: ["*"],
|
||||
},
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
data: classes.map(this.mapper.mapClassEntityToResponse.bind(this.mapper)),
|
||||
errors: null,
|
||||
meta: {
|
||||
page: query.page,
|
||||
per_page: query.per_page,
|
||||
total_pages: Math.ceil(count / query.per_page),
|
||||
total_items: count,
|
||||
},
|
||||
} satisfies ListResponse<FlightClassResponse>);
|
||||
}
|
||||
|
||||
async listClasses(req: Request, res: Response) {
|
||||
const parseQueryResult = paginationQuerySchema.safeParse(req.query);
|
||||
if (!parseQueryResult.success) {
|
||||
@@ -686,11 +654,6 @@ export class FlightController extends Controller {
|
||||
isAdminMiddleware(this.jwtService, [AdminPermission.createFlightClass]),
|
||||
this.createClass.bind(this),
|
||||
);
|
||||
router.get(
|
||||
"/classes",
|
||||
createOrmContextMiddleware,
|
||||
this.listAllClasses.bind(this),
|
||||
);
|
||||
router.get(
|
||||
"/:id/classes",
|
||||
createOrmContextMiddleware,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Controller } from "@/common/controller";
|
||||
import { createOrmContextMiddleware } from "@/common/middlewares/create-orm-context.middleware";
|
||||
import { paginationQuerySchema } from "@/common/schemas";
|
||||
import type { ListResponse } from "@/common/types";
|
||||
import { TransportationClass } from "@/database/entities/transportation-class.entity";
|
||||
import { orm } from "@/database/orm";
|
||||
import type { TransportationClassMapper } from "@/modules/transportation-class/transportation-class.mapper";
|
||||
import type { TransportationClassResponse } from "@/modules/transportation-class/transportation-class.types";
|
||||
import { Router, type Request, type Response } from "express";
|
||||
|
||||
export class TransportationClassController extends Controller {
|
||||
public constructor(private readonly mapper: TransportationClassMapper) {
|
||||
super();
|
||||
}
|
||||
|
||||
async list(req: Request, res: Response) {
|
||||
const parseQueryResult = paginationQuerySchema.safeParse(req.query);
|
||||
if (!parseQueryResult.success) {
|
||||
return this.handleZodError(parseQueryResult.error, res, "query");
|
||||
}
|
||||
const query = parseQueryResult.data;
|
||||
|
||||
const count = await orm.em.count(TransportationClass);
|
||||
|
||||
const transportationClasses = await orm.em.find(
|
||||
TransportationClass,
|
||||
{},
|
||||
{
|
||||
limit: query.per_page,
|
||||
offset: (query.page - 1) * query.per_page,
|
||||
orderBy: { createdAt: "DESC" },
|
||||
populate: ["*"],
|
||||
},
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
data: transportationClasses.map(
|
||||
this.mapper.mapEntityToResponse.bind(this.mapper),
|
||||
),
|
||||
errors: null,
|
||||
meta: {
|
||||
page: query.page,
|
||||
per_page: query.per_page,
|
||||
total_pages: Math.ceil(count / query.per_page),
|
||||
total_items: count,
|
||||
},
|
||||
} satisfies ListResponse<TransportationClassResponse>);
|
||||
}
|
||||
|
||||
public buildRouter(): Router {
|
||||
const router = Router();
|
||||
router.get("/", createOrmContextMiddleware, this.list.bind(this));
|
||||
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { TransportationClass } from "@/database/entities/transportation-class.entity";
|
||||
import type { TransportationMapper } from "@/modules/transportation/transportation.mapper";
|
||||
import type { TransportationClassResponse } from "@/modules/transportation/transportation.types";
|
||||
|
||||
export class TransportationClassMapper {
|
||||
public constructor(
|
||||
private readonly transportationMapper: TransportationMapper,
|
||||
) {}
|
||||
|
||||
public mapEntityToResponse(
|
||||
transportationClass: TransportationClass,
|
||||
): TransportationClassResponse {
|
||||
return {
|
||||
id: transportationClass.id,
|
||||
transportation: this.transportationMapper.mapEntityToResponse(
|
||||
transportationClass.transportation,
|
||||
),
|
||||
class: transportationClass.class,
|
||||
total_seats: transportationClass.totalSeats,
|
||||
images: transportationClass.images.getItems().map((image) => image.src),
|
||||
created_at: transportationClass.createdAt,
|
||||
updated_at: transportationClass.updatedAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import z from "zod";
|
||||
|
||||
export const transportationClassRequestSchema = z.object({
|
||||
class: z
|
||||
.string("Must be string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(100, "Max 100 characters."),
|
||||
total_seats: z
|
||||
.number("Must be number.")
|
||||
.int("Must be integer.")
|
||||
.positive("Must be positive."),
|
||||
images: z
|
||||
.array(
|
||||
z.base64("Must be base64 string.").nonempty("Must not empty."),
|
||||
"Must be array.",
|
||||
)
|
||||
.nonempty("Must not empty."),
|
||||
});
|
||||
|
||||
export const transportationClassParamsSchema = z.object({
|
||||
id: z
|
||||
.ulid("Must be ulid string.")
|
||||
.nonempty("Must not empty.")
|
||||
.max(30, "Max 30 characters."),
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import type {
|
||||
transportationClassParamsSchema,
|
||||
transportationClassRequestSchema,
|
||||
} from "@/modules/transportation-class/transportation-class.schemas";
|
||||
import type { TransportationResponse } from "@/modules/transportation/transportation.types";
|
||||
import z from "zod";
|
||||
|
||||
export type TransportationClassRequest = z.infer<
|
||||
typeof transportationClassRequestSchema
|
||||
>;
|
||||
|
||||
export type TransportationClassParams = z.infer<
|
||||
typeof transportationClassParamsSchema
|
||||
>;
|
||||
|
||||
export type TransportationClassResponse = {
|
||||
id: string;
|
||||
transportation: TransportationResponse;
|
||||
class: string;
|
||||
total_seats: number;
|
||||
images: string[];
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
};
|
||||
@@ -268,38 +268,6 @@ export class TransportationController extends Controller {
|
||||
} satisfies SingleResponse<TransportationClassResponse>);
|
||||
}
|
||||
|
||||
async listAllClasses(req: Request, res: Response) {
|
||||
const parseQueryResult = paginationQuerySchema.safeParse(req.query);
|
||||
if (!parseQueryResult.success) {
|
||||
return this.handleZodError(parseQueryResult.error, res, "query");
|
||||
}
|
||||
const query = parseQueryResult.data;
|
||||
|
||||
const count = await orm.em.count(TransportationClass);
|
||||
|
||||
const classes = await orm.em.find(
|
||||
TransportationClass,
|
||||
{},
|
||||
{
|
||||
limit: query.per_page,
|
||||
offset: (query.page - 1) * query.per_page,
|
||||
orderBy: { createdAt: "DESC" },
|
||||
populate: ["*"],
|
||||
},
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
data: classes.map(this.mapper.mapClassEntityToResponse.bind(this.mapper)),
|
||||
errors: null,
|
||||
meta: {
|
||||
page: query.page,
|
||||
per_page: query.per_page,
|
||||
total_pages: Math.ceil(count / query.per_page),
|
||||
total_items: count,
|
||||
},
|
||||
} satisfies ListResponse<TransportationClassResponse>);
|
||||
}
|
||||
|
||||
async listClasses(req: Request, res: Response) {
|
||||
const parseQueryResult = paginationQuerySchema.safeParse(req.query);
|
||||
if (!parseQueryResult.success) {
|
||||
@@ -596,11 +564,6 @@ export class TransportationController extends Controller {
|
||||
]),
|
||||
this.createClass.bind(this),
|
||||
);
|
||||
router.get(
|
||||
"/classes",
|
||||
createOrmContextMiddleware,
|
||||
this.listAllClasses.bind(this),
|
||||
);
|
||||
router.get(
|
||||
"/:id/classes",
|
||||
createOrmContextMiddleware,
|
||||
|
||||
Reference in New Issue
Block a user