Skip to content

Instantly share code, notes, and snippets.

@rdraward
Created December 14, 2022 16:16
Show Gist options
  • Save rdraward/a9028e5ad25a6ad1cc73281606aabe2a to your computer and use it in GitHub Desktop.
Save rdraward/a9028e5ad25a6ad1cc73281606aabe2a to your computer and use it in GitHub Desktop.
An example of a code effect added to the Update action of a Shopify Customer model that overrides the metafield data in Gadget first, then sends the information to Shopify so that the data stays in sync
/**
* Fetch the ids for metafields on a resource
*/
const fetchMetafieldIds = async ({ resource, shopifyApi, metafieldSchema, id }) => {
const gidResource = resource.charAt(0).toUpperCase() + resource.slice(1);
const gid = `gid://shopify/${gidResource}/${id}`;
return (
await shopifyApi.graphql(
`
query MetafieldIds($id: ID!) {
metafields: ${resource}(id: $id) {
${metafieldSchema.metafields.map(
({ namespace, key }) =>
`${namespace}_${key}: metafield(namespace: ${JSON.stringify(namespace)}, key: ${JSON.stringify(key)}) { id }`
)}
}
}
`,
{
id: gid,
}
)
).metafields;
};
/**
* Get the shopify metafield configuration for all metadata fields in `model`
*/
const getModelMetafieldSchema = (model) => {
const metafields = [];
const privateMetafields = [];
for (const field of Object.values(model.fields)) {
const contributorConfig = field.contributorConfiguration["shopify/contributor/field/metafield"];
if (!contributorConfig) continue;
const metafield = contributorConfig.metafield;
if (!metafield.namespace || !metafield.key) continue;
if (!metafield.isPrivate) {
metafields.push({ ...metafield, apiIdentifier: field.apiIdentifier });
} else {
privateMetafields.push({ ...metafield, apiIdentifier: field.apiIdentifier });
}
}
return { metafields, privateMetafields };
};
const buildRESTMetafieldsPayload = async ({ resource, shopifyApi, model, record }) => {
const metafieldSchema = getModelMetafieldSchema(model);
const metafieldIds = await fetchMetafieldIds({ resource, shopifyApi, metafieldSchema, id: record.id });
// Make sure the field we're updating has a metafield and that if it has a Shopify id then include id
return metafieldSchema.metafields.map(({ namespace, key, type, apiIdentifier }) => {
const gid = metafieldIds[`${namespace}_${key}`]?.id;
const id = gid ? gid.match(/^gid:\/\/shopify\/[A-Za-z]+\/(\d+)$/)[1] : null;
return {
id,
namespace,
key,
type,
value: record[apiIdentifier],
};
});
};
// Once a record has been updated in Gadget, fire off an update to the Shopify API
// to keep our meta fields in sync
module.exports = async ({ model, record, connections }) => {
const shopifyApi = await connections.shopify.forShopDomain("mycoolshop.myshopify.com");
// If customer's order count is greater than 10, mark them with a VIP metafield flag
if (record.ordersCount > 10) {
// Set VIP flag to true
// This field will have been set up as a metafield in the Shopify Customer model
record.isVip = true;
// Update metafield on a resource through the REST API
await shopifyApi.customer.update(Number(record.id), {
metafields: await buildRESTMetafieldsPayload({
resource: "customer",
shopifyApi,
model,
record,
}),
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment