UNPKG

windmill-yaml-validator

Version:

YAML validator for Windmill flow, schedule, and trigger files

237 lines (167 loc) 6.01 kB
# 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 `npm run gen` (`scripts/gen-schemas.js`) 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 install # cli/ installs this package with --omit=dev, so jest may be absent npm test ``` Run tests in watch mode: ```bash npm test:watch ``` ### Relationship with the CLI `wmill lint` does not use the published package: it imports `src/index.ts` directly, and `cli`'s `preinstall` regenerates `src/gen` from the repo's OpenAPI specs. Lint therefore always validates against the schema of the commit it is built from, and local changes here are picked up with no publish or link step: ```bash # In cli/ bun run src/main.ts lint ``` The published package still exists for editor integrations and is republished on every release tag. ### Schema Generation The validator uses a JSON schema generated from the OpenAPI specification: ```bash npm run gen ``` 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)`.