mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-15 05:41:51 +00:00
Validate override criteria and extra criteria. When loading existing configs, silently remove invalid overrides.
This commit is contained in:
parent
8ab6538744
commit
52e4b5b519
4 changed files with 62 additions and 5 deletions
|
@ -37,7 +37,7 @@ export async function validateGuildConfig(config: any): Promise<string | null> {
|
|||
const plugin = pluginNameToPlugin.get(pluginName)!;
|
||||
try {
|
||||
const mergedOptions = configUtils.mergeConfig(plugin.defaultOptions || {}, pluginOptions);
|
||||
await plugin.configPreprocessor?.((mergedOptions as unknown) as PluginOptions<any>);
|
||||
await plugin.configPreprocessor?.((mergedOptions as unknown) as PluginOptions<any>, true);
|
||||
} catch (err) {
|
||||
if (err instanceof ConfigValidationError || err instanceof StrictValidationError) {
|
||||
return `${pluginName}: ${err.message}`;
|
||||
|
|
|
@ -54,6 +54,19 @@ const PluginOverrideCriteriaType: t.Type<PluginOverrideCriteria<unknown>> = t.re
|
|||
}),
|
||||
);
|
||||
|
||||
const validTopLevelOverrideKeys = [
|
||||
"channel",
|
||||
"category",
|
||||
"level",
|
||||
"user",
|
||||
"role",
|
||||
"all",
|
||||
"any",
|
||||
"not",
|
||||
"extra",
|
||||
"config",
|
||||
];
|
||||
|
||||
const BasicPluginStructureType = t.type({
|
||||
enabled: tNullable(t.boolean),
|
||||
config: tNullable(t.unknown),
|
||||
|
@ -74,7 +87,7 @@ export function getPluginConfigPreprocessor(
|
|||
blueprint: ZeppelinPlugin,
|
||||
customPreprocessor?: ZeppelinPlugin["configPreprocessor"],
|
||||
) {
|
||||
return async (options: PluginOptions<any>) => {
|
||||
return async (options: PluginOptions<any>, strict?: boolean) => {
|
||||
// 1. Validate the basic structure of plugin config
|
||||
const basicOptionsValidation = validate(BasicPluginStructureType, options);
|
||||
if (basicOptionsValidation instanceof StrictValidationError) {
|
||||
|
@ -93,8 +106,32 @@ export function getPluginConfigPreprocessor(
|
|||
|
||||
if (options.overrides) {
|
||||
for (const override of options.overrides) {
|
||||
const partialOverrideConfigValidation = validate(partialConfigSchema, override.config || {});
|
||||
if (partialOverrideConfigValidation) {
|
||||
// Validate criteria and extra criteria
|
||||
// FIXME: This is ugly
|
||||
for (const key of Object.keys(override)) {
|
||||
if (!validTopLevelOverrideKeys.includes(key)) {
|
||||
if (strict) {
|
||||
throw new ConfigValidationError(`Unknown override criterion '${key}'`);
|
||||
}
|
||||
|
||||
delete override[key];
|
||||
}
|
||||
}
|
||||
if (override.extra != null) {
|
||||
for (const extraCriterion of Object.keys(override.extra)) {
|
||||
if (!blueprint.customOverrideCriteriaFunctions?.[extraCriterion]) {
|
||||
if (strict) {
|
||||
throw new ConfigValidationError(`Unknown override extra criterion '${extraCriterion}'`);
|
||||
}
|
||||
|
||||
delete override.extra[extraCriterion];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate override config
|
||||
const partialOverrideConfigValidation = decodeAndValidateStrict(partialConfigSchema, override.config || {});
|
||||
if (partialOverrideConfigValidation instanceof StrictValidationError) {
|
||||
throw strictValidationErrorToConfigValidationError(partialOverrideConfigValidation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
import * as t from "io-ts";
|
||||
import { getPluginConfigPreprocessor } from "../pluginUtils";
|
||||
import { TMarkdown } from "../types";
|
||||
import { Awaitable } from "knub/dist/utils";
|
||||
import { PluginOptions } from "knub/dist/config/configTypes";
|
||||
|
||||
/**
|
||||
* GUILD PLUGINS
|
||||
|
@ -26,6 +28,11 @@ export interface ZeppelinGuildPluginBlueprint<TPluginData extends GuildPluginDat
|
|||
usageGuide?: TMarkdown;
|
||||
configurationGuide?: TMarkdown;
|
||||
};
|
||||
|
||||
configPreprocessor?: (
|
||||
options: PluginOptions<TPluginData["_pluginType"]>,
|
||||
strict?: boolean,
|
||||
) => Awaitable<PluginOptions<TPluginData["_pluginType"]>>;
|
||||
}
|
||||
|
||||
export function zeppelinGuildPlugin<TBlueprint extends ZeppelinGuildPluginBlueprint>(
|
||||
|
@ -59,6 +66,7 @@ export function zeppelinGuildPlugin(...args) {
|
|||
export interface ZeppelinGlobalPluginBlueprint<TPluginType extends BasePluginType = BasePluginType>
|
||||
extends GlobalPluginBlueprint<GlobalPluginData<TPluginType>> {
|
||||
configSchema: t.TypeC<any>;
|
||||
configPreprocessor?: (options: PluginOptions<TPluginType>, strict?: boolean) => Awaitable<PluginOptions<TPluginType>>;
|
||||
}
|
||||
|
||||
export function zeppelinGlobalPlugin<TBlueprint extends ZeppelinGlobalPluginBlueprint>(
|
||||
|
|
|
@ -111,7 +111,11 @@ export function validate(schema: t.Type<any>, 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<T extends t.HasProps>(schema: T, value: any): StrictValidationError | any {
|
||||
export function decodeAndValidateStrict<T extends t.HasProps>(
|
||||
schema: T,
|
||||
value: any,
|
||||
debug = false,
|
||||
): StrictValidationError | any {
|
||||
const validationResult = t.exact(schema).decode(value);
|
||||
return pipe(
|
||||
validationResult,
|
||||
|
@ -119,6 +123,14 @@ export function decodeAndValidateStrict<T extends t.HasProps>(schema: T, value:
|
|||
err => report(validationResult),
|
||||
result => {
|
||||
// Make sure there are no extra properties
|
||||
if (debug)
|
||||
console.log(
|
||||
"JSON.stringify() check:",
|
||||
JSON.stringify(value) === JSON.stringify(result)
|
||||
? "they are the same, no excess"
|
||||
: "they are not the same, might have excess",
|
||||
result,
|
||||
);
|
||||
if (JSON.stringify(value) !== JSON.stringify(result)) {
|
||||
const diff = deepDiff(result, value);
|
||||
const errors = diff.filter(d => d.kind === "N").map(d => `Unknown property <${d.path.join(".")}>`);
|
||||
|
|
Loading…
Add table
Reference in a new issue