Skip to content

Commit 8303afd

Browse files
authored
refactor: Refactor backend (#63)
* refactor: clean legacy error handler * refactor: rename utils folder to helpers * feat: implement handlePrismaError * fix: type error * fix: remove dots * docs: update delete api doc * docs: update delete api doc * fix: handle not handled prisma errors * feat: add P2000 error * docs: update create vault doc * refactor: use StatusCodes * refactor: move errors into shared * refactor: add typing for handleApiRequest * revert: re-move HttpError in backend * revert: re-move HttpError in backend * feat: create InternalServerError * feat: implement new error handling * docs: add doc comment * refactor: rewrite errors dto * feat: Rewrite DTO (bodies, data, responses, errors) * fix: data doc * fix: api typing * fix: api docs typing * fix: api docs typing * feat: implement new error handling in ui * fix: error message display in modal * refactor: improve requests services * refactor: improve requests services * fix: body already consumed * Revert "fix: body already consumed" This reverts commit 683dc05. * Revert "refactor: improve requests services" This reverts commit 548de72. * Revert "refactor: improve requests services" This reverts commit c40df33. * fix: json parsing error while deleting * fix: remove length limit on secret on db * feat: implement VaultAlreadyExistsError * fix: async on parsing json on request service * refactor: get status of response in frontend * refactor: define options and params * refactor: tidy up dto by input or output * chore(linter): improve 'no-restricted-imports' * feat: add createdAt in vault table * feat: order the vaults list by creation date * feat: create Logger * feat: make Logger static * fix: set LoggerTagEnum to string * refactor: use Logger in api * refactor: use Logger in UI * refactor: create ApiLogger & UiLogger * refactor: add error objects in console errors * style: remove prefix in ui logger * fix: linter * fix: error message tests * fix: handleApiRequest tests * fix: useApi tests * fix: RequestService tests * test: Create Logger tests * fix: Rename utils to helpers in tests * fix: Move request.service.test.ts to abstract folder in tests * test: Create ApiLogger * test: Create UiLogger tests * fix: linter * fix: linter * fix: linter in request service test * fix: linter in request logger * fix: linter in api logger * fix: linter in handleApiRequest * fix: typo * fix: useless tag * fix: logger tests * fix: api logger tests * fix: ui logger tests * fix: handleApiRequest tests * fix: ApiLogger tests * fix: handleApiRequest tests * style: Format ESLint
1 parent 9136608 commit 8303afd

File tree

51 files changed

+1006
-205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1006
-205
lines changed

eslint.config.mjs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export default tseslint.config(
169169
'no-restricted-imports': [
170170
'error',
171171
{
172-
patterns: ['@api/*'],
172+
patterns: ['@api/*', '@prisma/*'],
173173
},
174174
],
175175
},
@@ -187,6 +187,18 @@ export default tseslint.config(
187187
},
188188
},
189189

190+
{
191+
files: ['src/modules/shared/**/*.{ts,tsx}'],
192+
rules: {
193+
'no-restricted-imports': [
194+
'error',
195+
{
196+
patterns: ['@api/*', '@ui/*', '@prisma/*'],
197+
},
198+
],
199+
},
200+
},
201+
190202
{
191203
files: ['**/*.test.ts', '**/*.spec.ts', '**/*.test.tsx', '**/*.spec.tsx'],
192204
plugins: { jest: eslintPluginJest },
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Vault" ALTER COLUMN "secret" SET DATA TYPE TEXT;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Vault" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;

prisma/schema.prisma

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ datasource db {
99
}
1010

1111
model Vault {
12-
uuid String @id @default(uuid())
13-
label String @db.VarChar(255)
14-
secret String @db.VarChar(255)
12+
uuid String @id @default(uuid())
13+
label String @db.VarChar(255)
14+
secret String
15+
createdAt DateTime @default(now())
1516
}

src/app/api/vaults/[id]/route.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import 'reflect-metadata';
22
import type { NextRequest, NextResponse } from 'next/server';
3-
import type { IdParam } from '@shared/dto/params/id.param';
43
import { container } from 'tsyringe';
5-
import { handleApiRequest } from '@api/utils/handle-api-request';
4+
import { handleApiRequest } from '@api/helpers/handle-api-request';
65
import { StatusCodes } from 'http-status-codes';
76
import { DeleteVaultUseCase } from '@api/usecases/vaults/delete-vault.usecase';
7+
import type { CreateVaultParams } from '@shared/dto/input/params/create-vault.params';
8+
import type { HttpOptions } from '@shared/dto/input/options/abstract/http-options';
89

910
/**
1011
* @swagger
@@ -24,22 +25,26 @@ import { DeleteVaultUseCase } from '@api/usecases/vaults/delete-vault.usecase';
2425
* 204:
2526
* description: The vault has been successfully deleted
2627
* 404:
27-
* description: Vault not found
28+
* description: Resource not found
29+
* content:
30+
* application/json:
31+
* schema:
32+
* $ref: '#/components/schemas/HttpErrorDto'
2833
* 500:
2934
* description: Internal Server Error
3035
* content:
3136
* application/json:
3237
* schema:
33-
* $ref: '#/components/schemas/HttpResponseDto'
38+
* $ref: '#/components/schemas/HttpErrorDto'
3439
*/
3540
export async function DELETE(
3641
request: NextRequest,
37-
params: IdParam
42+
options: HttpOptions<CreateVaultParams>
3843
): Promise<NextResponse> {
3944
const deleteVaultUseCase: DeleteVaultUseCase =
4045
container.resolve(DeleteVaultUseCase);
41-
return await handleApiRequest(
42-
() => deleteVaultUseCase.handle(params),
46+
return await handleApiRequest<void>(
47+
async () => deleteVaultUseCase.handle(await options.params),
4348
StatusCodes.NO_CONTENT
4449
);
4550
}

src/app/api/vaults/route.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import 'reflect-metadata';
22
import type { NextRequest, NextResponse } from 'next/server';
33
import { container } from 'tsyringe';
4-
import { handleApiRequest } from '@api/utils/handle-api-request';
4+
import { handleApiRequest } from '@api/helpers/handle-api-request';
55
import type { VaultModelDto } from '@shared/dto/models/vault.model.dto';
6-
import type { CreateVaultResponseDto } from '@shared/dto/responses/create-vault.response.dto';
76
import { CreateVaultUseCase } from '@api/usecases/vaults/create-vault.usecase';
8-
import type { GetMyVaultsResponseDto } from '@shared/dto/responses/get-my-vaults.response.dto';
97
import { GetMyVaultsUseCase } from '@api/usecases/vaults/get-my-vaults.usecase';
10-
import type { CreateVaultRequestDto } from '@shared/dto/requests/create-vault.request.dto';
8+
import type { CreateVaultRequestDto } from '@shared/dto/input/requests/create-vault.request.dto';
119
import { StatusCodes } from 'http-status-codes';
10+
import type { CreateVaultDataDto } from '@shared/dto/output/data/create-vault.data.dto';
11+
import type { GetMyVaultsDataDto } from '@shared/dto/output/data/get-my-vaults.data.dto';
12+
import type { HttpResponseDto } from '@shared/dto/output/responses/abstract/http.response.dto';
1213

1314
/**
1415
* @swagger
@@ -23,20 +24,22 @@ import { StatusCodes } from 'http-status-codes';
2324
* content:
2425
* application/json:
2526
* schema:
26-
* $ref: '#/components/schemas/GetMyVaultsResponseDto'
27+
* $ref: '#/components/schemas/GetMyVaultsBodyDto'
2728
* 500:
2829
* description: Internal Server Error
2930
* content:
3031
* application/json:
3132
* schema:
32-
* $ref: '#/components/schemas/HttpResponseDto'
33+
* $ref: '#/components/schemas/HttpErrorDto'
3334
*/
34-
export async function GET(): Promise<NextResponse> {
35+
export async function GET(): Promise<
36+
NextResponse<HttpResponseDto<GetMyVaultsDataDto>>
37+
> {
3538
const getMyVaultsUseCase: GetMyVaultsUseCase =
3639
container.resolve(GetMyVaultsUseCase);
37-
return await handleApiRequest(async () => {
40+
return await handleApiRequest<GetMyVaultsDataDto>(async () => {
3841
const myVaults: VaultModelDto[] = await getMyVaultsUseCase.handle();
39-
const response: GetMyVaultsResponseDto = { myVaults };
42+
const response: GetMyVaultsDataDto = { myVaults };
4043
return response;
4144
});
4245
}
@@ -60,21 +63,29 @@ export async function GET(): Promise<NextResponse> {
6063
* content:
6164
* application/json:
6265
* schema:
63-
* $ref: '#/components/schemas/CreateVaultResponseDto'
66+
* $ref: '#/components/schemas/CreateVaultBodyDto'
67+
* 400:
68+
* description: One of the requested values is too long
69+
* content:
70+
* application/json:
71+
* schema:
72+
* $ref: '#/components/schemas/HttpErrorDto'
6473
* 500:
6574
* description: Internal Server Error
6675
* content:
6776
* application/json:
6877
* schema:
69-
* $ref: '#/components/schemas/HttpResponseDto'
78+
* $ref: '#/components/schemas/HttpErrorDto'
7079
*/
71-
export async function POST(request: NextRequest): Promise<NextResponse> {
80+
export async function POST(
81+
request: NextRequest
82+
): Promise<NextResponse<HttpResponseDto<CreateVaultDataDto>>> {
7283
const params: CreateVaultRequestDto = await request.json();
7384
const createVaultUseCase: CreateVaultUseCase =
7485
container.resolve(CreateVaultUseCase);
75-
return await handleApiRequest(async () => {
86+
return await handleApiRequest<CreateVaultDataDto>(async () => {
7687
const vaultCreated: VaultModelDto = await createVaultUseCase.handle(params);
77-
const response: CreateVaultResponseDto = { vaultCreated };
88+
const response: CreateVaultDataDto = { vaultCreated };
7889
return response;
7990
}, StatusCodes.CREATED);
8091
}

src/app/ui/workspace/page.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import ErrorMessage from '@ui/components/common/ErrorMessage';
88
import CircularLoader from '@ui/components/common/CircularLoader';
99
import { VaultsGateway } from '@ui/gateways/vaults.gateway';
1010
import { container } from 'tsyringe';
11-
import type { GetMyVaultsResponseDto } from '@shared/dto/responses/get-my-vaults.response.dto';
1211
import { useApi } from '@ui/hooks/useApi';
1312
import {
1413
Box,
@@ -23,6 +22,8 @@ import {
2322
Typography,
2423
} from '@mui/material';
2524
import AddVaultModal from '@ui/components/modals/AddVaultModal';
25+
import type { GetMyVaultsDataDto } from '@shared/dto/output/data/get-my-vaults.data.dto';
26+
import { UiLogger } from '@ui/logs/ui.logger';
2627

2728
export default function WorkspacePage(): JSX.Element {
2829
const [vaults, setVaults] = useState<VaultModelDto[]>([]);
@@ -31,7 +32,7 @@ export default function WorkspacePage(): JSX.Element {
3132
const [deleteLoading, setDeleteLoading] = useState(false);
3233
const vaultsGateway: VaultsGateway = container.resolve(VaultsGateway);
3334

34-
const { loading } = useApi<GetMyVaultsResponseDto>({
35+
const { loading } = useApi<GetMyVaultsDataDto>({
3536
request: () => vaultsGateway.getMyVaults(),
3637
onSuccess: data => setVaults(data.myVaults),
3738
onError: error => setError(error),
@@ -53,7 +54,7 @@ export default function WorkspacePage(): JSX.Element {
5354
await vaultsGateway.deleteVault(id);
5455
} catch (error) {
5556
if (error instanceof Error) setError(error);
56-
else console.error('Unhandled API error:', error);
57+
else UiLogger.error('Unhandled API error: ', error);
5758
} finally {
5859
setDeleteLoading(false);
5960
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { HttpError } from '@api/errors/abstract/http-error';
2+
import { StatusCodes } from 'http-status-codes';
3+
4+
export class InternalServerError extends HttpError {
5+
public constructor() {
6+
super('Internal Server Error', StatusCodes.INTERNAL_SERVER_ERROR);
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { HttpError } from '@api/errors/abstract/http-error';
2+
import { StatusCodes } from 'http-status-codes';
3+
4+
export class InvalidRequestDataError extends HttpError {
5+
public constructor() {
6+
super('Invalid request data', StatusCodes.BAD_REQUEST);
7+
}
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { HttpError } from '@api/errors/abstract/http-error';
2+
import { StatusCodes } from 'http-status-codes';
3+
4+
export class RequestedValueTooLongError extends HttpError {
5+
public constructor() {
6+
super('One of the requested values is too long', StatusCodes.BAD_REQUEST);
7+
}
8+
}

0 commit comments

Comments
 (0)