Contact Service
Gerencia operações relacionadas aos contatos de clientes, incluindo cadastro e atualização.
Visão Geral
O ContactService é responsável por:
- Cadastrar novos contatos vinculados a clientes
- Atualizar informações de contatos existentes
- Validar dados de contatos (CPF, CNPJ, email, telefone, etc.)
Contatos são pessoas ou entidades associadas a um cliente, como representantes comerciais, compradores, ou pontos de contato específicos.
Localização: services/contact/contact.service.ts
Endpoints Utilizados
Base URL
Todos os endpoints são relativos à base URL configurada em services/api.service.ts
Métodos
registerContact(clientId, contact)
Cadastra um novo contato vinculado a um cliente.
Endpoint: POST /cliente/contato/{clientId}
Parâmetros:
clientId(string): ID do cliente ao qual o contato será vinculadocontact(ContactRegisterSchema): Dados do contato
ContactRegisterSchema:
type ContactRegisterSchema = {
nomeContato: string;
cargo?: string | null;
cpf?: string | null;
cnpj?: string | null;
inscEstad?: string | null;
celular?: string | null;
telefone?: string | null;
email?: string | null;
cep?: string | null;
uf?: string | null;
cidade?: string | null;
bairro?: string | null;
endereco?: string | null;
numeroEndereco?: string | null;
complemento?: string | null;
};
Body: JSON com os dados do contato
Retorno:
Promise<Contact>; // Contato criado com ID
Exemplo de uso:
import { contactService } from "@/services/contact/contact.service.instance";
// Cadastrar contato simples
const simpleContact = await contactService.registerContact("cliente-id-123", {
nomeContato: "João Silva",
cargo: "Gerente de Compras",
email: "joao.silva@empresa.com",
telefone: "(11) 98888-8888",
});
// Cadastrar contato completo
const fullContact = await contactService.registerContact("cliente-id-456", {
nomeContato: "Maria Santos",
cargo: "Diretora Comercial",
cpf: "123.456.789-00",
email: "maria@empresa.com",
celular: "(11) 99999-9999",
telefone: "(11) 3333-3333",
cep: "01000-000",
uf: "SP",
cidade: "São Paulo",
bairro: "Centro",
endereco: "Rua das Flores",
numeroEndereco: "100",
complemento: "Sala 10",
});
console.log("Contato criado:", fullContact.id);
Tratamento de erros:
- Lança
Errorse o status da resposta não for 2xx - Mensagem:
ContactService.registerContact failed: {status} - Validações são aplicadas antes da chamada (via Zod schema)
contactUpdate(contactId, contact)
Atualiza um contato existente.
Endpoint: PUT /cliente/contato/{contactId}
Parâmetros:
contactId(string): ID do contato a ser atualizadocontact(ContactRegisterSchema): Dados do contato (pode ser parcial)
Body: JSON com os dados do contato a serem atualizados
Retorno:
Promise<Contact>; // Contato atualizado
Exemplo de uso:
// Atualizar telefone e email
const updatedContact = await contactService.contactUpdate("contato-id-789", {
nomeContato: "João Silva",
telefone: "(11) 97777-7777",
email: "joao.novo@empresa.com",
});
// Atualizar cargo
const updatedRole = await contactService.contactUpdate("contato-id-790", {
nomeContato: "Maria Santos",
cargo: "CEO",
});
// Atualizar endereço completo
const updatedAddress = await contactService.contactUpdate("contato-id-791", {
nomeContato: "Pedro Costa",
cep: "02000-000",
uf: "RJ",
cidade: "Rio de Janeiro",
bairro: "Copacabana",
endereco: "Av. Atlântica",
numeroEndereco: "500",
complemento: "Apt 201",
});
Tratamento de erros:
- Lança
Errorse o status da resposta não for 2xx - Mensagem:
ContactService.contactUpdate failed: {status}
Estruturas de Dados
Interface Contact
interface Contact {
id: string;
idOrganization: string;
nomeContato: string;
cargo?: string;
cpf?: string;
cnpj?: string;
inscEstad?: string;
celular?: string;
telefone?: string;
email?: string;
cep?: string;
uf?: string;
cidade?: string;
bairro?: string;
endereco?: string;
numeroEndereco?: string;
complemento?: string;
}
Validações
As máscaras dos campos que fazem uso, como celular, telefone, cpf, cnpj e cep, são limpas antes de serem enviados para a API. Isso é feito no transform do Zod, por meio de funções helpers utilizadas no schema.
helpers/zod.ts
export const nullableStringWithRegex = ({
regex,
message,
}: NullableStringWithRegexParams) =>
z.preprocess(
(value) => (value === "" ? null : value),
z
.string()
.regex(regex, { message })
.transform((value) => value.replace(/\D/g, ""))
.nullable()
.optional(),
);
export const nullableString = z.preprocess(
(value) => (value === "" ? null : value),
z
.string()
.trim()
.transform((value) => value.replace(/\D/g, ""))
.nullable()
.optional(),
);
Regras de Validação (via Zod Schema)
Nome do Contato
- ✅ Obrigatório
- ✅ Deve ser string não vazia
- ✅ Espaços em branco são removidos (trim)
CPF
- ✅ Opcional
- ✅ Formatação é limpa no transform do Zod antes de ir para a API
CNPJ
- ✅ Opcional
- ✅ Formatação é limpa no transform do Zod antes de ir para a API
Celular / Telefone
- ✅ Opcional
- ✅ Formatação é limpa no transform do Zod antes de ir para a API
Email
- ✅ Opcional
- ✅ Se informado, deve ser um email válido
- ✅ Validação via Zod email validator
CEP
- ✅ Opcional
- ✅ Formatação é limpa no transform do Zod antes de ir para a API
Casos de Uso
Cadastrar Contato Mínimo
// Apenas nome e email (mínimo necessário)
const minimalContact = await contactService.registerContact("cliente-id", {
nomeContato: "Ana Costa",
email: "ana@empresa.com",
});
Cadastrar Contato Completo com Todos os Campos
const completeContact = await contactService.registerContact("cliente-id", {
// Dados básicos
nomeContato: "Carlos Mendes",
cargo: "Engenheiro de Vendas",
// Documentos
cpf: "987.654.321-00",
inscEstad: "123456789",
// Contato
email: "carlos@empresa.com",
celular: "(11) 99999-9999",
telefone: "(11) 3344-5566",
// Endereço
cep: "03000-000",
uf: "SP",
cidade: "São Paulo",
bairro: "Vila Mariana",
endereco: "Rua Domingos de Morais",
numeroEndereco: "2564",
complemento: "Conjunto 42",
});
console.log("Contato completo criado:", completeContact);
Helpers de Validação
cpfValidate
Localização: helpers/cpfValidate.ts
// Validar CPF
isValidCpf(cpf: string): boolean
// Aplicar máscara de CPF
cpfMask(cpf: string): string
// Exemplo: '12345678900' -> '123.456.789-00'
cnpjValidate
Localização: helpers/cnpjValidate.ts
// Validar CNPJ
isValidCnpj(cnpj: string): boolean
// Aplicar máscara de CNPJ
cnpjMask(cnpj: string): string
// Exemplo: '12345678000195' -> '12.345.678/0001-95'
Zod Helpers
Localização: helpers/zod.ts
Helpers customizados para validação com Zod:
// String nullable com regex
nullableStringWithRegex({ regex, message });
// String nullable simples
nullableString;
// Email nullable
nullableEmail;
Testes
Localização
services/contact/contact.service.test.ts
Executar Testes
# Teste específico do serviço
npx jest services/contact/contact.service.test.ts
# Modo watch
npx jest services/contact/contact.service.test.ts --watch
# Com coverage
npx jest services/contact/contact.service.test.ts --coverage
Casos de Teste
registerContact
- ✅ Deve chamar endpoint correto com dados corretos
- ✅ Deve lançar erro quando status não-2xx
- ✅ Deve retornar contato quando status 2xx
contactUpdate
- ✅ Deve chamar endpoint correto com dados corretos
- ✅ Deve lançar erro quando status não-2xx
- ✅ Deve retornar contato atualizado quando status 2xx
Exemplo de teste:
import { contactService } from "@/services/contact/contact.service.instance";
import {
contactRegisterMock,
contactResponseMock,
} from "@/__mocks__/contact.mock";
describe("ContactService", () => {
it("deve cadastrar contato com sucesso", async () => {
const result = await contactService.registerContact(
"cliente-id-123",
contactRegisterMock,
);
expect(result).toBeDefined();
expect(result.id).toBeDefined();
expect(result.nomeContato).toBe(contactRegisterMock.nomeContato);
});
});
Mocks
Localização
__mocks__/contact.mock.ts
Dados Disponíveis
// Contato de resposta da API
export const contactResponseMock: Contact = {
id: "contact-id",
idOrganization: "org-id",
nomeContato: "John Doe",
};
// Dados para cadastro
export const contactRegisterMock: ContactRegisterSchema = {
nomeContato: "John Doe",
};
Exemplo de uso em testes:
import {
contactRegisterMock,
contactResponseMock,
} from "@/__mocks__/contact.mock";
// Mock da API
const apiMock = {
post: jest.fn(async () => ({
status: 200,
data: contactResponseMock,
})),
};
const service = new ContactService({ apiWithoutAccessToken: apiMock });
const result = await service.registerContact("client-id", contactRegisterMock);
expect(result).toEqual(contactResponseMock);