// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package task import ( "fmt" "time" "code.gitea.io/sdk/gitea" "code.gitea.io/tea/modules/context" ) // EditIssueOption wraps around gitea.EditIssueOption which has bad & incosistent semantics. type EditIssueOption struct { Index int64 Title *string Body *string Ref *string Milestone *string Deadline *time.Time AddLabels []string RemoveLabels []string AddAssignees []string // RemoveAssignees []string // NOTE: with the current go-sdk, clearing assignees is not possible. } // Normalizes the options into parameters that can be passed to the sdk. // the returned value will be nil, when no change to this part of the issue is requested. func (o EditIssueOption) toSdkOptions(ctx *context.TeaContext, client *gitea.Client) (*gitea.EditIssueOption, *gitea.IssueLabelsOption, *gitea.IssueLabelsOption, error) { // labels have a separate API call, so they get their own options. var addLabelOpts, rmLabelOpts *gitea.IssueLabelsOption if o.AddLabels != nil && len(o.AddLabels) != 0 { ids, err := ResolveLabelNames(client, ctx.Owner, ctx.Repo, o.AddLabels) if err != nil { return nil, nil, nil, err } addLabelOpts = &gitea.IssueLabelsOption{Labels: ids} } if o.RemoveLabels != nil && len(o.RemoveLabels) != 0 { ids, err := ResolveLabelNames(client, ctx.Owner, ctx.Repo, o.RemoveLabels) if err != nil { return nil, nil, nil, err } rmLabelOpts = &gitea.IssueLabelsOption{Labels: ids} } issueOpts := gitea.EditIssueOption{} var issueOptsDirty bool if o.Title != nil { issueOpts.Title = *o.Title issueOptsDirty = true } if o.Body != nil { issueOpts.Body = o.Body issueOptsDirty = true } if o.Ref != nil { issueOpts.Ref = o.Ref issueOptsDirty = true } if o.Milestone != nil { if *o.Milestone == "" { issueOpts.Milestone = gitea.OptionalInt64(0) } else { ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, *o.Milestone) if err != nil { return nil, nil, nil, fmt.Errorf("Milestone '%s' not found", *o.Milestone) } issueOpts.Milestone = &ms.ID } issueOptsDirty = true } if o.Deadline != nil { issueOpts.Deadline = o.Deadline issueOptsDirty = true if o.Deadline.IsZero() { issueOpts.RemoveDeadline = gitea.OptionalBool(true) } } if o.AddAssignees != nil && len(o.AddAssignees) != 0 { issueOpts.Assignees = o.AddAssignees issueOptsDirty = true } if issueOptsDirty { return &issueOpts, addLabelOpts, rmLabelOpts, nil } return nil, addLabelOpts, rmLabelOpts, nil } // EditIssue edits an issue and returns the updated issue. func EditIssue(ctx *context.TeaContext, client *gitea.Client, opts EditIssueOption) (*gitea.Issue, error) { if client == nil { client = ctx.Login.Client() } issueOpts, addLabelOpts, rmLabelOpts, err := opts.toSdkOptions(ctx, client) if err != nil { return nil, err } if rmLabelOpts != nil { // NOTE: as of 1.17, there is no API to remove multiple labels at once. for _, id := range rmLabelOpts.Labels { _, err := client.DeleteIssueLabel(ctx.Owner, ctx.Repo, opts.Index, id) if err != nil { return nil, fmt.Errorf("could not remove labels: %s", err) } } } if addLabelOpts != nil { _, _, err := client.AddIssueLabels(ctx.Owner, ctx.Repo, opts.Index, *addLabelOpts) if err != nil { return nil, fmt.Errorf("could not add labels: %s", err) } } var issue *gitea.Issue if issueOpts != nil { issue, _, err = client.EditIssue(ctx.Owner, ctx.Repo, opts.Index, *issueOpts) if err != nil { return nil, fmt.Errorf("could not edit issue: %s", err) } } else { issue, _, err = client.GetIssue(ctx.Owner, ctx.Repo, opts.Index) if err != nil { return nil, fmt.Errorf("could not get issue: %s", err) } } return issue, nil }