mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
feat: lesson service
This commit is contained in:
parent
a4b663c771
commit
7e24c913a0
@ -2,10 +2,8 @@ import { Exception, inject, Ioc } from '@adonisjs/core/build/standalone';
|
|||||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models
|
* Services
|
||||||
*/
|
*/
|
||||||
import Course from 'App/Models/Course';
|
|
||||||
import User from 'App/Models/User';
|
|
||||||
import CourseService from 'App/Services/CourseService';
|
import CourseService from 'App/Services/CourseService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,15 +20,9 @@ import BaseController from '../../BaseController';
|
|||||||
export default class CoursesController extends BaseController {
|
export default class CoursesController extends BaseController {
|
||||||
private courseService: CourseService;
|
private courseService: CourseService;
|
||||||
|
|
||||||
private readonly Course: typeof Course;
|
|
||||||
|
|
||||||
private readonly User: typeof User;
|
|
||||||
|
|
||||||
constructor(courseService: CourseService) {
|
constructor(courseService: CourseService) {
|
||||||
super();
|
super();
|
||||||
this.courseService = courseService;
|
this.courseService = courseService;
|
||||||
this.Course = Course;
|
|
||||||
this.User = User;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import { Exception } from '@adonisjs/core/build/standalone';
|
import { Exception, inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models
|
* Services
|
||||||
*/
|
*/
|
||||||
import Lesson from 'App/Models/Lesson';
|
import LessonService from 'App/Services/LessonService';
|
||||||
import Course from 'App/Models/Course';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validators
|
* Validators
|
||||||
@ -15,15 +14,13 @@ import UpdateLessonValidator from 'App/Validators/Lesson/UpdateLessonValidator';
|
|||||||
|
|
||||||
import BaseController from '../../BaseController';
|
import BaseController from '../../BaseController';
|
||||||
|
|
||||||
|
@inject()
|
||||||
export default class LessonsController extends BaseController {
|
export default class LessonsController extends BaseController {
|
||||||
private readonly Lesson: typeof Lesson;
|
private lessonService: LessonService;
|
||||||
|
|
||||||
private readonly Course: typeof Course;
|
constructor(lessonService: LessonService) {
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
super();
|
||||||
this.Lesson = Lesson;
|
this.lessonService = lessonService;
|
||||||
this.Course = Course;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,9 +28,13 @@ export default class LessonsController extends BaseController {
|
|||||||
* GET /lessons
|
* GET /lessons
|
||||||
*/
|
*/
|
||||||
public async list(ctx: HttpContextContract) {
|
public async list(ctx: HttpContextContract) {
|
||||||
const lessons = await this.Lesson.query();
|
const result = await this.lessonService.fetchLessons();
|
||||||
|
|
||||||
return this.sendResponse(ctx, lessons, 'Fetched list lessons.');
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,9 +42,13 @@ export default class LessonsController extends BaseController {
|
|||||||
* GET /lessons/:id
|
* GET /lessons/:id
|
||||||
*/
|
*/
|
||||||
public async show(ctx: HttpContextContract) {
|
public async show(ctx: HttpContextContract) {
|
||||||
const lesson = await this.Lesson.findOrFail(ctx.params.id);
|
const result = await this.lessonService.fetchLesson(ctx.params.id);
|
||||||
|
|
||||||
return this.sendResponse(ctx, lesson, 'Fetched lesson by id.');
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,20 +57,13 @@ export default class LessonsController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
public async create(ctx: HttpContextContract) {
|
public async create(ctx: HttpContextContract) {
|
||||||
const payload = await ctx.request.validate(CreateLessonValidator);
|
const payload = await ctx.request.validate(CreateLessonValidator);
|
||||||
const course = await this.Course.findOrFail(payload.course_id);
|
const result = await this.lessonService.createLesson(payload);
|
||||||
const lesson = await this.Lesson.create({
|
|
||||||
title: payload.title,
|
|
||||||
description: payload.description,
|
|
||||||
});
|
|
||||||
|
|
||||||
await lesson.related('course').associate(course);
|
if (!result.success && result.error) {
|
||||||
await lesson.related('content').create({
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
video_url: payload.video_url,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
await lesson.load('content');
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
|
||||||
return this.sendResponse(ctx, lesson, 'Created new lesson.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,11 +71,13 @@ export default class LessonsController extends BaseController {
|
|||||||
* DELETE /lessons/:id
|
* DELETE /lessons/:id
|
||||||
*/
|
*/
|
||||||
public async delete(ctx: HttpContextContract) {
|
public async delete(ctx: HttpContextContract) {
|
||||||
const lesson = await this.Lesson.findOrFail(ctx.params.id);
|
const result = await this.lessonService.deleteLesson(ctx.params.id);
|
||||||
|
|
||||||
await lesson.delete();
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
return this.sendResponse(ctx, lesson, 'Lesson deleted.');
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,17 +86,13 @@ export default class LessonsController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
public async update(ctx: HttpContextContract) {
|
public async update(ctx: HttpContextContract) {
|
||||||
const payload = await ctx.request.validate(UpdateLessonValidator);
|
const payload = await ctx.request.validate(UpdateLessonValidator);
|
||||||
const lesson = await this.Lesson.firstOrFail(ctx.params.id);
|
const result = await this.lessonService.updateLesson(ctx.params.id, payload);
|
||||||
|
|
||||||
await lesson
|
if (!result.success && result.error) {
|
||||||
.merge({
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
course_id: payload.course_id,
|
}
|
||||||
title: payload.title,
|
|
||||||
description: payload.description,
|
|
||||||
})
|
|
||||||
.save();
|
|
||||||
|
|
||||||
return this.sendResponse(ctx, lesson, 'Lesson updated.');
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,21 +100,14 @@ export default class LessonsController extends BaseController {
|
|||||||
* GET /lessons/:id/content
|
* GET /lessons/:id/content
|
||||||
*/
|
*/
|
||||||
public async getContent(ctx: HttpContextContract) {
|
public async getContent(ctx: HttpContextContract) {
|
||||||
const user = await ctx.auth.use('api').authenticate();
|
const result = await this.lessonService.fetchLessonContent(ctx.params.id, ctx.auth);
|
||||||
const lesson = await this.Lesson.query()
|
|
||||||
.preload('content')
|
|
||||||
.preload('course')
|
|
||||||
.where('id', ctx.params.id)
|
|
||||||
.firstOrFail();
|
|
||||||
|
|
||||||
await user.load('courses');
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
const userHasCourse = user.courses.find(course => course.id === lesson.course.id);
|
|
||||||
|
|
||||||
if (!userHasCourse) {
|
|
||||||
throw new Exception('The user is not a student of this course.', 403, 'E_ACCESS_DENIED');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sendResponse(ctx, lesson.content, 'Lesson content fetched.');
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new Ioc().make(LessonsController);
|
||||||
|
|||||||
110
app/Repositories/LessonRepository.ts
Normal file
110
app/Repositories/LessonRepository.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Models
|
||||||
|
*/
|
||||||
|
import Course from 'App/Models/Course';
|
||||||
|
import Lesson from 'App/Models/Lesson';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validators
|
||||||
|
*/
|
||||||
|
import CreateLessonValidator from 'App/Validators/Lesson/CreateLessonValidator';
|
||||||
|
import UpdateLessonValidator from 'App/Validators/Lesson/UpdateLessonValidator';
|
||||||
|
|
||||||
|
export default class LessonRepository {
|
||||||
|
private Lesson: typeof Lesson;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.Lesson = Lesson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all lessons
|
||||||
|
*
|
||||||
|
* @returns Lessons
|
||||||
|
*/
|
||||||
|
public async getAll(): Promise<Lesson[]> {
|
||||||
|
const lessons = await this.Lesson.query();
|
||||||
|
return lessons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get one lesson by id
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @returns Lesson or null
|
||||||
|
*/
|
||||||
|
public async getById(id: string | number): Promise<Lesson | null> {
|
||||||
|
const lesson = await this.Lesson.query().where('id', id).first();
|
||||||
|
return lesson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content of lesson by id
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @returns Content of lesson or null
|
||||||
|
*/
|
||||||
|
public async getContentById(id: string | number): Promise<Lesson['content'] | null> {
|
||||||
|
const lesson = await this.Lesson.query().preload('content').where('id', id).first();
|
||||||
|
return lesson && lesson.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create lesson
|
||||||
|
*
|
||||||
|
* @param course Course of lesson
|
||||||
|
* @returns Lesson
|
||||||
|
*/
|
||||||
|
public async create(course: Course, data: CreateLessonValidator['schema']['props']): Promise<Lesson> {
|
||||||
|
const lesson = await this.Lesson.create({
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
await lesson.related('course').associate(course);
|
||||||
|
await lesson.related('content').create({ video_url: data.video_url });
|
||||||
|
await lesson.load('content');
|
||||||
|
|
||||||
|
return lesson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update lesson
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @returns Updated lesson
|
||||||
|
*/
|
||||||
|
public async update(id: string | number, data: UpdateLessonValidator['schema']['props']): Promise<Lesson | null> {
|
||||||
|
const lesson = await this.Lesson.query().where('id', id).first();
|
||||||
|
|
||||||
|
if (lesson) {
|
||||||
|
await lesson
|
||||||
|
.merge({
|
||||||
|
course_id: data.course_id,
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
})
|
||||||
|
.save();
|
||||||
|
|
||||||
|
return lesson;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete lesson
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @returns Deleted lesson or null
|
||||||
|
*/
|
||||||
|
public async delete(id: string | number): Promise<Lesson | null> {
|
||||||
|
const lesson = await this.Lesson.query().where('id', id).first();
|
||||||
|
|
||||||
|
if (lesson) {
|
||||||
|
await lesson.delete();
|
||||||
|
return lesson;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
219
app/Services/LessonService.ts
Normal file
219
app/Services/LessonService.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
|
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Datatypes
|
||||||
|
*/
|
||||||
|
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
||||||
|
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||||
|
/**
|
||||||
|
* Repositories
|
||||||
|
*/
|
||||||
|
import CourseRepository from 'App/Repositories/CourseRepository';
|
||||||
|
import LessonRepository from 'App/Repositories/LessonRepository';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validators
|
||||||
|
*/
|
||||||
|
import CreateLessonValidator from 'App/Validators/Lesson/CreateLessonValidator';
|
||||||
|
import UpdateLessonValidator from 'App/Validators/Lesson/UpdateLessonValidator';
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class LessonService {
|
||||||
|
private courseRepository: CourseRepository;
|
||||||
|
|
||||||
|
private lessonRepository: LessonRepository;
|
||||||
|
|
||||||
|
constructor(courseRepository: CourseRepository, lessonRepository: LessonRepository) {
|
||||||
|
this.courseRepository = courseRepository;
|
||||||
|
this.lessonRepository = lessonRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch list of lessons
|
||||||
|
*
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async fetchLessons(): Promise<IResponse> {
|
||||||
|
const lessons = await this.lessonRepository.getAll();
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Fetched all lessons.',
|
||||||
|
data: lessons,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch one lesson by id
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async fetchLesson(id: string | number): Promise<IResponse> {
|
||||||
|
const lesson = await this.lessonRepository.getById(id);
|
||||||
|
|
||||||
|
if (!lesson) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Lesson not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Fetched lesson.',
|
||||||
|
data: lesson,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create lesson
|
||||||
|
*
|
||||||
|
* @param data Data input
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async createLesson(data: CreateLessonValidator['schema']['props']): Promise<IResponse> {
|
||||||
|
const course = await this.courseRepository.getById(data.course_id);
|
||||||
|
|
||||||
|
if (!course) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Course not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const lesson = await this.lessonRepository.create(course, data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.CREATED,
|
||||||
|
message: 'Lesson created.',
|
||||||
|
data: lesson,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete lesson
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async deleteLesson(id: string | number): Promise<IResponse> {
|
||||||
|
const lesson = await this.lessonRepository.delete(id);
|
||||||
|
|
||||||
|
if (!lesson) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Lesson not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Lesson deleted.',
|
||||||
|
data: lesson,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update lesson
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @param data Data input
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async updateLesson(id: string | number, data: UpdateLessonValidator['schema']['props']): Promise<IResponse> {
|
||||||
|
const lesson = await this.lessonRepository.update(id, data);
|
||||||
|
|
||||||
|
if (!lesson) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Lesson not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Lesson updated.',
|
||||||
|
data: lesson,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch content of lesson by id
|
||||||
|
*
|
||||||
|
* @param id Lesson id
|
||||||
|
* @param auth AuthContract
|
||||||
|
* @returns Response
|
||||||
|
*/
|
||||||
|
public async fetchLessonContent(id: string | number, auth: AuthContract): Promise<IResponse> {
|
||||||
|
const user = await auth.use('api').authenticate();
|
||||||
|
await user.load('courses');
|
||||||
|
|
||||||
|
const lesson = await this.lessonRepository.getById(id);
|
||||||
|
|
||||||
|
if (!lesson) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Lesson not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await lesson.load('course');
|
||||||
|
|
||||||
|
const userHasCourse = user.courses.find(course => course.id === lesson.course.id);
|
||||||
|
|
||||||
|
if (!userHasCourse) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.FORBIDDEN,
|
||||||
|
message: 'The user is not a student of this course.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_FORBIDDEN',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await lesson.load('content');
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Fetched lesson content.',
|
||||||
|
data: lesson.content,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Ioc().make(LessonService);
|
||||||
Loading…
x
Reference in New Issue
Block a user