Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMPLEMENT THE COURSE CRUD LOGIC #8

Merged
merged 9 commits into from
Feb 1, 2025
27 changes: 16 additions & 11 deletions config/config.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { DataSource } from 'typeorm';
import User from '../entities/user.entity.js';
import Job from '../entities/job.entity.js';
import dotenv from "dotenv"
import { DataSource } from "typeorm";
import { Course } from "../entities/course.entity.js"
import { UserCourse } from "../entities/userCourse.entity.js"
import UserEntity from "../entities/user.entity.js";
import Job from "../entities/job.entity.js";

dotenv.config()

const AppDataSource = new DataSource({
type: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
type: "postgres",
host: process.env.DB_HOST,
port: Number.parseInt(process.env.DB_PORT),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: true,
dropSchema: false,
logging: false,
entities: [User, Job],
logging: true,
entities: [UserEntity, Course, UserCourse, Job],
subscribers: [],
migrations: [],
});
})

export default AppDataSource;

export default AppDataSource;
126 changes: 126 additions & 0 deletions controllers/course.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { CourseService } from "../services/courseServices.js"
import courseValidation from "../validation/course.validation.js"

class CourseController {
async createCourse(req, res) {
try {
const { error, value } = courseValidation.createCourse.validate(req.body)
if (error) {
return res.status(400).json({ message: error.details[0].message })
}

const course = await CourseService.createCourse(value)
res.status(201).json(course)
} catch (error) {
if (error.message === "Course already exists") {
res.status(409).json({ message: error.message })
} else {
res.status(400).json({ message: "Error creating course", error: error.message })
}
}
}

async getAllCourses(req, res) {
try {
const courses = await CourseService.getAllCourses()
res.json(courses)
} catch (error) {
res.status(500).json({ message: "Error fetching courses", error: error.message })
}
}

async getCourseById(req, res) {
try {
const { error, value } = courseValidation.courseIdParam.validate({ id: req.params.id })
if (error) {
return res.status(400).json({ message: error.details[0].message })
}

const course = await CourseService.getCourseById(value.id)
if (course) {
res.json(course)
} else {
res.status(404).json({ message: "Course not found" })
}
} catch (error) {
res.status(500).json({ message: "Error fetching course", error: error.message })
}
}

async updateCourse(req, res) {
try {
const idValidation = courseValidation.courseIdParam.validate({ id: req.params.id })
if (idValidation.error) {
return res.status(400).json({ message: idValidation.error.details[0].message })
}

const { error, value } = courseValidation.updateCourse.validate(req.body)
if (error) {
return res.status(400).json({ message: error.details[0].message })
}

const course = await CourseService.updateCourse(idValidation.value.id, value)
if (course) {
res.json(course)
} else {
res.status(404).json({ message: "Course not found" })
}
} catch (error) {
if (error.message === "Course already exists") {
res.status(409).json({ message: error.message })
} else {
res.status(400).json({ message: "Error updating course", error: error.message })
}
}
}

async deleteCourse(req, res) {
try {
const { error, value } = courseValidation.courseIdParam.validate({ id: req.params.id })
if (error) {
return res.status(400).json({ message: error.details[0].message })
}

await CourseService.deleteCourse(value.id)
res.status(204).send()
} catch (error) {
if (error.message === "Course not found") {
res.status(404).json({ message: error.message })
} else {
res.status(500).json({ message: "Error deleting course", error: error.message })
}
}
}

async updateCourseVideo(req, res) {
try {
const { error: idError, value: idValue } = courseValidation.courseIdParam.validate({ id: req.params.id })
if (idError) {
return res.status(400).json({ message: idError.details[0].message })
}

if (!req.file) {
return res.status(400).json({ message: "No video file uploaded" })
}

const videoUrl = `/uploads/courses/videos/${req.file.filename}`

const { error, value } = courseValidation.updateCourseVideo.validate({ videoUrl })
if (error) {
return res.status(400).json({ message: error.details[0].message })
}

const updatedCourse = await CourseService.updateCourseVideo(idValue.id, value.videoUrl)
if (updatedCourse) {
res.json(updatedCourse)
} else {
res.status(404).json({ message: "Course not found" })
}
} catch (error) {
res.status(500).json({ message: "Error updating course video", error: error.message })
}
}
}

export default new CourseController()

65 changes: 65 additions & 0 deletions entities/course.entity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { EntitySchema } from "typeorm"

export const Course = new EntitySchema({
name: "Course",
tableName: "courses",
columns: {
id: {
type: "int",
primary: true,
generated: true,
},
title: {
type: "varchar",
unique: true,
nullable: false,
},
description: {
type: "text",
nullable: true,
},
instructorId: {
type: "int",
nullable: false,
},
level: {
type: "varchar",
nullable: true,
},
duration: {
type: "int",
nullable: true,
},
videoUrl: {
type: "varchar",
nullable: true,
},
createdAt: {
type: "timestamp",
createDate: true,
},
},
relations: {
users: {
type: "many-to-many",
target: "UserEntity",
joinTable: {
name: "user_courses",
joinColumn: {
name: "courseId",
referencedColumnName: "id",
},
inverseJoinColumn: {
name: "userId",
referencedColumnName: "id",
},
},
},
userCourses: {
type: "one-to-many",
target: "UserCourse",
inverseSide: "course",
},
},
})

28 changes: 23 additions & 5 deletions entities/user.entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const UserEntity = new EntitySchema({
type: 'varchar',
length: 20,
},
// Generalizes to job seeker/employer/institution name
name: {
type: 'varchar',
length: 100,
Expand All @@ -26,18 +25,15 @@ const UserEntity = new EntitySchema({
type: 'varchar',
unique: true,
},
// Tutor-specific (nullable)
title: {
type: 'varchar',
length: 100,
nullable: true,
},
// Used as bio (Job Seeker, Tutor, Institution)/description (Company)
bio: {
type: 'text',
nullable: true,
},
// Tutor-specific (nullable)
skills: {
type: 'simple-array',
nullable: true,
Expand All @@ -47,11 +43,33 @@ const UserEntity = new EntitySchema({
createDate: true,
},
},
relations: {
courses: {
type: 'many-to-many',
target: 'Course',
joinTable: {
name: 'user_courses',
joinColumn: {
name: 'userId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'courseId',
referencedColumnName: 'id',
},
},
},
userCourses: {
type: 'one-to-many',
target: 'UserCourse',
inverseSide: 'user',
},
},
indices: [
{ columns: ['walletAddress'] },
{ columns: ['email'] },
{ columns: ['role'] }, // Index role if querying by it frequently
],
});

export default UserEntity;
export default UserEntity
45 changes: 45 additions & 0 deletions entities/userCourse.entity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { EntitySchema } from "typeorm"

export const UserCourse = new EntitySchema({
name: "UserCourse",
tableName: "user_courses",
columns: {
userId: {
primary: true,
type: "int",
},
courseId: {
primary: true,
type: "int",
},
progress: {
type: "int",
default: 0,
},
enrollmentDate: {
type: "timestamp",
createDate: true,
},
},
relations: {
user: {
type: "many-to-one",
target: "User",
joinColumn: {
name: "userId",
referencedColumnName: "id",
},
onDelete: "CASCADE",
},
course: {
type: "many-to-one",
target: "Course",
joinColumn: {
name: "courseId",
referencedColumnName: "id",
},
onDelete: "CASCADE",
},
},
})

46 changes: 46 additions & 0 deletions http/courseRoute.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@baseUrl = http://localhost:3000/api/courses

### Create a new course
POST {{baseUrl}}
Content-Type: application/json

{
"title": "Advance Blockchain",
"description": "Learn the basics of blockchain technology",
"instructorId": 1,
"level": "beginner",
"duration": 5
}

### Get all courses
GET {{baseUrl}}

### Get a specific course
GET {{baseUrl}}/1

### Update a course
PUT {{baseUrl}}/1
Content-Type: application/json
{
"title": "Advanced Blockchain Concepts",
"description": "Dive deeper into blockchain technology and its applications"
}

### Delete a course
DELETE {{baseUrl}}/4

### Create another course
POST {{baseUrl}}
Content-Type: application/json

{
"title": "Advance Smart Contract Development",
"description": "Learn how to create and deploy smart contracts on blockchain platforms",
"instructorId": 2,
"level": "beginner",
"duration": 5
}

### Get all courses after operations
GET {{baseUrl}}

Loading