106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
|
|
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);
|
||
|
|
});
|