78 lines
2.5 KiB
TypeScript
78 lines
2.5 KiB
TypeScript
|
|
import type { ActivityConfig } from "./activity.server";
|
||
|
|
|
||
|
|
type AdminGraphqlClient = {
|
||
|
|
graphql: (query: string, options?: { variables?: Record<string, unknown> }) => Promise<Response>;
|
||
|
|
};
|
||
|
|
|
||
|
|
type MetaobjectField = {
|
||
|
|
key: string;
|
||
|
|
value: string | null;
|
||
|
|
};
|
||
|
|
|
||
|
|
const METAOBJECT_QUERY = `#graphql
|
||
|
|
query DreameActivityConfig($type: String!, $handle: String!) {
|
||
|
|
metaobjectByHandle(handle: { type: $type, handle: $handle }) {
|
||
|
|
fields {
|
||
|
|
key
|
||
|
|
value
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
`;
|
||
|
|
|
||
|
|
export async function getActivityConfig(admin: AdminGraphqlClient): Promise<ActivityConfig> {
|
||
|
|
const type = process.env.METAOBJECT_TYPE || "dreame_activity";
|
||
|
|
const handle = process.env.METAOBJECT_HANDLE || "dreame-activity-config";
|
||
|
|
const response = await admin.graphql(METAOBJECT_QUERY, { variables: { type, handle } });
|
||
|
|
const payload = (await response.json()) as {
|
||
|
|
data?: { metaobjectByHandle?: { fields?: MetaobjectField[] } | null };
|
||
|
|
errors?: unknown;
|
||
|
|
};
|
||
|
|
|
||
|
|
const fields = payload.data?.metaobjectByHandle?.fields;
|
||
|
|
if (!fields?.length) {
|
||
|
|
throw new Error(`Dreame activity metaobject not found: ${type}/${handle}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const values = Object.fromEntries(fields.map((field) => [field.key, field.value]));
|
||
|
|
const buttonText = parseButtonText(values.button_text);
|
||
|
|
const activityId = Number(values.activity_id);
|
||
|
|
|
||
|
|
if (!Number.isFinite(activityId)) {
|
||
|
|
throw new Error("Metaobject field activity_id is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
activityId,
|
||
|
|
apiAppId: requireString(values.api_app_id, "api_app_id"),
|
||
|
|
apiSecret: requireString(values.api_secret, "api_secret"),
|
||
|
|
statusApiUrl: requireString(values.status_api_url, "status_api_url"),
|
||
|
|
pushApiUrl: requireString(values.push_api_url, "push_api_url"),
|
||
|
|
redirectUrl: requireString(values.redirect_url, "redirect_url"),
|
||
|
|
buttonText,
|
||
|
|
shop: values.shop || undefined,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function requireString(value: string | null | undefined, key: string): string {
|
||
|
|
if (!value) {
|
||
|
|
throw new Error(`Metaobject field ${key} is required`);
|
||
|
|
}
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseButtonText(value: string | null | undefined): Record<string, string> {
|
||
|
|
if (!value) {
|
||
|
|
throw new Error("Metaobject field button_text is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
const parsed = JSON.parse(value) as Record<string, unknown>;
|
||
|
|
const entries = Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === "string");
|
||
|
|
if (!entries.length) {
|
||
|
|
throw new Error("Metaobject field button_text must contain at least one locale");
|
||
|
|
}
|
||
|
|
|
||
|
|
return Object.fromEntries(entries);
|
||
|
|
}
|