Add lessons on the API. (#78)

This commit is contained in:
Zoe Roux
2022-09-26 22:48:26 +09:00
committed by GitHub
parent cdca0d4942
commit a897d7693c
28 changed files with 591 additions and 99 deletions
+4 -3
View File
@@ -5,10 +5,11 @@ import { PrismaService } from './prisma/prisma.service';
import { UsersModule } from './users/users.module';
import { PrismaModule } from './prisma/prisma.module';
import { AuthModule } from './auth/auth.module';
import { LessonModule } from './lesson/lesson.module';
@Module({
imports: [UsersModule, PrismaModule, AuthModule],
controllers: [AppController],
providers: [AppService, PrismaService],
imports: [UsersModule, PrismaModule, AuthModule, LessonModule],
controllers: [AppController],
providers: [AppService, PrismaService],
})
export class AppModule {}
+9 -7
View File
@@ -1,22 +1,24 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcryptjs';
import PayloadInterface from './interface/payload.interface';
@Injectable()
export class AuthService {
constructor(
private userService: UsersService,
private jwtService: JwtService
private jwtService: JwtService,
) {}
async validateUser(username: string, password: string): Promise<PayloadInterface> {
const user = await this.userService.user({username});
async validateUser(
username: string,
password: string,
): Promise<PayloadInterface | null> {
const user = await this.userService.user({ username });
if (user && bcrypt.compareSync(password, user.password)) {
return {
username: user.username,
id: user.id
id: user.id,
};
}
return null;
@@ -26,7 +28,7 @@ export class AuthService {
const payload = { username: user.username, id: user.id };
const access_token = this.jwtService.sign(payload);
return {
access_token
access_token,
};
}
}
+14 -12
View File
@@ -1,4 +1,3 @@
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
@@ -7,15 +6,18 @@ import PayloadInterface from './interface/payload.interface';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
constructor(private authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise<PayloadInterface> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
async validate(
username: string,
password: string,
): Promise<PayloadInterface> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
+18
View File
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LessonController } from './lesson.controller';
describe('LessonController', () => {
let controller: LessonController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [LessonController],
}).compile();
controller = module.get<LessonController>(LessonController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
+103
View File
@@ -0,0 +1,103 @@
import {
Controller,
Get,
Res,
Query,
Req,
Request,
Param,
ParseIntPipe,
DefaultValuePipe,
BadRequestException,
Post,
Body,
Delete,
NotFoundException,
} from '@nestjs/common';
import { Plage } from 'src/models/plage';
import { LessonService } from './lesson.service';
import { ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger';
import { Prisma, Skill } from '@prisma/client';
export class Lesson {
@ApiProperty()
id: number;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
requiredLevel: number;
@ApiProperty()
mainSkill: Skill;
}
@ApiTags('lessons')
@Controller('lesson')
export class LessonController {
constructor(private lessonService: LessonService) {}
@ApiOperation({
summary: 'Get all lessons',
})
@Get()
async getAll(
@Req() request: Request,
@Query() filter: Prisma.LessonWhereInput,
@Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number,
@Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number,
): Promise<Plage<Lesson>> {
try {
const ret = await this.lessonService.getAll({
skip,
take,
where: {
...filter,
requiredLevel: filter.requiredLevel
? +filter.requiredLevel
: undefined,
},
});
return new Plage(ret, request);
} catch (e) {
console.log(e);
throw new BadRequestException(null, e?.toString());
}
}
@ApiOperation({
summary: 'Get a particular lessons',
})
@Get(':id')
async get(@Param('id', ParseIntPipe) id: number): Promise<Lesson> {
const ret = await this.lessonService.get(id);
if (!ret) throw new NotFoundException();
return ret;
}
@ApiOperation({
summary: 'Create a lessons',
})
@Post()
async post(@Body() lesson: Lesson): Promise<Lesson> {
try {
return await this.lessonService.create(lesson);
} catch (e) {
console.log(e);
throw new BadRequestException(null, e.toString());
}
}
@ApiOperation({
summary: 'Delete a lessons',
})
@Delete(':id')
async delete(@Param('id', ParseIntPipe) id: number): Promise<Lesson> {
try {
return await this.lessonService.delete(id);
} catch (e) {
console.log(e);
throw new BadRequestException(null, e.toString());
}
}
}
+11
View File
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from 'src/prisma/prisma.module';
import { LessonController } from './lesson.controller';
import { LessonService } from './lesson.service';
@Module({
imports: [PrismaModule],
controllers: [LessonController],
providers: [LessonService],
})
export class LessonModule {}
+18
View File
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LessonService } from './lesson.service';
describe('LessonService', () => {
let service: LessonService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LessonService],
}).compile();
service = module.get<LessonService>(LessonService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
+41
View File
@@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import { Lesson, Prisma } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';
@Injectable()
export class LessonService {
constructor(private prisma: PrismaService) {}
async getAll(params: {
skip?: number;
take?: number;
cursor?: Prisma.LessonWhereUniqueInput;
where?: Prisma.LessonWhereInput;
orderBy?: Prisma.LessonOrderByWithRelationInput;
}): Promise<Lesson[]> {
const { skip, take, cursor, where, orderBy } = params;
return this.prisma.lesson.findMany({
skip,
take,
cursor,
where,
orderBy,
});
}
async get(id: number): Promise<Lesson | null> {
return this.prisma.lesson.findFirst({
where: {
id: id,
},
});
}
async create(lesson: Lesson): Promise<Lesson> {
return this.prisma.lesson.create({ data: lesson });
}
async delete(id: number): Promise<Lesson> {
return this.prisma.lesson.delete({ where: { id: id } });
}
}
+11 -1
View File
@@ -1,11 +1,21 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './prisma/prisma.service';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const prismaService = app.get(PrismaService);
await prismaService.enableShutdownHooks(app)
await prismaService.enableShutdownHooks(app);
const config = new DocumentBuilder()
.setTitle('Chromacase')
.setDescription('The chromacase API')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
+51
View File
@@ -0,0 +1,51 @@
/*
* Thanks to https://github.com/Arthi-chaud/Meelo/blob/master/src/pagination/models/paginated-response.ts
*/
import { ApiProperty } from '@nestjs/swagger';
export class Plage<T> {
@ApiProperty()
metadata: {
this: string;
next: string | null;
previous: string | null;
};
@ApiProperty()
data: T[];
constructor(data: T[], request: Request | any) {
this.data = data;
let take = Number(request.query['take'] ?? 20).valueOf();
if (take == 0) take = 20;
let skipped: number = Number(request.query['skip'] ?? 0).valueOf();
if (skipped % take) {
skipped += take - (skipped % take);
}
this.metadata = {
this: this.buildUrl(request.path, request.query),
next:
data.length >= take
? this.buildUrl(request.path, {
...request.query,
skip: skipped + take,
})
: null,
previous: skipped
? this.buildUrl(request.path, {
...request.query,
skip: Math.max(0, skipped - take),
})
: null,
};
}
private buildUrl(route: string, queryParameters: any) {
if (queryParameters.skip == 0) delete queryParameters.skip;
const builtQueryParameters = new URLSearchParams(
queryParameters,
).toString();
if (builtQueryParameters.length) return `${route}?${builtQueryParameters}`;
return route;
}
}
+45 -46
View File
@@ -7,55 +7,54 @@ import * as bcrypt from 'bcryptjs';
@Injectable()
export class UsersService {
[x: string]: any;
constructor(private prisma: PrismaService) {}
constructor(private prisma: PrismaService) {}
async user(
userWhereUniqueInput: Prisma.UserWhereUniqueInput,
): Promise<User | null> {
return this.prisma.user.findUnique({
where: userWhereUniqueInput,
});
}
async user(
userWhereUniqueInput: Prisma.UserWhereUniqueInput,
): Promise<User | null> {
return this.prisma.user.findUnique({
where: userWhereUniqueInput,
});
}
async users(params: {
skip?: number;
take?: number;
cursor?: Prisma.UserWhereUniqueInput;
where?: Prisma.UserWhereInput;
orderBy?: Prisma.UserOrderByWithRelationInput;
}): Promise<User[]> {
const { skip, take, cursor, where, orderBy } = params;
return this.prisma.user.findMany({
skip,
take,
cursor,
where,
orderBy,
});
}
async users(params: {
skip?: number;
take?: number;
cursor?: Prisma.UserWhereUniqueInput;
where?: Prisma.UserWhereInput;
orderBy?: Prisma.UserOrderByWithRelationInput;
}): Promise<User[]> {
const { skip, take, cursor, where, orderBy } = params;
return this.prisma.user.findMany({
skip,
take,
cursor,
where,
orderBy,
});
}
async createUser(data: Prisma.UserCreateInput): Promise<User> {
data.password = await bcrypt.hash(data.password, 8)
return this.prisma.user.create({
data,
});
}
async createUser(data: Prisma.UserCreateInput): Promise<User> {
data.password = await bcrypt.hash(data.password, 8);
return this.prisma.user.create({
data,
});
}
async updateUser(params: {
where: Prisma.UserWhereUniqueInput;
data: Prisma.UserUpdateInput;
}): Promise<User> {
const { where, data } = params;
return this.prisma.user.update({
data,
where,
});
}
async updateUser(params: {
where: Prisma.UserWhereUniqueInput;
data: Prisma.UserUpdateInput;
}): Promise<User> {
const { where, data } = params;
return this.prisma.user.update({
data,
where,
});
}
async deleteUser(where: Prisma.UserWhereUniqueInput): Promise<User> {
return this.prisma.user.delete({
where,
});
}
async deleteUser(where: Prisma.UserWhereUniqueInput): Promise<User> {
return this.prisma.user.delete({
where,
});
}
}