@causalfoundry/js-sdk
Version:
Causal Foundry WEB SDK (JS/TS)
252 lines (235 loc) • 7.32 kB
text/typescript
import { ItemObject } from "../typings";
import { ECommerceTypes } from "../typings";
import CfExceptionHandler from "../../../core/CfExceptionHandler";
import { isISOLocal } from "../../../utils";
import { z } from "zod";
const ItemObjectSchema = z.object({
id: z.string().min(1, "empty item id"),
type: z.string().min(1, "invalid item type"),
quantity: z.number().min(0, "invalid item quantity"),
price: z.number().min(0, "invalid item price"),
currency: z.string().min(2, "item currency required"),
stock_status: z.string().optional(),
promo_id: z.string().optional(),
facility_id: z.string().optional(),
discount: z.number().optional(),
});
const CheckoutPropertiesSchema = z
.object({
order_id: z.string().min(1, "empty checkout id"),
is_successful: z.boolean(),
cart_price: z.number().min(0, "negative checkout price"),
items: z.array(ItemObjectSchema).min(1, "checkout empty items list"),
cart_id: z.string().min(1, "empty checkout cart id"),
shop_mode: z.string().optional(),
meta: z.any().optional(),
})
.refine(
(data) => {
// Check for repeated IDs in items array
const itemIds = data.items.map((item) => item.id);
const uniqueIds = new Set(itemIds);
return uniqueIds.size === itemIds.length;
},
{
message: "checkout items list contains repeated ids",
path: ["items"],
}
);
const CartPropertiesSchema = z.object({
cart_id: z.string().min(1, "empty cart id"),
action: z.string().min(1, "cart action required"),
item: ItemObjectSchema,
meta: z.any().optional(),
});
const ItemRequestPropertiesSchema = z.object({
item_id: z.string().min(1, "empty request id"),
item_name: z.string().min(1, "empty request item name"),
manufacturer: z.string().min(1, "empty request item manufacturer"),
});
const ItemReportPropertiesSchema = z.object({
item_id: z.string().min(1, "empty item report id"),
item_type: z.string().min(1, "invalid item type"),
report_id: z.string().min(1, "empty report id"),
report_remarks: z.string().min(1, "empty report remarks"),
store_id: z.string().min(1, "report store id required"),
meta: z.any().optional(),
});
const CancelCheckoutPropertiesSchema = z
.object({
cancel_type_id: z.string().min(1, "empty cancel checkout id"),
type: z.string().min(1, "invalid cancel type"),
items: z.array(z.string()).min(1, "cancel checkout empty items list"),
reason: z.string().min(1, "cancel checkout reason required"),
meta: z.any().optional(),
})
.refine(
(data) => {
// Check for repeated IDs in items array
const uniqueItems = new Set(data.items);
return uniqueItems.size === data.items.length;
},
{
message: "cancel checkout items contains repeated ids",
path: ["items"],
}
);
const DeliveryPropertiesSchema = z
.object({
delivery_id: z.string().min(1, "empty delivery id"),
order_id: z.string().min(1, "empty delivery order id"),
is_urgent: z.boolean(),
action: z.string().min(1, "delivery action required"),
est_delivery_ts: z.string().optional(),
delivery_coordinates: z
.object({
lat: z.number(),
lon: z.number(),
})
.optional(),
dispatch_coordinates: z
.object({
lat: z.number(),
lon: z.number(),
})
.optional(),
meta: z.any().optional(),
})
.refine(
(data) => {
// is_urgent must be defined (not undefined)
return data.is_urgent !== undefined;
},
{
message: "delivery is_urgent value required",
path: ["is_urgent"],
}
)
.refine(
(data) => {
// Validate delivery_coordinates (lat/lon cannot be 0)
if (data.delivery_coordinates) {
return (
data.delivery_coordinates.lat !== 0 &&
data.delivery_coordinates.lon !== 0
);
}
return true;
},
{
message: "Invalid delivery_coordinates provided",
path: ["delivery_coordinates"],
}
)
.refine(
(data) => {
// Validate dispatch_coordinates (lat/lon cannot be 0)
if (data.dispatch_coordinates) {
return (
data.dispatch_coordinates.lat !== 0 &&
data.dispatch_coordinates.lon !== 0
);
}
return true;
},
{
message: "Invalid dispatch_coordinates provided",
path: ["dispatch_coordinates"],
}
)
.refine(
(data) => {
// Validate date format using isISOLocal
if (data.est_delivery_ts) {
return isISOLocal(data.est_delivery_ts);
}
return true;
},
{
message: "invalid delivery date format",
path: ["est_delivery_ts"],
}
);
export const verifyItemObject = (
eventName: ECommerceTypes,
itemObject: ItemObject
) => {
try {
ItemObjectSchema.parse(itemObject);
return true;
} catch (error) {
if (error instanceof z.ZodError) {
// Get the first validation error message
const firstError = error.issues[0];
const errorMessage = firstError?.message || "validation error";
new CfExceptionHandler().throwAndLogError(eventName, errorMessage);
} else {
// Fallback for unexpected errors
new CfExceptionHandler().throwAndLogError(eventName, "validation error");
}
return false;
}
};
// Event type to schema mapping
const eventSchemaMap = {
[ECommerceTypes.Item]: {
schema: ItemObjectSchema,
dataExtractor: (properties: any) => properties.item,
},
[ECommerceTypes.Checkout]: {
schema: CheckoutPropertiesSchema,
dataExtractor: (properties: any) => properties,
},
[ECommerceTypes.Cart]: {
schema: CartPropertiesSchema,
dataExtractor: (properties: any) => properties,
},
[ECommerceTypes.ItemRequest]: {
schema: ItemRequestPropertiesSchema,
dataExtractor: (properties: any) => properties,
},
[ECommerceTypes.ItemReport]: {
schema: ItemReportPropertiesSchema,
dataExtractor: (properties: any) => properties,
},
[ECommerceTypes.Delivery]: {
schema: DeliveryPropertiesSchema,
dataExtractor: (properties: any) => properties,
},
[ECommerceTypes.CancelCheckout]: {
schema: CancelCheckoutPropertiesSchema,
dataExtractor: (properties: any) => properties,
},
} as const;
// Generic validation function that handles all event types
export const validate = (
eventType: ECommerceTypes,
properties: any
): boolean => {
try {
const eventConfig = eventSchemaMap[eventType];
if (!eventConfig) {
new CfExceptionHandler().throwAndLogError(
eventType,
`Unknown event type: ${eventType}`
);
return false;
}
const { schema, dataExtractor } = eventConfig;
const dataToValidate = dataExtractor(properties);
// Parse the data - this will throw if validation fails
schema.parse(dataToValidate);
return true;
} catch (error) {
if (error instanceof z.ZodError) {
// Get the first validation error message
const firstError = error.issues[0];
const errorMessage = firstError?.message || "validation error";
new CfExceptionHandler().throwAndLogError(eventType, errorMessage);
} else {
// Fallback for unexpected errors
new CfExceptionHandler().throwAndLogError(eventType, "validation error");
}
return false;
}
};