Repository: kodustech/kodus-ai
Pull request #30: Fix/azure repos comments
Author: login: sartorijr92
Created: 2025-05-02T18:04:11Z | Merged: 2025-05-02T18:28:00Z

This pull request addresses improvements in the webhook handling for Azure Repos and GitHub within the `kodus-ai` repository. The changes in the `azureRepos.controller.ts` file include the introduction of webhook handling features such as token validation, duplicate request detection using a cache, and asynchronous processing. The update also highlights areas for potential enhancement, specifically in type safety for incoming payloads and the error handling response strategy. Additionally, the `github.controller.ts` file has been updated to include pull request event filtering in the webhook handler. The source branch for these changes is `fix/azure-repos-comments`, and the target branch is `main`.


Diff:
diff --git a/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/initial-comment.stage.ts b/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/initial-comment.stage.ts
index b1046ae0b..4047bc026 100644
--- a/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/initial-comment.stage.ts
+++ b/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/initial-comment.stage.ts
@@ -6,7 +6,6 @@ import {
 } from '@/core/domain/codeBase/contracts/CommentManagerService.contract';
 import { CodeReviewPipelineContext } from '../context/code-review-pipeline.context';
 import { PinoLoggerService } from '@/core/infrastructure/adapters/services/logger/pino.service';
-import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 
 @Injectable()
 export class InitialCommentStage extends BasePipelineStage<CodeReviewPipelineContext> {
@@ -28,7 +27,7 @@ export class InitialCommentStage extends BasePipelineStage<CodeReviewPipelineCon
             context.pullRequest.number,
             context.repository,
             context.changedFiles,
-            context.codeReviewConfig.languageResultPrompt,
+            context.codeReviewConfig?.languageResultPrompt ?? 'en-US',
             context.platformType,
         );
 
diff --git a/src/core/infrastructure/http/controllers/azureRepos.controller.ts b/src/core/infrastructure/http/controllers/azureRepos.controller.ts
index f7fd06adb..8f91e37f6 100644
--- a/src/core/infrastructure/http/controllers/azureRepos.controller.ts
+++ b/src/core/infrastructure/http/controllers/azureRepos.controller.ts
@@ -4,63 +4,117 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private readonly cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
+            const eventType = payload?.eventType as string;
+
+            if (!eventType) {
+                this.logger.log({
+                    message: 'Webhook Azure DevOps recebido sem eventType',
+                    context: AzureReposController.name,
+                    metadata: { payload },
+                });
+                return res
+                    .status(HttpStatus.BAD_REQUEST)
+                    .send('Evento não reconhecido');
+            }
+
+            // Verificar duplicação
+            const isDuplicate = await this.isDuplicateRequest(payload, req);
+            if (isDuplicate) {
+                return res
+                    .status(HttpStatus.OK)
+                    .send('Webhook already processed');
+            }
+
+            // Executa o processamento de forma assíncrona após enviar a resposta
+            res.status(HttpStatus.OK).send('Webhook received');
+
+            setImmediate(() => {
+                this.logger.log({
+                    message: `Webhook received, ${eventType}`,
+                    context: AzureReposController.name,
+                    metadata: {
+                        event: eventType,
+                        repositoryName: payload?.resource?.repository?.name,
+                        pullRequestId: payload?.resource?.pullRequestId,
+                        projectId: payload?.resourceContainers?.project?.id,
+                    },
+                });
+
+                this.receiveWebhookUseCase.execute({
+                    payload,
+                    event: eventType,
+                    platformType: PlatformType.AZURE_REPOS,
+                });
+            });
+        } catch (error) {
             this.logger.error({
-                message: 'Webhook Azure DevOps Not Token Valid',
+                message: 'Error processing webhook',
                 context: AzureReposController.name,
+                error: error,
             });
-            return res.status(403).send('Unauthorized');
         }
+    }
 
-        const payload = req.body as any;
-        const eventType = payload?.eventType as string;
+    private async isDuplicateRequest(
+        payload: any,
+        req: Request,
+    ): Promise<boolean> {
+        const prId = payload?.resource?.pullRequestId;
+        const eventType = payload?.eventType;
 
-        // Verificar se recebemos um tipo de evento válido
-        if (!eventType) {
-            this.logger.log({
-                message: 'Webhook Azure DevOps recebido sem eventType',
-                context: AzureReposController.name,
-                metadata: { payload },
-            });
-            return res
-                .status(HttpStatus.BAD_REQUEST)
-                .send('Evento não reconhecido');
-        }
+        if (!prId || !eventType) return false;
 
-        setImmediate(() => {
-            this.logger.log({
-                message: `Webhook received, ${eventType}`,
-                context: AzureReposController.name,
-                metadata: {
-                    event: eventType,
-                    repositoryName: payload?.resource?.repository?.name,
-                    pullRequestId: payload?.resource?.pullRequestId,
-                    projectId: payload?.resourceContainers?.project?.id,
-                },
-            });
+        // Usar o payload completo para comparação
+        const payloadHash = createHash('md5')
+            .update(
+                JSON.stringify({
+                    prId,
+                    eventType,
+                    createdDate: payload?.createdDate,
+                    id: payload?.id,
+                }),
+            )
+            .digest('hex');
+
+        // Chave de cache única baseada no conteúdo
+        const cacheKey = `azure_webhook:${prId}:${payloadHash}`;
 
-            this.receiveWebhookUseCase.execute({
-                payload,
-                event: eventType,
-                platformType: PlatformType.AZURE_REPOS,
+        const exists = await this.cacheService.cacheExists(cacheKey);
+        if (exists) {
+            this.logger.warn({
+                message: `Requisição duplicada detectada`,
+                context: AzureReposController.name,
+                metadata: { prId, eventType, payloadHash },
             });
-        });
+            return true;
+        }
 
-        console.log('@event azure webhook', eventType);
-        console.log('Webhook received');
-        return res.status(HttpStatus.OK).send('Webhook received');
+        await this.cacheService.addToCache(cacheKey, true, 60000); // 1 minuto
+        return false;
     }
 }
diff --git a/src/core/infrastructure/http/controllers/github.controller.ts b/src/core/infrastructure/http/controllers/github.controller.ts
index 13941d2b6..7e8631c7c 100644
--- a/src/core/infrastructure/http/controllers/github.controller.ts
+++ b/src/core/infrastructure/http/controllers/github.controller.ts
@@ -38,6 +38,17 @@ export class GithubController {
         const event = req.headers['x-github-event'] as string;
         const payload = req.body as any;
 
+        if (event === 'pull_request') {
+            if (
+                payload?.action !== 'opened' &&
+                payload?.action !== 'synchronize' &&
+                payload?.action !== 'closed' &&
+                payload?.action !== 'reopened'
+            ) {
+                return res.status(HttpStatus.OK).send('Webhook received');
+            }
+        }
+
         setImmediate(() => {
             this.logger.log({
                 message: `Webhook received, ${event}`,


Reviews:
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:39Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:42Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:45Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:48Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:51Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:54Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:10:57Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:03Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:09Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:19Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:21Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:27Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:30Z
user: login: kody-ai[bot]
body: # Found critical issues please review the requested changes

- [Remove the unreachable code attempting to send an OK response within the catch block and improve logging. The response is likely already sent before asynchronous processing.](https://github.com/kodustech/kodus-ai/pull/30#discussion_r2071978433)
- [Replace `any` type casting for `req.body` with a specific interface or type for the Azure DevOps webhook payload to improve type safety and prevent potential runtime errors.](https://github.com/kodustech/kodus-ai/pull/30#discussion_r2071978516)
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:11:55Z
user: login: kody-ai[bot]
body: # Found critical issues please review the requested changes

- [Remove the unreachable code attempting to send an OK response within the catch block and log the error appropriately, as the response is likely already sent before asynchronous processing.](https://github.com/kodustech/kodus-ai/pull/30#discussion_r2071978205)
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:12:02Z
user: login: kody-ai[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T18:24:51Z

Review comments:
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
+            const eventType = payload?.eventType as string;
+
+            if (!eventType) {
+                this.logger.log({
+                    message: 'Webhook Azure DevOps recebido sem eventType',
+                    context: AzureReposController.name,
+                    metadata: { payload },
+                });
+                return res
+                    .status(HttpStatus.BAD_REQUEST)
+                    .send('Evento não reconhecido');
+            }
+
+            // Verificar duplicação
+            const isDuplicate = await this.isDuplicateRequest(payload, req);
+            if (isDuplicate) {
+                return res
+                    .status(HttpStatus.OK)
+                    .send('Webhook already processed');
+            }
+
+            // Executa o processamento de forma assíncrona após enviar a resposta
+            res.status(HttpStatus.OK).send('Webhook received');
+
+            setImmediate(() => {
+                this.logger.log({
+                    message: `Webhook received, ${eventType}`,
+                    context: AzureReposController.name,
+                    metadata: {
+                        event: eventType,
+                        repositoryName: payload?.resource?.repository?.name,
+                        pullRequestId: payload?.resource?.pullRequestId,
+                        projectId: payload?.resourceContainers?.project?.id,
+                    },
+                });
+
+                this.receiveWebhookUseCase.execute({
+                    payload,
+                    event: eventType,
+                    platformType: PlatformType.AZURE_REPOS,
+                });
+            });
+        } catch (error) {
             this.logger.error({
-                message: 'Webhook Azure DevOps Not Token Valid',
+                message: 'Error processing webhook',
                 context: AzureReposController.name,
+                error: error,
             });
-            return res.status(403).send('Unauthorized');
+
+            // Garante que a resposta é enviada mesmo em caso de erro
+            if (!res.headersSent) {
+                return res.status(HttpStatus.OK).send('Webhook received');
+            }
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Error Handling](https://img.shields.io/badge/Error_Handling-FF6D00) ![critical](https://img.shields.io/badge/severity_level-critical-FF3D3D)
```typescript
// Headers were likely already sent by line 54.
            // Logging the error is crucial for debugging.  Remove the unreachable code and misleading comment.
            this.logger.error({
                message: 'Error processing webhook',
                context: AzureReposController.name,
                error,
            });
```
The `catch` block attempts to send an `HttpStatus.OK` response if headers haven't been sent. However, the primary success response (`res.status(HttpStatus.OK).send('Webhook received');`) is sent *before* the asynchronous processing begins (line 54). Therefore, `res.headersSent` will likely always be true when this catch block is entered due to an error within the `setImmediate` callback. This makes the `if (!res.headersSent)` block effectively unreachable for errors during async processing. Since the initial 'Webhook received' response indicates acceptance, simply logging the error might be sufficient. Sending OK on error could also be misleading.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Kody Rules](https://img.shields.io/badge/Kody_Rules-4527A0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
async handleWebhook(@Req() req: Request, @Res() res: Response): Promise<void> {
```
The `handleWebhook` method lacks an explicit return type.  While it implicitly returns `Promise<void>`, it's best practice to explicitly declare it for better readability and maintainability. Add `: Promise<void>` to the method signature.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Kody Rules](https://img.shields.io/badge/Kody_Rules-4527A0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
async handleWebhook(@Req() req: Request, @Res() res: Response): Promise<void> {
```
Methods in the codebase lack explicit return types, which reduces code clarity and maintainability.

This issue appears in multiple locations:
* src/core/infrastructure/http/controllers/azureRepos.controller.ts: Lines 19-19
* src/core/infrastructure/http/controllers/github.controller.ts: Lines 36-60
Please ensure all methods have explicit return types to improve code clarity and maintainability.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Kody Rules](https://img.shields.io/badge/Kody_Rules-4527A0) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
interface AzureDevOpsWebhookPayload { /* ... properties of the payload */ }
const payload = req.body as AzureDevOpsWebhookPayload;
```
The `payload` parameter in the `handleWebhook` method is typed as `any`. This should be replaced with a specific type or interface to adhere to Kody Rules and improve type safety.  Create an interface representing the expected structure of the webhook payload.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
+            const eventType = payload?.eventType as string;
+
+            if (!eventType) {
+                this.logger.log({
+                    message: 'Webhook Azure DevOps recebido sem eventType',
+                    context: AzureReposController.name,
+                    metadata: { payload },
+                });
+                return res
+                    .status(HttpStatus.BAD_REQUEST)
+                    .send('Evento não reconhecido');
+            }
+
+            // Verificar duplicação
+            const isDuplicate = await this.isDuplicateRequest(payload, req);
+            if (isDuplicate) {
+                return res
+                    .status(HttpStatus.OK)
+                    .send('Webhook already processed');
+            }
+
+            // Executa o processamento de forma assíncrona após enviar a resposta
+            res.status(HttpStatus.OK).send('Webhook received');
+
+            setImmediate(() => {
+                this.logger.log({
+                    message: `Webhook received, ${eventType}`,
+                    context: AzureReposController.name,
+                    metadata: {
+                        event: eventType,
+                        repositoryName: payload?.resource?.repository?.name,
+                        pullRequestId: payload?.resource?.pullRequestId,
+                        projectId: payload?.resourceContainers?.project?.id,
+                    },
+                });
+
+                this.receiveWebhookUseCase.execute({
+                    payload,
+                    event: eventType,
+                    platformType: PlatformType.AZURE_REPOS,
+                });
+            });
+        } catch (error) {
             this.logger.error({
-                message: 'Webhook Azure DevOps Not Token Valid',
+                message: 'Error processing webhook',
                 context: AzureReposController.name,
+                error: error,
             });
-            return res.status(403).send('Unauthorized');
+
+            // Garante que a resposta é enviada mesmo em caso de erro
+            if (!res.headersSent) {
+                return res.status(HttpStatus.OK).send('Webhook received');
+            }
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Error Handling](https://img.shields.io/badge/Error_Handling-FF6D00) ![critical](https://img.shields.io/badge/severity_level-critical-FF3D3D)
```typescript
// Headers were likely already sent by line 54.
            // Logging the error is crucial for debugging. Add more context to the log message.
            this.logger.error({
                message: 'Error processing webhook',
                context: AzureReposController.name,
                error,
            });
```
The `catch` block attempts to send an `HttpStatus.OK` response if headers haven't been sent. However, the primary success response (`res.status(HttpStatus.OK).send('Webhook received');`) is sent *before* the asynchronous processing begins (line 54). Therefore, `res.headersSent` will likely always be true when this catch block is entered due to an error within the `setImmediate` callback. This makes the `if (!res.headersSent)` block effectively unreachable for errors during async processing. Since the initial 'Webhook received' response indicates acceptance, simply logging the error might be sufficient. Sending OK on error could also be misleading.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2)  ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
interface AzureWebhookPayload {
  eventType: string;
  resource?: {
    pullRequestId?: number;
    repository?: { name?: string };
    // ... other expected properties
  };
  resourceContainers?: {
    project?: { id?: string };
    // ... other expected properties
  };
  createdDate?: string; // Or Date if parsed
  id?: string;
  // ... add other relevant fields based on Azure DevOps documentation
}

// ... inside the method
const payload: AzureWebhookPayload = req.body;
```
The webhook payload `req.body` is cast to `any`. This bypasses TypeScript's type checking, increasing the risk of runtime errors due to unexpected payload structures or accessing non-existent properties (e.g., `payload?.resource?.pullRequestId`). Define a specific interface or type for the expected Azure DevOps webhook payload structure and use it instead of `any`.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2)  ![critical](https://img.shields.io/badge/severity_level-critical-FF3D3D)
```typescript
interface AzureWebhookPayload {
  eventType: string;
  resource?: {
    pullRequestId?: number;
    repository?: { name?: string };
    // ... other expected properties
  };
  resourceContainers?: {
    project?: { id?: string };
    // ... other expected properties
  };
  createdDate?: string; // Or Date if parsed
  id?: string;
  // ... add other relevant fields based on Azure DevOps documentation
}

// ... inside the method
const payload: AzureWebhookPayload = req.body;
```
The webhook payload `req.body` is cast to `any`. This bypasses TypeScript's type checking, increasing the risk of runtime errors due to unexpected payload structures or accessing non-existent properties (e.g., `payload?.resource?.pullRequestId`). Define a specific interface or type for the expected Azure DevOps webhook payload structure and use it instead of `any`.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:10:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
+            const eventType = payload?.eventType as string;
+
+            if (!eventType) {
+                this.logger.log({
+                    message: 'Webhook Azure DevOps recebido sem eventType',
+                    context: AzureReposController.name,
+                    metadata: { payload },
+                });
+                return res
+                    .status(HttpStatus.BAD_REQUEST)
+                    .send('Evento não reconhecido');
+            }
+
+            // Verificar duplicação
+            const isDuplicate = await this.isDuplicateRequest(payload, req);
+            if (isDuplicate) {
+                return res
+                    .status(HttpStatus.OK)
+                    .send('Webhook already processed');
+            }
+
+            // Executa o processamento de forma assíncrona após enviar a resposta
+            res.status(HttpStatus.OK).send('Webhook received');
+
+            setImmediate(() => {
+                this.logger.log({
+                    message: `Webhook received, ${eventType}`,
+                    context: AzureReposController.name,
+                    metadata: {
+                        event: eventType,
+                        repositoryName: payload?.resource?.repository?.name,
+                        pullRequestId: payload?.resource?.pullRequestId,
+                        projectId: payload?.resourceContainers?.project?.id,
+                    },
+                });
+
+                this.receiveWebhookUseCase.execute({
+                    payload,
+                    event: eventType,
+                    platformType: PlatformType.AZURE_REPOS,
+                });
+            });
+        } catch (error) {
             this.logger.error({
-                message: 'Webhook Azure DevOps Not Token Valid',
+                message: 'Error processing webhook',
                 context: AzureReposController.name,
+                error: error,
             });
-            return res.status(403).send('Unauthorized');
+
+            // Garante que a resposta é enviada mesmo em caso de erro
+            if (!res.headersSent) {
+                return res.status(HttpStatus.OK).send('Webhook received');
+            }
         }
+    }
 
-        const payload = req.body as any;
-        const eventType = payload?.eventType as string;
+    private async isDuplicateRequest(
+        payload: any,
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2)  ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
// Assuming AzureWebhookPayload interface is defined as suggested previously
private async isDuplicateRequest(
    payload: AzureWebhookPayload,
    req: Request,
): Promise<boolean> {
```
The `payload` parameter in `isDuplicateRequest` is typed as `any`. Similar to the `handleWebhook` method, this weakens type safety. Use the same specific interface or type defined for the webhook payload here.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:11:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Kody Rules](https://img.shields.io/badge/Kody_Rules-4527A0) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
private readonly cacheService: CacheService
```
The `cacheService` property lacks a `readonly` modifier. Since this property is initialized in the constructor and is not reassigned elsewhere, it should be marked as `readonly` to prevent accidental modification. This adheres to Kody Rule 'ddba6511-3bac-44c0-9bd6-d0ae4f2ab0d1'.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:11:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,63 +4,122 @@ import { PinoLoggerService } from '../../adapters/services/logger/pino.service';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
 import { ReceiveWebhookUseCase } from '@/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case';
 import { validateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
+import { createHash } from 'crypto';
+import { CacheService } from '@/shared/utils/cache/cache.service';
 
 @Controller('azure-repos')
 export class AzureReposController {
     constructor(
         private readonly receiveWebhookUseCase: ReceiveWebhookUseCase,
         private logger: PinoLoggerService,
-    ) { }
+        private cacheService: CacheService,
+    ) {}
 
     @Post('/webhook')
-    handleWebhook(@Req() req: Request, @Res() res: Response) {
-        const encrypted = req.
-            query.token as string;
+    async handleWebhook(@Req() req: Request, @Res() res: Response) {
+        try {
+            const encrypted = req.query.token as string;
 
-        if (!validateWebhookToken(encrypted)) {
+            if (!validateWebhookToken(encrypted)) {
+                this.logger.error({
+                    message: 'Webhook Azure DevOps Not Token Valid',
+                    context: AzureReposController.name,
+                });
+                return res.status(403).send('Unauthorized');
+            }
+
+            const payload = req.body as any;
+            const eventType = payload?.eventType as string;
+
+            if (!eventType) {
+                this.logger.log({
+                    message: 'Webhook Azure DevOps recebido sem eventType',
+                    context: AzureReposController.name,
+                    metadata: { payload },
+                });
+                return res
+                    .status(HttpStatus.BAD_REQUEST)
+                    .send('Evento não reconhecido');
+            }
+
+            // Verificar duplicação
+            const isDuplicate = await this.isDuplicateRequest(payload, req);
+            if (isDuplicate) {
+                return res
+                    .status(HttpStatus.OK)
+                    .send('Webhook already processed');
+            }
+
+            // Executa o processamento de forma assíncrona após enviar a resposta
+            res.status(HttpStatus.OK).send('Webhook received');
+
+            setImmediate(() => {
+                this.logger.log({
+                    message: `Webhook received, ${eventType}`,
+                    context: AzureReposController.name,
+                    metadata: {
+                        event: eventType,
+                        repositoryName: payload?.resource?.repository?.name,
+                        pullRequestId: payload?.resource?.pullRequestId,
+                        projectId: payload?.resourceContainers?.project?.id,
+                    },
+                });
+
+                this.receiveWebhookUseCase.execute({
+                    payload,
+                    event: eventType,
+                    platformType: PlatformType.AZURE_REPOS,
+                });
+            });
+        } catch (error) {
             this.logger.error({
-                message: 'Webhook Azure DevOps Not Token Valid',
+                message: 'Error processing webhook',
                 context: AzureReposController.name,
+                error: error,
             });
-            return res.status(403).send('Unauthorized');
+
+            // Garante que a resposta é enviada mesmo em caso de erro
+            if (!res.headersSent) {
+                return res.status(HttpStatus.OK).send('Webhook received');
+            }
         }
+    }
 
-        const payload = req.body as any;
-        const eventType = payload?.eventType as string;
+    private async isDuplicateRequest(
+        payload: any,
path: src/core/infrastructure/http/controllers/azureRepos.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2)  ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
// Assuming AzureWebhookPayload interface is defined as suggested previously
private async isDuplicateRequest(
    payload: AzureWebhookPayload,
    req: Request,
): Promise<boolean> {
```
The `payload` parameter in `isDuplicateRequest` is typed as `any`. Similar to the `handleWebhook` method, this weakens type safety. Use the same specific interface or type defined for the webhook payload here.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:11:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +38,17 @@ export class GithubController {
         const event = req.headers['x-github-event'] as string;
         const payload = req.body as any;
 
+        if (event === 'pull_request') {
+            if (
+                payload?.action !== 'opened' &&
+                payload?.action !== 'synchronize' &&
+                payload?.action !== 'closed' &&
+                payload?.action !== 'reopened'
+            ) {
+                return res.status(HttpStatus.OK).send('Webhook received');
+            }
path: src/core/infrastructure/http/controllers/github.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Refactoring](https://img.shields.io/badge/Refactoring-304FFE) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
private isValidPullRequestAction(action?: string): boolean { return action === 'opened' || action === 'synchronize' || action === 'closed' || action === 'reopened'; }

if (event === 'pull_request' && !this.isValidPullRequestAction(payload?.action)) { return res.status(HttpStatus.OK).send('Webhook received'); }
```
Consider moving the pull request action check to a separate method for better code organization and maintainability.  Ensure the new method has explicit type definitions for parameters and return values as per Kody Rules.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:11:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +38,17 @@ export class GithubController {
         const event = req.headers['x-github-event'] as string;
         const payload = req.body as any;
path: src/core/infrastructure/http/controllers/github.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
interface GitHubPayload {
  action?: string;
  installation?: { id: number };
  repository?: { name: string };
  pull_request?: { number: number; merged: boolean; };
}
const payload: GitHubPayload = req.body;
```
Add type safety for the payload parameter to avoid potential runtime errors and improve code maintainability.  'any' types should be avoided according to Kody Rules.  Explicitly define the expected structure of the GitHub payload.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:11:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,6 +38,17 @@ export class GithubController {
         const event = req.headers['x-github-event'] as string;
         const payload = req.body as any;
path: src/core/infrastructure/http/controllers/github.controller.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Error Handling](https://img.shields.io/badge/Error_Handling-FF6D00) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
interface GitHubPayload { readonly action?: string; readonly installation?: { readonly id: number }; readonly repository?: { readonly name: string }; } const payload = req.body as GitHubPayload;
```
Add type safety for the payload parameter to avoid potential runtime errors. The current 'any' type could lead to undefined behavior. Also, ensure all properties of the interface are readonly to adhere to Kody Rule regarding immutability.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-02T18:11:30Z
author_association: CONTRIBUTOR

PR comments:
user: login: kody-ai[bot]
created_at: 2025-05-02T18:04:15Z
body: ## User License not found! 😢

To perform the review, ask the admin to add a subscription for your user in [subscription management](https://app.kodus.io/settings/subscription).

<!-- kody-codereview -->
author_association: CONTRIBUTOR
user: login: sartorijr92
created_at: 2025-05-02T18:08:35Z
body: @kody start-review
author_association: CONTRIBUTOR
user: login: kody-ai[bot]
created_at: 2025-05-02T18:08:41Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ❌ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ❌ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ❌ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: CONTRIBUTOR
user: login: kody-ai[bot]
created_at: 2025-05-02T18:24:13Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ❌ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ❌ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ❌ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: CONTRIBUTOR