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))
|
start := Time(time.Now().AddDate(0, 0, -2))
|
||||||
deadline := 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{
|
task, err := c.CreateProjectTask(NewProjectTaskRequest{
|
||||||
ProjectId: *project.ID,
|
ProjectId: *project.ID,
|
||||||
Title: "integration parent task",
|
Title: "integration parent task",
|
||||||
@@ -146,6 +163,7 @@ func TestIntegrationProjectAndTaskLifecycle(t *testing.T) {
|
|||||||
StartDate: start,
|
StartDate: start,
|
||||||
Deadline: deadline,
|
Deadline: deadline,
|
||||||
Priority: int(TaskPriorityNormal),
|
Priority: int(TaskPriorityNormal),
|
||||||
|
MilestoneId: int(*ms.ID),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CreateProjectTask: %v", err)
|
t.Fatalf("CreateProjectTask: %v", err)
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ func TestProjectsAlias(t *testing.T) {
|
|||||||
if cmd.Name() != "projects" {
|
if cmd.Name() != "projects" {
|
||||||
t.Fatalf("prj alias resolved to %q", cmd.Name())
|
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) {
|
func TestNewOOReturnsErrorWithoutCredentials(t *testing.T) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
onlyoffice "github.com/eslider/go-onlyoffice"
|
onlyoffice "github.com/eslider/go-onlyoffice"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -22,6 +23,7 @@ func init() {
|
|||||||
projectsCmd.AddCommand(prjListCmd())
|
projectsCmd.AddCommand(prjListCmd())
|
||||||
projectsCmd.AddCommand(prjGetCmd())
|
projectsCmd.AddCommand(prjGetCmd())
|
||||||
projectsCmd.AddCommand(prjMilestonesCmd())
|
projectsCmd.AddCommand(prjMilestonesCmd())
|
||||||
|
projectsCmd.AddCommand(prjMilestoneCreateCmd())
|
||||||
projectsCmd.AddCommand(prjCreateCmd())
|
projectsCmd.AddCommand(prjCreateCmd())
|
||||||
projectsCmd.AddCommand(prjUpdateCmd())
|
projectsCmd.AddCommand(prjUpdateCmd())
|
||||||
projectsCmd.AddCommand(prjDeleteCmd())
|
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 {
|
func prjCreateCmd() *cobra.Command {
|
||||||
var desc, resp string
|
var desc, resp string
|
||||||
var country, company string
|
var country, company string
|
||||||
|
|||||||
+55
-3
@@ -2,7 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
onlyoffice "github.com/eslider/go-onlyoffice"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -82,11 +86,12 @@ func taskGetCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func taskCreateCmd() *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{
|
cmd := &cobra.Command{
|
||||||
Use: "create TITLE",
|
Use: "create TITLE",
|
||||||
Aliases: []string{"add"},
|
Aliases: []string{"add"},
|
||||||
Short: "Create a project task",
|
Short: "Create a project task (Gantt bar when --start/--deadline/--milestone set)",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
c, err := newOO(cmd)
|
c, err := newOO(cmd)
|
||||||
@@ -100,6 +105,51 @@ func taskCreateCmd() *cobra.Command {
|
|||||||
case "low":
|
case "low":
|
||||||
p = -1
|
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)
|
out, err := c.AddTask(cmd.Context(), project, args[0], desc, p, deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -110,7 +160,9 @@ func taskCreateCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
cmd.Flags().StringVarP(&project, "project", "p", "", "project id (default $OO_PROJECT_ID)")
|
cmd.Flags().StringVarP(&project, "project", "p", "", "project id (default $OO_PROJECT_ID)")
|
||||||
cmd.Flags().StringVar(&desc, "description", "", "description")
|
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")
|
cmd.Flags().StringVar(&prio, "priority", "normal", "high|normal|low")
|
||||||
return cmd
|
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)))
|
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.
|
// GetProjectMilestones returns milestones for the given project.
|
||||||
// https://api1.onlyoffice.com/portals/method/project/post/api/2.0/project/%7bid%7d/milestone
|
// https://api1.onlyoffice.com/portals/method/project/post/api/2.0/project/%7bid%7d/milestone
|
||||||
func (c *Client) GetProjectMilestones(project *Project) ([]*Milestone, error) {
|
func (c *Client) GetProjectMilestones(project *Project) ([]*Milestone, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user