Skip to content

Commit caae61e

Browse files
committed
fix: Generic REST API for any DB model + Base models created
1 parent 7c847c0 commit caae61e

File tree

16 files changed

+366
-55
lines changed

16 files changed

+366
-55
lines changed

.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module.exports = {
3939
"no-nested-ternary": "off",
4040
"no-console": ["warn", { allow: ["warn", "error"] }],
4141
'import/prefer-default-export': 'off',
42+
'camelcase': 'off',
4243
},
4344
settings: {
4445
node: {

client/src/api/RecordRoutes.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
1+
import { Record } from '@fullstack-typescript-monorepo/prisma';
12
import Fetch from '../utils/fetcher';
23
import Super from './Super';
34
import { User } from './UserRoutes';
45

5-
export interface Record {
6-
id: number;
7-
actionTime: string;
8-
actionType: string;
9-
objectType: string;
6+
export type RecordWithAuthor = Record & {
107
author: User;
11-
}
12-
13-
interface RecordListProps {
14-
object?: string;
15-
fetchPath: string;
16-
}
8+
};
179

1810
const RecordRoutes = {
1911
...Super<Record>('record'),
20-
list: ({
21-
object,
22-
fetchPath,
23-
}: RecordListProps) => Fetch<Record[]>('/api/record/list', {
12+
list: (object?: string) => Fetch<RecordWithAuthor[]>('/api/record/list', {
2413
object,
25-
fetchPath,
2614
}),
2715
};
2816

client/src/api/RequestRoutes.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { Request as _Request } from '@fullstack-typescript-monorepo/prisma';
12
import Super from './Super';
23

3-
export interface Request {
4-
id: number;
5-
status: 'pending' | 'success' | 'error';
6-
response: object;
7-
}
4+
// Typescript reference error hack
5+
export type Request = {
6+
[key in keyof _Request]: Request[key];
7+
};
88

99
const RequestRoutes = {
1010
...Super<Request>('request'),

client/src/api/Super.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ const Super = <Model>(model: string) => ({
3030
state,
3131
}, 'POST'),
3232
update: (id: number, data: RecursivePartial<Model>, fetchPath?: string) => Fetch<Model>(`/api/${model}/${id}`, data, 'POST', { fetchPath }),
33-
import: (data: FormData) => Fetch<never>(`/api/${model}/import`, data, 'POST'),
3433
delete: (id: number) => Fetch<never>(`/api/${model}/${id}`, {}, 'DELETE'),
35-
empty: () => Fetch<never>(`/api/${model}/empty`),
3634
});
3735

3836
export default Super;

client/src/api/UserRoutes.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,24 @@ export interface User extends Omit<_User, 'password'> {
77
}
88

99
const UserRoutes = {
10-
...Super<User>('app-user'),
11-
authenticate: (login: string, password: string): Promise<User> => Fetch<User>('/api/app-user/authenticate', {
10+
...Super<User>('user'),
11+
authenticate: (login: string, password: string): Promise<User> => Fetch<User>('/api/user/authenticate', {
1212
login,
1313
password,
1414
}, 'POST'),
15-
changePassword: (id: number, password: string) => Fetch(`/api/app-user/${id}/change-password`, { password }, 'POST'),
15+
changePassword: (id: number, password: string) => Fetch(`/api/user/${id}/change-password`, { password }, 'POST'),
1616
sendPasswordResetMail: (
1717
login: string,
18-
) => Fetch(`/api/app-user/${login}/send-password-reset-mail`),
18+
) => Fetch(`/api/user/${login}/send-password-reset-mail`),
1919
checkResetCodeValidity: (
2020
login: string,
2121
code: string,
22-
) => Fetch<boolean>(`/api/app-user/${login}/reset-code-check`, { code }),
22+
) => Fetch<boolean>(`/api/user/${login}/reset-code-check`, { code }),
2323
resetPassword: (
2424
login: string,
2525
code: string,
2626
password: string,
27-
) => Fetch('/api/app-user/reset-password', { login, code, password }, 'POST'),
27+
) => Fetch('/api/user/reset-password', { login, code, password }, 'POST'),
2828
};
2929

3030
export default UserRoutes;

client/src/components/ActionsTable.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
22
import moment from 'moment';
3-
import React, { useEffect, useMemo } from 'react';
3+
import React, { useEffect } from 'react';
44
import { useTranslation } from 'react-i18next';
55
import RecordRoutes from '../api/RecordRoutes';
66
import useStateAsync from '../hooks/useStateAsync';
@@ -18,15 +18,10 @@ const ActionsTable = ({
1818
}: ActionsTableProps) => {
1919
const { t } = useTranslation('actions');
2020

21-
const recordsFetchProps = useMemo(() => ({
22-
object,
23-
fetchPath: '(id, actionTime, actionType, objectType, author(person(firstName, lastName)))',
24-
}), [object]);
25-
2621
const { data: records, reload: reloadRecords } = useStateAsync(
2722
[],
2823
RecordRoutes.list,
29-
recordsFetchProps,
24+
object,
3025
);
3126

3227
// Reload table when new record is added and/or when reloadActions is updated
@@ -67,10 +62,10 @@ const ActionsTable = ({
6762
<TableCell component="th" scope="row">{t('anonymousAuthor')}</TableCell>
6863
)}
6964
{!object && (
70-
<TableCell align="right">{action.objectType}</TableCell>
65+
<TableCell align="right">{action.object}</TableCell>
7166
)}
72-
<TableCell align="right">{action.actionType}</TableCell>
73-
<TableCell align="right">{moment(action.actionTime).format('DD/MM/YYYY HH:mm')}</TableCell>
67+
<TableCell align="right">{action.action}</TableCell>
68+
<TableCell align="right">{moment(action.date).format('DD/MM/YYYY HH:mm')}</TableCell>
7469
</TableRow>
7570
))
7671
) : (

client/src/utils/longRequest.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { RequestStatus } from '@fullstack-typescript-monorepo/prisma';
12
import RequestRoutes from '../api/RequestRoutes';
23

34
const longRequest = async (
@@ -12,15 +13,15 @@ const longRequest = async (
1213
const promise = new Promise((resolve, reject) => {
1314
const interval = setInterval(() => {
1415
RequestRoutes.get({ id: requestId, fetchPath: '(status, response)' }).then((req) => {
15-
if (req.status === 'success') {
16+
if (req.status === RequestStatus.SUCCESS) {
1617
resolve(req.response);
1718
clearInterval(interval);
1819

1920
// Delete request
2021
RequestRoutes.delete(requestId).catch(() => {
2122
console.error('Error while deleting request', requestId);
2223
});
23-
} else if (req.status === 'error') {
24+
} else if (req.status === RequestStatus.ERROR) {
2425
reject(req.response);
2526
clearInterval(interval);
2627

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
-- CreateEnum
2+
CREATE TYPE "RequestStatus" AS ENUM ('PENDING', 'SUCCESS', 'ERROR');
3+
4+
-- CreateEnum
5+
CREATE TYPE "RecordAction" AS ENUM ('CREATE', 'UPDATE', 'DELETE');
6+
7+
-- CreateTable
8+
CREATE TABLE "Request" (
9+
"id" SERIAL NOT NULL,
10+
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
11+
"status" "RequestStatus" NOT NULL,
12+
13+
CONSTRAINT "Request_pkey" PRIMARY KEY ("id")
14+
);
15+
16+
-- CreateTable
17+
CREATE TABLE "Record" (
18+
"id" SERIAL NOT NULL,
19+
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
20+
"action" "RecordAction" NOT NULL,
21+
"object" VARCHAR(255) NOT NULL,
22+
"newValue" VARCHAR(255) NOT NULL,
23+
"authorId" INTEGER NOT NULL,
24+
25+
CONSTRAINT "Record_pkey" PRIMARY KEY ("id")
26+
);
27+
28+
-- AddForeignKey
29+
ALTER TABLE "Record" ADD CONSTRAINT "Record_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
Warnings:
3+
4+
- Added the required column `response` to the `Request` table without a default value. This is not possible if the table is not empty.
5+
6+
*/
7+
-- AlterTable
8+
ALTER TABLE "Request" ADD COLUMN "response" JSONB NOT NULL;

server/prisma/schema.prisma

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,42 @@ model Person {
3535
}
3636

3737
model User {
38-
id Int @id @default(autoincrement())
39-
login String @unique @db.VarChar(255)
40-
admin Boolean @default(false)
38+
id Int @id @default(autoincrement())
39+
login String @unique @db.VarChar(255)
40+
admin Boolean @default(false)
4141
password String? @db.VarChar(255)
42-
active Boolean @default(true)
43-
connexionToken String @default("") @db.VarChar(255)
44-
person Person @relation(fields: [personId], references: [id])
45-
personId Int @unique
42+
active Boolean @default(true)
43+
connexionToken String @default("") @db.VarChar(255)
44+
person Person @relation(fields: [personId], references: [id])
45+
personId Int @unique
46+
records Record[]
47+
}
48+
49+
enum RequestStatus {
50+
PENDING
51+
SUCCESS
52+
ERROR
53+
}
54+
55+
model Request {
56+
id Int @id @default(autoincrement())
57+
date DateTime @default(now())
58+
status RequestStatus
59+
response Json
60+
}
61+
62+
enum RecordAction {
63+
CREATE
64+
UPDATE
65+
DELETE
66+
}
67+
68+
model Record {
69+
id Int @id @default(autoincrement())
70+
date DateTime @default(now())
71+
action RecordAction
72+
object String @db.VarChar(255)
73+
newValue String @db.VarChar(255)
74+
author User @relation(fields: [authorId], references: [id])
75+
authorId Int
4676
}

0 commit comments

Comments
 (0)