Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
1H0 committed Dec 19, 2022
0 parents commit 1ca1e17
Show file tree
Hide file tree
Showing 18 changed files with 1,433 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Go workspace file
go.work
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# matata

> **matata** is a unofficial api client written in go for the time tracking tool [Hakuna](https://www.hakuna.ch).
## Table of content

- [matata](#matata)
- [Table of content](#table-of-content)
- [Installation](#installation)
- [Documentation](#documentation)
- [ToDo](#todo)
- [License](#license)

## Installation

```bash
go get github.com/1H0/matata
```

## Documentation

[![Go Reference](https://pkg.go.dev/badge/github.com/1H0/matata.svg)](https://pkg.go.dev/github.com/1H0/matata)

## ToDo

- [ ] Testing
- [ ] Documentation

## License

The *GNU GENERAL PUBLIC LICENSE* - see [`LICENSE`](./LICENSE)
35 changes: 35 additions & 0 deletions absenceTypes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package matata

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

type AbsenceType struct {
ID int `json:"id"`
Name string `json:"name"`
Archived bool `json:"archived"`
GrantsWorkTime bool `json:"grants_work_time"`
IsVacation bool `json:"is_vacation"`
}

// AbsenceTypeList represents a list of AbsenceType objects.
type AbsenceTypeList []AbsenceType

func (c *Client) GetAbsenceTypes(ctx context.Context) (*AbsenceTypeList, error) {

req, err := http.NewRequest("GET", fmt.Sprintf("%s/absence_types", c.BaseURL), nil)

if err != nil {
return nil, err
}

res := AbsenceTypeList{}
if err := c.sendRequest(req, &res); err != nil {
return nil, err
}

return &res, nil

}
48 changes: 48 additions & 0 deletions absences.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package matata

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

type AbsenceOptions struct {
Year string
}

type Absence struct {
ID int `json:"id"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
FirstHalfDay bool `json:"first_half_day"`
SecondHalfDay bool `json:"second_half_day"`
IsRecurring bool `json:"is_recurring"`
WeeklyRepeatInterval interface{} `json:"weekly_repeat_interval"`
User User `json:"user"`
Type AbsenceType `json:"absence_type"`
}

type AbsenceList []Absence

func (c *Client) GetAbsences(ctx context.Context, options *AbsenceOptions) (*AbsenceList, error) {

if options.Year == "" {
options.Year = strconv.Itoa(time.Now().Year())
}

req, err := http.NewRequest("GET", fmt.Sprintf("%s/absences?year=%s", c.BaseURL, options.Year), nil)

if err != nil {
return nil, err
}

res := AbsenceList{}
if err := c.sendRequest(req, &res); err != nil {
return nil, err
}

return &res, nil

}
32 changes: 32 additions & 0 deletions company.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package matata

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

type Company struct {
CompanyName string `json:"company_name"`
DurationFormat string `json:"duration_format"`
AbsenceRequestsEnabled bool `json:"absence_requests_enabled"`
ProjectsEnabled bool `json:"projects_enabled"`
GroupsEnabled bool `json:"groups_enabled"`
}

func (c *Client) GetCompany(ctx context.Context) (*Company, error) {

req, err := http.NewRequest("GET", fmt.Sprintf("%s/company", c.BaseURL), nil)

if err != nil {
return nil, err
}

res := Company{}
if err := c.sendRequest(req, &res); err != nil {
return nil, err
}

return &res, nil

}
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/1H0/matata

go 1.18
Empty file added go.sum
Empty file.
43 changes: 43 additions & 0 deletions integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package matata

import (
"context"
"os"
"testing"
)

func TestPong(t *testing.T) {
c := NewClient(os.Getenv("HAKUNA_TENNANT"), os.Getenv("HAKUNA_TOKEN"))

res, err := c.Ping(context.TODO())

if err != nil {
t.Error(err)
}

if res.Pong.IsZero() {
t.Error("No Pong came back")
}

}

func TestGetAbsenceTypes(t *testing.T) {

c := NewClient(os.Getenv("HAKUNA_TENNANT"), os.Getenv("HAKUNA_TOKEN"))

res, err := c.GetAbsenceTypes(context.TODO())
if res == nil || err != nil {
t.Error(err)
}
}

func TestGetAbsences(t *testing.T) {

c := NewClient(os.Getenv("HAKUNA_TENNANT"), os.Getenv("HAKUNA_TOKEN"))

res, err := c.GetAbsences(context.TODO(), nil)
if res == nil || err != nil {
t.Error(err)
}

}
89 changes: 89 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package matata

import (
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"time"
)

const (
baseURL = "hakuna.ch/api/v1"
)

type Client struct {
BaseURL string
Tennant string
token string
Client *http.Client
}

// TODO: Is a bit weird, since somethines you get a Full error object with status code and message, and other times you just get a json object with at error attribute containing a string
type errorResponse struct {
HTTPStatus int
Status int `json:"status"`
Message string `json:"message"`
Error string `json:"error"`
}

func NewClient(tennant string, token string) *Client {

if tennant == "" {
log.Fatal("a tennant is required")
}

if token == "" {
log.Fatal("a token is required")

}

return &Client{
BaseURL: "https://" + tennant + "." + baseURL,
Tennant: tennant,
token: token,
Client: &http.Client{
Timeout: time.Minute,
},
}
}

func (c *Client) sendRequest(req *http.Request, v interface{}) error {
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("X-Auth-Token", c.token)

res, err := c.Client.Do(req)

if err != nil {
return err
}

defer res.Body.Close()

if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusBadRequest {

var errRes errorResponse
if err = json.NewDecoder(res.Body).Decode(&errRes); err == nil {

if errRes.Message == "" {
return fmt.Errorf(fmt.Sprintf("%d - %s", res.StatusCode, http.StatusText(res.StatusCode)))
}

return errors.New(errRes.Message)
}

return fmt.Errorf("unknown error, status code: %d", res.StatusCode)
}

if res.StatusCode == http.StatusNoContent {
return nil
}

if err = json.NewDecoder(res.Body).Decode(&v); err != nil {
return err
}

return nil

}
33 changes: 33 additions & 0 deletions organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package matata

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

type UserStatus struct {
User User `json:"user"`
AbsentFirstHalfDay bool `json:"absent_first_half_day"`
AbsentSecondHalfDay bool `json:"absent_second_half_day"`
HasTimerRunning bool `json:"has_timer_running"`
}

type OrganizationStatusList []UserStatus

func (c *Client) GetOrganizationStatus(ctx context.Context) (*OrganizationStatusList, error) {

req, err := http.NewRequest("GET", fmt.Sprintf("%s/organization/status", c.BaseURL), nil)

if err != nil {
return nil, err
}

res := OrganizationStatusList{}
if err := c.sendRequest(req, &res); err != nil {
return nil, err
}

return &res, nil

}
32 changes: 32 additions & 0 deletions overview.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package matata

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

type Overview struct {
Overtime string `json:"overtime"`
OvertimeInSeconds int `json:"overtime_in_seconds"`
Vacation struct {
RedeemedDays float64 `json:"redeemed_days"`
RemainingDays float64 `json:"remaining_days"`
} `json:"vacation"`
}

func (c *Client) GetOverview(ctx context.Context) (*Overview, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/overview", c.BaseURL), nil)

if err != nil {
return nil, err
}

res := Overview{}
if err := c.sendRequest(req, &res); err != nil {
return nil, err
}

return &res, nil

}
Loading

0 comments on commit 1ca1e17

Please sign in to comment.