Skip to content

Instantly share code, notes, and snippets.

@edlaver
Last active November 7, 2023 10:57
Show Gist options
  • Save edlaver/aad8220f1ee17fa45dc1246a499a35d7 to your computer and use it in GitHub Desktop.
Save edlaver/aad8220f1ee17fa45dc1246a499a35d7 to your computer and use it in GitHub Desktop.
Add as an update effect to a Gadget record that has your metafield.
module.exports = async ({ api, record, params, logger, connections }) => {
const metafieldNamespace = "<Your metafield namespace>";
const metafieldKey = "<Your metafield key>";
const metafieldType = "json"
const metafieldOwnerId = `gid://shopify/Shop/${record.id}`; // `record.id` here is the ID of our `shop` record
const gadgetFieldName = "<The matching field name in your gadget record, e.g. myMetafield>";
// Check if record change includes our metafield data
if (record.changed(gadgetFieldName)) {
logger.info(
`"${gadgetFieldName}" record was changed, attempting to save to Shopify`
);
const recordChanges = record.changes();
logger.debug({ record, recordChanges }, "Record changes");
logger.debug("Checking for current Shopify connection");
if (connections.shopify.current) {
// Check we're using an embedded app with shopify session token / tenancy. Should also prevent action from running on sync from Shopify...
const shopifyApi = connections.shopify.current;
// @ts-ignore
// shopifyApi.options.apiVersion = "2022-07"; // Override the version for this effect
let response;
try {
response = await shopifyApi.graphql(
`
mutation setMetafield($metafieldsSetInput: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafieldsSetInput) {
metafields {
id
namespace
key
}
userErrors {
field
message
}
}
}
`,
{
metafieldsSetInput: [
{
namespace: metafieldNamespace,
key: metafieldKey,
type: metafieldType,
ownerId: metafieldOwnerId,
value: record[gadgetFieldName],
},
],
}
);
} catch (err) {
logger.error(
err,
`Error running graphQL mutation - saving metafield "${gadgetFieldName}" to Shopify`
);
throw err;
}
if (response?.metafieldsSet?.userErrors?.length) {
const message = `User error(s) saving "${gadgetFieldName}" metafield to Shopify`;
logger.error(response?.metafieldsSet?.userErrors, message);
throw new Error(message);
} else {
logger.info(
response,
`"${gadgetFieldName}" metafield saved successfully to Shopify!`
);
}
} else {
logger.warn("Missing current Shopify connection");
}
} else {
// Settings were not changed
logger.debug(`"${gadgetFieldName}" not changed, nothing to do...`);
}
};
// More generic version of original function, that takes params rather than hardcoding the variables
export async function writeMetafieldsFromGadgetRecord({
metafieldNamespace, // <Your metafield namespace>
metafieldKey, // <Your metafield key>
metafieldType, // i.e. "json";
metafieldOwnerId, // i.e. `gid://shopify/Shop/${record.id}`; // `record.id` here is the ID of our `shop` record
gadgetFieldName, // The matching field name in your gadget record, e.g. myMetafield
metafieldValue, // The value to set the metafield to, coerced to the correct type. See: https://shopify.dev/docs/apps/custom-data/metafields/types;
force, // Force the metafield to be written to Shopify, even if the value hasn't changed
record, // Context from Gadget
logger, // Context from Gadget
connections, // Context from Gadget
}) {
// Check if record change includes our metafield data
if (force || record.changed(gadgetFieldName)) {
logger.info(
`"${gadgetFieldName}" record was changed (or 'force' is set), attempting to save to Shopify`
);
const recordChanges = record.changes();
logger.debug(
{ record, recordChanges },
"writeMetafieldsFromGadgetRecord > Record changes"
);
logger.debug(
"writeMetafieldsFromGadgetRecord > Checking for current Shopify connection"
);
// Check we're using an embedded app with shopify session token / tenancy. Should also prevent action from running on sync from Shopify...
let shopifyApi = connections?.shopify?.current;
// Otherwise, try to get a connection for the shopId, assuming it's attached to the record
// TODO: Allow the shopId to be passed in as a param
// TODO: Work with a passed in `domain` as well, i.e.
// `const shopifyApi = await connections.shopify.forShopDomain("mycoolshop.myshopify.com");`
if (!shopifyApi && record.shopId) {
shopifyApi = await connections.shopify.forShopId(record.shopId);
}
if (!shopifyApi) {
logger.error(
{ connections, record },
"writeMetafieldsFromGadgetRecord > Missing required Shopify connection"
);
return;
}
let response;
try {
response = await shopifyApi.graphql(
`#graphql
mutation setMetafield($metafieldsSetInput: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafieldsSetInput) {
metafields {
id
namespace
key
}
userErrors {
field
message
}
}
}
`,
{
metafieldsSetInput: [
{
namespace: metafieldNamespace,
key: metafieldKey,
type: metafieldType,
ownerId: metafieldOwnerId,
value: metafieldValue,
},
],
}
);
} catch (err) {
logger.error(
{ err },
`writeMetafieldsFromGadgetRecord > Error running graphQL mutation - saving metafield "${gadgetFieldName}" to Shopify`
);
throw err;
}
if (response?.metafieldsSet?.userErrors?.length) {
const message = `writeMetafieldsFromGadgetRecord > User error(s) saving "${gadgetFieldName}" metafield to Shopify`;
logger.error(
{ userErrors: response?.metafieldsSet?.userErrors },
message
);
throw new Error(message);
} else {
logger.info(
response,
`writeMetafieldsFromGadgetRecord > "${gadgetFieldName}" metafield saved successfully to Shopify!`
);
}
} else {
// Settings were not changed
logger.debug(
`writeMetafieldsFromGadgetRecord > "${gadgetFieldName}" not changed, nothing to do...`
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment