Skip to content

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

Package listing

@kody/gitlab

src/shape.ts

114 lines · 3.4 KB · TypeScript
import type { GitlabQuery } from './types.ts'

export function asRecord(value: unknown): Record<string, unknown> {
	return value && typeof value === 'object' ? (value as Record<string, unknown>) : {}
}

export function asString(value: unknown): string | null {
	return typeof value === 'string' ? value : null
}

export function asNumber(value: unknown): number | null {
	return typeof value === 'number' && Number.isFinite(value) ? value : null
}

export function asBoolean(value: unknown): boolean | null {
	return typeof value === 'boolean' ? value : null
}

export function asStringArray(value: unknown): string[] {
	if (!Array.isArray(value)) return []
	return value.filter((item): item is string => typeof item === 'string')
}

export function slimProject(data: Record<string, unknown>) {
	return {
		id: asNumber(data.id),
		name: asString(data.name),
		pathWithNamespace: asString(data.path_with_namespace),
		description: asString(data.description),
		visibility: asString(data.visibility),
		defaultBranch: asString(data.default_branch),
		webUrl: asString(data.web_url),
		lastActivityAt: asString(data.last_activity_at),
		archived: asBoolean(data.archived),
	}
}

export function slimIssue(data: Record<string, unknown>) {
	const author = asRecord(data.author)
	return {
		id: asNumber(data.id),
		iid: asNumber(data.iid),
		title: asString(data.title),
		state: asString(data.state),
		labels: asStringArray(data.labels),
		webUrl: asString(data.web_url),
		createdAt: asString(data.created_at),
		updatedAt: asString(data.updated_at),
		author: asString(author.username),
	}
}

export function slimMergeRequest(data: Record<string, unknown>) {
	const author = asRecord(data.author)
	return {
		id: asNumber(data.id),
		iid: asNumber(data.iid),
		title: asString(data.title),
		state: asString(data.state),
		draft: asBoolean(data.draft) ?? asBoolean(data.work_in_progress),
		sourceBranch: asString(data.source_branch),
		targetBranch: asString(data.target_branch),
		mergeStatus: asString(data.merge_status),
		webUrl: asString(data.web_url),
		createdAt: asString(data.created_at),
		updatedAt: asString(data.updated_at),
		author: asString(author.username),
	}
}

export function slimPipeline(data: Record<string, unknown>) {
	return {
		id: asNumber(data.id),
		iid: asNumber(data.iid),
		status: asString(data.status),
		ref: asString(data.ref),
		sha: asString(data.sha),
		source: asString(data.source),
		webUrl: asString(data.web_url),
		createdAt: asString(data.created_at),
		updatedAt: asString(data.updated_at),
	}
}

export function pickQuery(
	params: Record<string, unknown>,
	keys: readonly string[],
): GitlabQuery {
	const query: GitlabQuery = {}
	for (const key of keys) {
		const value = params[key]
		if (value === undefined || value === null) continue
		if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
			query[key] = value
		}
	}
	return query
}

export function readOptionalBoolean(value: unknown): boolean | undefined {
	return typeof value === 'boolean' ? value : undefined
}

export function readOptionalNumber(value: unknown): number | undefined {
	if (typeof value === 'number' && Number.isFinite(value)) return value
	if (typeof value === 'string' && value.trim() && Number.isFinite(Number(value))) {
		return Number(value)
	}
	return undefined
}

export function readOptionalString(value: unknown): string | undefined {
	return typeof value === 'string' && value.trim() ? value.trim() : undefined
}