Pular para o conteúdo principal

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á vinculado
  • contact (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 Error se 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 atualizado
  • contact (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 Error se 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);