Make product structure sync use product export
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m23s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 1m23s
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@ dist
|
||||
.env
|
||||
graphs_backfill_state.json
|
||||
graphs_backfill_stop.json
|
||||
product_structures_sync_state.json
|
||||
product_structures_sync_stop.json
|
||||
|
||||
@@ -22,6 +22,7 @@ RUN npm install --omit=dev
|
||||
|
||||
# Copy built code from the builder stage
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/data ./data
|
||||
|
||||
# Expose the port the app runs on
|
||||
EXPOSE 3000
|
||||
|
||||
7621
data/product-structure-products.csv
Normal file
7621
data/product-structure-products.csv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,10 @@ services:
|
||||
- GRAPHS_BACKFILL_ORDER_DELAY_MS=${GRAPHS_BACKFILL_ORDER_DELAY_MS:-0}
|
||||
- GRAPHS_BACKFILL_TINY_BLOCK_DELAY_MS=${GRAPHS_BACKFILL_TINY_BLOCK_DELAY_MS:-600000}
|
||||
- GRAPHS_BACKFILL_MAX_TINY_BLOCK_RETRIES=${GRAPHS_BACKFILL_MAX_TINY_BLOCK_RETRIES:-0}
|
||||
- PRODUCT_STRUCTURES_GRAPHS_API_URL=${PRODUCT_STRUCTURES_GRAPHS_API_URL}
|
||||
- PRODUCT_STRUCTURES_GRAPHS_API_KEY=${PRODUCT_STRUCTURES_GRAPHS_API_KEY}
|
||||
- PRODUCT_STRUCTURES_REQUEST_DELAY_MS=${PRODUCT_STRUCTURES_REQUEST_DELAY_MS:-2500}
|
||||
- PRODUCT_STRUCTURES_FETCH_COMPONENT_UNITS=${PRODUCT_STRUCTURES_FETCH_COMPONENT_UNITS:-true}
|
||||
- TINY_FETCH_SELLER_DETAILS=${TINY_FETCH_SELLER_DETAILS:-false}
|
||||
- TINY_ORDER_DETAILS_CACHE_TTL_MS=${TINY_ORDER_DETAILS_CACHE_TTL_MS:-120000}
|
||||
- TINY_LIVE_REQUEST_DELAY_MS=${TINY_LIVE_REQUEST_DELAY_MS:-6000}
|
||||
|
||||
16
src/index.ts
16
src/index.ts
@@ -10,6 +10,18 @@ dotenv.config();
|
||||
const app: Express = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
function deriveProductionOrdersUrl(env: NodeJS.ProcessEnv) {
|
||||
const raw = env.GRAPHS_API_URL || env.NEXSTAR_GRAPHS_API_URL || '';
|
||||
if (!raw) return null;
|
||||
|
||||
try {
|
||||
const parsed = new URL(raw);
|
||||
return `${parsed.origin}/api/production-orders/tiny-sync`;
|
||||
} catch {
|
||||
return raw.replace(/\/api\/data.*$/, '/api/production-orders/tiny-sync');
|
||||
}
|
||||
}
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
@@ -228,8 +240,8 @@ app.get('/api/trigger-product-structures-sync', (req: Request, res: Response) =>
|
||||
options: {
|
||||
skus: env.PRODUCT_STRUCTURE_SKUS || null,
|
||||
productIds: env.PRODUCT_STRUCTURE_PRODUCT_IDS || null,
|
||||
inputFile: env.PRODUCT_STRUCTURES_INPUT_FILE || null,
|
||||
graphsUrl: env.PRODUCT_STRUCTURES_GRAPHS_API_URL || env.PRODUCTION_ORDERS_GRAPHS_API_URL || env.GRAPHS_PRODUCTION_ORDERS_API_URL || null,
|
||||
inputFile: env.PRODUCT_STRUCTURES_INPUT_FILE || '/app/data/product-structure-products.csv',
|
||||
graphsUrl: env.PRODUCT_STRUCTURES_GRAPHS_API_URL || env.PRODUCTION_ORDERS_GRAPHS_API_URL || env.GRAPHS_PRODUCTION_ORDERS_API_URL || deriveProductionOrdersUrl(env),
|
||||
dryRun: env.PRODUCT_STRUCTURES_DRY_RUN || 'false',
|
||||
maxProducts: env.PRODUCT_STRUCTURES_MAX_PRODUCTS || null,
|
||||
requestDelayMs: env.PRODUCT_STRUCTURES_REQUEST_DELAY_MS || '2500',
|
||||
|
||||
@@ -48,6 +48,7 @@ type ProductStructurePayload = {
|
||||
type SyncState = {
|
||||
status?: 'running' | 'stopped' | 'completed';
|
||||
processedProducts?: number;
|
||||
skippedProducts?: number;
|
||||
failedProducts?: number;
|
||||
lastSku?: string;
|
||||
lastFailedSku?: string;
|
||||
@@ -60,12 +61,25 @@ type RequestedProduct = {
|
||||
sku: string;
|
||||
};
|
||||
|
||||
function deriveProductionOrdersUrl() {
|
||||
const raw = process.env.GRAPHS_API_URL || process.env.NEXSTAR_GRAPHS_API_URL || '';
|
||||
if (!raw) return '';
|
||||
|
||||
try {
|
||||
const parsed = new URL(raw);
|
||||
return `${parsed.origin}/api/production-orders/tiny-sync`;
|
||||
} catch {
|
||||
return raw.replace(/\/api\/data.*$/, '/api/production-orders/tiny-sync');
|
||||
}
|
||||
}
|
||||
|
||||
const TINY_API_TOKEN = process.env.TINY_API_TOKEN;
|
||||
const GRAPHS_PRODUCTION_ORDERS_URL = (
|
||||
process.env.PRODUCT_STRUCTURES_GRAPHS_API_URL ||
|
||||
process.env.PRODUCTION_ORDERS_GRAPHS_API_URL ||
|
||||
process.env.GRAPHS_PRODUCTION_ORDERS_API_URL ||
|
||||
(process.env.GRAPHS_API_BASE_URL ? `${process.env.GRAPHS_API_BASE_URL.replace(/\/+$/, '')}/api/production-orders/tiny-sync` : '') ||
|
||||
deriveProductionOrdersUrl() ||
|
||||
'http://localhost:3004/api/production-orders/tiny-sync'
|
||||
);
|
||||
const GRAPHS_PRODUCTION_ORDERS_API_KEY = (
|
||||
@@ -78,7 +92,8 @@ const GRAPHS_PRODUCTION_ORDERS_API_KEY = (
|
||||
process.env.API_KEY ||
|
||||
'nexstar_secret_key_123'
|
||||
);
|
||||
const INPUT_FILE = process.env.PRODUCT_STRUCTURES_INPUT_FILE || '';
|
||||
const DEFAULT_INPUT_FILE = path.join(process.cwd(), 'data', 'product-structure-products.csv');
|
||||
const INPUT_FILE = process.env.PRODUCT_STRUCTURES_INPUT_FILE || (fs.existsSync(DEFAULT_INPUT_FILE) ? DEFAULT_INPUT_FILE : '');
|
||||
const SKUS = String(process.env.PRODUCT_STRUCTURE_SKUS || '')
|
||||
.split(',')
|
||||
.map(value => value.trim())
|
||||
@@ -447,16 +462,18 @@ async function runSync() {
|
||||
saveState({
|
||||
status: 'running',
|
||||
processedProducts: 0,
|
||||
skippedProducts: 0,
|
||||
failedProducts: 0,
|
||||
lastMessage: 'Product structures sync started'
|
||||
});
|
||||
|
||||
let processedProducts = 0;
|
||||
let skippedProducts = 0;
|
||||
let failedProducts = 0;
|
||||
|
||||
for (const requested of requestedProducts) {
|
||||
assertNotStopped();
|
||||
if (MAX_PRODUCTS && processedProducts + failedProducts >= MAX_PRODUCTS) {
|
||||
if (MAX_PRODUCTS && processedProducts + skippedProducts + failedProducts >= MAX_PRODUCTS) {
|
||||
console.log(`[limit] Stopped after ${MAX_PRODUCTS} product(s).`);
|
||||
break;
|
||||
}
|
||||
@@ -470,6 +487,7 @@ async function runSync() {
|
||||
saveState({
|
||||
status: 'running',
|
||||
processedProducts,
|
||||
skippedProducts,
|
||||
failedProducts,
|
||||
lastFailedSku: label,
|
||||
lastMessage: `Product not found for ${label}`
|
||||
@@ -483,6 +501,7 @@ async function runSync() {
|
||||
saveState({
|
||||
status: 'running',
|
||||
processedProducts,
|
||||
skippedProducts,
|
||||
failedProducts,
|
||||
lastFailedSku: product.codigo || product.id,
|
||||
lastMessage: `Could not get structure for ${product.codigo || product.id}`
|
||||
@@ -490,6 +509,20 @@ async function runSync() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!payload.components.length) {
|
||||
skippedProducts += 1;
|
||||
console.log(`[Tiny] No structure components for ${payload.order.productSku}; skipping Graphs send.`);
|
||||
saveState({
|
||||
status: 'running',
|
||||
processedProducts,
|
||||
skippedProducts,
|
||||
failedProducts,
|
||||
lastSku: payload.order.productSku,
|
||||
lastMessage: `Skipped ${payload.order.productSku}; no structure components`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const sent = await sendStructure(payload);
|
||||
if (sent) {
|
||||
processedProducts += 1;
|
||||
@@ -500,6 +533,7 @@ async function runSync() {
|
||||
saveState({
|
||||
status: 'running',
|
||||
processedProducts,
|
||||
skippedProducts,
|
||||
failedProducts,
|
||||
lastSku: sent ? payload.order.productSku : undefined,
|
||||
lastFailedSku: sent ? undefined : payload.order.productSku,
|
||||
@@ -512,6 +546,7 @@ async function runSync() {
|
||||
saveState({
|
||||
status: 'running',
|
||||
processedProducts,
|
||||
skippedProducts,
|
||||
failedProducts,
|
||||
lastFailedSku: label,
|
||||
lastMessage: `Failed product ${label}; continuing`
|
||||
@@ -522,10 +557,11 @@ async function runSync() {
|
||||
saveState({
|
||||
status: 'completed',
|
||||
processedProducts,
|
||||
skippedProducts,
|
||||
failedProducts,
|
||||
lastMessage: failedProducts ? `Product structures sync completed with ${failedProducts} failed product(s)` : 'Product structures sync completed'
|
||||
});
|
||||
console.log(`Done. Synced ${processedProducts} product structure(s). Failed products: ${failedProducts}.`);
|
||||
console.log(`Done. Synced ${processedProducts} product structure(s). Skipped products: ${skippedProducts}. Failed products: ${failedProducts}.`);
|
||||
}
|
||||
|
||||
runSync().catch((error: any) => {
|
||||
|
||||
Reference in New Issue
Block a user