From a897d7693c177ecb4b885636e019a473a80f7b05 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 26 Sep 2022 22:48:26 +0900 Subject: [PATCH] Add lessons on the API. (#78) --- .editorconfig | 9 ++ .env.example | 6 + .gitignore | 5 +- back/Dockerfile.dev | 3 - back/nest-cli.json | 15 ++- back/package-lock.json | 35 +++++- back/package.json | 3 +- .../migrations/20220924080202_/migration.sql | 26 +++++ .../migrations/20220924081654_/migration.sql | 28 +++++ .../migrations/20220926065731_/migration.sql | 10 ++ back/prisma/migrations/migration_lock.toml | 3 + back/prisma/schema.prisma | 45 +++++++- back/src/app.module.ts | 7 +- back/src/auth/auth.service.ts | 16 +-- back/src/auth/local.strategy.ts | 26 +++-- back/src/lesson/lesson.controller.spec.ts | 18 +++ back/src/lesson/lesson.controller.ts | 103 ++++++++++++++++++ back/src/lesson/lesson.module.ts | 11 ++ back/src/lesson/lesson.service.spec.ts | 18 +++ back/src/lesson/lesson.service.ts | 41 +++++++ back/src/main.ts | 12 +- back/src/models/plage.ts | 51 +++++++++ back/src/users/users.service.ts | 91 ++++++++-------- back/test/robot/lesson/lesson.robot | 80 ++++++++++++++ back/test/robot/users/users.robot | 6 +- back/tsconfig.json | 4 +- node_modules/.yarn-integrity | 12 -- package-lock.json | 6 + 28 files changed, 591 insertions(+), 99 deletions(-) create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 back/prisma/migrations/20220924080202_/migration.sql create mode 100644 back/prisma/migrations/20220924081654_/migration.sql create mode 100644 back/prisma/migrations/20220926065731_/migration.sql create mode 100644 back/prisma/migrations/migration_lock.toml create mode 100644 back/src/lesson/lesson.controller.spec.ts create mode 100644 back/src/lesson/lesson.controller.ts create mode 100644 back/src/lesson/lesson.module.ts create mode 100644 back/src/lesson/lesson.service.spec.ts create mode 100644 back/src/lesson/lesson.service.ts create mode 100644 back/src/models/plage.ts create mode 100644 back/test/robot/lesson/lesson.robot delete mode 100644 node_modules/.yarn-integrity create mode 100644 package-lock.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b696824 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = tab diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7f48820 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_NAME= +POSTGRES_HOST= +DATABASE_URL= +JWT_SECRET= diff --git a/.gitignore b/.gitignore index c7564a0..0af2a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ pyvenv.cfg include .env prisma/migrations/* -.vscode \ No newline at end of file +.vscode +output.xml +report.html +log.html diff --git a/back/Dockerfile.dev b/back/Dockerfile.dev index d86196a..1d7d470 100644 --- a/back/Dockerfile.dev +++ b/back/Dockerfile.dev @@ -1,6 +1,3 @@ FROM node:17 WORKDIR /app -COPY ./package.json ./ -RUN npm install -COPY . . CMD npx prisma generate ; npx prisma migrate dev ; npm run start:dev diff --git a/back/nest-cli.json b/back/nest-cli.json index 2566481..a427513 100644 --- a/back/nest-cli.json +++ b/back/nest-cli.json @@ -1,5 +1,14 @@ { - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src" + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "plugins": [{ + "name": "@nestjs/swagger", + "options": { + "introspectComments": true, + "dtoFileNameSuffix": [] + } + }] + } } diff --git a/back/package-lock.json b/back/package-lock.json index c9fb02a..c4714a8 100644 --- a/back/package-lock.json +++ b/back/package-lock.json @@ -27,7 +27,8 @@ "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "swagger-ui-express": "^4.5.0" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -8194,6 +8195,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.14.0.tgz", + "integrity": "sha512-TBzhheU15s+o54Cgk9qxuYcZMiqSm/SkvKnapoGHOF66kz0Y5aGjpzj5BT/vpBbn6rTPJ9tUYXQxuDWfsjiGMw==" + }, + "node_modules/swagger-ui-express": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.5.0.tgz", + "integrity": "sha512-DHk3zFvsxrkcnurGvQlAcLuTDacAVN1JHKDgcba/gr2NFRE4HGwP1YeHIXMiGznkWR4AeS7X5vEblNn4QljuNA==", + "dependencies": { + "swagger-ui-dist": ">=4.11.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -15400,6 +15420,19 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swagger-ui-dist": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.14.0.tgz", + "integrity": "sha512-TBzhheU15s+o54Cgk9qxuYcZMiqSm/SkvKnapoGHOF66kz0Y5aGjpzj5BT/vpBbn6rTPJ9tUYXQxuDWfsjiGMw==" + }, + "swagger-ui-express": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.5.0.tgz", + "integrity": "sha512-DHk3zFvsxrkcnurGvQlAcLuTDacAVN1JHKDgcba/gr2NFRE4HGwP1YeHIXMiGznkWR4AeS7X5vEblNn4QljuNA==", + "requires": { + "swagger-ui-dist": ">=4.11.0" + } + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/back/package.json b/back/package.json index ac6a010..773af23 100644 --- a/back/package.json +++ b/back/package.json @@ -39,7 +39,8 @@ "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "swagger-ui-express": "^4.5.0" }, "devDependencies": { "@nestjs/cli": "^8.0.0", diff --git a/back/prisma/migrations/20220924080202_/migration.sql b/back/prisma/migrations/20220924080202_/migration.sql new file mode 100644 index 0000000..c51015f --- /dev/null +++ b/back/prisma/migrations/20220924080202_/migration.sql @@ -0,0 +1,26 @@ +-- CreateEnum +CREATE TYPE "DifficultyPoint" AS ENUM ('TwoHands', 'Rhythm', 'NoteCombo', 'Arpeggio', 'Distance', 'LeftHand', 'RightHand', 'LeadHandChange', 'ChordComplexity', 'ChordTiming', 'Length', 'PedalPoint', 'Precision'); + +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, + "email" TEXT NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Lesson" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "requiredLvl" INTEGER NOT NULL, + "difficulyPoint" "DifficultyPoint" NOT NULL, + + CONSTRAINT "Lesson_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); diff --git a/back/prisma/migrations/20220924081654_/migration.sql b/back/prisma/migrations/20220924081654_/migration.sql new file mode 100644 index 0000000..0bc5d2e --- /dev/null +++ b/back/prisma/migrations/20220924081654_/migration.sql @@ -0,0 +1,28 @@ +/* + Warnings: + + - You are about to drop the column `difficulyPoint` on the `Lesson` table. All the data in the column will be lost. + - You are about to drop the column `requiredLvl` on the `Lesson` table. All the data in the column will be lost. + - Added the required column `mainSkill` to the `Lesson` table without a default value. This is not possible if the table is not empty. + - Added the required column `requiredLevel` to the `Lesson` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Lesson" DROP COLUMN "difficulyPoint", +DROP COLUMN "requiredLvl", +ADD COLUMN "mainSkill" "DifficultyPoint" NOT NULL, +ADD COLUMN "requiredLevel" INTEGER NOT NULL; + +-- CreateTable +CREATE TABLE "LessonHistory" ( + "lessonID" INTEGER NOT NULL, + "userID" INTEGER NOT NULL, + + CONSTRAINT "LessonHistory_pkey" PRIMARY KEY ("lessonID","userID") +); + +-- AddForeignKey +ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_userID_fkey" FOREIGN KEY ("userID") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_lessonID_fkey" FOREIGN KEY ("lessonID") REFERENCES "Lesson"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926065731_/migration.sql b/back/prisma/migrations/20220926065731_/migration.sql new file mode 100644 index 0000000..5d85f48 --- /dev/null +++ b/back/prisma/migrations/20220926065731_/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `title` on the `Lesson` table. All the data in the column will be lost. + - Added the required column `name` to the `Lesson` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Lesson" DROP COLUMN "title", +ADD COLUMN "name" TEXT NOT NULL; diff --git a/back/prisma/migrations/migration_lock.toml b/back/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/back/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/back/prisma/schema.prisma b/back/prisma/schema.prisma index 3f22507..579d951 100644 --- a/back/prisma/schema.prisma +++ b/back/prisma/schema.prisma @@ -10,8 +10,45 @@ datasource db { } model User { - id Int @default(autoincrement()) @id - username String @unique - password String - email String + id Int @id @default(autoincrement()) + username String @unique + password String + email String + LessonHistory LessonHistory[] +} + +model Lesson { + id Int @id @default(autoincrement()) + name String + description String + requiredLevel Int + mainSkill Skill + LessonHistory LessonHistory[] +} + +model LessonHistory { + lesson Lesson @relation(fields: [lessonID], references: [id]) + lessonID Int + user User @relation(fields: [userID], references: [id]) + userID Int + + @@id([lessonID, userID]) +} + +enum Skill { + TwoHands + Rhythm + NoteCombo + Arpeggio + Distance + LeftHand + RightHand + LeadHandChange + ChordComplexity + ChordTiming + Length + PedalPoint + Precision + + @@map("DifficultyPoint") } diff --git a/back/src/app.module.ts b/back/src/app.module.ts index 6b93fd1..d87e0cb 100644 --- a/back/src/app.module.ts +++ b/back/src/app.module.ts @@ -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 {} diff --git a/back/src/auth/auth.service.ts b/back/src/auth/auth.service.ts index 7cca4b1..1568cd8 100644 --- a/back/src/auth/auth.service.ts +++ b/back/src/auth/auth.service.ts @@ -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 { - const user = await this.userService.user({username}); + async validateUser( + username: string, + password: string, + ): Promise { + 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, }; } } diff --git a/back/src/auth/local.strategy.ts b/back/src/auth/local.strategy.ts index efa05fb..c9d1cce 100644 --- a/back/src/auth/local.strategy.ts +++ b/back/src/auth/local.strategy.ts @@ -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 { - const user = await this.authService.validateUser(username, password); - if (!user) { - throw new UnauthorizedException(); - } - return user; - } -} \ No newline at end of file + async validate( + username: string, + password: string, + ): Promise { + const user = await this.authService.validateUser(username, password); + if (!user) { + throw new UnauthorizedException(); + } + return user; + } +} diff --git a/back/src/lesson/lesson.controller.spec.ts b/back/src/lesson/lesson.controller.spec.ts new file mode 100644 index 0000000..9f87a9d --- /dev/null +++ b/back/src/lesson/lesson.controller.spec.ts @@ -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); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/back/src/lesson/lesson.controller.ts b/back/src/lesson/lesson.controller.ts new file mode 100644 index 0000000..1b54bff --- /dev/null +++ b/back/src/lesson/lesson.controller.ts @@ -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> { + 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 { + 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 { + 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 { + try { + return await this.lessonService.delete(id); + } catch (e) { + console.log(e); + throw new BadRequestException(null, e.toString()); + } + } +} diff --git a/back/src/lesson/lesson.module.ts b/back/src/lesson/lesson.module.ts new file mode 100644 index 0000000..66799da --- /dev/null +++ b/back/src/lesson/lesson.module.ts @@ -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 {} diff --git a/back/src/lesson/lesson.service.spec.ts b/back/src/lesson/lesson.service.spec.ts new file mode 100644 index 0000000..c857a0f --- /dev/null +++ b/back/src/lesson/lesson.service.spec.ts @@ -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); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/back/src/lesson/lesson.service.ts b/back/src/lesson/lesson.service.ts new file mode 100644 index 0000000..aed4a1e --- /dev/null +++ b/back/src/lesson/lesson.service.ts @@ -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 { + const { skip, take, cursor, where, orderBy } = params; + return this.prisma.lesson.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + } + + async get(id: number): Promise { + return this.prisma.lesson.findFirst({ + where: { + id: id, + }, + }); + } + + async create(lesson: Lesson): Promise { + return this.prisma.lesson.create({ data: lesson }); + } + + async delete(id: number): Promise { + return this.prisma.lesson.delete({ where: { id: id } }); + } +} diff --git a/back/src/main.ts b/back/src/main.ts index 0f304a2..8a0ebd4 100644 --- a/back/src/main.ts +++ b/back/src/main.ts @@ -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(); diff --git a/back/src/models/plage.ts b/back/src/models/plage.ts new file mode 100644 index 0000000..1da0f13 --- /dev/null +++ b/back/src/models/plage.ts @@ -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 { + @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; + } +} diff --git a/back/src/users/users.service.ts b/back/src/users/users.service.ts index c167c87..df3a75d 100644 --- a/back/src/users/users.service.ts +++ b/back/src/users/users.service.ts @@ -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 { - return this.prisma.user.findUnique({ - where: userWhereUniqueInput, - }); - } + async user( + userWhereUniqueInput: Prisma.UserWhereUniqueInput, + ): Promise { + return this.prisma.user.findUnique({ + where: userWhereUniqueInput, + }); + } - async users(params: { - skip?: number; - take?: number; - cursor?: Prisma.UserWhereUniqueInput; - where?: Prisma.UserWhereInput; - orderBy?: Prisma.UserOrderByWithRelationInput; - }): Promise { - 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 { + const { skip, take, cursor, where, orderBy } = params; + return this.prisma.user.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + } - async createUser(data: Prisma.UserCreateInput): Promise { - data.password = await bcrypt.hash(data.password, 8) - return this.prisma.user.create({ - data, - }); - } + async createUser(data: Prisma.UserCreateInput): Promise { + data.password = await bcrypt.hash(data.password, 8); + return this.prisma.user.create({ + data, + }); + } - async updateUser(params: { - where: Prisma.UserWhereUniqueInput; - data: Prisma.UserUpdateInput; - }): Promise { - const { where, data } = params; - return this.prisma.user.update({ - data, - where, - }); - } + async updateUser(params: { + where: Prisma.UserWhereUniqueInput; + data: Prisma.UserUpdateInput; + }): Promise { + const { where, data } = params; + return this.prisma.user.update({ + data, + where, + }); + } - async deleteUser(where: Prisma.UserWhereUniqueInput): Promise { - return this.prisma.user.delete({ - where, - }); - } + async deleteUser(where: Prisma.UserWhereUniqueInput): Promise { + return this.prisma.user.delete({ + where, + }); + } } diff --git a/back/test/robot/lesson/lesson.robot b/back/test/robot/lesson/lesson.robot new file mode 100644 index 0000000..9c72ff7 --- /dev/null +++ b/back/test/robot/lesson/lesson.robot @@ -0,0 +1,80 @@ +*** Settings *** +Documentation Tests of the /lesson route. +... Ensures that the lesson CRUD works corectly. + +Resource ../rest.resource + + +*** Test Cases *** +Post a lesson + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + [Teardown] DELETE /lesson/${res.body.id} + +Get a lesson + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{get}= GET /lesson/${res.body.id} + Output + Should Be Equal ${res.body} ${get.body} + [Teardown] DELETE /lesson/${res.body.id} + +Get a non-lesson + [Documentation] Get a lesson + &{get}= GET /lesson/toto + Output + Integer response status 400 + +Get a not-existing-lesson + [Documentation] Get a lesson + &{get}= GET /lesson/99999999 + Output + Integer response status 404 + +Get all lessons + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{res2}= POST + ... /lesson + ... {"name": "tata", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{get}= GET /lesson + Output + Should Contain ${get.body.data} ${res.body} + Should Contain ${get.body.data} ${res2.body} + + [Teardown] Run Keywords DELETE /lesson/${res.body.id} + ... AND DELETE /lesson/${res2.body.id} + +Get all lessons filtered + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{res2}= POST + ... /lesson + ... {"name": "tata", "requiredLevel": 3, "mainSkill": "Distance", "description": "What am i doing"} + Output + Integer response status 201 + &{get}= GET /lesson?mainSkill=Distance + Output + Should Not Contain ${get.body.data} ${res.body} + Should Contain ${get.body.data} ${res2.body} + + [Teardown] Run Keywords DELETE /lesson/${res.body.id} + ... AND DELETE /lesson/${res2.body.id} diff --git a/back/test/robot/users/users.robot b/back/test/robot/users/users.robot index 45135e6..034d07d 100644 --- a/back/test/robot/users/users.robot +++ b/back/test/robot/users/users.robot @@ -1,14 +1,14 @@ *** Settings *** Documentation Tests of the /users route. ... Ensures that the users CRUD works corectly. + Resource ../rest.resource -*** Keywords *** *** Test Cases *** Create a user [Documentation] Create a user - POST /users {"username": "i-don-t-exist", "password": "pass", "email": "wow@gmail.com"} + &{res}= POST /users {"username": "louis-boufon", "password": "pass", "email": "wow@gmail.com"} Output Integer response status 201 - [Teardown] DELETE /users/1 \ No newline at end of file + [Teardown] DELETE /users/${res.body.id} diff --git a/back/tsconfig.json b/back/tsconfig.json index adb614c..cf106ab 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -12,10 +12,10 @@ "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, + "strictNullChecks": true, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": true } } diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity deleted file mode 100644 index 19ce570..0000000 --- a/node_modules/.yarn-integrity +++ /dev/null @@ -1,12 +0,0 @@ -{ - "systemParams": "linux-x64-108", - "modulesFolders": [ - "node_modules" - ], - "flags": [], - "linkedModules": [], - "topLevelPatterns": [], - "lockfileEntries": {}, - "files": [], - "artifacts": {} -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..64e05ed --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Chromacase", + "lockfileVersion": 2, + "requires": true, + "packages": {} +}