Add milestone create and task start dates so Gantt bars can exist.
CLI: oo projects milestone-create, oo tasks create --start --milestone.
This commit is contained in:
@@ -139,6 +139,23 @@ func TestIntegrationProjectAndTaskLifecycle(t *testing.T) {
|
||||
|
||||
start := Time(time.Now().AddDate(0, 0, -2))
|
||||
deadline := Time(time.Now().AddDate(0, 0, 2))
|
||||
ms, err := c.CreateMilestone(NewMilestoneRequest{
|
||||
ProjectID: *project.ID,
|
||||
Title: "integration milestone",
|
||||
Deadline: deadline,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateMilestone: %v", err)
|
||||
}
|
||||
if ms == nil || ms.ID == nil {
|
||||
t.Fatal("created milestone without id")
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := c.DeleteMilestone(*ms.ID); err != nil {
|
||||
t.Logf("cleanup DeleteMilestone: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
task, err := c.CreateProjectTask(NewProjectTaskRequest{
|
||||
ProjectId: *project.ID,
|
||||
Title: "integration parent task",
|
||||
@@ -146,6 +163,7 @@ func TestIntegrationProjectAndTaskLifecycle(t *testing.T) {
|
||||
StartDate: start,
|
||||
Deadline: deadline,
|
||||
Priority: int(TaskPriorityNormal),
|
||||
MilestoneId: int(*ms.ID),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateProjectTask: %v", err)
|
||||
|
||||
@@ -53,6 +53,13 @@ func TestProjectsAlias(t *testing.T) {
|
||||
if cmd.Name() != "projects" {
|
||||
t.Fatalf("prj alias resolved to %q", cmd.Name())
|
||||
}
|
||||
ms, _, err := rootCmd.Find([]string{"projects", "milestone-create"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ms.Name() != "milestone-create" {
|
||||
t.Fatalf("milestone-create resolved to %q", ms.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewOOReturnsErrorWithoutCredentials(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
onlyoffice "github.com/eslider/go-onlyoffice"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -22,6 +23,7 @@ func init() {
|
||||
projectsCmd.AddCommand(prjListCmd())
|
||||
projectsCmd.AddCommand(prjGetCmd())
|
||||
projectsCmd.AddCommand(prjMilestonesCmd())
|
||||
projectsCmd.AddCommand(prjMilestoneCreateCmd())
|
||||
projectsCmd.AddCommand(prjCreateCmd())
|
||||
projectsCmd.AddCommand(prjUpdateCmd())
|
||||
projectsCmd.AddCommand(prjDeleteCmd())
|
||||
@@ -118,6 +120,52 @@ func prjMilestonesCmd() *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func prjMilestoneCreateCmd() *cobra.Command {
|
||||
var deadline, desc string
|
||||
var key bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "milestone-create PROJECT_ID TITLE",
|
||||
Short: "Create a project milestone (Gantt row)",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := newOO(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pid, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("project id must be integer: %w", err)
|
||||
}
|
||||
if deadline == "" {
|
||||
return fmt.Errorf("--deadline YYYY-MM-DD is required")
|
||||
}
|
||||
day, err := time.Parse("2006-01-02", deadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deadline: %w", err)
|
||||
}
|
||||
ms, err := c.CreateMilestone(onlyoffice.NewMilestoneRequest{
|
||||
ProjectID: pid,
|
||||
Title: args[1],
|
||||
Deadline: onlyoffice.Time(day),
|
||||
Description: desc,
|
||||
IsKey: key,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(map[string]any{
|
||||
"id": derefInt64(ms.ID),
|
||||
"title": derefString(ms.Title),
|
||||
})
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&deadline, "deadline", "", "deadline YYYY-MM-DD")
|
||||
cmd.Flags().StringVar(&desc, "description", "", "description")
|
||||
cmd.Flags().BoolVar(&key, "key", false, "mark as key milestone")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func prjCreateCmd() *cobra.Command {
|
||||
var desc, resp string
|
||||
var country, company string
|
||||
|
||||
+55
-3
@@ -2,7 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
onlyoffice "github.com/eslider/go-onlyoffice"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -82,11 +86,12 @@ func taskGetCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func taskCreateCmd() *cobra.Command {
|
||||
var project, desc, deadline, prio string
|
||||
var project, desc, deadline, start, prio string
|
||||
var milestone int64
|
||||
cmd := &cobra.Command{
|
||||
Use: "create TITLE",
|
||||
Aliases: []string{"add"},
|
||||
Short: "Create a project task",
|
||||
Short: "Create a project task (Gantt bar when --start/--deadline/--milestone set)",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c, err := newOO(cmd)
|
||||
@@ -100,6 +105,51 @@ func taskCreateCmd() *cobra.Command {
|
||||
case "low":
|
||||
p = -1
|
||||
}
|
||||
if start != "" || milestone != 0 {
|
||||
pid, err := strconv.Atoi(project)
|
||||
if err != nil && project != "" {
|
||||
return fmt.Errorf("project id: %w", err)
|
||||
}
|
||||
if project == "" {
|
||||
pid, err = strconv.Atoi(os.Getenv("OO_PROJECT_ID"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("--project or OO_PROJECT_ID required")
|
||||
}
|
||||
}
|
||||
req := onlyoffice.NewProjectTaskRequest{
|
||||
ProjectId: pid,
|
||||
Title: args[0],
|
||||
Description: desc,
|
||||
Priority: p,
|
||||
MilestoneId: int(milestone),
|
||||
}
|
||||
if deadline == "" {
|
||||
deadline = time.Now().Add(14 * 24 * time.Hour).Format("2006-01-02")
|
||||
}
|
||||
d, err := time.Parse("2006-01-02", deadline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deadline: %w", err)
|
||||
}
|
||||
req.Deadline = onlyoffice.Time(d)
|
||||
if start != "" {
|
||||
s, err := time.Parse("2006-01-02", start)
|
||||
if err != nil {
|
||||
return fmt.Errorf("start: %w", err)
|
||||
}
|
||||
req.StartDate = onlyoffice.Time(s)
|
||||
} else {
|
||||
req.StartDate = onlyoffice.Time(time.Now())
|
||||
}
|
||||
task, err := c.CreateProjectTask(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(map[string]any{
|
||||
"id": derefInt(task.ID),
|
||||
"title": derefString(task.Title),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
out, err := c.AddTask(cmd.Context(), project, args[0], desc, p, deadline)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -110,7 +160,9 @@ func taskCreateCmd() *cobra.Command {
|
||||
}
|
||||
cmd.Flags().StringVarP(&project, "project", "p", "", "project id (default $OO_PROJECT_ID)")
|
||||
cmd.Flags().StringVar(&desc, "description", "", "description")
|
||||
cmd.Flags().StringVar(&deadline, "deadline", "", "deadline YYYY-MM-DD (default now+14d; always assigned to you)")
|
||||
cmd.Flags().StringVar(&deadline, "deadline", "", "deadline YYYY-MM-DD (default now+14d)")
|
||||
cmd.Flags().StringVar(&start, "start", "", "start YYYY-MM-DD (uses CreateProjectTask / Gantt)")
|
||||
cmd.Flags().Int64Var(&milestone, "milestone", 0, "milestone id (Gantt row)")
|
||||
cmd.Flags().StringVar(&prio, "priority", "normal", "high|normal|low")
|
||||
return cmd
|
||||
}
|
||||
|
||||
+45
@@ -134,6 +134,51 @@ func (c *Client) GetProjectByID(ctx context.Context, projectID string) (map[stri
|
||||
return c.ResponseObject(ctx, fmt.Sprintf("/api/2.0/project/%s.json", url.PathEscape(projectID)))
|
||||
}
|
||||
|
||||
// NewMilestoneRequest is the payload for CreateMilestone.
|
||||
type NewMilestoneRequest struct {
|
||||
ProjectID int `json:"-"`
|
||||
Title string `json:"title"`
|
||||
Deadline Time `json:"deadline"`
|
||||
Description string `json:"description,omitempty"`
|
||||
IsKey bool `json:"isKey"`
|
||||
IsNotify bool `json:"isNotify"`
|
||||
Responsible string `json:"responsible,omitempty"`
|
||||
}
|
||||
|
||||
// CreateMilestone creates a project milestone (Gantt row).
|
||||
// POST /api/2.0/project/{id}/milestone
|
||||
func (c *Client) CreateMilestone(req NewMilestoneRequest) (*Milestone, error) {
|
||||
if req.Responsible == "" {
|
||||
uid, err := c.SelfUserID(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CreateMilestone: self: %w", err)
|
||||
}
|
||||
req.Responsible = uid
|
||||
}
|
||||
ms := new(Milestone)
|
||||
err := c.Query(Request{
|
||||
Uri: fmt.Sprintf("/api/2.0/project/%d/milestone", req.ProjectID),
|
||||
Method: "POST",
|
||||
Body: req,
|
||||
}, &struct {
|
||||
MetaResponse `json:",inline"`
|
||||
Response *Milestone `json:"response"`
|
||||
}{Response: ms})
|
||||
return ms, err
|
||||
}
|
||||
|
||||
// DeleteMilestone removes a milestone by id.
|
||||
// DELETE /api/2.0/project/milestone/{id}
|
||||
func (c *Client) DeleteMilestone(id int64) error {
|
||||
return c.Query(Request{
|
||||
Uri: fmt.Sprintf("/api/2.0/project/milestone/%d", id),
|
||||
Method: "DELETE",
|
||||
}, &struct {
|
||||
MetaResponse `json:",inline"`
|
||||
Response *Milestone `json:"response"`
|
||||
}{})
|
||||
}
|
||||
|
||||
// GetProjectMilestones returns milestones for the given project.
|
||||
// https://api1.onlyoffice.com/portals/method/project/post/api/2.0/project/%7bid%7d/milestone
|
||||
func (c *Client) GetProjectMilestones(project *Project) ([]*Milestone, error) {
|
||||
|
||||
Reference in New Issue
Block a user