@jaybirdgroup/data-hub-connector-mailchimp
Version:
Connector to interact with MailChimp API in score of Data Hub.
769 lines (642 loc) • 20 kB
JavaScript
'use strict';
const _merge = require('lodash/merge');
const Moment = require('moment');
const MailChimpAPI = require('mailchimp-api-v3');
/**
* Class representing a basic Mail Chimp API calls wrapping
*/
class MailChimp {
/**
* Init class and apply defaults.
* @param {Object} config - MailChimp configuration.
*/
constructor(config) {
// default params
this.defaults = {
apikey: '',
fromEmail: '',
fromName: '',
listId: ''
};
// TODO: check for missing parameters
this.config = _merge({}, this.defaults, config);
this.instance = new MailChimpAPI(this.config.apikey);
}
/**
* Method to be called after getting Subscriber Lists or error occurred.
*
* @callback getSubscriberListsCallback
* @param {Error} [err] - JS Error object or null.
* @param {Object} subscriberListsResponse - An object with lists response
*/
/**
* Get information about all subscriber lists in the account.
* @param {number} [page=1] - A page of record to return
* @param {number} [limit=10] - A number of records to return.
* @param {string[]} [fields] - An array of fields to return. Reference parameters of sub-objects with dot notation.
* @param {getSubscriberListsCallback} next - The callback that handles the response.
* @return {Promise}
*/
getSubscriberLists(page = 1, limit = 10, fields = ['lists.id','lists.name','total_items'], next = null) {
const offset = (page - 1) * limit;
const fieldsStr = fields ? fields.join(',') : '';
return new Promise((resolve, reject) => {
this.instance.request({
method: 'get',
path: `/lists?offset=${offset}&count=${limit}&fields=${fieldsStr}`
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(err, response);
}
return resolve(response);
});
});
}
/**
* Create a Mail Chimp list
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
createList (options, next) {
this.instance.request({
method: 'post',
path: '/lists/',
body: {
name: options.presentationName,
email_type_option: true,
campaign_defaults: {
from_name: this.config.fromName,
from_email: this.config.fromEmail,
subject: options.subject,
language: 'en'
},
permission_reminder: 'You have been invited to the presentation',
contact: {
company: options.company.companyName,
address1: options.company.address1,
city: options.company.city,
state: options.company.state,
zip: options.company.zip.toString(),
country: options.company.country
}
}
}, (err, response) => {
next(err, response);
});
}
/**
* Get a Mail Chimp list
* @param {Object} instance - Mail Chimp wrapper
* @param {String} listId - List id to get
* @param {nextCallback} next - Callback function.
*/
checkSubscriberList (listId, next) {
if (!listId) {
return next(new Error('List id is not provided'));
}
this.instance.request({
method: 'get',
path: '/lists/' + listId
}, (err, response) => {
next(err, response);
});
}
/**
* Assign a batch of subscribers to the list
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
batchSubscribe (options, next) {
if (!options.listId) {
return next(new Error('List id is not provided'));
}
if (!Array.isArray(options.subscribers) || options.subscribers.length === 0) {
return next(new Error('Subscriber list is not provided'));
}
const members = [];
const ln = options.subscribers.length;
for (let i = 0; i < ln; ++i) {
members.push({
email_address: options.subscribers[i].email,
email_type: 'html',
merge_fields: {
'FNAME': options.subscribers[i].firstName,
'LNAME': options.subscribers[i].lastName
},
status: 'subscribed'
});
}
const formData = {
members,
update_existing: true
};
this.instance.request({
method: 'post',
path: '/lists/' + options.listId,
body: formData
}, (err, response) => {
next(err, response);
});
}
/**
* Create a Mail Chimp campaign
* @param {Object} type - A type of created campaign
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
createCampaign (type, options, next) {
return new Promise((resolve, reject) => {
let error = null;
if (!options) {
error = new Error('Options argument not provided to createCampaign');
}
if (!options.listId && !error) {
error = new Error('List Id is not provided');
}
if (!options.subject && !error) {
error = new Error('Subject is not provided');
}
if (!options.templateId && !error) {
error = new Error('Template Id is not provided');
}
if (error) {
if (next) {
next(error);
}
return reject(error);
}
this.instance.request({
method: 'post',
path: '/campaigns',
body: {
type,
recipients: {
list_id: options.listId
},
settings: {
subject_line: options.subject,
from_name: this.config.companyName,
reply_to: options.replyTo,
title: options.title,
template_id: options.templateId
},
tracking: {
opens: true,
html_clicks: true,
text_clicks: true
},
rss_opts: {
feed_url: options.feedUrl,
frequency: 'daily',
schedule: {
hour: options.schedule.hour,
daily_send: options.schedule.weekDays
}
}
}
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Update a Mail Chimp campaign
* @param {string} id - Id of the campaign
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
updateCampaign (id, options, next) {
return new Promise((resolve, reject) => {
let error = null;
if (!options) {
error = new Error('Options argument not provided to createCampaign');
}
if (!options.listId && !error) {
error = new Error('List Id is not provided');
}
if (!options.subject && !error) {
error = new Error('Subject is not provided');
}
if (!options.templateId && !error) {
error = new Error('Template Id is not provided');
}
if (error) {
if (next) {
next(error);
}
return reject(error);
}
this.instance.request({
method: 'patch',
path: `/campaigns/${id}`,
body: {
recipients: {
list_id: options.listId
},
settings: {
subject_line: options.subject,
from_name: this.config.companyName,
reply_to: options.replyTo,
title: options.title,
template_id: options.templateId
},
tracking: {
opens: true,
html_clicks: true,
text_clicks: true
},
rss_opts: {
feed_url: options.feedUrl,
frequency: 'daily',
schedule: {
hour: options.schedule.hour,
daily_send: options.schedule.weekDays
}
}
}
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Pause rss campaign
* @param {string} id - The id of the campaign
* @param next
* @return {Promise}
*/
pauseCampaign(id, next) {
return new Promise((resolve, reject) => {
if (!id) {
const error = new Error('Campaign ID is not provided');
if(next) {
next(error);
}
return reject(error);
}
this.instance.request({
method: 'post',
path: `/campaigns/${id}/actions/pause`
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Send an email dispatch to the subscribers, provided in a campaign lists
* @param {String} id - Id of the campaign
* @param {nextCallback} next - Callback function.
*/
sendCampaign(id, next) {
return new Promise((resolve, reject) => {
if (!id) {
const error = new Error('Campaign ID is not provided');
if(next) {
next(error);
}
return reject(error);
}
this.instance.request({
method: 'post',
path: `/campaigns/${id}/actions/send`
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Resume rss campaign
* @param id
* @param next
* @return {Promise}
*/
resumeCampaign(id, next) {
return new Promise((resolve, reject) => {
if (!id) {
const error = new Error('Campaign ID is not provided');
if(next) {
next(error);
}
return reject(error);
}
this.instance.request({
method: 'post',
path: `/campaigns/${id}/actions/resume`
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Delete rss campaign
* @param {string} id - The id of the campaign
* @param next
* @return {Promise}
*/
deleteCampaign(id, next) {
return new Promise((resolve, reject) => {
if (!id) {
const error = new Error('Campaign ID is not provided');
if(next) {
next(error);
}
return reject(error);
}
this.instance.request({
method: 'delete',
path: `/campaigns/${id}`
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Create a Mail Chimp template
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
createTemplate (instance, options, next) {
if (!options.name) {
return next(new Error('Template Name is not provided'));
}
if (!options.html) {
return next(new Error('Html is not provided'));
}
if (!options.folder_id) {
return next(new Error('Folder Id is not provided'));
}
instance.request({
method: 'post',
path: '/templates',
body: {
name: options.name,
folder_id: options.folder_id,
html: options.html
}
}, (err, response) => {
next(err, response);
});
}
/**
* Edit a Mail Chimp template
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
editTemplate (instance, options, next) {
if (!options.id) {
return next(new Error('Template ID is not provided'));
}
if (!options.html) {
return next(new Error('Html is not provided'));
}
if (!options.folder_id) {
return next(new Error('Folder Id is not provided'));
}
instance.request({
method: 'patch',
path: '/templates/' + options.id,
body: {
folder_id: options.folder_id,
html: options.html
}
}, (err, response) => {
next(err, response);
});
}
/**
* Delete a Mail Chimp template
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
deleteTemplate (instance, options, next) {
if (!options.id) {
return next(new Error('Template ID is not provided'));
}
instance.request({
method: 'delete',
path: '/templates/' + options.id
}, (err, response) => {
next(err, response);
});
}
/**
* Get a Mail Chimp campaign by id
* @param {String} campaignId - Campaign id to get via Mail Chimp API
* @param {nextCallback} next - Callback function.
*/
getCampaign (campaignId, next) {
return new Promise((resolve, reject) => {
if (!campaignId) {
const err = new Error('Campaign Id is not provided');
if (next) {
next(err);
}
return reject(err)
}
this.instance.request({
method: 'get',
path: '/campaigns/' + campaignId
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
/**
* Get a Mail Chimp campaigns under the specific folder
* @param {Object} instance - Mail Chimp wrapper
* @param {String} folderId - Folder id
* @param {nextCallback} next - Callback function.
*/
getCampaigns (folderId, next) {
this.instance.request({
method: 'get',
path: `/campaigns?folder_id=${folderId}&count=100`
}, (err, response) => {
next(err, response);
});
}
/**
* Get all campaign folders
* @param {nextCallback} next - Callback function.
*/
getCampaignFolders (next) {
return new Promise((resolve, reject) => {
this.instance.request({
method: 'get',
path: '/campaign-folders'
}, (err, response) => {
if (err) {
return typeof next === 'function' ? next(err) : reject(err);
}
return typeof next === 'function' ? next(null, response) : resolve(response);
});
});
}
/**
* Get all folders used to organize templates
* @param {nextCallback} next - Callback function.
*/
getTemplateFolders (next) {
return new Promise((resolve, reject) => {
this.instance.request({
method: 'get',
path: '/template-folders'
}, (err, response) => {
if (err) {
return typeof next === 'function' ? next(err) : reject(err);
}
return typeof next === 'function' ? next(null, response) : resolve(response);
});
});
}
/**
* Add a custom merge tags
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
addCustomMergeTags (options, next) {
if (!options.listId) {
return next(new Error('List Id is not provided'));
}
if (!(options.tags instanceof Array) || options.tags.length === 0) {
return next(new Error('Merge tags are not provided'));
}
const calls = options.tags.reduce((prev, nextCalls) => {
prev.push({
method: 'post',
path: '/lists/' + options.listId + '/merge-fields',
body: {
name: nextCalls.tagName,
type: 'text'
}
});
return prev;
}, []);
this.instance.batch(calls, (err, response) => {
next(err, response);
}, {
wait: true,
interval: 2000,
unpack: true
});
}
/**
* Add a custom merge tag
* @param {Object} instance - Mail Chimp wrapper
* @param {Object} options - Additional parameters to configure API call
* @param {nextCallback} next - Callback function.
*/
addCustomMergeTag (options, next) {
if (!options.listId) {
return next(new Error('List Id is not provided'));
}
if (!options.tagName) {
return next(new Error('Merge tag is not provided'));
}
this.instance.request({
method: 'post',
path: '/lists/' + options.listId + '/merge-fields',
body: {
name: options.tagName,
type: 'text'
}
}, (err, response) => {
next(err, response);
});
}
getAccount(next) {
return new Promise((resolve, reject) => {
this.instance.request({
method: 'get',
path: '/'
}, (err, response) => {
if (err) {
if (next) {
next(err);
}
return reject(err);
}
if (next) {
next(null, response);
}
resolve(response);
});
});
}
}
module.exports = MailChimp;