Skip to content

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

Package listing

@kody/planetscale

src/poll-anomalies.ts

111 lines · 3.4 KB · TypeScript
import { draftLoop } from './draft-loop.ts'
import { pickActiveAnomaly } from './loop-helpers.ts'
import { readLoopConfig } from './loop-storage.ts'
import { listAnomalies } from './list-anomalies.ts'
import runLoop from './run-loop.ts'
import { optionalBoolean, optionalString, requireRecord } from './types.ts'

export type PollAnomaliesInput = {
	organization?: string
	database?: string
	branch?: string
	account?: string
	secretName?: string
	dryRun?: boolean
	confirm?: boolean
}

/**
 * Scheduled Insights poll. Declared job starts disabled. Draft-only unless
 * loopEnabled is saved and confirm is set (scheduled runs pass no confirm, so
 * they stay draft unless the saved loop is enabled).
 * @example
 * import pollAnomalies from 'kody:@kody/planetscale/poll-anomalies'
 * await pollAnomalies({ dryRun: true })
 */
export async function pollAnomalies(input: PollAnomaliesInput = {}) {
	const stored = await readLoopConfig()
	const organization = optionalString(input.organization, 'organization') ?? stored.organization
	const database = optionalString(input.database, 'database') ?? stored.database
	const branch = optionalString(input.branch, 'branch') ?? stored.branch ?? 'main'
	if (!organization || !database) {
		return {
			ok: false,
			message: 'Set organization + database with configure-loop before polling Insights.',
		}
	}

	const anomalies = await listAnomalies({
		organization,
		database,
		branch,
		account: optionalString(input.account, 'account') ?? stored.account,
		secretName: optionalString(input.secretName, 'secretName'),
	})
	const anomaly = pickActiveAnomaly(anomalies.items)
	if (!anomaly) {
		return {
			ok: true,
			organization,
			database,
			branch,
			activeAnomalies: 0,
			message: 'No active Insights anomalies.',
		}
	}

	const draft = await draftLoop({
		organization,
		database,
		branch,
		anomaly,
		githubOwner: stored.githubOwner,
		githubRepo: stored.githubRepo,
		repositoryUrl: stored.repositoryUrl,
		labels: stored.labels,
		account: stored.account,
	})

	const scheduledLive = stored.loopEnabled === true && input.dryRun !== true
	const explicitLive = input.confirm === true && input.dryRun !== true
	if (!scheduledLive && !explicitLive) {
		return {
			ok: true,
			dryRun: true,
			activeAnomalies: anomalies.items.filter((item) => item.active === true).length,
			draft,
			next: stored.loopEnabled
				? 'Re-run with confirm: true to open the issue and launch the implementer.'
				: 'Draft only. Enable the loop with configure-loop ({ loopEnabled: true, confirm: true }).',
		}
	}

	return runLoop({
		organization,
		database,
		branch,
		anomalyId: anomaly.id,
		confirm: true,
	})
}

/**
 * Scheduled Insights poll. Declared job starts disabled.
 * @example
 * import pollAnomalies from 'kody:@kody/planetscale/poll-anomalies'
 * await pollAnomalies({ dryRun: true })
 */
export default async function pollAnomaliesEntrypoint(
	params: Partial<PollAnomaliesInput> & Record<string, unknown> = {},
) {
	const input = requireRecord(params, 'poll-anomalies')
	return pollAnomalies({
		organization: optionalString(input.organization, 'organization'),
		database: optionalString(input.database, 'database'),
		branch: optionalString(input.branch, 'branch'),
		account: optionalString(input.account, 'account'),
		secretName: optionalString(input.secretName, 'secretName'),
		dryRun: optionalBoolean(input.dryRun, 'dryRun'),
		confirm: optionalBoolean(input.confirm, 'confirm'),
	})
}