Skip to content

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

Package listing

@kody/zendesk

src/smoke-test.ts

111 lines · 3.8 KB · TypeScript
import { parseAuth } from './auth.ts'
import { accounts } from './accounts.ts'
import { getMe } from './me.ts'
import {
	inferOperationFromPath,
	normalizeSubdomain,
	normalizeZendeskPath,
	oauthConnectUrl,
	permissionForOperation,
	zendeskHost,
} from './setup.ts'
import type { ZendeskAuthInput } from './types.ts'
import { requireRecord } from './types.ts'

export type SmokeTestInput = ZendeskAuthInput & {
	/** When true, skip the live `/users/me` query even if credentials exist. */
	setupOnly?: boolean
}

/**
 * Verify Zendesk helpers without returning email. Without credentials this
 * still returns setup URLs so the package is forkable before anyone connects.
 * @example
 * import smokeTest from 'kody:@kody/zendesk/smoke-test'
 * const result = await smokeTest()
 */
export async function smokeTest(params: SmokeTestInput = {}) {
	const connectUrl = oauthConnectUrl('zendesk', 'your-subdomain')
	const selfCheck = {
		ok: true as const,
		subdomainNormalizesHost: normalizeSubdomain('Acme.zendesk.com') === 'acme',
		ticketPath: normalizeZendeskPath('/tickets') === '/tickets',
		searchIsRead: inferOperationFromPath('GET', '/search') === 'tickets.read',
		ticketWrite: inferOperationFromPath('POST', '/tickets') === 'tickets.write',
		userSearchIsRead: inferOperationFromPath('GET', '/users/search') === 'users.read',
		userWrite: inferOperationFromPath('POST', '/users') === 'users.write',
		articleWrite: inferOperationFromPath('POST', '/help_center/sections/1/articles') === 'articles.write',
		articleRead: inferOperationFromPath('GET', '/help_center/articles') === 'articles.read',
		meRead: inferOperationFromPath('GET', '/users/me') === 'me.read',
		ticketWritePermission: permissionForOperation('tickets.write') === 'tickets:write',
		connectUrlUsesPlaceholderHost: connectUrl.includes(encodeURIComponent('your-subdomain.zendesk.com')),
		connectUrlUsesAuthorizations: connectUrl.includes('oauth%2Fauthorizations%2Fnew'),
		hostIsGeneric: zendeskHost('acme') === 'acme.zendesk.com',
	}
	const info = await accounts(params)
	const canLive = Boolean(info.preferredAuth) && Boolean(info.subdomain) && params.setupOnly !== true

	if (!canLive) {
		return {
			ok: true,
			live: false,
			selfCheck,
			authMode: info.preferredAuth,
			integrationName: info.integrationName,
			secretName: info.secretName,
			usernameSecretName: info.usernameSecretName,
			oauthConnected: info.oauthConnected,
			apiTokenSaved: info.apiTokenSaved,
			apiUsernameSaved: info.apiUsernameSaved,
			hasSubdomain: Boolean(info.subdomain),
			hasUserId: false,
			setup: {
				connectUrl: info.connectUrl,
				apiTokenUrl: info.apiTokenUrl,
				apiUsernameUrl: info.apiUsernameUrl,
				callbackUrl: info.callbackUrl,
				requiredHosts: info.requiredHosts,
			},
		}
	}

	const me = await getMe(params)
	return {
		ok: true,
		live: true,
		selfCheck,
		authMode: info.preferredAuth,
		integrationName: info.integrationName,
		secretName: info.secretName,
		usernameSecretName: info.usernameSecretName,
		oauthConnected: info.oauthConnected,
		apiTokenSaved: info.apiTokenSaved,
		apiUsernameSaved: info.apiUsernameSaved,
		hasSubdomain: Boolean(info.subdomain),
		hasUserId: Boolean(me.id),
		role: me.role,
		setup: {
			connectUrl: info.connectUrl,
			apiTokenUrl: info.apiTokenUrl,
			apiUsernameUrl: info.apiUsernameUrl,
			callbackUrl: info.callbackUrl,
			requiredHosts: info.requiredHosts,
		},
	}
}

/**
 * Verify Zendesk helpers without returning email.
 * @example
 * import smokeTest from 'kody:@kody/zendesk/smoke-test'
 * const result = await smokeTest({ setupOnly: true, subdomain: 'acme' })
 */
export default async function smokeTestEntrypoint(
	params: SmokeTestInput & Record<string, unknown> = {},
) {
	const input = requireRecord(params, 'smoke-test')
	return smokeTest({
		...parseAuth(input),
		setupOnly: input.setupOnly === true,
	})
}