~emersion/hut-dev

todo ticket edit: new command v1 APPLIED

Simon Ser: 1
 todo ticket edit: new command

 4 files changed, 142 insertions(+), 0 deletions(-)
Thanks for the fixups!
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/hut-dev/patches/48744/mbox | git am -3
Learn more about email & git

[PATCH] todo ticket edit: new command Export this patch

---
 doc/hut.1.scd                    |  3 ++
 srht/todosrht/gql.go             | 35 ++++++++++++++++
 srht/todosrht/operations.graphql | 34 ++++++++++++++++
 todo.go                          | 70 ++++++++++++++++++++++++++++++++
 4 files changed, 142 insertions(+)

diff --git a/doc/hut.1.scd b/doc/hut.1.scd
index 80a49a9fcbdf..0e909428411c 100644
--- a/doc/hut.1.scd
+++ b/doc/hut.1.scd
@@ -744,6 +744,9 @@ Options are:
	*-y*, *--yes*
		Confirm deletion without prompt.

*ticket edit* <ID>
	Edit a ticket with _$EDITOR_.

*ticket label* <ID> [options...]
	Add a label to a ticket.

diff --git a/srht/todosrht/gql.go b/srht/todosrht/gql.go
index 8d2cf5dac345..d5b5a139e3b7 100644
--- a/srht/todosrht/gql.go
+++ b/srht/todosrht/gql.go
@@ -1218,6 +1218,29 @@ func TicketByUser(client *gqlclient.Client, ctx context.Context, username string
	return respData.User, err
}

func TicketBodyByName(client *gqlclient.Client, ctx context.Context, name string, id int32) (me *User, err error) {
	op := gqlclient.NewOperation("query ticketBodyByName ($name: String!, $id: Int!) {\n\tme {\n\t\ttracker(name: $name) {\n\t\t\tid\n\t\t\tticket(id: $id) {\n\t\t\t\t... ticketBody\n\t\t\t}\n\t\t}\n\t}\n}\nfragment ticketBody on Ticket {\n\tid\n\tsubject\n\tbody\n}\n")
	op.Var("name", name)
	op.Var("id", id)
	var respData struct {
		Me *User
	}
	err = client.Execute(ctx, op, &respData)
	return respData.Me, err
}

func TicketBodyByUser(client *gqlclient.Client, ctx context.Context, username string, tracker string, id int32) (user *User, err error) {
	op := gqlclient.NewOperation("query ticketBodyByUser ($username: String!, $tracker: String!, $id: Int!) {\n\tuser(username: $username) {\n\t\ttracker(name: $tracker) {\n\t\t\tid\n\t\t\tticket(id: $id) {\n\t\t\t\t... ticketBody\n\t\t\t}\n\t\t}\n\t}\n}\nfragment ticketBody on Ticket {\n\tid\n\tsubject\n\tbody\n}\n")
	op.Var("username", username)
	op.Var("tracker", tracker)
	op.Var("id", id)
	var respData struct {
		User *User
	}
	err = client.Execute(ctx, op, &respData)
	return respData.User, err
}

func LabelIDByName(client *gqlclient.Client, ctx context.Context, trackerName string, labelName string) (me *User, err error) {
	op := gqlclient.NewOperation("query labelIDByName ($trackerName: String!, $labelName: String!) {\n\tme {\n\t\ttracker(name: $trackerName) {\n\t\t\tlabel(name: $labelName) {\n\t\t\t\tid\n\t\t\t}\n\t\t}\n\t}\n}\n")
	op.Var("trackerName", trackerName)
@@ -1528,3 +1551,15 @@ func UnlabelTicket(client *gqlclient.Client, ctx context.Context, trackerId int3
	err = client.Execute(ctx, op, &respData)
	return respData.UnlabelTicket, err
}

func UpdateTicket(client *gqlclient.Client, ctx context.Context, trackerId int32, ticketId int32, input UpdateTicketInput) (updateTicket *Ticket, err error) {
	op := gqlclient.NewOperation("mutation updateTicket ($trackerId: Int!, $ticketId: Int!, $input: UpdateTicketInput!) {\n\tupdateTicket(trackerId: $trackerId, ticketId: $ticketId, input: $input) {\n\t\tid\n\t}\n}\n")
	op.Var("trackerId", trackerId)
	op.Var("ticketId", ticketId)
	op.Var("input", input)
	var respData struct {
		UpdateTicket *Ticket
	}
	err = client.Execute(ctx, op, &respData)
	return respData.UpdateTicket, err
}
diff --git a/srht/todosrht/operations.graphql b/srht/todosrht/operations.graphql
index 5ef233ce7dd4..1765a9778fda 100644
--- a/srht/todosrht/operations.graphql
+++ b/srht/todosrht/operations.graphql
@@ -418,6 +418,34 @@ fragment ticket on Ticket {
    }
}

query ticketBodyByName($name: String!, $id: Int!) {
    me {
        tracker(name: $name) {
            id
            ticket(id: $id) {
                ...ticketBody
            }
        }
    }
}

query ticketBodyByUser($username: String!, $tracker: String!, $id: Int!) {
    user(username: $username) {
        tracker(name: $tracker) {
            id
            ticket(id: $id) {
                ...ticketBody
            }
        }
    }
}

fragment ticketBody on Ticket {
    id
    subject
    body
}

query labelIDByName($trackerName: String!, $labelName: String!) {
    me {
        tracker(name: $trackerName) {
@@ -695,3 +723,9 @@ mutation unlabelTicket($trackerId: Int!, $ticketId: Int!, $labelId: Int!) {
        }
    }
}

mutation updateTicket($trackerId: Int!, $ticketId: Int!, $input: UpdateTicketInput!) {
    updateTicket(trackerId: $trackerId, ticketId: $ticketId, input: $input) {
        id
    }
}
diff --git a/todo.go b/todo.go
index 0c1f56806930..0cb26dede3bc 100644
--- a/todo.go
+++ b/todo.go
@@ -274,6 +274,7 @@ func newTodoTicketCommand() *cobra.Command {
	cmd.AddCommand(newTodoTicketShowCommand())
	cmd.AddCommand(newTodoTicketWebhookCommand())
	cmd.AddCommand(newTodoTicketCreateCommand())
	cmd.AddCommand(newTodoTicketEditCommand())
	cmd.AddCommand(newTodoTicketLabelCommand())
	cmd.AddCommand(newTodoTicketUnlabelCommand())
	return cmd
@@ -1540,6 +1541,75 @@ func newTodoTicketCreateCommand() *cobra.Command {
	return cmd
}

func newTodoTicketEditCommand() *cobra.Command {
	run := func(cmd *cobra.Command, args []string) {
		ctx := cmd.Context()

		ticketID, name, owner, instance, err := parseTicketResource(ctx, cmd, args[0])
		if err != nil {
			log.Fatal(err)
		}

		c := createClientWithInstance("todo", cmd, instance)
		var (
			user     *todosrht.User
			username string
		)

		if owner != "" {
			username = strings.TrimLeft(owner, ownerPrefixes)
			user, err = todosrht.TicketBodyByUser(c.Client, ctx, username, name, ticketID)
		} else {
			user, err = todosrht.TicketBodyByName(c.Client, ctx, name, ticketID)
		}
		tracker := user.Tracker
		ticket := tracker.Ticket

		prefill := ticket.Subject + "\n\n"
		if ticket.Body != nil {
			prefill += *ticket.Body
		}
		text, err := getInputWithEditor("hut_ticket*.md", prefill)
		if err != nil {
			log.Fatalf("failed to read ticket subject and description: %v", err)
		}

		parts := strings.SplitN(text, "\n", 2)
		subject := strings.TrimSpace(parts[0])
		var body string
		if len(parts) > 1 {
			body = strings.TrimSpace(parts[1])
		}

		if subject == "" {
			log.Println("Aborting due to empty subject.")
			os.Exit(1)
		}

		input := todosrht.UpdateTicketInput{
			Subject: &subject,
			Body:    &body,
		}
		ticket, err = todosrht.UpdateTicket(c.Client, ctx, tracker.Id, ticket.Id, input)
		if err != nil {
			log.Fatal(err)
		} else if ticket == nil {
			log.Fatal("failed to create ticket")
		}

		log.Printf("Updated ticket %v\n", termfmt.DarkYellow.Sprintf("#%v", ticket.Id))
	}

	cmd := &cobra.Command{
		Use:               "edit <ID>",
		Short:             "Edit a ticket",
		Args:              cobra.ExactArgs(1),
		ValidArgsFunction: completeTicketID,
		Run:               run,
	}
	return cmd
}

func newTodoTicketLabelCommand() *cobra.Command {
	var labelName string
	run := func(cmd *cobra.Command, args []string) {

base-commit: 499ef88754326222b1567261ac6b39b9f2a586ed
-- 
2.43.0
I pushed this with slightly improved error handling, thanks!