feat: lesson service

This commit is contained in:
Sergey Yarkov 2021-08-09 23:27:57 +03:00
parent a4b663c771
commit 7e24c913a0
4 changed files with 370 additions and 60 deletions

View File

@ -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;
} }
/** /**

View File

@ -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);

View 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;
}
}

View 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);