mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
basic Course crud & fix seeder
This commit is contained in:
parent
4143ed2ca5
commit
a96ad1e7b5
@ -3,11 +3,7 @@
|
|||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": ["airbnb-typescript/base", "prettier", "plugin:prettier/recommended"],
|
||||||
"airbnb-typescript/base",
|
|
||||||
"prettier",
|
|
||||||
"plugin:prettier/recommended"
|
|
||||||
],
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"Atomics": "readonly",
|
"Atomics": "readonly",
|
||||||
"SharedArrayBuffer": "readonly"
|
"SharedArrayBuffer": "readonly"
|
||||||
@ -17,7 +13,6 @@
|
|||||||
"ecmaVersion": 2018,
|
"ecmaVersion": 2018,
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"project": "./tsconfig.json",
|
"project": "./tsconfig.json",
|
||||||
"tsconfigRootDir": "server",
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.js", "test/**/*.ts"]
|
"include": ["src/**/*.ts", "src/**/*.js", "test/**/*.ts"]
|
||||||
},
|
},
|
||||||
"plugins": ["@typescript-eslint"],
|
"plugins": ["@typescript-eslint"],
|
||||||
|
|||||||
181
app/Controllers/Http/CoursesController.ts
Normal file
181
app/Controllers/Http/CoursesController.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { Exception } from '@adonisjs/core/build/standalone';
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
import Course from 'App/Models/Course';
|
||||||
|
import User from 'App/Models/User';
|
||||||
|
import CreateCourseValidator from 'App/Validators/CreateCourseValidator';
|
||||||
|
import UpdateCourseValidator from 'App/Validators/UpdateCourseValidator';
|
||||||
|
|
||||||
|
export default class CoursesController {
|
||||||
|
private readonly Course: typeof Course;
|
||||||
|
|
||||||
|
private readonly User: typeof User;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.Course = Course;
|
||||||
|
this.User = User;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all Courses
|
||||||
|
* GET /courses
|
||||||
|
*/
|
||||||
|
public async index({ response }: HttpContextContract) {
|
||||||
|
const courses = await this.Course.query().preload('teacher').preload('category').preload('lessons');
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: 'Fetched all courses.',
|
||||||
|
data: courses,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Course by "id"
|
||||||
|
* GET /courses/:id
|
||||||
|
*/
|
||||||
|
public async show({ response, params }: HttpContextContract) {
|
||||||
|
const course = await this.Course.query()
|
||||||
|
.preload('teacher')
|
||||||
|
.preload('category')
|
||||||
|
.preload('lessons')
|
||||||
|
.where('id', params.id)
|
||||||
|
.firstOrFail();
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: `Fetched course with id: "${course.id}"`,
|
||||||
|
data: course,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Teacher of Course with "id"
|
||||||
|
* GET /courses/:id/teacher
|
||||||
|
*/
|
||||||
|
public async showTeacher({ response, params }: HttpContextContract) {
|
||||||
|
const course = await this.Course.query()
|
||||||
|
.select('teacher_id')
|
||||||
|
.preload('teacher', user => user.preload('contacts').preload('roles'))
|
||||||
|
.where('id', params.id)
|
||||||
|
.firstOrFail();
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: `Fetched teacher by courseId: "${params.id}"`,
|
||||||
|
data: course.teacher,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Category of Course with "id"
|
||||||
|
* GET /courses/:id/category
|
||||||
|
*/
|
||||||
|
public async showCategory({ response, params }: HttpContextContract) {
|
||||||
|
const course = await this.Course.query()
|
||||||
|
.select('category_id')
|
||||||
|
.preload('category')
|
||||||
|
.where('id', params.id)
|
||||||
|
.firstOrFail();
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: `Fetched category by courseId: "${params.id}"`,
|
||||||
|
data: course.category,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Lessons of ourse with "id"
|
||||||
|
* GET /courses/:id/lessons
|
||||||
|
*/
|
||||||
|
public async showLessons({ response, params }: HttpContextContract) {
|
||||||
|
const course = await this.Course.query().select('id').preload('lessons').where('id', params.id).firstOrFail();
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: `Fetched lessons by courseId: "${params.id}"`,
|
||||||
|
data: course.lessons,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Students of Course with "id"
|
||||||
|
*/
|
||||||
|
public async showStudents({ response, params }: HttpContextContract) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new Course in a system
|
||||||
|
* POST /courses
|
||||||
|
*/
|
||||||
|
public async create({ response, request }: HttpContextContract) {
|
||||||
|
const payload = await request.validate(CreateCourseValidator);
|
||||||
|
|
||||||
|
const teacher = await this.User.findOrFail(payload.teacher_id);
|
||||||
|
await teacher.load('roles');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check user on role Teacher
|
||||||
|
*/
|
||||||
|
const roles = teacher.roles.map(r => r.slug);
|
||||||
|
if (!roles.includes('teacher')) {
|
||||||
|
throw new Exception(`User with id "${teacher.id}" not teacher.`, 400, 'ERR_USER_NOT_TEACHER');
|
||||||
|
}
|
||||||
|
|
||||||
|
const course = await this.Course.create({
|
||||||
|
title: payload.title,
|
||||||
|
description: payload.description,
|
||||||
|
teacher_id: teacher.id,
|
||||||
|
category_id: payload.category_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await course.save();
|
||||||
|
|
||||||
|
return response.created({
|
||||||
|
message: 'Course has been created',
|
||||||
|
data: course,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Course by "id"
|
||||||
|
* DELETE /courses/:id
|
||||||
|
*/
|
||||||
|
async destroy({ response, params }: HttpContextContract) {
|
||||||
|
const course = await this.Course.query()
|
||||||
|
.preload('teacher')
|
||||||
|
.preload('category')
|
||||||
|
.preload('lessons')
|
||||||
|
.where('id', params.id)
|
||||||
|
.firstOrFail();
|
||||||
|
|
||||||
|
await course.delete();
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: `Course with id "${params.id}" has been deleted.`,
|
||||||
|
data: course,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Course by "id"
|
||||||
|
* PATCH /courses/:id
|
||||||
|
*/
|
||||||
|
async update({ response, request, params }: HttpContextContract) {
|
||||||
|
const payload = await request.validate(UpdateCourseValidator);
|
||||||
|
const course = await this.Course.findOrFail(params.id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update course
|
||||||
|
*/
|
||||||
|
Object.keys(payload).forEach(k => {
|
||||||
|
if (payload[k] !== undefined) {
|
||||||
|
course[k] = payload[k];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await course.save();
|
||||||
|
await course.load('category');
|
||||||
|
await course.load('lessons');
|
||||||
|
await course.load('teacher');
|
||||||
|
|
||||||
|
return response.ok({
|
||||||
|
message: `Course with id: "${course.id}" was successfully updated.`,
|
||||||
|
data: course,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,10 +14,10 @@ export default class Category extends BaseModel {
|
|||||||
@column()
|
@column()
|
||||||
public description: string;
|
public description: string;
|
||||||
|
|
||||||
@column.dateTime({ autoCreate: true })
|
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||||
public createdAt: DateTime;
|
public createdAt: DateTime;
|
||||||
|
|
||||||
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
|
||||||
public updatedAt: DateTime;
|
public updatedAt: DateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -16,10 +16,10 @@ export default class Lesson extends BaseModel {
|
|||||||
@column()
|
@column()
|
||||||
public description: string;
|
public description: string;
|
||||||
|
|
||||||
@column.dateTime({ autoCreate: true })
|
@column.dateTime({ autoCreate: true, serializeAs: null })
|
||||||
public createdAt: DateTime;
|
public createdAt: DateTime;
|
||||||
|
|
||||||
@column.dateTime({ autoCreate: true, autoUpdate: true })
|
@column.dateTime({ autoCreate: true, autoUpdate: true, serializeAs: null })
|
||||||
public updatedAt: DateTime;
|
public updatedAt: DateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
15
app/Validators/CreateCourseValidator.ts
Normal file
15
app/Validators/CreateCourseValidator.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { schema, rules } from '@ioc:Adonis/Core/Validator';
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
|
||||||
|
export default class CreateCourseValidator {
|
||||||
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
title: schema.string({}, [rules.unique({ table: 'courses', column: 'title' })]),
|
||||||
|
description: schema.string(),
|
||||||
|
teacher_id: schema.string({}, [rules.exists({ table: 'users', column: 'id' })]),
|
||||||
|
category_id: schema.string({}, [rules.exists({ table: 'categories', column: 'id' })]),
|
||||||
|
});
|
||||||
|
|
||||||
|
public messages = {};
|
||||||
|
}
|
||||||
15
app/Validators/UpdateCourseValidator.ts
Normal file
15
app/Validators/UpdateCourseValidator.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { schema, rules } from '@ioc:Adonis/Core/Validator';
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
|
||||||
|
|
||||||
|
export default class UpdateCourseValidator {
|
||||||
|
constructor(protected ctx: HttpContextContract) {}
|
||||||
|
|
||||||
|
public schema = schema.create({
|
||||||
|
title: schema.string.optional({}, [rules.unique({ table: 'courses', column: 'title' })]),
|
||||||
|
description: schema.string.optional(),
|
||||||
|
teacher_id: schema.string.optional({}, [rules.exists({ table: 'users', column: 'id' })]),
|
||||||
|
category_id: schema.string.optional({}, [rules.exists({ table: 'categories', column: 'id' })]),
|
||||||
|
});
|
||||||
|
|
||||||
|
public messages = {};
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ export const UserFactory = Factory.define(User, ({ faker }) => {
|
|||||||
first_name: faker.name.firstName(),
|
first_name: faker.name.firstName(),
|
||||||
last_name: faker.name.lastName(),
|
last_name: faker.name.lastName(),
|
||||||
login: `${faker.lorem.word(6)}${faker.datatype.number(10)}`,
|
login: `${faker.lorem.word(6)}${faker.datatype.number(10)}`,
|
||||||
password: faker.internet.password(6),
|
password: '12345',
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.relation('contacts', () => ContactFactory)
|
.relation('contacts', () => ContactFactory)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
import BaseSeeder from '@ioc:Adonis/Lucid/Seeder';
|
||||||
|
import Course from 'App/Models/Course';
|
||||||
import User from 'App/Models/User';
|
import User from 'App/Models/User';
|
||||||
import { CourseFactory } from 'Database/factories';
|
import { CourseFactory } from 'Database/factories';
|
||||||
|
|
||||||
@ -9,11 +10,12 @@ export default class CourseSeeder extends BaseSeeder {
|
|||||||
this.User = User;
|
this.User = User;
|
||||||
|
|
||||||
const teacher = await this.User.query()
|
const teacher = await this.User.query()
|
||||||
.preload('roles', q => q.where('slug', 'teacher'))
|
.preload('roles')
|
||||||
|
.whereHas('roles', q => q.where('slug', 'teacher'))
|
||||||
.firstOrFail();
|
.firstOrFail();
|
||||||
|
|
||||||
await (await CourseFactory.with('category').with('lessons', 3).create()).related('teacher').associate(teacher);
|
await CourseFactory.with('category')
|
||||||
await (await CourseFactory.with('category').with('lessons', 2).create()).related('teacher').associate(teacher);
|
.with('lessons', 2)
|
||||||
await (await CourseFactory.with('category').with('lessons', 1).create()).related('teacher').associate(teacher);
|
.createMany(3, (course: Course) => course.related('teacher').associate(teacher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,9 +47,34 @@ Route.group(() => {
|
|||||||
Route.patch('me/password', 'MeController.changePassword')
|
Route.patch('me/password', 'MeController.changePassword')
|
||||||
.middleware('role:admin,teacher,student')
|
.middleware('role:admin,teacher,student')
|
||||||
.as('changePassword');
|
.as('changePassword');
|
||||||
|
|
||||||
Route.put('me/contacts', 'MeController.updateContacts')
|
Route.put('me/contacts', 'MeController.updateContacts')
|
||||||
.middleware('role:admin,teacher,student')
|
.middleware('role:admin,teacher,student')
|
||||||
.as('updateContacts');
|
.as('updateContacts');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Courses controller
|
||||||
|
*/
|
||||||
|
Route.get('courses', 'CoursesController.index').middleware('role:admin,teacher,student').as('showAllCourses');
|
||||||
|
Route.post('courses', 'CoursesController.create').middleware('role:admin,teacher').as('createCourse');
|
||||||
|
Route.get('courses/:id', 'CoursesController.show').middleware('role:admin,teacher,student').as('showCourseById');
|
||||||
|
Route.delete('courses/:id', 'CoursesController.destroy').middleware('role:admin,teacher').as('deleteCourseById');
|
||||||
|
Route.patch('courses/:id', 'CoursesController.update').middleware('role:admin,teacher').as('updateCourse');
|
||||||
|
Route.get('courses/:id/teacher', 'CoursesController.showTeacher')
|
||||||
|
.middleware('role:admin,teacher,student')
|
||||||
|
.as('showCourseTeacher');
|
||||||
|
|
||||||
|
Route.get('courses/:id/category', 'CoursesController.showCategory')
|
||||||
|
.middleware('role:admin,teacher,student')
|
||||||
|
.as('showCourseCategory');
|
||||||
|
|
||||||
|
Route.get('courses/:id/lessons', 'CoursesController.showLessons')
|
||||||
|
.middleware('role:admin,teacher,student')
|
||||||
|
.as('showCourseLessons');
|
||||||
|
|
||||||
|
Route.get('courses/:id/students', 'CourseController.showStudents')
|
||||||
|
.middleware('role:admin,teacher,student')
|
||||||
|
.as('showCourseStudents');
|
||||||
}).middleware('auth');
|
}).middleware('auth');
|
||||||
})
|
})
|
||||||
.prefix('/api/v1')
|
.prefix('/api/v1')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user