Add lessons on the API. (#78)
This commit is contained in:
@@ -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 {}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user