Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support user model and listing groups #92

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions teamcity/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ import (

// Group is the model for group entities in TeamCity
type Group struct {
Key string `json:"key,omitempty" xml:"key"`
Description string `json:"description,omitempty" xml:"description"`
Name string `json:"name,omitempty" xml:"name"`
Key string `json:"key,omitempty" xml:"key"`
Description string `json:"description,omitempty" xml:"description"`
Name string `json:"name,omitempty" xml:"name"`
Roles *roleAssignmentsJSON `json:"roles,omitempty" xml:"roles"`
Properties *Properties `json:"properties,omitempty" xml:"properties"`
}

// GroupList is the model for group list in TeamCity
type GroupList struct {
Count int `json:"count,omitempty" xml:"count"`
Items []Group `json:"group,omitempty" xml:"group"`
}

// NewGroup returns an instance of a Group. A non-empty Key and Name is required.
Expand Down Expand Up @@ -62,9 +70,17 @@ func (s *GroupService) Create(group *Group) (*Group, error) {

// GetByKey - Get a group by its group key
func (s *GroupService) GetByKey(key string) (*Group, error) {
return s.getByLocator(LocatorKey(key))
}

// GetByName - Get a group by its group name
func (s *GroupService) GetByName(name string) (*Group, error) {
return s.getByLocator(LocatorName(name))
}

func (s *GroupService) getByLocator(locator Locator) (*Group, error) {
var out Group
locator := LocatorKey(key).String()
err := s.restHelper.get(locator, &out, "group")
err := s.restHelper.get(locator.String(), &out, "group")
if err != nil {
return nil, err
}
Expand All @@ -78,3 +94,21 @@ func (s *GroupService) Delete(key string) error {
err := s.restHelper.delete(locator, "group")
return err
}

// List - List of groups in range [offset:limit)
func (s *GroupService) List(offset, limit int) (*GroupList, error) {
var out GroupList
err := s.restHelper.get("", &out, "group", buildQueryLocator(
LocatorStart(offset),
LocatorCount(limit),
))
if err != nil {
return nil, err
}
return &out, nil
}

// ListAll returns all groups
func (s *GroupService) ListAll() (*GroupList, error) {
return s.List(0, -1)
}
54 changes: 54 additions & 0 deletions teamcity/group_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package teamcity_test

import (
"fmt"
"testing"

"github.com/cvbarros/go-teamcity/teamcity"
Expand Down Expand Up @@ -42,6 +43,24 @@ func TestGroup_GetByKey(t *testing.T) {
assert.Equal(t, newGroup.Description, actual.Description)
}

func TestGroup_GetByName(t *testing.T) {
newGroup, _ := teamcity.NewGroup("TESTGROUPKEY2", "Test Group Name 2", "Test Group Description 2")
client := setup()
client.Groups.Create(newGroup)

actual, err := client.Groups.GetByName(newGroup.Name)

require.NoError(t, err)
require.NotNil(t, actual)
require.NotEmpty(t, actual.Key)

cleanUpGroup(t, client, actual.Key)

assert.Equal(t, newGroup.Key, actual.Key)
assert.Equal(t, newGroup.Name, actual.Name)
assert.Equal(t, newGroup.Description, actual.Description)
}

func TestGroup_Delete(t *testing.T) {
newGroup, _ := teamcity.NewGroup("TESTGROUPKEY", "Test Group Name", "Test Group Description")
client := setup()
Expand All @@ -58,6 +77,41 @@ func TestGroup_Delete(t *testing.T) {
assert.Contains(t, err.Error(), "404")
}

func TestGroup_List(t *testing.T) {
groups := []*teamcity.Group{}
client := setup()

groupListBefore, err := client.Groups.ListAll()
require.NoError(t, err)
for i := 0; i < 5; i++ {
group, err := teamcity.NewGroup(
fmt.Sprint("TESTGROUPLIST", i),
fmt.Sprint("Test Group List ", i),
fmt.Sprint("Test Group Description List ", i),
)
require.NoError(t, err)
groups = append(groups, group)
client.Groups.Create(group)
}
groupList, err := client.Groups.ListAll()

require.NoError(t, err)
assert.Equal(t, groupListBefore.Count+5, groupList.Count)

for _, group := range groupList.Items {
if group.Key == "ALL_USERS_GROUP" {
continue
}
_, err := client.Groups.GetByKey(group.Key)
require.NoError(t, err)

cleanUpGroup(t, client, group.Key)

_, err = client.Groups.GetByKey(group.Key)
require.Error(t, err)
}
}

func cleanUpGroup(t *testing.T, client *teamcity.Client, key string) {
client.Groups.Delete(key)
}
19 changes: 17 additions & 2 deletions teamcity/locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@ func LocatorID(id string) Locator {

//LocatorIDInt creates a locator for a Project/BuildType by Id where the Id's an integer
func LocatorIDInt(id int) Locator {
return Locator(url.QueryEscape("id:") + fmt.Sprintf("%d", id))
return Locator(url.QueryEscape("id:") + fmt.Sprint(id))
}

//LocatorName creates a locator for Project/BuildType by Name
//LocatorName creates a locator for User/Project/BuildType by Name
func LocatorName(name string) Locator {
return Locator(url.QueryEscape("name:") + url.PathEscape(name))
}

//LocatorUsername creates a locator for User by Username
func LocatorUsername(name string) Locator {
return Locator(url.QueryEscape("username:") + url.PathEscape(name))
}

//LocatorKey creates a locator for Group by Key
func LocatorKey(key string) Locator {
return Locator(url.QueryEscape("key:") + url.PathEscape(key))
Expand All @@ -34,6 +39,16 @@ func LocatorType(id string) Locator {
return Locator(url.QueryEscape("type:") + id)
}

//LocatorStart creates a locator to set offset
func LocatorStart(start int) Locator {
return Locator(url.QueryEscape("start:") + fmt.Sprint(start))
}

//LocatorCount creates a locator to set number of answers
func LocatorCount(count int) Locator {
return Locator(url.QueryEscape("count:") + fmt.Sprint(count))
}

func (l Locator) String() string {
return string(l)
}
17 changes: 17 additions & 0 deletions teamcity/query_locator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package teamcity

func buildQueryLocator(locators ...Locator) *queryStruct {
locatorQuery := ""
if len(locators) > 1 {
for _, locator := range locators[1:] {
locatorQuery += "," + locator.String()
}
}
if len(locators) >= 1 {
locatorQuery = locators[0].String() + locatorQuery
}
return &queryStruct{
key: "locator",
value: locatorQuery,
}
}
22 changes: 22 additions & 0 deletions teamcity/query_locator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package teamcity

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestBuildQueryLocator_Empty(t *testing.T) {
q := buildQueryLocator()
require.Equal(t, "", q.value)
}

func TestBuildQueryLocator_OneElement(t *testing.T) {
q := buildQueryLocator(LocatorStart(0))
require.Equal(t, "locator=start%3A0", q.String())
}

func TestBuildQueryLocator_TwoElements(t *testing.T) {
q := buildQueryLocator(LocatorStart(0), LocatorCount(1))
require.Equal(t, "locator=start%3A0,count%3A1", q.String())
}
15 changes: 14 additions & 1 deletion teamcity/rest_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ type restHelper struct {
sling *sling.Sling
}

type queryStruct struct {
key, value string
}

func (q *queryStruct) String() string {
return fmt.Sprintf("%s=%s", q.key, q.value)
}

func newRestHelper(httpClient *http.Client) *restHelper {
return newRestHelperWithSling(httpClient, nil)
}
Expand Down Expand Up @@ -54,8 +62,13 @@ func (r *restHelper) getCustom(path string, out interface{}, resourceDescription
return r.handleRestError(bodyBytes, response.StatusCode, "GET", resourceDescription)
}

func (r *restHelper) get(path string, out interface{}, resourceDescription string) error {
func (r *restHelper) get(path string, out interface{}, resourceDescription string, query ...*queryStruct) error {
request, _ := r.sling.New().Get(path).Request()
for _, q := range query {
if q != nil {
request.URL.Query().Set(q.key, q.value)
}
}
response, err := r.httpClient.Do(request)
if err != nil {
return err
Expand Down
38 changes: 21 additions & 17 deletions teamcity/teamcity.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ type Client struct {

commonBase *sling.Sling

AgentPools *AgentPoolsService
Projects *ProjectService
BuildTypes *BuildTypeService
Server *ServerService
VcsRoots *VcsRootService
Groups *GroupService
RoleAssignments *RoleAssignmentService
AgentPools *AgentPoolsService
Projects *ProjectService
BuildTypes *BuildTypeService
Server *ServerService
VcsRoots *VcsRootService
Groups *GroupService
Users *UserService
UserGroupMemberShip *UserGroupMemberShipService
RoleAssignments *RoleAssignmentService
}

func NewClient(auth Auth, httpClient *http.Client) (*Client, error) {
Expand Down Expand Up @@ -107,16 +109,18 @@ func newClientInstance(auth Auth, address string, httpClient *http.Client) (*Cli
}

return &Client{
address: address,
HTTPClient: httpClient,
commonBase: sharedClient,
AgentPools: newAgentPoolsService(sharedClient.New(), httpClient),
Projects: newProjectService(sharedClient.New(), httpClient),
BuildTypes: newBuildTypeService(sharedClient.New(), httpClient),
Server: newServerService(sharedClient.New()),
VcsRoots: newVcsRootService(sharedClient.New(), httpClient),
Groups: newGroupService(sharedClient.New(), httpClient),
RoleAssignments: newRoleAssignmentService(sharedClient.New(), httpClient),
address: address,
HTTPClient: httpClient,
commonBase: sharedClient,
AgentPools: newAgentPoolsService(sharedClient.New(), httpClient),
Projects: newProjectService(sharedClient.New(), httpClient),
BuildTypes: newBuildTypeService(sharedClient.New(), httpClient),
Server: newServerService(sharedClient.New()),
VcsRoots: newVcsRootService(sharedClient.New(), httpClient),
Groups: newGroupService(sharedClient.New(), httpClient),
Users: newUserService(sharedClient.New(), httpClient),
UserGroupMemberShip: newUserGroupMembershipService(sharedClient.New(), httpClient),
RoleAssignments: newRoleAssignmentService(sharedClient.New(), httpClient),
}, nil
}

Expand Down
Loading