Skip to content
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
92 changes: 92 additions & 0 deletions github/enterprise_licenses.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,34 @@ type LastLicenseSyncProperties struct {
Error string `json:"error"`
}

// VisualStudioSubscriptionAssignment represents a user's Visual Studio subscription assignment.
type VisualStudioSubscriptionAssignment struct {
VisualStudioSubscriptionEmail *string `json:"visual_studio_subscription_email,omitempty"`
SubscriptionID *string `json:"subscription_id,omitempty"`
Username *string `json:"username,omitempty"`
ManualMatch *bool `json:"manual_match,omitempty"`
}

// VisualStudioSubscriptions represents a list of Visual Studio subscriptions for an enterprise.
type VisualStudioSubscriptions struct {
TotalCount *int `json:"total_count,omitempty"`
VisualStudioSubscriptions []*VisualStudioSubscriptionAssignment `json:"visual_studio_subscriptions,omitempty"`
}

// VisualStudioSubscriptionAssignmentRequest represents the request body to add or update a subscription assignment.
type VisualStudioSubscriptionAssignmentRequest struct {
UserIdentifier *string `json:"user_identifier,omitempty"`
}

// ListVisualStudioSubscriptionsOptions specifies the optional parameters to
// EnterpriseService.ListVisualStudioSubscriptions.
type ListVisualStudioSubscriptionsOptions struct {
ListOptions

// IsUnmatchedOnly filters results to return only unmatched subscriptions.
IsUnmatchedOnly bool `url:"is_unmatched_only,omitempty"`
}

// ListConsumedLicenses collect information about the number of consumed licenses and a collection with all the users with consumed enterprise licenses.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing?apiVersion=2022-11-28#list-enterprise-consumed-licenses
Expand Down Expand Up @@ -136,3 +164,67 @@ func (s *EnterpriseService) GetLicenseSyncStatus(ctx context.Context, enterprise

return syncStatus, resp, nil
}

// ListVisualStudioSubscriptions gets a list of Visual Studio subscriptions for an enterprise.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing?apiVersion=2022-11-28#get-a-list-of-visual-studio-subscriptions-for-the-enterprise
//
//meta:operation GET /enterprises/{enterprise}/visual-studio-subscriptions
func (s *EnterpriseService) ListVisualStudioSubscriptions(ctx context.Context, enterprise string, opts *ListVisualStudioSubscriptionsOptions) (*VisualStudioSubscriptions, *Response, error) {
u := fmt.Sprintf("enterprises/%v/visual-studio-subscriptions", enterprise)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest(ctx, "GET", u, nil)
if err != nil {
return nil, nil, err
}

var subscriptions *VisualStudioSubscriptions
resp, err := s.client.Do(req, &subscriptions)
if err != nil {
return nil, resp, err
}

return subscriptions, resp, nil
}

// AddOrUpdateVisualStudioSubscriptionAssignment adds or updates a manual match between a user and a Visual Studio subscription.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing?apiVersion=2022-11-28#add-or-update-a-visual-studio-subscription-user-match
//
//meta:operation PUT /enterprises/{enterprise}/visual-studio-subscriptions/{visual_studio_subscription_id}
func (s *EnterpriseService) AddOrUpdateVisualStudioSubscriptionAssignment(ctx context.Context, enterprise, subscriptionID string, body VisualStudioSubscriptionAssignmentRequest) (*VisualStudioSubscriptionAssignment, *Response, error) {
u := fmt.Sprintf("enterprises/%v/visual-studio-subscriptions/%v", enterprise, subscriptionID)

req, err := s.client.NewRequest(ctx, "PUT", u, body)
if err != nil {
return nil, nil, err
}

var assignment *VisualStudioSubscriptionAssignment
resp, err := s.client.Do(req, &assignment)
if err != nil {
return nil, resp, err
}

return assignment, resp, nil
}

// DeleteVisualStudioSubscriptionAssignment deletes a manual match between a user and a Visual Studio subscription.
//
// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing?apiVersion=2022-11-28#delete-a-visual-studio-subscription-user-match
//
//meta:operation DELETE /enterprises/{enterprise}/visual-studio-subscriptions/{visual_studio_subscription_id}
func (s *EnterpriseService) DeleteVisualStudioSubscriptionAssignment(ctx context.Context, enterprise, subscriptionID string) (*Response, error) {
u := fmt.Sprintf("enterprises/%v/visual-studio-subscriptions/%v", enterprise, subscriptionID)

req, err := s.client.NewRequest(ctx, "DELETE", u, nil)
if err != nil {
return nil, err
}

return s.client.Do(req, nil)
}
140 changes: 140 additions & 0 deletions github/enterprise_licenses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,143 @@ func TestEnterpriseService_GetLicenseSyncStatus(t *testing.T) {
return resp, err
})
}

func TestEnterpriseService_ListVisualStudioSubscriptions(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/enterprises/e/visual-studio-subscriptions", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"page": "1", "per_page": "10", "is_unmatched_only": "true"})
fmt.Fprint(w, `{
"total_count": 1,
"visual_studio_subscriptions": [{
"visual_studio_subscription_email": "user@example.com",
"subscription_id": "sub-123",
"username": "monalisa",
"manual_match": true
}]
}`)
})

opt := &ListVisualStudioSubscriptionsOptions{
ListOptions: ListOptions{Page: 1, PerPage: 10},
IsUnmatchedOnly: true,
}
ctx := t.Context()
subscriptions, _, err := client.Enterprise.ListVisualStudioSubscriptions(ctx, "e", opt)
if err != nil {
t.Errorf("Enterprise.ListVisualStudioSubscriptions returned error: %v", err)
}

want := &VisualStudioSubscriptions{
TotalCount: Ptr(1),
VisualStudioSubscriptions: []*VisualStudioSubscriptionAssignment{
{
VisualStudioSubscriptionEmail: Ptr("user@example.com"),
SubscriptionID: Ptr("sub-123"),
Username: Ptr("monalisa"),
ManualMatch: Ptr(true),
},
},
}

if !cmp.Equal(subscriptions, want) {
t.Errorf("Enterprise.ListVisualStudioSubscriptions returned %+v, want %+v", subscriptions, want)
}

const methodName = "ListVisualStudioSubscriptions"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Enterprise.ListVisualStudioSubscriptions(ctx, "\n", opt)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.Enterprise.ListVisualStudioSubscriptions(ctx, "e", opt)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}

func TestEnterpriseService_AddOrUpdateVisualStudioSubscriptionAssignment(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

input := VisualStudioSubscriptionAssignmentRequest{
UserIdentifier: Ptr("monalisa"),
}

mux.HandleFunc("/enterprises/e/visual-studio-subscriptions/sub-123", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
testJSONBody(t, r, input)
fmt.Fprint(w, `{
"visual_studio_subscription_email": "user@example.com",
"subscription_id": "sub-123",
"username": "monalisa",
"manual_match": true
}`)
})

ctx := t.Context()
assignment, _, err := client.Enterprise.AddOrUpdateVisualStudioSubscriptionAssignment(ctx, "e", "sub-123", input)
if err != nil {
t.Errorf("Enterprise.AddOrUpdateVisualStudioSubscriptionAssignment returned error: %v", err)
}

want := &VisualStudioSubscriptionAssignment{
VisualStudioSubscriptionEmail: Ptr("user@example.com"),
SubscriptionID: Ptr("sub-123"),
Username: Ptr("monalisa"),
ManualMatch: Ptr(true),
}

if !cmp.Equal(assignment, want) {
t.Errorf("Enterprise.AddOrUpdateVisualStudioSubscriptionAssignment returned %+v, want %+v", assignment, want)
}

const methodName = "AddOrUpdateVisualStudioSubscriptionAssignment"
testBadOptions(t, methodName, func() (err error) {
_, _, err = client.Enterprise.AddOrUpdateVisualStudioSubscriptionAssignment(ctx, "\n", "sub-123", input)
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
got, resp, err := client.Enterprise.AddOrUpdateVisualStudioSubscriptionAssignment(ctx, "e", "sub-123", input)
if got != nil {
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
}
return resp, err
})
}

func TestEnterpriseService_DeleteVisualStudioSubscriptionAssignment(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/enterprises/e/visual-studio-subscriptions/sub-123", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusNoContent)
})

ctx := t.Context()
resp, err := client.Enterprise.DeleteVisualStudioSubscriptionAssignment(ctx, "e", "sub-123")
if err != nil {
t.Errorf("Enterprise.DeleteVisualStudioSubscriptionAssignment returned error: %v", err)
}

if resp.StatusCode != http.StatusNoContent {
t.Errorf("Enterprise.DeleteVisualStudioSubscriptionAssignment status code = %v, want %v", resp.StatusCode, http.StatusNoContent)
}

const methodName = "DeleteVisualStudioSubscriptionAssignment"
testBadOptions(t, methodName, func() (err error) {
_, err = client.Enterprise.DeleteVisualStudioSubscriptionAssignment(ctx, "\n", "sub-123")
return err
})

testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
return client.Enterprise.DeleteVisualStudioSubscriptionAssignment(ctx, "e", "sub-123")
})
}
64 changes: 64 additions & 0 deletions github/github-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading