Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions automations_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ type AutomationRuleUpdateRequest struct {
RuleID string `json:"rule_id,omitempty" toon:"rule_id,omitempty"`
// New rule name.
Name *string `json:"name,omitempty" toon:"name,omitempty"`
// Only the current value is accepted; personal/team scope is immutable after creation.
TeamID *int64 `json:"team_id,omitempty" toon:"team_id,omitempty"`
// Whether the rule is enabled.
Enabled *bool `json:"enabled,omitempty" toon:"enabled,omitempty"`
// Run cadence. Supports 4 fields (`hour day month weekday`, minute defaults to 0) and 5 fields (`minute hour day month weekday`).
Expand Down
42 changes: 42 additions & 0 deletions automations_models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package flashduty

import (
"encoding/json"
"reflect"
"strings"
"testing"
)
Expand Down Expand Up @@ -37,3 +38,44 @@ func TestAutomationRuleUpdateRequestTimezone(t *testing.T) {
t.Fatalf("named timezone must be sent, got %s", named)
}
}

func TestAutomationRuleCreateRequestExecutionDestination(t *testing.T) {
omitted, err := json.Marshal(&AutomationRuleCreateRequest{
Name: "Daily SRE brief",
CronExpr: "0 9 * * *",
Prompt: "Summarize yesterday's incidents",
})
if err != nil {
t.Fatal(err)
}
for _, field := range []string{"run_in", "team_id"} {
if strings.Contains(string(omitted), `"`+field+`"`) {
t.Fatalf("omitted %s must stay off the wire, got %s", field, omitted)
}
}

explicit, err := json.Marshal(&AutomationRuleCreateRequest{
Name: "Daily SRE brief",
CronExpr: "0 9 * * *",
Prompt: "Summarize yesterday's incidents",
RunIn: String("current-chat"),
TeamID: Int64(0),
})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{`"run_in":"current-chat"`, `"team_id":0`} {
if !strings.Contains(string(explicit), want) {
t.Fatalf("explicit destination must send %s, got %s", want, explicit)
}
}
}

func TestAutomationRuleUpdateRequestHasNoExecutionTarget(t *testing.T) {
typ := reflect.TypeFor[AutomationRuleUpdateRequest]()
for _, field := range []string{"TeamID", "RunIn"} {
if _, ok := typ.FieldByName(field); ok {
t.Fatalf("update must not expose immutable execution target field %s", field)
}
}
}
31 changes: 28 additions & 3 deletions models_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 87 additions & 11 deletions openapi/openapi.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3280,6 +3280,18 @@
"minLength": 1,
"type": "string"
},
"run_in": {
"description": "Execution destination. `current-chat` binds the rule to the authenticated live IM conversation; `web` runs in the web console. Omit it so the server selects `current-chat` for an authenticated IM invocation and `web` otherwise. The server rejects `current-chat` outside a live IM conversation.",
"enum": [
"current-chat",
"web",
null
],
"type": [
"string",
"null"
]
},
"schedule_trigger_enabled": {
"description": "Whether the schedule trigger is enabled. Defaults to true when omitted; HTTP-POST-only rules should send false.",
"type": [
Expand All @@ -3288,10 +3300,13 @@
]
},
"team_id": {
"description": "Scope team ID. 0 or omitted means a personal rule; >0 means a team in the account. Can be reassigned later via update (converting a team rule to personal is owner-only; moving into a team requires the caller to belong to it).",
"description": "Scope team ID. Explicit 0 means personal scope; >0 means a team in the account. Omit it to inherit the authenticated IM conversation scope when one is available, otherwise let the server select the normal default. A current-chat rule must match that conversation's scope.",
"format": "int64",
"minimum": 0,
"type": "integer"
"type": [
"integer",
"null"
]
},
"timezone": {
"description": "IANA timezone `cron_expr` is evaluated in, e.g. `Asia/Shanghai`. Must be a timezone name loadable by the server; an invalid value is rejected. Defaults to the caller's member timezone, then the account timezone, then the server default (Asia/Shanghai) when omitted.",
Expand All @@ -3317,6 +3332,55 @@
],
"type": "object"
},
"AutomationExecutionLocation": {
"description": "Read-only Automation execution destination. It never includes a chat, integration, or session identifier.",
"properties": {
"chat_name": {
"description": "Display name of the bound IM conversation when available.",
"type": "string"
},
"conversation_mode": {
"description": "Bound IM conversation mode when execution place is `im`.",
"type": "string"
},
"description": {
"description": "Human-readable destination description.",
"type": "string"
},
"place": {
"description": "Execution destination: `im` or `web_console`.",
"enum": [
"im",
"web_console"
],
"type": "string"
},
"platform": {
"description": "IM platform when execution place is `im`.",
"type": "string"
},
"run_scope": {
"description": "Pinned execution scope: `person` or `team`.",
"enum": [
"person",
"team"
],
"type": "string"
},
"team_id": {
"description": "Pinned scope team ID. 0 means personal scope.",
"format": "int64",
"type": "integer"
}
},
"required": [
"place",
"run_scope",
"team_id",
"description"
],
"type": "object"
},
"AutomationRuleItem": {
"description": "Automation rule.",
"properties": {
Expand Down Expand Up @@ -3355,6 +3419,17 @@
],
"type": "string"
},
"execution_location": {
"$ref": "#/components/schemas/AutomationExecutionLocation"
},
"execution_place": {
"description": "Configured execution destination for ordinary triggers. War-room triggers resolve their event war room at execution time.",
"enum": [
"im",
"web_console"
],
"type": "string"
},
"http_post_token": {
"description": "HTTP POST trigger token. Returned only on create or token rotation; save it immediately.",
"type": "string"
Expand Down Expand Up @@ -3466,6 +3541,7 @@
"prompt",
"environment_kind",
"environment_id",
"execution_place",
"schedule_trigger_enabled",
"http_post_trigger_enabled",
"can_edit",
Expand Down Expand Up @@ -3652,15 +3728,6 @@
"null"
]
},
"team_id": {
"description": "Reassign the rule's scope: 0 converts to a personal rule (only the rule owner may convert a team rule); >0 moves it into a team the caller belongs to. Omit to leave unchanged.",
"format": "int64",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"timezone": {
"description": "New IANA timezone for evaluating `cron_expr`. Omit or send null to leave the current timezone unchanged.",
"type": [
Expand Down Expand Up @@ -3700,6 +3767,14 @@
"format": "int64",
"type": "integer"
},
"execution_place": {
"description": "Actual execution destination: `im` or `web_console`. IM runs omit session details and deep links.",
"enum": [
"im",
"web_console"
],
"type": "string"
},
"error_code": {
"description": "Error code; empty when the run did not fail.",
"type": "string"
Expand Down Expand Up @@ -3800,6 +3875,7 @@
"started_at",
"completed_at",
"duration_ms",
"execution_place",
"created_at",
"updated_at",
"error_code",
Expand Down
Loading
Loading