diff --git a/back/.gitignore b/back/.gitignore index 22f55ad..69122cb 100644 --- a/back/.gitignore +++ b/back/.gitignore @@ -32,4 +32,9 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +# Robots tests +log.html +output.xml +report.html diff --git a/back/prisma/migrations/20220924084638_/migration.sql b/back/prisma/migrations/20220924084638_/migration.sql new file mode 100644 index 0000000..07bf1b5 --- /dev/null +++ b/back/prisma/migrations/20220924084638_/migration.sql @@ -0,0 +1,61 @@ +-- 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 "Song" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "artistId" INTEGER NOT NULL, + "albumId" INTEGER, + "genreId" INTEGER NOT NULL, + "difficulties" JSONB NOT NULL, + + CONSTRAINT "Song_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Genre" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Genre_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Artist" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Artist_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Album" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Album_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "Song_name_key" ON "Song"("name"); + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_albumId_fkey" FOREIGN KEY ("albumId") REFERENCES "Album"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926080922_/migration.sql b/back/prisma/migrations/20220926080922_/migration.sql new file mode 100644 index 0000000..41fdc96 --- /dev/null +++ b/back/prisma/migrations/20220926080922_/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "Song" DROP CONSTRAINT "Song_genreId_fkey"; + +-- AlterTable +ALTER TABLE "Song" ALTER COLUMN "genreId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926082047_/migration.sql b/back/prisma/migrations/20220926082047_/migration.sql new file mode 100644 index 0000000..6bb82d6 --- /dev/null +++ b/back/prisma/migrations/20220926082047_/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "Song" DROP CONSTRAINT "Song_artistId_fkey"; + +-- AlterTable +ALTER TABLE "Song" ALTER COLUMN "artistId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926084154_/migration.sql b/back/prisma/migrations/20220926084154_/migration.sql new file mode 100644 index 0000000..21b258d --- /dev/null +++ b/back/prisma/migrations/20220926084154_/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `description` on the `Song` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Song" DROP COLUMN "description"; diff --git a/back/prisma/schema.prisma b/back/prisma/schema.prisma index 579d951..f18c7b7 100644 --- a/back/prisma/schema.prisma +++ b/back/prisma/schema.prisma @@ -17,6 +17,39 @@ model User { LessonHistory LessonHistory[] } +model Song { + id Int @id @default(autoincrement()) + name String @unique + artistId Int? + artist Artist? @relation(fields: [artistId], references: [id]) + albumId Int? + album Album? @relation(fields: [albumId], references: [id]) + genreId Int? + genre Genre? @relation(fields: [genreId], references: [id]) + difficulties Json +} + +model Genre { + id Int @id @default(autoincrement()) + name String + + Song Song[] +} + +model Artist { + id Int @id @default(autoincrement()) + name String + + Song Song[] +} + +model Album { + id Int @id @default(autoincrement()) + name String + + Song Song[] +} + model Lesson { id Int @id @default(autoincrement()) name String diff --git a/back/src/app.controller.spec.ts b/back/src/app.controller.spec.ts index d22f389..e2c0215 100644 --- a/back/src/app.controller.spec.ts +++ b/back/src/app.controller.spec.ts @@ -3,20 +3,20 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; describe('AppController', () => { - let appController: AppController; + let appController: AppController; - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); - appController = app.get(AppController); - }); + appController = app.get(AppController); + }); - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); }); diff --git a/back/src/app.controller.ts b/back/src/app.controller.ts index cce879e..6509517 100644 --- a/back/src/app.controller.ts +++ b/back/src/app.controller.ts @@ -3,10 +3,10 @@ import { AppService } from './app.service'; @Controller() export class AppController { - constructor(private readonly appService: AppService) {} + constructor(private readonly appService: AppService) {} - @Get() - getHello(): string { - return this.appService.getHello(); - } + @Get() + getHello(): string { + return this.appService.getHello(); + } } diff --git a/back/src/app.module.ts b/back/src/app.module.ts index d87e0cb..23e15a7 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 { SongModule } from './song/song.module'; import { LessonModule } from './lesson/lesson.module'; @Module({ - imports: [UsersModule, PrismaModule, AuthModule, LessonModule], + imports: [UsersModule, PrismaModule, AuthModule, SongModule, LessonModule], controllers: [AppController], providers: [AppService, PrismaService], }) diff --git a/back/src/app.service.ts b/back/src/app.service.ts index 927d7cc..7e99928 100644 --- a/back/src/app.service.ts +++ b/back/src/app.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class AppService { - getHello(): string { - return 'Hello World!'; - } + getHello(): string { + return 'Hello World!'; + } } diff --git a/back/src/auth/auth.controller.spec.ts b/back/src/auth/auth.controller.spec.ts index 27a31e6..fa86936 100644 --- a/back/src/auth/auth.controller.spec.ts +++ b/back/src/auth/auth.controller.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthController } from './auth.controller'; describe('AuthController', () => { - let controller: AuthController; + let controller: AuthController; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AuthController], + }).compile(); - controller = module.get(AuthController); - }); + controller = module.get(AuthController); + }); - it('should be defined', () => { - expect(controller).toBeDefined(); - }); + it('should be defined', () => { + expect(controller).toBeDefined(); + }); }); diff --git a/back/src/auth/auth.controller.ts b/back/src/auth/auth.controller.ts index 1722424..446ee6b 100644 --- a/back/src/auth/auth.controller.ts +++ b/back/src/auth/auth.controller.ts @@ -7,7 +7,7 @@ import { Body, Delete, BadRequestException, - HttpCode, + HttpCode, } from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; @@ -62,7 +62,7 @@ export class AuthController { @UseGuards(JwtAuthGuard) @ApiBearerAuth() - @ApiOkResponse({ description: 'Successfully deleted', type: User}) + @ApiOkResponse({ description: 'Successfully deleted', type: User }) @ApiUnauthorizedResponse({ description: 'Invalid token' }) @Delete('me') deleteSelf(@Request() req: any): Promise { diff --git a/back/src/auth/auth.module.ts b/back/src/auth/auth.module.ts index 4c721b4..3f09593 100644 --- a/back/src/auth/auth.module.ts +++ b/back/src/auth/auth.module.ts @@ -10,16 +10,20 @@ import { ConfigService } from '@nestjs/config'; import { JwtStrategy } from './jwt.strategy'; @Module({ - imports: [ConfigModule, UsersModule, PassportModule, - JwtModule.registerAsync({ - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - secret: configService.get('JWT_SECRET'), - signOptions: { expiresIn: '1h' }, - }), - inject: [ConfigService], - })], - providers: [AuthService, LocalStrategy, JwtStrategy], - controllers: [AuthController] + imports: [ + ConfigModule, + UsersModule, + PassportModule, + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + secret: configService.get('JWT_SECRET'), + signOptions: { expiresIn: '1h' }, + }), + inject: [ConfigService], + }), + ], + providers: [AuthService, LocalStrategy, JwtStrategy], + controllers: [AuthController], }) export class AuthModule {} diff --git a/back/src/auth/auth.service.spec.ts b/back/src/auth/auth.service.spec.ts index 800ab66..a80b4b6 100644 --- a/back/src/auth/auth.service.spec.ts +++ b/back/src/auth/auth.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthService } from './auth.service'; describe('AuthService', () => { - let service: AuthService; + let service: AuthService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [AuthService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AuthService], + }).compile(); - service = module.get(AuthService); - }); + service = module.get(AuthService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/src/auth/constants.ts b/back/src/auth/constants.ts index 6f4f3ba..38163cb 100644 --- a/back/src/auth/constants.ts +++ b/back/src/auth/constants.ts @@ -1,12 +1,11 @@ -import { Injectable } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class Constants { - constructor(private configService: ConfigService) {} getSecret = () => { - return this.configService.get("JWT_SECRET"); - } -} \ No newline at end of file + return this.configService.get('JWT_SECRET'); + }; +} diff --git a/back/src/auth/dto/login.dto.ts b/back/src/auth/dto/login.dto.ts index 78d3526..2282784 100644 --- a/back/src/auth/dto/login.dto.ts +++ b/back/src/auth/dto/login.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty } from "class-validator"; +import { IsNotEmpty } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class LoginDto { diff --git a/back/src/auth/dto/register.dto.ts b/back/src/auth/dto/register.dto.ts index d8dd9a8..58529e4 100644 --- a/back/src/auth/dto/register.dto.ts +++ b/back/src/auth/dto/register.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty } from "class-validator"; +import { IsNotEmpty } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class RegisterDto { diff --git a/back/src/auth/interface/payload.interface.ts b/back/src/auth/interface/payload.interface.ts index e414f74..2fbce8a 100644 --- a/back/src/auth/interface/payload.interface.ts +++ b/back/src/auth/interface/payload.interface.ts @@ -1,4 +1,4 @@ export default interface PayloadInterface { username: string; id: number; -} \ No newline at end of file +} diff --git a/back/src/auth/jwt-auth.guard.ts b/back/src/auth/jwt-auth.guard.ts index 18588a5..2155290 100644 --- a/back/src/auth/jwt-auth.guard.ts +++ b/back/src/auth/jwt-auth.guard.ts @@ -2,4 +2,4 @@ import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() -export class JwtAuthGuard extends AuthGuard('jwt') {} \ No newline at end of file +export class JwtAuthGuard extends AuthGuard('jwt') {} diff --git a/back/src/auth/jwt.strategy.ts b/back/src/auth/jwt.strategy.ts index a03932e..844062c 100644 --- a/back/src/auth/jwt.strategy.ts +++ b/back/src/auth/jwt.strategy.ts @@ -1,4 +1,3 @@ - import { ExtractJwt, Strategy } from 'passport-jwt'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable } from '@nestjs/common'; @@ -6,15 +5,15 @@ import { ConfigService } from '@nestjs/config'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { - constructor(private configService: ConfigService) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: configService.get('JWT_SECRET'), - }); - } + constructor(private configService: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configService.get('JWT_SECRET'), + }); + } - async validate(payload: any) { - return { id: payload.id, username: payload.username }; - } -} \ No newline at end of file + async validate(payload: any) { + return { id: payload.id, username: payload.username }; + } +} diff --git a/back/src/auth/local-auth.guard.ts b/back/src/auth/local-auth.guard.ts index a3a4bf3..ccf962b 100644 --- a/back/src/auth/local-auth.guard.ts +++ b/back/src/auth/local-auth.guard.ts @@ -1,4 +1,3 @@ - import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; diff --git a/back/src/lesson/lesson.controller.spec.ts b/back/src/lesson/lesson.controller.spec.ts index 9f87a9d..9350d52 100644 --- a/back/src/lesson/lesson.controller.spec.ts +++ b/back/src/lesson/lesson.controller.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LessonController } from './lesson.controller'; describe('LessonController', () => { - let controller: LessonController; + let controller: LessonController; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [LessonController], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [LessonController], + }).compile(); - controller = module.get(LessonController); - }); + controller = module.get(LessonController); + }); - it('should be defined', () => { - expect(controller).toBeDefined(); - }); + it('should be defined', () => { + expect(controller).toBeDefined(); + }); }); diff --git a/back/src/lesson/lesson.service.spec.ts b/back/src/lesson/lesson.service.spec.ts index c857a0f..3713a08 100644 --- a/back/src/lesson/lesson.service.spec.ts +++ b/back/src/lesson/lesson.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LessonService } from './lesson.service'; describe('LessonService', () => { - let service: LessonService; + let service: LessonService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [LessonService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [LessonService], + }).compile(); - service = module.get(LessonService); - }); + service = module.get(LessonService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/src/main.ts b/back/src/main.ts index 8a0ebd4..f82b632 100644 --- a/back/src/main.ts +++ b/back/src/main.ts @@ -4,18 +4,18 @@ 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); + const app = await NestFactory.create(AppModule); + const prismaService = app.get(PrismaService); + 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); + 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); + await app.listen(3000); } bootstrap(); diff --git a/back/src/prisma/prisma.module.ts b/back/src/prisma/prisma.module.ts index e569e2d..3e6d1b6 100644 --- a/back/src/prisma/prisma.module.ts +++ b/back/src/prisma/prisma.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { PrismaService } from './prisma.service'; @Module({ - providers: [PrismaService], - exports: [PrismaService] + providers: [PrismaService], + exports: [PrismaService], }) export class PrismaModule {} diff --git a/back/src/prisma/prisma.service.spec.ts b/back/src/prisma/prisma.service.spec.ts index a68cb9e..d694c29 100644 --- a/back/src/prisma/prisma.service.spec.ts +++ b/back/src/prisma/prisma.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { PrismaService } from './prisma.service'; describe('PrismaService', () => { - let service: PrismaService; + let service: PrismaService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [PrismaService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [PrismaService], + }).compile(); - service = module.get(PrismaService); - }); + service = module.get(PrismaService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/src/prisma/prisma.service.ts b/back/src/prisma/prisma.service.ts index edf6532..6feee25 100644 --- a/back/src/prisma/prisma.service.ts +++ b/back/src/prisma/prisma.service.ts @@ -3,13 +3,13 @@ import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { - async onModuleInit() { - await this.$connect(); - } + async onModuleInit() { + await this.$connect(); + } - async enableShutdownHooks(app: INestApplication) { - this.$on('beforeExit', async () => { - await app.close(); - }); - } + async enableShutdownHooks(app: INestApplication) { + this.$on('beforeExit', async () => { + await app.close(); + }); + } } diff --git a/back/src/song/dto/create-song.dto.ts b/back/src/song/dto/create-song.dto.ts new file mode 100644 index 0000000..02b4a2d --- /dev/null +++ b/back/src/song/dto/create-song.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty } from 'class-validator'; + +export class CreateSongDto { + @IsNotEmpty() + @ApiProperty() + name: string; + + @IsNotEmpty() + @ApiProperty() + difficulties: object; +} diff --git a/back/src/song/song.controller.spec.ts b/back/src/song/song.controller.spec.ts new file mode 100644 index 0000000..bbe129b --- /dev/null +++ b/back/src/song/song.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SongController } from './song.controller'; + +describe('SongController', () => { + let controller: SongController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SongController], + }).compile(); + + controller = module.get(SongController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/back/src/song/song.controller.ts b/back/src/song/song.controller.ts new file mode 100644 index 0000000..cb06efe --- /dev/null +++ b/back/src/song/song.controller.ts @@ -0,0 +1,65 @@ +import { + BadRequestException, + Body, + Controller, + DefaultValuePipe, + Delete, + Get, + NotFoundException, + Param, + ParseIntPipe, + Post, + Query, + Req, +} from '@nestjs/common'; +import { Plage } from 'src/models/plage'; +import { CreateSongDto } from './dto/create-song.dto'; +import { SongService } from './song.service'; +import { Request } from 'express'; +import { Prisma, Song } from '@prisma/client'; + +@Controller('song') +export class SongController { + constructor(private readonly songService: SongService) {} + + @Post() + async create(@Body() createSongDto: CreateSongDto) { + return await this.songService.createSong(createSongDto); + } + + @Delete(':id') + async remove(@Param('id', ParseIntPipe) id: number) { + return await this.songService.deleteSong({ id }); + } + + @Get() + async findAll( + @Req() req: Request, + @Query() filter: Prisma.SongWhereInput, + @Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number, + @Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number, + ): Promise> { + try { + const ret = await this.songService.songs({ + skip, + take, + where: { + ...filter, + id: filter.id ? +filter.id : undefined, + }, + }); + return new Plage(ret, req); + } catch (e) { + console.log(e); + throw new BadRequestException(null, e?.toString()); + } + } + + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + let res = await this.songService.song({ id }); + + if (res === null) throw new NotFoundException('Song not found'); + return res; + } +} diff --git a/back/src/song/song.module.ts b/back/src/song/song.module.ts new file mode 100644 index 0000000..1cf9780 --- /dev/null +++ b/back/src/song/song.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { SongService } from './song.service'; +import { SongController } from './song.controller'; +import { PrismaModule } from 'src/prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + providers: [SongService], + controllers: [SongController], +}) +export class SongModule {} diff --git a/back/src/song/song.service.spec.ts b/back/src/song/song.service.spec.ts new file mode 100644 index 0000000..dd86410 --- /dev/null +++ b/back/src/song/song.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SongService } from './song.service'; + +describe('SongService', () => { + let service: SongService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SongService], + }).compile(); + + service = module.get(SongService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/back/src/song/song.service.ts b/back/src/song/song.service.ts new file mode 100644 index 0000000..4a7a285 --- /dev/null +++ b/back/src/song/song.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma, Song } from '@prisma/client'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Injectable() +export class SongService { + constructor(private prisma: PrismaService) {} + + async createSong(data: Prisma.SongCreateInput): Promise { + return this.prisma.song.create({ + data, + }); + } + + async song( + songWhereUniqueInput: Prisma.SongWhereUniqueInput, + ): Promise { + return this.prisma.song.findUnique({ + where: songWhereUniqueInput, + }); + } + + async songs(params: { + skip?: number; + take?: number; + cursor?: Prisma.SongWhereUniqueInput; + where?: Prisma.SongWhereInput; + orderBy?: Prisma.SongOrderByWithRelationInput; + }): Promise { + const { skip, take, cursor, where, orderBy } = params; + return this.prisma.song.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + } + + async deleteSong(where: Prisma.SongWhereUniqueInput): Promise { + return this.prisma.song.delete({ + where, + }); + } +} diff --git a/back/src/users/users.controller.spec.ts b/back/src/users/users.controller.spec.ts index a76d310..7721e48 100644 --- a/back/src/users/users.controller.spec.ts +++ b/back/src/users/users.controller.spec.ts @@ -3,18 +3,18 @@ import { UsersController } from './users.controller'; import { UsersService } from './users.service'; describe('UsersController', () => { - let controller: UsersController; + let controller: UsersController; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [UsersController], - providers: [UsersService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + providers: [UsersService], + }).compile(); - controller = module.get(UsersController); - }); + controller = module.get(UsersController); + }); - it('should be defined', () => { - expect(controller).toBeDefined(); - }); + it('should be defined', () => { + expect(controller).toBeDefined(); + }); }); diff --git a/back/src/users/users.module.ts b/back/src/users/users.module.ts index 89332aa..d1e76e7 100644 --- a/back/src/users/users.module.ts +++ b/back/src/users/users.module.ts @@ -4,9 +4,9 @@ import { UsersController } from './users.controller'; import { PrismaModule } from 'src/prisma/prisma.module'; @Module({ - imports: [PrismaModule], - controllers: [UsersController], - providers: [UsersService], - exports: [UsersService] + imports: [PrismaModule], + controllers: [UsersController], + providers: [UsersService], + exports: [UsersService], }) export class UsersModule {} diff --git a/back/src/users/users.service.spec.ts b/back/src/users/users.service.spec.ts index 62815ba..38b96e7 100644 --- a/back/src/users/users.service.spec.ts +++ b/back/src/users/users.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersService } from './users.service'; describe('UsersService', () => { - let service: UsersService; + let service: UsersService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [UsersService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); - service = module.get(UsersService); - }); + service = module.get(UsersService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/test/app.e2e-spec.ts b/back/test/app.e2e-spec.ts index 50cda62..5c49beb 100644 --- a/back/test/app.e2e-spec.ts +++ b/back/test/app.e2e-spec.ts @@ -4,21 +4,21 @@ import * as request from 'supertest'; import { AppModule } from './../src/app.module'; describe('AppController (e2e)', () => { - let app: INestApplication; + let app: INestApplication; - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); - app = moduleFixture.createNestApplication(); - await app.init(); - }); + app = moduleFixture.createNestApplication(); + await app.init(); + }); - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); }); diff --git a/back/test/robot/env/lib64 b/back/test/robot/env/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/back/test/robot/env/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/back/test/robot/songs/songs.robot b/back/test/robot/songs/songs.robot new file mode 100644 index 0000000..73379c6 --- /dev/null +++ b/back/test/robot/songs/songs.robot @@ -0,0 +1,84 @@ +*** Settings *** +Documentation Tests of the /song route. +... Ensures that the songs CRUD works corectly. +Resource ../rest.resource + + +*** Keywords *** +*** Test Cases *** +Create a song + [Documentation] Create a song + &{res}= POST /song {"name": "Mama mia", "difficulties": {}} + Output + Integer response status 201 + [Teardown] DELETE /song/${res.body.id} + +Find a song + [Documentation] Create a song and find it + &{res}= POST /song {"name": "Mama mia", "difficulties": {}} + Output + Integer response status 201 + &{get}= GET /song/${res.body.id} + Output + Integer response status 200 + Should Be Equal ${res.body} ${get.body} + [Teardown] DELETE /song/${res.body.id} + + +Find a song non existant + [Documentation] Find non existant song + &{get}= GET /song/9999 + Integer response status 404 + +Find multiples songs + [Documentation] Create two songs and find them + &{res}= POST /song {"name": "Mama mia", "difficulties": {}} + Output + Integer response status 201 + &{res2}= POST /song {"name": "Here we go again", "difficulties": {}} + Output + Integer response status 201 + + &{get}= GET /song + Output + Integer response status 200 + Should Contain ${get.body.data} ${res.body} + Should Contain ${get.body.data} ${res2.body} + [Teardown] Run Keywords DELETE /song/${res.body.id} + ... AND DELETE /song/${res2.body.id} + +Find multiples songs filtered + [Documentation] Create two songs and find them + &{res}= POST /song {"name": "Mamamia", "difficulties": {}} + Output + Integer response status 201 + &{res2}= POST /song {"name": "Here we go again", "difficulties": {}} + Output + Integer response status 201 + + &{get}= GET /song?name=Mamamia + Output + Integer response status 200 + Should Contain ${get.body.data} ${res.body} + Should Not Contain ${get.body.data} ${res2.body} + [Teardown] Run Keywords DELETE /song/${res.body.id} + ... AND DELETE /song/${res2.body.id} + + + +Find multiples songs filtered by type + [Documentation] Create two songs and find them + &{res}= POST /song {"name": "Mamamia", "difficulties": {}} + Output + Integer response status 201 + &{res2}= POST /song {"name": "Here we go again", "difficulties": {}} + Output + Integer response status 201 + + &{get}= GET /song?id=${res.body.id} + Output + Integer response status 200 + Should Contain ${get.body.data} ${res.body} + Should Not Contain ${get.body.data} ${res2.body} + [Teardown] Run Keywords DELETE /song/${res.body.id} + ... AND DELETE /song/${res2.body.id}