Skip to content

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

Package listing

@kody/planetscale

src/handle-webhook.ts

83 lines · 2.2 KB · TypeScript
import { draftLoop } from './draft-loop.ts'
import { pickActiveAnomaly, unwrapWebhookPayload } from './loop-helpers.ts'
import { readLoopConfig } from './loop-storage.ts'
import { listAnomalies } from './list-anomalies.ts'
import runLoop from './run-loop.ts'

/**
 * Handle a PlanetScale `branch.anomaly` webhook. Drafts the loop by default.
 * Live issue/PR creation only happens when configure-loop saved loopEnabled.
 * @example
 * import handleWebhook from 'kody:@kody/planetscale/handle-webhook'
 * await handleWebhook(payload)
 */
export default async function handleWebhook(input: unknown = {}) {
	const payload = unwrapWebhookPayload(input)
	const stored = await readLoopConfig()
	const organization = payload.organization ?? stored.organization
	const database = payload.database ?? stored.database
	const branch = payload.resource?.name ?? stored.branch ?? 'main'

	if (!organization || !database) {
		return {
			ok: false,
			message: 'Webhook is missing organization/database and none are saved in configure-loop.',
		}
	}

	if (payload.event && payload.event !== 'branch.anomaly') {
		return {
			ok: true,
			ignored: true,
			event: payload.event,
			message: 'Only branch.anomaly starts the Insights loop. Other events are ignored.',
		}
	}

	const anomalies = await listAnomalies({
		organization,
		database,
		branch,
		account: stored.account,
	})
	const anomaly = pickActiveAnomaly(anomalies.items)
	if (!anomaly) {
		return {
			ok: true,
			organization,
			database,
			branch,
			message: 'branch.anomaly received, but Insights currently lists no anomalies.',
		}
	}

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

	if (!stored.loopEnabled) {
		return {
			ok: true,
			dryRun: true,
			event: payload.event ?? 'branch.anomaly',
			draft,
			next: 'Set loopEnabled: true with configure-loop (confirm: true) if you want webhooks to open the issue and launch the implementer.',
		}
	}

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