Skip to content

Commit

Permalink
Merge pull request #9 from tpeetz/redmine-api
Browse files Browse the repository at this point in the history
Add Redmine API
  • Loading branch information
Thomas Peetz authored Aug 13, 2020
2 parents d68114a + 9fe5f54 commit 80329f9
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 3 deletions.
3 changes: 2 additions & 1 deletion pkg/server/gitserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server
import (
"github.com/tpeetz/pull-task/pkg/types/github"
"github.com/tpeetz/pull-task/pkg/types/gitlab"
"github.com/tpeetz/pull-task/pkg/types/redmine"
)

// GitServer definees the methods for server (Gitlab, Github, Redmine)
Expand Down Expand Up @@ -33,7 +34,7 @@ func NewGitServer(details map[string]interface{}) (GitServer, error) {
case "github":
server = &github.Server{}
case "redmine":
return nil, &UnkownServiceTypeError{"redmine"}
server = &redmine.Server{}
default:
return nil, &UnkownServiceTypeError{service.(string)}
}
Expand Down
7 changes: 5 additions & 2 deletions pkg/server/gitserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/tpeetz/pull-task/pkg/types/github"
"github.com/tpeetz/pull-task/pkg/types/gitlab"
"github.com/tpeetz/pull-task/pkg/types/redmine"
)

var (
Expand All @@ -19,8 +20,9 @@ var (
ghServer = &github.Server{URL: "https://api.github.com"}
redmineConfig = map[string]interface{}{
"service": "redmine",
//"limit": 120,
}
//rmServer = &GitServer{url: "https://redmine.com", token: "secretToken"}
rmServer = &redmine.Server{URL: "https://redmine.example.com", Limit: 120}
unknownConfig = map[string]interface{}{
"service": "unknown",
}
Expand All @@ -38,7 +40,8 @@ func TestNewGitServer(t *testing.T) {
}{
{"gitlab", args{gitlabConfig}, glServer, false},
{"github", args{githubConfig}, ghServer, false},
{"redmine", args{redmineConfig}, nil, true},
{"redmine", args{redmineConfig}, rmServer, false},
{"redmine-limit", args{map[string]interface{}{"service": "redmine", "limit": 99}}, &redmine.Server{URL: "https://redmine.example.com", Limit: 99}, false},
{"unknown", args{unknownConfig}, nil, true},
}
for _, tt := range tests {
Expand Down
14 changes: 14 additions & 0 deletions pkg/types/redmine/customfield.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package redmine

import "fmt"

// CustomField represents a custom field of issue in Redmine.
type CustomField struct {
ID int `json:"id"`
Name string `json:"name"`
Value string `json:"value"`
}

func (customField *CustomField) String() string {
return fmt.Sprintf("CustomField: %d=%s", customField.ID, customField.Name)
}
41 changes: 41 additions & 0 deletions pkg/types/redmine/issue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package redmine

import (
"encoding/json"
"fmt"
)

// Issue represents issue in Redmine.
type Issue struct {
ID int `json:"id"`
Project Project `json:"project"`
Tracker Tracker `json:"tracker"`
Status Status `json:"status"`
Priority Priority `json:"priority"`
Author User `json:"author"`
Assigned User `json:"assigned_to"`
Parent ParentIssue `json:"parent"`
Subject string `json:"subject"`
Description string `json:"description,omitempty"`
StartDate string `json:"start_date,omitempty"`
DueDate string `json:"due_date,omitempty"`
DoneRatio int `json:"done_ratio"`
Estimated json.Number `json:"estimated_hours"`
Created string `json:"created_on,omitempty"`
Updated string `json:"updated_on,omitempty"`
CustomFields []CustomField `json:"custom_fields,omitempty"`
}

func (issue Issue) String() string {
return fmt.Sprintf("Issue %d: %s\n %s\n Start : %s\n Due : %s\n %s\n", issue.ID, issue.Subject, issue.Project, issue.StartDate, issue.DueDate, issue.Status)
}

// Short returns ID and subject of issue.
func (issue Issue) Short() string {
return fmt.Sprintf("Issue %d: %s", issue.ID, issue.Subject)
}

// SingleIssue represents a JSON answer of a single issue in Redmine.
type SingleIssue struct {
Issue Issue `json:"issue"`
}
9 changes: 9 additions & 0 deletions pkg/types/redmine/issuelist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package redmine

// IssueList represents the list of issue in Redmine.
type IssueList struct {
Issues []Issue `json:"issues"`
TotalCount int `json:"total_count"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
12 changes: 12 additions & 0 deletions pkg/types/redmine/parentissue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package redmine

import "fmt"

// ParentIssue holds the ID of the parent issue of the current one.
type ParentIssue struct {
ID int `json:"id"`
}

func (parent *ParentIssue) String() string {
return fmt.Sprintf("Parent Issue: %d", parent.ID)
}
13 changes: 13 additions & 0 deletions pkg/types/redmine/priority.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package redmine

import "fmt"

// Priority represents priority of issue in Redmine.
type Priority struct {
ID int `json:"id"`
Name string `json:"name"`
}

func (priority Priority) String() string {
return fmt.Sprintf("Priority: %s", priority.Name)
}
13 changes: 13 additions & 0 deletions pkg/types/redmine/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package redmine

import "fmt"

// Project represents project in Redmine.
type Project struct {
ID int `json:"id"`
Name string `json:"name"`
}

func (project Project) String() string {
return fmt.Sprintf("Project: %s", project.Name)
}
102 changes: 102 additions & 0 deletions pkg/types/redmine/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package redmine

import (
"encoding/json"
"fmt"
"net/http"
)

// Server represents a instance on an Redmine server.
type Server struct {
URL string
Token string
Limit int
}

func (server *Server) String() string {
return fmt.Sprintf("Redmine server: %v", server.URL)
}

// Configure reads the configuration details from map and sets the server instance.
func (server *Server) Configure(details map[string]interface{}) error {
url, ok := details["domain"]
if ok {
serverURL, correctType := url.(string)
if correctType {
server.URL = serverURL
}
} else {
server.URL = "https://redmine.example.com"
}
token, ok := details["token"]
if ok {
serverToken, correctType := token.(string)
if correctType {
server.Token = serverToken
}
}
limit, ok := details["limit"]
if ok {
serverLimit, correctType := limit.(int)
if correctType {
server.Limit = serverLimit
}
} else {
server.Limit = 120
}
return nil
}

// LoadIssues gets issues from Redmine server.
func (server *Server) LoadIssues() error {
fmt.Printf("Redmine load issues from %s\n", server.URL)
issuesURL := fmt.Sprintf("%s/issues.json?limit=%d", server.URL, server.Limit)
request, err := http.NewRequest("GET", issuesURL, nil)
if err != nil {
fmt.Printf("creation of request failed: %v\n", err)
return err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("X-Redmine-API-Key", server.Token)
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
fmt.Printf("The HTTP request failed with error %s\n", err)
return err
}
defer response.Body.Close()
var issueList IssueList
if err := json.NewDecoder(response.Body).Decode(&issueList); err != nil {
fmt.Printf("Response could not parsed as JSON - %v\n", err)
return err
}
fmt.Printf("Issue List:\n%v\n", issueList)
return nil
}

// LoadProjects gets projects from Redmine server.
func (server *Server) LoadProjects() error {
fmt.Printf("Gitlab load projects from %s\n", server.URL)
projectsURL := fmt.Sprintf("%s/%s/projects?per_page=%d", server.URL, "api/v4", 120)
request, err := http.NewRequest("GET", projectsURL, nil)
if err != nil {
fmt.Printf("creation of request failed: %v\n", err)
return err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("PRIVATE-TOKEN", server.Token)
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
fmt.Printf("The HTTP request failed with error %s\n", err)
return err
}
defer response.Body.Close()
var projectList []Project
if err := json.NewDecoder(response.Body).Decode(&projectList); err != nil {
fmt.Printf("Response could not parsed as JSON - %v\n", err)
return err
}
fmt.Printf("Project List:\n%v\n", projectList)
return nil
}
21 changes: 21 additions & 0 deletions pkg/types/redmine/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package redmine

import (
"fmt"
"strings"
)

// Status represents the issue status in Redmine.
type Status struct {
ID int `json:"id"`
Name string `json:"name"`
}

func (status Status) String() string {
return fmt.Sprintf("Status : %s", status.Name)
}

// Convert removes whitespace from status name.
func (status Status) Convert() string {
return strings.Replace(status.Name, " ", "", 1)
}
13 changes: 13 additions & 0 deletions pkg/types/redmine/tracker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package redmine

import "fmt"

// Tracker represents a issue type in Redmine.
type Tracker struct {
ID int `json:"id"`
Name string `json:"name"`
}

func (tracker *Tracker) String() string {
return fmt.Sprintf("Tracker %s", tracker.Name)
}
13 changes: 13 additions & 0 deletions pkg/types/redmine/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package redmine

import "fmt"

// User represents an user in Redmine.
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}

func (user *User) String() string {
return fmt.Sprintf("User: %d=%s", user.ID, user.Name)
}

0 comments on commit 80329f9

Please sign in to comment.