Skip to content

Commit

Permalink
refactor: Create Interfaces for eCommerce (#964)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexs-mparticle committed Jan 21, 2025
1 parent c8340c3 commit 78a0cb2
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 30 deletions.
201 changes: 201 additions & 0 deletions src/ecommerce.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import {
ProductAction,
Product,
Promotion,
CommerceEvent,
} from '@mparticle/event-models';
import {
SDKEventAttrs,
SDKEventOptions,
TransactionAttributes,
} from '@mparticle/web-sdk';
import { valueof } from './utils';
import {
ProductActionType,
PromotionActionType,
CommerceEventType,
EventType,
} from './types';
import {
SDKEvent,
SDKEventCustomFlags,
SDKImpression,
SDKProduct,
SDKProductImpression,
SDKPromotion,
} from './sdkRuntimeModels';

interface IECommerceShared {
createProduct(
name: string,
sku: string | number,
price: string | number,
quantity?: string | number,
variant?: string,
category?: string,
brand?: string,
position?: number,
couponCode?: string,
attributes?: SDKEventAttrs
): SDKProduct | null;
createImpression(name: string, product: Product): SDKImpression | null;
createPromotion(
id: string | number,
creative?: string,
name?: string,
position?: number
): SDKPromotion | null;
createTransactionAttributes(
id: string | number,
affiliation?: string,
couponCode?: string,
revenue?: string | number,
shipping?: string | number,
tax?: number
): TransactionAttributes | null;
expandCommerceEvent(event: CommerceEvent): SDKEvent[] | null;
}

export interface SDKCart {
add(product: SDKProduct | SDKProduct[], logEvent?: boolean): void;
remove(product: SDKProduct | SDKProduct[], logEvent?: boolean): void;
clear(): void;
}

// Used for the public `eCommerce` namespace
export interface SDKECommerceAPI extends IECommerceShared {
logCheckout(
step: number,
option?: string,
attrs?: SDKEventAttrs,
customFlags?: SDKEventCustomFlags
): void;
logImpression(
impression: SDKProductImpression,
attrs?: SDKEventAttrs,
customFlags?: SDKEventCustomFlags,
eventOptions?: SDKEventOptions
): void;
logProductAction(
productActionType: valueof<typeof ProductActionType>,
product: SDKProduct | SDKProduct[],
attrs?: SDKEventAttrs,
customFlags?: SDKEventCustomFlags,
transactionAttributes?: TransactionAttributes,
eventOptions?: SDKEventOptions
): void;
logPromotion(
type: valueof<typeof PromotionActionType>,
promotion: SDKPromotion,
attrs?: SDKEventAttrs,
customFlags?: SDKEventCustomFlags,
eventOptions?: SDKEventOptions
): void;
setCurrencyCode(code: string): void;

/*
* @deprecated
*/
Cart: SDKCart;

/*
* @deprecated
*/
logPurchase(
transactionAttributes: TransactionAttributes,
product: SDKProduct | SDKProduct[],
clearCart?: boolean,
attrs?: SDKEventAttrs,
customFlags?: SDKEventCustomFlags
): void;

/*
* @deprecated
*/
logRefund(
transactionAttributes: TransactionAttributes,
product: SDKProduct | SDKProduct[],
clearCart?: boolean,
attrs?: SDKEventAttrs,
customFlags?: SDKEventCustomFlags
): void;
}

interface ExtractedActionAttributes {
Affiliation?: string;
'Coupon Code'?: string;
'Total Amount'?: number;
'Shipping Amount'?: number;
'Tax Amount'?: number;
'Checkout Option'?: string;
'Checkout Step'?: number;
'Transaction ID'?: string;
}
interface ExtractedProductAttributes {
'Coupon Code'?: string;
Brand?: string;
Category?: string;
Name?: string;
Id?: string;
'Item Price'?: number;
Quantity?: number;
Position?: number;
Variant?: string;
'Total Product Amount': number;
}
interface ExtractedPromotionAttributes {
Id?: string;
Creative?: string;
Name?: string;
Position?: number;
}
interface ExtractedTransactionId {
'Transaction ID'?: string;
}

// Used for the private `_Ecommerce` namespace
export interface IECommerce extends IECommerceShared {
buildProductList(event: SDKEvent, product: Product | Product[]): Product[];
convertProductActionToEventType(
productActionType: valueof<typeof ProductActionType>
): // https://go.mparticle.com/work/SQDSDKS-4801
typeof CommerceEventType | typeof EventType | null;
convertPromotionActionToEventType(
promotionActionType: valueof<typeof PromotionActionType>
): typeof CommerceEventType | null;
convertTransactionAttributesToProductAction(
transactionAttributes: TransactionAttributes,
productAction: ProductAction
): void;
createCommerceEventObject(
customFlags: SDKEventCustomFlags,
options?: SDKEventOptions
): SDKEvent | null;
expandProductAction(commerceEvent: CommerceEvent): SDKEvent[];
expandProductImpression(commerceEvent: CommerceEvent): SDKEvent[];
expandPromotionAction(commerceEvent: CommerceEvent): SDKEvent[];
extractActionAttributes(
attributes: ExtractedActionAttributes,
productAction: ProductAction
): void;
extractProductAttributes(
attributes: ExtractedProductAttributes,
product: Product
): void;
extractPromotionAttributes(
attributes: ExtractedPromotionAttributes,
promotion: Promotion
): void;
extractTransactionId(
attributes: ExtractedTransactionId,
productAction: ProductAction
): void;
generateExpandedEcommerceName(eventName: string, plusOne: boolean): string;
getProductActionEventName(
productActionType: valueof<typeof ProductActionType>
): string;
getPromotionActionEventName(
promotionActionType: valueof<typeof PromotionActionType>
): string;
sanitizeAmount(amount: string | number, category: string): number;
}
9 changes: 9 additions & 0 deletions src/ecommerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ var Messages = Constants.Messages;

export default function Ecommerce(mpInstance) {
var self = this;

// https://go.mparticle.com/work/SQDSDKS-4801
this.convertTransactionAttributesToProductAction = function(
transactionAttributes,
productAction
Expand Down Expand Up @@ -103,8 +105,11 @@ export default function Ecommerce(mpInstance) {
return Types.CommerceEventType.ProductRemoveFromCart;
case Types.ProductActionType.RemoveFromWishlist:
return Types.CommerceEventType.ProductRemoveFromWishlist;

// https://go.mparticle.com/work/SQDSDKS-4801
case Types.ProductActionType.Unknown:
return Types.EventType.Unknown;

case Types.ProductActionType.ViewDetail:
return Types.CommerceEventType.ProductViewDetail;
default:
Expand Down Expand Up @@ -139,6 +144,7 @@ export default function Ecommerce(mpInstance) {
);
};

// https://go.mparticle.com/work/SQDSDKS-4801
this.extractProductAttributes = function(attributes, product) {
if (product.CouponCode) {
attributes['Coupon Code'] = product.CouponCode;
Expand Down Expand Up @@ -170,12 +176,14 @@ export default function Ecommerce(mpInstance) {
attributes['Total Product Amount'] = product.TotalAmount || 0;
};

// https://go.mparticle.com/work/SQDSDKS-4801
this.extractTransactionId = function(attributes, productAction) {
if (productAction.TransactionId) {
attributes['Transaction Id'] = productAction.TransactionId;
}
};

// https://go.mparticle.com/work/SQDSDKS-4801
this.extractActionAttributes = function(attributes, productAction) {
self.extractTransactionId(attributes, productAction);

Expand Down Expand Up @@ -208,6 +216,7 @@ export default function Ecommerce(mpInstance) {
}
};

// https://go.mparticle.com/work/SQDSDKS-4801
this.extractPromotionAttributes = function(attributes, promotion) {
if (promotion.Id) {
attributes['Id'] = promotion.Id;
Expand Down
12 changes: 6 additions & 6 deletions src/identity-user-interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { AllUserAttributes, MPID, Product, User } from '@mparticle/web-sdk';
import { SDKIdentityTypeEnum } from './identity.interfaces';
import { MessageType } from './types';
import { BaseEvent } from './sdkRuntimeModels';
import { BaseEvent, SDKProduct } from './sdkRuntimeModels';
import Constants from './constants';
const { HTTPCodes } = Constants;

// Cart is Deprecated and private to mParticle user in @mparticle/web-sdk
// but we need to expose it here for type safety in some of our tests
interface Cart {
interface ICart {
/**
* @deprecated Cart persistence in mParticle has been deprecated. Please use mParticle.eCommerce.logProductAction(mParticle.ProductActionType.AddToCart, [products])
*/
add: (product: Product, logEventBoolean?: boolean) => void;
add: (product: SDKProduct, logEventBoolean?: boolean) => void;
/**
* @deprecated Cart persistence in mParticle has been deprecated. Please use mParticle.eCommerce.logProductAction(mParticle.ProductActionType.RemoveFromCart, [products])
*/
remove: (product: Product, logEventBoolean?: boolean) => void;
remove: (product: SDKProduct, logEventBoolean?: boolean) => void;
/**
* @deprecated Cart persistence in mParticle has been deprecated.
*/
clear: () => void;
/**
* @deprecated Cart Products have been deprecated
*/
getCartProducts: () => Product[];
getCartProducts: () => SDKProduct[];
}

// https://go.mparticle.com/work/SQDSDKS-5033
Expand All @@ -36,7 +36,7 @@ export interface IMParticleUser extends User {
/*
* @deprecated
*/
getCart(): Cart;
getCart(): ICart;
}

export interface ISDKUserIdentity {
Expand Down
13 changes: 11 additions & 2 deletions src/sdkRuntimeModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import IntegrationCapture from './integrationCapture';
import { INativeSdkHelpers } from './nativeSdkHelpers.interfaces';
import { ICookieSyncManager, IPixelConfiguration } from './cookieSyncManager';
import { IEvents } from './events.interfaces';
import { IECommerce, SDKECommerceAPI } from './ecommerce.interfaces';

// TODO: Resolve this with version in @mparticle/web-sdk
export type SDKEventCustomFlags = Dictionary<any>;
Expand Down Expand Up @@ -107,6 +108,11 @@ export interface SDKPromotion {
Position?: string;
}

export interface SDKImpression {
Name: string;
Product: SDKProduct;
}

export interface SDKProductImpression {
ProductImpressionList?: string;
ProductList?: SDKProduct[];
Expand Down Expand Up @@ -150,7 +156,9 @@ export interface SDKProduct {
Position?: number;
CouponCode?: string;
TotalAmount?: number;
Attributes?: { [key: string]: string };

// https://go.mparticle.com/work/SQDSDKS-4801
Attributes?: Record<string, unknown> | null;
}

export interface MParticleWebSDK {
Expand All @@ -170,6 +178,7 @@ export interface MParticleWebSDK {
_Forwarders: any;
_Helpers: SDKHelpersApi;
_Events: IEvents;
_Ecommerce: IECommerce;
config: SDKInitConfig;
_ServerModel: IServerModel;
_SessionManager: ISessionManager;
Expand Down Expand Up @@ -208,7 +217,7 @@ export interface MParticleWebSDK {
eventOptions?: SDKEventOptions
): void;
logBaseEvent(event: BaseEvent, eventOptions?: SDKEventOptions): void;
eCommerce: any;
eCommerce: SDKECommerceAPI;
logLevel: string;
ProductActionType: SDKProductActionType;
generateHash(value: string): string;
Expand Down
2 changes: 1 addition & 1 deletion src/sdkToEventsApiConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ export function convertProducts(
price: sdkProduct.Price,
quantity: sdkProduct.Quantity,
coupon_code: sdkProduct.CouponCode,
custom_attributes: sdkProduct.Attributes,
custom_attributes: sdkProduct.Attributes as Record<string, string>,
};
products.push(product);
}
Expand Down
2 changes: 1 addition & 1 deletion src/serverModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export interface IProductV2DTO {
ps: number;
cc: string | number;
tpa: number;
attrs: Record<string, string> | null;
attrs: Record<string, unknown> | undefined;
}

export interface IPromotionV2DTO {
Expand Down
10 changes: 2 additions & 8 deletions test/src/tests-batchUploader_3.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import sinon from 'sinon';
import { urls, apiKey, MPConfig, testMPID } from './config/constants';
import {
BaseEvent,
MParticleWebSDK,
SDKEvent,
SDKProductActionType,
} from '../../src/sdkRuntimeModels';
import { Batch, CustomEventData } from '@mparticle/event-models';
import Utils from './config/utils';
import { BatchUploader } from '../../src/batchUploader';
import { expect } from 'chai';
import _BatchValidator from '../../src/mockBatchCreator';
import Logger from '../../src/logger.js';
import { event0, event1, event2, event3 } from '../fixtures/events';
import fetchMock from 'fetch-mock/esm/client';
import { ProductActionType } from '../../src/types';
const { fetchMockSuccess, waitForCondition, hasIdentifyReturned } = Utils;

declare global {
Expand Down Expand Up @@ -137,7 +131,7 @@ describe('batch uploader', () => {
window.mParticle.logEvent('Test Event');

var product1 = window.mParticle.eCommerce.createProduct('iphone', 'iphoneSKU', 999);
window.mParticle.eCommerce.logProductAction(SDKProductActionType.AddToCart, product1);
window.mParticle.eCommerce.logProductAction(ProductActionType.AddToCart, product1);

const lastCall = fetchMock.lastCall();
const endpoint = lastCall[0];
Expand Down
Loading

0 comments on commit 78a0cb2

Please sign in to comment.