Commit b6c64ab8 authored by Phạm Quang Bảo's avatar Phạm Quang Bảo

feat(challenge_2); add method CUD for api classes, courses and optimize script

parent f3749432
......@@ -6,7 +6,7 @@
"type": "commonjs",
"main": "./src/index.ts",
"scripts": {
"dev": "tsx watch ./src/index.ts",
"dev": "tsx ./src/scripts/database-gen.ts && tsx ./src/scripts/swagger-gen.ts && tsx watch ./src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1",
"gen:db": "tsx src/scripts/database-gen.ts",
"gen:swagger": "tsx src/scripts/swagger-gen.ts"
......
......@@ -44,6 +44,43 @@ export default (_express: Application) => {
return res.status(500).json({ error: (error as Error).message });
}
}
},
/**
* @openapi
* /api/v1.0/classes:
* post:
* tags: [Classes]
* description: Thêm một lớp học mới vào hệ thống
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/CreateClassInput"
* responses:
* 201:
* description: Tạo lớp học mới thành công
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/Class"
* 400:
* description: Dữ liệu đầu vào không hợp lệ (Thiếu trường, sai định dạng)
* 500:
* description: Lỗi hệ thống phía Server
*/
post: {
handler: async (req, res) => {
try {
const newClass = await classesProvider.createClass(req.body);
return res.status(201).json(newClass);
} catch (error) {
console.error('Error creating class:', error);
return res.status(400).json({ error: (error as Error).message });
}
}
}
};
};
\ No newline at end of file
......@@ -6,11 +6,123 @@ export default (_express: Application) => {
const classesProvider = new ClassesProvider();
return <Resource>{
/**
* @openapi
* /api/v1.0/classes/{id}:
* get:
* tags: [Classes]
* parameters:
* - name: id
* in: path
* required: true
* description: Class ID
* schema:
* type: string
* responses:
* 200:
* description: Trả về thông tin lớp học thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Class"
*/
get: {
handler: async (req, res) => {
const classId = Number(req.params.id);
const classData = await classesProvider.getClassById(classId);
const { id } = req.params;
if (!id || typeof id !== 'string') {
return res.status(400).json({ error: "ID không hợp lệ!" });
}
try {
const classData = await classesProvider.getClassById(id);
return res.json(classData);
} catch (error) {
return res.status(404).json({ error: "Class not found" });
}
}
},
/**
* @openapi
* /api/v1.0/classes/{id}:
* put:
* tags: [Classes]
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: string
*
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/PostMutate"
*
* responses:
* 200:
* description: Trả về thông tin lớp học thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Class"
*/
put: {
handler: async (req, res) => {
const { id } = req.params;
if (!id || typeof id !== 'string') {
return res.status(400).json({ error: "ID không hợp lệ!" });
}
try {
const classData = await classesProvider.updateClassById(id, req.body);
return res.json(classData);
} catch (error) {
return res.status(404).json({ error: "Class not found" });
}
}
},
/**
* @openapi
* /api/v1.0/classes/{id}:
* delete:
* tags: [Classes]
* parameters:
* - name: id
* in: path
* required: true
* description: Class ID
* schema:
* type: string
* requestBody:
* required: true
* responses:
* 200:
* description: Xóa lớp học thành công
*/
delete: {
handler: async (req, res) => {
const { id } = req.params;
if (!id || typeof id !== 'string') {
return res.status(400).json({ error: "ID không hợp lệ!" });
}
try {
const classData = await classesProvider.deleteClassById(id);
return res.json(classData);
} catch (error) {
return res.status(404).json({ error: "Class not found" });
}
}
}
};
......
......@@ -44,6 +44,41 @@ export default (_express: Application) => {
return res.status(500).json({ error: (error as Error).message });
}
}
},
/**
* @openapi
* /api/v1.0/courses:
* post:
* tags: [Courses]
* description: Thêm một khóa học mới vào hệ thống
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/CreateCourseInput"
* responses:
* 201:
* description: Tạo khóa học mới thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Course"
*/
post: {
handler: async (req, res) => {
try {
const newCourse = await coursesProvider.createCourse(req.body);
return res.status(201).json(newCourse);
} catch (error) {
console.error('Error creating course:', error);
return res.status(400).json({ error: (error as Error).message });
}
}
}
};
};
\ No newline at end of file
......@@ -6,12 +6,124 @@ export default (_express: Application) => {
const coursesProvider = new CoursesProvider();
return <Resource>{
/**
* @openapi
* /api/v1.0/courses/{id}:
* get:
* tags: [Courses]
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: string
*
* responses:
* 200:
* description: Trả về thông tin khóa học thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Course"
*/
get: {
handler: async (req, res) => {
const courseId = Number(req.params.id);
const course = await coursesProvider.getCourseById(courseId);
const id = req.params.id;
if (!id || typeof id !== 'string') {
return res.status(400).json({ error: "ID không hợp lệ!" });
}
const course = await coursesProvider.getCourseById(id);
return res.json(course);
}
},
/**
* @openapi
* /api/v1.0/courses/{id}:
* put:
* tags: [Courses]
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: string
*
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/CreateCourseInput"
*
* responses:
* 200:
* description: Trả về thông tin khóa học thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Course"
*/
put: {
handler: async (req, res) => {
const { id } = req.params;
if (!id || typeof id !== 'string') {
return res.status(400).json({ error: "ID không hợp lệ!" });
}
try {
const updateCourse = await coursesProvider.updateCourse(id, req.body);
return res.json(updateCourse);
} catch (error) {
return res.status(500).json({ error: "Lỗi khi cập nhật khóa học!" });
}
}
},
/**
* @openapi
* /api/v1.0/courses/{id}:
* delete:
* tags: [Courses]
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: string
*
* responses:
* 200:
* description: Xóa khóa học thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Course"
*/
delete: {
handler: async (req, res) => {
const { id } = req.params;
if (!id || typeof id !== 'string') {
return res.status(400).json({ error: "ID không hợp lệ!" });
}
try {
const deletedCourse = await coursesProvider.deleteCourse(id);
return res.json(deletedCourse);
} catch (error) {
return res.status(500).json({ error: "Lỗi khi xóa khóa học!" });
}
}
}
};
};
\ No newline at end of file
......@@ -69,6 +69,62 @@
}
}
},
"CreateClassInput": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"example": "Lớp 12A1"
},
"description": {
"type": "string",
"nullable": true,
"example": "Lớp chuyên toán"
},
"status": {
"type": "string",
"nullable": true,
"example": "active"
},
"course_id": {
"type": "string",
"format": "uuid",
"nullable": true,
"example": "7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10"
}
}
},
"PostMutate": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"example": "Lớp 12A1"
},
"description": {
"type": "string",
"nullable": true,
"example": "Lớp chuyên toán"
},
"status": {
"type": "string",
"nullable": true,
"example": "active"
},
"course_id": {
"type": "string",
"format": "uuid",
"nullable": true,
"example": "7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10"
}
}
},
"Course": {
"type": "object",
"example": {
......@@ -111,6 +167,29 @@
"example": "2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11"
}
}
},
"CreateCourseInput": {
"type": "object",
"example": {
"name": "Khóa học 12A1",
"description": "Khóa học chuyên toán"
},
"properties": {
"name": {
"type": "string",
"example": "Khóa học 12A1"
},
"description": {
"type": "string",
"nullable": true,
"example": "Khóa học chuyên toán"
},
"status": {
"type": "string",
"nullable": true,
"example": "active"
}
}
}
},
"parameters": {
......@@ -149,6 +228,103 @@
}
},
"paths": {
"/api/v1.0/classes/{id}": {
"get": {
"tags": [
"Classes"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Class ID",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Trả về thông tin lớp học thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Class"
}
}
}
}
}
}
},
"put": {
"tags": [
"Classes"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostMutate"
}
}
}
},
"responses": {
"200": {
"description": "Trả về thông tin lớp học thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Class"
}
}
}
}
}
}
},
"delete": {
"tags": [
"Classes"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Class ID",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true
},
"responses": {
"200": {
"description": "Xóa lớp học thành công"
}
}
}
},
"/api/v1.0/classes": {
"get": {
"tags": [
......@@ -183,6 +359,142 @@
}
}
}
},
"post": {
"tags": [
"Classes"
],
"description": "Thêm một lớp học mới vào hệ thống",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateClassInput"
}
}
}
},
"responses": {
"201": {
"description": "Tạo lớp học mới thành công",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Class"
}
}
}
},
"400": {
"description": "Dữ liệu đầu vào không hợp lệ (Thiếu trường, sai định dạng)"
},
"500": {
"description": "Lỗi hệ thống phía Server"
}
}
}
},
"/api/v1.0/courses/{id}": {
"get": {
"tags": [
"Courses"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Trả về thông tin khóa học thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Course"
}
}
}
}
}
}
},
"put": {
"tags": [
"Courses"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateCourseInput"
}
}
}
},
"responses": {
"200": {
"description": "Trả về thông tin khóa học thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Course"
}
}
}
}
}
}
},
"delete": {
"tags": [
"Courses"
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Xóa khóa học thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Course"
}
}
}
}
}
}
}
},
"/api/v1.0/courses": {
......@@ -219,6 +531,37 @@
}
}
}
},
"post": {
"tags": [
"Courses"
],
"description": "Thêm một khóa học mới vào hệ thống",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateCourseInput"
}
}
}
},
"responses": {
"201": {
"description": "Tạo khóa học mới thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Course"
}
}
}
}
}
}
}
}
},
......
......@@ -7,6 +7,8 @@ import swaggerFile from '#/docs/swagger/swagger-output.json';
const app = express()
const port = 3000
app.use(express.json());
_autoroutes(app, {
dir: resolve(__dirname, './controllers'),
log: true
......
......@@ -7,6 +7,12 @@ interface GetAllClassesParams {
pageSize?: number;
}
interface CreateClassInput {
name: string;
description?: string;
status?: string;
}
export class ClassesProvider {
async getAllClasses(params: GetAllClassesParams) {
......@@ -22,8 +28,39 @@ export class ClassesProvider {
});
}
async getClassById(classId: number) {
async createClass(data: CreateClassInput) {
const newClass = await models.classes.create({
name: data.name,
description: data.description || "",
status: data.status || "active"
});
return newClass;
}
async getClassById(classId: string) {
const classData = await models.classes.findByPk(classId);
return classData;
}
async updateClassById(classId: string, data: CreateClassInput) {
const updatedClass = await models.classes.update(
{
name: data.name || "",
description: data.description || "",
status: data.status || "active"
},
{
where: { id: classId },
returning: true,
}
);
return updatedClass;
}
async deleteClassById(classId: string) {
const deletedClass = await models.classes.destroy({
where: { id: classId }
});
return deletedClass;
}
}
\ No newline at end of file
......@@ -6,6 +6,13 @@ interface GetAllCoursesParams {
page?: number;
pageSize?: number;
}
interface CreateCourseInput {
name: string;
description: string;
status?: string;
}
export class CoursesProvider {
async getAllCourses(params: GetAllCoursesParams) {
const { filters, sort, page = 1, pageSize = 10 } = params;
......@@ -20,8 +27,41 @@ export class CoursesProvider {
});
}
async getCourseById(courseId: number) {
async getCourseById(courseId: string) {
const course = await models.courses.findByPk(courseId);
return course;
}
async createCourse(input: CreateCourseInput) {
const course = await models.courses.create(
{
name: input.name,
description: input.description,
status: input.status || "active"
}
);
return course;
}
async updateCourse(courseId: string, data: CreateCourseInput) {
const updatedCourse = await models.courses.update(
{
name: data.name,
description: data.description,
status: data.status || "active"
},
{
where: { id: courseId },
returning: true,
}
);
return updatedCourse;
}
async deleteCourse(courseId: string) {
const deletedCourse = await models.courses.destroy({
where: { id: courseId }
});
return deletedCourse;
}
}
\ No newline at end of file
const classSchemas = {
// get schema
Class: {
type: 'object',
example: {
......@@ -62,6 +63,62 @@ const classSchemas = {
},
},
},
// post schema
CreateClassInput: {
type: 'object',
required: ['name'],
properties: {
name: {
type: 'string',
example: 'Lớp 12A1',
},
description: {
type: 'string',
nullable: true,
example: 'Lớp chuyên toán',
},
status: {
type: 'string',
nullable: true,
example: 'active',
},
course_id: {
type: 'string',
format: 'uuid',
nullable: true,
example: '7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10',
},
},
},
// put schema
PostMutate: {
type: 'object',
required: ['name'],
properties: {
name: {
type: 'string',
example: 'Lớp 12A1',
},
description: {
type: 'string',
nullable: true,
example: 'Lớp chuyên toán',
},
status: {
type: 'string',
nullable: true,
example: 'active',
},
course_id: {
type: 'string',
format: 'uuid',
nullable: true,
example: '7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10',
},
},
}
};
export default classSchemas;
\ No newline at end of file
......@@ -42,6 +42,30 @@ const courseSchemas = {
}
},
},
CreateCourseInput: {
type: 'object',
example: {
name: 'Khóa học 12A1',
description: 'Khóa học chuyên toán',
},
properties: {
name: {
type: 'string',
example: 'Khóa học 12A1',
},
description: {
type: 'string',
nullable: true,
example: 'Khóa học chuyên toán',
},
status: {
type: 'string',
nullable: true,
example: 'active',
}
},
},
};
export default courseSchemas;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment