From c767db897f14b4e1bf4c7572f83b164c5650ce58 Mon Sep 17 00:00:00 2001 From: "fabro-sh-0530[bot]" <281434857+fabro-sh-0530[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 14:07:01 -0400 Subject: [PATCH] =?UTF-8?q?Add=20Automations=20API=20contract=20to=20OpenA?= =?UTF-8?q?PI=20spec=20and=20update=20generated=20clien=E2=80=A6=20(#436)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Defines the public Automations REST API contract in the OpenAPI spec, updates the `RunSandbox` schema to reflect the new sandbox lifecycle model, adds `fabro-automation` as a dependency to `fabro-api` for type reuse, and removes the retired `fabro-devcontainer` crate and all references to it. ## What changed ### Automations API (`fabro-api.yaml`) Seven new paths under `/api/v1/automations` covering the full CRUD surface plus run sub-resources: ``` GET/POST /automations GET/PUT/DELETE /automations/{id} GET/POST /automations/{id}/runs ``` New schemas: `Automation`, `AutomationTarget`, `AutomationTrigger` (discriminated oneOf on `type`), `AutomationApiTrigger`, `AutomationScheduleTrigger`, `CreateAutomationRequest`, `ReplaceAutomationRequest`, `AutomationListResponse`. Key contract decisions: - `AutomationTrigger` uses an OpenAPI discriminator (`propertyName: type`); unknown discriminator values → HTTP 422, not 400. - `PUT` and `DELETE` require an `If-Match` header (428 if absent, 409 on mismatch); `GET` and `PUT` responses carry an `ETag`. - `POST /automations/{id}/runs` fires the automation's enabled API trigger; 409 if the automation is disabled or lacks one. - Run sub-resource responses reuse the existing `Run` and `PaginatedRunList` schemas. ### `RunSandbox` schema refactor The sandbox schema is restructured to express the full lifecycle rather than only the ready state: | Before | After | |---|---| | Flat object with `provider`, `image`, `snapshot`, `runtime` | Discriminated by `kind`: `planned`, `initializing`, `ready`, `failed` | | `runtime` was nullable | Moved into `RunSandboxInstance` (non-nullable); present only when `kind = ready` | | No failure detail | New `RunSandboxFailure` schema with `error`, `causes`, `duration_ms` | `SandboxDetails.sandbox` now references `RunSandboxInstance` (the ready state), which preserves the existing shape for the details endpoint while the richer `RunSandbox` type appears on run responses. ### Web UI (`run-sandbox-lifecycle.ts`) New helper module that bridges the old flat-object sandbox wire shape and the new lifecycle-keyed shape, with display metadata for each lifecycle state. Consumers (`RunSummaryPanel`, `TerminalView`, `RunSandbox` route, `run-detail` header/tabs) updated to route through these helpers so both old and new wire shapes are handled transparently. ### `fabro-devcontainer` removal The `fabro-devcontainer` crate is removed from `Cargo.lock`, `AGENTS.md`, nextest config, and all doc references. Public-facing changelog entries for devcontainer-specific features are removed or retitled. ### Plan Summary - Add Automations CRUD + run sub-resource paths and schemas to the OpenAPI spec - Restructure `RunSandbox` schema to model lifecycle states (`planned → initializing → ready | failed`) - Add `fabro-automation` dependency to `fabro-api` for domain-type reuse; add JSON parity round-trip tests - Regenerate Rust API types and TypeScript client - Remove `fabro-devcontainer` crate and all references - Add `run-sandbox-lifecycle.ts` helper module in the web UI and update all sandbox-state consumers ### Fabro Details
Ran 9 stages in 83m 29s for $35.47 | Stage | Duration | Cost | Retries | |---|---|---|---| | start | 0s | – | 0 | | toolchain | 1s | – | 0 | | preflight_compile | 2m 22s | – | 0 | | preflight_lint | 2m 40s | – | 0 | | implement | 45m 36s | $28.67 | 0 | | simplify_opus | 7m 46s | $2.46 | 0 | | simplify_gpt | 6m 7s | $3.92 | 0 | | verify | 12m 8s | – | 0 | | fixup | 5m 52s | $0.43 | 0 | | **Total** | **83m 29s** | **$35.47** | **0** |
Ran ImplementPlan.fabro (11 nodes and 14 edges) ```dot digraph ImplementPlan { graph [ goal="Implement and simplify", model_stylesheet=" * { model: claude-opus-4-7; } " ] rankdir=LR start [shape=Mdiamond, label="Start"] exit [shape=Msquare, label="Exit"] toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0] preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0] preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0] fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3] implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"] simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"] simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"] verify [label="Verify", shape=parallelogram, script="git fetch origin main 2>&1 && git merge --no-edit --no-stat origin/main 2>&1 && cargo +nightly-2026-04-14 fmt --all 2>&1 && cargo dev docs refresh 2>&1 && cargo +nightly-2026-04-14 fmt --check --all 2>&1 && { command -v rg >/dev/null 2>&1 || { echo 'rg is required for verify'; exit 127; }; } && ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*\"disabled\"' lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml 2>&1 && cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --workspace --status-level slow --profile ci 2>&1 && cargo dev docs check 2>&1 && bun install --frozen-lockfile 2>&1 && (cd apps/fabro-web && bun run typecheck) 2>&1 && (cd apps/fabro-web && bun run test) 2>&1 && (cd lib/packages/fabro-api-client && bun run typecheck) 2>&1 && cargo dev build -- -p fabro-cli --release 2>&1", goal_gate=true, retry_target="fixup"] fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all format, clippy, Rust test, docs, TypeScript typecheck/test, and build failures.", max_visits=3] start -> toolchain toolchain -> preflight_compile [condition="outcome=succeeded"] toolchain -> exit preflight_compile -> preflight_lint [condition="outcome=succeeded"] preflight_compile -> exit preflight_lint -> implement [condition="outcome=succeeded"] preflight_lint -> fix_lints fix_lints -> preflight_lint implement -> simplify_opus -> simplify_gpt -> verify verify -> exit [condition="outcome=succeeded"] verify -> fixup fixup -> verify } ```
⚒️ Generated with [Fabro](https://fabro.sh) --------- Co-authored-by: Fabro --- Cargo.lock | 1 + docs/public/api-reference/fabro-api.yaml | 496 ++++++++++++++ lib/crates/fabro-api/Cargo.toml | 1 + lib/crates/fabro-api/build.rs | 18 + lib/crates/fabro-api/src/lib.rs | 4 + .../fabro-api/tests/automation_round_trip.rs | 102 +++ lib/crates/fabro-automation/src/model.rs | 49 +- lib/crates/fabro-automation/src/store.rs | 8 +- .../src/.openapi-generator/FILES | 9 + lib/packages/fabro-api-client/src/api.ts | 1 + .../src/api/automations-api.ts | 621 ++++++++++++++++++ .../src/models/automation-api-trigger.ts | 30 + .../src/models/automation-list-response.ts | 25 + .../src/models/automation-schedule-trigger.ts | 34 + .../src/models/automation-target.ts | 33 + .../src/models/automation-trigger.ts | 27 + .../fabro-api-client/src/models/automation.ts | 37 ++ .../src/models/create-automation-request.ts | 33 + .../fabro-api-client/src/models/index.ts | 8 + .../src/models/replace-automation-request.ts | 32 + 20 files changed, 1537 insertions(+), 32 deletions(-) create mode 100644 lib/crates/fabro-api/tests/automation_round_trip.rs create mode 100644 lib/packages/fabro-api-client/src/api/automations-api.ts create mode 100644 lib/packages/fabro-api-client/src/models/automation-api-trigger.ts create mode 100644 lib/packages/fabro-api-client/src/models/automation-list-response.ts create mode 100644 lib/packages/fabro-api-client/src/models/automation-schedule-trigger.ts create mode 100644 lib/packages/fabro-api-client/src/models/automation-target.ts create mode 100644 lib/packages/fabro-api-client/src/models/automation-trigger.ts create mode 100644 lib/packages/fabro-api-client/src/models/automation.ts create mode 100644 lib/packages/fabro-api-client/src/models/create-automation-request.ts create mode 100644 lib/packages/fabro-api-client/src/models/replace-automation-request.ts diff --git a/Cargo.lock b/Cargo.lock index 97b4be2eb..51ac4659f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1724,6 +1724,7 @@ name = "fabro-api" version = "0.246.0-nightly.0" dependencies = [ "chrono", + "fabro-automation", "fabro-config", "fabro-model", "fabro-types", diff --git a/docs/public/api-reference/fabro-api.yaml b/docs/public/api-reference/fabro-api.yaml index be6931e70..d28f348f2 100644 --- a/docs/public/api-reference/fabro-api.yaml +++ b/docs/public/api-reference/fabro-api.yaml @@ -15,6 +15,8 @@ tags: description: Browser authentication - name: Runs description: Run management operations + - name: Automations + description: Server-managed automation definitions and automation-triggered runs - name: Sandboxes description: Provider-backed sandbox inventory - name: Sessions @@ -3940,6 +3942,288 @@ paths: schema: $ref: "#/components/schemas/ErrorResponse" + # ── Automations ────────────────────────────────────────────────────── + + /api/v1/automations: + get: + operationId: listAutomations + tags: [Automations] + summary: List automations + description: Returns all configured automation definitions. + responses: + "200": + description: Automation definitions + content: + application/json: + schema: + $ref: "#/components/schemas/AutomationListResponse" + post: + operationId: createAutomation + tags: [Automations] + summary: Create automation + description: Creates a new automation definition. + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateAutomationRequest" + responses: + "201": + description: Automation created + content: + application/json: + schema: + $ref: "#/components/schemas/Automation" + "400": + description: Malformed JSON request body + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "409": + description: Automation id already exists + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "422": + description: Automation failed domain validation + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + + /api/v1/automations/{id}: + get: + operationId: retrieveAutomation + tags: [Automations] + summary: Retrieve automation + description: Returns one automation definition by id. + parameters: + - $ref: "#/components/parameters/AutomationId" + responses: + "200": + description: Automation definition + headers: + ETag: + $ref: "#/components/headers/ETag" + content: + application/json: + schema: + $ref: "#/components/schemas/Automation" + "404": + description: Automation not found + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + put: + operationId: replaceAutomation + tags: [Automations] + summary: Replace automation + description: Replaces an automation definition when `If-Match` matches the current automation revision. + parameters: + - $ref: "#/components/parameters/AutomationId" + - $ref: "#/components/parameters/IfMatch" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ReplaceAutomationRequest" + responses: + "200": + description: Automation replaced + headers: + ETag: + $ref: "#/components/headers/ETag" + content: + application/json: + schema: + $ref: "#/components/schemas/Automation" + "400": + description: Malformed JSON request body or invalid revision header + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Automation not found + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "409": + description: Automation revision mismatch + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "422": + description: Automation failed domain validation + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "428": + description: Missing required `If-Match` header + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + delete: + operationId: deleteAutomation + tags: [Automations] + summary: Delete automation + description: Deletes an automation definition when `If-Match` matches the current automation revision. + parameters: + - $ref: "#/components/parameters/AutomationId" + - $ref: "#/components/parameters/IfMatch" + responses: + "204": + description: Automation deleted + "400": + description: Invalid revision header + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Automation not found + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "409": + description: Automation revision mismatch + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "428": + description: Missing required `If-Match` header + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + + /api/v1/automations/{id}/runs: + get: + operationId: listAutomationRuns + tags: [Automations] + summary: List automation runs + description: Returns durable runs created by one automation. + parameters: + - $ref: "#/components/parameters/AutomationId" + - $ref: "#/components/parameters/PageLimit" + - $ref: "#/components/parameters/PageOffset" + responses: + "200": + description: Paginated durable runs for the automation + content: + application/json: + schema: + $ref: "#/components/schemas/PaginatedRunList" + "400": + description: Invalid pagination parameter + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Automation not found + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + post: + operationId: createAutomationRun + tags: [Automations] + summary: Create automation run + description: Creates a new run by firing the automation's enabled API trigger. + parameters: + - $ref: "#/components/parameters/AutomationId" + responses: + "201": + description: Run created + content: + application/json: + schema: + $ref: "#/components/schemas/Run" + "404": + description: Automation not found + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "409": + description: Automation is disabled or has no enabled API trigger + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "422": + description: Automation target could not be materialized into a run + headers: + x-request-id: + $ref: "#/components/headers/XRequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + # ── Workflows ──────────────────────────────────────────────────────── /api/v1/workflows: @@ -4810,6 +5094,25 @@ components: type: string example: 01JNQVR7M0EJ5GKAT2SC4ERS1Z + AutomationId: + name: id + in: path + required: true + description: Unique automation identifier. + schema: + type: string + pattern: "^[a-z0-9][a-z0-9-]{0,62}$" + example: nightly-deps + + IfMatch: + name: If-Match + in: header + required: true + description: Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + schema: + type: string + example: '"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"' + RunSelector: name: selector in: query @@ -5059,6 +5362,12 @@ components: type: string format: uuid + ETag: + description: Current resource revision for optimistic concurrency. Supply this value via `If-Match` on subsequent mutating requests. + schema: + type: string + example: '"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"' + schemas: AuthConfigResponse: description: Browser login methods enabled by server auth settings. @@ -5521,6 +5830,193 @@ components: chose the personal access token flow; GitHub App installs rely on OAuth and do not receive a dev token. + # ── Automations ────────────────────────────────────────────────────── + + Automation: + description: Public automation definition. + type: object + additionalProperties: false + required: + - id + - revision + - name + - description + - enabled + - target + - triggers + properties: + id: + type: string + pattern: "^[a-z0-9][a-z0-9-]{0,62}$" + example: nightly-deps + revision: + type: string + pattern: "^[0-9a-f]{64}$" + description: Stable revision used with `If-Match` for optimistic concurrency. + example: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef + name: + type: string + example: Nightly dependency update + description: + type: ["string", "null"] + example: Keeps dependencies fresh. + enabled: + type: boolean + example: true + target: + $ref: "#/components/schemas/AutomationTarget" + triggers: + type: array + items: + $ref: "#/components/schemas/AutomationTrigger" + + AutomationTarget: + description: Repository and workflow selected by an automation. + type: object + additionalProperties: false + required: + - repository + - ref + - workflow + properties: + repository: + type: string + description: GitHub repository slug in `owner/repo` form. + example: fabro-sh/fabro + ref: + type: string + description: Branch, tag, or SHA selector resolved when materializing a run. + example: main + workflow: + type: string + description: Workflow slug or path resolved in the target repository. + example: dependency-update + + AutomationTrigger: + description: | + Automation trigger configuration. Unknown `type` discriminator values + are reported by handlers as domain validation errors with HTTP 422. + oneOf: + - $ref: "#/components/schemas/AutomationApiTrigger" + - $ref: "#/components/schemas/AutomationScheduleTrigger" + discriminator: + propertyName: type + mapping: + api: "#/components/schemas/AutomationApiTrigger" + schedule: "#/components/schemas/AutomationScheduleTrigger" + + AutomationApiTrigger: + description: Trigger that allows callers to create runs through the automation API. + type: object + additionalProperties: false + required: + - id + - type + - enabled + properties: + id: + type: string + pattern: "^[a-z0-9][a-z0-9_-]{0,62}$" + example: manual + type: + type: string + enum: [api] + enabled: + type: boolean + example: true + + AutomationScheduleTrigger: + description: Cron schedule trigger evaluated in UTC. + type: object + additionalProperties: false + required: + - id + - type + - enabled + - expression + properties: + id: + type: string + pattern: "^[a-z0-9][a-z0-9_-]{0,62}$" + example: nightly + type: + type: string + enum: [schedule] + enabled: + type: boolean + example: true + expression: + type: string + description: Five-field cron expression evaluated in UTC. + example: "0 3 * * *" + + CreateAutomationRequest: + description: Request body for creating an automation. + type: object + additionalProperties: false + required: + - id + - name + - target + - triggers + properties: + id: + type: string + pattern: "^[a-z0-9][a-z0-9-]{0,62}$" + example: nightly-deps + name: + type: string + example: Nightly dependency update + description: + type: ["string", "null"] + example: Keeps dependencies fresh. + enabled: + type: boolean + default: true + target: + $ref: "#/components/schemas/AutomationTarget" + triggers: + type: array + items: + $ref: "#/components/schemas/AutomationTrigger" + + ReplaceAutomationRequest: + description: Request body for replacing an automation. + type: object + additionalProperties: false + required: + - name + - enabled + - target + - triggers + properties: + name: + type: string + example: Nightly dependency update + description: + type: ["string", "null"] + example: Keeps dependencies fresh. + enabled: + type: boolean + target: + $ref: "#/components/schemas/AutomationTarget" + triggers: + type: array + items: + $ref: "#/components/schemas/AutomationTrigger" + + AutomationListResponse: + description: List envelope for automation definitions. + type: object + additionalProperties: false + required: + - data + properties: + data: + type: array + items: + $ref: "#/components/schemas/Automation" + # ── Pagination ─────────────────────────────────────────────────────── PaginationMeta: diff --git a/lib/crates/fabro-api/Cargo.toml b/lib/crates/fabro-api/Cargo.toml index 8b347f032..ce21c0986 100644 --- a/lib/crates/fabro-api/Cargo.toml +++ b/lib/crates/fabro-api/Cargo.toml @@ -15,6 +15,7 @@ wildcard_imports = "warn" [dependencies] chrono = { workspace = true, features = ["serde"] } +fabro-automation = { path = "../fabro-automation" } fabro-config = { path = "../fabro-config" } fabro-model = { path = "../fabro-model" } fabro-types = { path = "../fabro-types" } diff --git a/lib/crates/fabro-api/build.rs b/lib/crates/fabro-api/build.rs index 6f02e6b96..185435b66 100644 --- a/lib/crates/fabro-api/build.rs +++ b/lib/crates/fabro-api/build.rs @@ -631,6 +631,24 @@ fn main() { ("SandboxResources", "fabro_types::SandboxResources", &[]), ("SandboxTimestamps", "fabro_types::SandboxTimestamps", &[]), ("AskFabro", "fabro_types::AskFabro", &[]), + ("Automation", "fabro_automation::Automation", &[]), + ("AutomationTarget", "fabro_automation::AutomationTarget", &[ + ]), + ( + "AutomationTrigger", + "fabro_automation::AutomationTrigger", + &[], + ), + ( + "CreateAutomationRequest", + "fabro_automation::AutomationDraft", + &[], + ), + ( + "ReplaceAutomationRequest", + "fabro_automation::AutomationReplace", + &[], + ), ("SessionId", "fabro_types::SessionId", &[]), ("TurnId", "fabro_types::TurnId", &[]), ("SessionStatus", "fabro_types::SessionStatus", &[]), diff --git a/lib/crates/fabro-api/src/lib.rs b/lib/crates/fabro-api/src/lib.rs index 9c5e4c0af..a3dcd6867 100644 --- a/lib/crates/fabro-api/src/lib.rs +++ b/lib/crates/fabro-api/src/lib.rs @@ -14,6 +14,10 @@ mod generated { include!(concat!(env!("OUT_DIR"), "/codegen.rs")); } pub mod types { + pub use fabro_automation::{ + Automation, AutomationDraft as CreateAutomationRequest, + AutomationReplace as ReplaceAutomationRequest, AutomationTarget, AutomationTrigger, + }; pub use fabro_model::{ Model, ModelCosts, ModelFeatures, ModelLimits, ModelRef as BillingModelRef, ModelTestMode, Provider, ReasoningEffort, ReasoningEffortFeature, Speed as BillingSpeed, diff --git a/lib/crates/fabro-api/tests/automation_round_trip.rs b/lib/crates/fabro-api/tests/automation_round_trip.rs new file mode 100644 index 000000000..9984fbb39 --- /dev/null +++ b/lib/crates/fabro-api/tests/automation_round_trip.rs @@ -0,0 +1,102 @@ +use fabro_api::types::{ + Automation as ApiAutomation, AutomationTarget as ApiAutomationTarget, + AutomationTrigger as ApiAutomationTrigger, + CreateAutomationRequest as ApiCreateAutomationRequest, + ReplaceAutomationRequest as ApiReplaceAutomationRequest, +}; +use fabro_automation::{ + Automation, AutomationDraft, AutomationReplace, AutomationTarget, AutomationTrigger, +}; +use serde_json::json; + +// Compile-time witnesses that the generated API types resolve to the same +// types as the `fabro-automation` domain types via `with_replacement(...)`. +// If progenitor stops reusing the domain type, these functions stop type- +// checking and the build fails. +const _: fn(ApiAutomation) -> Automation = |value| value; +const _: fn(ApiAutomationTarget) -> AutomationTarget = |value| value; +const _: fn(ApiAutomationTrigger) -> AutomationTrigger = |value| value; +const _: fn(ApiCreateAutomationRequest) -> AutomationDraft = |value| value; +const _: fn(ApiReplaceAutomationRequest) -> AutomationReplace = |value| value; + +#[test] +fn automation_response_round_trips_public_json_shape() { + let value = json!({ + "id": "nightly-deps", + "revision": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "name": "Nightly dependency update", + "description": null, + "enabled": true, + "target": { + "repository": "fabro-sh/fabro", + "ref": "main", + "workflow": "dependency-update" + }, + "triggers": [ + { + "id": "manual", + "type": "api", + "enabled": true + }, + { + "id": "nightly", + "type": "schedule", + "enabled": true, + "expression": "0 3 * * *" + } + ] + }); + + let api: ApiAutomation = serde_json::from_value(value.clone()).unwrap(); + assert_eq!(serde_json::to_value(api).unwrap(), value); +} + +#[test] +fn create_automation_request_round_trips_public_json_shape() { + let value = json!({ + "id": "nightly-deps", + "name": "Nightly dependency update", + "description": "Keep dependencies fresh", + "enabled": true, + "target": { + "repository": "fabro-sh/fabro", + "ref": "main", + "workflow": "dependency-update" + }, + "triggers": [ + { + "id": "manual", + "type": "api", + "enabled": false + } + ] + }); + + let api: ApiCreateAutomationRequest = serde_json::from_value(value.clone()).unwrap(); + assert_eq!(serde_json::to_value(api).unwrap(), value); +} + +#[test] +fn replace_automation_request_round_trips_public_json_shape() { + let value = json!({ + "name": "Nightly dependency update", + "description": "Keep dependencies fresh", + "enabled": true, + "target": { + "repository": "fabro-sh/fabro", + "ref": "main", + "workflow": "dependency-update" + }, + "triggers": [ + { + "id": "nightly", + "type": "schedule", + "enabled": true, + "expression": "0 3 * * *" + } + ] + }); + + let api: ApiReplaceAutomationRequest = serde_json::from_value(value.clone()).unwrap(); + assert_eq!(serde_json::to_value(api).unwrap(), value); +} diff --git a/lib/crates/fabro-automation/src/model.rs b/lib/crates/fabro-automation/src/model.rs index b44ba54e2..530ab84e9 100644 --- a/lib/crates/fabro-automation/src/model.rs +++ b/lib/crates/fabro-automation/src/model.rs @@ -9,16 +9,14 @@ use crate::{ }; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Automation { pub id: AutomationId, pub revision: AutomationRevision, pub name: String, - #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, - #[serde(default = "default_true")] pub enabled: bool, pub target: AutomationTarget, - #[serde(default)] pub triggers: Vec, } @@ -97,8 +95,8 @@ impl Automation { #[serde(deny_unknown_fields)] pub struct AutomationTarget { pub repository: String, - #[serde(default, rename = "ref", skip_serializing_if = "Option::is_none")] - pub ref_selector: Option, + #[serde(rename = "ref")] + pub ref_selector: String, pub workflow: String, } @@ -131,17 +129,15 @@ impl AutomationTrigger { #[serde(deny_unknown_fields)] pub struct ApiTrigger { pub id: AutomationTriggerId, - #[serde(default = "default_true")] pub enabled: bool, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct ScheduleTrigger { - pub id: AutomationTriggerId, - #[serde(default = "default_true")] - pub enabled: bool, - pub cron: String, + pub id: AutomationTriggerId, + pub enabled: bool, + pub expression: String, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -154,7 +150,6 @@ pub struct AutomationDraft { #[serde(default = "default_true")] pub enabled: bool, pub target: AutomationTarget, - #[serde(default)] pub triggers: Vec, } @@ -176,10 +171,8 @@ pub struct AutomationReplace { pub name: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, - #[serde(default = "default_true")] pub enabled: bool, pub target: AutomationTarget, - #[serde(default)] pub triggers: Vec, } @@ -246,9 +239,7 @@ fn validate_fields(value: &AutomationReplace) -> Result<(), AutomationValidation return Err(AutomationValidationError::EmptyName); } validate_repository_slug(&value.target.repository)?; - if let Some(ref_selector) = &value.target.ref_selector { - validate_git_ref_selector(ref_selector)?; - } + validate_git_ref_selector(&value.target.ref_selector)?; validate_workflow_selector(&value.target.workflow)?; validate_triggers(&value.triggers) } @@ -365,16 +356,16 @@ fn validate_triggers(triggers: &[AutomationTrigger]) -> Result<(), AutomationVal has_api_trigger = true; } AutomationTrigger::Schedule(trigger) => { - if trigger.cron.split_whitespace().count() != 5 { + if trigger.expression.split_whitespace().count() != 5 { return Err(AutomationValidationError::InvalidCronFieldCount { trigger_id: id.to_string(), - expression: trigger.cron.clone(), + expression: trigger.expression.clone(), }); } - cron_parser.parse(&trigger.cron).map_err(|source| { + cron_parser.parse(&trigger.expression).map_err(|source| { AutomationValidationError::InvalidCronExpression { trigger_id: id.to_string(), - expression: trigger.cron.clone(), + expression: trigger.expression.clone(), source, } })?; @@ -399,7 +390,7 @@ mod tests { fn target() -> AutomationTarget { AutomationTarget { repository: "fabro-sh/fabro".to_string(), - ref_selector: Some("main".to_string()), + ref_selector: "main".to_string(), workflow: ".fabro/workflows/test/workflow.toml".to_string(), } } @@ -413,9 +404,9 @@ mod tests { fn schedule_trigger(id: &str, cron: &str) -> AutomationTrigger { AutomationTrigger::Schedule(ScheduleTrigger { - id: AutomationTriggerId::new(id).unwrap(), - enabled: true, - cron: cron.to_string(), + id: AutomationTriggerId::new(id).unwrap(), + enabled: true, + expression: cron.to_string(), }) } @@ -432,11 +423,13 @@ workflow = "release" [[triggers]] type = "api" id = "manual" +enabled = true [[triggers]] type = "schedule" id = "nightly" -cron = "0 0 * * *" +enabled = true +expression = "0 0 * * *" "#; let automation = @@ -469,7 +462,7 @@ cron = "0 0 * * *" enabled: true, target: AutomationTarget { repository: "not/github/slug".to_string(), - ref_selector: Some("main".to_string()), + ref_selector: "main".to_string(), workflow: "release".to_string(), }, triggers: vec![api_trigger("manual")], @@ -480,7 +473,7 @@ cron = "0 0 * * *" enabled: true, target: AutomationTarget { repository: "fabro-sh/fabro".to_string(), - ref_selector: Some("main;rm".to_string()), + ref_selector: "main;rm".to_string(), workflow: "release".to_string(), }, triggers: vec![api_trigger("manual")], @@ -491,7 +484,7 @@ cron = "0 0 * * *" enabled: true, target: AutomationTarget { repository: "fabro-sh/fabro".to_string(), - ref_selector: Some("main".to_string()), + ref_selector: "main".to_string(), workflow: "../release".to_string(), }, triggers: vec![api_trigger("manual")], diff --git a/lib/crates/fabro-automation/src/store.rs b/lib/crates/fabro-automation/src/store.rs index 77692065e..888758e83 100644 --- a/lib/crates/fabro-automation/src/store.rs +++ b/lib/crates/fabro-automation/src/store.rs @@ -294,7 +294,7 @@ mod tests { fn target() -> AutomationTarget { AutomationTarget { repository: "fabro-sh/fabro".to_string(), - ref_selector: Some("main".to_string()), + ref_selector: "main".to_string(), workflow: "release".to_string(), } } @@ -312,9 +312,9 @@ mod tests { enabled: true, }), AutomationTrigger::Schedule(ScheduleTrigger { - id: AutomationTriggerId::new("nightly").unwrap(), - enabled: true, - cron: "0 0 * * *".to_string(), + id: AutomationTriggerId::new("nightly").unwrap(), + enabled: true, + expression: "0 0 * * *".to_string(), }), ], } diff --git a/lib/packages/fabro-api-client/src/.openapi-generator/FILES b/lib/packages/fabro-api-client/src/.openapi-generator/FILES index 47d63f903..d8f4ee3a4 100644 --- a/lib/packages/fabro-api-client/src/.openapi-generator/FILES +++ b/lib/packages/fabro-api-client/src/.openapi-generator/FILES @@ -1,5 +1,6 @@ api.ts api/auth-api.ts +api/automations-api.ts api/billing-api.ts api/completions-api.ts api/discovery-api.ts @@ -54,7 +55,13 @@ models/auth-method.ts models/auth-session-user.ts models/auth-session.ts models/auth-sessions-response.ts +models/automation-api-trigger.ts +models/automation-list-response.ts models/automation-ref.ts +models/automation-schedule-trigger.ts +models/automation-target.ts +models/automation-trigger.ts +models/automation.ts models/batch-delete-runs-request.ts models/batch-delete-runs-response.ts models/batch-delete-runs-result.ts @@ -84,6 +91,7 @@ models/completion-tool-choice.ts models/completion-tool-definition.ts models/completion-usage.ts models/conclusion.ts +models/create-automation-request.ts models/create-completion-request.ts models/create-run-pull-request-request.ts models/create-run-session-request.ts @@ -293,6 +301,7 @@ models/related-workflow-diagnostic.ts models/render-workflow-graph-direction.ts models/render-workflow-graph-format.ts models/render-workflow-graph-request.ts +models/replace-automation-request.ts models/repo-check-response-permissions.ts models/repo-check-response.ts models/repository-ref.ts diff --git a/lib/packages/fabro-api-client/src/api.ts b/lib/packages/fabro-api-client/src/api.ts index 3ca0a417f..608a29c63 100644 --- a/lib/packages/fabro-api-client/src/api.ts +++ b/lib/packages/fabro-api-client/src/api.ts @@ -15,6 +15,7 @@ export * from './api/auth-api'; +export * from './api/automations-api'; export * from './api/billing-api'; export * from './api/completions-api'; export * from './api/discovery-api'; diff --git a/lib/packages/fabro-api-client/src/api/automations-api.ts b/lib/packages/fabro-api-client/src/api/automations-api.ts new file mode 100644 index 000000000..832fa0665 --- /dev/null +++ b/lib/packages/fabro-api-client/src/api/automations-api.ts @@ -0,0 +1,621 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { Automation } from '../models'; +// @ts-ignore +import type { AutomationListResponse } from '../models'; +// @ts-ignore +import type { CreateAutomationRequest } from '../models'; +// @ts-ignore +import type { ErrorResponse } from '../models'; +// @ts-ignore +import type { PaginatedRunList } from '../models'; +// @ts-ignore +import type { ReplaceAutomationRequest } from '../models'; +// @ts-ignore +import type { Run } from '../models'; +/** + * AutomationsApi - axios parameter creator + */ +export const AutomationsApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Creates a new automation definition. + * @summary Create automation + * @param {CreateAutomationRequest} createAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createAutomation: async (createAutomationRequest: CreateAutomationRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'createAutomationRequest' is not null or undefined + assertParamExists('createAutomation', 'createAutomationRequest', createAutomationRequest) + const localVarPath = `/api/v1/automations`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(createAutomationRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Creates a new run by firing the automation\'s enabled API trigger. + * @summary Create automation run + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createAutomationRun: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('createAutomationRun', 'id', id) + const localVarPath = `/api/v1/automations/{id}/runs` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Deletes an automation definition when `If-Match` matches the current automation revision. + * @summary Delete automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteAutomation: async (id: string, ifMatch: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('deleteAutomation', 'id', id) + // verify required parameter 'ifMatch' is not null or undefined + assertParamExists('deleteAutomation', 'ifMatch', ifMatch) + const localVarPath = `/api/v1/automations/{id}` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Accept'] = 'application/json'; + + if (ifMatch != null) { + localVarHeaderParameter['If-Match'] = String(ifMatch); + } + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Returns durable runs created by one automation. + * @summary List automation runs + * @param {string} id Unique automation identifier. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listAutomationRuns: async (id: string, pageLimit?: number, pageOffset?: number, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('listAutomationRuns', 'id', id) + const localVarPath = `/api/v1/automations/{id}/runs` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (pageLimit !== undefined) { + localVarQueryParameter['page[limit]'] = pageLimit; + } + + if (pageOffset !== undefined) { + localVarQueryParameter['page[offset]'] = pageOffset; + } + + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Returns all configured automation definitions. + * @summary List automations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listAutomations: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/automations`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Replaces an automation definition when `If-Match` matches the current automation revision. + * @summary Replace automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {ReplaceAutomationRequest} replaceAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + replaceAutomation: async (id: string, ifMatch: string, replaceAutomationRequest: ReplaceAutomationRequest, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('replaceAutomation', 'id', id) + // verify required parameter 'ifMatch' is not null or undefined + assertParamExists('replaceAutomation', 'ifMatch', ifMatch) + // verify required parameter 'replaceAutomationRequest' is not null or undefined + assertParamExists('replaceAutomation', 'replaceAutomationRequest', replaceAutomationRequest) + const localVarPath = `/api/v1/automations/{id}` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Content-Type'] = 'application/json'; + localVarHeaderParameter['Accept'] = 'application/json'; + + if (ifMatch != null) { + localVarHeaderParameter['If-Match'] = String(ifMatch); + } + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(replaceAutomationRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Returns one automation definition by id. + * @summary Retrieve automation + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + retrieveAutomation: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('retrieveAutomation', 'id', id) + const localVarPath = `/api/v1/automations/{id}` + .replace(`{${"id"}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication SessionCookie required + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + localVarHeaderParameter['Accept'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * AutomationsApi - functional programming interface + */ +export const AutomationsApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = AutomationsApiAxiosParamCreator(configuration) + return { + /** + * Creates a new automation definition. + * @summary Create automation + * @param {CreateAutomationRequest} createAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createAutomation(createAutomationRequest: CreateAutomationRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createAutomation(createAutomationRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.createAutomation']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Creates a new run by firing the automation\'s enabled API trigger. + * @summary Create automation run + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createAutomationRun(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createAutomationRun(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.createAutomationRun']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Deletes an automation definition when `If-Match` matches the current automation revision. + * @summary Delete automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteAutomation(id: string, ifMatch: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAutomation(id, ifMatch, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.deleteAutomation']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Returns durable runs created by one automation. + * @summary List automation runs + * @param {string} id Unique automation identifier. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listAutomationRuns(id: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listAutomationRuns(id, pageLimit, pageOffset, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.listAutomationRuns']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Returns all configured automation definitions. + * @summary List automations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listAutomations(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listAutomations(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.listAutomations']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Replaces an automation definition when `If-Match` matches the current automation revision. + * @summary Replace automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {ReplaceAutomationRequest} replaceAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async replaceAutomation(id: string, ifMatch: string, replaceAutomationRequest: ReplaceAutomationRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.replaceAutomation(id, ifMatch, replaceAutomationRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.replaceAutomation']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Returns one automation definition by id. + * @summary Retrieve automation + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async retrieveAutomation(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.retrieveAutomation(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['AutomationsApi.retrieveAutomation']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * AutomationsApi - factory interface + */ +export const AutomationsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = AutomationsApiFp(configuration) + return { + /** + * Creates a new automation definition. + * @summary Create automation + * @param {CreateAutomationRequest} createAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createAutomation(createAutomationRequest: CreateAutomationRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.createAutomation(createAutomationRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Creates a new run by firing the automation\'s enabled API trigger. + * @summary Create automation run + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createAutomationRun(id: string, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.createAutomationRun(id, options).then((request) => request(axios, basePath)); + }, + /** + * Deletes an automation definition when `If-Match` matches the current automation revision. + * @summary Delete automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteAutomation(id: string, ifMatch: string, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.deleteAutomation(id, ifMatch, options).then((request) => request(axios, basePath)); + }, + /** + * Returns durable runs created by one automation. + * @summary List automation runs + * @param {string} id Unique automation identifier. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listAutomationRuns(id: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listAutomationRuns(id, pageLimit, pageOffset, options).then((request) => request(axios, basePath)); + }, + /** + * Returns all configured automation definitions. + * @summary List automations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listAutomations(options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listAutomations(options).then((request) => request(axios, basePath)); + }, + /** + * Replaces an automation definition when `If-Match` matches the current automation revision. + * @summary Replace automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {ReplaceAutomationRequest} replaceAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + replaceAutomation(id: string, ifMatch: string, replaceAutomationRequest: ReplaceAutomationRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.replaceAutomation(id, ifMatch, replaceAutomationRequest, options).then((request) => request(axios, basePath)); + }, + /** + * Returns one automation definition by id. + * @summary Retrieve automation + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + retrieveAutomation(id: string, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.retrieveAutomation(id, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * AutomationsApi - object-oriented interface + */ +export class AutomationsApi extends BaseAPI { + /** + * Creates a new automation definition. + * @summary Create automation + * @param {CreateAutomationRequest} createAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public createAutomation(createAutomationRequest: CreateAutomationRequest, options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).createAutomation(createAutomationRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Creates a new run by firing the automation\'s enabled API trigger. + * @summary Create automation run + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public createAutomationRun(id: string, options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).createAutomationRun(id, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Deletes an automation definition when `If-Match` matches the current automation revision. + * @summary Delete automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public deleteAutomation(id: string, ifMatch: string, options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).deleteAutomation(id, ifMatch, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Returns durable runs created by one automation. + * @summary List automation runs + * @param {string} id Unique automation identifier. + * @param {number} [pageLimit] Maximum number of items to return per page. + * @param {number} [pageOffset] Number of items to skip before returning results. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public listAutomationRuns(id: string, pageLimit?: number, pageOffset?: number, options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).listAutomationRuns(id, pageLimit, pageOffset, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Returns all configured automation definitions. + * @summary List automations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public listAutomations(options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).listAutomations(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Replaces an automation definition when `If-Match` matches the current automation revision. + * @summary Replace automation + * @param {string} id Unique automation identifier. + * @param {string} ifMatch Current resource revision used for optimistic concurrency, as returned in the `ETag` response header. + * @param {ReplaceAutomationRequest} replaceAutomationRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public replaceAutomation(id: string, ifMatch: string, replaceAutomationRequest: ReplaceAutomationRequest, options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).replaceAutomation(id, ifMatch, replaceAutomationRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Returns one automation definition by id. + * @summary Retrieve automation + * @param {string} id Unique automation identifier. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + public retrieveAutomation(id: string, options?: RawAxiosRequestConfig) { + return AutomationsApiFp(this.configuration).retrieveAutomation(id, options).then((request) => request(this.axios, this.basePath)); + } +} diff --git a/lib/packages/fabro-api-client/src/models/automation-api-trigger.ts b/lib/packages/fabro-api-client/src/models/automation-api-trigger.ts new file mode 100644 index 000000000..12a830945 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/automation-api-trigger.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Trigger that allows callers to create runs through the automation API. + */ +export interface AutomationApiTrigger { + 'id': string; + 'type': AutomationApiTriggerTypeEnum; + 'enabled': boolean; +} + +export const AutomationApiTriggerTypeEnum = { + API: 'api' +} as const; + +export type AutomationApiTriggerTypeEnum = typeof AutomationApiTriggerTypeEnum[keyof typeof AutomationApiTriggerTypeEnum]; diff --git a/lib/packages/fabro-api-client/src/models/automation-list-response.ts b/lib/packages/fabro-api-client/src/models/automation-list-response.ts new file mode 100644 index 000000000..d3b89ab51 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/automation-list-response.ts @@ -0,0 +1,25 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { Automation } from './automation'; + +/** + * List envelope for automation definitions. + */ +export interface AutomationListResponse { + 'data': Array; +} diff --git a/lib/packages/fabro-api-client/src/models/automation-schedule-trigger.ts b/lib/packages/fabro-api-client/src/models/automation-schedule-trigger.ts new file mode 100644 index 000000000..85b9631b2 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/automation-schedule-trigger.ts @@ -0,0 +1,34 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Cron schedule trigger evaluated in UTC. + */ +export interface AutomationScheduleTrigger { + 'id': string; + 'type': AutomationScheduleTriggerTypeEnum; + 'enabled': boolean; + /** + * Five-field cron expression evaluated in UTC. + */ + 'expression': string; +} + +export const AutomationScheduleTriggerTypeEnum = { + SCHEDULE: 'schedule' +} as const; + +export type AutomationScheduleTriggerTypeEnum = typeof AutomationScheduleTriggerTypeEnum[keyof typeof AutomationScheduleTriggerTypeEnum]; diff --git a/lib/packages/fabro-api-client/src/models/automation-target.ts b/lib/packages/fabro-api-client/src/models/automation-target.ts new file mode 100644 index 000000000..6eb3daa75 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/automation-target.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * Repository and workflow selected by an automation. + */ +export interface AutomationTarget { + /** + * GitHub repository slug in `owner/repo` form. + */ + 'repository': string; + /** + * Branch, tag, or SHA selector resolved when materializing a run. + */ + 'ref': string; + /** + * Workflow slug or path resolved in the target repository. + */ + 'workflow': string; +} diff --git a/lib/packages/fabro-api-client/src/models/automation-trigger.ts b/lib/packages/fabro-api-client/src/models/automation-trigger.ts new file mode 100644 index 000000000..30e73dc81 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/automation-trigger.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationApiTrigger } from './automation-api-trigger'; +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationScheduleTrigger } from './automation-schedule-trigger'; + +/** + * @type AutomationTrigger + * Automation trigger configuration. Unknown `type` discriminator values are reported by handlers as domain validation errors with HTTP 422. + */ +export type AutomationTrigger = { type: 'api' } & AutomationApiTrigger | { type: 'schedule' } & AutomationScheduleTrigger; diff --git a/lib/packages/fabro-api-client/src/models/automation.ts b/lib/packages/fabro-api-client/src/models/automation.ts new file mode 100644 index 000000000..575759e19 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/automation.ts @@ -0,0 +1,37 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationTarget } from './automation-target'; +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationTrigger } from './automation-trigger'; + +/** + * Public automation definition. + */ +export interface Automation { + 'id': string; + /** + * Stable revision used with `If-Match` for optimistic concurrency. + */ + 'revision': string; + 'name': string; + 'description': string | null; + 'enabled': boolean; + 'target': AutomationTarget; + 'triggers': Array; +} diff --git a/lib/packages/fabro-api-client/src/models/create-automation-request.ts b/lib/packages/fabro-api-client/src/models/create-automation-request.ts new file mode 100644 index 000000000..5f21f795b --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/create-automation-request.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationTarget } from './automation-target'; +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationTrigger } from './automation-trigger'; + +/** + * Request body for creating an automation. + */ +export interface CreateAutomationRequest { + 'id': string; + 'name': string; + 'description'?: string | null; + 'enabled'?: boolean; + 'target': AutomationTarget; + 'triggers': Array; +} diff --git a/lib/packages/fabro-api-client/src/models/index.ts b/lib/packages/fabro-api-client/src/models/index.ts index eea7c071d..976cadc60 100644 --- a/lib/packages/fabro-api-client/src/models/index.ts +++ b/lib/packages/fabro-api-client/src/models/index.ts @@ -29,7 +29,13 @@ export * from './auth-method'; export * from './auth-session'; export * from './auth-session-user'; export * from './auth-sessions-response'; +export * from './automation'; +export * from './automation-api-trigger'; +export * from './automation-list-response'; export * from './automation-ref'; +export * from './automation-schedule-trigger'; +export * from './automation-target'; +export * from './automation-trigger'; export * from './batch-delete-runs-request'; export * from './batch-delete-runs-response'; export * from './batch-delete-runs-result'; @@ -59,6 +65,7 @@ export * from './completion-tool-choice'; export * from './completion-tool-definition'; export * from './completion-usage'; export * from './conclusion'; +export * from './create-automation-request'; export * from './create-completion-request'; export * from './create-run-pull-request-request'; export * from './create-run-session-request'; @@ -267,6 +274,7 @@ export * from './related-workflow-diagnostic'; export * from './render-workflow-graph-direction'; export * from './render-workflow-graph-format'; export * from './render-workflow-graph-request'; +export * from './replace-automation-request'; export * from './repo-check-response'; export * from './repo-check-response-permissions'; export * from './repository-ref'; diff --git a/lib/packages/fabro-api-client/src/models/replace-automation-request.ts b/lib/packages/fabro-api-client/src/models/replace-automation-request.ts new file mode 100644 index 000000000..4edb9df6b --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/replace-automation-request.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationTarget } from './automation-target'; +// May contain unused imports in some cases +// @ts-ignore +import type { AutomationTrigger } from './automation-trigger'; + +/** + * Request body for replacing an automation. + */ +export interface ReplaceAutomationRequest { + 'name': string; + 'description'?: string | null; + 'enabled': boolean; + 'target': AutomationTarget; + 'triggers': Array; +}