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 @@ ...@@ -6,7 +6,7 @@
"type": "commonjs", "type": "commonjs",
"main": "./src/index.ts", "main": "./src/index.ts",
"scripts": { "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", "test": "echo \"Error: no test specified\" && exit 1",
"gen:db": "tsx src/scripts/database-gen.ts", "gen:db": "tsx src/scripts/database-gen.ts",
"gen:swagger": "tsx src/scripts/swagger-gen.ts" "gen:swagger": "tsx src/scripts/swagger-gen.ts"
......
...@@ -44,6 +44,43 @@ export default (_express: Application) => { ...@@ -44,6 +44,43 @@ export default (_express: Application) => {
return res.status(500).json({ error: (error as Error).message }); 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) => { ...@@ -6,11 +6,123 @@ export default (_express: Application) => {
const classesProvider = new ClassesProvider(); const classesProvider = new ClassesProvider();
return <Resource>{ 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: { get: {
handler: async (req, res) => { handler: async (req, res) => {
const classId = Number(req.params.id); const { id } = req.params;
const classData = await classesProvider.getClassById(classId);
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); return res.json(classData);
} catch (error) {
return res.status(404).json({ error: "Class not found" });
}
} }
} }
}; };
......
...@@ -44,6 +44,41 @@ export default (_express: Application) => { ...@@ -44,6 +44,41 @@ export default (_express: Application) => {
return res.status(500).json({ error: (error as Error).message }); 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) => { ...@@ -6,12 +6,124 @@ export default (_express: Application) => {
const coursesProvider = new CoursesProvider(); const coursesProvider = new CoursesProvider();
return <Resource>{ 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: { get: {
handler: async (req, res) => { handler: async (req, res) => {
const courseId = Number(req.params.id); const id = req.params.id;
const course = await coursesProvider.getCourseById(courseId);
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); 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 @@ ...@@ -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": { "Course": {
"type": "object", "type": "object",
"example": { "example": {
...@@ -111,6 +167,29 @@ ...@@ -111,6 +167,29 @@
"example": "2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11" "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": { "parameters": {
...@@ -149,6 +228,103 @@ ...@@ -149,6 +228,103 @@
} }
}, },
"paths": { "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": { "/api/v1.0/classes": {
"get": { "get": {
"tags": [ "tags": [
...@@ -183,6 +359,142 @@ ...@@ -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": { "/api/v1.0/courses": {
...@@ -219,6 +531,37 @@ ...@@ -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'; ...@@ -7,6 +7,8 @@ import swaggerFile from '#/docs/swagger/swagger-output.json';
const app = express() const app = express()
const port = 3000 const port = 3000
app.use(express.json());
_autoroutes(app, { _autoroutes(app, {
dir: resolve(__dirname, './controllers'), dir: resolve(__dirname, './controllers'),
log: true log: true
......
...@@ -7,6 +7,12 @@ interface GetAllClassesParams { ...@@ -7,6 +7,12 @@ interface GetAllClassesParams {
pageSize?: number; pageSize?: number;
} }
interface CreateClassInput {
name: string;
description?: string;
status?: string;
}
export class ClassesProvider { export class ClassesProvider {
async getAllClasses(params: GetAllClassesParams) { async getAllClasses(params: GetAllClassesParams) {
...@@ -22,8 +28,39 @@ export class ClassesProvider { ...@@ -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); const classData = await models.classes.findByPk(classId);
return classData; 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 { ...@@ -6,6 +6,13 @@ interface GetAllCoursesParams {
page?: number; page?: number;
pageSize?: number; pageSize?: number;
} }
interface CreateCourseInput {
name: string;
description: string;
status?: string;
}
export class CoursesProvider { export class CoursesProvider {
async getAllCourses(params: GetAllCoursesParams) { async getAllCourses(params: GetAllCoursesParams) {
const { filters, sort, page = 1, pageSize = 10 } = params; const { filters, sort, page = 1, pageSize = 10 } = params;
...@@ -20,8 +27,41 @@ export class CoursesProvider { ...@@ -20,8 +27,41 @@ export class CoursesProvider {
}); });
} }
async getCourseById(courseId: number) { async getCourseById(courseId: string) {
const course = await models.courses.findByPk(courseId); const course = await models.courses.findByPk(courseId);
return course; 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 = { const classSchemas = {
// get schema
Class: { Class: {
type: 'object', type: 'object',
example: { example: {
...@@ -62,6 +63,62 @@ const classSchemas = { ...@@ -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; export default classSchemas;
\ No newline at end of file
...@@ -42,6 +42,30 @@ const courseSchemas = { ...@@ -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; 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