Hello!
I am currently trying to work out my first workflow.
I have been working on an app for a while and I wrote code which takes a very long time to execute. As I would like to trigger it regularly, I figured a retool-workflow would suit this perfectly.
As it stands I am having issues setting it up.
The code I would like to trigger regularly is the follwing:
const batchSize = 5;
const orcids = select_expertOrcid.data.orcid;
let allResults = [];
let seenIDs = new Set();
let dupl_counter = 0;
// Retry wrapper
//retries 3 times if response is empty
async function fetchPageWithRetry(query, cursor, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
await query_scopus_db.setValue(query);
await cursor_scopus_db.setValue(cursor);
const response = await searchScopus_db.trigger();
// Check if the response is undefined or empty
if (!response || !response["search-results"]) {
throw new Error("Received empty or undefined response from Scopus API.");
}
return response;
} catch (err) {
console.warn(` Attempt ${attempt}/${retries} failed: ${err.message || err}`);
if (attempt === retries) {
throw new Error(" Max retries reached — skipping batch.");
}
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
}
for (let i = 0; i < orcids.length; i += batchSize) {
const batchOrcids = orcids.slice(i, i + batchSize);
const query = batchOrcids
.filter(o => o && o.length > 3)
.map(o => `ORCID(${o})`)
.join(" OR ");
const batchIndex = i / batchSize + 1;
console.log(` Starting batch ${batchIndex}:`, query);
let cursor = "*";
let batchResults = [];
try {
while (true) {
console.log(` Fetching page for batch ${batchIndex}, cursor: ${cursor}`);
let response = await fetchPageWithRetry(query, cursor);
const entries = response["search-results"]?.entry || [];
console.log(` Entries this page: ${entries.length} | Total in batch: ${batchResults.length}`);
for (const entry of entries) {
const id = entry['dc:identifier'];
if (id && !seenIDs.has(id)) {
seenIDs.add(id);
batchResults.push(entry);
}
else if(seenIDs.has(id)){
dupl_counter++
console.log("number of dublicate entries: " + dupl_counter)
}
}
const nextCursor = response["search-results"].cursor?.["@next"];
if (!nextCursor || nextCursor === cursor) {
console.log(` Finished batch ${batchIndex}, collected ${batchResults.length} entries.`);
break;
}
cursor = nextCursor;
await new Promise(resolve => setTimeout(resolve, 3000)); // throttle between pages
}
} catch (err) {
console.error(` Batch ${batchIndex} failed: ${err.message}`);
// continue to next batch instead of halting
}
allResults.push(...batchResults);
await new Promise(resolve => setTimeout(resolve, 3000)); // throttle between batches
}
await tableResults_db.setValue(allResults);
console.log(`🎉 All batches complete! Total unique results: ${allResults.length}`);
searchScopus is an API query and select_expertOrcid is an SQL query connected to my database.
as the code suggests, query_scopus_db
, cursor_scopus_db
and table_results
are all states.
The code is meant to both send get requests for each ORCID in batches of 5 (because to many orcids at once lead to a timeout error) as well as pagination.
I would like to trigger this code once a month and collect the data (I would like to store it as a JSON string) and display it in a table-component in my main app.
I am struggling to execute the code in the workflow, because, as it seems, I cannot use states in workflows.
Thank you in advance !