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

@@ -1,2 +1,3 @@
export { UserRoutes } from './user.controller'
export { ProjectRoutes } from './project.controller'
export { routes as UserRoutes } from "./user.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 { ProjectService } from "../service/project.service";
import {
CreateResponse, DeleteResponse, IndexResponse,
UpdateResponse
CreateResponse,
DeleteResponse,
IndexResponse,
UpdateResponse,
} from "./typings/responses";
const router = Router();
export const ProjectRoutes = router;
export const routes = Router();
export const apiNamespace = "/projects";
router.get<typeof apiNamespace, IndexResponse<ProjectDto>>(
routes.get<typeof apiNamespace, IndexResponse<ProjectDto>>(
apiNamespace,
async (req, res) => {
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>(
apiNamespace,
(req, res) => {
const { name } = req.body;
routes.post<
typeof apiNamespace,
unknown,
CreateResponse<ProjectDto>,
NewProjectDto
>(apiNamespace, (req, res) => {
const { name } = req.body;
ProjectService.create(req.userId, {
name,
ProjectService.create(req.userId, {
name,
})
.then((project) => {
res.json({
data: {
id: project.id,
name: project.name,
},
});
})
.then((project) => {
res.json({
data: {
id: project.id,
name: project.name,
},
});
})
.catch((err) => {
res.status(422).json({
errors: [err.message],
});
.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`;
router.put<typeof putPath, { taskId: string }, UpdateResponse<ProjectDto>, UpdateProjectDto>(
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>(
const deletePath = `${apiNamespace}/:id`;
routes.delete<typeof deletePath, { id: string }, DeleteResponse>(
deletePath,
async (req, res) => {
const { taskId } = req.params;
const projectId = parseInt(taskId);
const { id } = req.params;
const projectId = parseInt(id);
const projecToBeDeleted = await ProjectService.findProjectFromUserById(
req.userId,

View File

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

View File

@@ -1,72 +1,91 @@
import { Router } from "express";
import { UserDto } from "../dto/user.dto";
import { AuthService } from "../service/auth.service";
import { UserService } from "../service/user.service";
import { CreateResponse, DeleteResponse } from "./typings/responses";
const router = Router();
export const UserRoutes = router;
export const routes = Router();
export const apiNamespace = "/users";
router.post(apiNamespace, (req, res) => {
const { email, password } = req.body;
routes.post<typeof apiNamespace, unknown, CreateResponse<UserDto>>(
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({
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,
});
})
.catch((err) => {
} else {
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 = {
id: number
email: string
}

View File

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

View File

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