dreame-action/scripts/setup-metaobject.ts

106 lines
3.2 KiB
TypeScript
Raw Normal View History

2026-06-19 00:53:13 +08:00
type GraphqlResponse<T> = {
data?: T;
errors?: unknown;
};
const shop = requiredEnv("SHOPIFY_SHOP_DOMAIN");
const token = requiredEnv("SHOPIFY_ADMIN_ACCESS_TOKEN");
const endpoint = `https://${shop}/admin/api/2026-01/graphql.json`;
const definitionMutation = `#graphql
mutation DreameMetaobjectDefinitionCreate($definition: MetaobjectDefinitionCreateInput!) {
metaobjectDefinitionCreate(definition: $definition) {
metaobjectDefinition {
id
type
}
userErrors {
field
message
}
}
}
`;
const metaobjectMutation = `#graphql
mutation DreameMetaobjectCreate($metaobject: MetaobjectCreateInput!) {
metaobjectCreate(metaobject: $metaobject) {
metaobject {
id
handle
}
userErrors {
field
message
}
}
}
`;
async function main() {
await graphql(definitionMutation, {
definition: {
type: "dreame_activity",
name: "Dreame Activity",
access: { storefront: "NONE" },
fieldDefinitions: [
{ key: "activity_id", name: "Activity ID", type: "number_integer", required: true },
{ key: "api_app_id", name: "API App ID", type: "single_line_text_field", required: true },
{ key: "api_secret", name: "API Secret", type: "single_line_text_field", required: true },
{ key: "status_api_url", name: "Status API URL", type: "url", required: true },
{ key: "push_api_url", name: "Push API URL", type: "url", required: true },
{ key: "redirect_url", name: "Redirect URL", type: "url", required: true },
{ key: "button_text", name: "Button Text", type: "json", required: true },
{ key: "shop", name: "Shop Override", type: "single_line_text_field", required: false },
],
},
});
await graphql(metaobjectMutation, {
metaobject: {
type: "dreame_activity",
handle: "dreame-activity-config",
fields: [
{ key: "activity_id", value: "12" },
{ key: "api_app_id", value: "mock-app-id" },
{ key: "api_secret", value: "mock-secret" },
{ key: "status_api_url", value: "https://third-party.example/status" },
{ key: "push_api_url", value: "https://third-party.example/push" },
{ key: "redirect_url", value: "https://activity.example.com" },
{ key: "button_text", value: JSON.stringify({ en: "Claim Your Reward", "zh-CN": "领取奖励" }) },
{ key: "shop", value: shop },
],
},
});
}
async function graphql<T>(query: string, variables: Record<string, unknown>): Promise<GraphqlResponse<T>> {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Shopify-Access-Token": token,
},
body: JSON.stringify({ query, variables }),
});
const payload = (await response.json()) as GraphqlResponse<T>;
if (!response.ok || payload.errors) {
throw new Error(JSON.stringify(payload.errors || payload));
}
console.log(JSON.stringify(payload, null, 2));
return payload;
}
function requiredEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`${name} is required`);
}
return value;
}
main().catch((error) => {
console.error(error);
process.exit(1);
});