Skip to content

Commit

Permalink
add task assignments and day entries
Browse files Browse the repository at this point in the history
  • Loading branch information
leahcnels committed Nov 15, 2016
1 parent 42e20e9 commit 62a85cd
Show file tree
Hide file tree
Showing 18 changed files with 1,421 additions and 33 deletions.
14 changes: 12 additions & 2 deletions api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package harvest

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
Expand All @@ -20,6 +21,7 @@ type API struct {

func NewBasicAuthAPI(subdomain, user, password string) *API {
a := API{}
a.client = http.DefaultClient
a.SubDomain = subdomain
a.User = user
a.Password = password
Expand All @@ -28,7 +30,7 @@ func NewBasicAuthAPI(subdomain, user, password string) *API {
}

func (a *API) Get(path string, args Arguments, target interface{}) error {
url := fmt.Sprintf("%s/%s", a.BaseURL, path)
url := fmt.Sprintf("%s%s", a.BaseURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, args.ToURLValues().Encode())

req, err := http.NewRequest("GET", urlWithParams, nil)
Expand All @@ -42,9 +44,17 @@ func (a *API) Get(path string, args Arguments, target interface{}) error {
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, err := ioutil.ReadAll(resp.Body)
var body []byte
body, err = ioutil.ReadAll(resp.Body)
return errors.Errorf("HTTP request failure on %s: %s %s", url, string(body), err)
}

decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(target)
if err != nil {
body, _ := ioutil.ReadAll(resp.Body)
return errors.Wrapf(err, "JSON decode failed on %s: %s", url, string(body))
}

return nil
}
8 changes: 8 additions & 0 deletions api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ func TestNewBasicAuthAPI(t *testing.T) {
if a1.BaseURL != "https://example.harvestapp.com" {
t.Errorf("Incorrect domain name '%s'.", a1.BaseURL)
}
if a1.client == nil {
t.Error("No http client")
}
}

func testAPI() *API {
a := NewBasicAuthAPI("example", "[email protected]", "password")
return a
}
5 changes: 5 additions & 0 deletions arguments.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package harvest

import (
Expand Down
Binary file modified cmd/harvesttest/harvesttest
Binary file not shown.
242 changes: 242 additions & 0 deletions coverage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }

</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">

<option value="file0">github.com/adlio/harvest/api.go (76.0%)</option>

<option value="file1">github.com/adlio/harvest/arguments.go (80.0%)</option>

<option value="file2">github.com/adlio/harvest/project.go (57.1%)</option>

</select>
</div>
<div id="legend">
<span>not tracked</span>

<span class="cov0">not covered</span>
<span class="cov8">covered</span>

</div>
</div>
<div id="content">

<pre class="file" id="file0" style="display: none">package harvest

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/pkg/errors"
)

const HARVEST_DOMAIN = "harvestapp.com"

type API struct {
client *http.Client
BaseURL string
SubDomain string
User string
Password string
}

func NewBasicAuthAPI(subdomain, user, password string) *API <span class="cov8" title="1">{
a := API{}
a.client = http.DefaultClient
a.SubDomain = subdomain
a.User = user
a.Password = password
a.BaseURL = fmt.Sprintf("https://%s.%s", subdomain, HARVEST_DOMAIN)
return &amp;a
}</span>

func (a *API) Get(path string, args Arguments, target interface{}) error <span class="cov8" title="1">{
url := fmt.Sprintf("%s/%s", a.BaseURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, args.ToURLValues().Encode())

req, err := http.NewRequest("GET", urlWithParams, nil)
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "Invalid GET request %s", url)
}</span>

<span class="cov8" title="1">resp, err := a.client.Do(req)
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "HTTP request failure on %s", url)
}</span>
<span class="cov8" title="1">defer resp.Body.Close()
if resp.StatusCode != 200 </span><span class="cov0" title="0">{
body, err := ioutil.ReadAll(resp.Body)
return errors.Errorf("HTTP request failure on %s: %s %s", url, string(body), err)
}</span>

<span class="cov8" title="1">decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(target)
if err != nil </span><span class="cov0" title="0">{
body, _ := ioutil.ReadAll(resp.Body)
return errors.Wrapf(err, "JSON decode failed on %s: %s", url, string(body))
}</span>

<span class="cov8" title="1">return nil</span>
}
</pre>

<pre class="file" id="file1" style="display: none">// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package harvest

import (
"net/url"
)

type Arguments map[string]string

func Defaults() Arguments <span class="cov8" title="1">{
return make(Arguments)
}</span>

func (args Arguments) ToURLValues() url.Values <span class="cov8" title="1">{
v := url.Values{}
for key, value := range args </span><span class="cov0" title="0">{
v.Set(key, value)
}</span>
<span class="cov8" title="1">return v</span>
}
</pre>

<pre class="file" id="file2" style="display: none">package harvest

import (
"fmt"
"time"
)

type ProjectResponse struct {
Project *Project `json:"project"`
}

type Project struct {
ID int64 `json:"id"`
ClientID int64 `json:"client_id"`
Name string `json:"name"`
Code string
Active bool
Billable bool
BillBy string
HourlyRate *float64
BudgetBy string
Budget *float64
NotifyWhenOverBudget bool
OverBudgetNotificationPercentage float64
OverBudgetNotifiedAt time.Time
ShowBudgetToAll bool
CreatedAt time.Time
UpdatedAt time.Time
StartsOn *Date
EndsOn *Date
Estimate *float64
EstimateBy string
Notes string
CostBudget *float64
CostBudgetIncludeExpenses bool
}

func (a *API) GetProject(projectID int64, args Arguments) (project *Project, err error) <span class="cov8" title="1">{
projectResponse := ProjectResponse{}
path := fmt.Sprintf("/projects/%v", projectID)
err = a.Get(path, args, &amp;projectResponse)
return projectResponse.Project, err
}</span>

func (a *API) GetProjects(args Arguments) (projects []*Project, err error) <span class="cov0" title="0">{
path := fmt.Sprintf("/projects")
err = a.Get(path, args, &amp;projects)
return
}</span>
</pre>

</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>
17 changes: 17 additions & 0 deletions coverage.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
mode: set
github.com/adlio/harvest/arguments.go:14.27,16.2 1 1
github.com/adlio/harvest/arguments.go:18.48,20.31 2 1
github.com/adlio/harvest/arguments.go:23.2,23.10 1 1
github.com/adlio/harvest/arguments.go:20.31,22.3 1 0
github.com/adlio/harvest/project.go:38.89,43.2 4 1
github.com/adlio/harvest/project.go:45.76,49.2 3 0
github.com/adlio/harvest/api.go:22.61,30.2 7 1
github.com/adlio/harvest/api.go:32.74,37.16 4 1
github.com/adlio/harvest/api.go:41.2,42.16 2 1
github.com/adlio/harvest/api.go:45.2,46.28 2 1
github.com/adlio/harvest/api.go:51.2,53.16 3 1
github.com/adlio/harvest/api.go:58.2,58.12 1 1
github.com/adlio/harvest/api.go:37.16,39.3 1 0
github.com/adlio/harvest/api.go:42.16,44.3 1 0
github.com/adlio/harvest/api.go:46.28,49.3 2 0
github.com/adlio/harvest/api.go:53.16,56.3 2 0
5 changes: 5 additions & 0 deletions coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

go test -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
open coverage.html
33 changes: 33 additions & 0 deletions day_entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package harvest

import (
"fmt"
"time"
)

type DayEntryResponse struct {
DayEntries []*DayEntry `json:"day_entries"`
}

type DayEntry struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
SpentAt string `json:"spent_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ProjectID string `json:"project_id"`
TaskID string `json:"task_id"`
Project string `json:"project"`
Task string `json:"task"`
Client string `json:"client"`
Notes string `json:"notes"`
HoursWithoutTimer int64 `json:"hours_without_timer"`
Hours int64 `json:"hours"`
}

func (a *API) GetTodayEntry(args Arguments) (dayentries []*DayEntry, err error) {
dayEntriesResponse := DayEntryResponse{}
path := fmt.Sprintf("/daily?slim=1")
err = a.Get(path, args, &dayEntriesResponse)
return dayEntriesResponse.DayEntries, err
}
22 changes: 22 additions & 0 deletions day_entry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package harvest

import "testing"

func TestGetTodayEntry(t *testing.T) {
a := testAPI()
dayEntryResponse := mockResponse("dayentries", "today-example.json")
a.BaseURL = dayEntryResponse.URL
dayentries, err := a.GetTodayEntry(Defaults())
if err != nil {
t.Fatal(err)
}
if len(dayentries) != 2 {
t.Errorf("Incorrect number of entries '%v'", len(dayentries))
}
if dayentries[0].ID != 538242480 {
t.Errorf("Incorrect day entry ID '%v'", dayentries[0].ID)
}
if dayentries[1].UserID != 1420761 {
t.Errorf("Incorrect UserID '%v'", dayentries[1].ID)
}
}
Loading

0 comments on commit 62a85cd

Please sign in to comment.