Providers

validate()

What the bridge checks before your validate runs, and how to report cross-field rules as errors and warnings.

validate(proposed) is called with the complete proposed values after the bridge has checked them against the schema (types, ranges, patterns, choices, required values, list sizes, duplicate keys and so on). Only check rules the schema cannot express, typically cross-field rules. Errors block the change; warnings are shown but never block.

VoucherConfigProvider.java
@Override
public ValidationResult validate(ConfigValues proposed) {
    ValidationResult.Builder result = ValidationResult.builder();
    if (proposed.getString("settings.prefix", "").indexOf('§') >= 0) {
        result.error("settings.prefix", "Use MiniMessage tags such as <gold> instead of § color codes.");
    }
    List<ConfigValues> vouchers = proposed.getObjectList("vouchers");
    boolean anyEnabled = false;
    for (int i = 0; i < vouchers.size(); i++) {
        ConfigValues voucher = vouchers.get(i);
        boolean enabled = voucher.getBoolean("enabled", false);
        anyEnabled |= enabled;
        if (enabled && voucher.getLong("cooldown-seconds", 0) < 60) {
            result.warning("vouchers[" + i + "].cooldown-seconds",
                    "A cooldown under 60 seconds lets players redeem this voucher almost continuously.");
        }
        if (!voucher.getString("permission", "").startsWith("vouchers.")) {
            result.warning("vouchers[" + i + "].permission",
                    "Permissions usually start with 'vouchers.' so they can be granted together.");
        }
    }
    if (!anyEnabled) {
        result.error("vouchers", "At least one voucher must be enabled.");
    }
    // Text that config.yml cannot store first, see "Text that YAML cannot store" below.
    return mapper.validate(proposed).merge(result.build());
}

Issue paths use the syntax of Values and paths; "" addresses the whole resource. Messages are shown to the server owner (at most 500 characters). ValidationIssue also accepts your own machine-readable code ([a-z0-9_]{1,64}); the builder methods use the code plugin.

CallResult
ValidationResult.ok()No issues
ValidationResult.error(path, message)One error
ValidationResult.of(issues)The given ValidationIssues
ValidationResult.builder()error(path, message), warning(path, message), issue(ValidationIssue), build()
result.merge(other)Both results' issues
result.isValid()Whether there is no error

The demo merges SectionMapper.validate(values), which reports text that config.yml cannot store, with its own rules; see Text that YAML cannot store.

What the bridge checks for you

Before your validate runs, the bridge converts the submitted JSON and checks it against your schema. All problems are collected and reported as errors with these codes:

CodeRule
unknown_fieldA key the schema does not declare, at any depth.
invalid_typeWrong JSON type. Integers accept numbers without a fractional part (5.0 becomes 5) within ±9007199254740991; decimals accept any finite number. Text containing an unpaired UTF-16 surrogate (for example the JSON escape "\ud800") is rejected too, because it cannot be encoded as UTF-8.
requiredMissing or null required value; empty required string, text, enum or material.
min, maxNumber outside the inclusive range.
min_length, max_lengthLength in code points outside the range (per item for string lists).
patternNo full match; the message is your patternMessage when set.
invalid_choiceEnum value or string list item that is not a choice.
min_items, max_itemsList size outside the range.
duplicate_itemRepeated item in a uniqueItems string list, reported at the repeat.
duplicate_keyRepeated itemKey value, reported at list[i].<itemKey> of the repeat.
unknown_material, material_filterMaterial missing on the server, or not matching itemsOnly() / blocksOnly().
read_onlyA read-only value differs from the current value (items added to an object list have none to compare with).
not_writableA value was submitted for a sensitive field that is not writeOnly.
no_changesApply only: the proposal equals the current values.

Consequently your validate and apply receive values that contain exactly your declared fields, with the declared types (Long, Double, String, Boolean, lists and maps), satisfying every declared constraint. Optional fields without a value are present as null. You never need to re-check types or ranges. The only exception: stored values of sensitive fields that the submission left out are passed through as read() returned them (see Sensitive fields).

Built from f6347d4 on

On this page