Files
advanced-camera-card/src/ha/fetch.ts
T

86 lines
2.1 KiB
TypeScript

import type { ZodSchema } from 'zod';
import { localize } from '../localize/localize';
import { AdvancedCameraCardError, type Endpoint } from '../types';
import { homeAssistantGetSignedURLIfNecessary } from './sign-path';
import type { HomeAssistant } from './types';
/**
* Fetch a JSON response from a signed or unsigned endpoint and validate it
* against a Zod schema.
* May throw.
*
* @param hass Home Assistant instance.
* @param endpoint The endpoint to fetch from (string or Endpoint object).
* @param schema The Zod schema to validate the response against.
* @returns The parsed data or throws if fetch/validation fails.
*/
export const homeAssistantSignAndFetch = async <T>(
hass: HomeAssistant,
endpoint: Endpoint,
schema: ZodSchema<T>,
options?: {
timeoutSeconds?: number;
},
): Promise<T> => {
let url: string | null;
try {
url = await homeAssistantGetSignedURLIfNecessary(hass, endpoint);
} catch (error) {
throw new AdvancedCameraCardError(localize('error.failed_sign'), {
endpoint,
error,
});
}
if (!url) {
throw new AdvancedCameraCardError(localize('error.failed_sign'), {
endpoint,
});
}
let response: Response;
try {
response = await fetch(url, {
...(options?.timeoutSeconds && {
signal: AbortSignal.timeout(options.timeoutSeconds * 1000),
}),
});
} catch (error) {
throw new AdvancedCameraCardError(`${localize('error.failed_fetch')}: ${url}`, {
endpoint,
error,
});
}
if (!response.ok) {
throw new AdvancedCameraCardError(localize('error.failed_response'), {
endpoint,
response,
});
}
let data: unknown;
try {
data = await response.json();
} catch (error) {
throw new AdvancedCameraCardError(localize('error.invalid_response'), {
endpoint,
response,
error,
});
}
const parsed = schema.safeParse(data);
if (!parsed.success) {
throw new AdvancedCameraCardError(localize('error.invalid_response'), {
endpoint,
data,
error: parsed.error,
});
}
return parsed.data;
};