@grouparoo/core
Version:
The Grouparoo Core
362 lines (361 loc) • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.plugin = void 0;
const actionhero_1 = require("actionhero");
const mustacheUtils_1 = require("./mustacheUtils");
const App_1 = require("../models/App");
const AppRefreshQuery_1 = require("../models/AppRefreshQuery");
const ApiKey_1 = require("../models/ApiKey");
const Destination_1 = require("../models/Destination");
const DestinationGroupMembership_1 = require("../models/DestinationGroupMembership");
const Export_1 = require("../models/Export");
const Group_1 = require("../models/Group");
const GroupMember_1 = require("../models/GroupMember");
const GroupRule_1 = require("../models/GroupRule");
const Import_1 = require("../models/Import");
const Mapping_1 = require("../models/Mapping");
const Option_1 = require("../models/Option");
const OAuthRequest_1 = require("../models/OAuthRequest");
const Permission_1 = require("../models/Permission");
const GrouparooRecord_1 = require("../models/GrouparooRecord");
const RecordProperty_1 = require("../models/RecordProperty");
const Property_1 = require("../models/Property");
const Filter_1 = require("../models/Filter");
const RecordMultipleAssociationShim_1 = require("../models/RecordMultipleAssociationShim");
const Run_1 = require("../models/Run");
const GrouparooModel_1 = require("../models/GrouparooModel");
const Schedule_1 = require("../models/Schedule");
const Session_1 = require("../models/Session");
const Setting_1 = require("../models/Setting");
const SetupStep_1 = require("../models/SetupStep");
const Source_1 = require("../models/Source");
const Notification_1 = require("../models/Notification");
const Team_1 = require("../models/Team");
const TeamMember_1 = require("../models/TeamMember");
const ExportProcessor_1 = require("../models/ExportProcessor");
const propertiesCache_1 = require("./caches/propertiesCache");
const oAuth_1 = require("../modules/oAuth");
// the order matters here - the children need to come before the parents (destinationGroup -> destination)
const models = [
App_1.App,
AppRefreshQuery_1.AppRefreshQuery,
ApiKey_1.ApiKey,
Source_1.Source,
Schedule_1.Schedule,
Destination_1.Destination,
DestinationGroupMembership_1.DestinationGroupMembership,
Option_1.Option,
OAuthRequest_1.OAuthRequest,
Filter_1.Filter,
Import_1.Import,
Run_1.Run,
Export_1.Export,
ExportProcessor_1.ExportProcessor,
GroupMember_1.GroupMember,
Group_1.Group,
GroupRule_1.GroupRule,
Permission_1.Permission,
GrouparooModel_1.GrouparooModel,
GrouparooRecord_1.GrouparooRecord,
RecordProperty_1.RecordProperty,
Property_1.Property,
RecordMultipleAssociationShim_1.RecordMultipleAssociationShim,
Mapping_1.Mapping,
Notification_1.Notification,
Setting_1.Setting,
Session_1.Session,
SetupStep_1.SetupStep,
Team_1.Team,
TeamMember_1.TeamMember,
];
const _oAuthAccessTokenGetters = {};
var plugin;
(function (plugin_1) {
/**
* This is needed when running in dev mode (TS) but you are using a compiled plugin (JS).
* The plugin will actually load the JS model while core will be loading the TS model.
* Both need to be "added" to sequelize to know which connection to use.
*/
function mountModels() {
models.map((model) => {
if (!model.isInitialized)
actionhero_1.api.sequelize.addModels([model]);
});
}
plugin_1.mountModels = mountModels;
/**
* Register a Grouparoo Plugin
*/
function registerPlugin(plugin, validate) {
actionhero_1.api.plugins.register(plugin, validate);
}
plugin_1.registerPlugin = registerPlugin;
/**
* Register a setting for your Grouparoo plugin
*/
async function registerSetting(pluginName, key, title, defaultValue, description, type, variant = "info") {
const setting = await Setting_1.Setting.findOne({ where: { pluginName, key } });
if (setting) {
return setting.update({
title,
defaultValue: defaultValue.toString(),
description,
type,
variant,
});
}
try {
const setting = await Setting_1.Setting.create({
pluginName,
key,
title,
value: defaultValue,
defaultValue,
description,
type,
variant,
});
return setting;
}
catch (error) {
throw new Error(`error registering setting: ${JSON.stringify({
pluginName,
key,
defaultValue: defaultValue.toString(),
description,
type,
})}: ${error}`);
}
}
plugin_1.registerSetting = registerSetting;
/**
* Read a setting for this plugin
*/
async function readSetting(pluginName, key) {
const setting = await Setting_1.Setting.findOne({
where: { pluginName, key },
});
if (!setting) {
throw new Error(`setting ${key} not registered for grouparoo plugin ${pluginName}`);
}
return setting;
}
plugin_1.readSetting = readSetting;
/**
* Update a setting for this plugin
*/
async function updateSetting(pluginName, key, value) {
const setting = await plugin.readSetting(pluginName, key);
setting.value = value;
await setting.save();
return setting;
}
plugin_1.updateSetting = updateSetting;
/**
* When your plugin has a record for a record, send it to this method. We will use the provided mapping against your raw data row to store the original data and mapped data to the record.
* mapping: an object whose keys are remote columns and whose values are the property keys, ie: {remoteColumnId: 'userId'}
* row: {email: 'abc@company.com', vip: true}
*/
async function createImport(mapping, run, row) {
const mappingKeys = Object.keys(mapping);
const mappedRecordProperties = {};
mappingKeys.forEach((k) => {
mappedRecordProperties[mapping[k]] = Array.isArray(row[k])
? row[k]
: [row[k]];
});
const _import = await Import_1.Import.create({
rawData: row,
data: mappedRecordProperties,
creatorType: "run",
creatorId: run.id,
});
return _import;
}
plugin_1.createImport = createImport;
/**
* Like plugin.createImport, but for many imports at once!
* * mapping: an object whose keys are remote columns and whose values are the property keys, ie: {remoteColumnId: 'userId'}
* rows: {email: 'abc@company.com', vip: true}[]
*/
async function createImports(mapping, run, rows) {
const bulkParams = [];
const mappingKeys = Object.keys(mapping);
for (const row of rows) {
const mappedRecordProperties = {};
mappingKeys.forEach((k) => {
mappedRecordProperties[mapping[k]] = Array.isArray(row[k])
? row[k]
: [row[k]];
});
bulkParams.push({
rawData: row,
data: mappedRecordProperties,
creatorType: "run",
creatorId: run.id,
});
}
if (bulkParams.length === 0)
return [];
let _imports = [];
while (bulkParams.length > 0) {
_imports = _imports.concat(await Import_1.Import.bulkCreate(bulkParams.splice(0, actionhero_1.config.batchSize.internalWrite)));
}
return _imports;
}
plugin_1.createImports = createImports;
/**
* data helpers
*/
function expandDates(raw) {
if (!raw) {
return null;
}
return {
raw,
iso: raw.toISOString(),
sql: raw.toISOString().slice(0, 19).replace("T", " "),
date: raw.toISOString().split("T")[0],
time: raw.toISOString().split("T")[1].split(".")[0],
};
}
plugin_1.expandDates = expandDates;
/**
* Takes a string with mustache variables and replaces them with the proper values for a schedule and run
*/
async function replaceTemplateRunVariables(string, run) {
if (string.indexOf("{{") < 0) {
return string;
}
const data = {
now: expandDates(new Date()),
run: {},
previousRun: {
id: "",
creatorId: "",
creatorType: "",
error: null,
state: "mocked",
createdAt: expandDates(new Date(0)),
updatedAt: expandDates(new Date(0)),
},
};
if (run) {
data.run = {
id: run.id,
creatorId: run.creatorId,
creatorType: run.creatorType,
state: run.state,
error: run.error,
createdAt: expandDates(run.createdAt),
updatedAt: expandDates(run.updatedAt),
};
const previousRun = await run.previousRun();
if (previousRun) {
data.previousRun = {
id: previousRun.id,
creatorId: previousRun.creatorId,
creatorType: previousRun.creatorType,
state: previousRun.state,
error: previousRun.error,
createdAt: expandDates(previousRun.createdAt),
updatedAt: expandDates(previousRun.updatedAt),
};
}
}
return mustacheUtils_1.MustacheUtils.strictlyRender(string, data);
}
plugin_1.replaceTemplateRunVariables = replaceTemplateRunVariables;
/**
* Takes a record and returns data with the values from the properties and current time.
*/
async function getRecordData(record) {
// TODO: we could do these types better to be string | number | etc | string[] | etc>
const dates = {
now: expandDates(new Date()),
createdAt: expandDates(record.createdAt),
updatedAt: expandDates(record.updatedAt),
};
const properties = await record.getProperties();
const propertyData = {};
for (const [key, property] of Object.entries(properties)) {
propertyData[key] =
property.values.length === 1
? property.values[0] instanceof Date
? expandDates(property.values[0])
: property.values[0]
: property.values.map((value) => (value || "").toString()).join(", ");
}
return Object.assign(dates, propertyData);
}
plugin_1.getRecordData = getRecordData;
/**
* Takes a string with mustache variables and replaces them with the proper values for a record
*/
async function replaceTemplateRecordVariables(string, record, strict = true) {
if (string.indexOf("{{") < 0)
return string;
const data = await getRecordData(record);
if (strict === true)
return mustacheUtils_1.MustacheUtils.strictlyRender(string, data);
return mustacheUtils_1.MustacheUtils.render(string, data);
}
plugin_1.replaceTemplateRecordVariables = replaceTemplateRecordVariables;
/**
* Takes a string with mustache variable (keys) and replaces them with the record property ids
* ie: `select * where id = {{{ userId }}}` => `select * where id = {{{ ppr_abc123 }}}`
*/
async function replaceTemplateRecordPropertyKeysWithRecordPropertyId(string, modelId) {
//though we default to 3 brackets, if someone inputs the double bracket notation, we should accept it
if (string.indexOf("{{") < 0)
return string;
const properties = (await propertiesCache_1.PropertiesCache.findAllWithCache(modelId, "ready")).filter((p) => p.isArray === false);
const data = {};
properties.forEach((rule) => {
data[rule.key] = `{{{ ${rule.id} }}}`;
});
return mustacheUtils_1.MustacheUtils.strictlyRender(string, data);
}
plugin_1.replaceTemplateRecordPropertyKeysWithRecordPropertyId = replaceTemplateRecordPropertyKeysWithRecordPropertyId;
/**
* Takes a string with mustache variable (ids) and replaces them with the record property keys
* ie: `select * where id = {{{ ppr_abc123 }}}` => `select * where id = {{{ userId }}}`
*/
async function replaceTemplateRecordPropertyIdsWithRecordPropertyKeys(string, modelId) {
//though we default to 3 brackets, if someone inputs the double bracket notation, we should accept it
if (string.indexOf("{{") < 0)
return string;
const properties = await propertiesCache_1.PropertiesCache.findAllWithCache(modelId, "ready");
const data = {};
properties.forEach((rule) => {
data[rule.id] = `{{{ ${rule.key} }}}`;
});
return mustacheUtils_1.MustacheUtils.strictlyRender(string, data);
}
plugin_1.replaceTemplateRecordPropertyIdsWithRecordPropertyKeys = replaceTemplateRecordPropertyIdsWithRecordPropertyKeys;
function setApmWrap(f) {
actionhero_1.api.apm.wrap = f;
}
plugin_1.setApmWrap = setApmWrap;
/**
* Returns the access token for an OAuth-based plugin app that uses refresh tokens.
* Manages cache and expiration of the token.
* In order to use this, app OAuth access must first be setup by the Grouparoo team.
*
* @param providerName the name of the provider (e.g. hubspot)
* @param refreshToken the refresh token stored by the app options
* @returns the access token
*/
async function getOAuthAppAccessToken(providerName, refreshToken) {
let getter = _oAuthAccessTokenGetters[providerName];
if (!getter) {
getter = new oAuth_1.oAuthAccessTokenGetter(providerName, refreshToken);
_oAuthAccessTokenGetters[providerName] = getter;
}
else if (getter.refreshToken !== refreshToken) {
getter.refreshToken = refreshToken;
}
return await getter.getAccessToken();
}
plugin_1.getOAuthAppAccessToken = getOAuthAppAccessToken;
})(plugin = exports.plugin || (exports.plugin = {}));