Skip to content

Commit

Permalink
desenvolvendo funcionalidade de upload da imagem do jogo
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro-Manoel committed Jun 22, 2022
1 parent 436aae7 commit efa65ba
Show file tree
Hide file tree
Showing 26 changed files with 630 additions and 57 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# api-rest-loja-games-online
# api-rest-loja-games-online
testando mo teclado d


5
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"express": "^4.17.3",
"express-async-errors": "^3.1.1",
"jsonwebtoken": "^8.5.1",
"multer": "^1.4.4",
"reflect-metadata": "^0.1.13",
"supertest": "^6.2.2",
"swagger-ui-express": "^4.3.0",
Expand All @@ -26,6 +27,7 @@
"@types/express": "^4.17.13",
"@types/jest": "^27.4.0",
"@types/jsonwebtoken": "^8.5.8",
"@types/multer": "^1.4.7",
"@types/supertest": "^2.0.11",
"@types/swagger-ui-express": "^4.1.3",
"@typescript-eslint/eslint-plugin": "^5.12.0",
Expand Down
50 changes: 25 additions & 25 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,46 @@ datasource db {
}

model User {
id String @id @default(uuid())
name String
email String @unique
password String
admin Boolean @default(false)
purchases Purchase[]
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@map("users")
id String @id @default(uuid())
name String
email String @unique
password String
admin Boolean @default(false)
purchases Purchase[]
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@map("users")
}

model Game {
id String @id @default(uuid())
title String @unique
release_date DateTime
value Float
description String
image_name String?
genres Genre[]
id String @id @default(uuid())
title String @unique
release_date DateTime
value Float
description String
image_name String?
genres Genre[]
purchases Purchase[]
created_at DateTime @default(now())
updated_at DateTime @updatedAt
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@map("games")
}

model Genre {
name String @id @unique
name String @id @unique
games Game[]
@@map("genres")
}

model Purchase {
user User @relation(fields: [userId], references: [id])
userId String
game Game @relation(fields: [gameId], references: [id])
gameId String
created_at DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId String
game Game @relation(fields: [gameId], references: [id])
gameId String
created_at DateTime @default(now())
@@id([userId, gameId])
@@map("purchases")
Expand Down
43 changes: 43 additions & 0 deletions src/config/upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import crypto from "crypto";
import { Request } from "express";
import multer, { FileFilterCallback } from "multer";
import { resolve } from "path";

import { AppError } from "@shared/errors/AppError";

const tmpFolder = resolve(__dirname, "..", "..", "tmp");

const configUploadImage = {
storage: multer.diskStorage({
destination: tmpFolder,
filename: (_, file, callback) => {
const fileHash = crypto.randomBytes(16).toString("hex");

const fileName = `${fileHash}-${file.originalname}`;

return callback(null, fileName);
},
}),

limits: {
fileSize: 5242880, // 5242880 Bytes = 5 MB
},

fileFilter: (
_: Request,
file: Express.Multer.File,
callback: FileFilterCallback
) => {
const validExtensions = ["jpeg", "png"];

if (!validExtensions.map((ex) => `image/${ex}`).includes(file.mimetype)) {
return callback(AppError.badRequest("Invalid file extension"));
}

return callback(null, true);
},
};

const uploadImage = multer(configUploadImage);

export { tmpFolder, uploadImage };
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { Prisma, PrismaPromise } from "@prisma/client";
import { prisma } from "@shared/infra/prisma";

class PrismaGamesRepository implements IGamesRepository {
async save(game: Game): Promise<void> {
await prisma.game.update({
where: { id: game.id },
data: game,
});
}

async create({
title,
release_date,
Expand Down
4 changes: 3 additions & 1 deletion src/modules/games/repositories/IGamesRepository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Game } from "@modules/games/entities/Game";

import { ICreateGameDTO } from "../dtos/ICreateGame";
import { Game } from "../entities/Game";

interface IGamesRepository {
create(data: ICreateGameDTO): Promise<Game>;
save(game: Game): Promise<void>;
findByTitle(title: string): Promise<Game>;
update(id: string, data: ICreateGameDTO): Promise<Game>;
findById(id: string): Promise<Game>;
Expand Down
6 changes: 6 additions & 0 deletions src/modules/games/repositories/fakes/FakeGamesRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import { IGamesRepository } from "../IGamesRepository";
class FakeGamesRepository implements IGamesRepository {
constructor(private games: Game[] = []) {}

async save(game: Game): Promise<void> {
const gameIndex = this.games.findIndex((g) => g.id === game.id);

this.games[gameIndex] = game;
}

async create({
title,
release_date,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* @jest-environment ./prisma/prisma-environment-jest
*/
import crypto from "crypto";
import request from "supertest";

import { app } from "@shared/infra/http/app";

import { createTestFile, deleteTestFile } from "./fileTest";

const URL = "/games";

let token: string;
let adminToken: string;
const imageName = "test.png";

describe("Upload game image controller", () => {
beforeAll(async () => {
const adminUserEmail = "[email protected]";
const userEmail = "[email protected]";
const password = "1234";

await request(app).post("/users").send({
name: "Test admin user",
email: adminUserEmail,
password,
admin: true,
});

const responseAdminToken = await request(app).post("/users/sessions").send({
email: adminUserEmail,
password,
});

adminToken = responseAdminToken.body.token;

await request(app).post("/users").send({
name: "Test user",
email: userEmail,
password,
admin: false,
});

const responseToken = await request(app).post("/users/sessions").send({
email: userEmail,
password,
});

token = responseToken.body.token;
await createTestFile(imageName, __dirname);
});

afterAll(async () => {
await deleteTestFile(imageName, __dirname);
});

it("should be able to upload the game image", async () => {
const gameCreatResponse = await request(app)
.post(URL)
.send({
title: "Test game name",
release_date: new Date(),
value: 78.5,
description: "Test game description",
genres: [],
})
.set({
Authorization: `Bearer ${adminToken}`,
});

const gameUpdateResponse = await request(app)
.patch(`${URL}/${gameCreatResponse.body.id}/image`)
.attach("image", `${__dirname}/${imageName}`)
.set({
Authorization: `Bearer ${adminToken}`,
});

expect(gameUpdateResponse.statusCode).toEqual(200);
expect(gameUpdateResponse.body).toHaveProperty("id");
expect(gameUpdateResponse.body).toHaveProperty("image_name");
expect(gameUpdateResponse.body.image_name).not.toBeNull();

await deleteTestFile(`games/${gameUpdateResponse.body.image_name}`);
});

it("should be able to update game image if it already exists", async () => {
await createTestFile(imageName, __dirname);

const gameCreatResponse = await request(app)
.post(URL)
.send({
title: "Test game name 2",
release_date: new Date(),
value: 78.5,
description: "Test game description",
genres: [],
})
.set({
Authorization: `Bearer ${adminToken}`,
});

const gameUpdateResponse1 = await request(app)
.patch(`${URL}/${gameCreatResponse.body.id}/image`)
.attach("image", `${__dirname}/${imageName}`)
.set({
Authorization: `Bearer ${adminToken}`,
});

await createTestFile(imageName, __dirname);

const gameUpdateResponse2 = await request(app)
.patch(`${URL}/${gameCreatResponse.body.id}/image`)
.attach("image", `${__dirname}/${imageName}`)
.set({
Authorization: `Bearer ${adminToken}`,
});

expect(gameUpdateResponse1.statusCode).toEqual(200);
expect(gameUpdateResponse2.statusCode).toEqual(200);
expect(gameUpdateResponse1.body).toHaveProperty("id");
expect(gameUpdateResponse2.body).toHaveProperty("id");
expect(gameUpdateResponse1.body).toHaveProperty("image_name");
expect(gameUpdateResponse2.body).toHaveProperty("image_name");
expect(gameUpdateResponse1.body.image_name).not.toBeNull();
expect(gameUpdateResponse2.body.image_name).not.toBeNull();
expect(gameUpdateResponse2.body.image_name).not.toEqual(
gameUpdateResponse1.body.image_name
);

await deleteTestFile(`games/${gameUpdateResponse2.body.image_name}`);
});

it("should not be able to upload the game image if user is not admin", async () => {
const gameCreatResponse = await request(app)
.post(URL)
.send({
title: "Test game name 3",
release_date: new Date(),
value: 78.5,
description: "Test game description",
genres: [],
})
.set({
Authorization: `Bearer ${adminToken}`,
});

const gameUpdateResponse = await request(app)
.patch(`${URL}/${gameCreatResponse.body.id}/image`)
.attach("image", `${__dirname}/${imageName}`)
.set({
Authorization: `Bearer ${token}`,
});

expect(gameUpdateResponse.statusCode).toEqual(400);
});

it("should not be able to upload the image unregistered game", async () => {
const fakeId = crypto.randomUUID();

const gameUpdateResponse = await request(app)
.patch(`${URL}/${fakeId}/image`)
.attach("image", `${__dirname}/${imageName}`)
.set({
Authorization: `Bearer ${adminToken}`,
});

expect(gameUpdateResponse.statusCode).toEqual(404);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Request, Response } from "express";
import { container } from "tsyringe";

import { HttpResponse } from "@shared/infra/http/models/HttpResponse";

import { UploadGameImageUseCase } from "./UploadGameImageUseCase";

class UploadGameImageController {
async handle(request: Request, response: Response): Promise<Response> {
const { id } = request.params;

const imageName = request.file.filename;

const uploadGameImageUseCase = container.resolve(UploadGameImageUseCase);

const game = await uploadGameImageUseCase.execute({
gameId: id,
imageName,
});

return new HttpResponse(response).ok(game);
}
}

export { UploadGameImageController };
Loading

0 comments on commit efa65ba

Please sign in to comment.