mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 08:00:45 +00:00
37d1277b91
* feat(yaml-validator)!: unify flow, schedule, and trigger validation - replace FlowValidator with WindmillYamlValidator.validate(doc, target) - generate schedule/trigger schemas from backend OpenAPI and OpenFlow refs - add schedule/trigger/filename-target tests and update AI agent fixtures - bump windmill-yaml-validator to 2.0.0 BREAKING CHANGE: FlowValidator and validateFlow() are replaced by WindmillYamlValidator.validate(doc, target). * add lint command * add deno-compat script and docs for local yaml-validator testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: make nullable fields pass yaml validation Add nullable: true to static_asset_config and authentication_resource_path in HttpTrigger schema. Post-process generated JSON schemas to add null to enums with nullable: true (AJV doesn't handle OpenAPI 3.0 nullable + enum). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add nullable to all Option<T> fields in trigger and schedule OpenAPI schemas Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(frontend): handle nullable fields from updated OpenAPI types Add ?? undefined coalescing at assignment sites where generated types now include | null from the OpenAPI nullable additions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(lint): show allowed values in enum validation errors Instead of "must be equal to one of the allowed values", now shows "must be one of: 'r', 'w', 'rw'" for enum validation failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add nullable to Edit/New trigger and schedule OpenAPI schemas Ensures create/update request body types accept null for the same fields that GET response types return as nullable, enabling clean round-tripping without type mismatches. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * use published package * publish * refactor(lint): remove unused --includes/--excludes/--extra-includes CLI options These options were defined but never wired to the file filtering logic. The lint command still respects includes/excludes from wmill.yaml via mergeConfigWithConfigFile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(lint): handle additionalProperties errors and expand test coverage Add formatting for AJV additionalProperties keyword to show the unknown property name. Add unit tests for all formatValidationError branches and integration tests for --json report shape, --fail-on-warn with mixed files, non-existent directory, and enum error output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add realistic validator tests for schedules, triggers, and edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add email trigger validation support Add email trigger schema generation, validation, and linting. Email triggers are no longer skipped with a warning — they are validated like all other trigger types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(cli): bump windmill-yaml-validator to 1.1.1 (email trigger support) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * publish * rm * fix: address PR review feedback for lint command - Add email to trigger kinds test loop instead of separate test - Add email to ValidationTarget docs in README - Type formatYamlDiagnostics param directly instead of unsafe cast - Destructure json option before mergeConfigWithConfigFile for clarity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add --lint option to sync push command Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
259 lines
6.3 KiB
Markdown
259 lines
6.3 KiB
Markdown
# Windmill YAML Validator
|
|
|
|
A TypeScript-based YAML validator for Windmill flow, schedule, and trigger files.
|
|
|
|
## Overview
|
|
|
|
The windmill-yaml-validator provides runtime validation for Windmill YAML files. It is used by editor integrations to show validation errors while editing:
|
|
|
|
- `flow.yaml` / `flow.yml`
|
|
- `*.schedule.yaml` / `*.schedule.yml`
|
|
- `*.{http|websocket|kafka|nats|postgres|mqtt|sqs|gcp}_trigger.yaml` (or `.yml`)
|
|
|
|
## Features
|
|
|
|
- **Unified validation API**: One validator class for flow/schedule/trigger files
|
|
- **Schema-based validation**: Uses OpenFlow and backend OpenAPI-derived schemas
|
|
- **Detailed error reporting**: Returns comprehensive error information with specific paths to invalid fields
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install windmill-yaml-validator
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Validation
|
|
|
|
```typescript
|
|
import { WindmillYamlValidator } from "windmill-yaml-validator";
|
|
|
|
const validator = new WindmillYamlValidator();
|
|
|
|
const flowYaml = `
|
|
summary: Test Flow
|
|
value:
|
|
modules: []
|
|
`;
|
|
|
|
const flowResult = validator.validate(flowYaml, { type: "flow" });
|
|
|
|
const scheduleYaml = `
|
|
schedule: "0 0 12 * * *"
|
|
timezone: "UTC"
|
|
enabled: true
|
|
script_path: "f/jobs/daily_sync"
|
|
is_flow: false
|
|
`;
|
|
|
|
const scheduleResult = validator.validate(scheduleYaml, { type: "schedule" });
|
|
|
|
const triggerYaml = `
|
|
script_path: "f/triggers/http_handler"
|
|
is_flow: false
|
|
route_path: "api/webhook"
|
|
request_type: "sync"
|
|
authentication_method: "none"
|
|
http_method: "post"
|
|
is_static_website: false
|
|
workspaced_route: false
|
|
wrap_body: false
|
|
raw_string: false
|
|
`;
|
|
|
|
const triggerResult = validator.validate(triggerYaml, {
|
|
type: "trigger",
|
|
triggerKind: "http",
|
|
});
|
|
|
|
console.log(flowResult.errors, scheduleResult.errors, triggerResult.errors);
|
|
```
|
|
|
|
### Target Inference by Filename
|
|
|
|
```typescript
|
|
import {
|
|
WindmillYamlValidator,
|
|
getValidationTargetFromFilename,
|
|
} from "windmill-yaml-validator";
|
|
|
|
const validator = new WindmillYamlValidator();
|
|
const target = getValidationTargetFromFilename(
|
|
"f/webhooks/order_created.http_trigger.yaml"
|
|
);
|
|
|
|
if (target) {
|
|
const result = validator.validate(fileContents, target);
|
|
console.log(result.errors);
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```typescript
|
|
const invalidYaml = `
|
|
summary: 123 # Should be a string
|
|
value:
|
|
modules:
|
|
- id: step1
|
|
value:
|
|
type: rawscript
|
|
language: invalid_language # Invalid enum value
|
|
`;
|
|
|
|
const result = validator.validate(invalidYaml, { type: "flow" });
|
|
|
|
result.errors.forEach((error) => {
|
|
console.log(`Error at ${error.instancePath}: ${error.message}`);
|
|
// Example output:
|
|
// Error at /summary: must be string
|
|
// Error at /value/modules/0/value/language: must be equal to one of the allowed values
|
|
});
|
|
```
|
|
|
|
## API
|
|
|
|
### `WindmillYamlValidator`
|
|
|
|
Main validator class for Windmill YAML validation.
|
|
|
|
#### Constructor
|
|
|
|
```typescript
|
|
new WindmillYamlValidator();
|
|
```
|
|
|
|
Initializes AJV validators for flow, schedule, and trigger schemas.
|
|
|
|
#### Methods
|
|
|
|
##### `validate(doc: string, target: ValidationTarget)`
|
|
|
|
Validates a YAML document against the selected target schema.
|
|
|
|
**Parameters:**
|
|
|
|
- `doc` (string): YAML document string
|
|
- `target` (`ValidationTarget`):
|
|
- `{ type: "flow" }`
|
|
- `{ type: "schedule" }`
|
|
- `{ type: "trigger", triggerKind: "http" | "websocket" | "kafka" | "nats" | "postgres" | "mqtt" | "sqs" | "gcp" | "email" }`
|
|
|
|
**Returns:**
|
|
|
|
```typescript
|
|
{
|
|
parsed: YamlParserResult<unknown>; // Parsed YAML with source pointers
|
|
errors: ErrorObject[]; // Array of validation errors (empty if valid)
|
|
}
|
|
```
|
|
|
|
**Throws:**
|
|
|
|
- Error if `doc` is not a string
|
|
|
|
### `getValidationTargetFromFilename(path: string)`
|
|
|
|
Infers validation target from file naming conventions. Returns `null` for unsupported files.
|
|
|
|
## Development
|
|
|
|
### Building
|
|
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
The build process:
|
|
|
|
1. Runs `gen_openflow_schema.sh` to generate:
|
|
- `src/gen/openflow.json`
|
|
- `src/gen/schedule.json`
|
|
- `src/gen/triggers/*.json`
|
|
2. Removes discriminator mappings (not supported by AJV)
|
|
3. Compiles TypeScript to JavaScript
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
Run tests in watch mode:
|
|
|
|
```bash
|
|
npm test:watch
|
|
```
|
|
|
|
### Testing locally with the CLI
|
|
|
|
The Windmill CLI (`cli/`) is Deno-based and imports this package via `npm:windmill-yaml-validator@1.1.0`. Since Deno's `npm:` specifier always resolves from the npm registry, local testing requires a compatibility script that makes the TypeScript sources directly importable by Deno.
|
|
|
|
The `deno-compat.sh` script handles two Deno requirements:
|
|
- Adding `.ts` extensions to relative imports
|
|
- Adding `with { type: "json" }` assertions to JSON imports
|
|
|
|
**Steps:**
|
|
|
|
1. Apply Deno compatibility:
|
|
|
|
```bash
|
|
./deno-compat.sh
|
|
```
|
|
|
|
2. Add the following entries to `cli/deno.json` imports:
|
|
|
|
```json
|
|
"npm:windmill-yaml-validator@1.1.0": "../windmill-yaml-validator/src/index.ts",
|
|
"ajv": "npm:ajv@^8.17.1",
|
|
"@stoplight/yaml": "npm:@stoplight/yaml@^4.3.0"
|
|
```
|
|
|
|
3. Run the CLI directly with Deno:
|
|
|
|
```bash
|
|
cd ../cli
|
|
deno run -A src/main.ts lint
|
|
```
|
|
|
|
4. When done, restore everything:
|
|
|
|
```bash
|
|
./deno-compat.sh -r # restore original imports
|
|
# Remove the 3 import map lines from cli/deno.json
|
|
```
|
|
|
|
### Schema Generation
|
|
|
|
The validator uses a JSON schema generated from the OpenAPI specification:
|
|
|
|
```bash
|
|
./gen_openflow_schema.sh
|
|
```
|
|
|
|
This script:
|
|
|
|
- Converts `openflow.openapi.yaml` and `backend/windmill-api/openapi.yaml` into JSON
|
|
- Removes discriminator mappings for AJV compatibility
|
|
- Removes the `ToolValue` discriminator entirely (see below)
|
|
- Generates standalone schedule/trigger schemas for CLI file shape
|
|
|
|
#### Why Remove Discriminators?
|
|
|
|
The OpenFlow schema uses OpenAPI discriminators for efficient type resolution in `oneOf` schemas. However, AJV's discriminator support has limitations:
|
|
|
|
1. **Discriminator Mappings**: Not fully supported by AJV, so they are removed from all schemas
|
|
2. **ToolValue Discriminator**: Completely removed because `FlowModuleTool` uses `allOf` composition, which prevents AJV from finding the discriminator property (`tool_type`) at the expected location
|
|
|
|
**Impact**: Without discriminators, AJV falls back to standard `oneOf` validation, which:
|
|
|
|
- Tests each alternative until one matches
|
|
- Is slightly slower but still performant for our use case
|
|
- Provides the same validation correctness
|
|
- Works correctly with complex schema compositions like `allOf`
|
|
|
|
## Breaking Change
|
|
|
|
`FlowValidator` and `validateFlow()` were replaced by `WindmillYamlValidator` and `validate(doc, target)`.
|