Product data updates in CMS variant objects (#324)

This commit is contained in:
Dawid 2023-03-24 13:36:59 +01:00 committed by GitHub
parent 84df6acf9c
commit 2f94183dc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 165 additions and 0 deletions

View file

@ -0,0 +1,36 @@
fragment WebhookProduct on Product {
id
name
slug
media {
url
}
channelListings {
id
channel {
id
slug
}
isPublished
}
variants {
id
name
sku
channelListings {
id
channel {
id
slug
}
price {
amount
currency
}
}
metadata {
key
value
}
}
}

View file

@ -5,6 +5,7 @@ import packageJson from "../../../package.json";
import { productVariantUpdatedWebhook } from "./webhooks/product-variant-updated";
import { productVariantCreatedWebhook } from "./webhooks/product-variant-created";
import { productVariantDeletedWebhook } from "./webhooks/product-variant-deleted";
import { productUpdatedWebhook } from "./webhooks/product-updated";
export default createManifestHandler({
async manifestFactory(context) {
@ -19,6 +20,7 @@ export default createManifestHandler({
productVariantCreatedWebhook.getWebhookManifest(context.appBaseUrl),
productVariantUpdatedWebhook.getWebhookManifest(context.appBaseUrl),
productVariantDeletedWebhook.getWebhookManifest(context.appBaseUrl),
productUpdatedWebhook.getWebhookManifest(context.appBaseUrl),
],
extensions: [],
author: "Saleor Commerce",

View file

@ -0,0 +1,127 @@
import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next";
import { gql } from "urql";
import {
ProductUpdatedWebhookPayloadFragment,
UntypedWebhookProductFragmentDoc,
} from "../../../../generated/graphql";
import { saleorApp } from "../../../../saleor-app";
import { getCmsKeysFromSaleorItem } from "../../../lib/cms/client/metadata";
import { getChannelsSlugsFromSaleorItem } from "../../../lib/cms/client/channels";
import {
createCmsOperations,
executeCmsOperations,
executeMetadataUpdate,
} from "../../../lib/cms/client";
import { logger as pinoLogger } from "../../../lib/logger";
import { createClient } from "../../../lib/graphql";
import { fetchProductVariantMetadata } from "../../../lib/metadata";
export const config = {
api: {
bodyParser: false,
},
};
export const ProductUpdatedWebhookPayload = gql`
${UntypedWebhookProductFragmentDoc}
fragment ProductUpdatedWebhookPayload on ProductUpdated {
product {
...WebhookProduct
}
}
`;
export const ProductUpdatedSubscription = gql`
${ProductUpdatedWebhookPayload}
subscription ProductUpdated {
event {
...ProductUpdatedWebhookPayload
}
}
`;
export const productUpdatedWebhook = new SaleorAsyncWebhook<ProductUpdatedWebhookPayloadFragment>({
name: "Cms-hub product updated webhook",
webhookPath: "api/webhooks/product-updated",
event: "PRODUCT_UPDATED",
apl: saleorApp.apl,
query: ProductUpdatedSubscription,
});
export const handler: NextWebhookApiHandler<ProductUpdatedWebhookPayloadFragment> = async (
req,
res,
context
) => {
const { product } = context.payload;
const { saleorApiUrl, token } = context.authData;
const logger = pinoLogger.child({
product,
});
logger.debug("Called webhook PRODUCT_UPDATED");
if (!product) {
return res.status(500).json({
errors: [
"No product product data payload provided. Cannot process product variants syncronisation in CMS providers.",
],
});
}
const client = createClient(saleorApiUrl, async () => ({
token: token,
}));
const allCMSErrors: string[] = [];
product.variants?.forEach(async (variant) => {
const { variants: _, ...productFields } = product;
const productVariant = {
product: productFields,
...variant,
};
const productVariantChannels = getChannelsSlugsFromSaleorItem(productVariant);
const productMetadata = await fetchProductVariantMetadata(client, productVariant.id);
const productVariantCmsKeys = getCmsKeysFromSaleorItem({ metadata: productMetadata });
const cmsOperations = await createCmsOperations({
context,
client,
productVariantChannels: productVariantChannels,
productVariantCmsKeys: productVariantCmsKeys,
});
// Do not touch product variants which are not created or should be deleted.
// These operations should and will be performed by PRODUCT_VARIANT_CREATED and PRODUCT_VARIANT_DELETED webhooks.
// Otherwise we will end up with duplicated product variants in CMS providers! (or failed variant delete operations).
const cmsUpdateOperations = cmsOperations.filter(
(operation) => operation.operationType === "updateProduct"
);
const {
cmsProviderInstanceProductVariantIdsToCreate,
cmsProviderInstanceProductVariantIdsToDelete,
cmsErrors,
} = await executeCmsOperations({
cmsOperations: cmsUpdateOperations,
productVariant,
});
allCMSErrors.push(...cmsErrors);
await executeMetadataUpdate({
context,
productVariant,
cmsProviderInstanceIdsToCreate: cmsProviderInstanceProductVariantIdsToCreate,
cmsProviderInstanceIdsToDelete: cmsProviderInstanceProductVariantIdsToDelete,
});
});
if (!allCMSErrors.length) {
return res.status(200).end();
} else {
return res.status(500).json({ errors: allCMSErrors });
}
};
export default productUpdatedWebhook.createHandler(handler);