-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(tiny-erp): Early respond webhooks with OK and property queue to r…
…etry on failure
- Loading branch information
Showing
3 changed files
with
108 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,111 @@ | ||
import type { Request, Response } from 'firebase-functions'; | ||
import type { Applications } from '@cloudcommerce/types'; | ||
import logger from 'firebase-functions/logger'; | ||
import { info } from 'firebase-functions/logger'; | ||
import api from '@cloudcommerce/api'; | ||
import config from '@cloudcommerce/firebase/lib/config'; | ||
import importProduct from './integration/import-product-from-tiny'; | ||
import importOrder from './integration/import-order-from-tiny'; | ||
|
||
let appData: Record<string, any> = {}; | ||
let application: Applications; | ||
import afterQueue from './integration/after-tiny-queue'; | ||
|
||
export default async (req: Request, res: Response) => { | ||
const tinyToken = req.query.token; | ||
if (typeof tinyToken === 'string' && tinyToken && req.body) { | ||
const { dados, tipo } = req.body; | ||
if (dados) { | ||
/* | ||
TODO: Check Tiny server IPs | ||
const clientIp = req.get('x-forwarded-for') || req.connection.remoteAddress | ||
*/ | ||
const { TINYERP_TOKEN } = process.env; | ||
if (!TINYERP_TOKEN || TINYERP_TOKEN !== tinyToken) { | ||
const { apps: { tinyErp: { appId } } } = config.get(); | ||
const applicationId = req.query._id; | ||
const appEndpoint = applicationId && typeof applicationId === 'string' | ||
? `applications/${applicationId}` | ||
: `applications/app_id:${appId}`; | ||
application = (await api.get(appEndpoint as `applications/${Applications['_id']}`)).data; | ||
appData = { | ||
...application.data, | ||
...application.hidden_data, | ||
}; | ||
if (appData.tiny_api_token !== tinyToken) { | ||
res.sendStatus(401); | ||
return; | ||
} | ||
process.env.TINYERP_TOKEN = tinyToken; | ||
} | ||
if (typeof tinyToken !== 'string' || !tinyToken) { | ||
res.sendStatus(403); | ||
return; | ||
} | ||
const { body } = req; | ||
if (typeof body !== 'object' || !body) { | ||
res.sendStatus(422); | ||
return; | ||
} | ||
const { dados, tipo } = body; | ||
if (typeof dados !== 'object' || !dados) { | ||
res.sendStatus(400); | ||
return; | ||
} | ||
/* | ||
TODO: Check Tiny server IPs | ||
const clientIp = req.get('x-forwarded-for') || req.connection.remoteAddress | ||
*/ | ||
|
||
if (dados.idVendaTiny) { | ||
const orderNumber = `id:${dados.idVendaTiny}`; | ||
const queueEntry = { | ||
nextId: orderNumber, | ||
isNotQueued: true, | ||
}; | ||
await importOrder({}, queueEntry); | ||
} else if ( | ||
(tipo === 'produto' || tipo === 'estoque') | ||
&& (dados.id || dados.idProduto) | ||
&& (dados.codigo || dados.sku) | ||
) { | ||
const nextId = String(dados.skuMapeamento || dados.sku || dados.codigo); | ||
const tinyStockUpdate = { | ||
ref: `${nextId}`, | ||
tipo, | ||
produto: { | ||
id: dados.idProduto, | ||
codigo: dados.sku, | ||
...dados, | ||
}, | ||
}; | ||
logger.info(`> Tiny webhook: ${nextId} => ${tinyStockUpdate.produto.saldo}`); | ||
const queueEntry = { | ||
nextId, | ||
tinyStockUpdate, | ||
isNotQueued: true, | ||
app: application, | ||
}; | ||
await importProduct({}, queueEntry, appData, false, true); | ||
} | ||
const { apps: { tinyErp: { appId } } } = config.get(); | ||
const applicationId = req.query._id; | ||
const appEndpoint = applicationId && typeof applicationId === 'string' | ||
? `applications/${applicationId}` as `applications/${Applications['_id']}` | ||
: `applications/app_id:${appId}` as const; | ||
const application = (await api.get(appEndpoint)).data; | ||
const appData = { | ||
...application.data, | ||
...application.hidden_data, | ||
}; | ||
if (!process.env.TINYERP_TOKEN) { | ||
process.env.TINYERP_TOKEN = appData.tiny_api_token; | ||
} | ||
if (process.env.TINYERP_TOKEN !== tinyToken) { | ||
res.sendStatus(401); | ||
return; | ||
} | ||
|
||
if (dados.idVendaTiny) { | ||
const orderNumber = `id:${dados.idVendaTiny}`; | ||
const queueEntry = { | ||
nextId: orderNumber, | ||
isNotQueued: true, | ||
}; | ||
importOrder({}, queueEntry).catch((err: any) => { | ||
return afterQueue(queueEntry, appData, application, err); | ||
}); | ||
} else if ( | ||
(tipo === 'produto' || tipo === 'estoque') | ||
&& (dados.id || dados.idProduto) | ||
&& (dados.codigo || dados.sku) | ||
) { | ||
const nextId = String(dados.skuMapeamento || dados.sku || dados.codigo); | ||
const tinyStockUpdate = { | ||
ref: `${nextId}`, | ||
tipo, | ||
produto: { | ||
id: dados.idProduto, | ||
codigo: dados.sku, | ||
...dados, | ||
}, | ||
}; | ||
info(`> Tiny webhook: ${nextId} => ${tinyStockUpdate.produto.saldo}`); | ||
const queueEntry = { | ||
nextId, | ||
tinyStockUpdate, | ||
isNotQueued: true, | ||
app: application, | ||
}; | ||
importProduct({}, queueEntry, appData, false, true).catch((err: any) => { | ||
return afterQueue(queueEntry, appData, application, err); | ||
}); | ||
} | ||
|
||
if (tipo === 'produto') { | ||
const mapeamentos: any[] = []; | ||
const parseTinyItem = (tinyItem) => { | ||
if (tinyItem) { | ||
const { | ||
idMapeamento, | ||
id, | ||
codigo, | ||
sku, | ||
} = tinyItem; | ||
mapeamentos.push({ | ||
idMapeamento: idMapeamento || id, | ||
skuMapeamento: codigo || sku, | ||
}); | ||
} | ||
}; | ||
parseTinyItem(dados); | ||
if (Array.isArray(dados.variacoes)) { | ||
dados.variacoes.forEach((variacao) => { | ||
parseTinyItem(variacao.id ? variacao : variacao.variacao); | ||
}); | ||
} | ||
res.status(200).send(mapeamentos); | ||
return; | ||
if (tipo === 'produto') { | ||
const mapeamentos: any[] = []; | ||
const parseTinyItem = (tinyItem) => { | ||
if (tinyItem) { | ||
const { | ||
idMapeamento, | ||
id, | ||
codigo, | ||
sku, | ||
} = tinyItem; | ||
mapeamentos.push({ | ||
idMapeamento: idMapeamento || id, | ||
skuMapeamento: codigo || sku, | ||
}); | ||
} | ||
res.sendStatus(200); | ||
return; | ||
}; | ||
parseTinyItem(dados); | ||
if (Array.isArray(dados.variacoes)) { | ||
dados.variacoes.forEach((variacao) => { | ||
parseTinyItem(variacao.id ? variacao : variacao.variacao); | ||
}); | ||
} | ||
logger.warn('< Invalid Tiny Webhook body', req.body); | ||
res.status(200).send(mapeamentos); | ||
return; | ||
} | ||
res.sendStatus(403); | ||
res.sendStatus(200); | ||
}; |