Classe Provider
Classe abstrata base que todos os providers devem estender. Fornece implementações comuns e métodos auxiliares.
Definição
export abstract class Provider implements IProvider {
// Propriedades
protected readonly baseUrl: string;
protected readonly timeout: number;
protected readonly token: string;
protected abstract readonly providerName: string;
protected readonly from?: string;
protected readonly customData?: Record<string, unknown>;
// Limites configuráveis
protected readonly maxBatchSize: number;
protected readonly maxMessageLength: number;
// Retry
protected readonly maxRetries: number;
protected readonly retryDelay: number;
// Construtor
public constructor(config: ProviderConfig);
// Métodos protegidos (validação)
protected validatePhone(phone: string): boolean;
protected validatePhoneOrThrow(phone: string): void;
protected validatePhones(phones: string[]): ValidatedPhone;
protected validateFromRequired(): void;
protected validateMessageLength(message: string): void;
protected validateNoUrls(message: string): void;
protected validateBatchSize(validCount: number): void;
protected validateCampaignName(campaignName?: string): void;
protected validatedBatchPhoneNumbers(phoneNumbers: string[]): ValidatedPhone;
// Métodos protegidos (utilitários)
protected buildHeaders(): HeadersInit;
protected normalizePhone(phone: string, internacional?: boolean): string;
protected normalizePhones(phones: string[], internacional?: boolean): string[];
protected async request(url: string, body: unknown): Promise<Response>;
protected handleErrorResponse(status: number, response: any): never;
protected extractMessageId(result: any): string | undefined;
// Retry
protected async withRetry<T>(fn: () => Promise<T>, retries?: number): Promise<T>;
// Métodos públicos
async sendBatch(data: SendBatchMessageDto): Promise<SendBatchMessageResponse>;
abstract send(data: SendMessageDto): Promise<SendMessageResponse>;
}Propriedades
| Propriedade | Tipo | Descrição |
|---|---|---|
baseUrl | string | URL base da API do provider |
timeout | number | Timeout em milissegundos |
token | string | API key ou token de acesso |
providerName | string | Abstract - Nome do provider (obrigatório) |
from | string | Remetente padrão (opcional) |
customData | object | Dados customizados do provider |
maxBatchSize | number | Limite de destinatários por lote (padrão: 1000) |
maxMessageLength | number | Limite de caracteres por mensagem (padrão: 160) |
maxRetries | number | Número de tentativas (padrão: 0) |
retryDelay | number | Delay inicial em ms (padrão: 1000) |
Construtor
public constructor(config: ProviderConfig)O construtor valida:
tokené obrigatóriobaseUrlé obrigatória- Configura limites via
config.data(opcional)
Métodos de Validação
validatePhone()
Valida um número angolano (retorna boolean).
protected validatePhone(phone: string): booleanvalidatePhoneOrThrow()
Valida um número e lança exceção se inválido.
protected validatePhoneOrThrow(phone: string): voidLança: ValidationError
validatePhones()
Valida múltiplos números.
protected validatePhones(phones: string[]): ValidatedPhoneRetorna { valid: string[], invalid: string[] }
validateFromRequired()
Valida se from está configurado (para providers que exigem).
protected validateFromRequired(): voidLança: ConfigurationError
validateMessageLength()
Valida o tamanho da mensagem.
protected validateMessageLength(message: string): voidLança: ValidationError se exceder maxMessageLength
validateNoUrls()
Valida ausência de URLs na mensagem.
protected validateNoUrls(message: string): voidLança: ValidationError se encontrar URL
validateBatchSize()
Valida o tamanho do lote.
protected validateBatchSize(validCount: number): voidLança: ValidationError se exceder maxBatchSize
validateCampaignName()
Valida se campaignName foi fornecido.
protected validateCampaignName(campaignName?: string): voidLança: ValidationError se não fornecido
validatedBatchPhoneNumbers()
Valida e retorna números válidos em uma única operação.
protected validatedBatchPhoneNumbers(phoneNumbers: string[]): ValidatedPhoneLança: ValidationError se não houver números válidos
Métodos Utilitários
buildHeaders()
Constrói os headers da requisição HTTP.
protected buildHeaders(): HeadersInitPadrão: Authorization: Bearer {token}
Sobrescreva para: X-API-Key, Token {token}, etc.
normalizePhone()
Normaliza um número de telefone.
protected normalizePhone(phone: string, internacional?: boolean): stringinternacional = false(padrão) → formato nacional (ex:923000000)internacional = true→ formato internacional (ex:+244923000000)
normalizePhones()
Normaliza múltiplos números.
protected normalizePhones(phones: string[], internacional?: boolean): string[]request()
Executa requisição HTTP com timeout.
protected async request(url: string, body: unknown): Promise<Response>handleErrorResponse()
Converte status HTTP em erro apropriado.
protected handleErrorResponse(status: number, response: any): never| Status | Erro |
|---|---|
| 401, 403 | AuthenticationError |
| 429 | RateLimitError |
| 400 | ValidationError |
| outros | ProviderError |
extractMessageId()
Extrai o ID da mensagem da resposta.
protected extractMessageId(result: any): string | undefinedBusca por: id, messageId, smsId, message_id
Retry Automático
withRetry()
Executa uma função com retry automático.
protected async withRetry<T>(fn: () => Promise<T>, retries?: number): Promise<T>Comportamento:
- Se
maxRetries = 0(padrão), executa apenas uma vez - Se
maxRetries > 0, tenta novamente em caso de falha - Backoff exponencial:
retryDelay,retryDelay * 2,retryDelay * 4... - Não retenta para
ValidationError,AuthenticationError,ConfigurationError
Configuração:
const sms = await createSender("ombala", {
token: "...",
baseUrl: "...",
from: "...",
data: {
maxRetries: 3, // 3 tentativas (total 4)
retryDelay: 1000, // delay inicial: 1s, 2s, 4s
},
});Métodos Públicos
sendBatch()
Implementação base para envio em lote (com retry).
async sendBatch(data: SendBatchMessageDto): Promise<SendBatchMessageResponse>Comportamento: Faz chamadas individuais para cada número usando send().
Sobrescreva quando: Provider suporta batch nativo.
send()
Abstract - Cada provider deve implementar.
abstract send(data: SendMessageDto): Promise<SendMessageResponse>Propriedade providerName (obrigatória)
Todos os providers devem declarar o nome:
export class OmbalaProvider extends Provider {
protected readonly providerName = "ombala";
}Fluxo do sendBatch base
1. validatePhones() → separa válidos/inválidos
2. Se não há válidos → ValidationError
3. Promise.allSettled(valid.map(phone => send()))
4. Coleta sucessos e falhas
5. Retorna SendBatchMessageResponseQuando sobrescrever métodos
| Método | Motivo para sobrescrever |
|---|---|
buildHeaders() | Autenticação diferente |
normalizePhone() | Formato específico do provider |
validateNoUrls() | Provider permite URLs |
sendBatch() | Provider tem batch nativo |
handleErrorResponse() | Códigos de erro diferentes |
extractMessageId() | Campo do ID diferente |