customerio-node icon indicating copy to clipboard operation
customerio-node copied to clipboard

Support for /api/v2/batch

Open samygch opened this issue 2 years ago • 6 comments

Hi, I was wondering if there's support for the Track V2 API, more specifically https://track.customer.io/api/v2/batch. If not, is there any workaround that we could use to send data through batch calls for identify and destroy functions?

samygch avatar Feb 14 '23 18:02 samygch

Hey @samygch, right now this library doesn't have any support for v2 of the track api. The workaround for now would be to make the request manually using an request library of your choice until we add support for it within the client library

mike-engel avatar Feb 16 '23 15:02 mike-engel

@mike-engel is there a public backlog or another place where we can track when this would be made available? Or get an estimation for it?

TimeTerminal avatar Jun 22 '23 23:06 TimeTerminal

@TimeTerminal unfortunately we don't have a public backlog, nor can I give any kind of timeline for when we'll be able to get this in ourselves

mike-engel avatar Jun 23 '23 07:06 mike-engel

A hacky but working way to use some of the v2 Track events before there are strongly typed signatures is to pass in the v2 url as part of the defaults constructor and dispatch on the TrackClient's request object directly.

For example, I needed to use track event v2 for dedup and back dating.

const cioV2 = new TrackClient(
  siteId
  apiKey,
  { url: 'https://track.customer.io/api/v2' },
);

cioV2.request.post(`${cioV2.trackRoot}/entity`, {
  type: 'person',
  identifiers: {
    id,
  },
  action: 'event',
  name: 'my-custom-event-name',  
  id: ulid,
  timestamp: eventUnixTime,
  attributes: {}
});

Zambonilli avatar Aug 08 '23 19:08 Zambonilli

+1 from our friends at Novelcrafter. 😄

frankiecio avatar Jul 24 '24 03:07 frankiecio

If anyone else is looking for an interim solution for things like entity support, I threw together this:

View typings
export type CioEntity = "person" | "object" | "delivery";

// Common Types
type CioRelationshipIdentifier = {
    id?: string;
    email?: string;
    object_type_id?: string;
    object_id?: string;
};

type CioRelationshipAttributes = {
    role?: string;
    date_created?: number;
};

type CioRelationship = {
    identifiers: CioRelationshipIdentifier;
    relationship_attributes?: CioRelationshipAttributes;
};

// Attribute Types for Different Types
type CioPersonAttributes = {
    cio_subscription_preferences?: {
        topics: Record<string, boolean>;
    };
};

type CioObjectAttributes = Record<string, any>;

type CioDeliveryAttributes = {
    device_token: string;
};

// Actions for Different Types
type CioPersonActions =
    | "identify"
    | "unsuppress"
    | "suppress"
    | "merge"
    | "delete_device"
    | "add_device"
    | "delete_relationships"
    | "add_relationships"
    | "page"
    | "screen"
    | "event"
    | "delete";

type CioObjectActions =
    | "delete_relationships"
    | "add_relationships"
    | "delete"
    | "identify_anonymous"
    | "identify";

type CioDeliveryActions = "event";

// Identifier Types for Each `type`
type CioPersonIdentifier = {
    id: string;
};

type CioObjectIdentifier = {
    object_type_id: string;
    object_id: string;
};

// Base Type for "person" and "object"
interface CioBaseType<
    TType extends CioEntity,
    TAction,
    TIdentifier,
    TAttributes,
> {
    type: TType;
    action: TAction;
    identifiers: TIdentifier;
    attributes: TAttributes;
    cio_relationships?: CioRelationship[];
}

// Specific Types for Each Type of Payload
type CioPersonPayload = CioBaseType<
    "person",
    CioPersonActions,
    CioPersonIdentifier,
    CioPersonAttributes
>;

type CioObjectPayload = CioBaseType<
    "object",
    CioObjectActions,
    CioObjectIdentifier,
    CioObjectAttributes
>;

// Separate CioDeliveryPayload Without Identifiers
type CioDeliveryPayload = {
    type: "delivery";
    action: CioDeliveryActions;
    name: string;
    attributes: CioDeliveryAttributes;
    cio_relationships?: CioRelationship[];
};

// Discriminated Union Type
export type CioEntityPayload =
    | CioPersonPayload
    | CioObjectPayload
    | CioDeliveryPayload;

// Mapping Type: CioEntityData
export type CioEntityData = {
    person: Omit<CioPersonPayload, "type">;
    object: Omit<CioObjectPayload, "type">;
    delivery: Omit<CioDeliveryPayload, "type">;
};

export type CioError = {
    errors: {
        reason: string;
        field: string;
        message: string;
    }[];
};
import type { CioEntity, CioEntityData } from "./cio.types";
import axios from "axios";
import { Buffer } from "buffer"; // Import Buffer to handle Base64 encoding

/**
 * Customer.io's Node SDK doesn't support their v2 tracking API,
 * so we just use their REST API directly.
 * https://github.com/customerio/customerio-node/issues/136
 */
export class CioTrackV2 {
    private baseUrl = "https://track.customer.io/api/v2";
    private authHeader: string;

    constructor(
        private siteId: NexusEnvKeys["CUSTOMERIO_TRACKING_SITE_ID"],
        private apiKey: NexusEnvKeys["CUSTOMERIO_TRACKING_API_KEY"],
    ) {
        // Set up Basic Auth header using Base64 encoding of API_key:
        const credentials = `${this.siteId}:${this.apiKey}`;
        this.authHeader = Buffer.from(credentials).toString("base64");
    }

    public async createEntity<T extends CioEntity>(
        entity: T,
        data: CioEntityData[T],
    ): Promise<void> {
        await axios.post(
            `${this.baseUrl}/entity`,
            {
                type: entity,
                ...data,
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Basic ${this.authHeader}`,
                },
            },
        );
    }
}

nenorrell avatar Nov 05 '24 08:11 nenorrell