bling-mcp
Version:
API MCP para integração com Bling
219 lines (209 loc) • 6.66 kB
text/typescript
import { blingClient, renovarToken } from '../blingClient.js';
import axios from 'axios';
/**
* Interfaces para Produto (baseado no schema ProdutosDadosDTO)
*/
export interface Produto {
id?: number;
nome: string;
codigo?: string;
preco?: number;
tipo: 'S' | 'P' | 'N';
situacao?: 'A' | 'I';
formato?: 'S' | 'V' | 'E';
descricaoCurta?: string;
imagemURL?: string;
dataValidade?: string;
unidade?: string;
pesoLiquido?: number;
pesoBruto?: number;
volumes?: number;
itensPorCaixa?: number;
gtin?: string;
gtinEmbalagem?: string;
tipoProducao?: 'P' | 'T';
condicao?: 0 | 1 | 2;
freteGratis?: boolean;
marca?: string;
descricaoComplementar?: string;
linkExterno?: string;
observacoes?: string;
descricaoEmbalagemDiscreta?: string;
categoria?: any;
estoque?: any;
fornecedor?: any;
actionEstoque?: 'Z' | 'T';
dimensoes?: any;
tributacao?: any;
midia?: any;
linhaProduto?: any;
estrutura?: any;
camposCustomizados?: any[];
variacoes?: Produto[];
[key: string]: any;
}
export interface ListarProdutosParams {
pagina?: number;
limite?: number;
criterio?: number;
tipo?: string;
idComponente?: number;
dataInclusaoInicial?: string;
dataInclusaoFinal?: string;
dataAlteracaoInicial?: string;
dataAlteracaoFinal?: string;
idCategoria?: number;
idLoja?: number;
nome?: string;
idsProdutos?: number[];
codigos?: string[];
}
export interface ProdutosResponse {
data: Produto[];
pagination: {
page: number;
totalPages: number;
totalItems: number;
limit: number;
};
}
/**
* Lista os produtos cadastrados no Bling
*/
export async function listarProdutos(params?: ListarProdutosParams): Promise<ProdutosResponse> {
try {
console.log('Listando produtos', params);
const response = await blingClient.get('/produtos', { params });
if (response.data && Array.isArray(response.data.data)) {
return {
data: response.data.data as Produto[],
pagination: {
page: response.data.page || 1,
totalPages: response.data.totalPages || 1,
totalItems: response.data.data.length,
limit: response.data.limit || response.data.data.length
}
};
}
if (Array.isArray(response.data)) {
return {
data: response.data,
pagination: {
page: 1,
totalPages: 1,
totalItems: response.data.length,
limit: 50
}
};
}
return {
data: Array.isArray(response.data) ? response.data : (response.data && Array.isArray(response.data.data)) ? response.data.data : [],
pagination: {
page: 1,
totalPages: 1,
totalItems: Array.isArray(response.data) ? response.data.length : (response.data && Array.isArray(response.data.data)) ? response.data.data.length : 0,
limit: 50
}
};
} catch (error) {
return handleApiError(error, 'listarProdutos', params);
}
}
/**
* Obtém um produto pelo ID
*/
export async function obterProduto(id: number | string): Promise<Produto> {
try {
console.log('Obtendo produto', id);
const response = await blingClient.get(`/produtos/${id}`);
if (response.data && response.data.data) {
return response.data.data as Produto;
}
return response.data as Produto;
} catch (error) {
return handleApiError(error, 'obterProduto', { id });
}
}
/**
* Cria um novo produto
*/
export async function criarProduto(produto: Produto): Promise<Produto> {
try {
console.log('Criando produto', produto);
const response = await blingClient.post('/produtos', produto);
if (response.data && response.data.data) {
return response.data.data as Produto;
}
return response.data as Produto;
} catch (error) {
return handleApiError(error, 'criarProduto', produto);
}
}
/**
* Atualiza um produto existente
*/
export async function atualizarProduto(id: number | string, produto: Partial<Produto>): Promise<Produto> {
try {
console.log('Atualizando produto', id, produto);
const response = await blingClient.put(`/produtos/${id}`, produto);
if (response.data && response.data.data) {
return response.data.data as Produto;
}
return response.data as Produto;
} catch (error) {
return handleApiError(error, 'atualizarProduto', { id, produto });
}
}
/**
* Remove um produto pelo ID
*/
export async function excluirProduto(id: number | string): Promise<void> {
try {
console.log('Excluindo produto', id);
await blingClient.delete(`/produtos/${id}`);
console.log('Produto excluído com sucesso');
} catch (error) {
return handleApiError(error, 'excluirProduto', { id });
}
}
/**
* Remove múltiplos produtos pelos IDs
*/
export async function excluirProdutos(idsProdutos: number[]): Promise<void> {
try {
console.log('Excluindo múltiplos produtos', idsProdutos);
await blingClient.delete('/produtos', { params: { idsProdutos } });
console.log('Produtos excluídos com sucesso');
} catch (error) {
return handleApiError(error, 'excluirProdutos', { idsProdutos });
}
}
/**
* Função auxiliar para tratamento de erros da API
*/
async function handleApiError(error: unknown, funcName: string, params: any): Promise<any> {
if (axios.isAxiosError(error) && error.response?.status === 401) {
console.log(`Token expirado (401) em ${funcName}, tentando renovar...`);
try {
await renovarToken();
if (funcName === 'listarProdutos') return listarProdutos(params as ListarProdutosParams);
if (funcName === 'obterProduto') return obterProduto(params.id);
if (funcName === 'criarProduto') return criarProduto(params);
if (funcName === 'atualizarProduto') return atualizarProduto(params.id, params.produto);
if (funcName === 'excluirProduto') return excluirProduto(params.id);
if (funcName === 'excluirProdutos') return excluirProdutos(params.idsProdutos);
throw new Error(`Função desconhecida: ${funcName}`);
} catch (renewError) {
console.error(`Erro ao renovar token em ${funcName}:`, renewError);
throw new Error(`Falha na renovação do token: ${renewError instanceof Error ? renewError.message : 'Erro desconhecido'}`);
}
}
if (axios.isAxiosError(error)) {
console.error(`Erro em ${funcName}:`);
console.error('Status:', error.response?.status);
console.error('Data:', JSON.stringify(error.response?.data, null, 2));
throw new Error(`Erro ao ${funcName}: ${error.response?.data?.message || error.message}`);
}
console.error(`Erro não relacionado ao Axios em ${funcName}:`, error);
throw error;
}