Skip to content

Commit

Permalink
Merge pull request #52 from gin-melodic/dev
Browse files Browse the repository at this point in the history
Fix issues and support Query and Activity.
  • Loading branch information
manuelbcd authored Nov 22, 2022
2 parents 0868445 337ce51 commit 4f574ff
Show file tree
Hide file tree
Showing 41 changed files with 2,564 additions and 446 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 1,3 @@
.idea
schemas/*.json
.DS_Store
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 72,20 @@ func main() {
}
```
## Supported objects
| Endpoint | GET single | GET many | POST | PUT | DELETE |
| ------------- | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |
| Attachments (Info) | :heavy_check_mark: | :heavy_check_mark: | *implementing* | - | *pending* |
| Endpoint | GET single | GET many | POST | PUT | DELETE |
|------------------------| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |
| Attachments (Info) | :heavy_check_mark: | :heavy_check_mark: | *implementing* | - | *pending* |
| Attachments (Download) | :heavy_check_mark: | - | - | - | - |
| Categories | :heavy_check_mark: | :heavy_check_mark: | - | - | - |
| Documents | *implementing* | - | - | - | - |
| Projects | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | *pending* | *pending* | *pending* |
| Queries | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | - | :heavy_check_mark: |
| Schemas | *pending* |
| Statuses | :heavy_check_mark: | :heavy_check_mark: | *pending* | *pending* | *pending* |
| Users | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: |
| Wiki Pages | :heavy_check_mark: | *pending* | *pending* | *pending* | *pending* |
| WorkPackages | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: |
| Categories | :heavy_check_mark: | :heavy_check_mark: | - | - | - |
| Documents | *implementing* | - | - | - | - |
| Projects | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | *pending* | *pending* | *pending* |
| Queries | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | - | :heavy_check_mark: |
| Schemas | *pending* |
| Statuses | :heavy_check_mark: | :heavy_check_mark: | *pending* | *pending* | *pending* |
| Users | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: |
| Wiki Pages | :heavy_check_mark: | *pending* | *pending* | *pending* | *pending* |
| WorkPackages | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: |
| Activities | :heavy_check_mark: | :heavy_check_mark: | | | |

## Thanks
Thanks [Wieland](https://github.com/wielinde), [Oliver](https://github.com/oliverguenther) and [OpenProject](https://github.com/opf/openproject) team for your support.
Expand Down
68 changes: 68 additions & 0 deletions acitivities_test.go
Original file line number Diff line number Diff line change
@@ -0,0 1,68 @@
package openproject

import (
"fmt"
"net/http"
"os"
"testing"
)

func TestActivitiesServiceGet(t *testing.T) {
setup()
defer teardown()
testAPIEdpoint := "/api/v3/activities/357978"

raw, err := os.ReadFile("./mocks/get/get-activity.json")
if err != nil {
t.Error(err.Error())
return
}
testMux.HandleFunc(testAPIEdpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, testAPIEdpoint)
fmt.Fprint(w, string(raw))
})

activity, _, err := testClient.Activities.Get("357978")
if err != nil {
t.Errorf("Error given: %s", err)
}
if activity == nil {
t.Error("Expected activities. Activities is nil")
return
}
if activity.Id != 357978 {
t.Errorf("Unexpected activity id %d", activity.Id)
}
}

func TestActivitiesService_GetFromWPHref(t *testing.T) {
setup()
defer teardown()
testAPIEdpoint := "/api/v3/work_packages/36353/activities"

raw, err := os.ReadFile("./mocks/get/get-activities.json")
if err != nil {
t.Error(err.Error())
return
}

testMux.HandleFunc(testAPIEdpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, testAPIEdpoint)
fmt.Fprint(w, string(raw))
})

activities, _, err := testClient.Activities.GetFromWPHref("/api/v3/work_packages/36353/activities")
if err != nil {
t.Errorf("Error given: %s", err)
return
}
if activities == nil {
t.Error("Expected activities. Activities is nil")
return
}
if activities.Count != 6 {
t.Errorf("Unexpected activities count %d", activities.Count)
}
}
76 changes: 76 additions & 0 deletions activities.go
Original file line number Diff line number Diff line change
@@ -0,0 1,76 @@
package openproject

import (
"context"
"fmt"
"strings"
"time"
)

// ActivitiesService handles activities for the OpenProject instance / API.
type ActivitiesService struct {
client *Client
}

type Activity struct {
Type string `json:"_type"`
Id int `json:"id"`
Comment OPGenericDescription `json:"comment"`
Details []OPGenericDescription `json:"details"`
Version int `json:"version"`
CreatedAt time.Time `json:"createdAt"`
Links struct {
Self OPGenericLink `json:"self"`
WorkPackage OPGenericLink `json:"workPackage"`
User OPGenericLink `json:"user"`
Update OPGenericLink `json:"update"`
} `json:"_links"`
}

type Activities struct {
Type string `json:"_type"`
Total int `json:"total"`
Count int `json:"count"`
Embedded struct {
Elements []Activity `json:"elements"`
} `json:"_embedded"`
Links struct {
Self OPGenericLink `json:"self"`
} `json:"_links"`
}

// GetWithContext gets activity from OpenProject using its ID
func (s *ActivitiesService) GetWithContext(ctx context.Context, activitiesID string) (*Activity, *Response, error) {
apiEndPoint := fmt.Sprintf("api/v3/activities/%s", activitiesID)
obj, resp, err := GetWithContext(ctx, s, apiEndPoint)
if err != nil {
return nil, resp, err
}
return obj.(*Activity), resp, err
}

// Get wraps GetWithContext using the background context.
func (s *ActivitiesService) Get(activitiesID string) (*Activity, *Response, error) {
return s.GetWithContext(context.Background(), activitiesID)
}

// GetFromWPHrefWithContext gets activities from OpenProject using work package href string, like '/api/v3/work_packages/36353/activities'
func (s *ActivitiesService) GetFromWPHrefWithContext(ctx context.Context, href string) (*Activities, *Response, error) {
if strings.HasPrefix(href, "/") && len(href) > 1 {
href = href[1:]
}
if href == "" {
return nil, nil, fmt.Errorf("href is empty")
}
apiEndPoint := href
obj, resp, err := GetListWithContext(ctx, s, apiEndPoint, nil, 0, 0)
if err != nil {
return nil, resp, err
}
return obj.(*Activities), resp, err
}

// GetFromWPHref wraps GetFromWPHrefWithContext using the background context.
func (s *ActivitiesService) GetFromWPHref(href string) (*Activities, *Response, error) {
return s.GetFromWPHrefWithContext(context.Background(), href)
}
77 changes: 67 additions & 10 deletions attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 3,8 @@ package openproject
import (
"context"
"fmt"
"io/ioutil"
"io"
"time"
)

// AttachmentService handles attachments for the OpenProject instance / API.
Expand All @@ -12,15 13,71 @@ type AttachmentService struct {
}

// Attachment is the object representing OpenProject attachments.
// TODO: Complete fields and complex fields (user, links, downloadlocation, container...)
type Attachment struct {
Type string `json:"_type,omitempty" structs:"_type,omitempty"`
ID int `json:"id,omitempty" structs:"id,omitempty"`
FileName string `json:"filename,omitempty" structs:"filename,omitempty"`
FileSize int `json:"filesize,omitempty" structs:"filesize,omitempty"`
Description OPGenericDescription `json:"description,omitempty" structs:"description,omitempty"`
ContentType string `json:"contentType,omitempty" structs:"contentType,omitempty"`
Digest AttachmentDigest `json:"digest,omitempty" structs:"digest,omitempty"`
Embedded struct {
Author struct {
Type string `json:"_type,omitempty"`
ID int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Avatar string `json:"avatar,omitempty"`
Links struct {
Self OPGenericLink `json:"self,omitempty"`
} `json:"_links,omitempty"`
} `json:"author,omitempty"`
Container struct {
Type string `json:"_type,omitempty"`
ID int `json:"id,omitempty"`
RowCount int `json:"rowCount,omitempty"`
ColumnCount int `json:"columnCount,omitempty"`
Options struct {
} `json:"options,omitempty"`
Widgets []struct {
Type string `json:"_type,omitempty"`
ID int `json:"id,omitempty"`
Identifier string `json:"identifier,omitempty"`
StartRow int `json:"startRow,omitempty"`
EndRow int `json:"endRow,omitempty"`
StartColumn int `json:"startColumn,omitempty"`
EndColumn int `json:"endColumn,omitempty"`
Options struct {
Name string `json:"name,omitempty"`
Text struct {
Format string `json:"format,omitempty"`
Raw string `json:"raw,omitempty"`
HTML string `json:"html,omitempty"`
} `json:"text,omitempty"`
QueryID string `json:"queryId,omitempty"`
ChartType string `json:"chartType,omitempty"`
} `json:"options,omitempty"`
} `json:"widgets,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
Links struct {
Attachments OPGenericLink `json:"attachments,omitempty"`
Scope OPGenericLink `json:"scope,omitempty"`
Self OPGenericLink `json:"self,omitempty"`
} `json:"_links,omitempty"`
} `json:"container,omitempty"`
} `json:"_embedded,omitempty"`
Type string `json:"_type,omitempty"`
ID int `json:"id,omitempty"`
FileName string `json:"fileName,omitempty"`
FileSize int `json:"fileSize,omitempty"`
Description struct {
Format string `json:"format,omitempty"`
Raw string `json:"raw,omitempty"`
HTML string `json:"html,omitempty"`
} `json:"description,omitempty"`
ContentType string `json:"contentType,omitempty"`
Digest AttachmentDigest `json:"digest,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
Links struct {
Self OPGenericLink `json:"self,omitempty"`
Author OPGenericLink `json:"author,omitempty"`
Container OPGenericLink `json:"container,omitempty"`
StaticDownloadLocation OPGenericLink `json:"staticDownloadLocation,omitempty"`
DownloadLocation OPGenericLink `json:"downloadLocation,omitempty"`
} `json:"_links,omitempty"`
}

// AttachmentDigest wraps algorithm and hash
Expand Down Expand Up @@ -55,7 112,7 @@ func (s *AttachmentService) DownloadWithContext(ctx context.Context, attachmentI
return nil, err
}

respBytes, err := ioutil.ReadAll(resp.Body)
respBytes, err := io.ReadAll(resp.Body)

return &respBytes, err
}
Expand Down
8 changes: 4 additions & 4 deletions attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 2,8 @@ package openproject

import (
"fmt"
"io/ioutil"
"net/http"
"os"
"testing"
)

Expand All @@ -12,7 12,7 @@ func TestAttachmentService_Get(t *testing.T) {
defer teardown()
testAPIEdpoint := "/api/v3/attachments/5"

raw, err := ioutil.ReadFile("./mocks/get/get-attachment-from-workpackage.json")
raw, err := os.ReadFile("./mocks/get/get-attachment-from-workpackage.json")
if err != nil {
t.Error(err.Error())
}
Expand All @@ -30,7 30,7 @@ func TestAttachmentService_Get(t *testing.T) {
t.Error("Expected attachment. Attachment is nil")
return
}
if attachment.FileName != "Rollen-Ticket-Sichtbarkeit.jpg" {
if attachment.FileName != "Leerzeile.png" {
t.Errorf("Unexpected attachment filename %s", attachment.FileName)
}
}
Expand All @@ -40,7 40,7 @@ func TestAttachmentService_Download(t *testing.T) {
defer teardown()
testAPIEdpoint := "/api/v3/attachments/5/content"

raw, err := ioutil.ReadFile("./mocks/get/download-attachment-file.jpg")
raw, err := os.ReadFile("./mocks/get/download-attachment-file.jpg")
if err != nil {
t.Error(err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 4,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
)

Expand Down Expand Up @@ -138,7 138,7 @@ func (s *AuthenticationService) GetCurrentUserWithContext(ctx context.Context) (

defer resp.Body.Close()
ret := new(Session)
data, err := ioutil.ReadAll(resp.Body)
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("couldn't read body from the response : %s", err)
}
Expand Down
12 changes: 6 additions & 6 deletions authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 3,7 @@ package openproject
import (
"bytes"
"fmt"
"io/ioutil"
"io"
"net/http"
"reflect"
"testing"
Expand All @@ -15,7 15,7 @@ func TestAuthenticationService_AcquireSessionCookie_Failure(t *testing.T) {
testMux.HandleFunc("/rest/auth/1/session", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/auth/1/session")
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("Error in read body: %s", err)
}
Expand Down Expand Up @@ -49,7 49,7 @@ func TestAuthenticationService_AcquireSessionCookie_Success(t *testing.T) {
testMux.HandleFunc("/rest/auth/1/session", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/auth/1/session")
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("Error in read body: %s", err)
}
Expand Down Expand Up @@ -140,7 140,7 @@ func TestAithenticationService_GetUserInfo_AccessForbidden_Fail(t *testing.T) {
if r.Method == "POST" {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/auth/1/session")
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("Error in read body: %s", err)
}
Expand Down Expand Up @@ -178,7 178,7 @@ func TestAuthenticationService_GetUserInfo_NonOkStatusCode_Fail(t *testing.T) {
if r.Method == "POST" {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/auth/1/session")
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("Error in read body: %s", err)
}
Expand Down Expand Up @@ -234,7 234,7 @@ func TestAuthenticationService_GetUserInfo_Success(t *testing.T) {
if r.Method == "POST" {
testMethod(t, r, "POST")
testRequestURL(t, r, "/rest/auth/1/session")
b, err := ioutil.ReadAll(r.Body)
b, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("Error in read body: %s", err)
}
Expand Down
Loading

0 comments on commit 4f574ff

Please sign in to comment.