Help

Re: Airtable Script Stuck at Fetching Records for a Table with 1700 Lines

1692 0
cancel
Showing results for 
Search instead for 
Did you mean: 
Karek_Rami
5 - Automation Enthusiast
5 - Automation Enthusiast

Hello Airtable Community,

I'm working on a script in the Airtable Scripting App that processes a table with 1700 records. The script is designed to fetch records from the table, generate tracking links using the Impact.com API, and update the records with the newly generated tracking links.

Unfortunately, I'm experiencing an issue where the script gets stuck at the "Fetching records" step. The script does not proceed to fetch any records or send API requests, and no further logs are displayed. I've tried various approaches, including fetching records in smaller batches, but the issue persists.

Here's the latest version of the script:

let settings = input.config({
    title: "Add Impact.com tracking links",
    description: `This script generates Impact.com tracking links for the specified Envato links and adds them to a new field in the same record.
To use this script, you will need an Impact.com API key, affiliate ID, program ID, and media partner property ID. You can learn more about the API here: https://developer.impact.com/partner-api/`,
    items: [
        input.config.text("impactApiUsername", {
            label: "Impact.com API username",
            description: "Your Impact.com API username.",
        }),
        input.config.text("impactApiPassword", {
            label: "Impact.com API password",
            description: "Your Impact.com API password.",
            isPassword: true,
        }),
        input.config.text("impactAffiliateId", {
            label: "Impact.com affiliate ID",
            description: "Your Impact.com affiliate ID.",
        }),
        input.config.text("impactProgramId", {
            label: "Impact.com program ID",
            description: "The ID of the Impact.com program you want to track sales for.",
        }),
        input.config.text("impactMediaPartnerPropertyId", {
            label: "Impact.com media partner property ID",
            description: "The ID of the Impact.com media partner property that you use to track sales.",
        }),
        input.config.table("table", { label: "Table" }),
        input.config.field("envatoLinkField", {
            parentTable: "table",
            label: "Field containing Envato links",
        }),
        input.config.field("trackingLinkField", {
            parentTable: "table",
            label: "New field to store the tracking links",
        }),
        input.config.field("customSlugField", {
            parentTable: "table",
            label: "Field containing custom slug",
        }),
    ],
});

async function generateTrackingLink(envatoLink, apiUsername, apiPassword, affiliateId, programId, mediaPartnerPropertyId, customSlug) {
    const apiUrl = `https://api.impact.com/Mediapartners/${affiliateId}/Programs/${programId}/TrackingLinks?MediaPartnerPropertyId=${mediaPartnerPropertyId}&DeepLink=${encodeURIComponent(envatoLink)}&Type=vanity&CustomPath=${encodeURIComponent(customSlug)}-march-2023`;

    const headers = {
        "Content-Type": "application/json",
        "Authorization": "Basic " + btoa(apiUsername + ":" + apiPassword),
    };

    // Send the API request
    const response = await fetch(apiUrl, {
        method: "POST",
        headers: headers,
    });

    const result = await response.json();

    return result.trackingLink;
}

async function main() {
    console.log("Starting script");

    let table = settings.table;
    let envatoLinkField = settings.envatoLinkField;
    let trackingLinkField = settings.trackingLinkField;
    let customSlugField = settings.customSlugField;

    console.log("Fetching records");
    let allRecords = [];
    let page = null;
    do {
        page = await table.selectRecordsAsync({ pageSize: 10, startCursor: page ? page.cursor : undefined });
        allRecords.push(...page.records);
        console.log("Fetched", page.records.length, "records");
    } while (page.hasNextPage);
    console.log("Records fetched:", allRecords.length);


    for (let record of query.records) {
        console.log("Processing record:", record.id);

        let envatoLink = record.getCellValue(envatoLinkField);
        let customSlug = record.getCellValue(customSlugField);

        console.log("Envato link:", envatoLink);
        console.log("Custom slug:", customSlug);

        if (envatoLink && !record.getCellValue(trackingLinkField)) {
            console.log("Generating tracking link");
            let trackingLink = await generateTrackingLink(envatoLink, settings.impactApiPassword, settings.impactAffiliateId, settings.impactProgramId, settings.impactMediaPartnerPropertyId, customSlug);
            console.log("Generated tracking link:", trackingLink); console.log("Updating record");
            await table.updateRecordAsync(record, { [trackingLinkField.id]: trackingLink });
            console.log("Record updated");
        }
    }

    console.log("Script completed");
}

main();

I've double-checked the table configuration, tried fetching records from a different table, reloaded the Airtable Scripting App, and checked the browser's console for errors, but none of these steps have resolved the issue.

I would appreciate any help or guidance on how to resolve this problem. Are there any known limitations or issues with fetching records from a table with a large number of records? Has anyone experienced a similar problem, and if so, how did you overcome it?

Thank you in advance for your assistance!

Best regards,
ANS

2 Replies 2

Hey @Karek_Rami

let settings = input.config({
	title: "Add Impact.com tracking links",
	description: `This script generates Impact.com tracking links for the specified Envato links and adds them to a new field in the same record.
To use this script, you will need an Impact.com API key, affiliate ID, program ID, and media partner property ID. You can learn more about the API here: https://developer.impact.com/partner-api/`,
	items: [
		input.config.text("impactApiUsername", {
			label: "Impact.com API username",
			description: "Your Impact.com API username."
		}),
		input.config.text("impactApiPassword", {
			label: "Impact.com API password",
			description: "Your Impact.com API password.",
			isPassword: true
		}),
		input.config.text("impactAffiliateId", {
			label: "Impact.com affiliate ID",
			description: "Your Impact.com affiliate ID."
		}),
		input.config.text("impactProgramId", {
			label: "Impact.com program ID",
			description:
				"The ID of the Impact.com program you want to track sales for."
		}),
		input.config.text("impactMediaPartnerPropertyId", {
			label: "Impact.com media partner property ID",
			description:
				"The ID of the Impact.com media partner property that you use to track sales."
		}),
		input.config.table("table", { label: "Table" }),
		input.config.field("envatoLinkField", {
			parentTable: "table",
			label: "Field containing Envato links"
		}),
		input.config.field("trackingLinkField", {
			parentTable: "table",
			label: "New field to store the tracking links"
		}),
		input.config.field("customSlugField", {
			parentTable: "table",
			label: "Field containing custom slug"
		})
	]
});

async function generateTrackingLink(
	envatoLink,
	apiUsername,
	apiPassword,
	affiliateId,
	programId,
	mediaPartnerPropertyId,
	customSlug
) {
	const apiUrl = `https://api.impact.com/Mediapartners/${affiliateId}/Programs/${programId}/TrackingLinks?MediaPartnerPropertyId=${mediaPartnerPropertyId}&DeepLink=${encodeURIComponent(
		envatoLink
	)}&Type=vanity&CustomPath=${encodeURIComponent(customSlug)}-march-2023`;

	const headers = {
		"Content-Type": "application/json",
		Authorization: "Basic " + btoa(apiUsername + ":" + apiPassword)
	};

	// Send the API request
	const response = await fetch(apiUrl, {
		method: "POST",
		headers: headers
	})
        .then(response => response.json());

	return response.trackingLink;
}

async function main() {
	console.log("Starting script");
	console.log("Fetching records");

	let table = settings.table;
	let envatoLinkField = settings.envatoLinkField;
	let trackingLinkField = settings.trackingLinkField;
	let customSlugField = settings.customSlugField;

	let allRecords = await table
		.selectRecordsAsync()
		.then(query => query.records)
		.then(records => records.map(record));

	allRecords.forEach(async record => {
		console.log(`Processing Record: ${record.id}`);

		let envatoLink = record.getCellValue(envatoLinkField);
		let customSlug = record.getCellValue(customSlugField);

		if (envatoLink && !record.getCellValue(trackingLinkField)) {
			console.log("Generating tracking link");

			let trackingLink = await generateTrackingLink(
				envatoLink,
				settings.impactApiPassword,
				settings.impactAffiliateId,
				settings.impactProgramId,
				settings.impactMediaPartnerPropertyId,
				customSlug
			);

			console.log("Generated tracking link:", trackingLink);
			console.log("Updating record");
			await table .updateRecordAsync(record, { [trackingLinkField.id]: trackingLink })
				.then(() => console.log("Record updated"));
		}
	});

	console.log("Script completed");
}

await main();

I was going through and realized that the critical issue was that you were missing the await operator when you call the main() function at the bottom of the script.
Give it a whirl and see what happens if you add the missing await.

I got caught up and forgot to mention something else about your posted script.

When you're working with Airtable scripting, pagination is not implemented.
Pagination in record queries is implemented when you are interacting with the table object via the REST API.

I have zero idea what this snippet would return:

let page = null;
do {
    page = await table.selectRecordsAsync({ pageSize: 10, startCursor: page ? page.cursor : undefined });
    allRecords.push(...page.records);
    console.log("Fetched", page.records.length, "records");
} while (page.hasNextPage);

 This snippet does what you were trying to do originally and is more readable:

let allRecords = await table.selectRecordsAsync()
    .then(query => query.records)
    .then(records => records.map(record));

console.log(`Fetched ${allRecords.length} records`);