mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-11 10:59:37 -06:00
- 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
294 lines
7.1 KiB
Plaintext
294 lines
7.1 KiB
Plaintext
---
|
|
description: Service layer patterns for Evolution API
|
|
globs:
|
|
- "src/api/services/**/*.ts"
|
|
- "src/api/integrations/**/services/*.ts"
|
|
alwaysApply: false
|
|
---
|
|
|
|
# Evolution API Service Rules
|
|
|
|
## Service Structure Pattern
|
|
|
|
### Standard Service Class
|
|
```typescript
|
|
export class ExampleService {
|
|
constructor(private readonly waMonitor: WAMonitoringService) {}
|
|
|
|
private readonly logger = new Logger('ExampleService');
|
|
|
|
public async create(instance: InstanceDto, data: ExampleDto) {
|
|
await this.waMonitor.waInstances[instance.instanceName].setData(data);
|
|
return { example: { ...instance, data } };
|
|
}
|
|
|
|
public async find(instance: InstanceDto): Promise<ExampleDto> {
|
|
try {
|
|
const result = await this.waMonitor.waInstances[instance.instanceName].findData();
|
|
|
|
if (Object.keys(result).length === 0) {
|
|
throw new Error('Data not found');
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
return null; // Evolution pattern - return null on error
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Dependency Injection Pattern
|
|
|
|
### Constructor Pattern
|
|
```typescript
|
|
// CORRECT - Evolution API pattern
|
|
constructor(
|
|
private readonly waMonitor: WAMonitoringService,
|
|
private readonly prismaRepository: PrismaRepository,
|
|
private readonly configService: ConfigService,
|
|
) {}
|
|
|
|
// INCORRECT - Don't use
|
|
constructor(waMonitor, prismaRepository, configService) {} // ❌ No types
|
|
```
|
|
|
|
## Logger Pattern
|
|
|
|
### Standard Logger Usage
|
|
```typescript
|
|
// CORRECT - Evolution API pattern
|
|
private readonly logger = new Logger('ServiceName');
|
|
|
|
// Usage
|
|
this.logger.log('Operation started');
|
|
this.logger.error('Operation failed', error);
|
|
|
|
// INCORRECT
|
|
console.log('Operation started'); // ❌ Use Logger
|
|
```
|
|
|
|
## WAMonitor Integration Pattern
|
|
|
|
### Instance Access Pattern
|
|
```typescript
|
|
// CORRECT - Standard pattern
|
|
public async operation(instance: InstanceDto, data: DataDto) {
|
|
await this.waMonitor.waInstances[instance.instanceName].performAction(data);
|
|
return { result: { ...instance, data } };
|
|
}
|
|
|
|
// Instance validation
|
|
const waInstance = this.waMonitor.waInstances[instance.instanceName];
|
|
if (!waInstance) {
|
|
throw new NotFoundException('Instance not found');
|
|
}
|
|
```
|
|
|
|
## Error Handling Pattern
|
|
|
|
### Try-Catch Pattern
|
|
```typescript
|
|
// CORRECT - Evolution API pattern
|
|
public async find(instance: InstanceDto): Promise<DataDto> {
|
|
try {
|
|
const result = await this.waMonitor.waInstances[instance.instanceName].findData();
|
|
|
|
if (Object.keys(result).length === 0) {
|
|
throw new Error('Data not found');
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
this.logger.error('Find operation failed', error);
|
|
return null; // Return null on error (Evolution pattern)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Cache Integration Pattern
|
|
|
|
### Cache Service Usage
|
|
```typescript
|
|
export class CacheAwareService {
|
|
constructor(
|
|
private readonly cache: CacheService,
|
|
private readonly chatwootCache: CacheService,
|
|
private readonly baileysCache: CacheService,
|
|
) {}
|
|
|
|
public async getCachedData(key: string): Promise<any> {
|
|
const cached = await this.cache.get(key);
|
|
if (cached) return cached;
|
|
|
|
const data = await this.fetchFromSource(key);
|
|
await this.cache.set(key, data, 300); // 5 min TTL
|
|
return data;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Integration Service Patterns
|
|
|
|
### Chatbot Service Base Pattern
|
|
```typescript
|
|
export class ChatbotService extends BaseChatbotService<BotType, SettingsType> {
|
|
constructor(
|
|
waMonitor: WAMonitoringService,
|
|
prismaRepository: PrismaRepository,
|
|
configService: ConfigService,
|
|
) {
|
|
super(waMonitor, prismaRepository, 'ChatbotService', configService);
|
|
}
|
|
|
|
protected async processBot(
|
|
waInstance: any,
|
|
remoteJid: string,
|
|
bot: BotType,
|
|
session: any,
|
|
settings: any,
|
|
content: string,
|
|
): Promise<void> {
|
|
// Implementation
|
|
}
|
|
}
|
|
```
|
|
|
|
### Channel Service Pattern
|
|
```typescript
|
|
export class ChannelService extends ChannelStartupService {
|
|
constructor(
|
|
configService: ConfigService,
|
|
eventEmitter: EventEmitter2,
|
|
prismaRepository: PrismaRepository,
|
|
cache: CacheService,
|
|
chatwootCache: CacheService,
|
|
baileysCache: CacheService,
|
|
) {
|
|
super(configService, eventEmitter, prismaRepository, cache, chatwootCache, baileysCache);
|
|
}
|
|
|
|
public readonly logger = new Logger('ChannelService');
|
|
public client: WASocket;
|
|
public readonly instance: wa.Instance = {};
|
|
}
|
|
```
|
|
|
|
## Service Initialization Pattern
|
|
|
|
### Service Registration
|
|
```typescript
|
|
// In server.module.ts pattern
|
|
export const templateService = new TemplateService(
|
|
waMonitor,
|
|
prismaRepository,
|
|
configService,
|
|
);
|
|
|
|
export const settingsService = new SettingsService(waMonitor);
|
|
```
|
|
|
|
## Async Operation Patterns
|
|
|
|
### Promise Handling
|
|
```typescript
|
|
// CORRECT - Evolution API pattern
|
|
public async sendMessage(instance: InstanceDto, data: MessageDto) {
|
|
const waInstance = this.waMonitor.waInstances[instance.instanceName];
|
|
return await waInstance.sendMessage(data);
|
|
}
|
|
|
|
// INCORRECT - Don't use .then()
|
|
public sendMessage(instance: InstanceDto, data: MessageDto) {
|
|
return this.waMonitor.waInstances[instance.instanceName]
|
|
.sendMessage(data)
|
|
.then(result => result); // ❌ Use async/await
|
|
}
|
|
```
|
|
|
|
## Configuration Access Pattern
|
|
|
|
### Config Service Usage
|
|
```typescript
|
|
// CORRECT - Evolution API pattern
|
|
const serverConfig = this.configService.get<HttpServer>('SERVER');
|
|
const authConfig = this.configService.get<Auth>('AUTHENTICATION');
|
|
const dbConfig = this.configService.get<Database>('DATABASE');
|
|
|
|
// Type-safe configuration access
|
|
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED) {
|
|
// Chatwoot logic
|
|
}
|
|
```
|
|
|
|
## Event Emission Pattern
|
|
|
|
### EventEmitter2 Usage
|
|
```typescript
|
|
// CORRECT - Evolution API pattern
|
|
this.eventEmitter.emit(Events.INSTANCE_CREATE, {
|
|
instanceName: instance.name,
|
|
status: 'created',
|
|
});
|
|
|
|
// Chatwoot event pattern
|
|
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
|
|
this.chatwootService.eventWhatsapp(
|
|
Events.STATUS_INSTANCE,
|
|
{ instanceName: this.instance.name },
|
|
{
|
|
instance: this.instance.name,
|
|
status: 'created',
|
|
},
|
|
);
|
|
}
|
|
```
|
|
|
|
## Service Method Naming
|
|
|
|
### Standard Method Names
|
|
- `create()` - Create new resource
|
|
- `find()` - Find single resource
|
|
- `findAll()` - Find multiple resources
|
|
- `update()` - Update resource
|
|
- `delete()` - Delete resource
|
|
- `fetch*()` - Fetch from external API
|
|
- `send*()` - Send data/messages
|
|
- `process*()` - Process data
|
|
|
|
## Service Testing Pattern
|
|
|
|
### Unit Test Structure
|
|
```typescript
|
|
describe('ExampleService', () => {
|
|
let service: ExampleService;
|
|
let waMonitor: jest.Mocked<WAMonitoringService>;
|
|
let prismaRepository: jest.Mocked<PrismaRepository>;
|
|
|
|
beforeEach(() => {
|
|
const mockWaMonitor = {
|
|
waInstances: {
|
|
'test-instance': {
|
|
performAction: jest.fn(),
|
|
},
|
|
},
|
|
};
|
|
|
|
service = new ExampleService(
|
|
mockWaMonitor as any,
|
|
prismaRepository,
|
|
configService,
|
|
);
|
|
});
|
|
|
|
it('should perform action successfully', async () => {
|
|
const instance = { instanceName: 'test-instance' };
|
|
const data = { test: 'data' };
|
|
|
|
const result = await service.create(instance, data);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(waMonitor.waInstances['test-instance'].performAction).toHaveBeenCalledWith(data);
|
|
});
|
|
});
|
|
``` |