Skip to content

Commit

Permalink
#9 - Added changes according to feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Anders164a committed Oct 4, 2023
1 parent 76672cc commit d63a7b8
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 29 deletions.
4 changes: 2 additions & 2 deletions src/locales/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@
"required": "Farve er påkrævet",
"type": "Farve skal være en tekst"
},
"itemid": {
"type": "Item id skal være et tal"
"parentId": {
"type": "Parent id skal være et tal"
}
}
}
4 changes: 2 additions & 2 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@
"required": "Color is required",
"type": "Color must be a string"
},
"itemid": {
"type": "Item id must be a number"
"parentId": {
"type": "Parent id must be a number"
}
}
}
44 changes: 43 additions & 1 deletion src/modules/item/folder/__test__/add.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import { User } from '@prisma/client';
import UserService from '../../../auth/user.service';
import AuthService from '../../../auth/auth.service';
import FolderService from '../folder.service';

describe('POST /api/folder', () => {
let userService: UserService;
let authService: AuthService;
let folderService: FolderService;

let user: User;
let otherUser: User;

beforeAll(async () => {
authService = new AuthService();
userService = new UserService();
folderService = new FolderService();

user = await userService.createUser({
name: 'Joe Biden the 1st',
email: '[email protected]',
password: '1234',
});
otherUser = await userService.createUser({
name: 'Joe Biden the 2nd',
email: '[email protected]',
password: '4321',
});
});

it('should return status 200 and folder', async () => {
Expand Down Expand Up @@ -72,6 +81,39 @@ describe('POST /api/folder', () => {
});
});

it('should return status 401, when parent id is provided, but no access to parent', async () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
name: 'Folder1',
ownerId: otherUser.id,
parentId: null,
color: '#78BC61',
});

const response = await global.fastify.inject({
method: 'POST',
url: '/api/folder',
headers: {
authorization: 'Bearer ' + accessToken,
},
payload: {
name: 'Folder Name',
color: '#78BC61',
parentId: folder.id,
},
});

expect(response.statusCode).toBe(401);
expect(response.json()).toEqual({
error: 'UnauthorizedError',
errors: {
_: ['Unauthorized'],
},
statusCode: 401,
});
});

it('should return status 401, when folder name is not provided', async () => {
const { accessToken } = await authService.createTokens(user.id);

Expand Down Expand Up @@ -142,7 +184,7 @@ describe('POST /api/folder', () => {
expect(response.json()).toEqual({
error: 'ValidationError',
errors: {
parentId: ['Item id must be a number'],
parentId: ['Parent id must be a number'],
},
statusCode: 400,
});
Expand Down
5 changes: 1 addition & 4 deletions src/modules/item/folder/__test__/delete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ describe('DELETE /api/folder/:id', () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: user.id,
parentId: null,
Expand All @@ -58,7 +57,6 @@ describe('DELETE /api/folder/:id', () => {

it('should return status 401, when unauthorized', async () => {
const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: user.id,
parentId: null,
Expand All @@ -83,11 +81,10 @@ describe('DELETE /api/folder/:id', () => {
});
});

it('should return status 401, when folder id is provided but you do not own it', async () => {
it('should return status 401, when folder id is not accessible to you', async () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: otherUser.id,
parentId: null,
Expand Down
5 changes: 1 addition & 4 deletions src/modules/item/folder/__test__/edit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ describe('PUT /api/folder', () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: user.id,
parentId: null,
Expand Down Expand Up @@ -65,7 +64,6 @@ describe('PUT /api/folder', () => {

it('should return status 401, when unauthorized', async () => {
const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: user.id,
parentId: null,
Expand Down Expand Up @@ -95,11 +93,10 @@ describe('PUT /api/folder', () => {
});
});

it('should return status 401, when folder id is provided but you do not own it', async () => {
it('should return status 401, when folder id is not accessible to you', async () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: otherUser.id,
parentId: null,
Expand Down
5 changes: 1 addition & 4 deletions src/modules/item/folder/__test__/read.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ describe('GET /api/folder/:id', () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: user.id,
parentId: null,
Expand All @@ -58,7 +57,6 @@ describe('GET /api/folder/:id', () => {

it('should return status 401, when unauthorized', async () => {
const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: user.id,
parentId: null,
Expand All @@ -83,11 +81,10 @@ describe('GET /api/folder/:id', () => {
});
});

it('should return status 401, when folder id is provided but you do not own it', async () => {
it('should return status 401, when folder id is not accessible to you', async () => {
const { accessToken } = await authService.createTokens(user.id);

const folder = await folderService.createFolder({
mimeType: 'application/vnd.cloudstore.folder',
name: 'Folder1',
ownerId: otherUser.id,
parentId: null,
Expand Down
22 changes: 22 additions & 0 deletions src/modules/item/folder/access.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import FolderService from './folder.service';
import ItemService from '../item.service';

export default class AccessService {
private itemService: ItemService;
private folderService: FolderService;

constructor(itemService: ItemService, folderService: FolderService) {
this.itemService = itemService;
this.folderService = folderService;
}

public async hasAccessToItem(itemId: number, userId: number): Promise<boolean> {
const item = await this.itemService.getById(itemId);

if (item.ownerId === userId) {
return true;
}

return false;
}
}
22 changes: 16 additions & 6 deletions src/modules/item/folder/folder.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { FastifyReply, FastifyRequest } from 'fastify';
import { ReadInput, EditInput, AddInput, DeleteInput } from './folder.schema';
import FolderService from './folder.service';
import AccessService from './access.service';

export default class ItemController {
export default class FolderController {
private folderService: FolderService;
private accessService: AccessService;

constructor(folderService: FolderService) {
constructor(folderService: FolderService, accessService: AccessService) {
this.folderService = folderService;
this.accessService = accessService;
}

public async readHandler(
Expand All @@ -18,7 +21,7 @@ export default class ItemController {
try {
const folder = await this.folderService.getByItemId(request.params.id);

if (folder.ownerId !== request.user.sub) {
if (!(await this.accessService.hasAccessToItem(folder.id, request.user.sub))) {
return reply.unauthorized();
}

Expand All @@ -42,7 +45,7 @@ export default class ItemController {
try {
const folder = await this.folderService.getByItemId(request.body.id);

if (folder.ownerId !== request.user.sub) {
if (!(await this.accessService.hasAccessToItem(folder.id, request.user.sub))) {
return reply.unauthorized();
}

Expand All @@ -66,9 +69,16 @@ export default class ItemController {
reply: FastifyReply,
) {
try {
if (
request.body.parentId !== null &&
request.body.parentId !== undefined &&
!(await this.accessService.hasAccessToItem(request.body.parentId, request.user.sub))
) {
return reply.unauthorized();
}

const folder = await this.folderService.createFolder({
name: request.body.name,
mimeType: 'application/vnd.cloudstore.folder',
color: request.body.color,
ownerId: request.user.sub,
parentId: request.body.parentId ?? null,
Expand All @@ -90,7 +100,7 @@ export default class ItemController {
try {
const folder = await this.folderService.getByItemId(request.params.id);

if (folder.ownerId !== request.user.sub) {
if (!(await this.accessService.hasAccessToItem(folder.id, request.user.sub))) {
return reply.unauthorized();
}

Expand Down
8 changes: 7 additions & 1 deletion src/modules/item/folder/folder.route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { FastifyInstance } from 'fastify';
import FolderController from './folder.controller';
import FolderService from './folder.service';
import AccessService from './access.service';
import ItemService from '../item.service';

export default async (fastify: FastifyInstance) => {
const folderController = new FolderController(new FolderService());
const folderService = new FolderService();
const folderController = new FolderController(
folderService,
new AccessService(new ItemService(), folderService),
);

fastify.get(
'/:id',
Expand Down
7 changes: 3 additions & 4 deletions src/modules/item/folder/folder.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const editFolderSchema = {
parentId: {
type: ['number', 'null'],
errorMessage: {
type: 'folder.itemid.type',
type: 'folder.parentId.type',
},
},
},
Expand Down Expand Up @@ -144,7 +144,7 @@ const addFolderSchema = {
parentId: {
type: ['number', 'null'],
errorMessage: {
type: 'folder.itemid.type',
type: 'folder.parentId.type',
},
},
},
Expand Down Expand Up @@ -218,14 +218,13 @@ export type DeleteInput = FromSchema<typeof deleteFolderSchema>;
export type AddFolder = {
name: string;
color: string;
mimeType: string;
ownerId: number;
parentId: number | null;
};

export type ItemFolder = prismaItemFolderType & { item: Item };
export type Folder = Omit<prismaItemFolderType, 'id' | 'itemId'> & Item;
export type UpdateFolder = { id: number } & Partial<AddFolder> & UpdateItem;
export type UpdateFolder = { id: number } & Partial<AddFolder> & Omit<UpdateItem, 'mimeType'>;

export const folderSchemas = [
addFolderSchema,
Expand Down
2 changes: 1 addition & 1 deletion src/modules/item/folder/folder.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default class FolderService {
item: {
create: {
name: input.name,
mimeType: input.mimeType,
mimeType: 'application/vnd.cloudstore.folder',
ownerId: input.ownerId,
parentId: input.parentId,
},
Expand Down

0 comments on commit d63a7b8

Please sign in to comment.