diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md new file mode 100644 index 0000000000..478d25398c --- /dev/null +++ b/.agents/skills/writer/SKILL.md @@ -0,0 +1,47 @@ +--- +name: writer +description: Write, edit, and restructure user-facing and developer-facing documentation for the Spine `validation` repository. Use when asked to create/update docs such as `README.md`, `docs/**`, `CONTRIBUTING.md`, and other Markdown documentation; when drafting tutorials, guides, troubleshooting pages, or migration notes; and when improving inline API documentation (KDoc) and examples. +--- + +# Write documentation (repo-specific) + +## Decide the target and audience + +- Identify the target reader: end user, contributor, maintainer, or tooling/automation. +- Identify the task type: new doc, update, restructure, or documentation audit. +- Identify the acceptance criteria: “what is correct when the reader is done?” + +## Choose where the content should live + +- Prefer updating an existing doc over creating a new one. +- Place content in the most discoverable location: + - `README.md`: project entry point and “what is this?”. + - `docs/`: longer-form docs (follow existing conventions in that tree). + - `CONTRIBUTING.md`: contributor workflows. + - Source KDoc: API usage, examples, and semantics that belong with the code. + +## Follow local documentation conventions + +- Follow `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`. +- Use fenced code blocks for commands and examples; format file/dir names as code. +- Avoid widows, runts, orphans, and rivers by reflowing paragraphs when needed. + +## Make docs actionable + +- Prefer steps the reader can execute (commands + expected outcome). +- Prefer concrete examples over abstract descriptions. +- Include prerequisites (versions, OS, environment) when they are easy to miss. +- Use consistent terminology (match code identifiers and existing docs). + +## KDoc-specific guidance + +- For public/internal APIs, include at least one example snippet demonstrating common usage. +- When converting from Javadoc/inline comments to KDoc: + - Remove HTML like `

` and preserve meaning. + - Prefer short paragraphs and blank lines over HTML formatting. + +## Validate changes + +- For code changes, follow `.agents/running-builds.md`. +- For documentation-only changes in Kotlin/Java sources, prefer `./gradlew dokka`. + diff --git a/.agents/skills/writer/agents/openai.yaml b/.agents/skills/writer/agents/openai.yaml new file mode 100644 index 0000000000..44eaa4e241 --- /dev/null +++ b/.agents/skills/writer/agents/openai.yaml @@ -0,0 +1,5 @@ +interface: + display_name: "Writer" + short_description: "Write and update user/developer docs" + default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, keep changes concise and actionable, and include concrete examples and commands where appropriate." + diff --git a/.agents/skills/writer/assets/templates/doc-page.md b/.agents/skills/writer/assets/templates/doc-page.md new file mode 100644 index 0000000000..f405b71e15 --- /dev/null +++ b/.agents/skills/writer/assets/templates/doc-page.md @@ -0,0 +1,23 @@ +# Title + +## Goal + +State what the reader will accomplish. + +## Prerequisites + +- List versions/tools the reader needs. + +## Steps + +1. Do the first thing. +2. Do the next thing. + +## Verify + +Show how the reader can confirm success. + +## Troubleshooting + +- Common failure: likely cause → fix. + diff --git a/.agents/skills/writer/assets/templates/kdoc-example.md b/.agents/skills/writer/assets/templates/kdoc-example.md new file mode 100644 index 0000000000..fdbd9b6a0d --- /dev/null +++ b/.agents/skills/writer/assets/templates/kdoc-example.md @@ -0,0 +1,11 @@ +````kotlin +/** + * Explain what this API does in one sentence. + * + * ## Example + * ```kotlin + * // Show the typical usage pattern. + * val result = doThing() + * ``` + */ +```` diff --git a/.agents/skills/writer/assets/templates/kotlin-java-example.md b/.agents/skills/writer/assets/templates/kotlin-java-example.md new file mode 100644 index 0000000000..5517516f56 --- /dev/null +++ b/.agents/skills/writer/assets/templates/kotlin-java-example.md @@ -0,0 +1,13 @@ +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +``` +{{< /code-tab >}} + +{{< /code-tabs >}} diff --git a/.agents/tasks/archive/concepts-plan.md b/.agents/tasks/archive/concepts-plan.md new file mode 100644 index 0000000000..2185a4137c --- /dev/null +++ b/.agents/tasks/archive/concepts-plan.md @@ -0,0 +1,57 @@ +**Goal**: explain how Validation works (one layer deeper than “getting started”) +- Add a Concepts landing page and an “options overview” page that explains: + where options come from, how they’re applied, and what code gets generated. +- Keep this conceptual (no option-by-option details yet). +- Target outcome: a reader can explain that Validation is enforced by generated code + (not by parsing descriptor/options at runtime) and that the Gradle plugin wires the + Validation Compiler into the build to augment `protoc` output. +- Terminology: use “Validation Compiler” consistently (it is a plugin to the Spine Compiler). +- Pages to create/update: + - Concepts landing: `docs/content/docs/validation/02-concepts/_index.md` + - Options overview: `docs/content/docs/validation/02-concepts/options-overview.md` +- Concepts landing page: planned content (conceptual, 1 screen deeper than “Getting started”) + - Open with a 2–3 sentence “mental model”: + - Constraints are declared in `.proto` files using built-in options imported from `spine/options.proto`. + - The Validation Compiler updates Java sources generated by `protoc` so runtime checks happen in code. + - Explain the “build-time vs runtime” boundary: + - Build-time: options are read from descriptors and used to generate/inject checks into message code. + - Runtime: there is no descriptor parsing; validation runs as regular code in `build()` / `validate()`. + - Add a small pipeline diagram (Mermaid) showing: + `.proto` + `spine/options.proto` import → `protoc` (Java) → Validation Compiler (via Gradle plugin) + → generated Java API (`build()`, `validate()`). + - Define (briefly) what Protobuf custom options are with a one-sentence refresher, + then link to Protobuf docs for details. + - Clarify “what you write” vs “what you get”: + - You write: option annotations on fields/messages. + - You get: runtime enforcement through `build()` throwing `ValidationException` and + `validate()` returning an error object (as introduced in “Getting started”). + - Mention extensibility without diving in: + - Note that custom validation options + codegen are possible; link forward to + `docs/content/docs/validation/08-custom-validation/_index.md`. + - Close with “What’s next” links: + - Options overview page. + - Custom validation (for organization-specific rules). + - Developers guide (architecture/key modules) for internals. +- Options overview page: planned content (concrete examples, but not an option catalog) + - Purpose statement: explain where built-in options come from and how they influence generated code. + - Where options come from (built-ins only): + - Options are defined in `spine/options.proto` (and related Spine option protos). + - Users import the option proto(s) and annotate their own message/field definitions. + - One-sentence refresher on Protobuf options + a link to Protobuf docs about custom options. + - How options are applied at build time: + - The Gradle plugin integrates Validation into the build. + - Validation Compiler uses the compiled descriptors (including option values) to augment `protoc` output. + - What code gets generated (high-level, user-facing): + - `build()` performs validation and throws `ValidationException` on violations. + - `validate()` can be used to obtain a structured validation error without throwing. + - (Avoid helper-class details; keep terminology aligned with “Using the generated code”.) + - What does *not* happen: + - Validation does not parse descriptor option data at runtime to decide what to validate. + - 1–3 tiny illustrative `.proto` snippets (no deep semantics): + - Example: `(required)` on a field. + - Example: `(pattern)` on a string field. + - Example: `(min)/(max)` on a numeric field. + Each snippet should be used only to anchor the “options → generated code” explanation. + - Add “What’s next” links: + - Built-in options reference (future section 4). + - Custom validation (for defining your own options/rules). diff --git a/.agents/tasks/archive/error-messages-plan.md b/.agents/tasks/archive/error-messages-plan.md new file mode 100644 index 0000000000..78921306b9 --- /dev/null +++ b/.agents/tasks/archive/error-messages-plan.md @@ -0,0 +1,34 @@ +# Task: Write documentation on working with validation error messages +- Placement of the page: Under the Concepts section after the “Options overview” page. +- Purpose: explain how validation error messages work, how to customize them, and how to use them in code. +- Target outcome: a reader can explain the relationship between validation options and error messages, +- how to define custom messages, and how to format them for end users or diagnostics. +- Terminology: `TemplateString`, `ValidationError`, `ConstraintViolation`, `format()`, `formatUnsafe()` +- Describe that validation options have default error messages and user-defined error messages + (via the `error_msg` field of a validation option, such as `(pattern).error_msg`). +- Give an example of a custom validation error message with placeholders + defined in a proto field option. Use `spine/options.proto` as a reference for + the option definition and the `error_msg` field. +- Explain the notion of placeholders in error messages and how they correspond to + values provided at runtime when a violation occurs. +- Describe how `TemplateString` works (placeholders + values) and how to convert it to a + human-readable message (formatting). +- Clarify the recommended ways to work with Validation errors in: + - Kotlin: `TemplateString.format()` / `TemplateString.formatUnsafe()`. + - Java: `io.spine.validation.TemplateStrings.format(TemplateString)` + - Java: `io.spine.validation.TemplateStrings.formatUnsafe(TemplateString)`. +- Explain the structure of `ValidationError` / `ConstraintViolation`, and what fields developers + should use when: + - displaying messages to end users; + - logging diagnostics (e.g. include `type_name`, `field_path`, and the unformatted template). +- Add troubleshooting notes for common runtime formatting problems (e.g. missing placeholder + values; choosing `formatUnsafe()` when partial substitution is acceptable). +- Source references to anchor the docs: + - `jvm-runtime/src/main/proto/spine/validation/error_message.proto` + - `jvm-runtime/src/main/proto/spine/validation/validation_error.proto` + - `jvm-runtime/src/main/kotlin/io/spine/validation/TemplateStringExts.kt` + - `jvm-runtime/src/main/kotlin/io/spine/validation/RuntimeErrorPlaceholder.kt` + +## Output + +Implemented as `docs/content/docs/validation/02-concepts/error-messages.md`. diff --git a/.agents/tasks/built-in-options-plan.md b/.agents/tasks/built-in-options-plan.md new file mode 100644 index 0000000000..90df03f04d --- /dev/null +++ b/.agents/tasks/built-in-options-plan.md @@ -0,0 +1,7 @@ +# Task: Publish a minimal reference set on built-in validation options +- Placement: a separate section coming after the "Concepts" section. +- From `docs/_options/options.proto`, + enumerate the built-in options and group them (fields, strings, numbers, collections, message-level). +- For each documented option: purpose, supported field types, common pitfalls, and a short `.proto` example. +- Start with the options already used in docs/examples: `(required)`, `(pattern)`, `(min)/(max)`, + `(distinct)`, `(validate)`. diff --git a/.agents/tasks/custom-validation-plan.md b/.agents/tasks/custom-validation-plan.md new file mode 100644 index 0000000000..831e9c6115 --- /dev/null +++ b/.agents/tasks/custom-validation-plan.md @@ -0,0 +1,47 @@ +# Task: Expand “Custom validation” docs (custom options + codegen) + +## Goal + +Turn the existing “Custom validation” landing page into a complete, end-to-end guide for +implementing organization-specific rules via custom Protobuf options and code generation. + +Target outcome: a reader can define a custom option, register it, implement reaction/view/generator, +and verify it works in a consumer project. + +## Placement + +- Placement of the pages: `docs/content/docs/validation/08-custom-validation/`. +- Keep the current landing page as an overview and add a single practical walkthrough page. + +## Planned content + +- Clarify the extension surface: + - Custom Protobuf option definition (`extend google.protobuf.*Options`). + - Option discovery + validation (reaction). + - Accumulating a model (views, plus policies if needed). + - Generating/injecting Java validation code (generator). +- Make the steps actionable: + - Show file/Gradle locations where each piece belongs in a consumer project. + - Explain registration points: + - `io.spine.option.OptionsProvider` + - `io.spine.tools.validation.java.ValidationOption` (SPI for custom option implementations) +- Provide a minimal “walkthrough” with the existing `(currency)` sample: + - Point to the option declaration, the reaction/view/generator, and the registration. + - Explain the intended contract: what rule is enforced, where the error message comes from. +- Add a short troubleshooting section: + - Option not discovered (missing `OptionsProvider`). + - Generator not invoked (missing `ValidationOption` SPI entry). + - Illegal option application should fail compilation (where to look for error messages). + +## Source references to anchor the docs + +- Existing overview page: + - `docs/content/docs/validation/08-custom-validation/_index.md` +- Full working example: + - `:tests:extensions` module (custom `(currency)` option implementation) + +## Output + +- Update: `docs/content/docs/validation/08-custom-validation/_index.md`. +- Add: `docs/content/docs/validation/08-custom-validation/currency-example.md`. + diff --git a/.agents/tasks/developers-guide-plan.md b/.agents/tasks/developers-guide-plan.md new file mode 100644 index 0000000000..7d584d77e2 --- /dev/null +++ b/.agents/tasks/developers-guide-plan.md @@ -0,0 +1,51 @@ +# Task: Complete Developer’s guide (architecture and key modules) + +## Goal + +Make the “Developer’s guide” pages sufficient for maintainers and contributors: +the reader should understand the compilation pipeline, where each responsibility lives, +and where to start when debugging a doc/codegen/runtime issue. + +## Placement + +- Placement of the pages: `docs/content/docs/validation/09-developers-guide/`. +- Expand the existing pages without adding many new sections (keep it minimal). + +## Planned content + +- Architecture page (`architecture.md`) + - Add a legend / step-by-step explanation for the existing diagram: + - where options are discovered; + - where the validation model is built (policies/views); + - where Java code is generated/injected; + - what artifacts flow between stages (descriptors, generated sources, resources). + - Call out the main extension points and where they plug in: + - custom options (reaction/view/generator + SPI); + - external message validators (`MessageValidator` + `@Validator` + KSP discovery). + - Add “Where to look” links: + - built-in options reference; + - custom validation section; + - key modules page. + +- Key modules page (`key-modules.md`) + - Keep the tables, but add a 1–2 paragraph “debugging map”: + - build-time problems (compiler/plugin) vs runtime problems (generated code / runtime library). + - Add a short list of “common entry points” by scenario: + - option semantics or compilation errors → `:context`, `:java`, `:gradle-plugin`; + - validator discovery problems → `:ksp`, `:java`; + - error message formatting → `:jvm-runtime`. + +## Source references to anchor the docs + +- Existing pages: + - `docs/content/docs/validation/09-developers-guide/architecture.md` + - `docs/content/docs/validation/09-developers-guide/key-modules.md` +- External message validator mechanism (for cross-linking): + - `jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt` + - `jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt` + +## Output + +- Update: `docs/content/docs/validation/09-developers-guide/architecture.md`. +- Update: `docs/content/docs/validation/09-developers-guide/key-modules.md`. + diff --git a/.agents/tasks/third-party-messages-plan.md b/.agents/tasks/third-party-messages-plan.md new file mode 100644 index 0000000000..690a7f7ec9 --- /dev/null +++ b/.agents/tasks/third-party-messages-plan.md @@ -0,0 +1,60 @@ +# Task: Document validating third-party (external) messages + +## Goal + +Explain how to enforce validation rules for Protobuf message classes that are **already generated** +by third parties and therefore cannot be updated via `.proto` option annotations + codegen. + +Target outcome: a reader can pick the right strategy depending on whether they control the `.proto` +source, and can implement an external message validator that is automatically applied when +validating local messages. + +## Placement + +- Placement of the page: after “Built-in options”, before “Custom validation”. +- Hugo section (minimal change): add the page under `docs/content/docs/validation/02-concepts/`. + If the site navigation later gains an “Advanced topics” section, the page can move there. + +## Planned content + +- Define “local” vs “external” messages: + - Local: `.proto` sources compiled in the current build; Validation injects checks into generated code. + - External: message classes already compiled (e.g., come from dependencies); codegen cannot be applied. +- What does and does not work: + - You cannot attach Validation options to fields of external messages unless you rebuild their `.proto`. + - External validators are applied **only when an external message is a field inside a local message**. + - External validators are not applied transitively inside other external messages. +- Recommended strategy decision tree: + - If you control `.proto`: prefer built-in options or custom validation options (codegen). + - If you don’t control `.proto`: use `MessageValidator` + `@Validator`. +- How external validation works (high-level): + - Implement `io.spine.validation.MessageValidator`. + - Annotate the implementation with `@io.spine.validation.Validator(M::class)`. + - Ensure a `public` no-args constructor (required by discovery/instantiation). + - Validation invokes the validator for: + - singular fields of type `M`; + - repeated fields of type `M`; + - map values of type `M`. +- Error reporting shape: + - Return `List`. + - Use `FieldViolation` (and other available violation types) to point at a field path and value. + - Mention that the runtime converts `DetectedViolation` into `ConstraintViolation`/`ValidationError`. +- Constraints and guardrails: + - Exactly one validator per external message type (duplicate is an error). + - Validators for local messages are prohibited (use options/codegen instead). +- Example walkthrough (short, copy-pastable): + - Implement `EarphonesValidator` (from `:tests:validator`) and show how it affects a local message + that contains an `Earphones` field. + +## Source references to anchor the docs + +- External validation API and requirements: + - `jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt` + - `jvm-runtime/src/main/kotlin/io/spine/validation/Validator.kt` +- Example implementation: + - `tests/validator/src/main/kotlin/io/spine/tools/validation/test/EarphonesValidator.kt` + +## Output + +Implemented as `docs/content/docs/validation/02-concepts/third-party-messages.md`. + diff --git a/.agents/tasks/validation-documentation-plan.md b/.agents/tasks/validation-documentation-plan.md index fb45460d54..8152490abd 100644 --- a/.agents/tasks/validation-documentation-plan.md +++ b/.agents/tasks/validation-documentation-plan.md @@ -6,12 +6,11 @@ buildable documentation set, without expanding scope unnecessarily. ## Key locations (source of truth) -- Docs content (Hugo): `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/` +- Docs content (Hugo): `docs/content/docs/validation/` - Protobuf options reference (for built-ins): - - `/Users/sanders/Projects/Spine/validation/docs/_options/options.proto` - - `/Users/sanders/Projects/Spine/validation/docs/_options/time_options.proto` -- Example projects for embedded snippets: `/Users/sanders/Projects/Spine/validation/docs/_examples/` -- Docs build notes: `/Users/sanders/Projects/Spine/validation/docs/GRADLE.md` + - `docs/_options/options.proto` +- Example projects for embedded snippets: `docs/_examples/` +- Docs build notes: `docs/GRADLE.md` ## Definition of done (for a “first complete docs cut”) @@ -25,51 +24,44 @@ buildable documentation set, without expanding scope unnecessarily. 1) Information architecture (IA): make Hugo navigation coherent - Status: DONE (2026-02-23). - Added/updated section landing pages: - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/_index.md` - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/09-developers-guide/_index.md` + - `docs/content/docs/validation/_index.md` + - `docs/content/docs/validation/09-developers-guide/_index.md` - Replaced broken `.../index.md` links with directory links where appropriate: - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/00-intro/_index.md` - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/01-getting-started/_index.md` + - `docs/content/docs/validation/00-intro/_index.md` + - `docs/content/docs/validation/01-getting-started/_index.md` - Added “What’s next” navigation to keep a clear reading path: - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/00-intro/target-audience.md` - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/00-intro/philosophy.md` - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/09-developers-guide/architecture.md` - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/09-developers-guide/key-modules.md` - - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/01-getting-started/first-model.md` + - `docs/content/docs/validation/00-intro/target-audience.md` + - `docs/content/docs/validation/00-intro/philosophy.md` + - `docs/content/docs/validation/09-developers-guide/architecture.md` + - `docs/content/docs/validation/09-developers-guide/key-modules.md` + - `docs/content/docs/validation/01-getting-started/first-model.md` - Fixed an obvious broken image reference: - `/Users/sanders/Projects/Spine/validation/docs/content/docs/validation/08-custom-validation/_index.md` + `docs/content/docs/validation/08-custom-validation/_index.md` 2) Complete “Getting started” flow - Status: DONE (2026-02-24). - Validate that the "Getting started" section covers: importing options, build-time validation, `build()` vs `buildPartial()`, and `validate()`. -3) Concepts: explain how Validation works (one layer deeper than “getting started”) -- Add a Concepts landing page and an “options overview” page that explains: - where options come from, how they’re applied, and what code gets generated. -- Keep this conceptual (no option-by-option details yet). - -4) Built-in options: publish a minimal reference set -- From `/Users/sanders/Projects/Spine/validation/docs/_options/options.proto` and - `/Users/sanders/Projects/Spine/validation/docs/_options/time_options.proto`, - enumerate the built-in options and group them (fields, strings, numbers, collections, message-level, time). -- For each documented option: purpose, supported field types, common pitfalls, and a short `.proto` example. -- Start with the options already used in docs/examples: `(required)`, `(pattern)`, `(min)/(max)`, - `(distinct)`, `(validate)`, `(when)`. - -5) Runtime API usage (Java + Kotlin) -- Document the two primary usage patterns: - - fail-fast on `build()` (throws `ValidationException`); - - non-throwing `validate()` (returns `Optional`). -- Link to the runtime entry points used by generated code: - `/Users/sanders/Projects/Spine/validation/jvm-runtime/src/main/java/io/spine/validation/ValidatableMessage.java`, - `/Users/sanders/Projects/Spine/validation/jvm-runtime/src/main/java/io/spine/validation/ValidatingBuilder.java`, - `/Users/sanders/Projects/Spine/validation/jvm-runtime/src/main/java/io/spine/validation/Validate.java`, - `/Users/sanders/Projects/Spine/validation/jvm-runtime/src/main/java/io/spine/validation/ValidationException.java`, - `/Users/sanders/Projects/Spine/validation/jvm-runtime/src/main/kotlin/io/spine/validation/MessageExtensions.kt`. - -6) Verification pass (keep it tight; fix only doc-related issues) -- From `/Users/sanders/Projects/Spine/validation/docs/`, run: +3) [Concepts](archive/concepts-plan.md) +- Status: DONE (2026-02-26). + +4) [Working with error messages](archive/error-messages-plan.md) +- Status: DONE (2026-02-27). + +5) [Built-in options](built-in-options-plan.md): publish a minimal reference set + +6) [Validating third-party messages](third-party-messages-plan.md) + +7) [Custom validation: defining your own options and codegen](custom-validation-plan.md) + +8) [Developer's guide: architecture and key modules](developers-guide-plan.md) + +## Verification pass + +Keep it tight; fix only doc-related issues. + +- From `docs/`, run: - `./gradlew embedCode` - `./gradlew checkSamples` - `./gradlew buildSite` diff --git a/dependencies.md b/dependencies.md index bf23d06224..7ce35d6304 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1139,14 +1139,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:06 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1731,7 +1731,7 @@ This report was generated on **Tue Feb 24 20:44:06 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:02 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -1752,7 +1752,7 @@ This report was generated on **Mon Feb 23 18:35:33 WET 2026** using -# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -2841,14 +2841,14 @@ This report was generated on **Mon Feb 23 18:35:33 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:06 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -3935,14 +3935,14 @@ This report was generated on **Tue Feb 24 20:44:06 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:06 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. @@ -4005,14 +4005,14 @@ This report was generated on **Tue Feb 24 20:44:06 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:01 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:validation-jvm-runtime:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine:validation-jvm-runtime:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -4845,14 +4845,14 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:06 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-ksp:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-ksp:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. @@ -5781,14 +5781,14 @@ This report was generated on **Tue Feb 24 20:44:06 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:06 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -6379,14 +6379,14 @@ This report was generated on **Tue Feb 24 20:44:06 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:01 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -6897,14 +6897,14 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -7588,14 +7588,14 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8217,14 +8217,14 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8889,14 +8889,14 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:06 WET 2026** using +This report was generated on **Thu Feb 26 22:11:03 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -9647,14 +9647,14 @@ This report was generated on **Tue Feb 24 20:44:06 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:02 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -9924,14 +9924,14 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:02 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.398` +# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.399` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -10282,6 +10282,6 @@ This report was generated on **Tue Feb 24 20:44:05 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Feb 24 20:44:05 WET 2026** using +This report was generated on **Thu Feb 26 22:11:01 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/_examples b/docs/_examples index c15b15240a..4d3727541f 160000 --- a/docs/_examples +++ b/docs/_examples @@ -1 +1 @@ -Subproject commit c15b15240a2d784dae161f84d316f9cf435843fc +Subproject commit 4d3727541f0c4f771a6c1a215eef065cd3e74032 diff --git a/docs/_options/time_options.proto b/docs/_options/time_options.proto deleted file mode 100644 index bc73fe15f5..0000000000 --- a/docs/_options/time_options.proto +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2025, TeamDev. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -syntax = "proto3"; - -// API Note on Packaging -// --------------------- -// We do not define the package for this file to allow shorter options for user-defined types. -// This allows to write: -// -// [(when).in = FUTURE]; -// -// instead of: -// -// [(spine.time.when).in = FUTURE]; -// - -import "spine/options.proto"; - -option (type_url_prefix) = "type.spine.io"; -option java_package = "io.spine.time.validation"; -option java_outer_classname = "TimeOptionsProto"; -option java_multiple_files = true; - -import "google/protobuf/descriptor.proto"; - -extend google.protobuf.FieldOptions { - - // See `TimeOption`. - TimeOption when = 73819; -} - -// Specifies that the field value is a point in time lying either in the future or in the past. -// -// Applicable to `google.protobuf.Timestamp` and types introduced in the `spine.time` package -// that describe time-related concepts. -// -// Repeated fields are supported. -// -// Example: Using the `(when)` option. -// -// message ScheduleMeeting { -// spine.time.ZonedDateTime start = 1 [(when).in = FUTURE]; -// } -// -message TimeOption { - - // The default error message. - option (default_message) = "The field `${parent.type}.${field.path}`" - " of the type `${field.type}` must be in the `${when.in}`." - " The encountered value: `${field.value}`."; - - // Defines a restriction for the timestamp. - Time in = 1; - - // Deprecated: please use `error_msg` instead. - string msg_format = 2 [deprecated = true]; - - // A user-defined error message. - // - // The specified message may include the following placeholders: - // - // 1. `${field.path}` – the field path. - // 2. `${field.value}` - the field value. - // 3. `${field.type}` – the fully qualified name of the field type. - // 4. `${parent.type}` – the fully qualified name of the validated message. - // 5. `${when.in}` – the specified timestamp restriction. It is either "past" or "future". - // - string error_msg = 3; -} - -// This enumeration defines restriction for date/time values. -enum Time { - - // The default value (if the time option is not set). - TIME_UNDEFINED = 0; - - // The value must be in the past. - PAST = 1; - - // The value must be in the future. - FUTURE = 2; -} diff --git a/docs/_preview/go.mod b/docs/_preview/go.mod index ced82e07df..3d83edfef3 100644 --- a/docs/_preview/go.mod +++ b/docs/_preview/go.mod @@ -3,7 +3,7 @@ module spine.io/validation/docs/preview go 1.25.6 require ( - github.com/SpineEventEngine/site-commons v0.0.0-20260225164144-d5e941ada2ae // indirect + github.com/SpineEventEngine/site-commons v0.0.0-20260226201948-3aec673b8046 // indirect github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20400 // indirect github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2 v2.21100.20000 // indirect github.com/twbs/bootstrap v5.3.8+incompatible // indirect diff --git a/docs/_preview/go.sum b/docs/_preview/go.sum index 69c70e17db..f70140dc27 100644 --- a/docs/_preview/go.sum +++ b/docs/_preview/go.sum @@ -1,5 +1,5 @@ -github.com/SpineEventEngine/site-commons v0.0.0-20260225164144-d5e941ada2ae h1:pNjNP8lCXbbzArqX42kc2Rbj2LpqU18ltF+skxZ/xO4= -github.com/SpineEventEngine/site-commons v0.0.0-20260225164144-d5e941ada2ae/go.mod h1:tkAl4StIREKmz9r5PiJtuDhvwMMkFXKWcaTyxhIikho= +github.com/SpineEventEngine/site-commons v0.0.0-20260226201948-3aec673b8046 h1:qx3XD7j5i3xhtDu/iRLdqwT16BxHg+9Z7wtXae1VWRU= +github.com/SpineEventEngine/site-commons v0.0.0-20260226201948-3aec673b8046/go.mod h1:tkAl4StIREKmz9r5PiJtuDhvwMMkFXKWcaTyxhIikho= github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20400 h1:L6+F22i76xmeWWwrtijAhUbf3BiRLmpO5j34bgl1ggU= github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20400/go.mod h1:uekq1D4ebeXgduLj8VIZy8TgfTjrLdSl6nPtVczso78= github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2 v2.21100.20000 h1:GZxx4Hc+yb0/t3/rau1j8XlAxLE4CyXns2fqQbyqWfs= diff --git a/docs/content/docs/validation/00-intro/philosophy.md b/docs/content/docs/validation/00-intro/philosophy.md index d2827f2104..0da8cbf498 100644 --- a/docs/content/docs/validation/00-intro/philosophy.md +++ b/docs/content/docs/validation/00-intro/philosophy.md @@ -123,8 +123,10 @@ front-end models, or JSON schemas. Its focus is entirely on: -``` -Protobuf → generated Java/Kotlin/TypeScript → domain logic +```mermaid +graph LR + A(Protobuf) --> B(Generated Java/Kotlin/TypeScript) + B --> C(Domain logic) ``` Everything else (frontend validation, OpenAPI, view models) should build on top diff --git a/docs/content/docs/validation/01-getting-started/adding-to-build.md b/docs/content/docs/validation/01-getting-started/adding-to-build.md index 74495d484a..8a3026ca63 100644 --- a/docs/content/docs/validation/01-getting-started/adding-to-build.md +++ b/docs/content/docs/validation/01-getting-started/adding-to-build.md @@ -82,7 +82,7 @@ Add the Validation plugin to the build. ```kotlin plugins { module - id("io.spine.validation") version "2.0.0-SNAPSHOT.398" + id("io.spine.validation") version "2.0.0-SNAPSHOT.399" } ``` diff --git a/docs/content/docs/validation/02-concepts/_index.md b/docs/content/docs/validation/02-concepts/_index.md index 86bc83dcb5..54c4ec69e0 100644 --- a/docs/content/docs/validation/02-concepts/_index.md +++ b/docs/content/docs/validation/02-concepts/_index.md @@ -6,4 +6,105 @@ headline: Documentation # Concepts -_Draft. We will add content later._ +This page introduces the core vocabulary and mechanics of Spine Validation. +It answers two questions: + +- **How do I express validation rules?** +- **How does the runtime report violations?** + +If you are new to the library, start with [declaring constraints](../01-getting-started/first-model) +in `.proto` files and then come back here for the details. + + +## Vocabulary + +Spine Validation uses a small set of concepts consistently across code generation and runtime: + +- **Constraint** — a validation rule declared in Protobuf using an option (for example, + `(required)`, `(min)`, `(max)`, `(pattern)`, `(distinct)`, `(validate)`, `(set_once)`). +- **Option application** — a concrete place where an option is applied (a field, a message, + a `oneof`, etc.). +- **Violation** — an instance of a constraint being broken. Violations are represented + as `ConstraintViolation`. +- **Validation error** — a collection of violations. Validation results are represented + as `ValidationError`. + + +## Where constraints live + +Constraints are part of the model. +You declare them next to message fields in your `.proto` files by importing +`spine/options.proto`. + +This makes validation rules: + +- versioned together with the data model, +- shared between services that reuse the same Protobuf definitions, +- enforced automatically by generated code. + + +## How validation is executed + +Validation is executed through **generated code** for your messages and builders. +The generated API exposes two main ways to validate data: + +1. **Fail-fast validation on creation.** + If a constraint is violated, `build()` throws `ValidationException`. + +2. **Validation of an existing instance.** + Generated messages implement `ValidatableMessage` and provide `validate()`, which returns + `Optional`. + +See [Using the generated code](../01-getting-started/generated-code.md) for end-to-end examples. + + +## What a violation contains + +Each `ConstraintViolation` points to the invalid value and explains what went wrong: + +- `field_path` — the path to the invalid field (for example, `contacts.email.value`). +- `type_name` — the name of the validated root message type. +- `message` — a templated, machine-friendly error message (`TemplateString`). +- `field_value` — the invalid value packed as `google.protobuf.Any`. + +When you need a human-readable message, format the `TemplateString` from the violation. +See [Working with error messages](error-messages.md) for message formatting, +placeholders, and customization. + + +## Nested validation and `(validate)` + +Protobuf messages often contain other messages. +Spine Validation supports validating nested structures and reporting correct field paths. + +When a nested message is validated as part of another message’s validation, a violation: + +- keeps the **root** type in `type_name`, and +- reports the **full path** to the invalid field in `field_path`. + +This allows you to surface errors as “`contacts.email.value` is invalid” while still knowing +which message was validated at the top level. + + +## Custom constraints + +If built-in options are not enough, you can add organization-specific options and generate +code for them. + +See [Custom validation](../08-custom-validation/) for the workflow and a reference example. + + +## What’s next + +- Declare rules in your model: + [Define constraints in `.proto` files](../01-getting-started/first-model.md). +- Learn how runtime validation behaves: + [Using the generated code](../01-getting-started/generated-code.md). +- See how options influence generated code: + [Options overview](options-overview.md). +- Customize and format messages: + [Working with error messages](error-messages.md). +- [Built-in options](../03-built-in-options/) +- [Validating third-party messages](../04-third-party-messages/) +- Add custom validation options: + [Custom validation](../08-custom-validation/). diff --git a/docs/content/docs/validation/02-concepts/error-messages.md b/docs/content/docs/validation/02-concepts/error-messages.md new file mode 100644 index 0000000000..ba00d250bd --- /dev/null +++ b/docs/content/docs/validation/02-concepts/error-messages.md @@ -0,0 +1,142 @@ +--- +title: Working with error messages +description: How validation error messages are built, customized, and formatted. +headline: Documentation +--- + +# Working with error messages + +When a message violates validation constraints, Spine Validation reports violations as +`ConstraintViolation` entries inside a `ValidationError`. + +Each violation contains a machine-friendly error message (`TemplateString`) which you can: + +- format for end users, +- log for diagnostics, and +- customize in your `.proto` model. + + +## Where error messages come from + +Every built-in validation option defines a **default** error message. +These messages come as the value of the `default_message` option declared for each option type. + +For example, the `pattern` option defines the following default message: + +```protobuf +message PatternOption { + + // The default error message. + option (default_message) = "The `${parent.type}.${field.path}` field" + " must match the regular expression `${regex.pattern}` (modifiers: `${regex.modifiers}`)." + " The passed value: `${field.value}`."; + + // ... +} +``` + +When you apply an option in a `.proto` file, you can **override** the default message by +setting the option’s `error_msg` field. + +Example: custom message for a regex pattern. + +```protobuf +import "spine/options.proto"; + +message CreateAccount { + string id = 1 [ + (pattern).regex = "^[A-Za-z0-9+]+$", + (pattern).error_msg = "ID must be alphanumerical in `${parent.type}`. Provided: `${field.value}`." + ]; +} +``` + +The placeholders (like `${field.value}`) are substituted at runtime when the violation is created. + +{{% note-block class="note" %}} +Each option documents the placeholders it supports next to its `error_msg` field +in `spine/options.proto`. +{{% /note-block %}} + +## Placeholders and `TemplateString` + +`ConstraintViolation.message` is a `TemplateString`: + +- `with_placeholders` — a template string that may contain placeholders like `${field.path}`. +- `placeholder_value` — a map from placeholder keys to their runtime values. + +The placeholder keys in the map do **not** include `${}` — for example, `field.path`. + +{{% note-block class="warning" %}} +The map may include extra keys that are not referenced by the template, but every placeholder +used in `with_placeholders` **must** have a corresponding value. +Otherwise, the template is invalid. +{{% /note-block %}} + +## Formatting messages in code + +To format a `TemplateString`, use: + +- Kotlin: `TemplateString.format()` / `TemplateString.formatUnsafe()` +- Java: `TemplateStrings.format(TemplateString)` / `TemplateStrings.formatUnsafe(TemplateString)` + +`format()` validates that all placeholders have values and throws `IllegalArgumentException` otherwise. +`formatUnsafe()` does not validate and leaves missing placeholders unsubstituted. + +{{< code-tabs langs="Kotlin, Java">}} + +{{< code-tab lang="Kotlin" >}} +```kotlin +val error = message.validate().orElse(null) ?: return +val violation = error.constraintViolationList.first() +val text = violation.message.format() +``` +{{< /code-tab >}} + +{{< code-tab lang="Java" >}} +```java +var error = message.validate(); +if (error.isEmpty()) { + return; +} +var violation = error.get().getConstraintViolation(0); +var text = TemplateStrings.format(violation.getMessage()); +``` +{{< /code-tab >}} + +{{< /code-tabs >}} + +## Choosing what to show vs what to log + +For **end-user** output: + +- format and display `ConstraintViolation.message`; +- avoid leaking internal type names unless your product requires them. + +For **diagnostics** and support logs: + +- include `type_name` and `field_path` to pinpoint the location of the violation; +- include the raw template (`message.with_placeholders`) and the placeholder map for debugging. + + +## Troubleshooting formatting issues + +### `IllegalArgumentException` from `format()` + +This means `with_placeholders` references a placeholder that has no entry in `placeholder_value`. + +Recommended actions: + +- treat this as a bug in the option/message definition, and fix the custom `error_msg` in your `.proto`; +- if you can tolerate partial substitution (for example, in logs), use `formatUnsafe()`. + +## What’s next + +- See how constraints are expressed as options and compiled into runtime checks: + [Options overview](options-overview.md). +- Explore the built-in options: + [Built-in options](../03-built-in-options/). +- Learn how to validate message types from third-party libraries: + [Validating third-party messages](../04-third-party-messages/). +- If built-in options are not enough, define your own constraints and messages: + [Custom validation](../08-custom-validation/). diff --git a/docs/content/docs/validation/02-concepts/options-overview.md b/docs/content/docs/validation/02-concepts/options-overview.md new file mode 100644 index 0000000000..a304cca991 --- /dev/null +++ b/docs/content/docs/validation/02-concepts/options-overview.md @@ -0,0 +1,114 @@ +--- +title: Options overview +description: Where validation options come from and how they influence generated code. +headline: Documentation +--- + +# Options overview + +Spine Validation rules are expressed as **Protobuf options**. +You annotate your `.proto` model with built-in options, and the **Validation Compiler** +turns those option values into runtime checks in the generated Java code. + +This page explains where the built-in options come from, how they are applied at build time, +and what API you get at runtime. + + +## Where options come from + +The built-in validation options are defined in `spine/options.proto`. + +The file comes in the `spine-base.jar` artifact, which is an API dependency +of the Validation runtime library (`spine-validation-java-runtime.jar`) added +to your project by the Validation Gradle plugin. + +To use an option, import the proto that defines it and annotate your fields and messages. + +```protobuf +import "spine/options.proto"; +``` + +If you need a refresher on how custom options work in Protobuf, see +[Protobuf custom options](https://protobuf.dev/programming-guides/proto3/#customoptions). + + +## How options are applied (build time) + +Spine Validation is enforced by **generated code**, not by interpreting option values at runtime. + +At build time: + +1. `protoc` compiles your `.proto` files into **descriptors** and generates Java sources. +2. The Spine Validation Gradle plugin wires the **Validation Compiler** into the build. +3. The Validation Compiler reads the compiled descriptors (including your option values) and + augments `protoc` output with validation logic. + +The result is a Java API that enforces the rules you declared in the model. + +```mermaid +flowchart LR + A[".proto
  +
spine/options.proto"] --> B["protoc
(Java)"] + B --> C["Validation Compiler
via Gradle plugin"] + C --> D["Generated Java API
build(), validate()"] +``` + +## What code you get (runtime) + +Generated messages and builders provide a small validation-focused API surface: + +- `build()` performs validation and throws `ValidationException` if a constraint is violated. +- `buildPartial()` builds without validation. +- `validate()` checks an existing instance and returns `Optional`. + +See [Using the generated code](../01-getting-started/generated-code.md) for Java and Kotlin examples. + + +## What does not happen + +At runtime, Spine Validation does **not** parse descriptor option data to decide what to validate. +All checks are already translated into the generated message/builder code. + + +## Tiny examples + +These examples are intentionally minimal. +They are only meant to illustrate the “annotate with options → get runtime checks” flow. + +### Required field + +```protobuf +import "spine/options.proto"; + +message UserEmail { + string value = 1 [(required) = true]; +} +``` + +### String pattern + +```protobuf +import "spine/options.proto"; + +message OrderId { + string value = 1 [(pattern).regex = "^[A-Z]{3}-\\d{6}$"]; +} +``` + +### Numeric range + +```protobuf +import "spine/options.proto"; + +message Temperature { + int32 kelvin = 1 [ + (min).value = "0", + (max).value = "10000" + ]; +} +``` + + +## What’s next + +- [Built-in options](../03-built-in-options/) +- [Custom validation](../08-custom-validation/) diff --git a/docs/content/docs/validation/03-built-in-options/_index.md b/docs/content/docs/validation/03-built-in-options/_index.md new file mode 100644 index 0000000000..6e186d9a85 --- /dev/null +++ b/docs/content/docs/validation/03-built-in-options/_index.md @@ -0,0 +1,12 @@ +--- +title: Built-in options +description: Reference for built-in Spine Validation options. +headline: Documentation +--- + +# Built-in options + +## What’s next + +- [Validating third-party messages](../04-third-party-messages/) +- [Custom validation](../08-custom-validation/) diff --git a/docs/content/docs/validation/04-third-party-messages/_index.md b/docs/content/docs/validation/04-third-party-messages/_index.md new file mode 100644 index 0000000000..18665689ab --- /dev/null +++ b/docs/content/docs/validation/04-third-party-messages/_index.md @@ -0,0 +1,12 @@ +--- +title: Validating third-party messages +description: How to validate message types generated outside your build. +headline: Documentation +--- + +# Validating third-party messages + +## What’s next + +- [Custom validation](../08-custom-validation/) +- [Architecture](../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/08-custom-validation/_index.md b/docs/content/docs/validation/08-custom-validation/_index.md index 91409e9a93..681741c310 100644 --- a/docs/content/docs/validation/08-custom-validation/_index.md +++ b/docs/content/docs/validation/08-custom-validation/_index.md @@ -25,6 +25,7 @@ Below is a workflow diagram for a typical option: ## What’s next +- [Validating third-party messages](../04-third-party-messages/) - Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). Take a look at the `:tests:extensions` module that contains a full example of diff --git a/docs/content/docs/validation/_index.md b/docs/content/docs/validation/_index.md index 6ac06fcda9..3d075a52b3 100644 --- a/docs/content/docs/validation/_index.md +++ b/docs/content/docs/validation/_index.md @@ -16,5 +16,7 @@ options, and runs those checks automatically when you build messages. ## Deeper topics +- [Built-in options](03-built-in-options/) +- [Validating third-party messages](04-third-party-messages/) - How it works: [Architecture](09-developers-guide/architecture.md) - Extension points: [Custom validation](08-custom-validation/) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index c1058569cb..907ffec735 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -31,6 +31,14 @@ children: - page: Concepts file_path: 02-concepts + - page: Options overview + file_path: 02-concepts/options-overview + - page: Working with error messages + file_path: 02-concepts/error-messages + - page: Built-in options + file_path: 03-built-in-options + - page: Validating third-party messages + file_path: 04-third-party-messages - page: Custom validation file_path: 08-custom-validation - page: Developer’s guide diff --git a/pom.xml b/pom.xml index b36592557f..ed86aa7649 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools validation -2.0.0-SNAPSHOT.398 +2.0.0-SNAPSHOT.399 2015 diff --git a/version.gradle.kts b/version.gradle.kts index 69fe447967..8b272b178d 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -29,4 +29,4 @@ * * For Spine-based dependencies please see [io.spine.dependency.local.Spine]. */ -val validationVersion by extra("2.0.0-SNAPSHOT.398") +val validationVersion by extra("2.0.0-SNAPSHOT.399")