From 83cac3c523f6598f266f349699f3ab320fd9815b Mon Sep 17 00:00:00 2001 From: tomfeigin <10672011+tomfeigin@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:14:04 +0300 Subject: [PATCH] Support querying organization custom roles Added support for querying, creating, updating and deleting organization custom roles. --- github/github-accessors.go | 54 ++++++++++- github/github-accessors_test.go | 78 ++++++++++++++-- github/orgs_custom_roles.go | 133 ++++++++++++++++++++++++--- github/orgs_custom_roles_test.go | 149 ++++++++++++++++++++++++++++++- 4 files changed, 390 insertions(+), 24 deletions(-) diff --git a/github/github-accessors.go b/github/github-accessors.go index 57a7165f7f..2f497508cb 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -4351,7 +4351,7 @@ func (c *CreateOrgInvitationOptions) GetRole() string { } // GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. -func (c *CreateOrUpdateCustomRoleOptions) GetBaseRole() string { +func (c *CreateOrUpdateCustomRepoRoleOptions) GetBaseRole() string { if c == nil || c.BaseRole == nil { return "" } @@ -4359,7 +4359,7 @@ func (c *CreateOrUpdateCustomRoleOptions) GetBaseRole() string { } // GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (c *CreateOrUpdateCustomRoleOptions) GetDescription() string { +func (c *CreateOrUpdateCustomRepoRoleOptions) GetDescription() string { if c == nil || c.Description == nil { return "" } @@ -4367,7 +4367,23 @@ func (c *CreateOrUpdateCustomRoleOptions) GetDescription() string { } // GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CreateOrUpdateCustomRoleOptions) GetName() string { +func (c *CreateOrUpdateCustomRepoRoleOptions) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateOrUpdateOrgRoleOptions) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CreateOrUpdateOrgRoleOptions) GetName() string { if c == nil || c.Name == nil { return "" } @@ -4686,6 +4702,30 @@ func (c *CustomDeploymentProtectionRuleRequest) GetIntegrationID() int64 { return *c.IntegrationID } +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CustomOrgRoles) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CustomOrgRoles) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CustomOrgRoles) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + // GetDefaultValue returns the DefaultValue field if it's non-nil, zero value otherwise. func (c *CustomProperty) GetDefaultValue() string { if c == nil || c.DefaultValue == nil { @@ -12598,6 +12638,14 @@ func (o *OrganizationCustomRepoRoles) GetTotalCount() int { return *o.TotalCount } +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (o *OrganizationCustomRoles) GetTotalCount() int { + if o == nil || o.TotalCount == nil { + return 0 + } + return *o.TotalCount +} + // GetAction returns the Action field if it's non-nil, zero value otherwise. func (o *OrganizationEvent) GetAction() string { if o == nil || o.Action == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index bbf9fb8422..7b4424e9b3 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -5105,31 +5105,51 @@ func TestCreateOrgInvitationOptions_GetRole(tt *testing.T) { c.GetRole() } -func TestCreateOrUpdateCustomRoleOptions_GetBaseRole(tt *testing.T) { +func TestCreateOrUpdateCustomRepoRoleOptions_GetBaseRole(tt *testing.T) { var zeroValue string - c := &CreateOrUpdateCustomRoleOptions{BaseRole: &zeroValue} + c := &CreateOrUpdateCustomRepoRoleOptions{BaseRole: &zeroValue} c.GetBaseRole() - c = &CreateOrUpdateCustomRoleOptions{} + c = &CreateOrUpdateCustomRepoRoleOptions{} c.GetBaseRole() c = nil c.GetBaseRole() } -func TestCreateOrUpdateCustomRoleOptions_GetDescription(tt *testing.T) { +func TestCreateOrUpdateCustomRepoRoleOptions_GetDescription(tt *testing.T) { var zeroValue string - c := &CreateOrUpdateCustomRoleOptions{Description: &zeroValue} + c := &CreateOrUpdateCustomRepoRoleOptions{Description: &zeroValue} c.GetDescription() - c = &CreateOrUpdateCustomRoleOptions{} + c = &CreateOrUpdateCustomRepoRoleOptions{} c.GetDescription() c = nil c.GetDescription() } -func TestCreateOrUpdateCustomRoleOptions_GetName(tt *testing.T) { +func TestCreateOrUpdateCustomRepoRoleOptions_GetName(tt *testing.T) { var zeroValue string - c := &CreateOrUpdateCustomRoleOptions{Name: &zeroValue} + c := &CreateOrUpdateCustomRepoRoleOptions{Name: &zeroValue} c.GetName() - c = &CreateOrUpdateCustomRoleOptions{} + c = &CreateOrUpdateCustomRepoRoleOptions{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateOrUpdateOrgRoleOptions_GetDescription(tt *testing.T) { + var zeroValue string + c := &CreateOrUpdateOrgRoleOptions{Description: &zeroValue} + c.GetDescription() + c = &CreateOrUpdateOrgRoleOptions{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateOrUpdateOrgRoleOptions_GetName(tt *testing.T) { + var zeroValue string + c := &CreateOrUpdateOrgRoleOptions{Name: &zeroValue} + c.GetName() + c = &CreateOrUpdateOrgRoleOptions{} c.GetName() c = nil c.GetName() @@ -5513,6 +5533,36 @@ func TestCustomDeploymentProtectionRuleRequest_GetIntegrationID(tt *testing.T) { c.GetIntegrationID() } +func TestCustomOrgRoles_GetDescription(tt *testing.T) { + var zeroValue string + c := &CustomOrgRoles{Description: &zeroValue} + c.GetDescription() + c = &CustomOrgRoles{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCustomOrgRoles_GetID(tt *testing.T) { + var zeroValue int64 + c := &CustomOrgRoles{ID: &zeroValue} + c.GetID() + c = &CustomOrgRoles{} + c.GetID() + c = nil + c.GetID() +} + +func TestCustomOrgRoles_GetName(tt *testing.T) { + var zeroValue string + c := &CustomOrgRoles{Name: &zeroValue} + c.GetName() + c = &CustomOrgRoles{} + c.GetName() + c = nil + c.GetName() +} + func TestCustomProperty_GetDefaultValue(tt *testing.T) { var zeroValue string c := &CustomProperty{DefaultValue: &zeroValue} @@ -14746,6 +14796,16 @@ func TestOrganizationCustomRepoRoles_GetTotalCount(tt *testing.T) { o.GetTotalCount() } +func TestOrganizationCustomRoles_GetTotalCount(tt *testing.T) { + var zeroValue int + o := &OrganizationCustomRoles{TotalCount: &zeroValue} + o.GetTotalCount() + o = &OrganizationCustomRoles{} + o.GetTotalCount() + o = nil + o.GetTotalCount() +} + func TestOrganizationEvent_GetAction(tt *testing.T) { var zeroValue string o := &OrganizationEvent{Action: &zeroValue} diff --git a/github/orgs_custom_roles.go b/github/orgs_custom_roles.go index 45de896a2f..f98953d457 100644 --- a/github/orgs_custom_roles.go +++ b/github/orgs_custom_roles.go @@ -10,6 +10,20 @@ import ( "fmt" ) +// OrganizationCustomRoles represents custom organization roles available in specified organization. +type OrganizationCustomRoles struct { + TotalCount *int `json:"total_count,omitempty"` + CustomRepoRoles []*CustomOrgRoles `json:"roles,omitempty"` +} + +// CustomOrgRoles represents custom organization role available in specified organization. +type CustomOrgRoles struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Permissions []string `json:"permissions,omitempty"` +} + // OrganizationCustomRepoRoles represents custom repository roles available in specified organization. type OrganizationCustomRepoRoles struct { TotalCount *int `json:"total_count,omitempty"` @@ -27,6 +41,113 @@ type CustomRepoRoles struct { Permissions []string `json:"permissions,omitempty"` } +// CreateOrUpdateOrgRoleOptions represents options required to create or update a custom organization role. +type CreateOrUpdateOrgRoleOptions struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Permissions []string `json:"permissions,omitempty"` +} + +// CreateOrUpdateCustomRepoRoleOptions represents options required to create or update a custom repository role. +type CreateOrUpdateCustomRepoRoleOptions struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + BaseRole *string `json:"base_role,omitempty"` + Permissions []string `json:"permissions,omitempty"` +} + +// ListRoles lists the custom roles available in this organization. +// In order to see custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles#get-all-organization-roles-for-an-organization +// +//meta:operation GET /orgs/{org}/organization-roles +func (s *OrganizationsService) ListRoles(ctx context.Context, org string) (*OrganizationCustomRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles", org) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + customRepoRoles := new(OrganizationCustomRoles) + resp, err := s.client.Do(ctx, req, customRepoRoles) + if err != nil { + return nil, resp, err + } + + return customRepoRoles, resp, nil +} + +// CreateCustomOrgRole creates a custom role in this organization. +// In order to create custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles#create-a-custom-organization-role +// +//meta:operation POST /orgs/{org}/organization-roles +func (s *OrganizationsService) CreateCustomOrgRole(ctx context.Context, org string, opts *CreateOrUpdateOrgRoleOptions) (*CustomOrgRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles", org) + + req, err := s.client.NewRequest("POST", u, opts) + if err != nil { + return nil, nil, err + } + + resultingRole := new(CustomOrgRoles) + resp, err := s.client.Do(ctx, req, resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// UpdateCustomOrgRole updates a custom role in this organization. +// In order to update custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles#update-a-custom-organization-role +// +//meta:operation PATCH /orgs/{org}/organization-roles/{role_id} +func (s *OrganizationsService) UpdateCustomOrgRole(ctx context.Context, org, roleID string, opts *CreateOrUpdateOrgRoleOptions) (*CustomOrgRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v", org, roleID) + + req, err := s.client.NewRequest("PATCH", u, opts) + if err != nil { + return nil, nil, err + } + + resultingRole := new(CustomOrgRoles) + resp, err := s.client.Do(ctx, req, resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// DeleteCustomOrgRole deletes an existing custom role in this organization. +// In order to delete custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles#delete-a-custom-organization-role +// +//meta:operation DELETE /orgs/{org}/organization-roles/{role_id} +func (s *OrganizationsService) DeleteCustomOrgRole(ctx context.Context, org, roleID string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v", org, roleID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + resultingRole := new(CustomOrgRoles) + resp, err := s.client.Do(ctx, req, resultingRole) + if err != nil { + return resp, err + } + + return resp, nil +} + // ListCustomRepoRoles lists the custom repository roles available in this organization. // In order to see custom repository roles in an organization, the authenticated user must be an organization owner. // @@ -50,21 +171,13 @@ func (s *OrganizationsService) ListCustomRepoRoles(ctx context.Context, org stri return customRepoRoles, resp, nil } -// CreateOrUpdateCustomRoleOptions represents options required to create or update a custom repository role. -type CreateOrUpdateCustomRoleOptions struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - BaseRole *string `json:"base_role,omitempty"` - Permissions []string `json:"permissions,omitempty"` -} - // CreateCustomRepoRole creates a custom repository role in this organization. // In order to create custom repository roles in an organization, the authenticated user must be an organization owner. // // GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#create-a-custom-repository-role // //meta:operation POST /orgs/{org}/custom-repository-roles -func (s *OrganizationsService) CreateCustomRepoRole(ctx context.Context, org string, opts *CreateOrUpdateCustomRoleOptions) (*CustomRepoRoles, *Response, error) { +func (s *OrganizationsService) CreateCustomRepoRole(ctx context.Context, org string, opts *CreateOrUpdateCustomRepoRoleOptions) (*CustomRepoRoles, *Response, error) { u := fmt.Sprintf("orgs/%v/custom-repository-roles", org) req, err := s.client.NewRequest("POST", u, opts) @@ -87,7 +200,7 @@ func (s *OrganizationsService) CreateCustomRepoRole(ctx context.Context, org str // GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#update-a-custom-repository-role // //meta:operation PATCH /orgs/{org}/custom-repository-roles/{role_id} -func (s *OrganizationsService) UpdateCustomRepoRole(ctx context.Context, org, roleID string, opts *CreateOrUpdateCustomRoleOptions) (*CustomRepoRoles, *Response, error) { +func (s *OrganizationsService) UpdateCustomRepoRole(ctx context.Context, org, roleID string, opts *CreateOrUpdateCustomRepoRoleOptions) (*CustomRepoRoles, *Response, error) { u := fmt.Sprintf("orgs/%v/custom-repository-roles/%v", org, roleID) req, err := s.client.NewRequest("PATCH", u, opts) diff --git a/github/orgs_custom_roles_test.go b/github/orgs_custom_roles_test.go index 08810909a0..7cb91633b7 100644 --- a/github/orgs_custom_roles_test.go +++ b/github/orgs_custom_roles_test.go @@ -14,6 +14,151 @@ import ( "github.com/google/go-cmp/cmp" ) +func TestOrganizationsService_ListRoles(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/organization-roles", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count": 1, "roles": [{ "id": 1, "name": "Auditor", "permissions": ["read_audit_logs"]}]}`) + }) + + ctx := context.Background() + apps, _, err := client.Organizations.ListRoles(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListRoles returned error: %v", err) + } + + want := &OrganizationCustomRoles{TotalCount: Int(1), CustomRepoRoles: []*CustomOrgRoles{{ID: Int64(1), Name: String("Auditor"), Permissions: []string{"read_audit_logs"}}}} + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListRoles returned %+v, want %+v", apps, want) + } + + const methodName = "ListRoles" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListRoles(ctx, "\no") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListRoles(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateCustomOrgRole(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/organization-roles", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":8030,"name":"Reader","description":"A role for reading custom org roles","permissions":["read_organization_custom_org_role"]}`) + }) + + ctx := context.Background() + + opts := &CreateOrUpdateOrgRoleOptions{ + Name: String("Reader"), + Description: String("A role for reading custom org roles"), + Permissions: []string{"read_organization_custom_org_role"}, + } + gotRoles, _, err := client.Organizations.CreateCustomOrgRole(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.CreateCustomOrgRole returned error: %v", err) + } + + want := &CustomOrgRoles{ID: Int64(8030), Name: String("Reader"), Permissions: []string{"read_organization_custom_org_role"}, Description: String("A role for reading custom org roles")} + + if !cmp.Equal(gotRoles, want) { + t.Errorf("Organizations.CreateCustomOrgRole returned %+v, want %+v", gotRoles, want) + } + + const methodName = "CreateCustomOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateCustomOrgRole(ctx, "\no", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateCustomOrgRole(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateCustomOrgRole(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/organization-roles/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"id":8030,"name":"Updated Name","description":"Updated Description","permissions":["read_organization_custom_org_role"]}`) + }) + + ctx := context.Background() + + opts := &CreateOrUpdateOrgRoleOptions{ + Name: String("Updated Name"), + Description: String("Updated Description"), + } + gotRoles, _, err := client.Organizations.UpdateCustomOrgRole(ctx, "o", "8030", opts) + if err != nil { + t.Errorf("Organizations.UpdateCustomOrgRole returned error: %v", err) + } + + want := &CustomOrgRoles{ID: Int64(8030), Name: String("Updated Name"), Permissions: []string{"read_organization_custom_org_role"}, Description: String("Updated Description")} + + if !cmp.Equal(gotRoles, want) { + t.Errorf("Organizations.UpdateCustomOrgRole returned %+v, want %+v", gotRoles, want) + } + + const methodName = "UpdateCustomOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateCustomOrgRole(ctx, "\no", "8030", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateCustomOrgRole(ctx, "o", "8030", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteCustomOrgRole(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/organization-roles/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := context.Background() + + resp, err := client.Organizations.DeleteCustomOrgRole(ctx, "o", "8030") + if err != nil { + t.Errorf("Organizations.DeleteCustomOrgRole returned error: %v", err) + } + + if !cmp.Equal(resp.StatusCode, 204) { + t.Errorf("Organizations.DeleteCustomOrgRole returned status code %+v, want %+v", resp.StatusCode, "204") + } + + const methodName = "DeleteCustomOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DeleteCustomOrgRole(ctx, "\no", "8030") + return err + }) +} + func TestOrganizationsService_ListCustomRepoRoles(t *testing.T) { client, mux, _, teardown := setup() defer teardown() @@ -60,7 +205,7 @@ func TestOrganizationsService_CreateCustomRepoRole(t *testing.T) { ctx := context.Background() - opts := &CreateOrUpdateCustomRoleOptions{ + opts := &CreateOrUpdateCustomRepoRoleOptions{ Name: String("Labeler"), Description: String("A role for issue and PR labelers"), BaseRole: String("read"), @@ -103,7 +248,7 @@ func TestOrganizationsService_UpdateCustomRepoRole(t *testing.T) { ctx := context.Background() - opts := &CreateOrUpdateCustomRoleOptions{ + opts := &CreateOrUpdateCustomRepoRoleOptions{ Name: String("Updated Name"), Description: String("Updated Description"), }