Skip to content

Commit

Permalink
Convert user value type to complex type with timezone
Browse files Browse the repository at this point in the history
  • Loading branch information
berlam committed Oct 4, 2019
1 parent 52b111b commit f3a874f
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 117 deletions.
8 changes: 5 additions & 3 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ func Projects(projects []string) []pkg.Project {
return result
}

func Users(users []string) []pkg.User {
result := make([]pkg.User, len(users))
func Users(users []string) []*pkg.User {
result := make([]*pkg.User, len(users))
for i, user := range users {
result[i] = pkg.User(user)
result[i] = &pkg.User{
DisplayName: user,
}
}
return result
}
Expand Down
12 changes: 7 additions & 5 deletions pkg/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ func (ts Timesheet) ReadCsv(data []byte, spec *CsvSpecification) (Timesheet, err

effort := Effort{}
if spec.user.enabled {
effort.User = User(row[spec.user.index])
effort.User = &User{
DisplayName: row[spec.user.index],
}
}
if spec.project.enabled {
effort.Project = Project(row[spec.project.index])
Expand Down Expand Up @@ -191,7 +193,7 @@ func (ts Timesheet) WriteCsv(writer io.Writer, spec *CsvSpecification, printEmpt
currentUser := ts[0].User
currentDate := time.Date(ts[0].Date.Year(), time.Month(ts[0].Date.Month()), 1, 0, 0, 0, 0, time.UTC)
for _, effort := range ts {
if currentUser != effort.User {
if currentUser.DisplayName != effort.User.DisplayName {
if currentDate.Day() > 1 {
emptyLinesForDaysBetween(csvw, spec, currentDate, currentDate.AddDate(0, 1, 1-currentDate.Day()), currentUser)
}
Expand All @@ -204,7 +206,7 @@ func (ts Timesheet) WriteCsv(writer io.Writer, spec *CsvSpecification, printEmpt
}

if spec.user.enabled {
result[spec.user.index] = string(effort.User)
result[spec.user.index] = effort.User.DisplayName
}
if spec.project.enabled {
result[spec.project.index] = string(effort.Project)
Expand Down Expand Up @@ -233,10 +235,10 @@ func (ts Timesheet) WriteCsv(writer io.Writer, spec *CsvSpecification, printEmpt
csvw.Flush()
}

func emptyLinesForDaysBetween(csvw *csv.Writer, spec *CsvSpecification, from, to time.Time, user User) {
func emptyLinesForDaysBetween(csvw *csv.Writer, spec *CsvSpecification, from, to time.Time, user *User) {
result := make([]string, spec.fields)
if spec.user.enabled {
result[spec.user.index] = string(user)
result[spec.user.index] = user.DisplayName
}
if spec.duration.enabled {
var duration time.Duration
Expand Down
5 changes: 4 additions & 1 deletion pkg/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import (
"io"
"net/http"
"net/url"
"time"
)

func NewHttpClient() *http.Client {
return &http.Client{}
return &http.Client{
Timeout: time.Second * 60,
}
}

func CreateJsonRequest(client *http.Client, httpMethod string, server *url.URL, userinfo *url.Userinfo, payload io.Reader) (*http.Response, error) {
Expand Down
11 changes: 8 additions & 3 deletions pkg/jira/cloud/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package cloud
import (
"eager/pkg"
"eager/pkg/jira/model"
v2 "eager/pkg/jira/v2"
"eager/pkg/jira/v2"
"net/http"
"net/url"
"time"
)

const (
Expand Down Expand Up @@ -35,11 +36,15 @@ func (api Api) Projects(startAt int) ([]pkg.Project, error) {
return api.previousVersion().Projects(startAt)
}

func (api Api) User(user pkg.User, projects []pkg.Project) (model.Account, error) {
func (api Api) Me() (model.Account, *time.Location, error) {
return api.previousVersion().Me()
}

func (api Api) User(user *pkg.User, projects []pkg.Project) (model.Account, *time.Location, error) {
return api.previousVersion().User(user, projects)
}

func (api Api) Issues(jql model.Jql, startAt int) (model.Account, []model.Issue, error) {
func (api Api) Issues(jql model.Jql, startAt int) ([]model.Issue, error) {
return api.previousVersion().Issues(jql, startAt)
}

Expand Down
13 changes: 9 additions & 4 deletions pkg/jira/cloud/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/url"
"os"
"testing"
"time"
)

func Test(t *testing.T) {
Expand All @@ -26,13 +27,17 @@ func Test(t *testing.T) {
t.Error("Project not found")
return
}
user, e := api.User(pkg.User("Berla Atlassian Test User 2"), projects)
if e != nil || user == "" {
user := &pkg.User{
DisplayName: "Berla Atlassian Test User 2",
TimeZone: time.UTC,
}
account, _, e := api.User(user, projects)
if e != nil || account == "" {
t.Error("User not found")
return
}
jql := model.Jql{}.Users(pkg.User(user)).Projects(projects...)
_, issues, e := api.Issues(jql, 0)
jql := model.Jql{}.Users(account).Projects(projects...)
issues, e := api.Issues(jql, 0)
if e != nil || len(issues) == 0 {
t.Error("Issues not found")
return
Expand Down
87 changes: 43 additions & 44 deletions pkg/jira/jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,49 +69,42 @@ func GetTimesheet(client *http.Client, server *url.URL, userinfo *url.Userinfo,
log.Println("Could not get api version.", err)
return pkg.Timesheet{}
}
fromDate, toDate := pkg.GetTimeRange(year, month)

jql := new(model.Jql).Between(fromDate, toDate).Me().Projects(projects...)
accountId, issues, err := api.Issues(jql, 0)

accountId, timezone, err := api.Me()
if err != nil {
log.Println("Could not get Jira issues.", err)
log.Println("Could not get user.", err)
return pkg.Timesheet{}
}

c := make(chan pkg.Timesheet)
var wg sync.WaitGroup
wg.Add(len(issues))
for _, item := range issues {
go func(item model.Issue) {
defer wg.Done()
items, err := api.Worklog(item.Key(), 0)
if err != nil {
log.Println("Could not get effort for "+item.Key(), err)
return
}
c <- item.Worklog(map[model.Account]pkg.User{}, items, fromDate, toDate)[accountId]
}(item)
}
go func() {
defer close(c)
wg.Wait()
}()
var timesheet pkg.Timesheet
for effort := range c {
timesheet = append(timesheet, effort...)
accounts := map[model.Account]*pkg.User{}
accounts[accountId] = &pkg.User{
TimeZone: timezone,
}

return timesheet
return do(api, year, month, projects, accounts)
}

func GetBulkTimesheet(client *http.Client, server *url.URL, userinfo *url.Userinfo, year int, month time.Month, projects []pkg.Project, users []pkg.User) pkg.Timesheet {
func GetBulkTimesheet(client *http.Client, server *url.URL, userinfo *url.Userinfo, year int, month time.Month, projects []pkg.Project, users []*pkg.User) pkg.Timesheet {
var err error
api, err := getApiVersion(client, server, userinfo)
if err != nil {
log.Println("Could not get api version.", err)
return pkg.Timesheet{}
}

accounts, err := accounts(api, projects, users)
if err != nil {
log.Println("Could not get user.", err)
return pkg.Timesheet{}
}

return do(api, year, month, projects, accounts)
}

func do(api model.Api, year int, month time.Month, projects []pkg.Project, accounts map[model.Account]*pkg.User) pkg.Timesheet {
var err error

// TODO Calculate max timezone offset for each user to have the right from and to date.
// The jql query uses afaik the time zone of the requesting user.
fromDate, toDate := pkg.GetTimeRange(year, month)

if projects == nil || len(projects) == 0 {
Expand All @@ -122,44 +115,47 @@ func GetBulkTimesheet(client *http.Client, server *url.URL, userinfo *url.Userin
}
}

accounts, err := accounts(api, projects, users)
if err != nil {
log.Println("Could not get user.", err)
return pkg.Timesheet{}
}
i := 0
accountIds := make([]model.Account, len(accounts))
for account := range accounts {
users[i] = pkg.User(account)
accountIds[i] = account
i++
}

jql := new(model.Jql).Between(fromDate, toDate).Users(users...).Projects(projects...)
_, issues, err := api.Issues(jql, 0)
jql := new(model.Jql).Between(fromDate, toDate).Users(accountIds...).Projects(projects...)
issues, err := api.Issues(jql, 0)

if err != nil {
log.Println("Could not get issues.", err)
return pkg.Timesheet{}
}

burstLimit := 5
// If we do not throttle here, the server will sometimes respond with 401.
// That looks like a rate limit but I have not found any numbers in the docs for the cloud setup.
throttle := make(chan struct{}, burstLimit)
c := make(chan pkg.Timesheet)
var wg sync.WaitGroup
wg.Add(len(issues))
for _, item := range issues {
go func(item model.Issue) {
defer wg.Done()
throttle <- struct{}{}
items, err := api.Worklog(item.Key(), 0)
<-throttle
if err != nil {
log.Println("Could not get effort for "+item.Key(), err)
return
}
worklog := item.Worklog(accounts, items, fromDate, toDate)
for _, user := range users {
c <- worklog[model.Account(user)]
for account := range accounts {
c <- worklog[account]
}
}(item)
}
go func() {
defer close(c)
defer close(throttle)
wg.Wait()
}()
var timesheet pkg.Timesheet
Expand All @@ -170,20 +166,23 @@ func GetBulkTimesheet(client *http.Client, server *url.URL, userinfo *url.Userin
return timesheet
}

func accounts(api model.Api, projects []pkg.Project, users []pkg.User) (map[model.Account]pkg.User, error) {
result := make(map[model.Account]pkg.User, len(users))
func accounts(api model.Api, projects []pkg.Project, users []*pkg.User) (map[model.Account]*pkg.User, error) {
result := make(map[model.Account]*pkg.User, len(users))
c := make(chan error)
var wg sync.WaitGroup
wg.Add(len(users))
for _, user := range users {
go func(user pkg.User) {
go func(user *pkg.User) {
defer wg.Done()
account, err := api.User(user, projects)
account, location, err := api.User(user, projects)
if err != nil {
c <- err
return
}
result[account] = user
result[account] = &pkg.User{
DisplayName: user.DisplayName,
TimeZone: location,
}
}(user)
}
go func() {
Expand Down
12 changes: 4 additions & 8 deletions pkg/jira/model/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import (
"time"
)

const (
HeaderAccountId = "X-AACCOUNTID"
)

type Account string

type IssueKey string
Expand All @@ -25,11 +21,12 @@ type ProjectAccessor interface {
}

type UserAccessor interface {
User(user pkg.User, projects []pkg.Project) (Account, error)
Me() (Account, *time.Location, error)
User(user *pkg.User, projects []pkg.Project) (Account, *time.Location, error)
}

type IssueAccessor interface {
Issues(jql Jql, startAt int) (Account, []Issue, error)
Issues(jql Jql, startAt int) ([]Issue, error)
}

type WorklogAccessor interface {
Expand All @@ -38,11 +35,10 @@ type WorklogAccessor interface {

type Issue interface {
Key() IssueKey
Worklog(accounts map[Account]pkg.User, worklog []Worklog, fromDate, toDate time.Time) map[Account]pkg.Timesheet
Worklog(accounts map[Account]*pkg.User, worklog []Worklog, fromDate, toDate time.Time) map[Account]pkg.Timesheet
}

type Worklog interface {
IsBetween(fromDate, toDate time.Time) bool
Author() Author
Date() time.Time
Comment() pkg.Description
Expand Down
7 changes: 1 addition & 6 deletions pkg/jira/model/jql.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,10 @@ const (
jqlWorklogDate = "worklogDate >= '%s' AND worklogDate < '%s'"
jqlWorklogProject = "project in ('%s')"
jqlWorklogAuthor = "worklogAuthor in (%s)"
currentUser = "currentUser()"
)

type Jql []string

func (query Jql) Me() Jql {
return Jql(append(query, fmt.Sprintf(jqlWorklogAuthor, currentUser)))
}

func (query Jql) Projects(projects ...pkg.Project) Jql {
if projects == nil || len(projects) == 0 {
return query
Expand All @@ -31,7 +26,7 @@ func (query Jql) Projects(projects ...pkg.Project) Jql {
return Jql(append(query, fmt.Sprintf(jqlWorklogProject, strings.Join(result, "','"))))
}

func (query Jql) Users(users ...pkg.User) Jql {
func (query Jql) Users(users ...Account) Jql {
if users == nil || len(users) == 0 {
return query
}
Expand Down
Loading

0 comments on commit f3a874f

Please sign in to comment.