import { kody } from 'kody:runtime' import { API_KEY_SETUP_URL, APPLICATION_KEY_SETUP_URL, DATADOG_API_HOSTS, resolveApiHost, resolveApiKeySecretName, resolveApplicationKeySecretName, secretSetupUrl, } from './core.ts' import { createDashboard, deleteDashboard, listDashboards, updateDashboard, } from './dashboards.ts' import { createIncident, deleteIncident, updateIncident } from './incidents.ts' import { createMonitor, deleteMonitor, listMonitors, muteMonitor, updateMonitor, } from './monitors.ts' import { validateKeys } from './validate.ts' import type { DatadogSmokeTestInput } from './types.ts' function secretEntries(result: unknown): Array<{ name?: string; scope?: string }> { if (result && typeof result === 'object' && Array.isArray((result as { secrets?: unknown }).secrets)) { return (result as { secrets: Array<{ name?: string; scope?: string }> }).secrets } return Array.isArray(result) ? result : [] } async function listUserSecretNames(): Promise> { try { const listed = await kody.secretList({ scope: 'user' }) const names = new Set() for (const entry of secretEntries(listed)) { if (entry?.name && (entry.scope === 'user' || !entry.scope)) names.add(entry.name) } return names } catch { return new Set() } } async function mutationSelfCheck() { const monitor = { monitorId: 1, type: 'query alert', query: 'avg(last_5m):avg:system.cpu.user{*} > 95', name: 'smoke-preview', message: 'dry-run only', } const create = await createMonitor({ ...monitor, dryRun: true }) const update = await updateMonitor({ ...monitor, dryRun: true }) const mute = await muteMonitor({ monitorId: 1, dryRun: true }) const del = await deleteMonitor({ monitorId: 1, dryRun: true }) const incident = await createIncident({ title: 'smoke-preview', customerImpacted: false, dryRun: true }) const incidentUpdate = await updateIncident({ incidentId: 'incident-smoke', title: 'smoke-preview', dryRun: true, }) const incidentDelete = await deleteIncident({ incidentId: 'incident-smoke', dryRun: true }) const dashboard = await createDashboard({ title: 'smoke-preview', layoutType: 'ordered', widgets: [], dryRun: true, }) const dashboardUpdate = await updateDashboard({ dashboardId: 'abc-def', title: 'smoke-preview', layoutType: 'ordered', widgets: [], dryRun: true, }) const dashboardDelete = await deleteDashboard({ dashboardId: 'abc-def', dryRun: true }) const checks = [ create, update, mute, del, incident, incidentUpdate, incidentDelete, dashboard, dashboardUpdate, dashboardDelete, ] for (const check of checks) { if (!('dryRun' in check) || check.dryRun !== true) { throw new Error('Datadog dryRun mutation self-check failed.') } } return { monitors: true, incidents: true, dashboards: true, } } /** * Local dry-run checks plus an optional live key validation and sample reads. * * Never mutates monitors, incidents, or dashboards. * * @example * import smokeTest from 'kody:@kody/datadog/smoke-test' * const result = await smokeTest() */ export async function smokeTest(input: DatadogSmokeTestInput = {}) { const dryRunMutations = await mutationSelfCheck() const apiKeySecret = resolveApiKeySecretName(input) const applicationKeySecret = resolveApplicationKeySecretName(input) const names = await listUserSecretNames() const hasApiKey = names.has(apiKeySecret) const hasApplicationKey = names.has(applicationKeySecret) const host = resolveApiHost(input) if (!hasApiKey || !hasApplicationKey) { return { ok: true, live: false, selfCheck: { dryRunMutations }, secrets: { apiKeySecret, applicationKeySecret, hasApiKey, hasApplicationKey }, setup: { auth: 'api-key-and-application-key', hosts: [...DATADOG_API_HOSTS], site: input.site ?? 'us1', host, nextSteps: [ 'Create an API key and application key in Datadog Organization Settings.', hasApiKey ? null : secretSetupUrl(apiKeySecret), hasApplicationKey ? null : secretSetupUrl(applicationKeySecret), 'Approve the Datadog API hosts for those secrets.', ].filter(Boolean), }, } } const validation = await validateKeys(input) const monitors = input.includeMonitors === false ? null : await listMonitors({ ...input, pageSize: 5 }) const dashboards = input.includeDashboards === false ? null : await listDashboards({ ...input, count: 5 }) return { ok: true, live: true, selfCheck: { dryRunMutations }, secrets: { apiKeySecret, applicationKeySecret, hasApiKey, hasApplicationKey }, validation, monitorCount: monitors?.count ?? null, monitorsSample: monitors?.monitors.slice(0, 5) ?? [], dashboardCount: dashboards?.count ?? null, dashboardsSample: dashboards?.dashboards.slice(0, 5) ?? [], setup: { auth: 'api-key-and-application-key', hosts: [...DATADOG_API_HOSTS], host, apiKeyUrl: API_KEY_SETUP_URL, applicationKeyUrl: APPLICATION_KEY_SETUP_URL, }, } } export default smokeTest