Merge pull request #285 from multica-ai/feature/attachment-improvements
feat: improve attachment support across issue and comment APIs
This commit is contained in:
commit
20a2332c94
3 changed files with 37 additions and 2 deletions
|
|
@ -123,6 +123,7 @@ func init() {
|
||||||
issueCreateCmd.Flags().String("parent", "", "Parent issue ID")
|
issueCreateCmd.Flags().String("parent", "", "Parent issue ID")
|
||||||
issueCreateCmd.Flags().String("due-date", "", "Due date (RFC3339 format)")
|
issueCreateCmd.Flags().String("due-date", "", "Due date (RFC3339 format)")
|
||||||
issueCreateCmd.Flags().String("output", "json", "Output format: table or json")
|
issueCreateCmd.Flags().String("output", "json", "Output format: table or json")
|
||||||
|
issueCreateCmd.Flags().StringSlice("attachment", nil, "File path(s) to attach (can be specified multiple times)")
|
||||||
|
|
||||||
// issue update
|
// issue update
|
||||||
issueUpdateCmd.Flags().String("title", "", "New title")
|
issueUpdateCmd.Flags().String("title", "", "New title")
|
||||||
|
|
@ -276,7 +277,13 @@ func runIssueCreate(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
// Use a longer timeout when attachments are present (file uploads can be slow).
|
||||||
|
timeout := 15 * time.Second
|
||||||
|
attachments, _ := cmd.Flags().GetStringSlice("attachment")
|
||||||
|
if len(attachments) > 0 {
|
||||||
|
timeout = 60 * time.Second
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
body := map[string]any{"title": title}
|
body := map[string]any{"title": title}
|
||||||
|
|
@ -309,6 +316,19 @@ func runIssueCreate(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("create issue: %w", err)
|
return fmt.Errorf("create issue: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload attachments and link them to the newly created issue.
|
||||||
|
issueID := strVal(result, "id")
|
||||||
|
for _, filePath := range attachments {
|
||||||
|
data, readErr := os.ReadFile(filePath)
|
||||||
|
if readErr != nil {
|
||||||
|
return fmt.Errorf("read attachment %s: %w", filePath, readErr)
|
||||||
|
}
|
||||||
|
if _, uploadErr := client.UploadFile(ctx, data, filePath, issueID); uploadErr != nil {
|
||||||
|
return fmt.Errorf("upload attachment %s: %w", filePath, uploadErr)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Uploaded %s\n", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output, _ := cmd.Flags().GetString("output")
|
||||||
if output == "table" {
|
if output == "table" {
|
||||||
headers := []string{"ID", "TITLE", "STATUS", "PRIORITY"}
|
headers := []string{"ID", "TITLE", "STATUS", "PRIORITY"}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,9 @@ func (h *Handler) CreateComment(w http.ResponseWriter, r *http.Request) {
|
||||||
h.linkAttachmentsByIDs(r.Context(), comment.ID, issue.ID, req.AttachmentIDs)
|
h.linkAttachmentsByIDs(r.Context(), comment.ID, issue.ID, req.AttachmentIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := commentToResponse(comment, nil, nil)
|
// Fetch linked attachments so the response includes them.
|
||||||
|
groupedAtt := h.groupAttachments(r, []pgtype.UUID{comment.ID})
|
||||||
|
resp := commentToResponse(comment, nil, groupedAtt[uuidToString(comment.ID)])
|
||||||
slog.Info("comment created", append(logger.RequestAttrs(r), "comment_id", uuidToString(comment.ID), "issue_id", issueID)...)
|
slog.Info("comment created", append(logger.RequestAttrs(r), "comment_id", uuidToString(comment.ID), "issue_id", issueID)...)
|
||||||
h.publish(protocol.EventCommentCreated, uuidToString(issue.WorkspaceID), authorType, authorID, map[string]any{
|
h.publish(protocol.EventCommentCreated, uuidToString(issue.WorkspaceID), authorType, authorID, map[string]any{
|
||||||
"comment": resp,
|
"comment": resp,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ type IssueResponse struct {
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
UpdatedAt string `json:"updated_at"`
|
UpdatedAt string `json:"updated_at"`
|
||||||
Reactions []IssueReactionResponse `json:"reactions,omitempty"`
|
Reactions []IssueReactionResponse `json:"reactions,omitempty"`
|
||||||
|
Attachments []AttachmentResponse `json:"attachments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type agentTriggerSnapshot struct {
|
type agentTriggerSnapshot struct {
|
||||||
|
|
@ -142,6 +143,18 @@ func (h *Handler) GetIssue(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch issue-level attachments.
|
||||||
|
attachments, err := h.Queries.ListAttachmentsByIssue(r.Context(), db.ListAttachmentsByIssueParams{
|
||||||
|
IssueID: issue.ID,
|
||||||
|
WorkspaceID: issue.WorkspaceID,
|
||||||
|
})
|
||||||
|
if err == nil && len(attachments) > 0 {
|
||||||
|
resp.Attachments = make([]AttachmentResponse, len(attachments))
|
||||||
|
for i, a := range attachments {
|
||||||
|
resp.Attachments[i] = h.attachmentToResponse(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeJSON(w, http.StatusOK, resp)
|
writeJSON(w, http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue