Fix golangci-lint unused failure — export OpenAPI response types
CI / Canvas Deploy Reminder (pull_request) Blocked by required conditions
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 7s
CI / Python Lint & Test (pull_request) Successful in 4s
E2E API Smoke Test / detect-changes (pull_request) Successful in 7s
E2E Chat / detect-changes (pull_request) Successful in 9s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 6s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 5s
Harness Replays / detect-changes (pull_request) Successful in 2s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 4s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 3s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
gate-check-v3 / gate-check (pull_request) Successful in 9s
qa-review / approved (pull_request) Failing after 3s
security-review / approved (pull_request) Failing after 3s
sop-checklist / review-refire (pull_request) Has been skipped
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Successful in 4s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m8s
CI / Canvas (Next.js) (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
E2E Chat / E2E Chat (pull_request) Successful in 5s
Harness Replays / Harness Replays (pull_request) Successful in 3s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m7s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2m19s
CI / Platform (Go) (pull_request) Successful in 5m50s
CI / all-required (pull_request) Successful in 14m14s
audit-force-merge / audit (pull_request) Successful in 3s

CI / Platform (Go) flagged `internal/handlers/schedules.go:19:6: type
errorResponse is unused (unused)` because golangci-lint's `unused`
checker doesn't recognise swaggo's `@Success {object} errorResponse`
annotations as references — the types are only used in comments
from Go's perspective.

Fix: export all OpenAPI request/response types. Exported names are
skipped by the unused-checker (package consumers may exist outside
the package, even if absent inside it).

Renamed:
- errorResponse → ErrorResponse
- statusResponse → StatusResponse
- createScheduleResponse → CreateScheduleResponse
- runNowResponse → RunNowResponse
- historyEntry → HistoryEntry
- scheduleResponse → ScheduleResponse
- createScheduleRequest → CreateScheduleRequest
- updateScheduleRequest → UpdateScheduleRequest
- scheduleHealthResponse → ScheduleHealthResponse (test ref updated)

Regenerated docs/openapi/swagger.{yaml,json} with new type names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 00:14:16 -07:00
parent f01636cd76
commit f86e151c02
4 changed files with 98 additions and 98 deletions
+27 -27
View File
@@ -42,14 +42,14 @@
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/handlers.scheduleResponse" "$ref": "#/definitions/handlers.ScheduleResponse"
} }
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
} }
} }
@@ -85,7 +85,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/handlers.createScheduleRequest" "$ref": "#/definitions/handlers.CreateScheduleRequest"
} }
} }
], ],
@@ -93,19 +93,19 @@
"201": { "201": {
"description": "Created", "description": "Created",
"schema": { "schema": {
"$ref": "#/definitions/handlers.createScheduleResponse" "$ref": "#/definitions/handlers.CreateScheduleResponse"
} }
}, },
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
} }
} }
@@ -146,19 +146,19 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/handlers.statusResponse" "$ref": "#/definitions/handlers.StatusResponse"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
} }
} }
@@ -201,7 +201,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/handlers.updateScheduleRequest" "$ref": "#/definitions/handlers.UpdateScheduleRequest"
} }
} }
], ],
@@ -209,25 +209,25 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/handlers.scheduleResponse" "$ref": "#/definitions/handlers.ScheduleResponse"
} }
}, },
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
} }
} }
@@ -270,14 +270,14 @@
"schema": { "schema": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/handlers.historyEntry" "$ref": "#/definitions/handlers.HistoryEntry"
} }
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
} }
} }
@@ -318,19 +318,19 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/handlers.runNowResponse" "$ref": "#/definitions/handlers.RunNowResponse"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/handlers.errorResponse" "$ref": "#/definitions/handlers.ErrorResponse"
} }
} }
} }
@@ -338,7 +338,7 @@
} }
}, },
"definitions": { "definitions": {
"handlers.createScheduleRequest": { "handlers.CreateScheduleRequest": {
"type": "object", "type": "object",
"required": [ "required": [
"cron_expr", "cron_expr",
@@ -362,7 +362,7 @@
} }
} }
}, },
"handlers.createScheduleResponse": { "handlers.CreateScheduleResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"id": { "id": {
@@ -376,7 +376,7 @@
} }
} }
}, },
"handlers.errorResponse": { "handlers.ErrorResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"error": { "error": {
@@ -384,7 +384,7 @@
} }
} }
}, },
"handlers.historyEntry": { "handlers.HistoryEntry": {
"type": "object", "type": "object",
"properties": { "properties": {
"duration_ms": { "duration_ms": {
@@ -404,7 +404,7 @@
} }
} }
}, },
"handlers.runNowResponse": { "handlers.RunNowResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"prompt": { "prompt": {
@@ -418,7 +418,7 @@
} }
} }
}, },
"handlers.scheduleResponse": { "handlers.ScheduleResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"created_at": { "created_at": {
@@ -469,7 +469,7 @@
} }
} }
}, },
"handlers.statusResponse": { "handlers.StatusResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"status": { "status": {
@@ -477,7 +477,7 @@
} }
} }
}, },
"handlers.updateScheduleRequest": { "handlers.UpdateScheduleRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
"cron_expr": { "cron_expr": {
+27 -27
View File
@@ -1,6 +1,6 @@
basePath: / basePath: /
definitions: definitions:
handlers.createScheduleRequest: handlers.CreateScheduleRequest:
properties: properties:
cron_expr: cron_expr:
type: string type: string
@@ -16,7 +16,7 @@ definitions:
- cron_expr - cron_expr
- prompt - prompt
type: object type: object
handlers.createScheduleResponse: handlers.CreateScheduleResponse:
properties: properties:
id: id:
type: string type: string
@@ -25,12 +25,12 @@ definitions:
status: status:
type: string type: string
type: object type: object
handlers.errorResponse: handlers.ErrorResponse:
properties: properties:
error: error:
type: string type: string
type: object type: object
handlers.historyEntry: handlers.HistoryEntry:
properties: properties:
duration_ms: duration_ms:
type: integer type: integer
@@ -43,7 +43,7 @@ definitions:
timestamp: timestamp:
type: string type: string
type: object type: object
handlers.runNowResponse: handlers.RunNowResponse:
properties: properties:
prompt: prompt:
type: string type: string
@@ -52,7 +52,7 @@ definitions:
workspace_id: workspace_id:
type: string type: string
type: object type: object
handlers.scheduleResponse: handlers.ScheduleResponse:
properties: properties:
created_at: created_at:
type: string type: string
@@ -87,12 +87,12 @@ definitions:
workspace_id: workspace_id:
type: string type: string
type: object type: object
handlers.statusResponse: handlers.StatusResponse:
properties: properties:
status: status:
type: string type: string
type: object type: object
handlers.updateScheduleRequest: handlers.UpdateScheduleRequest:
properties: properties:
cron_expr: cron_expr:
type: string type: string
@@ -130,12 +130,12 @@ paths:
description: OK description: OK
schema: schema:
items: items:
$ref: '#/definitions/handlers.scheduleResponse' $ref: '#/definitions/handlers.ScheduleResponse'
type: array type: array
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
security: security:
- BearerAuth: [] - BearerAuth: []
OrgSlugAuth: [] OrgSlugAuth: []
@@ -156,22 +156,22 @@ paths:
name: body name: body
required: true required: true
schema: schema:
$ref: '#/definitions/handlers.createScheduleRequest' $ref: '#/definitions/handlers.CreateScheduleRequest'
produces: produces:
- application/json - application/json
responses: responses:
"201": "201":
description: Created description: Created
schema: schema:
$ref: '#/definitions/handlers.createScheduleResponse' $ref: '#/definitions/handlers.CreateScheduleResponse'
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
security: security:
- BearerAuth: [] - BearerAuth: []
OrgSlugAuth: [] OrgSlugAuth: []
@@ -197,15 +197,15 @@ paths:
"200": "200":
description: OK description: OK
schema: schema:
$ref: '#/definitions/handlers.statusResponse' $ref: '#/definitions/handlers.StatusResponse'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
security: security:
- BearerAuth: [] - BearerAuth: []
OrgSlugAuth: [] OrgSlugAuth: []
@@ -231,26 +231,26 @@ paths:
name: body name: body
required: true required: true
schema: schema:
$ref: '#/definitions/handlers.updateScheduleRequest' $ref: '#/definitions/handlers.UpdateScheduleRequest'
produces: produces:
- application/json - application/json
responses: responses:
"200": "200":
description: OK description: OK
schema: schema:
$ref: '#/definitions/handlers.scheduleResponse' $ref: '#/definitions/handlers.ScheduleResponse'
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
security: security:
- BearerAuth: [] - BearerAuth: []
OrgSlugAuth: [] OrgSlugAuth: []
@@ -277,12 +277,12 @@ paths:
description: OK description: OK
schema: schema:
items: items:
$ref: '#/definitions/handlers.historyEntry' $ref: '#/definitions/handlers.HistoryEntry'
type: array type: array
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
security: security:
- BearerAuth: [] - BearerAuth: []
OrgSlugAuth: [] OrgSlugAuth: []
@@ -308,15 +308,15 @@ paths:
"200": "200":
description: OK description: OK
schema: schema:
$ref: '#/definitions/handlers.runNowResponse' $ref: '#/definitions/handlers.RunNowResponse'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/handlers.errorResponse' $ref: '#/definitions/handlers.ErrorResponse'
security: security:
- BearerAuth: [] - BearerAuth: []
OrgSlugAuth: [] OrgSlugAuth: []
+42 -42
View File
@@ -15,32 +15,32 @@ import (
"github.com/Molecule-AI/molecule-monorepo/platform/internal/scheduler" "github.com/Molecule-AI/molecule-monorepo/platform/internal/scheduler"
) )
// errorResponse is returned for 4xx/5xx errors. (OpenAPI doc shape — used by swaggo.) // ErrorResponse is returned for 4xx/5xx errors. (OpenAPI doc shape — used by swaggo.)
type errorResponse struct { type ErrorResponse struct {
Error string `json:"error"` Error string `json:"error"`
} }
// statusResponse is returned by mutating endpoints that only echo a status verb. // StatusResponse is returned by mutating endpoints that only echo a status verb.
type statusResponse struct { type StatusResponse struct {
Status string `json:"status"` Status string `json:"status"`
} }
// createScheduleResponse is returned by POST /workspaces/{id}/schedules. // CreateScheduleResponse is returned by POST /workspaces/{id}/schedules.
type createScheduleResponse struct { type CreateScheduleResponse struct {
ID string `json:"id"` ID string `json:"id"`
Status string `json:"status"` Status string `json:"status"`
NextRunAt time.Time `json:"next_run_at"` NextRunAt time.Time `json:"next_run_at"`
} }
// runNowResponse is returned by POST /workspaces/{id}/schedules/{scheduleId}/run. // RunNowResponse is returned by POST /workspaces/{id}/schedules/{scheduleId}/run.
type runNowResponse struct { type RunNowResponse struct {
Status string `json:"status"` Status string `json:"status"`
WorkspaceID string `json:"workspace_id"` WorkspaceID string `json:"workspace_id"`
Prompt string `json:"prompt"` Prompt string `json:"prompt"`
} }
// historyEntry is one row of /workspaces/{id}/schedules/{scheduleId}/history. // HistoryEntry is one row of /workspaces/{id}/schedules/{scheduleId}/history.
type historyEntry struct { type HistoryEntry struct {
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
DurationMs *int `json:"duration_ms"` DurationMs *int `json:"duration_ms"`
Status *string `json:"status"` Status *string `json:"status"`
@@ -54,7 +54,7 @@ func NewScheduleHandler() *ScheduleHandler {
return &ScheduleHandler{} return &ScheduleHandler{}
} }
type scheduleResponse struct { type ScheduleResponse struct {
ID string `json:"id"` ID string `json:"id"`
WorkspaceID string `json:"workspace_id"` WorkspaceID string `json:"workspace_id"`
Name string `json:"name"` Name string `json:"name"`
@@ -78,8 +78,8 @@ type scheduleResponse struct {
// @Tags schedules // @Tags schedules
// @Produce json // @Produce json
// @Param id path string true "Workspace ID" // @Param id path string true "Workspace ID"
// @Success 200 {array} scheduleResponse // @Success 200 {array} ScheduleResponse
// @Failure 500 {object} errorResponse // @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules [get] // @Router /workspaces/{id}/schedules [get]
// @Security BearerAuth && OrgSlugAuth // @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) List(c *gin.Context) { func (h *ScheduleHandler) List(c *gin.Context) {
@@ -100,9 +100,9 @@ func (h *ScheduleHandler) List(c *gin.Context) {
} }
defer rows.Close() defer rows.Close()
schedules := make([]scheduleResponse, 0) schedules := make([]ScheduleResponse, 0)
for rows.Next() { for rows.Next() {
var s scheduleResponse var s ScheduleResponse
if err := rows.Scan( if err := rows.Scan(
&s.ID, &s.WorkspaceID, &s.Name, &s.CronExpr, &s.Timezone, &s.ID, &s.WorkspaceID, &s.Name, &s.CronExpr, &s.Timezone,
&s.Prompt, &s.Enabled, &s.LastRunAt, &s.NextRunAt, &s.RunCount, &s.Prompt, &s.Enabled, &s.LastRunAt, &s.NextRunAt, &s.RunCount,
@@ -120,7 +120,7 @@ func (h *ScheduleHandler) List(c *gin.Context) {
c.JSON(http.StatusOK, schedules) c.JSON(http.StatusOK, schedules)
} }
type createScheduleRequest struct { type CreateScheduleRequest struct {
Name string `json:"name"` Name string `json:"name"`
CronExpr string `json:"cron_expr" binding:"required"` CronExpr string `json:"cron_expr" binding:"required"`
Timezone string `json:"timezone"` Timezone string `json:"timezone"`
@@ -135,17 +135,17 @@ type createScheduleRequest struct {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param id path string true "Workspace ID" // @Param id path string true "Workspace ID"
// @Param body body createScheduleRequest true "Schedule fields" // @Param body body CreateScheduleRequest true "Schedule fields"
// @Success 201 {object} createScheduleResponse // @Success 201 {object} CreateScheduleResponse
// @Failure 400 {object} errorResponse // @Failure 400 {object} ErrorResponse
// @Failure 500 {object} errorResponse // @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules [post] // @Router /workspaces/{id}/schedules [post]
// @Security BearerAuth && OrgSlugAuth // @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) Create(c *gin.Context) { func (h *ScheduleHandler) Create(c *gin.Context) {
workspaceID := c.Param("id") workspaceID := c.Param("id")
ctx := c.Request.Context() ctx := c.Request.Context()
var body createScheduleRequest var body CreateScheduleRequest
if err := c.ShouldBindJSON(&body); err != nil { if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "cron_expr and prompt are required"}) c.JSON(http.StatusBadRequest, gin.H{"error": "cron_expr and prompt are required"})
return return
@@ -199,7 +199,7 @@ func (h *ScheduleHandler) Create(c *gin.Context) {
}) })
} }
type updateScheduleRequest struct { type UpdateScheduleRequest struct {
Name *string `json:"name"` Name *string `json:"name"`
CronExpr *string `json:"cron_expr"` CronExpr *string `json:"cron_expr"`
Timezone *string `json:"timezone"` Timezone *string `json:"timezone"`
@@ -216,11 +216,11 @@ type updateScheduleRequest struct {
// @Produce json // @Produce json
// @Param id path string true "Workspace ID" // @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID" // @Param scheduleId path string true "Schedule ID"
// @Param body body updateScheduleRequest true "Partial schedule fields (only provided keys are updated)" // @Param body body UpdateScheduleRequest true "Partial schedule fields (only provided keys are updated)"
// @Success 200 {object} scheduleResponse // @Success 200 {object} ScheduleResponse
// @Failure 400 {object} errorResponse // @Failure 400 {object} ErrorResponse
// @Failure 404 {object} errorResponse // @Failure 404 {object} ErrorResponse
// @Failure 500 {object} errorResponse // @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId} [patch] // @Router /workspaces/{id}/schedules/{scheduleId} [patch]
// @Security BearerAuth && OrgSlugAuth // @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) Update(c *gin.Context) { func (h *ScheduleHandler) Update(c *gin.Context) {
@@ -228,7 +228,7 @@ func (h *ScheduleHandler) Update(c *gin.Context) {
workspaceID := c.Param("id") // #113: bind to owning workspace to prevent IDOR workspaceID := c.Param("id") // #113: bind to owning workspace to prevent IDOR
ctx := c.Request.Context() ctx := c.Request.Context()
var body updateScheduleRequest var body UpdateScheduleRequest
if err := c.ShouldBindJSON(&body); err != nil { if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON"}) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON"})
return return
@@ -304,9 +304,9 @@ func (h *ScheduleHandler) Update(c *gin.Context) {
// @Produce json // @Produce json
// @Param id path string true "Workspace ID" // @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID" // @Param scheduleId path string true "Schedule ID"
// @Success 200 {object} statusResponse // @Success 200 {object} StatusResponse
// @Failure 404 {object} errorResponse // @Failure 404 {object} ErrorResponse
// @Failure 500 {object} errorResponse // @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId} [delete] // @Router /workspaces/{id}/schedules/{scheduleId} [delete]
// @Security BearerAuth && OrgSlugAuth // @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) Delete(c *gin.Context) { func (h *ScheduleHandler) Delete(c *gin.Context) {
@@ -337,9 +337,9 @@ func (h *ScheduleHandler) Delete(c *gin.Context) {
// @Produce json // @Produce json
// @Param id path string true "Workspace ID" // @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID" // @Param scheduleId path string true "Schedule ID"
// @Success 200 {object} runNowResponse // @Success 200 {object} RunNowResponse
// @Failure 404 {object} errorResponse // @Failure 404 {object} ErrorResponse
// @Failure 500 {object} errorResponse // @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId}/run [post] // @Router /workspaces/{id}/schedules/{scheduleId}/run [post]
// @Security BearerAuth && OrgSlugAuth // @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) RunNow(c *gin.Context) { func (h *ScheduleHandler) RunNow(c *gin.Context) {
@@ -378,8 +378,8 @@ func (h *ScheduleHandler) RunNow(c *gin.Context) {
// @Produce json // @Produce json
// @Param id path string true "Workspace ID" // @Param id path string true "Workspace ID"
// @Param scheduleId path string true "Schedule ID" // @Param scheduleId path string true "Schedule ID"
// @Success 200 {array} historyEntry // @Success 200 {array} HistoryEntry
// @Failure 500 {object} errorResponse // @Failure 500 {object} ErrorResponse
// @Router /workspaces/{id}/schedules/{scheduleId}/history [get] // @Router /workspaces/{id}/schedules/{scheduleId}/history [get]
// @Security BearerAuth && OrgSlugAuth // @Security BearerAuth && OrgSlugAuth
func (h *ScheduleHandler) History(c *gin.Context) { func (h *ScheduleHandler) History(c *gin.Context) {
@@ -407,9 +407,9 @@ func (h *ScheduleHandler) History(c *gin.Context) {
} }
defer rows.Close() defer rows.Close()
entries := make([]historyEntry, 0) entries := make([]HistoryEntry, 0)
for rows.Next() { for rows.Next() {
var e historyEntry var e HistoryEntry
var reqStr string var reqStr string
if err := rows.Scan(&e.Timestamp, &e.DurationMs, &e.Status, &e.ErrorDetail, &reqStr); err != nil { if err := rows.Scan(&e.Timestamp, &e.DurationMs, &e.Status, &e.ErrorDetail, &reqStr); err != nil {
continue continue
@@ -421,11 +421,11 @@ func (h *ScheduleHandler) History(c *gin.Context) {
c.JSON(http.StatusOK, entries) c.JSON(http.StatusOK, entries)
} }
// scheduleHealthResponse is the read-only health view of a schedule. // ScheduleHealthResponse is the read-only health view of a schedule.
// It deliberately omits prompt and cron_expr so sensitive task content is // It deliberately omits prompt and cron_expr so sensitive task content is
// never exposed to peer workspaces — only execution-state fields needed to // never exposed to peer workspaces — only execution-state fields needed to
// detect silent cron failures are returned (issue #249). // detect silent cron failures are returned (issue #249).
type scheduleHealthResponse struct { type ScheduleHealthResponse struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
@@ -494,9 +494,9 @@ func (h *ScheduleHandler) Health(c *gin.Context) {
} }
defer rows.Close() defer rows.Close()
schedules := make([]scheduleHealthResponse, 0) schedules := make([]ScheduleHealthResponse, 0)
for rows.Next() { for rows.Next() {
var s scheduleHealthResponse var s ScheduleHealthResponse
if err := rows.Scan( if err := rows.Scan(
&s.ID, &s.Name, &s.Enabled, &s.LastRunAt, &s.NextRunAt, &s.ID, &s.Name, &s.Enabled, &s.LastRunAt, &s.NextRunAt,
&s.RunCount, &s.LastStatus, &s.LastError, &s.RunCount, &s.LastStatus, &s.LastError,
@@ -234,7 +234,7 @@ func TestScheduleHealth_SelfCall_Allowed(t *testing.T) {
t.Fatalf("expected 200 for self-call, got %d: %s", w.Code, w.Body.String()) t.Fatalf("expected 200 for self-call, got %d: %s", w.Code, w.Body.String())
} }
var resp []scheduleHealthResponse var resp []ScheduleHealthResponse
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to parse response: %v", err) t.Fatalf("failed to parse response: %v", err)
} }
@@ -284,7 +284,7 @@ func TestScheduleHealth_CanCommunicatePeer_LegacyNoToken(t *testing.T) {
t.Fatalf("expected 200 for peer with no tokens, got %d: %s", w.Code, w.Body.String()) t.Fatalf("expected 200 for peer with no tokens, got %d: %s", w.Code, w.Body.String())
} }
var resp []scheduleHealthResponse var resp []ScheduleHealthResponse
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to parse response: %v", err) t.Fatalf("failed to parse response: %v", err)
} }