~emersion/hut

c096589b7ba247f939886649c5d58b8f1cc95fd4 — Simon Ser 2 months ago 7406ef3
todo ticket edit: new command
4 files changed, 150 insertions(+), 0 deletions(-)

M doc/hut.1.scd
M srht/todosrht/gql.go
M srht/todosrht/operations.graphql
M todo.go
M doc/hut.1.scd => doc/hut.1.scd +3 -0
@@ 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.


M srht/todosrht/gql.go => srht/todosrht/gql.go +35 -0
@@ 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
}

M srht/todosrht/operations.graphql => srht/todosrht/operations.graphql +34 -0
@@ 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
    }
}

M todo.go => todo.go +78 -0
@@ 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,83 @@ 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)
		}
		if err != nil {
			log.Fatal(err)
		} else if user == nil {
			log.Fatalf("no such user %q", username)
		} else if user.Tracker == nil {
			log.Fatalf("no such tracker %q", name)
		}

		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 edit 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) {