Skip to content

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

Package listing

@kody/zoom

src/shape.ts

202 lines · 5.5 KB · TypeScript
import type { ZoomQuery } 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 pickQuery(
	params: Record<string, unknown>,
	keys: readonly string[],
): ZoomQuery {
	const query: ZoomQuery = {}
	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
}

export function slimUser(data: Record<string, unknown>) {
	return {
		id: asString(data.id),
		email: asString(data.email),
		displayName: asString(data.display_name),
		firstName: asString(data.first_name),
		lastName: asString(data.last_name),
		type: asNumber(data.type),
		status: asString(data.status),
		timezone: asString(data.timezone),
		dept: asString(data.dept),
	}
}

export function slimMeeting(data: Record<string, unknown>) {
	return {
		id: asNumber(data.id) ?? asString(data.id),
		uuid: asString(data.uuid),
		topic: asString(data.topic),
		type: asNumber(data.type),
		status: asString(data.status),
		startTime: asString(data.start_time),
		duration: asNumber(data.duration),
		timezone: asString(data.timezone),
		joinUrl: asString(data.join_url),
		hostId: asString(data.host_id),
		hostEmail: asString(data.host_email),
		agenda: asString(data.agenda),
		createdAt: asString(data.created_at),
	}
}

export function slimRecordingMeeting(data: Record<string, unknown>) {
	const files = Array.isArray(data.recording_files)
		? data.recording_files.map((file) => slimRecordingFile(asRecord(file)))
		: []
	return {
		id: asNumber(data.id) ?? asString(data.id),
		uuid: asString(data.uuid),
		topic: asString(data.topic),
		startTime: asString(data.start_time),
		duration: asNumber(data.duration),
		shareUrl: asString(data.share_url),
		totalSize: asNumber(data.total_size),
		recordingCount: asNumber(data.recording_count),
		files,
	}
}

export function slimRecordingFile(data: Record<string, unknown>) {
	return {
		id: asString(data.id),
		meetingId: asString(data.meeting_id),
		recordingStart: asString(data.recording_start),
		recordingEnd: asString(data.recording_end),
		fileType: asString(data.file_type),
		fileSize: asNumber(data.file_size),
		playUrl: asString(data.play_url),
		downloadUrl: asString(data.download_url),
		status: asString(data.status),
		recordingType: asString(data.recording_type),
	}
}

export function slimWebinar(data: Record<string, unknown>) {
	return {
		id: asNumber(data.id) ?? asString(data.id),
		uuid: asString(data.uuid),
		topic: asString(data.topic),
		type: asNumber(data.type),
		status: asString(data.status),
		startTime: asString(data.start_time),
		duration: asNumber(data.duration),
		timezone: asString(data.timezone),
		joinUrl: asString(data.join_url),
		hostId: asString(data.host_id),
		agenda: asString(data.agenda),
	}
}

export function pageMeta(data: Record<string, unknown>) {
	return {
		pageCount: asNumber(data.page_count),
		pageNumber: asNumber(data.page_number),
		pageSize: asNumber(data.page_size),
		totalRecords: asNumber(data.total_records),
		nextPageToken: asString(data.next_page_token),
	}
}

const MEETING_BODY_KEYS = [
	'topic',
	'type',
	'start_time',
	'duration',
	'timezone',
	'password',
	'agenda',
	'schedule_for',
	'recurrence',
	'settings',
	'tracking_fields',
	'template_id',
] as const

const WEBINAR_BODY_KEYS = [
	'topic',
	'type',
	'start_time',
	'duration',
	'timezone',
	'password',
	'agenda',
	'schedule_for',
	'recurrence',
	'settings',
	'tracking_fields',
] as const

export function buildMeetingBody(params: Record<string, unknown>): Record<string, unknown> {
	return pickBody(params, MEETING_BODY_KEYS, {
		start_time: ['start_time', 'startTime'],
		schedule_for: ['schedule_for', 'scheduleFor'],
		template_id: ['template_id', 'templateId'],
		tracking_fields: ['tracking_fields', 'trackingFields'],
	})
}

export function buildWebinarBody(params: Record<string, unknown>): Record<string, unknown> {
	return pickBody(params, WEBINAR_BODY_KEYS, {
		start_time: ['start_time', 'startTime'],
		schedule_for: ['schedule_for', 'scheduleFor'],
		tracking_fields: ['tracking_fields', 'trackingFields'],
	})
}

function pickBody(
	params: Record<string, unknown>,
	keys: readonly string[],
	aliases: Record<string, readonly string[]> = {},
): Record<string, unknown> {
	const body: Record<string, unknown> = {}
	for (const key of keys) {
		const names = aliases[key] ?? [key]
		for (const name of names) {
			if (params[name] !== undefined) {
				body[key] = params[name]
				break
			}
		}
	}
	return body
}