Skip to content

Feat: Finalizado#59

Open
Rodrigofroes wants to merge 1 commit into
corelabbr:mainfrom
Rodrigofroes:corelab-api-challenge
Open

Feat: Finalizado#59
Rodrigofroes wants to merge 1 commit into
corelabbr:mainfrom
Rodrigofroes:corelab-api-challenge

Conversation

@Rodrigofroes

@Rodrigofroes Rodrigofroes commented Mar 24, 2025

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive task management API with endpoints for creating, retrieving, updating, deleting, and listing favorite tasks.
    • Added Docker support with a multi-container setup for the application and database.
  • Documentation

    • Updated the README to reflect the new API focus, architecture, and deployment instructions.
  • Tests

    • Implemented end-to-end tests covering the complete lifecycle of task operations.
  • Chores

    • Migrated from the previous framework by removing legacy configurations and streamlining dependency management.

@coderabbitai

coderabbitai Bot commented Mar 24, 2025

Copy link
Copy Markdown

Walkthrough

This update removes numerous AdonisJS-specific configuration files, controllers, contracts, and middleware while transitioning the project to a Node.js/TypeScript ecosystem using Express. New configuration files for Docker, Jest, TypeScript, and environment variables have been introduced. A comprehensive task management API is implemented with new Express routes, infrastructure components, use case classes, repositories, domain entities, and database integration. Legacy test files have been replaced by new integration tests using supertest.

Changes

File(s) Change Summary
.adonisrc.json, ace, ace-manifest.json, app/Controllers/VehiclesController.ts, app/Exceptions/Handler.ts, app/Types/Vehicle.ts, commands/index.ts, config/{app,bodyparser,cors,drive,hash}.ts, contracts/{drive,env,events,hash,tests}.ts, start/{kernel,routes}.ts, server.ts, test.ts, tests/{bootstrap,functional/vehicles-api.spec.ts}, Leiame.md, providers/AppProvider.ts Removed legacy AdonisJS configurations, controllers, contracts, routes, middleware, and test setups.
.editorconfig, .gitignore, .gitignore copy, .prettierignore Removed or simplified editor configuration and ignore files.
.env.example, package.json, tsconfig.json, jest.config.js Updated environment variables, dependency configuration, and TypeScript/Jest settings.
Dockerfile, docker-compose.yml Introduced Dockerfile for Node.js and docker-compose for MySQL and app services.
src/Infrastructures/api.ts, src/Infrastructures/express/api.express.ts, src/Infrastructures/express/createApp.ts, src/Infrastructures/express/routes/*, src/Infrastructures/package/banco.ts, src/Infrastructures/repositories/taks/tasks.infrastructure.repository.ts, src/db/database.ts, src/domains/entities/task.entity.ts, src/domains/gateway/task.gateway.ts, src/index.ts, src/usecases/{task/*,usecase.ts} Added Express API implementation and complete task management feature including routes, infrastructure, repository, database operations, domain entities, and use cases.
tests/test.ts Added new integration tests for the task management API using supertest.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant E as APIExpress
    participant R as Route Handler
    participant U as CreateTaskUsecase
    participant T as TasksRepository
    participant D as Database

    C->>E: POST /task (payload)
    E->>R: Invoke getHandler()
    R->>U: execute(input)
    U->>U: validate & create TaskEntity
    U->>T: createTask(task)
    T->>D: ExecutaComandoLastInserted(sql, params)
    D-->>T: New Task ID
    T-->>U: Confirmation with Task ID
    U-->>R: Return output DTO
    R-->>E: Response with 201 Created
    E-->>C: 201 Created (task id)
Loading

Poem

Hoppin’ through the code so bright,
I leave old paths in the night.
New routes bloom with each fresh line,
Tasks and tests now all align.
My whiskers twitch with pure delight 🐰
As Docker and Jest join the fight.
CodeRabbit cheers: “Full speed ahead!”

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Rodrigofroes

Copy link
Copy Markdown
Author

Finalizado

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 28

🧹 Nitpick comments (60)
.gitignore (1)

1-3: Simplified .gitignore for Node.js project structure.

Your .gitignore has been simplified to focus only on essential patterns. While this works, consider adding additional common patterns for a Node.js/TypeScript project.

Consider adding these common patterns:

 .env

 /node_modules
+# Build outputs
+/dist
+/build
+
+# Logs
+*.log
+npm-debug.log*
+
+# Coverage directory
+/coverage
+
+# IDE files
+.idea/
+.vscode/
+
+# OS files
+.DS_Store
src/Infrastructures/package/banco.ts (1)

1-3: Consider using dependency injection instead of a global singleton.

Using a global singleton for database access makes the code harder to test and tightly couples components to this implementation.

A more flexible approach would be:

-import { Database } from "../../db/database";
-
-export const banco = new Database();
+import { Database } from "../../db/database";
+
+export const createDatabaseConnection = (): Database => {
+  return new Database();
+};
+
+// For backward compatibility
+export const banco = createDatabaseConnection();

This allows for easier testing by injecting mock database instances where needed.

src/usecases/usecase.ts (1)

2-2: Minor formatting issue in the execute method signature.

There's an unnecessary space after the colon in the method declaration.

-    execute:(input: InputDto) => Promise<OutputDto>;
+    execute: (input: InputDto) => Promise<OutputDto>;
src/Infrastructures/express/routes/task/index.express.route.ts (1)

4-4: Fix inconsistent file naming convention.

There's an inconsistency in the naming pattern: GetTasks.express.route.ts uses PascalCase while other files use camelCase. This inconsistency can make it harder to navigate the codebase.

-export * from './GetTasks.express.route';
+export * from './getTasks.express.route';
.vscode/launch.json (2)

15-15: Configure a more generic entry point for debugging.

The launch configuration points to a specific route file rather than the main application entry point. This makes it less convenient for debugging the application as a whole.

-            "program": "${workspaceFolder}\\src\\Infrastructures\\express\\routes\\task\\getTasksFavorite.express.route.ts",
+            "program": "${workspaceFolder}/src/index.ts",

15-15: Use cross-platform path separators.

The configuration uses Windows-style backslashes (\\) which can cause issues on non-Windows systems. Using forward slashes works on all platforms.

-            "program": "${workspaceFolder}\\src\\Infrastructures\\express\\routes\\task\\getTasksFavorite.express.route.ts",
+            "program": "${workspaceFolder}/src/Infrastructures/express/routes/task/getTasksFavorite.express.route.ts",
Dockerfile (1)

1-11: Add security and environment improvements to Docker configuration.

The Dockerfile follows good practices for layer caching, but could benefit from security and environment best practices.

 FROM node:20

 WORKDIR /app

 COPY package*.json ./
 RUN npm install
 COPY . .

+# Create a non-root user for better security
+RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
+USER appuser
+
+# Set NODE_ENV to production for better performance
+ENV NODE_ENV=production
+
 EXPOSE 8080

+# Add a health check to improve container monitoring
+HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
+  CMD node -e "const http = require('http'); const options = { timeout: 2000, host: 'localhost', port: 8080, path: '/health' }; const req = http.request(options, (res) => process.exit(res.statusCode === 200 ? 0 : 1)); req.on('error', () => process.exit(1)); req.end();"
+
 CMD ["npm", "run", "start"]
src/Infrastructures/express/createApp.ts (2)

2-2: Fix typo in import path.

There's a typo in the directory name - "taks" should be "tasks".

-import TasksInfrastructureRepository from '../repositories/taks/tasks.infrastructure.repository';
+import TasksInfrastructureRepository from '../repositories/tasks/tasks.infrastructure.repository';

4-9: Standardize exports across use cases.

There's inconsistency in how use cases are exported. Some use default exports while others use named exports, which leads to inconsistent import syntax.

Either standardize all as named exports:

-import DeleteTaskUsecase from '../../usecases/task/deleteTask.usecase';
-import GetTaskByIdUsecase from '../../usecases/task/getTaskById.usecase';
-import GetTasksUsecase from '../../usecases/task/getTasks.usecase';
-import UpdateTaskUsecase from '../../usecases/task/updateTask.usecase';
-import GetTasksFavoriteUsecase from '../../usecases/task/getTasksFavorite.usecase';
+import { DeleteTaskUsecase } from '../../usecases/task/deleteTask.usecase';
+import { GetTaskByIdUsecase } from '../../usecases/task/getTaskById.usecase';
+import { GetTasksUsecase } from '../../usecases/task/getTasks.usecase';
+import { UpdateTaskUsecase } from '../../usecases/task/updateTask.usecase';
+import { GetTasksFavoriteUsecase } from '../../usecases/task/getTasksFavorite.usecase';

Or standardize all as default exports:

-import { CreateTaskUsecase } from '../../usecases/task/createTask.usecase';
+import CreateTaskUsecase from '../../usecases/task/createTask.usecase';
src/index.ts (2)

25-25: Fix variable naming typo

There's a typo in the variable name getTaks which should be getTasks for consistency and clarity.

-  const getTaks = GetTasksUsecase.create(repository)
+  const getTasks = GetTasksUsecase.create(repository)

Remember to update the variable usage on line 32 as well.


36-38: Consider more structured route registration

The current approach passes all routes directly to ApiExpress.create(), which works but isn't flexible for future route additions.

Consider organizing routes by functionality and supporting dynamic registration:

-  const api = ApiExpress.create([createTaskRoute, deleteTaskRoute, getTaskByIdRoute, getTasksRoute, updateTaskRoute, getTasksFavoriteRoute])
+  const api = ApiExpress.create()
+  
+  // Task routes
+  const taskRoutes = [
+    createTaskRoute,
+    deleteTaskRoute,
+    getTaskByIdRoute, 
+    getTasksRoute,
+    updateTaskRoute,
+    getTasksFavoriteRoute
+  ]
+  
+  // Register routes
+  api.registerRoutes(taskRoutes)

  api.start(process.env.PORT || 3000)

This assumes you modify the ApiExpress class to support this pattern.

src/Infrastructures/express/routes/task/updateTask.express.route.ts (1)

6-19: Consider REST resource path with ID parameter

The route path "/task" for updating a task doesn't follow REST conventions, which typically use the pattern "/task/:id" for resource-specific operations.

Since the ID is in the request body rather than URL path, consider making one of these changes:

  1. Move ID to path parameter:
public static create(updateTaskUsecase: UpdateTaskUsecase) {
    return new UpdateTaskRoute(
-        "/task",
+        "/task/:id",
        HttpMethod.PUT,
        updateTaskUsecase
    )
}

// Then update getHandler() to get id from req.params
  1. Or use PATCH instead of PUT for partial updates:
public static create(updateTaskUsecase: UpdateTaskUsecase) {
    return new UpdateTaskRoute(
        "/task",
-        HttpMethod.PUT,
+        HttpMethod.PATCH,
        updateTaskUsecase
    )
}
src/domains/gateway/task.gateway.ts (3)

3-10: Follow naming convention for parameters

The interface looks good overall, but there's an inconsistency in parameter naming.

export interface TaskGateway {
-    createTask(Task: TaskEntity): Promise<void>;
+    createTask(task: TaskEntity): Promise<void>;
    getTaskById(id: string): Promise<TaskEntity>;
    getTasks(): Promise<TaskEntity[]>;
-    updateTask(Task: TaskEntity): Promise<void>;
+    updateTask(task: TaskEntity): Promise<void>;
    deleteTask(id: string): Promise<void>;
    getTasksFavorite(): Promise<TaskEntity[]>;
}

Following JavaScript/TypeScript conventions, parameter names should use camelCase starting with lowercase.


4-4: Consider returning the created task ID

The createTask method returns Promise<void>, but it might be useful to return the created task's ID or the complete entity.

-    createTask(Task: TaskEntity): Promise<void>;
+    createTask(task: TaskEntity): Promise<string>; // Return task ID

This would allow clients to know the ID of the newly created task without having to pass it in and track it separately.


9-9: Consider consistent method naming pattern

The method getTasksFavorite doesn't follow the same pattern as other methods where the entity name comes first.

-    getTasksFavorite(): Promise<TaskEntity[]>;
+    getFavoriteTasks(): Promise<TaskEntity[]>;

This makes the API more consistent and easier to understand, especially if you add more filtered getters in the future.

tsconfig.json (2)

2-9: Enhance TypeScript configuration with additional options

The current TypeScript configuration is good but could be improved with source maps for debugging and additional type-checking options.

"compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "./dist",
-   "resolveJsonModule": true
+   "resolveJsonModule": true,
+   "sourceMap": true,
+   "forceConsistentCasingInFileNames": true,
+   "noImplicitReturns": true,
+   "baseUrl": ".",
+   "paths": {
+     "@/*": ["src/*"]
+   }
}

11-13: Include test files in TypeScript compilation

The current configuration only includes files from the src directory, which means test files aren't being type-checked.

"include": [
-   "src/**/*"
+   "src/**/*",
+   "tests/**/*"
]
src/domains/entities/task.entity.ts (1)

1-6: Consider adding validation for the color property.

The color property has no validation. Consider using a specific format (e.g., hex color codes) or an enum of valid color values.

You could enhance the type definition to be more specific about accepted color formats:

export type TaskProps = {
    id: string;
    title: string;
    is_favorite: boolean;
    color: string; // e.g., "#RRGGBB" hex format
}
src/usecases/task/updateTask.usecase.ts (3)

12-12: Replace void with undefined for better type clarity.

The static analysis tool correctly flags that void can be confusing outside of return types or type parameters.

-export type UpdateTaskOutput = void;
+export type UpdateTaskOutput = undefined;
🧰 Tools
🪛 Biome (1.9.4)

[error] 12-12: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


28-34: Use const instead of let for immutable variables.

The updatedTask variable is never reassigned, so it should use const instead of let.

-        let updatedTask = TaskEntity.restore({
+        const updatedTask = TaskEntity.restore({
            id: task.id,
            title: input.title,
            is_favorite: input.is_favorite,
            color: input.color,
        });

51-53: Use strict equality (===) when checking for undefined.

For better type safety, use strict equality (===) instead of loose equality (==) when checking for undefined.

-        if (input.is_favorite == undefined) {
+        if (input.is_favorite === undefined) {
            throw new Error('Is favorite is required');
        }
src/usecases/task/getTasks.usecase.ts (2)

3-3: Remove the unnecessary comment about adjusting the path.

The comment "// Adjust the path as needed" should be removed in the final code as it suggests uncertainty about the import path.

-import { TaskEntity } from "../../domains/entities/task.entity"; // Adjust the path as needed
+import { TaskEntity } from "../../domains/entities/task.entity";

5-10: Use PascalCase for type names.

Following TypeScript conventions, type names should use PascalCase. Rename getTasksOutput to GetTasksOutput.

-export type getTasksOutput = {
+export type GetTasksOutput = {
    id: string;
    title: string;
    is_favorite: boolean;
    color: string;
}[];

Also update all references to this type in the class implementation.

src/db/database.ts (2)

1-18: Database connection setup looks good, but consider environment variable validation

The database connection setup is well-structured using environment variables and appropriate connection pool settings.

Consider adding validation for required environment variables to prevent runtime errors if they're missing:

import { createPool, Pool } from 'mysql2';
import dotenv from 'dotenv';
dotenv.config();

+// Validate required environment variables
+const requiredEnvVars = ['DB_HOST', 'DB_USER', 'DB_PASSWORD', 'DB_NAME'];
+for (const envVar of requiredEnvVars) {
+  if (!process.env[envVar]) {
+    throw new Error(`Missing required environment variable: ${envVar}`);
+  }
+}

export class Database {
    private pool: Pool;

    constructor() {
        this.pool = createPool({
            host: process.env.DB_HOST,
            user: process.env.DB_USER,
            password: process.env.DB_PASSWORD,
            database: process.env.DB_NAME,
            waitForConnections: true,
            connectionLimit: 10,
            queueLimit: 0
        });
    }

1-58: Improve type safety and naming conventions

The current implementation uses any types extensively and mixes naming conventions (possibly Portuguese and English).

Consider the following improvements:

  1. Use more specific types instead of any
  2. Standardize method naming to follow a single language convention
  3. Add proper JSDoc comments to explain method purposes

Example of improved method signature:

/**
 * Executes a SQL query and returns the resulting rows.
 * @param sql SQL query string
 * @param parameters Parameters for the SQL query
 * @returns Query result rows
 */
public async executeQuery(sql: string, parameters: unknown[] = []): Promise<Record<string, unknown>[]> {
    const [rows] = await this.pool.promise().query(sql, parameters);
    return rows as Record<string, unknown>[];
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 37-37: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 46-46: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 55-55: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

src/Infrastructures/express/api.express.ts (1)

45-56: Avoid using internal Express property _router

The _router property is an internal/private API that might change in future Express versions.

Consider using Express's official API to get registered routes:

private listRoutes() {
    const routes: { path: string; method: string }[] = [];
    
    const expressRouter = this.app._router;
    if (expressRouter) {
        expressRouter.stack
            .filter((layer: any) => layer.route)
            .forEach((layer: any) => {
                const route = layer.route;
                const methods = Object.keys(route.methods)
                    .filter(method => route.methods[method]);
                
                routes.push({
                    path: route.path,
                    method: methods[0]
                });
            });
    }
    
    console.log(routes);
}

Also consider implementing a more structured logging approach rather than directly using console.log.

src/usecases/task/deleteTask.usecase.ts (3)

8-8: Replace void type with undefined for better clarity

Using void as a type alias outside a return type context can be confusing.

- export type DeleteTaskOutput = void;
+ export type DeleteTaskOutput = undefined;

This aligns with TypeScript best practices and makes the type definition clearer.

🧰 Tools
🪛 Biome (1.9.4)

[error] 8-8: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


17-25: Add more specific error handling for the not found case

The current implementation throws a generic error when a task is not found.

Consider creating specific error classes for domain errors:

+ // Add this to a shared errors file
+ export class NotFoundError extends Error {
+   constructor(entity: string, id: string) {
+     super(`${entity} with id ${id} not found`);
+     this.name = 'NotFoundError';
+   }
+ }

async execute(input: DeleteTaskInput): Promise<DeleteTaskOutput> {
  this.validate(input);
  const task = await this.taskGateway.getTaskById(input.id);
  if (!task) {
-    throw new Error('task not found');
+    throw new NotFoundError('Task', input.id);
  }

  return await this.taskGateway.deleteTask(input.id);
}

This provides more detailed error information and enables better error handling at the API level.


24-24: Remove unnecessary await on return statement

Since you're simply returning the result of this.taskGateway.deleteTask(input.id), there's no need to await it and then return.

- return await this.taskGateway.deleteTask(input.id);
+ return this.taskGateway.deleteTask(input.id);
src/Infrastructures/express/routes/task/GetTasks.express.route.ts (1)

23-23: Rename variable for clarity

The variable task should be named tasks since it holds multiple task objects.

- const task = await this.getTasksUsecase.execute();
+ const tasks = await this.getTasksUsecase.execute();

This improves code readability and reduces confusion.

README.md (4)

3-3: Consider adjusting the grammatical agreement in the introduction.

As per the grammar rule in Portuguese, the adjective "construída" should agree with "gerenciamento" rather than "API", so consider changing to "construído".

-Uma API RESTful para gerenciamento de tarefas construída com Node.js, TypeScript, Express e MySQL, seguindo os princípios da Clean Architecture.
+Uma API RESTful para gerenciamento de tarefas construído com Node.js, TypeScript, Express e MySQL, seguindo os princípios da Clean Architecture.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~3-~3: Possível erro de concordância. Considere alterar para “gerenciamento de tarefas construído”.
Context: ...amento de Tarefas Uma API RESTful para gerenciamento de tarefas construída com Node.js, TypeScript, Express e MySQ...

(CONCORDANCIA_NOMINAL_COMPLEXA)


16-24: Add a language specifier to the code block.

Adding a language specifier improves syntax highlighting and readability in Markdown.

-```
+```text
 src/
 ├── usecases/         # Regras de negócio da aplicação
 ├── Infrastructures/  # Frameworks e ferramentas externas
 │   ├── express/     # Configuração do Express.js
 │   ├── package/     # Pacotes externos
 │   └── repositories/# Implementações de acesso a dados
 └── db/              # Configuração do banco de dados
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

16-16: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


49-57: Add a language specifier to the environment variables code block.

Adding a language specifier improves syntax highlighting and readability in Markdown.

-```
+```env
 PORT=8080
 DB_HOST=mysql
 DB_USER=seu_usuario
 DB_PASSWORD=sua_senha
 DB_NAME=nome_do_banco
 DB_PORT=3306
 DB_ROOT_PASSWORD=senha_root
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

49-49: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


104-108: Fix the duplicate punctuation in the task structure section.

There's a punctuation issue in the list of task fields.

-Cada tarefa possui os seguintes campos:
+Cada tarefa possui os seguintes campos:

 - `id`: Identificador único
 - `title`: Título da tarefa
 - `is_favorite`: Indica se a tarefa é favorita
 - `color`: Cor associada à tarefa
🧰 Tools
🪛 LanguageTool

[uncategorized] ~104-~104: Pontuação duplicada
Context: ... Cada tarefa possui os seguintes campos: - id: Identificador único - title: Título d...

(DOUBLE_PUNCTUATION_XML)

src/Infrastructures/express/routes/task/deleteTask.express.route.ts (1)

27-29: Remove unnecessary condition check.

The DeleteTaskUsecase.execute() method will throw an error if the task is not found, so checking if task != null is redundant. Additionally, according to the relevant code snippet, the deleteTask method in TaskGateway returns void, not the deleted task.

-            if (task != null) {
                 res.status(200).send({ message: "Task deleted" });
-            }
src/usecases/task/getTasksFavorite.usecase.ts (3)

5-12: Use PascalCase for TypeScript type definitions.

TypeScript conventions typically use PascalCase for type definitions. Consider renaming the types to follow this convention.

-export type getTasksFavoriteInputDto = null;
+export type GetTasksFavoriteInputDto = null;

-export type getTasksFavoriteOutputDto = {
+export type GetTasksFavoriteOutputDto = {
    id: string,
    title: string,
    is_favorite: boolean,
    color: string,
}[]

14-14: Update the interface implementation to use the revised type names.

If you change the type names to PascalCase as suggested, update the class implementation to match.

-export default class GetTasksFavoriteUsecase implements Usecase<getTasksFavoriteInputDto, getTasksFavoriteOutputDto> {
+export default class GetTasksFavoriteUsecase implements Usecase<GetTasksFavoriteInputDto, GetTasksFavoriteOutputDto> {

26-35: Update the return type in the mapToOutput method.

If you change the type name to PascalCase as suggested, update the method signature to match.

-private mapToOutput(tasks: TaskEntity[]): getTasksFavoriteOutputDto {
+private mapToOutput(tasks: TaskEntity[]): GetTasksFavoriteOutputDto {
    return tasks.map((task) => {
        return {
            id: task.id,
            title: task.title,
            is_favorite: task.is_favorite,
            color: task.color,
        };
    });
}
src/Infrastructures/repositories/taks/tasks.infrastructure.repository.ts (4)

1-1: There's a typo in the directory path.

The directory name is spelled "taks" instead of "tasks". You should rename the directory to correct this typo.

Path should be: src/Infrastructures/repositories/tasks/tasks.infrastructure.repository.ts

5-9: Rename the variable to improve clarity.

The variable name banco should be renamed to database for better clarity, especially since the class is being written in English.

-private constructor(private readonly banco: Database) { }
+private constructor(private readonly database: Database) { }

public static create(banco: Database): TasksInfrastructureRepository {
-    return new TasksInfrastructureRepository(banco);
+    return new TasksInfrastructureRepository(database);
}

11-14: Add return type annotation and update method implementation.

The getTasks method is missing a return type annotation and should use the renamed variable if you make that change.

-async getTasks() {
+async getTasks(): Promise<any[]> {
    let sql = `SELECT * FROM tasks ORDER BY is_favorite DESC`;
-    return await this.banco.ExecutaComando(sql);
+    return await this.database.ExecutaComando(sql);
}

37-40: Add proper return type to getTasksFavorite.

The getTasksFavorite method is missing a return type annotation.

-async getTasksFavorite() {
+async getTasksFavorite(): Promise<any[]> {
    let sql = `SELECT * FROM tasks WHERE is_favorite = 1`;
-    return await this.banco.ExecutaComando(sql);
+    return await this.database.ExecutaComando(sql);
}
src/Infrastructures/express/routes/task/getTaskById.express.route.ts (1)

21-31: Consider using consistent response formats

For better API consistency, standardize the response structure across all endpoints. Currently, the success response directly sends the task object, while other routes might use a different format.

if (task) {
-   res.status(200).send(task);
+   res.status(200).json({
+       message: "Task retrieved successfully",
+       data: task
+   });
}
src/usecases/task/getTaskById.usecase.ts (3)

26-29: Avoid throwing generic Error objects

Consider creating custom error classes for different error scenarios to improve error handling across the application.

-if (!task) {
-    throw new Error('Task not found');
-}
+if (!task) {
+    throw new TaskNotFoundError(input.id);
+}

// Then define this class elsewhere in your application:
// export class TaskNotFoundError extends Error {
//   constructor(taskId: string) {
//     super(`Task with id ${taskId} not found`);
//     this.name = 'TaskNotFoundError';
//   }
// }

40-47: Consider adding type validation for the Task entity properties

The mapToOutput method assumes that all properties from the task entity will be valid, but doesn't validate types or handle missing values.

While the current implementation relies on the TaskEntity getters providing the correct types, consider adding validation or null checks if these properties could potentially be undefined or of incorrect types in some edge cases.


34-38: Improve input validation

The current validation only checks if the ID is present but not if it's a valid format (e.g., UUID).

private validate(input: GetTaskByIdInputDto): void {
    if (!input.id) {
        throw new Error('Id is required');
    }
+   // If using UUID format
+   const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
+   if (!uuidRegex.test(input.id)) {
+       throw new Error('Invalid ID format');
+   }
}
src/Infrastructures/express/routes/task/createTask.express.route.ts (1)

29-32: Return the complete task in the response

Consider returning the complete task object in the response instead of just the ID, which would eliminate the need for an additional request to get the task details.

res.status(201).json({
    message: "Task created",
-   id: task.id
+   task: task
});
src/usecases/task/createTask.usecase.ts (3)

38-50: Enhance validation to include type checking

The validation checks only for the presence of values but not their types. Adding type validation would prevent potential runtime errors.

private validate(input: CreateTaskInputDto): void {
    if (!input.title) {
        throw new Error('Title is required');
    }
+   if (typeof input.title !== 'string') {
+       throw new Error('Title must be a string');
+   }

    if (!input.color) {
        throw new Error('Color is required');
    }
+   if (typeof input.color !== 'string') {
+       throw new Error('Color must be a string');
+   }

    if (input.is_favorite == undefined) {
        throw new Error('is_favorite is required');
    }
+   if (typeof input.is_favorite !== 'boolean') {
+       throw new Error('is_favorite must be a boolean');
+   }
}

47-49: Improve the condition for checking is_favorite

The current check using == undefined is correct but using strict equality (=== undefined) is generally preferred in TypeScript for type safety.

-if (input.is_favorite == undefined) {
+if (input.is_favorite === undefined) {
    throw new Error('is_favorite is required');
}

25-36: Consider adding input sanitization

The current implementation passes user input directly to the entity creation without sanitization. Consider trimming string inputs to prevent issues with leading/trailing whitespace.

public async execute(input: CreateTaskInputDto): Promise<CreateTaskOutputDto> {
    this.validate(input);

+   // Sanitize inputs
+   const sanitizedInput = {
+       title: input.title.trim(),
+       is_favorite: input.is_favorite,
+       color: input.color.trim()
+   };

    const task = TaskEntity.create(
-       input.title,
-       input.is_favorite,
-       input.color
+       sanitizedInput.title,
+       sanitizedInput.is_favorite,
+       sanitizedInput.color
    );
package.json (3)

4-5: Metadata Fields: Description and Main Entry
Adding a "description" field and specifying "main": "index.js" helps improve module documentation and entry point clarity. Consider updating the description with a meaningful summary once available.


7-8: Script Commands: Test and Start
The new "test": "npx jest" and "start": "ts-node-dev --respawn --transpile-only src/index.ts" commands are appropriate for a TypeScript development environment. Note that while ts-node-dev is great for development, you might need a different build/deployment strategy for production (e.g., transpiled files in a dist folder).


14-21: Project Metadata: License, Keywords, Author, and Homepage
Adding metadata like "keywords", "author", "license", "type", "bugs", and "homepage" enriches the package information. A minor suggestion: consider filling in the "author" field and adding descriptive keywords to improve discoverability.

.env.example (1)

1-7: Consider enhancing the .env.example file with documentation

The environment variables are correctly set up for the database configuration, but the file could benefit from adding comments to explain each variable's purpose and including example values to guide new developers.

-PORT=
-DB_HOST=
-DB_USER=
-DB_PASSWORD=
-DB_NAME=
-DB_PORT=
-DB_ROOT_PASSWORD=
+# Application configuration
+PORT=3000                  # Port on which the application will run
+
+# Database configuration
+DB_HOST=localhost          # Database host (use 'mysql' for Docker setup)
+DB_USER=app_user           # Database username
+DB_PASSWORD=password123    # Database user password
+DB_NAME=task_manager       # Database name
+DB_PORT=3306               # Database port
+DB_ROOT_PASSWORD=root_pwd  # MySQL root password (used by Docker)
docker-compose.yml (3)

18-28: Add health check and restart policy to app service

The app service is missing a health check and restart policy that would improve reliability.

  app:
    build: .
    depends_on:
      - mysql
+   restart: always
    environment:
      DB_HOST: mysql
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: ${DB_NAME}
      DB_PORT: ${DB_PORT}
      PORT: ${PORT}
+   healthcheck:
+     test: ["CMD", "wget", "--spider", "-q", "http://localhost:${PORT}/health"]
+     interval: 30s
+     timeout: 10s
+     retries: 3

Don't forget to implement a /health endpoint in your Express application.


4-16: Add health check to MySQL service

The MySQL service would benefit from a health check to ensure the database is truly ready for connections.

  mysql:
    image: mysql:8.3
    container_name: mysql-db
    restart: always
+   healthcheck:
+     test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"]
+     interval: 10s
+     timeout: 5s
+     retries: 5
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}

3-39: Consider adding a custom network for the services

For improved security and isolation, consider defining a custom network for your services instead of using the default bridge network.

services:
  mysql:
    image: mysql:8.3
    container_name: mysql-db
    restart: always
+   networks:
+     - backend
    # ... rest of mysql config
  
  app:
    build: .
    depends_on:
      - mysql
+   networks:
+     - backend
    # ... rest of app config

volumes:
  mysql_data:

+ networks:
+   backend:
+     driver: bridge
tests/test.ts (3)

5-5: Use proper typing for Express app

The app variable should have a specific type rather than 'any' to maintain type safety.

-let app: any;
+import { Express } from 'express';
+let app: Express;

8-10: Add afterAll hook for cleanup

The test suite should include an afterAll hook to clean up resources after tests complete.

beforeAll(async () => {
    app = await createApp();
});

+afterAll(async () => {
+    // Clean up resources, close connections, etc.
+    // For example: await closeDatabase();
+});

12-73: Consider adding error case tests and making tests more independent

The current tests only verify the happy path and depend on each other's state. Consider adding tests for error cases and making tests more independent for better maintainability.

Here are examples of additional tests you could add:

  1. Error cases for each endpoint:
it('Should return 400 when creating a task with missing required fields', async () => {
    const res = await request(app)
        .post('/task')
        .send({ /* missing title */ });
    
    expect(res.statusCode).toBe(400);
});

it('Should return 404 when trying to fetch a non-existent task', async () => {
    const res = await request(app).get('/task/non-existent-id');
    expect(res.statusCode).toBe(404);
});
  1. To make tests more independent, consider using a beforeEach hook to set up the test state:
beforeEach(async () => {
    // Reset database or create a fresh task for each test
    const res = await request(app)
        .post('/task')
        .send({ title: 'Test Task', is_favorite: false, color: '#123456' });
    createdTaskId = res.body.id;
});

afterEach(async () => {
    // Clean up created tasks
    if (createdTaskId) {
        await request(app).delete(`/task/${createdTaskId}`);
    }
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 328ccdd and b3f6555.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (63)
  • .adonisrc.json (0 hunks)
  • .editorconfig (0 hunks)
  • .env.example (1 hunks)
  • .gitignore (1 hunks)
  • .gitignore copy (0 hunks)
  • .prettierignore (0 hunks)
  • .vscode/launch.json (1 hunks)
  • Dockerfile (1 hunks)
  • Leiame.md (0 hunks)
  • README.md (1 hunks)
  • ace (0 hunks)
  • ace-manifest.json (0 hunks)
  • app/Controllers/VehiclesController.ts (0 hunks)
  • app/Exceptions/Handler.ts (0 hunks)
  • app/Types/Vehicle.ts (0 hunks)
  • commands/index.ts (0 hunks)
  • config/app.ts (0 hunks)
  • config/bodyparser.ts (0 hunks)
  • config/cors.ts (0 hunks)
  • config/drive.ts (0 hunks)
  • config/hash.ts (0 hunks)
  • contracts/drive.ts (0 hunks)
  • contracts/env.ts (0 hunks)
  • contracts/events.ts (0 hunks)
  • contracts/hash.ts (0 hunks)
  • contracts/tests.ts (0 hunks)
  • docker-compose.yml (1 hunks)
  • env.ts (0 hunks)
  • jest.config.js (1 hunks)
  • package.json (1 hunks)
  • providers/AppProvider.ts (0 hunks)
  • server.ts (0 hunks)
  • src/Infrastructures/api.ts (1 hunks)
  • src/Infrastructures/express/api.express.ts (1 hunks)
  • src/Infrastructures/express/createApp.ts (1 hunks)
  • src/Infrastructures/express/routes/route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/GetTasks.express.route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/createTask.express.route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/deleteTask.express.route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/getTaskById.express.route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/getTasksFavorite.express.route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/index.express.route.ts (1 hunks)
  • src/Infrastructures/express/routes/task/updateTask.express.route.ts (1 hunks)
  • src/Infrastructures/package/banco.ts (1 hunks)
  • src/Infrastructures/repositories/taks/tasks.infrastructure.repository.ts (1 hunks)
  • src/db/database.ts (1 hunks)
  • src/domains/entities/task.entity.ts (1 hunks)
  • src/domains/gateway/task.gateway.ts (1 hunks)
  • src/index.ts (1 hunks)
  • src/usecases/task/createTask.usecase.ts (1 hunks)
  • src/usecases/task/deleteTask.usecase.ts (1 hunks)
  • src/usecases/task/getTaskById.usecase.ts (1 hunks)
  • src/usecases/task/getTasks.usecase.ts (1 hunks)
  • src/usecases/task/getTasksFavorite.usecase.ts (1 hunks)
  • src/usecases/task/updateTask.usecase.ts (1 hunks)
  • src/usecases/usecase.ts (1 hunks)
  • start/kernel.ts (0 hunks)
  • start/routes.ts (0 hunks)
  • test.ts (0 hunks)
  • tests/bootstrap.ts (0 hunks)
  • tests/functional/vehicles-api.spec.ts (0 hunks)
  • tests/test.ts (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (29)
  • .prettierignore
  • app/Exceptions/Handler.ts
  • env.ts
  • .editorconfig
  • tests/functional/vehicles-api.spec.ts
  • app/Types/Vehicle.ts
  • contracts/env.ts
  • start/routes.ts
  • contracts/hash.ts
  • commands/index.ts
  • server.ts
  • ace
  • config/hash.ts
  • app/Controllers/VehiclesController.ts
  • start/kernel.ts
  • Leiame.md
  • config/cors.ts
  • providers/AppProvider.ts
  • config/app.ts
  • contracts/events.ts
  • contracts/tests.ts
  • .gitignore copy
  • tests/bootstrap.ts
  • ace-manifest.json
  • contracts/drive.ts
  • config/drive.ts
  • .adonisrc.json
  • test.ts
  • config/bodyparser.ts
🧰 Additional context used
🧬 Code Definitions (17)
src/Infrastructures/package/banco.ts (1)
src/db/database.ts (1)
  • Database (5-58)
src/index.ts (12)
src/Infrastructures/package/banco.ts (1)
  • banco (3-3)
src/Infrastructures/repositories/taks/tasks.infrastructure.repository.ts (1)
  • TasksInfrastructureRepository (4-41)
src/usecases/task/deleteTask.usecase.ts (1)
  • DeleteTaskUsecase (10-32)
src/usecases/task/getTaskById.usecase.ts (1)
  • GetTaskByIdUsecase (16-48)
src/usecases/task/getTasks.usecase.ts (1)
  • GetTasksUsecase (12-34)
src/usecases/task/updateTask.usecase.ts (1)
  • UpdateTaskUsecase (14-55)
src/usecases/task/getTasksFavorite.usecase.ts (1)
  • GetTasksFavoriteUsecase (14-36)
src/Infrastructures/express/routes/task/createTask.express.route.ts (1)
  • CreateTaskRoute (6-47)
src/Infrastructures/express/routes/task/deleteTask.express.route.ts (1)
  • DeleteTaskRoute (6-44)
src/Infrastructures/express/routes/task/getTaskById.express.route.ts (1)
  • GetTaskByIdRoute (6-44)
src/Infrastructures/express/routes/task/updateTask.express.route.ts (1)
  • UpdateTaskRoute (6-45)
src/Infrastructures/express/routes/task/getTasksFavorite.express.route.ts (1)
  • GetTasksFavoriteRoute (5-40)
src/Infrastructures/express/createApp.ts (14)
src/Infrastructures/package/banco.ts (1)
  • banco (3-3)
src/Infrastructures/repositories/taks/tasks.infrastructure.repository.ts (1)
  • TasksInfrastructureRepository (4-41)
src/Infrastructures/express/routes/task/createTask.express.route.ts (1)
  • CreateTaskRoute (6-47)
src/usecases/task/createTask.usecase.ts (1)
  • CreateTaskUsecase (18-60)
src/usecases/task/deleteTask.usecase.ts (1)
  • DeleteTaskUsecase (10-32)
src/Infrastructures/express/routes/task/getTaskById.express.route.ts (1)
  • GetTaskByIdRoute (6-44)
src/usecases/task/getTaskById.usecase.ts (1)
  • GetTaskByIdUsecase (16-48)
src/Infrastructures/express/routes/task/GetTasks.express.route.ts (1)
  • GetTasksRoute (6-41)
src/usecases/task/getTasks.usecase.ts (1)
  • GetTasksUsecase (12-34)
src/Infrastructures/express/routes/task/updateTask.express.route.ts (1)
  • UpdateTaskRoute (6-45)
src/usecases/task/updateTask.usecase.ts (1)
  • UpdateTaskUsecase (14-55)
src/Infrastructures/express/routes/task/getTasksFavorite.express.route.ts (1)
  • GetTasksFavoriteRoute (5-40)
src/usecases/task/getTasksFavorite.usecase.ts (1)
  • GetTasksFavoriteUsecase (14-36)
src/Infrastructures/express/api.express.ts (1)
  • ApiExpress (9-61)
tests/test.ts (1)
src/Infrastructures/express/createApp.ts (1)
  • createApp (20-35)
src/Infrastructures/express/routes/task/getTasksFavorite.express.route.ts (1)
src/Infrastructures/express/routes/route.ts (3)
  • Route (12-17)
  • HttpMethod (3-3)
  • HttpMethod (5-10)
src/domains/gateway/task.gateway.ts (1)
src/domains/entities/task.entity.ts (2)
  • TaskEntity (8-41)
  • id (26-28)
src/Infrastructures/express/api.express.ts (2)
src/Infrastructures/api.ts (1)
  • Api (1-3)
src/Infrastructures/express/routes/route.ts (1)
  • Route (12-17)
src/Infrastructures/express/routes/task/updateTask.express.route.ts (2)
src/Infrastructures/express/routes/route.ts (3)
  • Route (12-17)
  • HttpMethod (3-3)
  • HttpMethod (5-10)
src/usecases/task/updateTask.usecase.ts (1)
  • UpdateTaskUsecase (14-55)
src/Infrastructures/express/routes/task/deleteTask.express.route.ts (2)
src/Infrastructures/express/routes/route.ts (3)
  • Route (12-17)
  • HttpMethod (3-3)
  • HttpMethod (5-10)
src/usecases/task/deleteTask.usecase.ts (1)
  • DeleteTaskUsecase (10-32)
src/Infrastructures/express/routes/task/GetTasks.express.route.ts (2)
src/Infrastructures/express/routes/route.ts (3)
  • Route (12-17)
  • HttpMethod (3-3)
  • HttpMethod (5-10)
src/usecases/task/getTasks.usecase.ts (1)
  • GetTasksUsecase (12-34)
src/usecases/task/getTasks.usecase.ts (3)
src/usecases/usecase.ts (1)
  • Usecase (1-3)
src/domains/gateway/task.gateway.ts (1)
  • TaskGateway (3-10)
src/domains/entities/task.entity.ts (1)
  • TaskEntity (8-41)
src/usecases/task/updateTask.usecase.ts (3)
src/usecases/usecase.ts (1)
  • Usecase (1-3)
src/domains/gateway/task.gateway.ts (1)
  • TaskGateway (3-10)
src/domains/entities/task.entity.ts (1)
  • TaskEntity (8-41)
src/Infrastructures/repositories/taks/tasks.infrastructure.repository.ts (3)
src/Infrastructures/package/banco.ts (1)
  • banco (3-3)
src/db/database.ts (1)
  • Database (5-58)
src/domains/entities/task.entity.ts (2)
  • id (26-28)
  • TaskEntity (8-41)
src/usecases/task/deleteTask.usecase.ts (2)
src/usecases/usecase.ts (1)
  • Usecase (1-3)
src/domains/gateway/task.gateway.ts (1)
  • TaskGateway (3-10)
src/usecases/task/getTasksFavorite.usecase.ts (3)
src/usecases/usecase.ts (1)
  • Usecase (1-3)
src/domains/gateway/task.gateway.ts (1)
  • TaskGateway (3-10)
src/domains/entities/task.entity.ts (1)
  • TaskEntity (8-41)
src/usecases/task/getTaskById.usecase.ts (3)
src/usecases/usecase.ts (1)
  • Usecase (1-3)
src/domains/gateway/task.gateway.ts (1)
  • TaskGateway (3-10)
src/domains/entities/task.entity.ts (1)
  • TaskEntity (8-41)
src/usecases/task/createTask.usecase.ts (3)
src/usecases/usecase.ts (1)
  • Usecase (1-3)
src/domains/gateway/task.gateway.ts (1)
  • TaskGateway (3-10)
src/domains/entities/task.entity.ts (1)
  • TaskEntity (8-41)
🪛 LanguageTool
README.md

[uncategorized] ~3-~3: Possível erro de concordância. Considere alterar para “gerenciamento de tarefas construído”.
Context: ...amento de Tarefas Uma API RESTful para gerenciamento de tarefas construída com Node.js, TypeScript, Express e MySQ...

(CONCORDANCIA_NOMINAL_COMPLEXA)


[uncategorized] ~104-~104: Pontuação duplicada
Context: ... Cada tarefa possui os seguintes campos: - id: Identificador único - title: Título d...

(DOUBLE_PUNCTUATION_XML)

🪛 markdownlint-cli2 (0.17.2)
README.md

16-16: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


49-49: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🪛 Biome (1.9.4)
src/db/database.ts

[error] 37-37: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 46-46: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)


[error] 55-55: The catch clause that only rethrows the original error is useless.

An unnecessary catch clause can be confusing.
Unsafe fix: Remove the try/catch clause.

(lint/complexity/noUselessCatch)

src/usecases/task/updateTask.usecase.ts

[error] 12-12: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

src/usecases/task/deleteTask.usecase.ts

[error] 8-8: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

🔇 Additional comments (13)
src/Infrastructures/api.ts (1)

1-3: LGTM! Clean interface definition.

The Api interface is well-defined with a clear contract for starting the server.

src/usecases/usecase.ts (1)

1-3: LGTM! Well-defined generic interface for use cases.

The Usecase interface follows the command pattern and provides a clear contract with generic input/output types.

src/Infrastructures/express/routes/task/index.express.route.ts (1)

1-6: LGTM! Barrel export pattern simplifies imports.

The use of barrel exports is a good practice that consolidates all task-related route exports in one place, making imports cleaner throughout the application.

jest.config.js (1)

1-6: LGTM! Standard Jest configuration for TypeScript.

The configuration properly sets up ts-jest preset for TypeScript support, uses the node environment for server-side testing, and includes both src and tests directories for test discovery.

src/Infrastructures/express/createApp.ts (1)

23-35: LGTM! Clean architecture implementation.

The code follows clean architecture principles with proper separation of concerns. The dependency injection pattern is well implemented, creating a clear flow from repositories to use cases to routes.

src/Infrastructures/express/routes/route.ts (2)

3-10: Good definition of HTTP methods with type safety

The HttpMethod type and constant object provide a clean, type-safe way to handle HTTP methods throughout the application. This approach eliminates magic strings and ensures consistency.

For even better type safety, consider adding as HttpMethod to all constants, not just GET:

export const HttpMethod = {
    GET: 'get' as HttpMethod,
-   POST: 'post',
-   PUT: 'put',
-   DELETE: 'delete'
+   POST: 'post' as HttpMethod,
+   PUT: 'put' as HttpMethod,
+   DELETE: 'delete' as HttpMethod
} as const;

12-17: Well-designed Route interface

The interface provides a clear contract for all route implementations with appropriate method signatures. This promotes consistency across route classes and aids in code organization.

src/db/database.ts (1)

20-30: Task table schema looks good

The table creation logic is correct and provides a solid foundation for the task management functionality.

src/usecases/task/deleteTask.usecase.ts (1)

1-32: Well-structured use case with good separation of concerns

Overall, this use case follows clean architecture principles with good separation of concerns. The validate method properly checks for required inputs, and the execute method follows a clear workflow.

🧰 Tools
🪛 Biome (1.9.4)

[error] 8-8: void is confusing outside a return type or a type parameter.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

src/Infrastructures/express/routes/task/GetTasks.express.route.ts (1)

6-19: Well-structured route class with good separation of concerns

The route class follows a clean architecture approach with good separation of concerns. The static factory method pattern for creation is a good practice, and the implementation of the Route interface is thorough.

package.json (3)

10-13: Repository Information Added
The newly added "repository" field with type "git" and the repository URL will be beneficial for contributors and for GitHub integration. This change is well implemented.


23-32: Updated Dependencies List
The dependencies are now aligned with your transition to a Node.js/TypeScript ecosystem using Express. Removing AdonisJS-specific packages in favor of packages like express, dotenv, and database drivers is appropriate. Ensure that all deprecated dependencies have been fully removed from other parts of the project.


34-43: Updated DevDependencies for TypeScript and Testing
The updated devDependencies, including TypeScript definitions and testing libraries (like jest and ts-jest), support the new ecosystem effectively. Just make sure that the Jest configuration file (jest.config.js) is properly set up to work with ts-jest.

Comment on lines +1 to +3
import { Database } from "../../db/database";

export const banco = new Database(); No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Singleton database connection is good but lacks initialization.

Creating a singleton database instance is a good practice, but the code doesn't ensure that the database tables exist before use.

Consider adding table initialization:

 import { Database } from "../../db/database";
 
 export const banco = new Database();
+
+// Initialize database tables
+banco.ensureTablesExist().catch(error => {
+  console.error('Failed to initialize database tables:', error);
+  process.exit(1);
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Database } from "../../db/database";
export const banco = new Database();
import { Database } from "../../db/database";
export const banco = new Database();
// Initialize database tables
banco.ensureTablesExist().catch(error => {
console.error('Failed to initialize database tables:', error);
process.exit(1);
});

Comment on lines +20 to +22
export async function createApp() {
await banco.ensureTablesExist();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for database initialization.

The function doesn't handle potential errors that might occur during database table creation.

 export async function createApp() {
-    await banco.ensureTablesExist();
+    try {
+        await banco.ensureTablesExist();
+    } catch (error) {
+        console.error('Failed to initialize database tables:', error);
+        throw new Error('Application initialization failed: Database setup error');
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function createApp() {
await banco.ensureTablesExist();
export async function createApp() {
try {
await banco.ensureTablesExist();
} catch (error) {
console.error('Failed to initialize database tables:', error);
throw new Error('Application initialization failed: Database setup error');
}
// rest of your code...
}

Comment thread src/index.ts
import { ApiExpress } from "./Infrastructures/express/api.express";
import { DeleteTaskRoute } from './Infrastructures/express/routes/task/deleteTask.express.route';
import { GetTaskByIdRoute } from './Infrastructures/express/routes/task/getTaskById.express.route';
import { GetTasksRoute } from './Infrastructures/express/routes/task/GetTasks.express.route';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix import path casing

There's a casing inconsistency in the import path. TypeScript imports are case-sensitive and this could cause issues in case-sensitive file systems (like Linux).

-import { GetTasksRoute } from './Infrastructures/express/routes/task/GetTasks.express.route';
+import { GetTasksRoute } from './Infrastructures/express/routes/task/getTasks.express.route';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { GetTasksRoute } from './Infrastructures/express/routes/task/GetTasks.express.route';
import { GetTasksRoute } from './Infrastructures/express/routes/task/getTasks.express.route';

Comment thread src/index.ts
Comment on lines +19 to +20
async function main() {
await banco.ensureTablesExist()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling to the main function

The main function initializes critical components but lacks error handling. This could lead to unhandled promise rejections if database initialization fails.

async function main() {
+  try {
    await banco.ensureTablesExist()
    // Rest of the function
+  } catch (error) {
+    console.error('Failed to initialize application:', error)
+    process.exit(1)
+  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function main() {
await banco.ensureTablesExist()
async function main() {
try {
await banco.ensureTablesExist()
// Rest of the function
} catch (error) {
console.error('Failed to initialize application:', error)
process.exit(1)
}
}

Comment on lines +21 to +32
getHandler(): (req: Request, res: Response) => Promise<void> {
return async (req: Request, res: Response) => {
const { id, title, is_favorite, color } = req.body;
const task = await this.updateTaskUsecase.execute({
id,
title,
is_favorite,
color
});
res.status(200).json({ message: "Task updated" });
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and input validation

The current implementation doesn't handle potential errors from the use case or validate input properly.

getHandler(): (req: Request, res: Response) => Promise<void> {
    return async (req: Request, res: Response) => {
+       try {
            const { id, title, is_favorite, color } = req.body;
+           
+           // Basic validation
+           if (!id || !title || is_favorite === undefined || !color) {
+               return res.status(400).json({ 
+                   message: "Missing required fields",
+                   details: { id, title, is_favorite, color }
+               });
+           }
+           
            const task = await this.updateTaskUsecase.execute({
                id,
                title,
                is_favorite,
                color
            });
            res.status(200).json({ message: "Task updated" });
+       } catch (error) {
+           console.error('Error updating task:', error);
+           if (error.message === 'Task not found') {
+               return res.status(404).json({ message: error.message });
+           }
+           res.status(500).json({ message: "Internal server error" });
+       }
    }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getHandler(): (req: Request, res: Response) => Promise<void> {
return async (req: Request, res: Response) => {
const { id, title, is_favorite, color } = req.body;
const task = await this.updateTaskUsecase.execute({
id,
title,
is_favorite,
color
});
res.status(200).json({ message: "Task updated" });
}
}
getHandler(): (req: Request, res: Response) => Promise<void> {
return async (req: Request, res: Response) => {
try {
const { id, title, is_favorite, color } = req.body;
// Basic validation
if (!id || !title || is_favorite === undefined || !color) {
return res.status(400).json({
message: "Missing required fields",
details: { id, title, is_favorite, color }
});
}
const task = await this.updateTaskUsecase.execute({
id,
title,
is_favorite,
color
});
res.status(200).json({ message: "Task updated" });
} catch (error) {
console.error('Error updating task:', error);
if (error.message === 'Task not found') {
return res.status(404).json({ message: error.message });
}
res.status(500).json({ message: "Internal server error" });
}
}
}

Comment thread src/Infrastructures/express/routes/task/createTask.express.route.ts
Comment on lines +22 to +23
return async (req: Request, res: Response) => {
const { title, is_favorite, color } = req.body;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add request body validation

There's no validation for the request body format before trying to extract properties. This could lead to runtime errors if the request body is malformed.

return async (req: Request, res: Response) => {
+   if (!req.body || typeof req.body !== 'object') {
+       return res.status(400).json({
+           message: "Invalid request body"
+       });
+   }
    const { title, is_favorite, color } = req.body;
+   
+   // Check if is_favorite is a boolean
+   if (is_favorite !== undefined && typeof is_favorite !== 'boolean') {
+       return res.status(400).json({
+           message: "is_favorite must be a boolean"
+       });
+   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return async (req: Request, res: Response) => {
const { title, is_favorite, color } = req.body;
return async (req: Request, res: Response) => {
if (!req.body || typeof req.body !== 'object') {
return res.status(400).json({
message: "Invalid request body"
});
}
const { title, is_favorite, color } = req.body;
// Check if is_favorite is a boolean
if (is_favorite !== undefined && typeof is_favorite !== 'boolean') {
return res.status(400).json({
message: "is_favorite must be a boolean"
});
}

Comment on lines +38 to +50
private validate(input: CreateTaskInputDto): void {
if (!input.title) {
throw new Error('Title is required');
}

if (!input.color) {
throw new Error('Color is required');
}

if (input.is_favorite == undefined) {
throw new Error('is_favorite is required');
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add validation for color format

Consider validating the color format to ensure it's a valid hexadecimal color code or follows your application's color format requirements.


🌐 Web query:

What are common formats for color representation in web applications?

💡 Result:

Common formats for color representation in web applications include hexadecimal, RGB/RGBA, HSL/HSLA, and predefined color names, each with distinct use cases and technical considerations. Modern CSS specifications also introduce advanced formats like HWB, LCH, and OKLCH for wider color gamuts. Here's a detailed breakdown:

Hexadecimal (Hex)

  • Syntax: #RRGGBB or #RRGGBBAA for alpha transparency (e.g., #FF0000 for red, #FF000088 for semi-transparent red).
  • Features:
    • Compact and widely supported[1][2][11].
    • Represents sRGB colors using base-16 values (0-9, A-F)[5][7].
    • Supports shorthand notation (e.g., #F00 instead of #FF0000)[2][11].

RGB/RGBA

  • Syntax: rgb(red, green, blue) or rgba(red, green, blue, alpha) (e.g., rgb(255, 0, 0) for red, rgba(0, 0, 255, 0.5) for semi-transparent blue).
  • Features:
    • Values range from 0-255 or 0%-100%[1][8][11].
    • Alpha channel (opacity) supported in RGBA[11][13].
    • More readable than hex for code maintenance[1][5].

HSL/HSLA

  • Syntax: hsl(hue, saturation%, lightness%) or hsla(hue, saturation%, lightness%, alpha) (e.g., hsl(0, 100%, 50%) for red).
  • Features:
    • Intuitive for adjusting hue (0-360°), saturation, and lightness[4][11][13].
    • Limited to 3.6 million colors in sRGB compared to RGB's 16.8 million[1].
    • Preferred for interactive color selection due to perceptual clarity[1][12].

Named Colors

  • Examples: red, dodgerblue, goldenrod[8][13].
  • Features:
    • 140 predefined names in CSS for quick implementation[8][11].
    • Includes special keywords like transparent and currentColor[13].

Emerging Formats

  1. HWB (Hue-Whiteness-Blackness)

    • Syntax: hwb(hue deg, whiteness%, blackness%) (e.g., hwb(200deg 25% 25%))[12].
    • Designed for intuitive color mixing but rarely used in practice[1][12].
  2. LCH/OKLCH

    • Syntax: lch(lightness, chroma, hue) or oklch(lightness, chroma, hue)[4][14].
    • Supports wider color gamuts beyond sRGB (e.g., P3 displays)[12][14].
    • Better perceptual uniformity for color adjustments[14].
  3. CSS Color 4/5

    • Adds color-mix(), relative color syntax, and device-independent spaces like LAB[4][14].
    • Enables CMYK support via custom profiles[4].

Key Considerations

  • sRGB Dominance: Most web colors use sRGB, ensuring consistency across devices[7][12].
  • Accessibility: Contrast ratios rely on sRGB values; newer formats require careful implementation[7][14].
  • Browser Support: Hex/RGB/HSL work universally, while LCH/OKLCH require fallbacks for older browsers[14].

For example, a gradient using modern and legacy formats might look like:

.background {
  background: linear-gradient(
    to right,
    hsl(248.5 95% 60.8%),  /* Modern HSL syntax */
    oklch(70% 0.25 290)    /* Wider-gamut OKLCH */
  );
}

Developers often choose HSL for readability during manual adjustments, while hex remains popular for its brevity and design-tool compatibility[1][14].

Citations:


Validation for Color Format Needed

  • Issue: The current validate method only checks for the presence of a color value but does not verify whether it adheres to a valid color format.
  • Recommendation: Add logic within validate() to confirm that input.color matches an acceptable color format. For example, if your application expects hexadecimal colors, you might use a regular expression like /^#(?:[A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/ to validate the input.
  • Considerations:
    • If your application should support additional formats such as RGB/RGBA or HSL/HSLA, ensure the validation covers those cases or explicitly restrict acceptable formats in the documentation.
    • Update the error message to indicate that the provided color does not match the required format (e.g. "Invalid color format").

Comment thread docker-compose.yml
Comment on lines +29 to +36
command: >
sh -c "
echo 'Aguardando o MySQL...';
sleep 10;
echo 'Rodando migrations...';
echo 'Iniciando aplicação...';
npm run start;
"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve application startup reliability

The command section has several issues:

  1. It uses a fixed sleep time (10 seconds) instead of properly waiting for MySQL to be ready
  2. It mentions running migrations but doesn't actually run them
  3. The comments are in Portuguese rather than English
  command: >
    sh -c "
-      echo 'Aguardando o MySQL...';
-      sleep 10;
-      echo 'Rodando migrations...';
-      echo 'Iniciando aplicação...';
+      echo 'Waiting for MySQL to be ready...';
+      while ! nc -z mysql 3306; do
+        sleep 1;
+      done;
+      echo 'Running migrations...';
+      npm run migrate;
+      echo 'Starting application...';
      npm run start;
    "

Make sure to add the netcat tool to your Dockerfile or replace with another solution like dockerize for checking service availability.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
command: >
sh -c "
echo 'Aguardando o MySQL...';
sleep 10;
echo 'Rodando migrations...';
echo 'Iniciando aplicação...';
npm run start;
"
command: >
sh -c "
echo 'Waiting for MySQL to be ready...';
while ! nc -z mysql 3306; do
sleep 1;
done;
echo 'Running migrations...';
npm run migrate;
echo 'Starting application...';
npm run start;
"

Comment thread tests/test.ts
Comment on lines +58 to +65
it('⭐ Listar favoritas (GET /task/star/favorite)', async () => {
const res = await request(app).get('/task/star/favorite');

expect(res.statusCode).toBe(200);
const task = res.body.find((t: any) => t.id === createdTaskId);
expect(task).toBeDefined();
expect(task.is_favorite).toBe(1);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Address type inconsistency in is_favorite property

There's a type inconsistency where is_favorite is set as a boolean (line 50) but checked as a number (line 64). This suggests a mismatch between the API contract and database representation.

    expect(res.statusCode).toBe(200);
    const task = res.body.find((t: any) => t.id === createdTaskId);
    expect(task).toBeDefined();
-   expect(task.is_favorite).toBe(1);
+   expect(task.is_favorite).toBe(true);

This issue indicates a potential problem in how boolean values are handled between your API and database. You should ensure consistent types throughout your application.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('⭐ Listar favoritas (GET /task/star/favorite)', async () => {
const res = await request(app).get('/task/star/favorite');
expect(res.statusCode).toBe(200);
const task = res.body.find((t: any) => t.id === createdTaskId);
expect(task).toBeDefined();
expect(task.is_favorite).toBe(1);
});
it('⭐ Listar favoritas (GET /task/star/favorite)', async () => {
const res = await request(app).get('/task/star/favorite');
expect(res.statusCode).toBe(200);
const task = res.body.find((t: any) => t.id === createdTaskId);
expect(task).toBeDefined();
expect(task.is_favorite).toBe(true);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant