package gitlab import ( "context" "errors" "fmt" "git.awesome-for.me/liuzhiguo/go-scm/scm/driver/internal/null" "net/url" "time" "git.awesome-for.me/liuzhiguo/go-scm/scm" ) type milestoneService struct { client *wrapper } // isoTime represents an ISO 8601 formatted date type isoTime time.Time // ISO 8601 date format const iso8601 = "2006-01-02" // MarshalJSON implements the json.Marshaler interface func (t isoTime) MarshalJSON() ([]byte, error) { if y := time.Time(t).Year(); y < 0 || y >= 10000 { // ISO 8901 uses 4 digits for the years return nil, errors.New("json: ISOTime year outside of range [0,9999]") } b := make([]byte, 0, len(iso8601)+2) b = append(b, '"') b = time.Time(t).AppendFormat(b, iso8601) b = append(b, '"') return b, nil } // UnmarshalJSON implements the json.Unmarshaler interface func (t *isoTime) UnmarshalJSON(data []byte) error { // Ignore null, like in the main JSON package if string(data) == "null" { return nil } isotime, err := time.Parse(`"`+iso8601+`"`, string(data)) *t = isoTime(isotime) return err } // EncodeValues implements the query.Encoder interface func (t *isoTime) EncodeValues(key string, v *url.Values) error { if t == nil || (time.Time(*t)).IsZero() { return nil } v.Add(key, t.String()) return nil } // String implements the Stringer interface func (t isoTime) String() string { return time.Time(t).Format(iso8601) } type milestone struct { ID int `json:"id"` IID int `json:"iid"` ProjectID int `json:"project_id"` Title string `json:"title"` Description string `json:"description"` State string `json:"state"` DueDate isoTime `json:"due_date"` StartDate isoTime `json:"start_date"` CreatedAt null.Time `json:"created_at"` UpdatedAt null.Time `json:"updated_at"` Expired bool `json:"expired"` } type milestoneInput struct { Title *string `json:"title"` StateEvent *string `json:"state_event,omitempty"` Description *string `json:"description"` DueDate isoTime `json:"due_date"` } func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) { path := fmt.Sprintf("api/v4/projects/%s/milestones/%d", encode(repo), id) out := new(milestone) res, err := s.client.do(ctx, "GET", path, nil, out) return convertMilestone(out), res, err } func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) { path := fmt.Sprintf("api/v4/projects/%s/milestones?%s", encode(repo), encodeMilestoneListOptions(opts)) out := []*milestone{} res, err := s.client.do(ctx, "GET", path, nil, &out) return convertMilestoneList(out), res, err } func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) { path := fmt.Sprintf("api/v4/projects/%s/milestones", encode(repo)) dueDateIso := isoTime(input.DueDate) in := &milestoneInput{ Title: &input.Title, Description: &input.Description, DueDate: dueDateIso, } out := new(milestone) res, err := s.client.do(ctx, "POST", path, in, out) return convertMilestone(out), res, err } func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) { path := fmt.Sprintf("api/v4/projects/%s/milestones/%d", encode(repo), id) res, err := s.client.do(ctx, "DELETE", path, nil, nil) return res, err } func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) { path := fmt.Sprintf("api/v4/projects/%s/milestones/%d", encode(repo), id) in := &milestoneInput{} if input.Title != "" { in.Title = &input.Title } if input.State != "" { if input.State == "open" { activate := "activate" in.StateEvent = &activate } else { in.StateEvent = &input.State } } if input.Description != "" { in.Description = &input.Description } if !input.DueDate.IsZero() { dueDateIso := isoTime(input.DueDate) in.DueDate = dueDateIso } out := new(milestone) res, err := s.client.do(ctx, "PATCH", path, in, out) return convertMilestone(out), res, err } func convertMilestoneList(from []*milestone) []*scm.Milestone { var to []*scm.Milestone for _, m := range from { to = append(to, convertMilestone(m)) } return to } func convertMilestone(from *milestone) *scm.Milestone { if from == nil || from.Title == "" { return nil } dueDate := time.Time(from.DueDate) return &scm.Milestone{ Number: from.ID, ID: from.ID, Title: from.Title, Description: from.Description, State: from.State, DueDate: dueDate, } }