diff --git a/src/v1/sources/shopify/config.js b/src/v1/sources/shopify/config.js index 5d88353d60..81f095dbe9 100644 --- a/src/v1/sources/shopify/config.js +++ b/src/v1/sources/shopify/config.js @@ -31,10 +31,17 @@ const PIXEL_EVENT_MAPPING = { checkout_address_info_submitted: 'Checkout Address Info Submitted', checkout_contact_info_submitted: 'Checkout Contact Info Submitted', checkout_shipping_info_submitted: 'Checkout Shipping Info Submitted', - payment_info_submitted: 'Payment Info Submitted', + payment_info_submitted: 'Payment Info Entered', search_submitted: 'Search Submitted', }; +const RUDDER_ECOM_MAP = { + checkouts_create: 'Checkout Started Server', + checkouts_update: 'Checkout Updated', + orders_updated: 'Order Updated', + orders_create: 'Order Created', +}; + const contextualFieldMappingJSON = JSON.parse( fs.readFileSync(path.resolve(__dirname, 'pixelEventsMappings', 'contextualFieldMapping.json')), ); @@ -63,6 +70,14 @@ const checkoutStartedCompletedEventMappingJSON = JSON.parse( ), ); +const productMappingJSON = JSON.parse( + fs.readFileSync(path.resolve(__dirname, 'webhookEventsMapping', 'productMapping.json')), +); + +const lineItemsMappingJSON = JSON.parse( + fs.readFileSync(path.resolve(__dirname, 'webhookEventsMapping', 'lineItemsMapping.json')), +); + const pixelEventToCartTokenLocationMapping = { cart_viewed: 'properties.cart_id', checkout_address_info_submitted: commonCartTokenLocation, @@ -79,6 +94,7 @@ module.exports = { INTEGERATION, PIXEL_EVENT_TOPICS, PIXEL_EVENT_MAPPING, + RUDDER_ECOM_MAP, contextualFieldMappingJSON, cartViewedEventMappingJSON, productListViewedEventMappingJSON, @@ -86,4 +102,6 @@ module.exports = { productToCartEventMappingJSON, checkoutStartedCompletedEventMappingJSON, pixelEventToCartTokenLocationMapping, + productMappingJSON, + lineItemsMappingJSON, }; diff --git a/src/v1/sources/shopify/webhookEventsMapping/identifyMapping.json b/src/v1/sources/shopify/webhookEventsMapping/identifyMapping.json new file mode 100644 index 0000000000..0367267502 --- /dev/null +++ b/src/v1/sources/shopify/webhookEventsMapping/identifyMapping.json @@ -0,0 +1,112 @@ +[ + { + "sourceKeys": "id", + "destKeys": "userId" + }, + { + "sourceKeys": "email", + "destKeys": "traits.email" + }, + + { + "sourceKeys": "first_name", + "destKeys": "traits.firstName" + }, + + { + "sourceKeys": "last_name", + "destKeys": "traits.lastName" + }, + + { + "sourceKeys": "phone", + "destKeys": "traits.phone" + }, + + { + "sourceKeys": "addresses", + "destKeys": "traits.addressList" + }, + + { + "sourceKeys": "default_address", + "destKeys": "traits.address" + }, + + { + "sourceKeys": "shipping_address", + "destKeys": "traits.shippingAddress" + }, + + { + "sourceKeys": "billing_address", + "destKeys": "traits.billingAddress" + }, + + { + "sourceKeys": "accepts_marketing", + "destKeys": "traits.acceptsMarketing" + }, + + { + "sourceKeys": "orders_count", + "destKeys": "traits.orderCount" + }, + + { + "sourceKeys": "state", + "destKeys": "traits.state" + }, + { + "sourceKeys": "total_spent", + "destKeys": "traits.totalSpent" + }, + { + "sourceKeys": "note", + "destKeys": "traits.note" + }, + { + "sourceKeys": "verified_email", + "destKeys": "traits.verifiedEmail" + }, + { + "sourceKeys": "multipass_identifier", + "destKeys": "traits.multipassIdentifier" + }, + { + "sourceKeys": "tax_exempt", + "destKeys": "traits.taxExempt" + }, + { + "sourceKeys": "tags", + "destKeys": "traits.tags" + }, + { + "sourceKeys": "last_order_name", + "destKeys": "traits.lastOrderName" + }, + { + "sourceKeys": "currency", + "destKeys": "traits.currency" + }, + { + "sourceKeys": "marketing_opt_in_level", + "destKeys": "traits.marketingOptInLevel" + }, + { + "sourceKeys": "tax_exemptions", + "destKeys": "traits.taxExemptions" + }, + { + "sourceKeys": "sms_marketing_consent", + "destKeys": "traits.smsMarketingConsent" + }, + { + "sourceKeys": "admin_graphql_api_id", + "destKeys": "traits.adminGraphqlApiId" + }, + { + "sourceKeys": "accepts_marketing_updated_at", + "destKeys": "traits.acceptsMarketingUpdatedAt" + } +] diff --git a/src/v1/sources/shopify/webhookEventsMapping/lineItemsMapping.json b/src/v1/sources/shopify/webhookEventsMapping/lineItemsMapping.json new file mode 100644 index 0000000000..3bcef331b0 --- /dev/null +++ b/src/v1/sources/shopify/webhookEventsMapping/lineItemsMapping.json @@ -0,0 +1,32 @@ +[ + { + "sourceKeys": "product_id", + "destKey": "product_id", + "metadata": { + "type": "toString" + } + }, + { + "sourceKeys": "sku", + "destKey": "sku" + }, + { + "sourceKeys": "name", + "destKey": "title" + }, + { + "sourceKeys": "price", + "destKey": "price", + "metadata": { + "type": "toNumber" + } + }, + { + "sourceKeys": "vendor", + "destKey": "brand" + }, + { + "sourceKeys": "quantity", + "destKey": "quantity" + } +] diff --git a/src/v1/sources/shopify/webhookEventsMapping/mapping.json b/src/v1/sources/shopify/webhookEventsMapping/mapping.json new file mode 100644 index 0000000000..6c268ef13c --- /dev/null +++ b/src/v1/sources/shopify/webhookEventsMapping/mapping.json @@ -0,0 +1,10 @@ +[ + { + "sourceKeys": "line_items", + "destKeys": "products" + }, + { + "sourceKeys": "id", + "destKeys": "properties.order_id" + } +] diff --git a/src/v1/sources/shopify/webhookEventsMapping/productMapping.json b/src/v1/sources/shopify/webhookEventsMapping/productMapping.json new file mode 100644 index 0000000000..e78ed50dfc --- /dev/null +++ b/src/v1/sources/shopify/webhookEventsMapping/productMapping.json @@ -0,0 +1,24 @@ +[ + { + "sourceKeys": "id", + "destKey": "order_id", + "metadata": { + "type": "toString" + } + }, + { + "sourceKeys": "total_price", + "destKey": "value" + }, + { + "sourceKeys": "total_tax", + "destKey": "tax", + "metadata": { + "type": "toNumber" + } + }, + { + "sourceKeys": "currency", + "destKey": "currency" + } +] diff --git a/src/v1/sources/shopify/webhookTransformations/serverSideTransform.js b/src/v1/sources/shopify/webhookTransformations/serverSideTransform.js index 292a980a93..d0221f6950 100644 --- a/src/v1/sources/shopify/webhookTransformations/serverSideTransform.js +++ b/src/v1/sources/shopify/webhookTransformations/serverSideTransform.js @@ -11,11 +11,11 @@ const { MAPPING_CATEGORIES, IDENTIFY_TOPICS, ECOM_TOPICS, - RUDDER_ECOM_MAP, SUPPORTED_TRACK_EVENTS, SHOPIFY_TRACK_MAP, lineItemsMappingJSON, } = require('../../../../v0/sources/shopify/config'); +const { RUDDER_ECOM_MAP } = require('../config'); const { createPropertiesForEcomEventFromWebhook, getProductsFromLineItems, @@ -46,7 +46,7 @@ const ecomPayloadBuilder = (event, shopifyTopic) => { message.setEventType(EventType.TRACK); message.setEventName(RUDDER_ECOM_MAP[shopifyTopic]); - const properties = createPropertiesForEcomEventFromWebhook(event); + const properties = createPropertiesForEcomEventFromWebhook(event, shopifyTopic); message.properties = removeUndefinedAndNullValues(properties); // Map Customer details if present const customerDetails = get(event, 'customer'); diff --git a/src/v1/sources/shopify/webhookTransformations/serverSideUtils.test.js b/src/v1/sources/shopify/webhookTransformations/serverSideUtils.test.js index 4a2839103b..b94ec9c3dd 100644 --- a/src/v1/sources/shopify/webhookTransformations/serverSideUtils.test.js +++ b/src/v1/sources/shopify/webhookTransformations/serverSideUtils.test.js @@ -98,11 +98,11 @@ describe('serverSideUtils.js', () => { const result = createPropertiesForEcomEventFromWebhook(message); expect(result).toEqual({ products: [ - { brand: 'Hydrogen Vendor', price: '600.00', product_id: 7234590408818, quantity: 1 }, + { brand: 'Hydrogen Vendor', price: 600.0, product_id: '7234590408818', quantity: 1 }, { brand: 'Hydrogen Vendor', - price: '600.00', - product_id: 7234590408817, + price: 600.0, + product_id: '7234590408817', quantity: 1, title: 'The Collection Snowboard: Nitrogen', }, diff --git a/src/v1/sources/shopify/webhookTransformations/serverSideUtlis.js b/src/v1/sources/shopify/webhookTransformations/serverSideUtlis.js index 951fa479e4..df33d7a347 100644 --- a/src/v1/sources/shopify/webhookTransformations/serverSideUtlis.js +++ b/src/v1/sources/shopify/webhookTransformations/serverSideUtlis.js @@ -1,9 +1,6 @@ const { isDefinedAndNotNull } = require('@rudderstack/integrations-lib'); const { constructPayload } = require('../../../../v0/util'); -const { - lineItemsMappingJSON, - productMappingJSON, -} = require('../../../../v0/sources/shopify/config'); +const { lineItemsMappingJSON, productMappingJSON } = require('../config'); /** * Returns an array of products from the lineItems array received from the webhook event @@ -28,12 +25,15 @@ const getProductsFromLineItems = (lineItems, mapping) => { * @param {Object} message * @returns {Object} properties */ -const createPropertiesForEcomEventFromWebhook = (message) => { +const createPropertiesForEcomEventFromWebhook = (message, shopifyTopic) => { const { line_items: lineItems } = message; if (!lineItems || lineItems.length === 0) { return []; } const mappedPayload = constructPayload(message, productMappingJSON); + if (shopifyTopic === 'orders_updated' || shopifyTopic === 'checkouts_update') { + delete mappedPayload.value; + } mappedPayload.products = getProductsFromLineItems(lineItems, lineItemsMappingJSON); return mappedPayload; }; diff --git a/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts b/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts index 38f682ac6d..3db2e3ab10 100644 --- a/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts +++ b/test/integrations/sources/shopify/pixelTestScenarios/CheckoutStepsTests.ts @@ -2037,7 +2037,7 @@ export const pixelCheckoutStepsScenarios = [ SHOPIFY: true, }, type: 'track', - event: 'Payment Info Submitted', + event: 'Payment Info Entered', properties: { buyerAcceptsEmailMarketing: false, buyerAcceptsSmsMarketing: false, diff --git a/test/integrations/sources/shopify/webhookTestScenarios/CheckoutEventsTests.ts b/test/integrations/sources/shopify/webhookTestScenarios/CheckoutEventsTests.ts index a154ccb890..673f246455 100644 --- a/test/integrations/sources/shopify/webhookTestScenarios/CheckoutEventsTests.ts +++ b/test/integrations/sources/shopify/webhookTestScenarios/CheckoutEventsTests.ts @@ -217,16 +217,16 @@ export const checkoutEventsTestScenarios = [ SHOPIFY: true, }, type: 'track', - event: 'Checkout Started', + event: 'Checkout Started Server', properties: { - order_id: 35550298931313, + order_id: '35550298931313', value: '600.00', - tax: '0.00', + tax: 0, currency: 'USD', products: [ { - product_id: 7234590408817, - price: '600.00', + product_id: '7234590408817', + price: 600.0, brand: 'Hydrogen Vendor', quantity: 1, }, @@ -533,17 +533,16 @@ export const checkoutEventsTestScenarios = [ }, properties: { currency: 'USD', - order_id: 35374569160817, + order_id: '35374569160817', products: [ { brand: 'pixel-testing-rs', - price: '729.95', - product_id: 7234590638193, + price: 729.95, + product_id: '7234590638193', quantity: 1, }, ], - tax: '0.00', - value: '736.85', + tax: 0, }, timestamp: '2024-09-17T07:29:02.000Z', traits: { @@ -1207,15 +1206,14 @@ export const checkoutEventsTestScenarios = [ type: 'track', event: 'Order Updated', properties: { - order_id: 5778367414385, - value: '600.00', - tax: '0.00', + order_id: '5778367414385', + tax: 0, currency: 'USD', products: [ { - product_id: 7234590408817, + product_id: '7234590408817', title: 'The Collection Snowboard: Hydrogen', - price: '600.00', + price: 600, brand: 'Hydrogen Vendor', quantity: 1, }, @@ -1598,15 +1596,15 @@ export const checkoutEventsTestScenarios = [ type: 'track', event: 'Order Created', properties: { - order_id: 5778367414385, + order_id: '5778367414385', value: '600.00', - tax: '0.00', + tax: 0, currency: 'USD', products: [ { - product_id: 7234590408817, + product_id: '7234590408817', title: 'The Collection Snowboard: Hydrogen', - price: '600.00', + price: 600, brand: 'Hydrogen Vendor', quantity: 1, },