322 lines
12 KiB
Markdown
322 lines
12 KiB
Markdown
# SCHEMA.md
|
||
|
||
# Conductor Project JSON Schema
|
||
|
||
This document describes the canonical JSON schema for Conductor project definitions and explains how to use it.
|
||
|
||
---
|
||
|
||
## Location
|
||
|
||
```
|
||
shared/schemas/conductor-project.schema.json
|
||
```
|
||
|
||
The schema lives in `shared/` so it is accessible to both the backend (Node.js/TypeScript) and any future frontend tooling without duplicating the file.
|
||
|
||
---
|
||
|
||
## Purpose
|
||
|
||
Every Conductor project is represented as a single structured JSON document — the *project definition*. This document is the authoritative source of truth for a project. All editors (Visual Editor, JSON Editor) read from and write to this document. The backend persists it as-is to SQLite.
|
||
|
||
The JSON schema:
|
||
|
||
- Documents the exact shape of a valid project definition.
|
||
- Enables offline validation during development.
|
||
- Drives IDE autocomplete and inline error highlighting when `$schema` is set in a project file.
|
||
- Will be used by the backend's validation endpoint (Step 11) to reject malformed saves.
|
||
- Makes project definitions portable, diffable in Git, and importable/exportable.
|
||
|
||
---
|
||
|
||
## Schema Version
|
||
|
||
The current schema version is **`0.1.0`** (semver).
|
||
|
||
Every project definition **must** include a `schemaVersion` field:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": "0.1.0",
|
||
"project": { ... }
|
||
}
|
||
```
|
||
|
||
Consumers (backend, editor, preview runtime) must check the `MAJOR` version component. A document with a higher major version than the consumer understands should be rejected with a clear error.
|
||
|
||
---
|
||
|
||
## Top-Level Structure
|
||
|
||
| Field | Type | Required | Description |
|
||
|-----------------|----------|----------|--------------------------------------------------|
|
||
| `schemaVersion` | `string` | ✅ | Schema version in `MAJOR.MINOR.PATCH` format. |
|
||
| `project` | `object` | ✅ | Root project object containing all definitions. |
|
||
|
||
---
|
||
|
||
## `project` Fields
|
||
|
||
| Field | Type | Required | Description |
|
||
|---------------|----------|----------|----------------------------------------------------------------------|
|
||
| `id` | `string` | ✅ | Stable unique identifier (UUID or URL-safe slug). Must not change. |
|
||
| `name` | `string` | ✅ | Human-readable display name (1–200 characters). |
|
||
| `description` | `string` | — | Optional free-text description. Defaults to `""`. |
|
||
| `pages` | `array` | ✅ | Ordered list of `Page` objects. May be empty. |
|
||
| `actions` | `array` | ✅ | Project-level REST action definitions. May be empty. |
|
||
| `bindings` | `array` | ✅ | Project-level binding definitions. May be empty. |
|
||
| `variables` | `object` | ✅ | Named global variable declarations. May be empty (`{}`). |
|
||
| `settings` | `object` | ✅ | Project display and canvas settings. May be empty (`{}`). |
|
||
|
||
---
|
||
|
||
## Key Sub-Types
|
||
|
||
### `Page`
|
||
|
||
Represents one view in the project. Required fields: `id`, `name`, `components`.
|
||
|
||
```json
|
||
{
|
||
"id": "page_home",
|
||
"name": "Home",
|
||
"description": "",
|
||
"order": 0,
|
||
"components": [],
|
||
"events": []
|
||
}
|
||
```
|
||
|
||
### `Component`
|
||
|
||
A UI element placed on a page canvas. Required fields: `id`, `type`, `name`, `position`, `size`.
|
||
|
||
Allowed `type` values:
|
||
`Button` · `TextInput` · `TextArea` · `Dropdown` · `Checkbox` · `RadioGroup` · `Label` · `Table` · `JsonViewer` · `StatusPanel` · `Container`
|
||
|
||
```json
|
||
{
|
||
"id": "cmp_launch_btn",
|
||
"type": "Button",
|
||
"name": "launchButton",
|
||
"position": { "x": 24, "y": 284 },
|
||
"size": { "width": 160, "height": 44 },
|
||
"properties": {
|
||
"label": "Launch Workflow",
|
||
"visible": true,
|
||
"disabled": false
|
||
},
|
||
"events": [
|
||
{
|
||
"event": "onClick",
|
||
"actionId": "action_trigger_workflow"
|
||
}
|
||
],
|
||
"bindings": []
|
||
}
|
||
```
|
||
|
||
`ComponentEvent.inputMap` is retained in schema version `0.1.0` for compatibility
|
||
but is not consumed by Preview. Request values are configured in REST action
|
||
templates using `{{components.<name>.value}}` or `{{variables.<name>}}`.
|
||
Visual request-input authoring must write those executed request fields.
|
||
|
||
### `Action` (REST Action)
|
||
|
||
A REST API call definition. Required fields: `id`, `name`, `method`, `url`, `authenticationType`.
|
||
|
||
Allowed `method` values: `GET` · `POST` · `PUT` · `PATCH` · `DELETE`
|
||
|
||
Allowed `authenticationType` values: `anonymous` · `bearerToken` · `basicAuth` · `apiKeyHeader` · `apiKeyQueryParameter`. Credential-backed actions use `secretReferenceId`, an opaque ID resolved only by the backend; anonymous actions omit it.
|
||
|
||
```json
|
||
{
|
||
"id": "action_trigger_workflow",
|
||
"name": "Trigger Workflow",
|
||
"description": "Calls the Concert RIA API to trigger a workflow run.",
|
||
"method": "POST",
|
||
"url": "https://api.example.com/workflows/{{workflowId}}/run",
|
||
"headers": {
|
||
"Content-Type": "application/json",
|
||
"Accept": "application/json"
|
||
},
|
||
"queryParameters": {
|
||
"environment": "{{environment}}"
|
||
},
|
||
"pathParameters": {
|
||
"workflowId": "{{workflowId}}"
|
||
},
|
||
"bodyTemplate": "{\"params\": {{params}}}",
|
||
"authenticationType": "bearerToken"
|
||
}
|
||
```
|
||
|
||
New response mappings belong in top-level `project.bindings`, not inside the action:
|
||
|
||
```json
|
||
{
|
||
"id": "bind_trigger_status",
|
||
"source": "actions.action_trigger_workflow.response.body.data.status",
|
||
"target": "variables.lastStatus",
|
||
"trigger": "onSuccess"
|
||
}
|
||
```
|
||
|
||
> **Security note:** Never store actual secret values (passwords, tokens, API keys) in the project definition. The `authenticationType` field declares the authentication strategy only; the backend resolves credentials from server-side secrets at execution time.
|
||
|
||
#### REST Action Fields
|
||
|
||
| Field | Type | Required | Description |
|
||
|----------------------|----------|----------|-----------------------------------------------------------------------------|
|
||
| `id` | `string` | ✅ | Unique identifier within the project. |
|
||
| `name` | `string` | ✅ | Human-readable action name shown in the Actions panel. |
|
||
| `description` | `string` | — | Optional description of what this action does. |
|
||
| `method` | `string` | ✅ | HTTP method: `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. |
|
||
| `url` | `string` | ✅ | Target URL. Use `{{paramName}}` for path parameter placeholders. |
|
||
| `headers` | `object` | — | Static request headers. Values may use `{{variableName}}` syntax. |
|
||
| `queryParameters` | `object` | — | URL query string parameters. Values may use `{{variableName}}` syntax. |
|
||
| `pathParameters` | `object` | — | Path segment substitutions. Keys match `{{paramName}}` in the URL. |
|
||
| `bodyTemplate` | `string` | — | Request body template. Use `{{variableName}}` for runtime substitutions. |
|
||
| `authenticationType` | `string` | ✅ | Authentication strategy (see allowed values above). Credentials are never stored here. |
|
||
| `responseMapping` | `array` | — | Deprecated legacy action-local mappings. New mappings use top-level `project.bindings`. |
|
||
|
||
REST actions do not have a configurable timeout field in schema version
|
||
`0.1.0`. The backend applies a fixed 30-second execution timeout to every
|
||
proxied request. A configurable timeout is deferred until the proxy-policy work
|
||
can define safe minimum and maximum limits consistently.
|
||
|
||
### `Binding`
|
||
|
||
Declares data flow between a source and a target. Required fields: `id`, `source`, `target`.
|
||
|
||
```json
|
||
{
|
||
"id": "bind_status_panel",
|
||
"source": "variables.lastRunStatus",
|
||
"target": "components.statusPanel.message",
|
||
"trigger": "onChange"
|
||
}
|
||
```
|
||
|
||
### `Variable`
|
||
|
||
A named global variable. Required field: `type`.
|
||
|
||
Allowed `type` values: `string` · `number` · `boolean` · `object` · `array`
|
||
|
||
```json
|
||
{
|
||
"lastRunStatus": {
|
||
"type": "string",
|
||
"defaultValue": "",
|
||
"description": "Status returned by the most recent workflow trigger."
|
||
}
|
||
}
|
||
```
|
||
|
||
### `ProjectSettings`
|
||
|
||
Optional canvas and display configuration.
|
||
|
||
```json
|
||
{
|
||
"theme": "system",
|
||
"defaultPageId": "page_home",
|
||
"canvasWidth": 1280,
|
||
"canvasHeight": 900
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Example Files
|
||
|
||
| File | Description |
|
||
|-----------------------------------------------------------------------|-----------------------------------------------------------|
|
||
| `examples/project-definitions/valid-minimal.json` | Smallest valid project definition (one empty page). |
|
||
| `examples/project-definitions/valid-full.json` | Full example: Concert Workflow Launcher with all features. |
|
||
| `examples/project-definitions/valid-rest-actions.json` | Showcases all five authentication types across seven REST actions. No canvas components — validates the action model in isolation. |
|
||
| `examples/project-definitions/valid-visual-rest-actions.json` | Slice 2 visual-authoring fixture covering anonymous GET/POST request fields. |
|
||
| `examples/project-definitions/invalid-missing-required.json` | Intentionally invalid document showing schema errors. |
|
||
|
||
---
|
||
|
||
## Validation
|
||
|
||
### Using `ajv-cli` (recommended)
|
||
|
||
Install once globally or run via `npx`:
|
||
|
||
```bash
|
||
# Install globally
|
||
npm install -g ajv-cli ajv-formats
|
||
|
||
# Validate a valid document — should print: valid
|
||
ajv validate \
|
||
-s shared/schemas/conductor-project.schema.json \
|
||
-d examples/project-definitions/valid-minimal.json \
|
||
--spec=draft2020
|
||
|
||
# Validate the full example
|
||
ajv validate \
|
||
-s shared/schemas/conductor-project.schema.json \
|
||
-d examples/project-definitions/valid-full.json \
|
||
--spec=draft2020
|
||
|
||
# Validate the REST actions showcase (all five auth types)
|
||
ajv validate \
|
||
-s shared/schemas/conductor-project.schema.json \
|
||
-d examples/project-definitions/valid-rest-actions.json \
|
||
--spec=draft2020
|
||
|
||
# Validate the intentionally invalid document — should print validation errors
|
||
ajv validate \
|
||
-s shared/schemas/conductor-project.schema.json \
|
||
-d examples/project-definitions/invalid-missing-required.json \
|
||
--spec=draft2020
|
||
```
|
||
|
||
### Using VS Code
|
||
|
||
1. Open any example `.json` file.
|
||
2. The `"$schema"` field at the top of the file points to the schema.
|
||
3. VS Code will underline validation errors inline and provide autocomplete.
|
||
|
||
### In the Backend (future — Step 11)
|
||
|
||
The backend will use [`ajv`](https://ajv.js.org/) at runtime to validate project definitions on save:
|
||
|
||
```ts
|
||
import Ajv from 'ajv';
|
||
import schema from '../../shared/schemas/conductor-project.schema.json';
|
||
|
||
const ajv = new Ajv({ strict: true });
|
||
const validate = ajv.compile(schema);
|
||
|
||
function validateProjectJson(doc: unknown): string[] {
|
||
const valid = validate(doc);
|
||
if (valid) return [];
|
||
return (validate.errors ?? []).map(e => `${e.instancePath} ${e.message}`);
|
||
}
|
||
```
|
||
|
||
> This is documented here for reference. The validation endpoint itself is not part of Step 7.
|
||
|
||
---
|
||
|
||
## Versioning Policy
|
||
|
||
| Change type | Version bump |
|
||
|----------------------------------------------------|--------------|
|
||
| Add optional field | MINOR |
|
||
| Add required field or remove existing field | MAJOR |
|
||
| Change allowed enum values | MAJOR |
|
||
| Clarify description with no structural change | PATCH |
|
||
|
||
---
|
||
|
||
## Schema Stability
|
||
|
||
The schema is currently at **`0.x`** (pre-stable). Breaking changes may occur between minor versions until `1.0.0` is declared.
|