Skip to content

Commit

Permalink
klaviyo device mode onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
utsabc committed Feb 23, 2021
1 parent f071344 commit b216fcb
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 3 deletions.
175 changes: 175 additions & 0 deletions integrations/Klaviyo/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable no-underscore-dangle */
import logger from "../../utils/logUtil";
import ScriptLoader from "../ScriptLoader";
import { extractCustomFields } from "../../utils/utils";

class Klaviyo {
constructor(config) {
this.publicApiKey = config.publicApiKey;
this.apiKey = config.privateApiKey;
this.listId = config.listId;
this.consent = config.consent;
this.smsConsent = config.smsConsent;
this.name = "KLAVIYO";
this.CONFIG_MEMBERSHIP = "Membership";
this.CONFIG_SUBSCRIBE = "Subscribe";
}

init() {
logger.debug("===in init Klaviyo===");
ScriptLoader(
"klaviyo-integration",
`https://static.klaviyo.com/onsite/js/klaviyo.js?company_id=${this.publicApiKey}`
);
}

isLoaded() {
logger.debug("===in isLoaded Klaviyo===");

return !!window._learnq;
}

isReady() {
logger.debug("===in isReady Klaviyo===");

return !!window._learnq;
}

// To be used later for any requirement for list api using native SDK

/*
addUserToList(message, conf) {
const BASE_ENDPOINT = "https://a.klaviyo.com";
// Check if list Id is present in message properties, if yes override
let targetUrl = `${BASE_ENDPOINT}/api/v2/list/${this.listId}`;
if (message.context.traits.properties.listId) {
targetUrl = `${BASE_ENDPOINT}/api/v2/list/${message.context.traits.properties.listId}`;
}
const profile = {
email: message.context.traits.email,
phone_number: message.context.traits.phone,
};
// If func is called as membership func else subscribe func
if (conf === "Membership") {
targetUrl = `${targetUrl}/members`;
} else {
// get consent statuses from message if availabe else from dest config
targetUrl = `${targetUrl}/subscribe`;
profile.sms_consent = message.context.traits.properties.smsConsent
? message.context.traits.properties.smsConsent
: this.smsConsent;
profile.$consent = message.context.traits.properties.consent
? message.context.traits.properties.consent
: this.consent;
}
fetch(targetUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Origin: "*",
},
body: JSON.stringify({
api_key: this.apiKey,
profiles: [profile],
}),
})
.then((response) => response.status)
.then((data) => {
logger.log("Success:", data);
})
.catch((error) => {
logger.error("Error:", error);
});
}
*/

identify(rudderElement) {
const { message } = rudderElement;
if (!message.context.traits) logger.error("user traits not present");
let payload = {
$id: message.userId,
$email: message.context.traits.email,
$phone_number: message.context.traits.phone,
$first_name: message.context.traits.firstName,
$last_name: message.context.traits.lastName,
$organization: message.context.traits.organization,
$title: message.context.traits.title,
$city: message.context.traits.city,
$region: message.context.traits.region,
$country: message.context.traits.country,
$zip: message.context.traits.zip,
};
if (!payload.$email && !payload.$phone_number) {
logger.error("user phone or email not present");
return;
}
// Extract other K-V property from traits about user custom properties
try {
payload = extractCustomFields(
message,
payload,
["context.traits"],
[
"email",
"firstName",
"lastName",
"phone",
"title",
"organization",
"city",
"region",
"country",
"zip",
"image",
"timezone",
"anonymousId",
"userId",
"properties",
]
);
} catch (err) {
logger.debug(`Error occured at extractCustomFields ${err}`);
}
window._learnq.push(["identify", payload]);
// TO be used for List API for future requirements
/*
const addToList = message.context.traits.properties
? message.context.traits.properties.addToList
: false;
if (
(!!this.listId || !!message.context.traits.properties.listId) &&
addToList
) {
this.addUserToList(message, this.CONFIG_MEMBERSHIP);
this.addUserToList(message, this.CONFIG_SUBSCRIBE);
} else {
logger.info(
`Cannot process list operation as listId is not available, both in message or config`
);
}
*/
}

track(rudderElement) {
const { message } = rudderElement;
if (message.properties)
window._learnq.push(["track", message.event, message.properties]);
else window._learnq.push(["track", message.event]);
}

page(rudderElement) {
const { message } = rudderElement;
if (message.properties.additionalInfo)
window._learnq.push([
"track",
`Catagoty: ${message.categoty}, Page: ${message.name}`,
message.properties.pageInfo,
]);
else window._learnq.push(["track"]);
}
}

export default Klaviyo;
3 changes: 3 additions & 0 deletions integrations/Klaviyo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Klaviyo from "./browser";

export default Klaviyo;
3 changes: 2 additions & 1 deletion integrations/client_server_name.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const clientToServerNames = {
PENDO: "Pendo",
LYTICS: "Lytics",
APPCUES: "Appcues",
POSTHOG: "PostHog"
POSTHOG: "PostHog",
KLAVIYO: "Klaviyo"
};

export { clientToServerNames };
4 changes: 3 additions & 1 deletion integrations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import * as Pendo from "./Pendo";
import * as Lytics from "./Lytics";
import * as Appcues from "./Appcues";
import * as Posthog from "./Posthog";
import * as Klaviyo from "./Klaviyo";

// the key names should match the destination.name value to keep partity everywhere
// (config-plan name, native destination.name , exported integration name(this one below))
Expand Down Expand Up @@ -54,7 +55,8 @@ const integrations = {
PENDO: Pendo.default,
LYTICS: Lytics.default,
APPCUES: Appcues.default,
POSTHOG: Posthog.default
POSTHOG: Posthog.default,
KLAVIYO: Klaviyo.default
};

export { integrations };
4 changes: 3 additions & 1 deletion integrations/integration_cname.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ const commonNames = {
APPCUES: "APPCUES",
POSTHOG: "POSTHOG",
PostHog: "POSTHOG",
Posthog: "POSTHOG"
Posthog: "POSTHOG",
KLAVIYO: "KLAVIYO",
Klaviyo: "KLAVIYO"
};

export { commonNames };
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"crypto-js": "3.1.9-1",
"each": "^1.2.2",
"fs": "0.0.1-security",
"get-value": "^3.0.1",
"gh-release": "^3.5.0",
"is": "^3.3.0",
"json3": "^3.3.3",
Expand All @@ -70,6 +71,7 @@
"regenerator-runtime": "^0.13.7",
"rudder-component-cookie": "0.0.1",
"semver": "^7.1.3",
"set-value": "^3.0.2",
"universal-analytics": "^0.4.20",
"xmlhttprequest": "^1.8.0"
},
Expand Down
41 changes: 41 additions & 0 deletions utils/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// import * as XMLHttpRequestNode from "Xmlhttprequest";
import { parse } from "component-url";
import get from "get-value";
import set from "set-value";
import logger from "./logUtil";
import { commonNames } from "../integrations/integration_cname";
import { clientToServerNames } from "../integrations/client_server_name";
Expand Down Expand Up @@ -491,6 +493,44 @@ function flattenJsonPayload(data) {
}
/* ------- End FlattenJson ----------- */

// Extract fileds from message with exclusions
// Pass the keys of message for extraction and
// exclusion fields to exlude and the payload to map into
/* eslint-disable array-callback-return */
// -----------------Example-------------------
// extractCustomFields(message,payload,["traits", "context.traits", "properties"], "email",
// ["firstName",
// "lastName",
// "phone",
// "title",
// "organization",
// "city",
// "region",
// "country",
// "zip",
// "image",
// "timezone"])
// -------------------------------------------
// The above call will map the fields other than the
// exlusion list from the given keys to the destination payload
function extractCustomFields(message, destination, keys, exclusionFields) {
keys.map((key) => {
const messageContext = get(message, key);
if (messageContext) {
const values = [];
Object.keys(messageContext).map((value) => {
if (!exclusionFields.includes(value)) values.push(value);
});
values.map((val) => {
if (!(typeof messageContext[val] === "undefined")) {
set(destination, val, get(messageContext, val));
}
});
}
});
return destination;
}

export {
replacer,
generateUUID,
Expand All @@ -508,4 +548,5 @@ export {
type,
flattenJsonPayload,
checkReservedKeywords,
extractCustomFields,
};

0 comments on commit b216fcb

Please sign in to comment.