mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-18 19:32:21 -06:00
feat: add project guidelines and configuration files for development standards
- Introduce AGENTS.md for repository guidelines and project structure - Add core development principles in .cursor/rules/core-development.mdc - Establish project-specific context in .cursor/rules/project-context.mdc - Implement Cursor IDE configuration in .cursor/rules/cursor.json - Create specialized rules for controllers, services, DTOs, guards, routes, and integrations - Update .gitignore to exclude unnecessary files - Enhance CLAUDE.md with project overview and common development commands
This commit is contained in:
342
.cursor/rules/specialized-rules/controller-rules.mdc
Normal file
342
.cursor/rules/specialized-rules/controller-rules.mdc
Normal file
@@ -0,0 +1,342 @@
|
||||
---
|
||||
description: Controller patterns for Evolution API
|
||||
globs:
|
||||
- "src/api/controllers/**/*.ts"
|
||||
- "src/api/integrations/**/controllers/*.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Evolution API Controller Rules
|
||||
|
||||
## Controller Structure Pattern
|
||||
|
||||
### Standard Controller Class
|
||||
```typescript
|
||||
export class ExampleController {
|
||||
constructor(private readonly exampleService: ExampleService) {}
|
||||
|
||||
public async createExample(instance: InstanceDto, data: ExampleDto) {
|
||||
return this.exampleService.create(instance, data);
|
||||
}
|
||||
|
||||
public async findExample(instance: InstanceDto) {
|
||||
return this.exampleService.find(instance);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Injection Pattern
|
||||
|
||||
### Service Injection
|
||||
```typescript
|
||||
// CORRECT - Evolution API pattern
|
||||
export class ChatController {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
public async whatsappNumber({ instanceName }: InstanceDto, data: WhatsAppNumberDto) {
|
||||
return await this.waMonitor.waInstances[instanceName].getWhatsAppNumbers(data);
|
||||
}
|
||||
}
|
||||
|
||||
// INCORRECT - Don't inject multiple services when waMonitor is sufficient
|
||||
export class ChatController {
|
||||
constructor(
|
||||
private readonly waMonitor: WAMonitoringService,
|
||||
private readonly prismaRepository: PrismaRepository, // ❌ Unnecessary if waMonitor has access
|
||||
private readonly configService: ConfigService, // ❌ Unnecessary if waMonitor has access
|
||||
) {}
|
||||
}
|
||||
```
|
||||
|
||||
## Method Signature Pattern
|
||||
|
||||
### Instance Parameter Pattern
|
||||
```typescript
|
||||
// CORRECT - Evolution API pattern (destructuring instanceName)
|
||||
public async fetchCatalog({ instanceName }: InstanceDto, data: getCatalogDto) {
|
||||
return await this.waMonitor.waInstances[instanceName].fetchCatalog(instanceName, data);
|
||||
}
|
||||
|
||||
// CORRECT - Alternative pattern for full instance (when using services)
|
||||
public async createTemplate(instance: InstanceDto, data: TemplateDto) {
|
||||
return this.templateService.create(instance, data);
|
||||
}
|
||||
|
||||
// INCORRECT - Don't use generic method names
|
||||
public async methodName(instance: InstanceDto, data: DataDto) { // ❌ Use specific names
|
||||
return this.service.performAction(instance, data);
|
||||
}
|
||||
```
|
||||
|
||||
## WAMonitor Access Pattern
|
||||
|
||||
### Direct WAMonitor Usage
|
||||
```typescript
|
||||
// CORRECT - Standard pattern in controllers
|
||||
export class CallController {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
public async offerCall({ instanceName }: InstanceDto, data: OfferCallDto) {
|
||||
return await this.waMonitor.waInstances[instanceName].offerCall(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Controller Registration Pattern
|
||||
|
||||
### Server Module Registration
|
||||
```typescript
|
||||
// In server.module.ts
|
||||
export const templateController = new TemplateController(templateService);
|
||||
export const businessController = new BusinessController(waMonitor);
|
||||
export const chatController = new ChatController(waMonitor);
|
||||
export const callController = new CallController(waMonitor);
|
||||
```
|
||||
|
||||
## Error Handling in Controllers
|
||||
|
||||
### Let Services Handle Errors
|
||||
```typescript
|
||||
// CORRECT - Let service handle errors
|
||||
public async fetchCatalog(instance: InstanceDto, data: getCatalogDto) {
|
||||
return await this.waMonitor.waInstances[instance.instanceName].fetchCatalog(instance.instanceName, data);
|
||||
}
|
||||
|
||||
// INCORRECT - Don't add try-catch in controllers unless specific handling needed
|
||||
public async fetchCatalog(instance: InstanceDto, data: getCatalogDto) {
|
||||
try {
|
||||
return await this.waMonitor.waInstances[instance.instanceName].fetchCatalog(instance.instanceName, data);
|
||||
} catch (error) {
|
||||
throw error; // ❌ Unnecessary try-catch
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Complex Controller Pattern
|
||||
|
||||
### Instance Controller Pattern
|
||||
```typescript
|
||||
export class InstanceController {
|
||||
constructor(
|
||||
private readonly waMonitor: WAMonitoringService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly prismaRepository: PrismaRepository,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly chatwootService: ChatwootService,
|
||||
private readonly settingsService: SettingsService,
|
||||
private readonly proxyService: ProxyController,
|
||||
private readonly cache: CacheService,
|
||||
private readonly chatwootCache: CacheService,
|
||||
private readonly baileysCache: CacheService,
|
||||
private readonly providerFiles: ProviderFiles,
|
||||
) {}
|
||||
|
||||
private readonly logger = new Logger('InstanceController');
|
||||
|
||||
// Multiple methods handling different aspects
|
||||
public async createInstance(data: InstanceDto) {
|
||||
// Complex instance creation logic
|
||||
}
|
||||
|
||||
public async deleteInstance({ instanceName }: InstanceDto) {
|
||||
// Complex instance deletion logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Channel Controller Pattern
|
||||
|
||||
### Base Channel Controller
|
||||
```typescript
|
||||
export class ChannelController {
|
||||
public prismaRepository: PrismaRepository;
|
||||
public waMonitor: WAMonitoringService;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
this.prisma = prismaRepository;
|
||||
this.monitor = waMonitor;
|
||||
}
|
||||
|
||||
// Getters and setters for dependency access
|
||||
public set prisma(prisma: PrismaRepository) {
|
||||
this.prismaRepository = prisma;
|
||||
}
|
||||
|
||||
public get prisma() {
|
||||
return this.prismaRepository;
|
||||
}
|
||||
|
||||
public set monitor(waMonitor: WAMonitoringService) {
|
||||
this.waMonitor = waMonitor;
|
||||
}
|
||||
|
||||
public get monitor() {
|
||||
return this.waMonitor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extended Channel Controller
|
||||
```typescript
|
||||
export class EvolutionController extends ChannelController implements ChannelControllerInterface {
|
||||
private readonly logger = new Logger('EvolutionController');
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
integrationEnabled: boolean;
|
||||
|
||||
public async receiveWebhook(data: any) {
|
||||
const numberId = data.numberId;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhookEvolution -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Chatbot Controller Pattern
|
||||
|
||||
### Base Chatbot Controller
|
||||
```typescript
|
||||
export abstract class BaseChatbotController<BotType = any, BotData extends BaseChatbotDto = BaseChatbotDto>
|
||||
extends ChatbotController
|
||||
implements ChatbotControllerInterface
|
||||
{
|
||||
public readonly logger: Logger;
|
||||
integrationEnabled: boolean;
|
||||
|
||||
// Abstract methods to be implemented
|
||||
protected abstract readonly integrationName: string;
|
||||
protected abstract processBot(/* parameters */): Promise<void>;
|
||||
protected abstract getFallbackBotId(settings: any): string | undefined;
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
// Base implementation methods
|
||||
public async createBot(instance: InstanceDto, data: BotData) {
|
||||
// Common bot creation logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Method Naming Conventions
|
||||
|
||||
### Standard Method Names
|
||||
- `create*()` - Create operations
|
||||
- `find*()` - Find operations
|
||||
- `fetch*()` - Fetch from external APIs
|
||||
- `send*()` - Send operations
|
||||
- `receive*()` - Receive webhook/data
|
||||
- `handle*()` - Handle specific actions
|
||||
- `offer*()` - Offer services (like calls)
|
||||
|
||||
## Return Patterns
|
||||
|
||||
### Direct Return Pattern
|
||||
```typescript
|
||||
// CORRECT - Direct return from service
|
||||
public async createTemplate(instance: InstanceDto, data: TemplateDto) {
|
||||
return this.templateService.create(instance, data);
|
||||
}
|
||||
|
||||
// CORRECT - Direct return from waMonitor
|
||||
public async offerCall({ instanceName }: InstanceDto, data: OfferCallDto) {
|
||||
return await this.waMonitor.waInstances[instanceName].offerCall(data);
|
||||
}
|
||||
```
|
||||
|
||||
## Controller Testing Pattern
|
||||
|
||||
### Unit Test Structure
|
||||
```typescript
|
||||
describe('ExampleController', () => {
|
||||
let controller: ExampleController;
|
||||
let service: jest.Mocked<ExampleService>;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockService = {
|
||||
create: jest.fn(),
|
||||
find: jest.fn(),
|
||||
};
|
||||
|
||||
controller = new ExampleController(mockService as any);
|
||||
service = mockService as any;
|
||||
});
|
||||
|
||||
describe('createExample', () => {
|
||||
it('should call service create method', async () => {
|
||||
const instance = { instanceName: 'test' };
|
||||
const data = { test: 'data' };
|
||||
const expectedResult = { success: true };
|
||||
|
||||
service.create.mockResolvedValue(expectedResult);
|
||||
|
||||
const result = await controller.createExample(instance, data);
|
||||
|
||||
expect(service.create).toHaveBeenCalledWith(instance, data);
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Interface Implementation
|
||||
|
||||
### Controller Interface Pattern
|
||||
```typescript
|
||||
export interface ChannelControllerInterface {
|
||||
integrationEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface ChatbotControllerInterface {
|
||||
integrationEnabled: boolean;
|
||||
createBot(instance: InstanceDto, data: any): Promise<any>;
|
||||
findBot(instance: InstanceDto): Promise<any>;
|
||||
// ... other methods
|
||||
}
|
||||
```
|
||||
|
||||
## Controller Organization
|
||||
|
||||
### File Naming Convention
|
||||
- `*.controller.ts` - Main controllers
|
||||
- `*/*.controller.ts` - Integration-specific controllers
|
||||
|
||||
### Method Organization
|
||||
1. Constructor
|
||||
2. Public methods (alphabetical order)
|
||||
3. Private methods (if any)
|
||||
|
||||
### Import Organization
|
||||
```typescript
|
||||
// DTOs first
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { ExampleDto } from '@api/dto/example.dto';
|
||||
|
||||
// Services
|
||||
import { ExampleService } from '@api/services/example.service';
|
||||
|
||||
// Types
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
```
|
||||
Reference in New Issue
Block a user