fix: throttle Graphs backfill by Tiny request
All checks were successful
Build and Deploy / build-and-push (push) Successful in 45s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 45s
This commit is contained in:
@@ -12,7 +12,10 @@ type CachedOrderDetails = {
|
||||
whatsappVendedor: string;
|
||||
};
|
||||
|
||||
type OrderDetailsResult = Omit<CachedOrderDetails, 'expiresAt'>;
|
||||
|
||||
const orderDetailsCache = new Map<string, CachedOrderDetails>();
|
||||
const inFlightOrderDetails = new Map<string, Promise<OrderDetailsResult | null>>();
|
||||
const configuredCacheTtlMs = Number(process.env.TINY_ORDER_DETAILS_CACHE_TTL_MS || 120000);
|
||||
const ORDER_DETAILS_CACHE_TTL_MS = Number.isFinite(configuredCacheTtlMs) ? configuredCacheTtlMs : 120000;
|
||||
|
||||
@@ -47,6 +50,88 @@ function setCachedOrderDetails(orderId: string, cached: Omit<CachedOrderDetails,
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchOrderDetailsFromTiny(orderId: string, tinyApiToken: string): Promise<OrderDetailsResult | null> {
|
||||
console.log(`Fetching full details for Order ID: ${orderId} from Tiny API...`);
|
||||
const params = new URLSearchParams();
|
||||
params.append('token', tinyApiToken);
|
||||
params.append('id', orderId);
|
||||
params.append('formato', 'JSON');
|
||||
|
||||
const apiResponse = await axios.post('https://api.tiny.com.br/api2/pedido.obter.php', params, {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
|
||||
if (apiResponse.data?.retorno?.status !== 'OK') {
|
||||
console.error('Tiny API returned an error:', apiResponse.data?.retorno?.erros || 'Unknown error');
|
||||
return null;
|
||||
}
|
||||
|
||||
const fullOrderDetails = apiResponse.data.retorno.pedido;
|
||||
const statusProcessamento = apiResponse.data.retorno.status_processamento || "";
|
||||
let whatsappVendedor = "";
|
||||
console.log(`Successfully fetched order details! Found phone: ${fullOrderDetails.cliente?.celular || fullOrderDetails.cliente?.fone || 'None'}`);
|
||||
|
||||
// Seller details are off by default because this is an extra Tiny API call per order.
|
||||
const idVendedor = fullOrderDetails.id_vendedor;
|
||||
if (process.env.TINY_FETCH_SELLER_DETAILS === 'true' && idVendedor && idVendedor !== "0") {
|
||||
console.log(`Fetching seller details for Seller ID: ${idVendedor}...`);
|
||||
const vendorParams = new URLSearchParams();
|
||||
vendorParams.append('token', tinyApiToken);
|
||||
vendorParams.append('id', idVendedor);
|
||||
vendorParams.append('formato', 'JSON');
|
||||
|
||||
const vendorResponse = await axios.post('https://api.tiny.com.br/api2/contato.obter.php', vendorParams, {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
|
||||
if (vendorResponse.data?.retorno?.status === 'OK') {
|
||||
const contato = vendorResponse.data.retorno.contato;
|
||||
whatsappVendedor = contato?.celular || contato?.fone || contato?.telefone || "";
|
||||
console.log(`Successfully fetched seller WhatsApp: ${whatsappVendedor || 'None'}`);
|
||||
} else {
|
||||
console.error('Failed to fetch seller details:', JSON.stringify(vendorResponse.data?.retorno?.erros || 'Unknown API error'));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fullOrderDetails,
|
||||
statusProcessamento,
|
||||
whatsappVendedor
|
||||
};
|
||||
}
|
||||
|
||||
async function getOrFetchOrderDetails(orderId: string, tinyApiToken: string): Promise<OrderDetailsResult | null> {
|
||||
const cachedDetails = getCachedOrderDetails(orderId);
|
||||
if (cachedDetails) {
|
||||
console.log(`Using cached order details for Order ID: ${orderId}.`);
|
||||
return {
|
||||
fullOrderDetails: cachedDetails.fullOrderDetails,
|
||||
statusProcessamento: cachedDetails.statusProcessamento,
|
||||
whatsappVendedor: cachedDetails.whatsappVendedor
|
||||
};
|
||||
}
|
||||
|
||||
const existingFetch = inFlightOrderDetails.get(orderId);
|
||||
if (existingFetch) {
|
||||
console.log(`Waiting for in-flight Tiny fetch for Order ID: ${orderId}.`);
|
||||
return existingFetch;
|
||||
}
|
||||
|
||||
const fetchPromise = fetchOrderDetailsFromTiny(orderId, tinyApiToken)
|
||||
.then(result => {
|
||||
if (result) {
|
||||
setCachedOrderDetails(orderId, result);
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.finally(() => {
|
||||
inFlightOrderDetails.delete(orderId);
|
||||
});
|
||||
|
||||
inFlightOrderDetails.set(orderId, fetchPromise);
|
||||
return fetchPromise;
|
||||
}
|
||||
|
||||
export const handleTinyOrderUpdate = async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
// 1. Security Check: Verify token from Tiny
|
||||
@@ -84,58 +169,13 @@ export const handleTinyOrderUpdate = async (req: Request, res: Response): Promis
|
||||
let statusProcessamento = "";
|
||||
let whatsappVendedor = "";
|
||||
|
||||
const cachedDetails = getCachedOrderDetails(String(orderId));
|
||||
if (cachedDetails) {
|
||||
fullOrderDetails = cachedDetails.fullOrderDetails;
|
||||
statusProcessamento = cachedDetails.statusProcessamento;
|
||||
whatsappVendedor = cachedDetails.whatsappVendedor;
|
||||
console.log(`Using cached order details for Order ID: ${orderId}.`);
|
||||
} else if (tinyApiToken) {
|
||||
if (tinyApiToken) {
|
||||
try {
|
||||
console.log(`Fetching full details for Order ID: ${orderId} from Tiny API...`);
|
||||
const params = new URLSearchParams();
|
||||
params.append('token', tinyApiToken);
|
||||
params.append('id', orderId);
|
||||
params.append('formato', 'JSON');
|
||||
|
||||
const apiResponse = await axios.post('https://api.tiny.com.br/api2/pedido.obter.php', params, {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
|
||||
if (apiResponse.data?.retorno?.status === 'OK') {
|
||||
fullOrderDetails = apiResponse.data.retorno.pedido;
|
||||
statusProcessamento = apiResponse.data.retorno.status_processamento || "";
|
||||
console.log(`Successfully fetched order details! Found phone: ${fullOrderDetails.cliente?.celular || fullOrderDetails.cliente?.fone || 'None'}`);
|
||||
|
||||
// OPTION B: Fetch Vendor's WhatsApp
|
||||
const idVendedor = fullOrderDetails.id_vendedor;
|
||||
if (process.env.TINY_FETCH_SELLER_DETAILS === 'true' && idVendedor && idVendedor !== "0") {
|
||||
console.log(`Fetching seller details for Seller ID: ${idVendedor}...`);
|
||||
const vendorParams = new URLSearchParams();
|
||||
vendorParams.append('token', tinyApiToken);
|
||||
vendorParams.append('id', idVendedor);
|
||||
vendorParams.append('formato', 'JSON');
|
||||
|
||||
const vendorResponse = await axios.post('https://api.tiny.com.br/api2/contato.obter.php', vendorParams, {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
|
||||
if (vendorResponse.data?.retorno?.status === 'OK') {
|
||||
const contato = vendorResponse.data.retorno.contato;
|
||||
whatsappVendedor = contato?.celular || contato?.fone || contato?.telefone || "";
|
||||
console.log(`Successfully fetched seller WhatsApp: ${whatsappVendedor || 'None'}`);
|
||||
} else {
|
||||
console.error('Failed to fetch seller details:', JSON.stringify(vendorResponse.data?.retorno?.erros || 'Unknown API error'));
|
||||
}
|
||||
}
|
||||
|
||||
setCachedOrderDetails(String(orderId), {
|
||||
fullOrderDetails,
|
||||
statusProcessamento,
|
||||
whatsappVendedor
|
||||
});
|
||||
} else {
|
||||
console.error('Tiny API returned an error:', apiResponse.data?.retorno?.erros || 'Unknown error');
|
||||
const details = await getOrFetchOrderDetails(String(orderId), tinyApiToken);
|
||||
if (details) {
|
||||
fullOrderDetails = details.fullOrderDetails;
|
||||
statusProcessamento = details.statusProcessamento;
|
||||
whatsappVendedor = details.whatsappVendedor;
|
||||
}
|
||||
} catch (apiError: any) {
|
||||
console.error('Failed to fetch from Tiny API:', apiError.message);
|
||||
|
||||
Reference in New Issue
Block a user