@grouparoo/core
Version:
The Grouparoo Core
254 lines (232 loc) • 7.51 kB
text/typescript
import { Initializer, api, log, utils } from "actionhero";
import { GrouparooPlugin } from "../classes/plugin";
import { plugin } from "../modules/plugin";
import { App } from "../models/App";
import { getPluginManifest } from "../modules/pluginDetails";
import { CLS } from "../modules/cls";
import { getGrouparooRunMode } from "../modules/runMode";
declare module "actionhero" {
export interface Api {
plugins: {
plugins: GrouparooPlugin[];
validate: (plugin: GrouparooPlugin) => boolean;
register: (plugin: GrouparooPlugin, validate: boolean) => void;
announcePlugins: () => void;
persistentConnections: {
[id: string]: any;
};
};
}
}
export class Plugins extends Initializer {
constructor() {
super();
this.name = "plugins";
this.loadPriority = 999;
this.startPriority = 1;
this.stopPriority = 900;
}
async initialize() {
api.plugins = {
plugins: [],
persistentConnections: {},
validate: this.validatePlugin,
register: this.registerPlugin,
announcePlugins: this.announcePlugins,
};
this.checkPluginEnvironmentVariables();
// --- Add the core plugins --- //
plugin.registerPlugin({
name: "@grouparoo/core",
icon: "/public/@grouparoo/logo.png",
});
// --- Add the UI pLugin --- //
const pluginManifest = getPluginManifest();
const uiPlugins = pluginManifest.plugins
.filter((p) => p.name.match(/^@grouparoo\/ui-/))
.sort((p) => (p.name.match("config") ? 1 : -1));
if (uiPlugins) {
const relevantUiPlugin =
uiPlugins[
getGrouparooRunMode() === "cli:config" ? uiPlugins.length - 1 : 0
];
if (relevantUiPlugin) {
this.registerPlugin({
name: relevantUiPlugin.name,
icon: "/public/@grouparoo/logo.png",
});
process.env.GROUPAROO_UI_EDITION = relevantUiPlugin.name.replace(
"@grouparoo/ui-",
""
);
}
}
}
async start() {
api.plugins.announcePlugins();
}
async stop() {
await CLS.wrap(async () => {
for (const id in api.plugins.persistentConnections) {
const app = await App.findById(id).catch(() => null);
await app?.disconnect();
}
await utils.sleep(100);
});
}
validatePlugin(plugin: GrouparooPlugin) {
let errors: string[] = [];
if (!plugin.name) {
errors.push(`name is required for a Grouparoo Plugin`);
}
if (api.plugins.plugins.map((p) => p.name).includes(plugin.name)) {
errors.push(`a plugin named ${plugin.name} is already registered`);
}
if (plugin.apps) {
plugin?.apps.forEach((app) => {
if (!app.name) {
errors.push(`app.name is required for a Grouparoo Plugin App`);
}
if (
api.plugins.plugins
.map((p) => p.apps?.map((app) => app.name))
.flat()
.includes(plugin.name)
) {
errors.push(`a plugin app named ${app.name} is already registered`);
}
if (!app.options) {
errors.push(`app.options is required for a Grouparoo Plugin App`);
}
if (!app.methods.test) {
errors.push(
`app.methods.test is required for a Grouparoo Plugin App`
);
}
if (
(app.methods.connect && !app.methods.disconnect) ||
(!app.methods.connect && app.methods.disconnect)
) {
errors.push(
`both app.methods.connect and app.methods.disconnect are required for a Grouparoo Plugin App`
);
}
});
}
if (plugin.connections) {
plugin.connections.forEach((connection) => {
if (!connection.name) {
errors.push(
`connection.name is required for a Grouparoo Plugin Connection`
);
}
if (
api.plugins.plugins
.map((p) => p.connections?.map((conn) => conn.name))
.flat()
.includes(connection.name)
) {
errors.push(
`a plugin connection named ${connection.name} is already registered`
);
}
if (!connection.apps || connection.apps.length < 1) {
errors.push(
`connection.apps is required for a Grouparoo Plugin Connection (${connection.name})`
);
}
if (!connection.description) {
errors.push(
`connection.description is required for a Grouparoo Plugin Connection (${connection.name})`
);
}
if (!connection.direction) {
errors.push(
`connection.direction is required for a Grouparoo Plugin Connection (${connection.name})`
);
}
if (!connection.options) {
errors.push(
`connection.options is required for a Grouparoo Plugin Connection (${connection.name})`
);
}
if (!["import", "export"].includes(connection.direction)) {
errors.push(
`connection.direction must be either import or export for a Grouparoo Plugin Connection (${connection.name})`
);
}
if (
connection.direction === "import" &&
!connection.methods.importRecords &&
!connection.methods.recordProperty &&
!connection.methods.recordProperties
) {
errors.push(
`import connections must provide a way to import records (${connection.name})`
);
}
if (
connection.direction === "import" &&
connection.methods.importRecords &&
connection.supportIncrementalSchedule === undefined
) {
errors.push(
`import connections that support schedules must declare supportIncrementalSchedule`
);
}
if (
connection.direction === "export" &&
!connection.methods.exportRecord &&
!connection.methods.exportRecords
) {
errors.push(
`export connections must provide either connection.methods.exportRecord or connection.methods.exportRecords (${connection.name})`
);
}
});
}
if (errors.length > 0) {
throw new Error(
`Plugin validation failure for ${
plugin.name ? `"${plugin.name}"` : "plugin"
}: ${errors.join(", ")}`
);
}
return true;
}
registerPlugin(plugin: GrouparooPlugin, validate = true) {
if (validate) api.plugins.validate(plugin);
api.plugins.plugins.push(plugin);
}
announcePlugins() {
log(`active plugins: ${api.plugins.plugins.map((p) => p.name).join(", ")}`);
}
/*
* ensure that the ENV variables for the plugins are set
*/
checkPluginEnvironmentVariables() {
const pluginManifest = getPluginManifest();
pluginManifest.plugins.forEach((plugin) => {
if (plugin?.grouparoo?.env?.api) {
plugin.grouparoo.env.api.forEach((e) => {
if (!process.env[e]) {
log(
`Plugin ${plugin.name} requires environment variable ${e} to be set`,
"warning"
);
}
});
}
if (plugin?.grouparoo?.env?.web) {
plugin.grouparoo.env.web.forEach((e) => {
if (!process.env[e]) {
log(
`plugin ${plugin.name} requires environment variable ${e} to be set`,
"warning"
);
}
});
}
});
}
}