Skip to content

Commit

Permalink
GetTimeOffsMapped(): function to return timeOffs with accurate time s…
Browse files Browse the repository at this point in the history
…egment (#49)
  • Loading branch information
lyind committed Mar 26, 2024
1 parent 092b96f commit f3021da
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add `v1.GetTimeOffsMapped()` function

## [0.3.0] - 2023-07-05

### Added
Expand Down
46 changes: 46 additions & 0 deletions v1/personio.go
Expand Up @@ -11,6 +11,8 @@ import (
"strconv"
"strings"
"time"

util "github.com/giantswarm/personio-go"
)

const DefaultBaseUrl = "https://api.personio.de/v1"
Expand Down Expand Up @@ -530,3 +532,47 @@ func (personio *Client) GetTimeOffs(start *time.Time, end *time.Time, offset int

return timeOffs, nil
}

// GetTimeOffsMapped returns a slice of timeOffs with times mapped from HalfDayStart/HalfDayEnd/DaysCount
func (personio *Client) GetTimeOffsMapped(start time.Time, end time.Time) ([]*TimeOff, error) {

timeOffs, err := personio.GetTimeOffs(&start, &end, 0, 2147483647)
if err != nil {
return nil, err
}

var matchedTimeOffs []*TimeOff
for _, timeOff := range timeOffs {

// we need to adjust the wall-clock time according to other fields
if timeOff.DaysCount > 1 {
if timeOff.HalfDayStart {
timeOff.StartDate = timeOff.StartDate.Add(time.Hour * 12)
}
if timeOff.HalfDayEnd {
timeOff.EndDate = timeOff.EndDate.Add(time.Hour * 12)
} else {
timeOff.EndDate = timeOff.EndDate.Add(time.Hour * 24)
}
} else {
if timeOff.HalfDayStart && !timeOff.HalfDayEnd {
timeOff.EndDate = timeOff.EndDate.Add(time.Hour * 12)
} else if !timeOff.HalfDayStart && timeOff.HalfDayEnd {
timeOff.StartDate = timeOff.StartDate.Add(time.Hour * 12)
timeOff.EndDate = timeOff.EndDate.Add(time.Hour * 24)
} else {
timeOff.EndDate = timeOff.EndDate.Add(time.Hour * 24)
}
}

overlap := util.GetTimeIntersection(timeOff.StartDate, timeOff.EndDate, start, end)
if overlap <= 0 {
// timeOff and queried region doesn't overlap
continue
}

matchedTimeOffs = append(matchedTimeOffs, timeOff)
}

return matchedTimeOffs, nil
}
76 changes: 73 additions & 3 deletions v1/personio_test.go
Expand Up @@ -101,12 +101,17 @@ func (p *PersonioMock) PersonioMockHandler(w http.ResponseWriter, req *http.Requ
var errEnd error
if startArg != "" {
start, errStart = time.Parse(queryDateFormat, startArg)
// Personio seems to report more events around the selected start date
// try to match that behavior
start = start.Add(time.Second*24*60*60*-1 - 1)
} else {
start = time.Time{}
}

if endArg != "" {
end, errEnd = time.Parse(queryDateFormat, endArg)
// Personio seems to report more events around the selected end date
end = end.Add(time.Second*24*60*60 - 1)
} else {
end = util.PersonioDateMax
}
Expand Down Expand Up @@ -466,7 +471,7 @@ type timeOffTestCase struct {
func TestClient_GetTimeOffs(t *testing.T) {

tsTooEarly := makeTime("1971-01-01T00:00:00Z")
tsEarly := makeTime("2022-09-06T05:00:00Z")
tsEarly := makeTime("2022-09-05T05:00:00Z")
tsMiddle := makeTime("2022-09-08T06:00:00Z")
tsMiddlePlus6h := tsMiddle.Add(makeDuration("6h"))
tsLate := makeTime("2022-09-10T05:00:00Z")
Expand All @@ -475,9 +480,9 @@ func TestClient_GetTimeOffs(t *testing.T) {
{start: nil, end: nil, wantIds: []int64{125814620, 125682392}},
{start: nil, end: &tsTooEarly, wantIds: []int64{}},
{start: nil, end: &tsEarly, wantIds: []int64{125814620}},
{start: &tsLate, end: &util.PersonioDateMax, wantIds: []int64{125682392}},
{start: &tsLate, end: &util.PersonioDateMax, wantIds: []int64{125682392, 125682393}},
{start: &tsMiddle, end: &tsMiddlePlus6h, wantIds: []int64{125682392, 125814620}},
{start: &tsTooLate, end: nil, wantIds: []int64{}},
{start: &tsTooLate, end: nil, wantIds: []int64{125682393}},
}

server, err := newTestServer()
Expand Down Expand Up @@ -547,3 +552,68 @@ func TestClient_GetTimeOffs(t *testing.T) {
}
}
}

func TestClient_GetTimeOffsMapped(t *testing.T) {

tsStart := makeTime("2022-09-10T00:00:00+02:00")
tsEnd := makeTime("2022-12-01T00:00:00+02:00")
tsStart2 := makeTime("2022-12-01T00:00:00+02:00")
tsEnd2 := makeTime("2022-12-01T12:01:00+02:00")
tsStart3 := makeTime("2022-12-01T00:00:00+02:00")
tsEnd3 := makeTime("2022-12-01T12:00:00+02:00")
timeOffCases := []timeOffTestCase{
{start: &tsStart, end: &tsEnd, wantIds: []int64{125682392}},
{start: &tsStart2, end: &tsEnd2, wantIds: []int64{125682393}},
{start: &tsStart3, end: &tsEnd3, wantIds: []int64{}},
}

server, err := newTestServer()
if err != nil {
t.Errorf("Failed to setup mock Personio server: failed to listen: %s", err)
return
}

defer func() {
_ = server.Close()
}()

personioCredentials := Credentials{ClientId: "abc", ClientSecret: "def"}
personio, err := NewClient(context.TODO(), fmt.Sprintf("http://localhost:%d", server.port), personioCredentials)
if err != nil {
t.Errorf("Failed to create Personio API v1 client: %s", err)
return
}

for testNumber, testCase := range timeOffCases {
timeOffs, err := personio.GetTimeOffsMapped(*testCase.start, *testCase.end)
if err != nil {
t.Errorf("[%d] Failed to query time-offs: %s", testNumber, err)
continue
}

if len(testCase.wantIds) != len(timeOffs) {
t.Errorf("[%d] Expected %d time-offs, got %d", testNumber, len(testCase.wantIds), len(timeOffs))
continue
}

for _, timeOff := range timeOffs {
if len(timeOff.Comment) <= 0 {
t.Errorf("[%d] Time-off with ID %d has empty comment", testNumber, timeOff.Id)
}
}

for _, id := range testCase.wantIds {
found := false
for i := range timeOffs {
if timeOffs[i].Id == id {
found = true
break
}
}
if !found {
t.Errorf("[%d] Time-off with ID %d not found in time-offs", testNumber, id)
break
}
}
}
}
56 changes: 56 additions & 0 deletions v1/testdata/time-offs-body.json
Expand Up @@ -112,6 +112,62 @@
"created_at": "2022-07-26T11:52:55+02:00",
"updated_at": "2022-10-02T19:13:09+02:00"
}
},
{
"type": "TimeOffPeriod",
"attributes": {
"id": 125682393,
"status": "approved",
"comment": "later-this-year",
"start_date": "2022-12-01T00:00:00+02:00",
"end_date": "2022-12-01T00:00:00+02:00",
"days_count": 0.5,
"half_day_start": 0,
"half_day_end": 1,
"time_off_type": {
"type": "TimeOffType",
"attributes": {
"id": 155627,
"name": "Vacation",
"category": "paid_vacation"
}
},
"employee": {
"type": "Employee",
"attributes": {
"id": {
"label": "ID",
"value": 6205887,
"type": "integer",
"universal_id": "id"
},
"first_name": {
"label": "First name",
"value": "El",
"type": "standard",
"universal_id": "first_name"
},
"last_name": {
"label": "Last name",
"value": "Gonzo",
"type": "standard",
"universal_id": "last_name"
},
"email": {
"label": "Email",
"value": "gonzo@giantswarm.io",
"type": "standard",
"universal_id": "email"
}
}
},
"created_by": "El Gonzo",
"certificate": {
"status": "not-required"
},
"created_at": "2022-07-26T11:52:55+02:00",
"updated_at": "2022-10-02T19:13:09+02:00"
}
}
]
}

0 comments on commit f3021da

Please sign in to comment.