From 81e511ed1950f4189810c592afcdaa9b8ff0ce8f Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Mon, 17 Jan 2022 22:50:15 +0300 Subject: [PATCH] feat: lesson updating --- .../Http/Api/v1/LessonsController.ts | 2 +- app/Repositories/CourseRepository.ts | 5 +- app/Repositories/LessonRepository.ts | 172 +++++++++++------- .../Lesson/UpdateLessonValidator.ts | 17 +- routes/apis/v1/lessons.ts | 2 +- 5 files changed, 126 insertions(+), 72 deletions(-) diff --git a/app/Controllers/Http/Api/v1/LessonsController.ts b/app/Controllers/Http/Api/v1/LessonsController.ts index 0d81bb5..c7a6e1c 100644 --- a/app/Controllers/Http/Api/v1/LessonsController.ts +++ b/app/Controllers/Http/Api/v1/LessonsController.ts @@ -88,7 +88,7 @@ export default class LessonsController extends BaseController { /** * Update lesson by id. - * PUT /lessons/:id + * PATCH /lessons/:id */ public async update(ctx: HttpContextContract) { const payload = await ctx.request.validate(UpdateLessonValidator); diff --git a/app/Repositories/CourseRepository.ts b/app/Repositories/CourseRepository.ts index 0581e30..6ac9cea 100644 --- a/app/Repositories/CourseRepository.ts +++ b/app/Repositories/CourseRepository.ts @@ -283,10 +283,11 @@ export default class CourseRepository { /** * Load other updated data */ - course.load('category'); - course.load('teacher'); + await course.load('category'); + await course.load('teacher'); await course.save(); + return course; } diff --git a/app/Repositories/LessonRepository.ts b/app/Repositories/LessonRepository.ts index 256fef2..e2fc6c3 100644 --- a/app/Repositories/LessonRepository.ts +++ b/app/Repositories/LessonRepository.ts @@ -1,4 +1,5 @@ import Drive from '@ioc:Adonis/Core/Drive'; +import { MultipartFileContract } from '@ioc:Adonis/Core/BodyParser'; import { cuid } from '@ioc:Adonis/Core/Helpers'; /** @@ -115,58 +116,15 @@ export default class LessonRepository { /** * Move video file to disk and save to database */ - const videoName = `${cuid()}.${data.video.extname}`; - await data.video.moveToDisk('videos', { name: videoName }); - if (data.video.state === 'moved') { - await lesson.related('video').create({ - name: videoName, - clientName: data.video.clientName, - ext: data.video.extname, - size: data.video.size, - url: `/video/${videoName}`, - }); - } + await this.createVideo(lesson, data.video); /** * Create materials */ if (data.materials) { - /** - * Move each file to disk - */ - const files = await Promise.all( - data.materials.map(async file => { - const name = `${cuid()}.${file.extname}`; - await file.moveToDisk('materials', { name }); - return { file, name }; - }) - ); - - /** - * Create lesson material - */ - const materials = await Promise.all( - files.map(async ({ file, name }) => { - if (file.state === 'moved' && file.fileName && file.extname) { - const material = new LessonMaterial(); - - material.name = name; - material.size = file.size; - material.clientName = file.clientName; - material.ext = file.extname; - - return material; - } - - return null; - }) - ); - - /** - * Save lesson materials to database - */ - await lesson.related('materials').createMany(materials.filter((l): l is LessonMaterial => l !== null)); + await this.createMaterials(lesson, data.materials); } + /** * Load data */ @@ -182,19 +140,48 @@ export default class LessonRepository { * @returns Updated lesson */ public async update(id: string | number, data: UpdateLessonValidator['schema']['props']): Promise { - const lesson = await this.Lesson.query().where('id', id).first(); + const lesson = await this.Lesson.query().preload('video').preload('color').where('id', id).first(); if (lesson) { - await lesson - .merge({ - course_id: data.course_id, - title: data.title, - description: data.description, - }) - .save(); + /** + * Update files + */ + lesson.merge({ + title: data.title, + description: data.description, + course_id: data.course_id, + }); + + /** + * Update video + */ + if (data.video) { + /** + * Delete video from drive and database + */ + await this.Drive.delete(`videos/${lesson.video.name}`); + await lesson.related('video').query().delete(); + + /** + * Upload new video to drive and save to database + */ + await this.createVideo(lesson, data.video); + } + + /** + * Update materials + */ + if (data.materials && data.materials !== null) { + await this.deleteMaterials(lesson); + await this.createMaterials(lesson, data.materials); + } else if (data.materials === null) await this.deleteMaterials(lesson); + + await lesson.load(loader => loader.load('video').load('materials')); + await lesson.save(); return lesson; } + return null; } @@ -205,23 +192,14 @@ export default class LessonRepository { * @returns Deleted lesson or null */ public async delete(id: string | number): Promise { - const lesson = await this.Lesson.query() - .where('id', id) - .preload('video') - .preload('materials') - .preload('color') - .first(); + const lesson = await this.Lesson.query().where('id', id).preload('video').preload('color').first(); if (lesson) { /** * Delete files from disk */ await this.Drive.delete(`videos/${lesson.video.name}`); - await Promise.all( - lesson.materials.map(async material => { - await this.Drive.delete(`materials/${material.name}`); - }) - ); + await this.deleteMaterials(lesson); /** * Delete lesson from database @@ -232,4 +210,68 @@ export default class LessonRepository { return null; } + + // eslint-disable-next-line class-methods-use-this + public async createVideo(lesson: Lesson, video: MultipartFileContract): Promise { + const videoName = `${cuid()}.${video.extname}`; + await video.moveToDisk('videos', { name: videoName }); + if (video.state === 'moved') { + await lesson.related('video').create({ + name: videoName, + clientName: video.clientName, + ext: video.extname, + size: video.size, + url: `/video/${videoName}`, + }); + } + } + + // eslint-disable-next-line class-methods-use-this + public async createMaterials(lesson: Lesson, materials: MultipartFileContract[]): Promise { + /** + * Move each file to disk + */ + const files = await Promise.all( + materials.map(async file => { + const name = `${cuid()}.${file.extname}`; + await file.moveToDisk('materials', { name }); + return { file, name }; + }) + ); + + /** + * Create lesson material + */ + const promises = await Promise.all( + files.map(async ({ file, name }) => { + if (file.state === 'moved' && file.fileName && file.extname) { + const material = new LessonMaterial(); + + material.name = name; + material.size = file.size; + material.clientName = file.clientName; + material.ext = file.extname; + + return material; + } + + return null; + }) + ); + + /** + * Save lesson materials to database + */ + await lesson.related('materials').createMany(promises.filter((l): l is LessonMaterial => l !== null)); + } + + public async deleteMaterials(lesson: Lesson) { + await lesson.load('materials'); + await Promise.all( + lesson.materials.map(async material => { + await this.Drive.delete(`materials/${material.name}`); + await material.delete(); + }) + ); + } } diff --git a/app/Validators/Lesson/UpdateLessonValidator.ts b/app/Validators/Lesson/UpdateLessonValidator.ts index 6dca473..909fc7d 100644 --- a/app/Validators/Lesson/UpdateLessonValidator.ts +++ b/app/Validators/Lesson/UpdateLessonValidator.ts @@ -5,9 +5,20 @@ export default class UpdateLessonValidator { constructor(protected ctx: HttpContextContract) {} public schema = schema.create({ - course_id: schema.string({}, [rules.exists({ table: 'courses', column: 'id' })]), - title: schema.string({}, [rules.maxLength(255)]), - description: schema.string(), + course_id: schema.string.optional({}, [rules.exists({ table: 'courses', column: 'id' })]), + title: schema.string.optional({}, [rules.maxLength(255)]), + description: schema.string.optional(), + duration: schema.date.optional({ format: 'HH:mm:ss' }), + video: schema.file.optional({ + size: '5000mb', + extnames: ['mp4', 'mov', 'avi', 'wmv', 'webm', 'flv'], + }), + materials: schema.array.nullableAndOptional().members( + schema.file({ + size: '100mb', + extnames: ['pdf', 'zip', 'rar', 'doc', 'docx'], + }) + ), }); public messages = {}; diff --git a/routes/apis/v1/lessons.ts b/routes/apis/v1/lessons.ts index ba16db5..7893120 100644 --- a/routes/apis/v1/lessons.ts +++ b/routes/apis/v1/lessons.ts @@ -8,7 +8,7 @@ Route.group(() => { .as('lessons.update-order'); Route.get('/:id', 'Api/v1/LessonsController.show').middleware('role:admin,teacher,student').as('lessons.show'); Route.delete('/:id', 'Api/v1/LessonsController.delete').middleware('role:admin,teacher').as('lessons.delete'); - Route.put('/:id', 'Api/v1/LessonsController.update').middleware('role:admin,teacher').as('lessons.update'); + Route.patch('/:id', 'Api/v1/LessonsController.update').middleware('role:admin,teacher').as('lessons.update'); Route.get('/:id/content', 'Api/v1/LessonsController.getContent') .middleware('role:admin,teacher,student') .as('lessons.get-content');