improve typing and responses

This commit is contained in:
João Geonizeli
2022-07-10 15:13:40 -03:00
parent e3729ae4c0
commit acaba0f9ca
10 changed files with 289 additions and 248 deletions

View File

@@ -31,8 +31,8 @@ export const NewAccount = () => {
}) })
.then(async (res) => { .then(async (res) => {
const result = await res.json(); const result = await res.json();
if (result.error) { if (result.errors) {
setError(result.error); setError(result.errors[0]);
} else { } else {
login(data.email, data.password); login(data.email, data.password);
} }

View File

@@ -47,7 +47,7 @@ export const AuthProvider = ({ children, ...rest }: AuthProviderProps) => {
}), }),
}).then(async (res) => { }).then(async (res) => {
if (res.status === 200) { if (res.status === 200) {
setToken((await res.json()).token); setToken((await res.json()).data.token);
setIsLoginDialogOpen(false); setIsLoginDialogOpen(false);
} }
}); });

View File

@@ -1,2 +1,3 @@
export { UserRoutes } from './user.controller' export { routes as UserRoutes } from "./user.controller";
export { ProjectRoutes } from './project.controller' export { routes as ProjectRoutes } from "./project.controller";
export { routes as TaskRoutes } from "./task.controller";

View File

@@ -4,16 +4,17 @@ import { NewProjectDto } from "../dto/project.new.dto";
import { UpdateProjectDto } from "../dto/project.update.dto"; import { UpdateProjectDto } from "../dto/project.update.dto";
import { ProjectService } from "../service/project.service"; import { ProjectService } from "../service/project.service";
import { import {
CreateResponse, DeleteResponse, IndexResponse, CreateResponse,
UpdateResponse DeleteResponse,
IndexResponse,
UpdateResponse,
} from "./typings/responses"; } from "./typings/responses";
const router = Router(); export const routes = Router();
export const ProjectRoutes = router;
export const apiNamespace = "/projects"; export const apiNamespace = "/projects";
router.get<typeof apiNamespace, IndexResponse<ProjectDto>>( routes.get<typeof apiNamespace, IndexResponse<ProjectDto>>(
apiNamespace, apiNamespace,
async (req, res) => { async (req, res) => {
const projects = await ProjectService.listAllByUserId(req.userId); const projects = await ProjectService.listAllByUserId(req.userId);
@@ -27,67 +28,71 @@ router.get<typeof apiNamespace, IndexResponse<ProjectDto>>(
} }
); );
router.post<typeof apiNamespace, unknown, CreateResponse<ProjectDto>, NewProjectDto>( routes.post<
apiNamespace, typeof apiNamespace,
(req, res) => { unknown,
const { name } = req.body; CreateResponse<ProjectDto>,
NewProjectDto
>(apiNamespace, (req, res) => {
const { name } = req.body;
ProjectService.create(req.userId, { ProjectService.create(req.userId, {
name, name,
})
.then((project) => {
res.json({
data: {
id: project.id,
name: project.name,
},
});
}) })
.then((project) => { .catch((err) => {
res.json({ res.status(422).json({
data: { errors: [err.message],
id: project.id,
name: project.name,
},
});
})
.catch((err) => {
res.status(422).json({
errors: [err.message],
});
}); });
});
});
const putPath = `${apiNamespace}/:id`;
routes.put<
typeof putPath,
{ id: string },
UpdateResponse<ProjectDto>,
UpdateProjectDto
>(putPath, async (req, res) => {
const { id } = req.params;
const { name } = req.body;
const projectId = parseInt(id);
const projectToBeUpdated = await ProjectService.findProjectFromUserById(
req.userId,
projectId
);
if (projectToBeUpdated) {
ProjectService.update(projectToBeUpdated, {
name,
}).then((success) => {
if (success) {
res.status(204).json();
} else {
res.status(402).json();
}
});
} else {
res.status(404).json({
errors: ["Project not found"],
});
} }
); });
const putPath = `${apiNamespace}/:taskId`; const deletePath = `${apiNamespace}/:id`;
router.put<typeof putPath, { taskId: string }, UpdateResponse<ProjectDto>, UpdateProjectDto>( routes.delete<typeof deletePath, { id: string }, DeleteResponse>(
putPath,
async (req, res) => {
const { taskId } = req.params;
const { name } = req.body;
const projectId = parseInt(taskId);
const projectToBeUpdated = await ProjectService.findProjectFromUserById(
req.userId,
projectId
);
if (projectToBeUpdated) {
ProjectService.update(projectToBeUpdated, {
name,
}).then((success) => {
if (success) {
res.status(204).json();
} else {
res.status(402).json();
}
});
} else {
res.status(404).json({
errors: ["Project not found"],
});
}
}
);
const deletePath = `${apiNamespace}/:taskId`;
router.delete<typeof deletePath, { taskId: string }, DeleteResponse>(
deletePath, deletePath,
async (req, res) => { async (req, res) => {
const { taskId } = req.params; const { id } = req.params;
const projectId = parseInt(taskId); const projectId = parseInt(id);
const projecToBeDeleted = await ProjectService.findProjectFromUserById( const projecToBeDeleted = await ProjectService.findProjectFromUserById(
req.userId, req.userId,

View File

@@ -1,77 +1,90 @@
import { Router } from "express"; import { Router } from "express";
import { ProjectDto } from "../dto/poject.dto"; import { NewTaskDto } from "../dto/task.new.dto";
import { ProjectService } from "../service/project.service"; import { ProjectService } from "../service/project.service";
import { TaskService } from "../service/task.service"; import { TaskService } from "../service/task.service";
import {
CreateResponse,
DeleteResponse,
IndexResponse,
ShowResponse,
UpdateResponse,
} from "./typings/responses";
const router = Router(); export const routes = Router();
export const TaskRoutes = router;
export const apiNamespace = "/projects/:projectId"; export const apiNamespace = "/projects/:projectId/tasks";
router.get(`${apiNamespace}/tasks`, async (req, res) => { routes.get<typeof apiNamespace, { projectId: string }, IndexResponse<TaskDto>>(
apiNamespace,
async (req, res) => {
const { projectId } = req.params;
const parsedProjctId = parseInt(projectId);
const project = await ProjectService.findProjectFromUserById(
req.userId,
parsedProjctId
);
if (!project) {
res.status(404).send("Not found");
} else {
const tasks = await TaskService.findByProjectId(parsedProjctId);
res.json({
data: tasks.map((task) => ({
id: task.id,
description: task.description,
createdAt: task.createdAt,
finishedAt: task.finishedAt,
})),
});
}
}
);
routes.post<
typeof apiNamespace,
{ projectId: string },
CreateResponse<TaskDto>,
NewTaskDto
>(apiNamespace, async (req, res) => {
let { projectId } = req.params; let { projectId } = req.params;
const parsedProjctId = parseInt(projectId);
const project = await ProjectService.findProjectFromUserById( const project = await ProjectService.findProjectFromUserById(
req.userId, req.userId,
parsedProjctId parseInt(projectId)
); );
if (!project) { if (!project) {
res.status(404).json({ res.status(404).send("Not found");
error: "Project not found", } else {
const task = await TaskService.create(project, {
description: req.body.description,
});
res.json({
data: {
id: task.id,
createdAt: task.createdAt,
description: task.description,
finishedAt: task.finishedAt,
},
}); });
return;
} }
const tasks: TaskDto[] = (
await TaskService.findByProjectId(parsedProjctId)
).map((task) => ({
id: task.id,
description: task.description,
createdAt: task.createdAt,
finishedAt: task.finishedAt,
}));
res.json({
data: tasks,
});
}); });
router.post(`${apiNamespace}/tasks`, async (req, res) => { const puthPath = `${apiNamespace}/:taskId`;
let { projectId } = req.params; routes.put<
const parsedProjctId = parseInt(projectId); typeof puthPath,
{
const project = await ProjectService.findProjectFromUserById( projectId: string;
req.userId, taskId: string;
parsedProjctId },
); UpdateResponse<TaskDto>,
UpdateTaskDto
if (!project) { >(puthPath, async (req, res) => {
res.status(404).json(); const { projectId, taskId } = req.params;
return;
}
const task = await TaskService.create(project, {
description: req.body.description,
});
const taskDto: TaskDto = {
id: task.id,
createdAt: task.createdAt,
description: task.description,
finishedAt: task.finishedAt,
};
res.json({
data: taskDto,
});
});
router.put(`${apiNamespace}/tasks/:id`, async (req, res) => {
let { projectId, id } = req.params;
const { description }: UpdateTaskDto = req.body; const { description }: UpdateTaskDto = req.body;
const parsedProjctId = parseInt(projectId); const parsedProjctId = parseInt(projectId);
const project = await ProjectService.findProjectFromUserById( const project = await ProjectService.findProjectFromUserById(
@@ -80,31 +93,36 @@ router.put(`${apiNamespace}/tasks/:id`, async (req, res) => {
); );
if (!project) { if (!project) {
res.status(404).json(); res.status(404).send("Not found");
return;
} }
const task = await TaskService.findByProjectIdAndTaskId( const task = await TaskService.findByProjectIdAndTaskId(
parsedProjctId, parsedProjctId,
parseInt(id) parseInt(taskId)
); );
await TaskService.update(task, { description }); await TaskService.update(task, { description });
const taskDto: TaskDto = {
id: task.id,
createdAt: task.createdAt,
description: task.description,
finishedAt: task.finishedAt,
};
res.json({ res.json({
data: taskDto, data: {
id: task.id,
createdAt: task.createdAt,
description: task.description,
finishedAt: task.finishedAt,
},
}); });
}); });
router.delete(`${apiNamespace}/tasks/:id`, async (req, res) => { const deletePath = `${apiNamespace}/:taskId`;
let { projectId, id } = req.params; routes.delete<
typeof deletePath,
{
projectId: string;
taskId: string;
},
DeleteResponse
>(deletePath, async (req, res) => {
let { projectId, taskId } = req.params;
const parsedProjctId = parseInt(projectId); const parsedProjctId = parseInt(projectId);
const project = await ProjectService.findProjectFromUserById( const project = await ProjectService.findProjectFromUserById(
@@ -112,32 +130,32 @@ router.delete(`${apiNamespace}/tasks/:id`, async (req, res) => {
parsedProjctId parsedProjctId
); );
if (!project) {
res.status(404).json();
return;
}
const task = await TaskService.findByProjectIdAndTaskId( const task = await TaskService.findByProjectIdAndTaskId(
parsedProjctId, parsedProjctId,
parseInt(id) parseInt(taskId)
); );
if (!task) { if (!project || !task) {
res.status(404).json(); res.status(404).send("Not found");
return;
}
const success = await TaskService.destroy(task);
if (success) {
res.status(204).json();
} else { } else {
res.status(504).json(); const success = await TaskService.destroy(task);
res.status(success ? 204 : 505).json({
success,
});
} }
}); });
router.get(`${apiNamespace}/tasks/:id/finish`, async (req, res) => { const finishPath = `${apiNamespace}/:taskId/finish`;
const { projectId, id } = req.params; routes.get<
typeof finishPath,
{
projectId: string;
taskId: string;
},
ShowResponse<TaskDto>
>(finishPath, async (req, res) => {
const { projectId, taskId } = req.params;
const parsedProjctId = parseInt(projectId); const parsedProjctId = parseInt(projectId);
const project = await ProjectService.findProjectFromUserById( const project = await ProjectService.findProjectFromUserById(
@@ -145,27 +163,23 @@ router.get(`${apiNamespace}/tasks/:id/finish`, async (req, res) => {
parsedProjctId parsedProjctId
); );
const task = await TaskService.findByProjectIdAndTaskId( const task = await TaskService.findByProjectIdAndTaskId(
parsedProjctId, parsedProjctId,
parseInt(id) parseInt(taskId)
); );
if (!project || !task) { if (!project || !task) {
res.status(404).json(); res.status(404).json();
return; } else {
await TaskService.finish(task);
res.json({
data: {
id: task.id,
createdAt: task.createdAt,
description: task.description,
finishedAt: task.finishedAt,
},
});
} }
const result = await TaskService.finish(task);
const resultDto: TaskDto = {
id: result.id,
createdAt: result.createdAt,
description: result.description,
finishedAt: result.finishedAt,
};
res.json({
data: resultDto,
});
}); });

View File

@@ -1,10 +1,14 @@
export type ErrorResponse = { export type ErrorResponse = {
errors: string[]; errors: string[];
}; } | string;
export type IndexResponse<TData> = { export type IndexResponse<TData> = {
data: TData[]; data: TData[];
}; } | string;
export type ShowResponse<TData> = {
data: TData;
}
export type CreateResponse<TData> = export type CreateResponse<TData> =
| { | {

View File

@@ -1,72 +1,91 @@
import { Router } from "express"; import { Router } from "express";
import { UserDto } from "../dto/user.dto";
import { AuthService } from "../service/auth.service"; import { AuthService } from "../service/auth.service";
import { UserService } from "../service/user.service"; import { UserService } from "../service/user.service";
import { CreateResponse, DeleteResponse } from "./typings/responses";
const router = Router(); export const routes = Router();
export const UserRoutes = router;
export const apiNamespace = "/users"; export const apiNamespace = "/users";
router.post(apiNamespace, (req, res) => { routes.post<typeof apiNamespace, unknown, CreateResponse<UserDto>>(
const { email, password } = req.body; apiNamespace,
(req, res) => {
const { email, password } = req.body;
UserService.create({
email,
password,
})
.then((user) => {
res.json({
data: {
email: user.email,
},
});
})
.catch((err) => {
res.status(422).json({
errors: [err.message],
});
});
}
);
const signInPath = `${apiNamespace}/sign_in`;
routes.post<typeof signInPath, unknown, CreateResponse<SessionDto>>(
signInPath,
async (req, res) => {
const { email, password } = req.body;
const user = await UserService.findByEmail(email);
if (!user) {
res.status(500).json({
errors: ["Invalid credentails"],
});
return;
}
const isPasswordValid = await AuthService.isUserPasswordValid(
user,
password
);
if (isPasswordValid) {
const token = await AuthService.createSession(user);
UserService.create({
email,
password,
})
.then(() => {
res.json({ res.json({
data: {
token,
},
});
} else {
res.status(500).json({
data: {
token: null,
},
});
}
}
);
const signOutPath = `${apiNamespace}/sign_out`;
routes.delete<typeof signOutPath, unknown, DeleteResponse>(
signOutPath,
async (req, res) => {
const token = req.headers["x-access-token"];
if (typeof token === "string") {
await AuthService.destoySession(token);
res.status(204).json({
success: true, success: true,
}); });
}) } else {
.catch((err) => {
res.status(422).json({ res.status(422).json({
error: err.message, success: false,
}); });
}); }
});
router.post(`${apiNamespace}/sign_in`, async (req, res) => {
const { email, password } = req.body;
const user = await UserService.findByEmail(email);
if (!user) {
res.status(500).json({
error: "Invalid credentails",
});
return;
} }
);
const isPasswordValid = await AuthService.isUserPasswordValid(user, password);
if (isPasswordValid) {
const token = await AuthService.createSession(user);
res.json({
auth: true,
token,
});
} else {
res.status(500).json({
auth: false,
token: null,
});
}
});
router.delete(`${apiNamespace}/sign_out`, async (req, res) => {
const token = req.headers["x-access-token"];
if (typeof token === "string") {
await AuthService.destoySession(token);
res.status(204).json({
success: true,
});
} else {
res.status(422).json({
success: false,
});
}
});

View File

@@ -1,4 +1,3 @@
export type UserDto = { export type UserDto = {
id: number
email: string email: string
} }

View File

@@ -1,3 +1,3 @@
type SessionDto = { type SessionDto = {
userEmail: string; token: string | null;
} };

View File

@@ -1,9 +1,8 @@
import * as cors from 'cors'; import * as cors from "cors";
import * as dotenv from 'dotenv'; import * as dotenv from "dotenv";
import * as express from 'express'; import * as express from "express";
import "reflect-metadata"; import "reflect-metadata";
import { ProjectRoutes, UserRoutes } from "./controller"; import { ProjectRoutes, UserRoutes, TaskRoutes } from "./controller";
import { TaskRoutes } from "./controller/task.controller";
import { AppDataSource } from "./infra/dataSource"; import { AppDataSource } from "./infra/dataSource";
import { RedisConnection } from "./infra/redis"; import { RedisConnection } from "./infra/redis";
import { sessionMiddleware } from "./middleware/session.middleware"; import { sessionMiddleware } from "./middleware/session.middleware";
@@ -12,28 +11,28 @@ dotenv.config();
const app = express(); const app = express();
app.use(cors()) app.use(cors());
app.use(express.json()); app.use(express.json());
app.use(sessionMiddleware) app.use(sessionMiddleware);
app.use(UserRoutes) app.use(UserRoutes);
app.use(ProjectRoutes) app.use(ProjectRoutes);
app.use(TaskRoutes) app.use(TaskRoutes);
const startApp = async () => { const startApp = async () => {
console.log('[redis]: connecting') console.log("[redis]: connecting");
await RedisConnection.connect() await RedisConnection.connect();
console.log('[redis]: connected') console.log("[redis]: connected");
console.log('[database]: connecting') console.log("[database]: connecting");
await AppDataSource.initialize() await AppDataSource.initialize();
console.log('[database]: connected') console.log("[database]: connected");
const port = process.env.PORT; const port = process.env.PORT;
app.listen(port, () => { app.listen(port, () => {
console.log(`[server] is running at ${port} 🚀`); console.log(`[server] is running at ${port} 🚀`);
}); });
} };
startApp() startApp();