Commit c11d1666 authored by Guilherme Matheus Dornelles's avatar Guilherme Matheus Dornelles
Browse files

Creating courses module.

>
>
Co-authored-by: default avatarpedro.tigre <pedro.tigre@edu.pucrs.br>
Co-authored-by: Vítor Mariano Becker's avatarvitor.mariano <vitor.mariano@edu.pucrs.br>
parent 98213cd6
Showing with 451 additions and 6 deletions
+451 -6
import { MigrationInterface, QueryRunner } from "typeorm";
export class Decola1713820689674 implements MigrationInterface {
name = 'Decola1713820689674'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TYPE "public"."course_types_course_group_enum" AS ENUM('PRE_ONBOARDING', 'ONBOARDING', 'POST_ONBOARDING', 'RE_ONBOARDING')`);
await queryRunner.query(`CREATE TABLE "course_types" ("id" SERIAL NOT NULL, "name" character varying(500) NOT NULL, "course_group" "public"."course_types_course_group_enum" NOT NULL, CONSTRAINT "PK_c114da3a99c7e356aad447443cb" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "courses" ("id" SERIAL NOT NULL, "account_id" integer NOT NULL, "name" character varying(500) NOT NULL, "description" character varying(2500) NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "course_type_id" integer NOT NULL, CONSTRAINT "PK_3f70a487cc718ad8eda4e6d58c9" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_bd0caed0b4565ced1012f70852" ON "courses" ("account_id") `);
await queryRunner.query(`ALTER TABLE "courses" ADD CONSTRAINT "FK_4c924d3f96fbb9ce579ae91152b" FOREIGN KEY ("course_type_id") REFERENCES "course_types"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "courses" ADD CONSTRAINT "fk_course_account_id_account" FOREIGN KEY ("account_id") REFERENCES "accounts"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "courses" DROP CONSTRAINT "fk_course_account_id_account"`);
await queryRunner.query(`ALTER TABLE "courses" DROP CONSTRAINT "FK_4c924d3f96fbb9ce579ae91152b"`);
await queryRunner.query(`DROP INDEX "public"."IDX_bd0caed0b4565ced1012f70852"`);
await queryRunner.query(`DROP TABLE "courses"`);
await queryRunner.query(`DROP TABLE "course_types"`);
await queryRunner.query(`DROP TYPE "public"."course_types_course_group_enum"`);
}
}
......@@ -16,7 +16,7 @@ export class UserBaseDto {
@ApiProperty({ example: 'Bob' }) @Expose() @IsString() name: string
@ApiProperty({ example: 'Esponja' }) @Expose() @IsString() lastName: string
@ApiProperty({ example: 'bob@mail.com' }) @Expose() @IsEmail() email: string
@ApiProperty({}) @Expose() @IsNumber() accountId: number
@ApiProperty({ example: 123 }) @Expose() @IsNumber() accountId: number
@ApiProperty({ example: '12345678' }) @IsString() @MinLength(8) password: string
@ApiProperty({ example: 'Bob Friendly' }) @Expose() @IsString() friendlyName: string
@ApiProperty({ example: 'COLLABORATOR' }) @Expose() @IsOptional() @IsString() role: UserRole
......
......@@ -3,13 +3,15 @@ import { UserController } from './controller'
import { UserRepository } from './repository'
import { UserService } from './service'
import { TypeOrmModule } from '@nestjs/typeorm'
import { User } from '../../../models/User.entity'
import { Course } from 'src/models/Course.entity'
import { CourseRepository } from 'src/apis/onboarding/courses/repository'
import { User } from 'src/models/User.entity'
import { Account } from 'src/models/Account.entity'
import { AccountRepository } from '../accounts/repository'
import { Account } from '../../../models/Account.entity'
@Module({
imports: [TypeOrmModule.forFeature([User, Account])],
providers: [UserRepository, UserService, AccountRepository],
imports: [TypeOrmModule.forFeature([User, Account, Course])],
providers: [UserRepository, UserService, AccountRepository, CourseRepository],
controllers: [UserController]
})
export class UserModule {}
import { Controller, Get, Post, Body, Param, Delete, ParseIntPipe } from '@nestjs/common'
import { CourseTypeService } from './service'
import { serialize } from 'src/utils/serializer'
import { Authenticated } from 'src/decorators/authenticated'
import { Roles } from 'src/decorators/roles'
import { UserRole } from 'src/models/enum/UsersEnum'
import { CourseTypeCreateDto, CourseTypeItemDto } from './dto/CourseTypeDto'
import { ApiTags } from '@nestjs/swagger'
@Controller('onboarding/courseTypes')
@ApiTags('seila/courseTypes')
export class CourseTypeController {
constructor(private readonly courseTypeService: CourseTypeService) {}
@Post('/')
@Authenticated()
@Roles(UserRole.ADMIN)
async create(@Body() payload: CourseTypeCreateDto) {
const createdCourseType = await this.courseTypeService.create(payload)
return serialize(CourseTypeItemDto, createdCourseType)
}
@Get('/')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
async getAll() {
const courseTypes = await this.courseTypeService.findAll()
return serialize(CourseTypeItemDto, courseTypes)
}
@Get('/:courseTypeId')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
async getById(@Param('courseTypeId', ParseIntPipe) courseTypeId: number) {
const courseType = await this.courseTypeService.findOne(courseTypeId)
return serialize(CourseTypeItemDto, courseType)
}
@Delete('/:courseTypeId')
@Authenticated()
@Roles(UserRole.ADMIN)
async remove(@Param('courseTypeId', ParseIntPipe) courseTypeId: number) {
return this.courseTypeService.remove(courseTypeId)
}
}
import { ApiProperty, PickType } from '@nestjs/swagger'
import { Expose } from 'class-transformer'
import { IsEnum, IsNumber, IsString } from 'class-validator'
import { CourseGroup } from 'src/models/enum/CoursesEnum'
export class CourseTypeBaseDto {
@ApiProperty() @Expose() @IsNumber() id: number
@ApiProperty({ example: CourseGroup.ONBOARDING })
@Expose()
@IsEnum(CourseGroup)
courseGroup: CourseGroup
@ApiProperty({ example: 'Onboarding' }) @Expose() @IsString() name: string
}
export class CourseTypeCreateDto extends PickType(CourseTypeBaseDto, ['name', 'courseGroup']) {}
export class CourseTypeItemDto extends PickType(CourseTypeBaseDto, ['id', 'name', 'courseGroup']) {}
import { Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { CourseTypeRepository } from './repository'
import { CourseType } from 'src/models/CourseType.entity'
import { CourseTypeService } from './service'
import { CourseTypeController } from './controller'
@Module({
imports: [TypeOrmModule.forFeature([CourseType])],
providers: [CourseTypeRepository, CourseTypeService],
controllers: [CourseTypeController]
})
export class CourseTypeModule {}
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { CourseType } from 'src/models/CourseType.entity'
import { FindOptionsWhere, Repository } from 'typeorm'
import { CourseTypeCreateDto } from './dto/CourseTypeDto'
@Injectable()
export class CourseTypeRepository {
constructor(
@InjectRepository(CourseType)
private courseTypeRepository: Repository<CourseType>
) {}
public async create(payload: CourseTypeCreateDto) {
return this.courseTypeRepository.save(payload)
}
public async findAll() {
return this.courseTypeRepository.find()
}
public async findBy(options: FindOptionsWhere<CourseType>) {
return this.courseTypeRepository.findBy(options)
}
public async removeById(courseTypeId: number) {
return this.courseTypeRepository.delete({ id: courseTypeId })
}
}
import { Injectable, NotFoundException } from '@nestjs/common'
import { CourseTypeCreateDto } from './dto/CourseTypeDto'
import { CourseTypeRepository } from './repository'
@Injectable()
export class CourseTypeService {
constructor(private courseTypeRepository: CourseTypeRepository) {}
async create(payload: CourseTypeCreateDto) {
const course = await this.courseTypeRepository.create(payload)
return course
}
async findAll() {
const courseTypes = await this.courseTypeRepository.findAll()
return courseTypes
}
async findOne(id: number) {
const courseType = await this.courseTypeRepository.findBy({ id })
if (!courseType.length) {
throw new NotFoundException(`Course Type with Id ${id} not found.`)
}
return courseType[0]
}
async remove(id: number) {
const courseType = await this.courseTypeRepository.findBy({ id })
if (!courseType.length) {
throw new NotFoundException(`Course Type with ID ${id} not found.`)
}
return await this.courseTypeRepository.removeById(id)
}
}
import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe } from '@nestjs/common'
import { CourseService } from './service'
import { serialize } from 'src/utils/serializer'
import { Authenticated } from 'src/decorators/authenticated'
import { Roles } from 'src/decorators/roles'
import { UserRole } from 'src/models/enum/UsersEnum'
import { CourseCreateDto, CourseItemDto, UpdateCourseDto } from './dto/CourseDto'
import { ACCOUNTID } from 'src/decorators/accountId'
import { ApiResponse, ApiTags } from '@nestjs/swagger'
@Controller('onboarding/courses')
@ApiTags('courses')
export class CourseController {
constructor(private readonly coursesService: CourseService) {}
@Post('/')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
@ApiResponse({ type: CourseItemDto })
async create(@ACCOUNTID() accountId: number, @Body() payload: CourseCreateDto) {
const createdCourse = await this.coursesService.create(accountId, payload)
return serialize(CourseItemDto, createdCourse)
}
@Get('/')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
@ApiResponse({ type: CourseItemDto })
async getAll(@ACCOUNTID() accountId: number) {
const courses = await this.coursesService.findAll(accountId)
return serialize(CourseItemDto, courses)
}
@Get('/:courseId')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
@ApiResponse({ type: CourseItemDto })
async getById(@ACCOUNTID() accountId: number, @Param('courseId', ParseIntPipe) courseId: number) {
const course = await this.coursesService.findOne(accountId, courseId)
return serialize(CourseItemDto, course)
}
@Patch('/:courseId')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
@ApiResponse({ type: CourseItemDto })
async update(
@ACCOUNTID() accountId: number,
@Param('courseId', ParseIntPipe) courseId: number,
@Body() updateCourseDto: UpdateCourseDto
) {
return this.coursesService.update(accountId, courseId, updateCourseDto)
}
@Delete('/:courseId')
@Authenticated()
@Roles(UserRole.ADMIN, UserRole.COLLABORATOR)
async remove(@ACCOUNTID() accountId: number, @Param('courseId', ParseIntPipe) courseId: number) {
return this.coursesService.remove(accountId, courseId)
}
}
import { ApiProperty, PickType } from '@nestjs/swagger'
import { Expose, Type } from 'class-transformer'
import { IsDate, IsNumber, IsOptional, IsString, MinLength, MaxLength } from 'class-validator'
import { CourseType } from 'src/models/CourseType.entity'
export class CourseBaseDto {
@ApiProperty() @Expose() @IsNumber() id: number
@ApiProperty({ example: 'Curso 1' }) @IsString() @MinLength(5) @MaxLength(500) name: string
@ApiProperty({ example: 'Descricao do curso' })
@IsString()
@MinLength(10)
@MaxLength(500)
description: string
@ApiProperty() @IsOptional() @Expose() type: CourseType
@ApiProperty({ example: 123 }) @Expose() @IsNumber() courseTypeId: number
@ApiProperty({ example: 123 }) @Expose() @IsNumber() accountId: number
@ApiProperty() @Expose() @IsOptional() @IsDate() @Type(() => Date) createdAt: Date
}
export class CourseCreateDto extends PickType(CourseBaseDto, [
'name',
'description',
'courseTypeId',
'accountId'
]) {}
export class CourseItemDto extends PickType(CourseBaseDto, [
'id',
'name',
'description',
'courseTypeId',
'accountId',
'createdAt'
]) {}
export class UpdateCourseDto extends PickType(CourseCreateDto, [
'name',
'description',
'courseTypeId'
]) {}
import { Module } from '@nestjs/common'
import { CourseService } from './service'
import { CourseController } from './controller'
import { TypeOrmModule } from '@nestjs/typeorm'
import { Course } from 'src/models/Course.entity'
import { CourseRepository } from './repository'
import { CourseTypeRepository } from '../courseTypes/repository'
import { CourseType } from 'src/models/CourseType.entity'
@Module({
imports: [TypeOrmModule.forFeature([Course, CourseType])],
controllers: [CourseController],
providers: [CourseService, CourseRepository, CourseTypeRepository]
})
export class CourseModule {}
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Course } from 'src/models/Course.entity'
import { FindOptionsWhere, Repository } from 'typeorm'
import { CourseCreateDto, UpdateCourseDto } from './dto/CourseDto'
@Injectable()
export class CourseRepository {
constructor(
@InjectRepository(Course)
private courseRepository: Repository<Course>
) {}
public async create(payload: CourseCreateDto) {
return this.courseRepository.save(payload)
}
public async update(courseId: number, payload: Partial<UpdateCourseDto>) {
return this.courseRepository.save({
id: courseId,
...payload
})
}
public async findBy(options: FindOptionsWhere<Course>) {
return this.courseRepository.findBy(options)
}
public async removeById(courseId: number) {
return this.courseRepository.delete({ id: courseId })
}
}
import { ConflictException, Injectable, NotFoundException } from '@nestjs/common'
import { CourseCreateDto, UpdateCourseDto } from './dto/CourseDto'
import { CourseRepository } from './repository'
import { CourseTypeRepository } from '../courseTypes/repository'
@Injectable()
export class CourseService {
constructor(
private courseRepository: CourseRepository,
private courseTypeRepository: CourseTypeRepository
) {}
async create(accountId: number, payload: CourseCreateDto) {
if (accountId !== payload.accountId) {
throw new ConflictException('Account must be the same as logged')
}
const courseType = await this.courseTypeRepository.findBy({ id: payload.courseTypeId })
if (!courseType.length) {
throw new NotFoundException(`Course Type with Id ${payload.courseTypeId} not found.`)
}
return await this.courseRepository.create(payload)
}
async findAll(accountId: number) {
const courses = await this.courseRepository.findBy({ accountId })
return courses
}
async findOne(accountId: number, id: number) {
const course = await this.courseRepository.findBy({ accountId, id })
if (!course.length) {
throw new NotFoundException(`Course with Id ${id} not found.`)
}
return course[0]
}
async update(accountId: number, id: number, payload: UpdateCourseDto) {
if (payload?.courseTypeId) {
const courseType = await this.courseTypeRepository.findBy({ id: payload.courseTypeId })
if (!courseType.length) {
throw new NotFoundException(`Course Type with Id ${payload.courseTypeId} not found.`)
}
}
const existingCourse = await this.courseRepository.findBy({ accountId, id })
if (!existingCourse.length) {
throw new NotFoundException(`Course with ID ${id} not found.`)
}
return this.courseRepository.update(id, payload)
}
async remove(accountId: number, id: number) {
const course = await this.courseRepository.findBy({ accountId, id })
if (!course.length) {
throw new NotFoundException(`Course with ID ${id} not found.`)
}
return await this.courseRepository.removeById(id)
}
}
import { Module } from '@nestjs/common'
import { CourseModule } from './courses/module'
import { CourseTypeModule } from './courseTypes/module'
@Module({
imports: [CourseModule, CourseTypeModule]
})
export class OnBoardingModule {}
import { Module } from '@nestjs/common'
import { DatabaseModule } from './database/database.module'
import { CoreModule } from './apis/core/core.module'
import { OnBoardingModule } from './apis/onboarding/onboarding.module'
@Module({
imports: [DatabaseModule, CoreModule]
imports: [DatabaseModule, CoreModule, OnBoardingModule]
})
export class AppModule {}
......@@ -2,6 +2,7 @@ import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm'
import { AccountType } from './enum/AccountsEnum'
import { User } from './User.entity'
import { Area } from './Area.entity'
import { Course } from './Course.entity'
@Entity('accounts')
export class Account {
......@@ -25,4 +26,7 @@ export class Account {
@OneToMany(() => Area, (area) => area.account)
areas: Area[]
@OneToMany(() => Course, (course) => course.account)
courses: Course[]
}
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, Index } from 'typeorm'
import { CourseType } from './CourseType.entity'
import { Account } from './Account.entity'
@Entity('courses')
export class Course {
@PrimaryGeneratedColumn()
id: number
@Index()
@Column({ name: 'account_id' })
accountId: number
@Column({ length: 500 })
name: string
@Column({ length: 2500 })
description: string
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date
@Column({ name: 'course_type_id' })
courseTypeId: number
@ManyToOne(() => CourseType, {
cascade: false,
nullable: false
})
@JoinColumn({ name: 'course_type_id' })
type: CourseType
@ManyToOne(() => Account, (account) => account.areas, { cascade: true, nullable: false })
@JoinColumn({ name: 'account_id', foreignKeyConstraintName: 'fk_course_account_id_account' })
account: Account
}
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'
import { CourseGroup } from './enum/CoursesEnum'
@Entity('course_types')
export class CourseType {
@PrimaryGeneratedColumn()
id: number
@Column({ length: 500 })
name: string
@Column({ type: 'enum', enum: CourseGroup, nullable: false })
courseGroup: CourseGroup
}
export enum CourseGroup {
PRE_ONBOARDING = 'PRE_ONBOARDING',
ONBOARDING = 'ONBOARDING',
POST_ONBOARDING = 'POST_ONBOARDING',
RE_ONBOARDING = 'RE_ONBOARDING'
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment