diff --git a/backend/src/pluginUtils.ts b/backend/src/pluginUtils.ts index 86fbba80..b5239557 100644 --- a/backend/src/pluginUtils.ts +++ b/backend/src/pluginUtils.ts @@ -4,11 +4,13 @@ import { Member } from "eris"; import { configUtils, helpers, PluginBlueprint, PluginData, PluginOptions } from "knub"; -import { decodeAndValidateStrict, StrictValidationError } from "./validatorUtils"; -import { deepKeyIntersect, errorMessage, successMessage } from "./utils"; +import { decodeAndValidateStrict, StrictValidationError, validate } from "./validatorUtils"; +import { deepKeyIntersect, errorMessage, successMessage, tNullable } from "./utils"; import { ZeppelinPluginBlueprint } from "./plugins/ZeppelinPluginBlueprint"; import { TZeppelinKnub } from "./types"; import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; // TODO: Export from Knub index +import * as t from "io-ts"; +import { PluginOverrideCriteria } from "knub/dist/config/configTypes"; const { getMemberLevel } = helpers; @@ -27,35 +29,69 @@ export function hasPermission(pluginData: PluginData, permission: string, m return helpers.hasPermission(config, permission); } +const PluginOverrideCriteriaType = t.recursion("PluginOverrideCriteriaType", () => + t.type({ + channel: tNullable(t.union([t.string, t.array(t.string)])), + category: tNullable(t.union([t.string, t.array(t.string)])), + level: tNullable(t.union([t.string, t.array(t.string)])), + user: tNullable(t.union([t.string, t.array(t.string)])), + role: tNullable(t.union([t.string, t.array(t.string)])), + + all: tNullable(t.array(PluginOverrideCriteriaType)), + any: tNullable(t.array(PluginOverrideCriteriaType)), + not: tNullable(PluginOverrideCriteriaType), + + extra: t.unknown, + }), +); + +const BasicPluginStructureType = t.type({ + enabled: tNullable(t.boolean), + config: tNullable(t.unknown), + overrides: tNullable(t.array(PluginOverrideCriteriaType)), + replaceDefaultOverrides: tNullable(t.boolean), +}); + export function getPluginConfigPreprocessor( blueprint: ZeppelinPluginBlueprint, customPreprocessor?: PluginBlueprint["configPreprocessor"], ) { return async (options: PluginOptions) => { + const basicOptionsValidation = validate(BasicPluginStructureType, options); + if (basicOptionsValidation instanceof StrictValidationError) { + throw basicOptionsValidation; + } + if (customPreprocessor) { options = await customPreprocessor(options); } - const decodedConfig = blueprint.configSchema - ? decodeAndValidateStrict(blueprint.configSchema, options.config) - : options.config; - if (decodedConfig instanceof StrictValidationError) { - throw decodedConfig; + let decodedConfig = {}; + const decodedOverrides = []; + + if (options.config) { + decodedConfig = blueprint.configSchema + ? decodeAndValidateStrict(blueprint.configSchema, options.config) + : options.config; + if (decodedConfig instanceof StrictValidationError) { + throw decodedConfig; + } } - const decodedOverrides = []; - for (const override of options.overrides || []) { - const overrideConfigMergedWithBaseConfig = configUtils.mergeConfig(options.config, override.config || {}); - const decodedOverrideConfig = blueprint.configSchema - ? decodeAndValidateStrict(blueprint.configSchema, overrideConfigMergedWithBaseConfig) - : overrideConfigMergedWithBaseConfig; - if (decodedOverrideConfig instanceof StrictValidationError) { - throw decodedOverrideConfig; + if (options.overrides) { + for (const override of options.overrides || []) { + const overrideConfigMergedWithBaseConfig = configUtils.mergeConfig(options.config, override.config || {}); + const decodedOverrideConfig = blueprint.configSchema + ? decodeAndValidateStrict(blueprint.configSchema, overrideConfigMergedWithBaseConfig) + : overrideConfigMergedWithBaseConfig; + if (decodedOverrideConfig instanceof StrictValidationError) { + throw decodedOverrideConfig; + } + decodedOverrides.push({ + ...override, + config: deepKeyIntersect(decodedOverrideConfig, override.config || {}), + }); } - decodedOverrides.push({ - ...override, - config: deepKeyIntersect(decodedOverrideConfig, override.config || {}), - }); } return { diff --git a/backend/src/validatorUtils.ts b/backend/src/validatorUtils.ts index 3ced8eaf..d2141487 100644 --- a/backend/src/validatorUtils.ts +++ b/backend/src/validatorUtils.ts @@ -98,7 +98,7 @@ export function validate(schema: t.Type, value: any): StrictValidationError * Decodes and validates the given value against the given schema while also disallowing extra properties * See: https://github.com/gcanti/io-ts/issues/322 */ -export function decodeAndValidateStrict(schema: t.HasProps, value: any): StrictValidationError | any { +export function decodeAndValidateStrict(schema: T, value: any): StrictValidationError | any { const validationResult = t.exact(schema).decode(value); return pipe( validationResult,