Validate basic plugin options structure and override criteria types

This commit is contained in:
Dragory 2020-07-30 00:29:44 +03:00
parent 70e072e664
commit ed757dcbe2
No known key found for this signature in database
GPG key ID: 5F387BA66DF8AAC1
2 changed files with 56 additions and 20 deletions

View file

@ -4,11 +4,13 @@
import { Member } from "eris"; import { Member } from "eris";
import { configUtils, helpers, PluginBlueprint, PluginData, PluginOptions } from "knub"; import { configUtils, helpers, PluginBlueprint, PluginData, PluginOptions } from "knub";
import { decodeAndValidateStrict, StrictValidationError } from "./validatorUtils"; import { decodeAndValidateStrict, StrictValidationError, validate } from "./validatorUtils";
import { deepKeyIntersect, errorMessage, successMessage } from "./utils"; import { deepKeyIntersect, errorMessage, successMessage, tNullable } from "./utils";
import { ZeppelinPluginBlueprint } from "./plugins/ZeppelinPluginBlueprint"; import { ZeppelinPluginBlueprint } from "./plugins/ZeppelinPluginBlueprint";
import { TZeppelinKnub } from "./types"; import { TZeppelinKnub } from "./types";
import { ExtendedMatchParams } from "knub/dist/config/PluginConfigManager"; // TODO: Export from Knub index 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; const { getMemberLevel } = helpers;
@ -27,35 +29,69 @@ export function hasPermission(pluginData: PluginData<any>, permission: string, m
return helpers.hasPermission(config, permission); 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( export function getPluginConfigPreprocessor(
blueprint: ZeppelinPluginBlueprint, blueprint: ZeppelinPluginBlueprint,
customPreprocessor?: PluginBlueprint<any>["configPreprocessor"], customPreprocessor?: PluginBlueprint<any>["configPreprocessor"],
) { ) {
return async (options: PluginOptions<any>) => { return async (options: PluginOptions<any>) => {
const basicOptionsValidation = validate(BasicPluginStructureType, options);
if (basicOptionsValidation instanceof StrictValidationError) {
throw basicOptionsValidation;
}
if (customPreprocessor) { if (customPreprocessor) {
options = await customPreprocessor(options); options = await customPreprocessor(options);
} }
const decodedConfig = blueprint.configSchema let decodedConfig = {};
? decodeAndValidateStrict(blueprint.configSchema, options.config) const decodedOverrides = [];
: options.config;
if (decodedConfig instanceof StrictValidationError) { if (options.config) {
throw decodedConfig; decodedConfig = blueprint.configSchema
? decodeAndValidateStrict(blueprint.configSchema, options.config)
: options.config;
if (decodedConfig instanceof StrictValidationError) {
throw decodedConfig;
}
} }
const decodedOverrides = []; if (options.overrides) {
for (const override of options.overrides || []) { for (const override of options.overrides || []) {
const overrideConfigMergedWithBaseConfig = configUtils.mergeConfig(options.config, override.config || {}); const overrideConfigMergedWithBaseConfig = configUtils.mergeConfig(options.config, override.config || {});
const decodedOverrideConfig = blueprint.configSchema const decodedOverrideConfig = blueprint.configSchema
? decodeAndValidateStrict(blueprint.configSchema, overrideConfigMergedWithBaseConfig) ? decodeAndValidateStrict(blueprint.configSchema, overrideConfigMergedWithBaseConfig)
: overrideConfigMergedWithBaseConfig; : overrideConfigMergedWithBaseConfig;
if (decodedOverrideConfig instanceof StrictValidationError) { if (decodedOverrideConfig instanceof StrictValidationError) {
throw decodedOverrideConfig; throw decodedOverrideConfig;
}
decodedOverrides.push({
...override,
config: deepKeyIntersect(decodedOverrideConfig, override.config || {}),
});
} }
decodedOverrides.push({
...override,
config: deepKeyIntersect(decodedOverrideConfig, override.config || {}),
});
} }
return { return {

View file

@ -98,7 +98,7 @@ 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(schema: t.HasProps, value: any): StrictValidationError | any { export function decodeAndValidateStrict<T extends t.HasProps>(schema: T, value: any): StrictValidationError | any {
const validationResult = t.exact(schema).decode(value); const validationResult = t.exact(schema).decode(value);
return pipe( return pipe(
validationResult, validationResult,