Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/airtable

src/update-record.ts

117 lines · 3.3 KB · TypeScript
import { pickAuthInput } from './auth.ts'
import { airtableRequest } from './client.ts'
import { mapRecord } from './models.ts'
import { recordsPath } from './setup.ts'
import type {
	AirtableAuthInput,
	AirtableRecordSummary,
	DryRunResult,
	JsonRecord,
} from './types.ts'
import {
	optionalBoolean,
	requireConfirmOrDryRun,
	requireRecord,
	requireString,
} from './types.ts'

export type UpdateRecordInput = AirtableAuthInput & {
	baseId: string
	tableIdOrName: string
	recordId: string
	fields: JsonRecord
	typecast?: boolean
	confirm?: boolean
	dryRun?: boolean
}

function updatePayload(input: UpdateRecordInput): {
	path: string
	body: JsonRecord
} {
	const fields = requireRecord(input.fields, 'fields')
	if (Object.keys(fields).length === 0) {
		throw new Error('update-record requires at least one field to change.')
	}
	const body: JsonRecord = {
		records: [
			{
				id: requireString(input.recordId, 'recordId'),
				fields,
			},
		],
	}
	const typecast = optionalBoolean(input.typecast, 'typecast')
	if (typecast !== undefined) body.typecast = typecast
	return {
		path: recordsPath(
			requireString(input.baseId, 'baseId'),
			requireString(input.tableIdOrName, 'tableIdOrName'),
		),
		body,
	}
}

/**
 * Update an Airtable record. Requires `confirm: true`, or use `dryRun: true`.
 * Needs `data.records:write`.
 * @example
 * import updateRecord from 'kody:@kody/airtable/update-record'
 * const preview = await updateRecord({
 *   baseId: 'appXXXXXXXXXXXXXX',
 *   tableIdOrName: 'tblXXXXXXXXXXXXXX',
 *   recordId: 'recXXXXXXXXXXXXXX',
 *   fields: { Status: 'Done' },
 *   dryRun: true,
 * })
 */
export async function updateRecord(
	input: UpdateRecordInput,
): Promise<
	AirtableRecordSummary | DryRunResult<{ method: string; path: string; body: JsonRecord }>
> {
	const payload = updatePayload(input)
	if (requireConfirmOrDryRun(input, 'Updating an Airtable record') === 'dryRun') {
		return { dryRun: true, wouldCall: { method: 'PATCH', ...payload } }
	}

	const result = await airtableRequest<{ records?: Array<unknown> }>({
		...input,
		operation: 'records.write',
		method: 'PATCH',
		path: payload.path,
		body: payload.body,
	})
	const body = result.data && typeof result.data === 'object' ? result.data : {}
	const updated = Array.isArray(body.records) ? body.records[0] : null
	if (!updated) throw new Error('Airtable update record did not return a record.')
	return mapRecord(updated)
}

/**
 * Update an Airtable record. Requires `confirm: true`, or use `dryRun: true`.
 * @example
 * import updateRecord from 'kody:@kody/airtable/update-record'
 * const preview = await updateRecord({
 *   baseId: 'appXXXXXXXXXXXXXX',
 *   tableIdOrName: 'Tasks',
 *   recordId: 'recXXXXXXXXXXXXXX',
 *   fields: { Status: 'Done' },
 *   dryRun: true,
 * })
 */
export default async function updateRecordEntrypoint(
	params: Partial<UpdateRecordInput> & Record<string, unknown> = {},
) {
	const input = requireRecord(params, 'update-record')
	return updateRecord({
		baseId: requireString(input.baseId, 'baseId'),
		tableIdOrName: requireString(input.tableIdOrName, 'tableIdOrName'),
		recordId: requireString(input.recordId, 'recordId'),
		fields: requireRecord(input.fields, 'fields'),
		typecast: optionalBoolean(input.typecast, 'typecast'),
		...pickAuthInput(input),
		confirm: optionalBoolean(input.confirm, 'confirm'),
		dryRun: optionalBoolean(input.dryRun, 'dryRun'),
	})
}