Skip to content

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

Package listing

@kody/datadog

src/incidents.ts

154 lines · 5.9 KB · TypeScript
import { asStringArray, clean, datadogRequest, mutationPreview, positiveInt } from './core.ts'
import type { DatadogIncidentInput } from './types.ts'

function incidentId(params: DatadogIncidentInput): string {
	const id = clean(params.incidentId ?? params.id)
	if (!id) throw new Error('incidentId is required.')
	return id
}

function summarizeIncident(item: any) {
	const attributes = item?.attributes ?? item ?? {}
	return {
		id: item?.id ?? attributes.id ?? null,
		title: attributes.title ?? null,
		publicId: attributes.public_id ?? attributes.publicId ?? null,
		state: attributes.state ?? null,
		severity: attributes.fields?.severity?.value ?? attributes.severity ?? null,
		customerImpacted: attributes.customer_impacted ?? attributes.customerImpacted ?? null,
		created: attributes.created ?? attributes.created_at ?? null,
		modified: attributes.modified ?? attributes.modified_at ?? null,
		resolved: attributes.resolved ?? attributes.resolved_at ?? null,
	}
}

function incidentPayload(params: DatadogIncidentInput, type = 'incidents') {
	if (params.body && typeof params.body === 'object') return params.body
	const attributes: Record<string, unknown> = { ...(params.attributes ?? {}) }
	if (clean(params.title)) attributes.title = clean(params.title)
	if (params.customerImpacted != null) attributes.customer_impacted = params.customerImpacted
	if (params.fields && typeof params.fields === 'object') attributes.fields = params.fields
	return {
		data: {
			type,
			attributes,
		},
	}
}

/** List incidents. GET /api/v2/incidents */
export async function listIncidents(params: DatadogIncidentInput = {}) {
	const query: Record<string, unknown> = {}
	if (params.pageSize != null || params.perPage != null) {
		query['page[size]'] = positiveInt(params.pageSize ?? params.perPage, 10, 100)
	}
	if (params.page != null) query['page[offset]'] = positiveInt(params.page, 0, 10_000)
	const include = asStringArray(params.include)
	if (include.length) query.include = include.join(',')
	const body = await datadogRequest('/api/v2/incidents', { ...params, query })
	const incidents = Array.isArray(body?.data) ? body.data : []
	return {
		count: incidents.length,
		incidents: incidents.map(summarizeIncident),
		meta: body?.meta ?? null,
	}
}

/** Get one incident. GET /api/v2/incidents/{incident_id} */
export async function getIncident(params: DatadogIncidentInput = {}) {
	const id = incidentId(params)
	const include = asStringArray(params.include)
	const query = include.length ? { include: include.join(',') } : undefined
	const body = await datadogRequest('/api/v2/incidents/' + encodeURIComponent(id), {
		...params,
		query,
	})
	return { incident: summarizeIncident(body?.data ?? body), raw: body }
}

/** Search incidents. GET /api/v2/incidents/search */
export async function searchIncidents(params: DatadogIncidentInput = {}) {
	const query: Record<string, unknown> = {}
	if (clean(params.query)) query.query = clean(params.query)
	if (params.pageSize != null || params.perPage != null) {
		query['page[size]'] = positiveInt(params.pageSize ?? params.perPage, 10, 100)
	}
	if (params.page != null) query['page[offset]'] = positiveInt(params.page, 0, 10_000)
	const include = asStringArray(params.include)
	if (include.length) query.include = include.join(',')
	const body = await datadogRequest('/api/v2/incidents/search', { ...params, query })
	const incidents = Array.isArray(body?.data) ? body.data : []
	return {
		count: incidents.length,
		incidents: incidents.map(summarizeIncident),
		meta: body?.meta ?? null,
	}
}

/** Create an incident. Requires confirm. Pass dryRun: true to preview. */
export async function createIncident(params: DatadogIncidentInput = {}) {
	const payload = incidentPayload(params)
	const title = clean((payload as any)?.data?.attributes?.title)
	if (!title) throw new Error('Creating an incident requires title (or a full body).')
	const preview = mutationPreview(params, {
		action: 'create incident ' + title,
		method: 'POST',
		path: '/api/v2/incidents',
		body: payload,
	})
	if (preview) return preview
	const created = await datadogRequest('/api/v2/incidents', {
		...params,
		method: 'POST',
		body: payload,
	})
	return { ok: true, action: 'create', incident: summarizeIncident(created?.data ?? created), raw: created }
}

/** Update an incident. Requires confirm. Pass dryRun: true to preview. */
export async function updateIncident(params: DatadogIncidentInput = {}) {
	const id = incidentId(params)
	const payload = incidentPayload(params)
	;(payload as any).data.id = id
	const preview = mutationPreview(params, {
		action: 'update incident ' + id,
		method: 'PATCH',
		path: '/api/v2/incidents/' + encodeURIComponent(id),
		body: payload,
	})
	if (preview) return preview
	const updated = await datadogRequest('/api/v2/incidents/' + encodeURIComponent(id), {
		...params,
		method: 'PATCH',
		body: payload,
	})
	return { ok: true, action: 'update', incident: summarizeIncident(updated?.data ?? updated), raw: updated }
}

/** Delete an incident. Requires confirm. Pass dryRun: true to preview. */
export async function deleteIncident(params: DatadogIncidentInput = {}) {
	const id = incidentId(params)
	const preview = mutationPreview(params, {
		action: 'delete incident ' + id,
		method: 'DELETE',
		path: '/api/v2/incidents/' + encodeURIComponent(id),
	})
	if (preview) return preview
	const deleted = await datadogRequest('/api/v2/incidents/' + encodeURIComponent(id), {
		...params,
		method: 'DELETE',
	})
	return { ok: true, action: 'delete', incidentId: id, raw: deleted }
}

/**
 * List, search, or get Datadog incidents. Mutations live on named exports.
 * @example
 * import { listIncidents } from 'kody:@kody/datadog/incidents'
 * const result = await listIncidents({ pageSize: 10 })
 */
export default async function incidents(params: DatadogIncidentInput = {}) {
	if (params.incidentId || params.id) return await getIncident(params)
	if (clean(params.query)) return await searchIncidents(params)
	return await listIncidents(params)
}