Skip to content

Commit

Permalink
Merge pull request #3 from faktas2/master
Browse files Browse the repository at this point in the history
Adding Accounts Service
  • Loading branch information
oschwald authored Jul 28, 2023
2 parents bb64636 + d4795c5 commit d696793
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 16 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/oschwald/go-pivotaltracker

go 1.20
41 changes: 41 additions & 0 deletions v5/pivotal/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package pivotal

import (
"fmt"
"net/http"
"time"
)

type Account struct {
CreatedAt *time.Time `json:"created_at"`
ID int `json:"id"`
Kind string `json:"kind"`
Name string `json:"name"`
Plan string `json:"plan"`
Status string `json:"status"`
UpdatedAt *time.Time `json:"updated_at"`
}

type AccountsService struct {
client *Client
}

func newAccountsService(client *Client) *AccountsService {
return &AccountsService{client}
}

func (service *AccountsService) Get(accountID int) (*Account, *http.Response, error) {
u := fmt.Sprintf("accounts/%d", accountID)
req, err := service.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}

var account Account
resp, err := service.client.Do(req, &account)
if err != nil {
return nil, resp, err
}

return &account, resp, nil
}
4 changes: 4 additions & 0 deletions v5/pivotal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type Client struct {
// User-Agent header to use when connecting to the Pivotal Tracker API.
userAgent string

// Accounts service
Accounts *AccountsService

// Me service
Me *MeService

Expand Down Expand Up @@ -72,6 +75,7 @@ func NewClient(apiToken string) *Client {
baseURL: baseURL,
userAgent: defaultUserAgent,
}
client.Accounts = newAccountsService(client)
client.Me = newMeService(client)
client.Projects = newProjectService(client)
client.Stories = newStoryService(client)
Expand Down
45 changes: 32 additions & 13 deletions v5/pivotal/me.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,40 @@ import (
"time"
)

// MeProject represents the fields of the projects returned by the MeService.
// These are the only fields returned by the MeService unlike
// the Project service.
type MeProject struct {
Kind string `json:"kind"`
ID int `json:"id"`
ProjectID int `json:"project_id"`
ProjectName string `json:"project_name"`
ProjectColor string `json:"project_color"`
Favorite bool `json:"favorite"`
Role string `json:"owner"`
LastViewedAt *time.Time `json:"last_viewed_at"`
}

// Me is the primary data object for the MeService.
type Me struct {
ID int `json:"id"`
Name string `json:"name"`
Initials string `json:"initials"`
Username string `json:"username"`
TimeZone *TimeZone `json:"time_zone"`
APIToken string `json:"api_token"`
HasGoogleIdentity bool `json:"has_google_identity"`
ProjectIDs *[]int `json:"project_ids"`
WorkspaceIDs *[]int `json:"workspace_ids"`
Email string `json:"email"`
ReceivedInAppNotifications bool `json:"receives_in_app_notifications"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
ID int `json:"id"`
Name string `json:"name"`
Initials string `json:"initials"`
Username string `json:"username"`
TimeZone *TimeZone `json:"time_zone"`
APIToken string `json:"api_token"`
HasGoogleIdentity bool `json:"has_google_identity"`

// TODO: The ProjectIDs field needs to be requested explicitly using
// the fields query parameter. It is never populated unlike Projects,
// which is populated by default.
ProjectIDs *[]int `json:"project_ids"`
Projects *[]MeProject `json:"projects"`
WorkspaceIDs *[]int `json:"workspace_ids"`
Email string `json:"email"`
ReceivedInAppNotifications bool `json:"receives_in_app_notifications"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
}

// MeService wraps the client context for interacting with the Me logic.
Expand Down
56 changes: 53 additions & 3 deletions v5/pivotal/stories.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"io"
"net/http"
"net/url"
"strings"
"time"
)

Expand Down Expand Up @@ -59,6 +60,7 @@ type Story struct {
AcceptedAt *time.Time `json:"accepted_at,omitempty"`
Deadline *time.Time `json:"deadline,omitempty"`
RequestedByID int `json:"requested_by_id,omitempty"`
RequestedBy Person `json:"requested_by,omitempty"`
OwnerIDs []int `json:"owner_ids,omitempty"`
LabelIDs []int `json:"label_ids,omitempty"`
Labels []*Label `json:"labels,omitempty"`
Expand Down Expand Up @@ -198,7 +200,7 @@ func newStoryService(client *Client) *StoryService {
// is not always sorted when using a filter, this approach is required to get
// the right data. Not sure whether this is a bug or a feature.
func (service *StoryService) List(projectID int, filter string) ([]*Story, error) {
reqFunc := newStoriesRequestFunc(service.client, projectID, filter)
reqFunc := newStoriesRequestFunc(service.client, projectID, filter, nil)
cursor, err := newCursor(service.client, reqFunc, 0)
if err != nil {
return nil, err
Expand All @@ -211,17 +213,65 @@ func (service *StoryService) List(projectID int, filter string) ([]*Story, error
return stories, nil
}

func newStoriesRequestFunc(client *Client, projectID int, filter string) func() *http.Request {
func newStoriesRequestFunc(client *Client, projectID int, filter string, fields []string) func() *http.Request {
return func() *http.Request {
u := fmt.Sprintf("projects/%v/stories", projectID)
if filter != "" {
u += "?filter=" + url.QueryEscape(filter)
if len(fields) != 0 {
u += "&fields=" + url.QueryEscape(fieldsToQuery(fields))
}
}
req, _ := client.NewRequest("GET", u, nil)
return req
}
}

var DefaultFields = []string{
"id",
"project_id",
"name",
"description",
"story_type",
"current_state",
"estimate",
"accepted_at",
"deadline",
"requested_by_id",
"owner_ids",
"task_ids",
"follower_ids",
"created_at",
"updated_at",
"url",
"kind",
}

// function returns the fields in a query string format.
func fieldsToQuery(fields []string) string {
if len(fields) == 0 {
fields = DefaultFields
}
return strings.Join(fields, ",")
}

// ListOnlyFields returns all the fields of the stories matching the filter given.
// Example: fields = []string{"id", "name", "description"}
// Having nil or empty fields will return all the fields.
func (service *StoryService) ListWithFields(projectID int, filter string, fields []string) ([]*Story, error) {
reqFunc := newStoriesRequestFunc(service.client, projectID, filter, fields)
cursor, err := newCursor(service.client, reqFunc, 0)
if err != nil {
return nil, err
}

var stories []*Story
if err := cursor.all(&stories); err != nil {
return nil, err
}
return stories, nil
}

// StoryCursor is used to implement the iterator pattern.
type StoryCursor struct {
*cursor
Expand Down Expand Up @@ -250,7 +300,7 @@ func (c *StoryCursor) Next() (s *Story, err error) {
// Iterate returns a cursor that can be used to iterate over the stories specified
// by the filter. More stories are fetched on demand as needed.
func (service *StoryService) Iterate(projectID int, filter string) (c *StoryCursor, err error) {
reqFunc := newStoriesRequestFunc(service.client, projectID, filter)
reqFunc := newStoriesRequestFunc(service.client, projectID, filter, nil)
cursor, err := newCursor(service.client, reqFunc, PageLimit)
if err != nil {
return nil, err
Expand Down

0 comments on commit d696793

Please sign in to comment.