mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-16 14:11:50 +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)!;
|
const plugin = pluginNameToPlugin.get(pluginName)!;
|
||||||
try {
|
try {
|
||||||
const mergedOptions = configUtils.mergeConfig(plugin.defaultOptions || {}, pluginOptions);
|
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) {
|
} catch (err) {
|
||||||
if (err instanceof ConfigValidationError || err instanceof StrictValidationError) {
|
if (err instanceof ConfigValidationError || err instanceof StrictValidationError) {
|
||||||
return `${pluginName}: ${err.message}`;
|
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({
|
const BasicPluginStructureType = t.type({
|
||||||
enabled: tNullable(t.boolean),
|
enabled: tNullable(t.boolean),
|
||||||
config: tNullable(t.unknown),
|
config: tNullable(t.unknown),
|
||||||
|
@ -74,7 +87,7 @@ export function getPluginConfigPreprocessor(
|
||||||
blueprint: ZeppelinPlugin,
|
blueprint: ZeppelinPlugin,
|
||||||
customPreprocessor?: ZeppelinPlugin["configPreprocessor"],
|
customPreprocessor?: ZeppelinPlugin["configPreprocessor"],
|
||||||
) {
|
) {
|
||||||
return async (options: PluginOptions<any>) => {
|
return async (options: PluginOptions<any>, strict?: boolean) => {
|
||||||
// 1. Validate the basic structure of plugin config
|
// 1. Validate the basic structure of plugin config
|
||||||
const basicOptionsValidation = validate(BasicPluginStructureType, options);
|
const basicOptionsValidation = validate(BasicPluginStructureType, options);
|
||||||
if (basicOptionsValidation instanceof StrictValidationError) {
|
if (basicOptionsValidation instanceof StrictValidationError) {
|
||||||
|
@ -93,8 +106,32 @@ export function getPluginConfigPreprocessor(
|
||||||
|
|
||||||
if (options.overrides) {
|
if (options.overrides) {
|
||||||
for (const override of options.overrides) {
|
for (const override of options.overrides) {
|
||||||
const partialOverrideConfigValidation = validate(partialConfigSchema, override.config || {});
|
// Validate criteria and extra criteria
|
||||||
if (partialOverrideConfigValidation) {
|
// 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);
|
throw strictValidationErrorToConfigValidationError(partialOverrideConfigValidation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import {
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { getPluginConfigPreprocessor } from "../pluginUtils";
|
import { getPluginConfigPreprocessor } from "../pluginUtils";
|
||||||
import { TMarkdown } from "../types";
|
import { TMarkdown } from "../types";
|
||||||
|
import { Awaitable } from "knub/dist/utils";
|
||||||
|
import { PluginOptions } from "knub/dist/config/configTypes";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GUILD PLUGINS
|
* GUILD PLUGINS
|
||||||
|
@ -26,6 +28,11 @@ export interface ZeppelinGuildPluginBlueprint<TPluginData extends GuildPluginDat
|
||||||
usageGuide?: TMarkdown;
|
usageGuide?: TMarkdown;
|
||||||
configurationGuide?: TMarkdown;
|
configurationGuide?: TMarkdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
configPreprocessor?: (
|
||||||
|
options: PluginOptions<TPluginData["_pluginType"]>,
|
||||||
|
strict?: boolean,
|
||||||
|
) => Awaitable<PluginOptions<TPluginData["_pluginType"]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function zeppelinGuildPlugin<TBlueprint extends ZeppelinGuildPluginBlueprint>(
|
export function zeppelinGuildPlugin<TBlueprint extends ZeppelinGuildPluginBlueprint>(
|
||||||
|
@ -59,6 +66,7 @@ export function zeppelinGuildPlugin(...args) {
|
||||||
export interface ZeppelinGlobalPluginBlueprint<TPluginType extends BasePluginType = BasePluginType>
|
export interface ZeppelinGlobalPluginBlueprint<TPluginType extends BasePluginType = BasePluginType>
|
||||||
extends GlobalPluginBlueprint<GlobalPluginData<TPluginType>> {
|
extends GlobalPluginBlueprint<GlobalPluginData<TPluginType>> {
|
||||||
configSchema: t.TypeC<any>;
|
configSchema: t.TypeC<any>;
|
||||||
|
configPreprocessor?: (options: PluginOptions<TPluginType>, strict?: boolean) => Awaitable<PluginOptions<TPluginType>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function zeppelinGlobalPlugin<TBlueprint extends ZeppelinGlobalPluginBlueprint>(
|
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
|
* Decodes and validates the given value against the given schema while also disallowing extra properties
|
||||||
* See: https://github.com/gcanti/io-ts/issues/322
|
* 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);
|
const validationResult = t.exact(schema).decode(value);
|
||||||
return pipe(
|
return pipe(
|
||||||
validationResult,
|
validationResult,
|
||||||
|
@ -119,6 +123,14 @@ export function decodeAndValidateStrict<T extends t.HasProps>(schema: T, value:
|
||||||
err => report(validationResult),
|
err => report(validationResult),
|
||||||
result => {
|
result => {
|
||||||
// Make sure there are no extra properties
|
// 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)) {
|
if (JSON.stringify(value) !== JSON.stringify(result)) {
|
||||||
const diff = deepDiff(result, value);
|
const diff = deepDiff(result, value);
|
||||||
const errors = diff.filter(d => d.kind === "N").map(d => `Unknown property <${d.path.join(".")}>`);
|
const errors = diff.filter(d => d.kind === "N").map(d => `Unknown property <${d.path.join(".")}>`);
|
||||||
|
|
Loading…
Add table
Reference in a new issue