0.1.8 • Published 3 years ago

mkto-manager v0.1.8

Weekly downloads
8
License
ISC
Repository
github
Last release
3 years ago

BETA - Use at your own risk

I am still working out the final method corrections and improved error handling, I do not consider this ready for production use. However, PRs are welcome if you would like to help develop this package or have opinions on how to improve the infrastructure.

TODOs

  • Implement Database, Lead, & Activities Handler classes
  • Implement User Management classes
  • Implement IoC Container
  • Improve BaseAsset validation with Yup
  • Improve MktoResponse validation with Yup
  • Review MktoRequest retry method requirement
  • Implement Event Emitter on MktoBase.find() (for database hooks)
  • Develop Event Emitting MktoBase.stream() method to replace BulkProcess
  • Implement Event Emitter on MktoRequest (for database hooks) - Deprecated
  • Implement Event Emitter on BulkProcess instead of synchronous callback system - Deprecated
  • Cleanup BulkProcess logging - Deprecated
  • Develop tests and stubs for API

MktoManager

Dedicated Node library for scripting automated changes and reports for Marketo instances. Emphasis on Asset (Email, Landing Page, Form, File, Folder, etc) interactions, as well as Lead record and User record management. Attempts to standardize and compensate for Marketo's inconsistent API structures.

Read more on the Marketo REST API Here: Marketo REST Api Docs

Usage

Installation

$ npm i --save mkto-manager

Create your Manager Object

//  Retrieve the mkto-manager factory
const makeManager = require("mkto-manager");

//  Define your REST API Credentials
const marketoRestCredentials = {
	mktoBaseUrl: "https://<Marketo Instance ID>.mktorest.com",
	mktoClientId: "marketo-client-id-guid-here",
	mktoClientSecret: "marketoClientSecretHashHere",
};

//  Instantiate your MktoManager instance
const mktoManager = new makeManager(marketoRestCredentails);

Assets

The primary focus of this library is for Asset management. Some Asset handlers are still in progress.

  • mktoManager.assets.Channel
  • mktoManager.assets.Email
  • mktoManager.assets.EmailTemplate
  • mktoManager.assets.File
  • mktoManager.assets.Folder
  • mktoManager.assets.Form
  • mktoManager.assets.LandingPage
  • mktoManager.assets.LandingPageRedirect
  • mktoManager.assets.LandingPageTemplate
  • mktoManager.assets.Program
  • mktoManager.assets.Segment
  • mktoManager.assets.SmartCampaign
  • mktoManager.assets.SmartList
  • mktoManager.assets.Tag

Querying the API for an Asset record using .find()

Each Asset Handler class contains a static async find() method for retrieving records from the Marketo API. The .find() method will define and amend the Endpoint URI as needed depending on the Asset Handler class and the search parameters you pass to it.

Examples:

//  Find Landing Page by ID
mktoManager.assets.LandingPage.find({ id: 1234 });

//  Find Landing Page by Name
mktoManager.assets.LandingPage.find({ name: "My Cool Landing Page" });

//  Find Landing Pages by Parent Folder
mktoManager.assets.LandingPage.find({
	folder: {
		id: 123, //  Folder ID
		type: "Folder", //  ["Program", "Folder"]
	},
});

//  Get multiple Programs
mktoManager.assets.Program.find({
	offset: 0, //  Offset value, like a paging token (sort of)
	maxReturn: 200, //  Defaults to 20 per the API Docs, maximum 200
});

Query Responses

All .find() method responses will return an instance of MktoResponse. MktoResponse acts as a wrapper around the real Marketo Response.

The MktoResponse will have a single property for determining the success of the request, as well as helper methods for accessing the results.

mktoManager.assets.LandingPage
	//  Retrieve Landing Page by ID
	.find({ id: 1234 })
	//  Utilize the Marketo Response
	.then(function (mktoResponse) {
		//  Check if the API Response was successful
		if (mktoResponse.success === true) {
			//  Get the first result - still needed if you only expect 1 result
			const firstLandingPageResult = mktoResponse.getFirst();
			//  firstLandingPageResult is an instantiated instance of the LandingPage Handler

			//  Get all results as an array of instantiated instances of the Handler
			const allLandingPageResults = mktoResponse.getAll();
		} else {
			//  Capture Mkto API Warnings or Errors
			const mktoWarnings = mktoResponse.warnings;
			const mktoErrors = mktoResponse.errors;
		}
	});

Find Request Events

You can attach listeners for various steps within the .find() request.

Pre-Request - Emitted just before the HTTP Request

mktoManager.assets.LandingPage.on("find_request", requestConfig => {
	// requestConfig = {
	//     url: <URI String>,
	//     method: 'get',
	//     params: <Search Params Object>,
	// }
});

Find Response - Emitted upon successful response

mktoManager.assets.LandingPage.on("find_response", mktoResponse => {
	//  Returns the instance of mktoResponse that is also returned by the method.
});

Find Error - Emitted upon a failed HTTP Response

mktoManager.assets.LandingPage.on("find_error", AxiosError => {
	//  Returns the AxiosError object from Axios
});

Note: Attaching listeners to mktoManager.assets.LandingPage using the on() method will trigger the event listener for all subsequent find methods. Use once() or off() to manage your event listeners on Asset Handlers.

Read more on the component/emitter package here.

Streaming Asset Records using stream()

Due to Marketo's constraint of a maximum of 200 Asset records returned per find() request, we also have included a static method for creating an API Record stream to easily iterate over all API records that are qualified by your find() search parameters passed the 200 return limit.

NOTE: The stream method offered on all Asset Handler classes is intended to replace the original BulkProcess helper class, which will be deprecated.

stream() is used similar to find(), but must be synchronously composed, and then asynchronously run.

stream() will return a single array of all retrieved records instantiated as instances of the Asset Handler class.

//  Retrieve ALL Landing Pages that fit our search criteria without the maxReturn bounds
//  Construct our stream handler that we can attach event listeners to and execute on-demand
const streamHandler = mktoManager.assets.LandingPage.stream({
	//  Search Conditions
	status: "approved",

	//  Return modifiers
	offset: 0, //  Defaults to 0 to start, and is auto-incremented within the stream method()
	//  Use a custom offset if you prefer to skip the initial records

	maxReturn: 200, //  Defaults to the maximum 200.
	//  Reduce this maxReturn to reduce the number of results returned in the emitted "request_success" event
	//  This can be used to manage the size of your looping logic per "request_success" event
});

//  Stream will bubble the internal `find()` requests "find_request" event carrying the request config object
streamHandler.on("find_request", requestConfig => {
	// requestConfig = {
	//     url: <URI String>,
	//     method: 'get',
	//     params: <Search Params Object>,
	// }
});

//  Each iterative `find()` request will emit a "request_success" event that carries the mktoResponse from `find()`
streamHandler.on("success", mktoResponse => {
	//  We can safely assume this event was triggered when this
	//  `mktoResponse` instance was successful - `mktoResponse.success === true`

	//  Iterate over the results and do whatever you need
	mktoResponse.getAll().forEach(landingPageAsset => {
		//  Your code here...
	});
});

//  We also have emitted events for issues with the HTTP Request
streamHandler.on("request_http_error", mktoResponse => {
	//  mktoResponse.status !== 200
});

//  And issues with the Marketo Request
streamHandler.on("request_mkto_error", mktoResponse => {
	//  mktoResponse.success === false
});

//  Finally, we have an emitted event when the stream is finished
streamHandler.on("finished", streamHandlerInstance => {
	//  Returns the copy of the streamHandler.
	//  All retrieved data can be accessed via streamHandlerInstance.data
	// streamHandlerInstance.data = [
	//     <LandingPage>,
	//     <LandingPage>,
	//     <LandingPage>,
	//     <LandingPage>,
	//     <LandingPage>,
	//     <LandingPage>,
	//     <LandingPage>
	// ]
});

//  After attaching all of your event listeners, you can execute the async stream
streamHandler.run().then(streamHandlerData => {
	//  The async `run()` method will also return the streamHandlerInstance.data property
	//  so you can use it from the resulting `finished` event or await the results here.
    //  However - it is recommended you operate on the emitted `success` event and individual mktoResponse record collections
});

Asset Management

Each Asset instance shares many properties and methods.

Data Management

Access the Record ID value using the computed .id property. This is mapped to retrieve the ID from the property where it is stored.

Manage the Asset data using the .set() and .get() methods.

const landingPageName = myLandingPageAsset.get("name");
//  landingPageName = "My Cool Landing Page"

//  Set a new Landing Page Name
myLandingPageAsset.set("name", "My Cooler Landing Page");

This updates the local data property, but has not triggered the update to the API.

Check if data has been changed

By using a data caching system, we preserve a non-mutated copy of the data.

.isChanged is a computed boolean that will define if the data has been changed. This allows us to quickly check if the data has been mutated, and if we need to send the updated data to the Marketo API.

You can also review what data has been mutated by using the changedData computed property. This will return an object of just the properties that have been altered.


Advanced

Library

All Marketo API logic is contained within lib/.

lib/index.js will read the lib/assets/ directory and load all Asset Handlers into the module export. This include BaseAsset, which all other Asset Handlers are based on. Usage, User, and ~Lead~ Handler library information is also consumed here.

:warning: All HTTP request methods are asynchronous and return Promises.

Core Functions

MktoRequest

The MktoRequest class is a Marketo specific Axios/RateLimit/ApiConsumer wrapper that instantiates our Axios HTTP instance with necessary details to communicate with our Marketo API. MktoRequest accepts the necessary API credentials as parameters, and returns an object with initial methods.

Uses axios-rate-limit to set a default Rate Per Second to try and remain compliant with Marketo's 100 calls per 20 seconds and Maximum of 10 concurrent API calls.

Default Rate Limit: {maxRPS: 4}.

This default rate limit is set to meet the exact rate limit criteria that Marketo defines:

Daily Quota: Subscriptions are allocated 50,000 API calls per day (which resets daily at 12:00AM CST).  You can increase your daily quota through your account manager.
Rate Limit: API access per instance limited to 100 calls per 20 seconds.
Concurrency Limit:  Maximum of 10 concurrent API calls.

However, Marketo still complains if you run updates at the maximum Rate/Concurrency Limit :)

You will not need to use MktoRequest directly.

MktoResponse

MktoResponse wraps API responses in an interactive Collection-type object. MktoResponse validates HTTP and API responses and offers result-array getter methods that return instantiated instances of each Asset's corresponding Handler.

//  Get all Landing Page's in a specific Folder
mktoManager.assets.LandingPage.find({
	folder: {
		id: 123, //  Folder or Program ID
		type: "Folder", //  ["Program", "Folder"]
	},
}).then(function (mktoResponse) {
	//  Check if the API Response was successful
	if (mktoResponse.success === true) {
		//  Get the first result - still needed if you only expect 1 result
		const firstLandingPageResult = mktoResponse.getFirst();
		//  firstLandingPageResult is an instantiated instance of the LandingPage Handler

		//  Get all results as an array of instantiated instances of the Handler
		const allLandingPageResults = mktoResponse.getAll();
	} else {
		//  Capture Mkto API Warnings or Errors
		const mktoWarnings = mktoResponse.warnings;
		const mktoErrors = mktoResponse.errors;
	}
});

MktoResponse Properties

PropertyDescription
_resFull Axios Response Object - minus the Axios data property.
_resultClassStores the Handler instance if one was passed.
_dataRaw Axios data property.
------
statusHTTP Status Code as returned by Axios.
successHandler specific logic for True/False success. Successful responses can still return Zero results.
resultRaw Marketo 'result' data, usually an array of records.
warningsArray of Marketo Warnings - will return empty array if no warnings.
errorsArray of Marketo Errors - will return empty array if no errors.
dataHandler specific - either an Array of results Instantiated as Handler instances, or a single Instantiated Handler object.

A summary prop is also available that offers a quick summary of Axios Request and Response / Mkto API Response information - great for quickly visualizing a summary of the response when developing.

mktoResponse.summary = {
	//  Request
	header: this._res.request._header,
	requestURL: this._res.config.url,
	method: this._res.config.method,
	params: this._res.config.params,
	//  Response
	status: this._res.status,
	success: this.success,
	result: this.result,
	errors: this.errors,
	warnings: this.warnings,
};

Full mktoResponse Example Print

/*
mktoResponse = {
    status: 200,    //  HTTP Status Code
    success: true,  //  API Response Success

    //  Array of raw results
    result: [
        { id: 1, name: 'My Landing Page' ....}
        ....
    ],
    //  `result` Array as Instantiated Handler instances
    data: [
        <Instantiated Handler Result>,
        <Instantiated Handler Result>,
        ....
    ],

    //  Response Warnings and Errors
    warnings: [
        //  Array of Marketo Warnings, if they were sent
        //  Defaults to empty array
    ],
    errors: [
        //  Array of Marketo Errors, if they were sent
        //  Defaults to empty array
    ],

    summary: {
        //  Request
        header: this._res.request._header,
        requestURL: this._res.config.url,
        method: this._res.config.method,
        params: this._res.config.params,
        //  Response
        status: this._res.status,
        success: this.success,
        result: this.result,
        errors: this.errors,
        warnings: this.warnings,
    }


    //  Original Marketo Response Data
    _data: {
        success: true,
        requestId: '#asdasdasd',
        result: [
            { id: 1, name: 'Toaster' ....} <Raw Asset Results>
            ....
        ],
    }

    //  Reference to the Handler Instance
    _resultClass: <Asset Class reference>,

    //  Raw Axios Response
    _res: <Raw Axios Response>
}
*/

NOTE: MktoResponse is extended for some of the "special" Marketo endpoints, like User Management. More details below in the User section.


BaseAsset

BaseAsset is a factory function that creates a starting point for all Asset API "Handlers", including the instantiation of our shared instance of MktoRequest. API Credentials are passed to the exported factory function. Each Asset Handler Instance shares this MktoRequest instance for REST API communication.

Create Asset

To create a net-new Asset, you can instantiate an instance of the Asset Handler, and then call the create() method.

const myNewLandingPageData = {
	name: "My First Landing Page", //  Page Name, required
	description: "", //  Page Description, optional
	template: 9, //  Template ID, required
	folder: {
		//  Folder Object, required
		type: "Folder",
		id: 11,
	},
};

const myNewLandingPage = new mktoManager.assets.LandingPage(myNewLandingPageData);

//  Send the Create request for our new Landing Page
const createMktoResponse = await myNewLandingPage.create();

if (createMktoResponse.success === true) {
	//  The Response Object should now contain a newly instantiated Landing Page with the data from the API, including the new ID
	//  Get the first (and only) result
	const myLandingPage = createMktoResponse.getFirst();
}

You can now use this instantiated instance to set your Landing Page content!

Update Asset

Each extended Class defines an Active Record type approach to API record management.

For example: A retrieved Landing Page record will store it's record data (only metadata per the API) in the data property. Record properties are retrieved and set via the corresponding methods:

Method/PropertyDescription
Asset.dataObject with all Asset or User record data.
Asset._dataObject with all Asset or User record data - we store this object to compare changes to the data property.
Asset.get(propertyName)Retrieves the given Property from the Asset.data object.
Asset.set(propertyName, newValue)Sets the given Property in the Asset.data object to newValue.
Asset.isChangedComputed boolean for depicting if any data property has been altered from it's original API data.
Asset.changedDataComputed object that will always list the data properties that have been altered from what was last retrieved from the API (the _data property).

Here is an example of retrieving a record from the API, and updating one of it's properties:

//  Find Landing Page by ID
const specialPageSearchResponse = await mktoManager.assets.LandingPage.find({
	id: 1234,
});

//  Local reference to our first (and only) Landing Page Result
const mySpecialLandingPage = specialPageSearchResponse.getFirst();

//  Check the Landing Page Name property
if (mySpecialLandingPage.get("name") === "My Special LandingPage") {
	//  Update the Landing Page Name property
	mySpecialLandingPage.set("name", "My Super Special Landing Page");
}

At this point, the instance of mySpecialLandingPage has one of it's properties changed, but the update() call has not been made to the API.

You can check if a record instance has pending updated property data with the computed properties:

//  Check if the record has pending local changes (Not submitted to the API)
if (mySpecialLandingPage.isChanged) {
	//  Is true because we changed the `name` property

	//  Get the properties that have been changed locally
	console.log(mySpecialLandingPage.changedData);
	//  Prints:
	//  {
	//    name: "My Special Updated Landing Page"
	//  }
}

Now that we have updated the local instance of the API Record, we can make the update() call to POST the updated data back to Marketo:

const updateMktoResponse = await mySpecialLandingPage.update();

//  This returns a new instance of `MktoResponse` - you check for API success the same way as we did before.

if (updateMktoResponse.success === true) {
	//  Successful update of the Landing Page name property!
	//  If the `update()` response was successful,
	//    the record self updates the `_data` property,
	//    so it no longer is "changed"
	//  mySpecialLandingPage.isChanged === false
}

The original record self updates its property tracking to acknowledge the update() success, meaning isChanged will now be false.

I recommend you use the original mySpecialLandingPage instance - updateMktoResponse.getFirst() may not be a fully instantiated instance of LandingPage Asset because Marketo only returns minimal information from the update() call.

Mixins

To standardize and consolidate certain record type logic, shared functionality (props and methods) are written in Mixin objects and assigned to Class definitions where required.

Examples: Clone, Delete, Content, and Variables methods and property handlers.

Variables

//  Retrieve the current Asset's approved Node variables
Asset.getVariables({
    status: 'approved'  //  Optional, ['approved', 'draft']
}).then(getVariablesMktoResponse => ...)

Asset.updateVariables(variableId, newValue).then(updateVariableMktoResponse => ...)

Content

//  Retrieve the current Asset's approved Node content items
Asset.getContent({
    status: 'approved'  //  Optional, ['approved', 'draft']
}).then(getContentMktoResponse => ...)
//  Content Response structure is unique to each Asset. See Asset details below.

Asset.updateContent(contentId, newContent).then(updateContentMktoResponse => ...)
//  newContent is encoded as a Query String using the qs package.

Clone

//  Traditionally, only a Folder target is needed for cloning an Asset
Asset.clone({
    folder: {
        type: "Folder",  //  ["Folder", "Program"]
        id: 0            //  Folder ID
    }
}).then(cloneAssetMktoResponse => ...)

Delete

//  Not all Assets can be deleted - some Assets must be "Unapproved" prior to deletion
Asset.delete().then(deleteAssetMktoResponse => ...)
//  Traditionally returns { id: <ID of deleted asset> }

Drafts

//  Approve a Draft Node, if one exists
Asset.approveDraft().then(approveAssetMktoResponse => ...)
//  Traditionally returns { id: <ID of asset> }

//  Discard a Draft Node, if one exists
Asset.discardDraft().then(discardAssetDraftMktoResponse => ...)
//  Traditionally returns { id: <ID of asset> }

//  Unapprove an Approved Node, if one exists
Asset.unapprove().then(unapproveAssetMktoResponse => ...)
//  Traditionally returns { id: <ID of asset> }

All Asset method instructions:

All Assets extend BaseAsset, so the above mentions of data property management and create() and update() methods remains the same unless otherwise noted.

Additional Asset specific methods mentioned below.

Channel

Marketo API does not allow for Channel Creation or Updating.

Methods create() and update() are voided. This is primarily offered for the static find() method.

Email

Update

Due to Marketo's interesting choice of splitting Email "Metadata" updates to two separate endpoints, this method will need to check changedData for certain props and fire TWO Post requests.

The first POST is for the Email Metadata:

  • 'name',
  • 'description',
  • 'operational',
  • 'published',
  • 'textOnly',
  • 'webView'

The second POST is for the Email "Content" - but not email body content.

  • 'fromEmail',
  • 'fromName',
  • 'replyEmail',
  • 'subject'

This returns a custom response object to compensate for sending two POST requests.

//  ./lib/assets/Email.js - Line: 46
//  Return the boolean response of both
let returnData = {
	status: metaDataResponse.status === 200 && contentResponse.status === 200 ? 200 : 666,
	success: metaDataResponse.success && contentResponse.success ? true : false,
	errors: [...(metaDataResponse.errors ? metaDataResponse.errors : []), ...(contentResponse.errors ? contentResponse.errors : [])],
	warnings: [
		...(metaDataResponse.warnings ? metaDataResponse.warnings : []),
		...(contentResponse.warnings ? contentResponse.warnings : []),
	],

	metaDataResponse: metaDataResponse,
	contentResponse: contentResponse,
};

Send Sample Email

Send a Sample Email by supplying a single Email Address, and optional LeadID for token/personalization processing.

const sendEmailResponse = await myEmail.sendSample({
	emailAddress: "text-inbox@example.com", //  Required, will return false if not provided
	leadId: 1234, //  Optional, allows you to sample email token/personalization processing by Lead Record
	textOnly: false, //  Optional
});

Get Variables

Returns Array of Variable Data such as Strings, Colors, Booleans, Numbers, Lists.

const variablesEmailResponse = await myEmail.getVariables({
	status: "approved", //  Optional, Status string, ['approved', 'draft']
});
//  Data will be an array of EmailVariableResponse objects
variablesEmailResponse.data = [
	//<EmailVariableResponse>,
	//<EmailVariableResponse>,
];
/*
EmailVariableResponse {
    "name": "twoArticlesSpacer6",   //  Treat this like the ID
    "value": "15",                  //  Value - String, 
    "moduleScope": false
}
*/

Get Content

Returns Array of Content Sections such as Modules, Rich Text areas, Images, etc. Does not return Variables.

const contentEmailResponse = await myEmail.getContent({
	status: "approved", //  Optional, Status string, ['approved', 'draft']
});
//  Data will be an array of EmailContentResponse objects
contentEmailResponse.data = [
	//<EmailContentResponse>,
	//<EmailContentResponse>,
];
/*
EmailContentResponse {
    contentType (string): Type of content to set for the section. ,
    htmlId (string): HTML id of the content section ,
    index (integer, optional),
    isLocked (boolean, optional),
    parentHtmlId (string, optional),
    value (object): Contents of the section
}
*/

Get Full Content

Returns the Full HTML Content of an Email Record for Version 2 Emails. A shim is in place to return the JSON string content from getContent() method for Version 1 emails.

const fullContentEmailResponse = await myEmail.getFullContent({
	status: "approved", //  Optional, Status string, ['approved', 'draft']
	leadId: "", //  Optional, process HTML by lead record
	type: "HTML", //  Optional, render as HTML or plain text
});

Approve Draft

See Drafts Mixin

Unapprove Email

See Drafts Mixin

Discard Draft

See Drafts Mixin

Delete Email

See Delete Mixin

Landing Page

Get Content

Returns Array of Content Sections such as Modules, Rich Text areas, Images, etc. Does not return Variables.

const contentLandingPageResponse = await myLandingPage.getContent({
	status: "approved", //  Optional, Status string, ['approved', 'draft']
});
//  Data will be an array of LandingPageContentResponse objects
contentLandingPageResponse.data = [
	//<LandingPageContentResponse>,
	//<LandingPageContentResponse>,
];
/*
LandingPageContentResponse {
    content (object, optional): Content of the section. Expected values vary based on type. Image: An image URL. RichText: HTML Content. HTML: HTML Content. Form: A form id. Rectangle: Empty. Snippet: A snippet id. ,
    type (string): Type of content section = ['Image', 'SocialButton', 'Form', 'DynamicContent', 'Rectangle', 'Snippet', 'RichText', 'HTML', 'Video', 'Poll', 'ReferralOffer', 'Sweepstakes']

    followupType (string, optional): Follow-up behavior of a form. Only available for form-type content sections. Defaults to form defined behavior. = ['url', 'lp', 'formDefined'],
    followupValue (string, optional): Where to follow-up on form submission. When followupType is lp, accepts the integer id of a landing page. For url, it accepts a url string. ,
    formattingOptions (JsonNode, optional),
    id (object): Id of the content section, may be a string or an int ,
    index (integer, optional): Index of the content section. Index orients the elements from lowest to highest ,
}
*/

Get Full Content

Returns the Full HTML Content of an Approved Landing Page Record. This utilizes it's own Axios instance for a simple HTTP Get request to the Page URL. Returns false if the Landing Page is not approved.

const fullContentEmailResponse = await myLandingPage.getFullContent();
if (fullContentEmailResponse.success === true) {
	fullContentEmailResponse.data === "<doctype>...";
} else {
	fullContentEmailResponse.data === { axiosError };
}

Approve Draft

See Drafts Mixin

Unapprove Email

See Drafts Mixin

Discard Draft

See Drafts Mixin

Delete Email

See Delete Mixin


Marketo REST API Inconsistencies

Likely due to the evolution of Marketo and its REST API over time, there are some serious inconsistencies with how the API responds, Asset to Asset.

I have tried to standardize the API interaction within this library as much as possible. However, some issues are unavoidable and should be taken into consideration.

I have documented these inconsistencies over at my personal blog: Coming Soon


BulkProcess

Do Not Use - Deprecated as of 1.0.0. Leaving the docs here for anyone still using pre v1.0.0.

:warning: Work in progress

Due to Marketo's API return limit of 200, BulkProcess acts as an event-emitting auto-paging processor for large scale content reviews/updates.

//  Note - this instantiation is for pre 1.0.0 releases. See docs above for current setup.
const { mktoManager, bulkProcess } = new MktoManagerInit(marketoRestCredentails);

Pass BulkProcess a config param detailing the Asset Handler, search criteria, and asynchronous success & error callbacks to handle large scale reviews/updates.

Example BulkProcess Config

{
    handler: null, //  <BaseAsset> Asset Specific instance

    searchParams: {}, //  getAsset Search Params

    //  Depicts if we should wait for the successCallback to finish before continuing to next iteration
    awaitSuccess: false,
    awaitError: false,

    successCallback: async function ( /*MktoResponse*/ response) {
        //  Accepts the getAsset method response MktoResponse instance

        if (response.success) {

        }
    },
    errorCallback: async function ( /*MktoResponse*/ response) {
        //  Accepts the getAsset method response MktoResponse instance

        if (response.success) {

        }
    }
}
Config PropertyDescription
handlerMktoManager Asset Class, such as mktoManager.assets.LandingPage.
searchParamsObject passed to the find() method for narrowing the API Get results.
offsetStarting offset value for the API request.
cycleMaxReturnSet an integer for the maxReturn value of records from Marketo. Determines the number of results that will be offered to your successCallback() method or success event. Defaults to 5, max 200.
cycleMaxIterationSet an integer for the maximum iterations of the while loop. Offered as a safety feature to help limit the total number of API calls per BulkProcess usage, and to mitigate run-away looping if there is a break in the logic.
------
awaitSuccessBoolean if you want the while loop to await the finished Promise for your successCallback()
awaitErrorBoolean if you want the while loop to await the finished Promise for your errorCallback()
successCallback()Async method to be used on every successful retrieval from the API. Optional, event listener can be used instead.
errorCallback()Async method to be used on every failed retrieval from the API. Optional, event listener can be used instead.

:warning: NOTE: To process the returned API results once they are returned, you can either define an asynchronous successCallback() method within the BulkProcess config, OR attach a listener to the success BulkProcess event.

Personally, I prefer the event listener usage.

successCallback() Usage Example

//  Set BulkProcess config
const myBulkProcessConfig = {
	handler: mktoManager.assets.LandingPage, //  Define which Asset type we are retrieving and processing
	searchParams: {
		status: "approved", // Will only retrieve and process Approved records
	},

	awaitSuccess: true, //  Will Await your successCallback before continuing
	successCallback: async function (/*MktoResponse*/ response) {
		//  Accepts the getAsset method response MktoResponse instance

		if (response.success) {
			//  Save all results into my fake db
			response.getAll().forEach(landingPage => {
				db.insert("landingpages", landingPage.data);
			});
		}
	},
};

//  Instantiate the BulkProcess
const processor = new this.bulkProcess(myBulkProcessConfig);

//  Run the BulkProcess
processor.run();

success Event Listener Usage Example

//  Set BulkProcess config
const myBulkProcessConfig = {
	handler: mktoManager.assets.LandingPage, //  Define which Asset type we are retrieving and processing
	searchParams: {
		status: "approved", // Will only retrieve and process Approved records
	},

	//  No successCalback() definition required
};

//  Instantiate the BulkProcess
const processor = new this.bulkProcess(myBulkProcessConfig);

//  Add Event Listeners
processor.on("success", response => {
	if (response.success) {
		//  Save all results into my fake db
		response.getAll().forEach(landingPage => {
			db.insert("landingpages", landingPage.data);
		});
	}
});

//  Run the BulkProcess
processor.run();

BulkProcess Events

EventDescription
loggerFired every time a BulkProcess log is recorded. Receives data object from Tracer Logger. Easily log or print data.output to view the BulkProcess log.
request_http_errorFired when the Axios status !== 200. Recevies MktoResponse instance response object.
request_mkto_errorFired when the MktoResponse.success === false. Recevies MktoResponse instance response object
request_successFired when the MktoResponse.success === true. Recevies MktoResponse instance response object.
successFired when the MktoResponse.success === true AND we have some Mkto Results. Recevies MktoResponse instance response object. Also the event when we would fire the successCallback within the BulkProcess config, if one was passed.
finishedFired when the BulkProcess while loop is completed. Receives the entire BulkProcess instance.

Under continuous improvement.