Skip to content

Commit

Permalink
Merge pull request #197 from run-crank/rhart/AssociateMarketoCookie
Browse files Browse the repository at this point in the history
Marketo Associate Web Activity Step
  • Loading branch information
russellbot authored Nov 7, 2022
2 parents b6c9958 + fd7c73d commit 367dd70
Show file tree
Hide file tree
Showing 7 changed files with 464 additions and 4 deletions.
138 changes: 135 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"grpc": "^1.24.11",
"lighthouse": "^5.6.0",
"moment": "^2.29.1",
"node-marketo-rest": "^0.7.8",
"psl": "^1.8.0",
"puppeteer": "^5.5.0",
"puppeteer-cluster": "^0.16.0",
Expand Down
21 changes: 20 additions & 1 deletion src/client/client-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BasicInteractionAware, DomAware, ResponseAware, MarketoAware, GoogleAna
import { Field } from '../core/base-step';
import { Page, Request } from 'puppeteer';
import * as Lighthouse from 'lighthouse';
import * as Marketo from 'node-marketo-rest';

class ClientWrapper {

Expand All @@ -12,12 +13,30 @@ class ClientWrapper {
public lighthouse: any;
public idMap: any;
public blobContainerClient: any;
public marketoClient: Marketo;
public marketoConnected: boolean = false;
public delayInSeconds: number;

constructor (page: Page, auth: grpc.Metadata, idMap: any, blobContainerClient: any, lighthouse = Lighthouse) {
constructor (page: Page, auth: grpc.Metadata, idMap: any, blobContainerClient: any, lighthouse = Lighthouse, delayInSeconds = 3) {
this.client = page;
this.idMap = idMap;
this.blobContainerClient = blobContainerClient;
this.lighthouse = lighthouse;

// Make a marketo connection if the auth metadata is passed.
if (auth.get('endpoint').length && auth.get('clientId').length && auth.get('clientSecret').length) {
this.marketoClient = new Marketo({
endpoint: `${auth.get('endpoint')[0]}/rest`,
identity: `${auth.get('endpoint')[0]}/identity`,
clientId: auth.get('clientId')[0],
clientSecret: auth.get('clientSecret')[0],
...(!!auth.get('partnerId')[0] && { partnerId: auth.get('partnerId')[0] }),
});
this.marketoConnected = true;
}

this.delayInSeconds = delayInSeconds;

// Keeps track of the number of inflight requests. @see this.waitForNetworkIdle()
this.client['__networkRequestsInflight'] = this.client['__networkRequestsInflight'] || 0;

Expand Down
12 changes: 12 additions & 0 deletions src/client/mixins/basic-interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,16 @@ export class BasicInteractionAware {
selector,
);
}

/**
* Returns an array of cookies that match the given cookieName.
*
* @param {String} cookieName - The name of the cookie.
* @returns {Array} - An array of cookies that match the cookieName.
*/
public async getCookie(cookieName: string) {
const cookies = await this.client.cookies();
const cookieArray = cookies.filter((cookie) => cookie.name === cookieName);
return cookieArray;
}
}
83 changes: 83 additions & 0 deletions src/client/mixins/marketo.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,85 @@
import * as Marketo from 'node-marketo-rest';
export class MarketoAware {
marketoClient: Marketo;
leadDescription: any;
delayInSeconds: number;
mustHaveFields = [
'email',
'updatedAt',
'createdAt',
'lastName',
'firstName',
'id',
'leadPartitionId',
].filter((f) => !!f);

public async findLeadByField(field: string, value: string, justInCaseField: string = null, partitionId: number = null) {
this.delayInSeconds > 0 ? await this.delay(this.delayInSeconds) : null;
const fields = await this.describeLeadFields();
const fieldList: string[] = fields.result.filter((field) => field.rest).map((field: any) => field.rest.name);
let response: any = {};

if (fieldList.join(',').length > 7168 && fieldList.length >= 1000) {
// If the length of the get request would be over 7KB, then the request
// would fail. And if the amount of fields is over 1000, it is likely
// not worth it to cache with the if statement below.
// Instead, we will only request the needed fields.
response = await this.marketoClient.lead.find(field, [value], { fields: [justInCaseField, ...this.mustHaveFields, partitionId ? 'leadPartitionId' : null] });
} else if (fieldList.join(',').length > 7168) {
// If the length of the get request would be over 7KB, then the request
// would fail. Instead, we will split the request every 200 fields, and
// combine the results.
response = await this.marketoRequestHelperFuntion(fieldList, field, value);
} else {
response = await this.marketoClient.lead.find(field, [value], { fields: fieldList });
}

// If a partition ID was provided, filter the returned leads accordingly.
if (partitionId && response && response.result && response.result.length) {
response.result = response.result.filter((lead: Record<string, any>) => {
return lead.leadPartitionId && lead.leadPartitionId === partitionId;
});
}

return response;
}

public async associateLeadById(leadId: string, cookie: string) {
this.delayInSeconds > 0 ? await this.delay(this.delayInSeconds) : null;
return await this.marketoClient.lead.associateLead(leadId, cookie);
}

private async marketoRequestHelperFuntion(fieldList, field, value) {
const response: any = {};
let allFields: { [key: string]: string; } = {};

for (let i = 0; i < fieldList.length && i <= 800; i += 200) {
const currFields = fieldList.slice(i, i + 200);
const currResponse = await this.marketoClient.lead.find(field, [value], { fields: currFields });
allFields = { ...allFields, ...currResponse.result[0] };
if (!i) {
response.requestId = currResponse.requestId;
response.success = currResponse.success;
}
}
response.result = [allFields];

return response;
}

public async describeLeadFields() {
this.delayInSeconds > 0 ? await this.delay(this.delayInSeconds) : null;
// This safely reduces the number of API calls that might have to be made
// in lead field check steps, but is an imcomplete solution.
// @todo Incorporate true caching based on https://github.com/run-crank/cli/pull/40
if (!this.leadDescription) {
this.leadDescription = await this.marketoClient.lead.describe();
}

return this.leadDescription;
}

public async delay(seconds: number) {
return new Promise((resolve) => { setTimeout(resolve, seconds * 1000); });
}
}
Loading

0 comments on commit 367dd70

Please sign in to comment.