120 lines
4.1 KiB
TypeScript
120 lines
4.1 KiB
TypeScript
import { getCellValueByColumnName } from "./sheetUtils"
|
|
import { Product } from "./Product"
|
|
import { Shop } from "./shopifyApi"
|
|
|
|
// --- Constants ---
|
|
const BATCH_INTERVAL_MS = 30 * 1000 // 30 seconds
|
|
const LOCK_TIMEOUT_MS = 10 * 1000 // 10 seconds for lock acquisition
|
|
const CACHE_KEY_EDITS = "pendingEdits"
|
|
const CACHE_KEY_LAST_EDIT_TIME = "lastEditTime"
|
|
const SCRIPT_PROPERTY_TRIGGER_SCHEDULED = "batchTriggerScheduled"
|
|
|
|
export function onEditQueue(e) {
|
|
const sheet = e.source.getActiveSheet()
|
|
if (sheet.getName() !== "product_inventory") {
|
|
console.log("skipping edit on sheet " + sheet.getName())
|
|
return
|
|
}
|
|
const range = e.range
|
|
const row = range.getRow()
|
|
const sku = getCellValueByColumnName(sheet, "sku", row)
|
|
if (!sku) {
|
|
console.log("No SKU found for row " + row)
|
|
return
|
|
}
|
|
// Make sure SKU conforms to expected patterns
|
|
if (sku.match(`\\?`) || sku.match(`n$`)) {
|
|
console.log("SKU is a placeholder ('?' or 'n...'), skipping batching.")
|
|
return
|
|
}
|
|
// Acquire a user lock to prevent multiple onEdit calls from clashing
|
|
const documentLock = LockService.getDocumentLock()
|
|
try {
|
|
documentLock.waitLock(LOCK_TIMEOUT_MS)
|
|
|
|
const scriptProperties = PropertiesService.getScriptProperties() // Shared cache for all users
|
|
|
|
// 1. Accumulate edits in cache
|
|
let pendingEdits = []
|
|
try {
|
|
pendingEdits = JSON.parse(
|
|
scriptProperties.getProperty(CACHE_KEY_EDITS) || "[]"
|
|
)
|
|
} catch (e) {
|
|
console.log("Cache corruption: " + e.message)
|
|
scriptProperties.setProperty(CACHE_KEY_EDITS, "[]")
|
|
}
|
|
const existingIndex = pendingEdits.findIndex((item) => item.sku === sku)
|
|
if (existingIndex !== -1) {
|
|
console.log("New edit on queued SKU '"+sku+"', resetting timer...")
|
|
pendingEdits[existingIndex].timestamp = Date.now()
|
|
} else {
|
|
console.log("New SKU '"+sku+"' added to queue.")
|
|
pendingEdits.push({ sku, timestamp: Date.now() })
|
|
}
|
|
scriptProperties.setProperty(CACHE_KEY_EDITS, JSON.stringify(pendingEdits))
|
|
} catch (error) {
|
|
console.log(
|
|
"Error in onEdit (lock acquisition or cache operation): " + error.message
|
|
)
|
|
} finally {
|
|
documentLock.releaseLock()
|
|
}
|
|
}
|
|
|
|
export function processBatchedEdits() {
|
|
const scriptLock = LockService.getScriptLock() // Use script lock for the processing function
|
|
try {
|
|
scriptLock.waitLock(LOCK_TIMEOUT_MS)
|
|
|
|
const scriptProperties = PropertiesService.getScriptProperties()
|
|
|
|
let pendingEdits = []
|
|
try {
|
|
const pendingEditsStr = scriptProperties.getProperty(CACHE_KEY_EDITS)
|
|
pendingEdits = pendingEditsStr ? JSON.parse(pendingEditsStr) : []
|
|
} catch (e) {
|
|
console.log("Cache corruption: " + e.message)
|
|
scriptProperties.setProperty(CACHE_KEY_EDITS, "[]")
|
|
}
|
|
|
|
console.log(`Total SKUs in queue: ${pendingEdits.length}`)
|
|
const now = Date.now()
|
|
const toProcess = pendingEdits.filter(
|
|
(edit) => now - edit.timestamp > BATCH_INTERVAL_MS
|
|
)
|
|
if (toProcess.length > 0) {
|
|
let shop = new Shop()
|
|
console.log(`Processing ${toProcess.length} SKUs...`)
|
|
toProcess.forEach((edit) => {
|
|
console.log(
|
|
`Processing SKU ${edit.sku}, Timestamp: ${new Date(edit.timestamp)}`
|
|
)
|
|
// Make sure SKU conforms to expected patterns
|
|
if (!edit.sku.match(/^\w+-\d{4}$/)) {
|
|
console.log(`SKU ${edit.sku} is not valid, skipping processing.`)
|
|
return
|
|
}
|
|
let p = new Product(edit.sku)
|
|
p.UpdateShopifyProduct(shop)
|
|
})
|
|
|
|
pendingEdits = pendingEdits.filter(
|
|
edit => !toProcess.some(p => p.sku === edit.sku)
|
|
);
|
|
scriptProperties.setProperty(CACHE_KEY_EDITS, JSON.stringify(pendingEdits));
|
|
|
|
console.log(`Processed ${toProcess.length} edits.`)
|
|
} else {
|
|
console.log("No pending edits to process.")
|
|
}
|
|
} catch (error) {
|
|
console.log(
|
|
"Error in processBatchedEdits (lock acquisition or processing): " +
|
|
error.message
|
|
)
|
|
} finally {
|
|
scriptLock.releaseLock()
|
|
}
|
|
}
|