- Introduced a new `Type` field in the `ActivityQuery` struct to allow filtering activities by type (e.g., execution.started, execution.completed, execution.failed, execution.cancelled). - Updated the `ListActivities` method in `ExecutionStore` to handle the new type filter, mapping it to corresponding execution statuses. - Enhanced unit tests in `execution_test.go` to validate filtering by activity type, including tests for valid and invalid type scenarios. - Modified OpenAPI definitions and related types to support the new type filter in the activities endpoint, improving API usability and flexibility.
107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
package robot
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/yaoapp/kun/log"
|
|
robotapi "github.com/yaoapp/yao/agent/robot/api"
|
|
robottypes "github.com/yaoapp/yao/agent/robot/types"
|
|
"github.com/yaoapp/yao/openapi/oauth/authorized"
|
|
"github.com/yaoapp/yao/openapi/response"
|
|
)
|
|
|
|
// ==================== Activities Handler ====================
|
|
// Activities are derived from execution status changes across all robots in a team
|
|
|
|
// ListActivities lists recent activities for the user's team
|
|
// GET /v1/agent/robots/activities
|
|
func ListActivities(c *gin.Context) {
|
|
// Get authorized information
|
|
authInfo := authorized.GetInfo(c)
|
|
|
|
// Get team_id from auth - activities are team-scoped
|
|
teamID := ""
|
|
if authInfo != nil {
|
|
teamID = authInfo.TeamID
|
|
// If no team_id, fall back to user_id for personal users
|
|
if teamID == "" {
|
|
teamID = authInfo.UserID
|
|
}
|
|
}
|
|
|
|
if teamID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrAccessDenied.Code,
|
|
ErrorDescription: "Unable to determine team scope",
|
|
}
|
|
response.RespondWithError(c, response.StatusForbidden, errorResp)
|
|
return
|
|
}
|
|
|
|
// Parse query parameters
|
|
var filter ActivityFilter
|
|
if err := c.ShouldBindQuery(&filter); err != nil {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Invalid query parameters: " + err.Error(),
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
|
|
// Apply defaults
|
|
if filter.Limit <= 0 {
|
|
filter.Limit = 20
|
|
}
|
|
if filter.Limit > 100 {
|
|
filter.Limit = 100
|
|
}
|
|
|
|
// Create robot context
|
|
ctx := &robottypes.Context{}
|
|
|
|
// Build API query
|
|
query := &robotapi.ActivityQuery{
|
|
TeamID: teamID,
|
|
Limit: filter.Limit,
|
|
Type: filter.Type, // Pass type filter
|
|
}
|
|
|
|
// Parse 'since' if provided
|
|
if filter.Since != "" {
|
|
since, err := time.Parse(time.RFC3339, filter.Since)
|
|
if err != nil {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Invalid 'since' parameter: must be RFC3339 format",
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
query.Since = &since
|
|
}
|
|
|
|
// Call API layer
|
|
result, err := robotapi.ListActivities(ctx, query)
|
|
if err != nil {
|
|
log.Error("Failed to list activities for team %s: %v", teamID, err)
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to list activities: " + err.Error(),
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
return
|
|
}
|
|
|
|
// Convert to response
|
|
data := make([]*ActivityResponse, 0, len(result.Data))
|
|
for _, item := range result.Data {
|
|
data = append(data, NewActivityResponse(item))
|
|
}
|
|
|
|
resp := &ActivityListResponse{
|
|
Data: data,
|
|
}
|
|
response.RespondWithSuccess(c, response.StatusOK, resp)
|
|
}
|