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

Add Copilot endpoints #2973

Merged
merged 40 commits into from Dec 19, 2023
Merged

Add Copilot endpoints #2973

merged 40 commits into from Dec 19, 2023

Conversation

o-sama
Copy link
Contributor

@o-sama o-sama commented Oct 24, 2023

Closes #2938.

@codecov
Copy link

codecov bot commented Oct 24, 2023

Codecov Report

Attention: 3 lines in your changes are missing coverage. Please review.

Comparison is base (a354a6c) 97.71% compared to head (36bc1f4) 97.71%.

Files Patch % Lines
github/copilot.go 98.21% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           master    #2973    +/-   ##
========================================
  Coverage   97.71%   97.71%            
========================================
  Files         151      152     +1     
  Lines       13072    13241   +169     
========================================
+ Hits        12773    12939   +166     
- Misses        211      213     +2     
- Partials       88       89     +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@o-sama
Copy link
Contributor Author

o-sama commented Oct 24, 2023

CodeCov hasn't updated as of last commit, but I think it should increase the coverage to close to 100% for the new file.

Copy link
Collaborator

@gmlewis gmlewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @o-sama - this is looking great!
Just a few tweaks, please, before we ask for a second review.

InactiveThisCycle int64 `json:"inactive_this_cycle"`
}

type CopilotSeats struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing godoc-style comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved
github/copilot_test.go Show resolved Hide resolved
"github.com/google/go-cmp/cmp"
)

// Test invalid JSON responses, vlaid responses are covered in the other tests
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Test invalid JSON responses, vlaid responses are covered in the other tests
// Test invalid JSON responses, valid responses are covered in the other tests

@o-sama
Copy link
Contributor Author

o-sama commented Oct 24, 2023

Thanks @gmlewis, I'll add in the changes! My bad on the http method being PUT, I was doing some other work with S3 and I think I carried that context over.

}

var copilotSeats *CopilotSeats
resp, err := s.client.Do(ctx, req, &copilotSeats)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we handling pagination here for more than 100 users?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be adding support for this, thanks for the callout 🙏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pagination works in current version

Copy link
Contributor

@WillAbides WillAbides left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good over all. I realize I left quite a few comments, but they are mostly nit-picks about naming and comments.

github/copilot.go Outdated Show resolved Hide resolved
return nil, nil, err
}

var SeatCancellations *SeatCancellations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The var name should be downcased "seatCancellations"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, my bad there

return nil, nil, err
}

var SeatCancellations *SeatCancellations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Downcase seatCancellations

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

github/copilot.go Outdated Show resolved Hide resolved
// CopilotSeatDetails represents the details of a Copilot for Business seat.
// Assignee can either be a User, Team, or Organization.
type CopilotSeatDetails struct {
Assignee interface{} `json:"assignee"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be ok to use *User here. There are a few other places where we use *User and let users distinguish using the Type field. @gmlewis will know what the preferred method is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this might be a better option since we're explicitly setting the correct type for the field, but both approaches leave type-checking to the end user.

There are minor differences as far as I'm aware between the types, e.g. Team has no Login field, but has a Name field, which both User and Organization have. Login is used for the username in the case of a user, whereas Name is used for team name.

I'll leave you guys with these thoughts and will be happy to make changes based on what you think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point about Team being different. I think this might be the best way to handle it after all.


// AddCopilotTeams Adds teams to the Copilot for Business subscription for an organization
//
// https://docs.github.com/en/rest/copilot/copilot-for-business#add-teams-to-the-copilot-for-business-subscription-for-an-organization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// https://docs.github.com/en/rest/copilot/copilot-for-business#add-teams-to-the-copilot-for-business-subscription-for-an-organization
// GitHub API docs: https://docs.github.com/en/rest/copilot/copilot-for-business#add-teams-to-the-copilot-for-business-subscription-for-an-organization

github/copilot.go Outdated Show resolved Hide resolved
github/copilot.go Outdated Show resolved Hide resolved

// RemoveCopilotUsers Removes users from the Copilot for Business subscription for an organization
//
// https://docs.github.com/en/rest/copilot/copilot-for-business#remove-users-from-the-copilot-for-business-subscription-for-an-organization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// https://docs.github.com/en/rest/copilot/copilot-for-business#remove-users-from-the-copilot-for-business-subscription-for-an-organization
// GitHub API docs: https://docs.github.com/en/rest/copilot/copilot-for-business#remove-users-from-the-copilot-for-business-subscription-for-an-organization


// GetSeatDetails Gets Copilot for Business seat assignment details for a user
//
// https://docs.github.com/en/rest/copilot/copilot-for-business#get-copilot-for-business-seat-assignment-details-for-a-user
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// https://docs.github.com/en/rest/copilot/copilot-for-business#get-copilot-for-business-seat-assignment-details-for-a-user
// GitHub API docs: https://docs.github.com/en/rest/copilot/copilot-for-business#get-copilot-for-business-seat-assignment-details-for-a-user

@gmlewis
Copy link
Collaborator

gmlewis commented Oct 24, 2023

Thank you, @WillAbides for your review feedback and @gauraw10 for your excellent pagination question which I seem to frequently forget about until it becomes an issue later, unfortunately. It is definitely better to add pagination support up front rather than later. 😁

I've got some work to do but see that there are some questions for me, so I will try to circle back later this afternoon and address them.

Comment on lines 14 to 18
tests := map[string]struct {
data string
want *CopilotSeatDetails
wantErr bool
}{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be:

tests := map[string]struct {
  name string
  data string
} {
  {
    name: "invalid JSON",
    data: `{`,
  },
...
}
  

Two things changed here:

  • Make it a slice instead of a map for defined order when testing.
  • Because this only tests invalid, the "want" and "wantErr" fields are always the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make that change but want your thoughts on keeping the want and wantErr fields. Reason being is as of right now I'm only testing invalid cases and the unmarshaling isn't too complex, but in the future we might be adding valid cases here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll defer to your judgement on want and wantErr. We may or may not need them in the future, and it doesn't hurt anything to keep them around.

seatDetails := &CopilotSeatDetails{}

t.Run(name, func(t *testing.T) {
err := seatDetails.UnmarshalJSON([]byte(tc.data))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to test with json.Unmarshal instead of directly calling UnmarshalJSON because that is how users will call it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call 🙂

@o-sama
Copy link
Contributor Author

o-sama commented Oct 24, 2023

Apologies guys, clearly this isn't my best work. I really appreciate the review comments. I'll apply most suggestions and reply to the few that I think we should discuss more.

@gauraw10 Similarly to @gmlewis, I tend to forget about pagination sometimes. I'll add support for that.

Co-authored-by: WillAbides <233500+WillAbides@users.noreply.github.com>
Co-authored-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com>
@gmlewis
Copy link
Collaborator

gmlewis commented Oct 24, 2023

Watching the conversations go by, I'm wondering if it might be nice to have 3 helper methods on the struct like GetUser() (*User, bool), etc.

Thoughts?

@o-sama
Copy link
Contributor Author

o-sama commented Oct 24, 2023

@gmlewis Would that be while keeping Assignee as interface{}? I'm in favour of that since it allows easy checking/fetching with if, ok.

@gmlewis
Copy link
Collaborator

gmlewis commented Oct 24, 2023

@gmlewis Would that be while keeping Assignee as interface{}? I'm in favour of that since it allows easy checking/fetching with if, ok.

Yes, that's what I was thinking might be nice, but I'm open to ideas.

@WillAbides
Copy link
Contributor

I think that UnmarshalJSON will panic if "type" is not set. Would you add a test case where "type" isn't set and fix the issue if it panics?

@o-sama
Copy link
Contributor Author

o-sama commented Oct 25, 2023

Good call, I wanted to expand coverage for that 🙂

What do you think about not returning errors for unmarshaling the Assignee field once we've done the type assertions? I'm not sure we would get errors there since we know the map matches the type of the struct we're unmarshaling into, and I can't think of a way to test for that.

@WillAbides
Copy link
Contributor

In general, I would much rather have untested error branches than trust our assumption that an error can't happen and end up with a panic. Even though codecov puts a big, ugly ❌ on your PR, it isn't a hard rule that test coverage can't decrease.

That said, I think it will be possible to cover those cases with just the right json data. Let me have a look and see if I can come up with something.

@WillAbides
Copy link
Contributor

@o-sama I see you have already cases that should cover those lines but don't. I think the problem is the comma at the end of "id": "bad",. That's causing the first json.Unmarshal to return an error so it doesn't make it to the part you are trying to test.

@o-sama
Copy link
Contributor Author

o-sama commented Oct 25, 2023

Ah good catch @WillAbides, it's always the commas 🙃

@gmlewis
Copy link
Collaborator

gmlewis commented Dec 19, 2023

@gmlewis I know that second lgtm is needed. However, is there somekind of way to find that another person who could give that LGTM? This has been waiting for sometime and at least we would like to use this feature.

OK, first let me resolve the merge conflicts, @zetaab - then maybe @valbeat has time to take a look at this one.

caseyduquettesc and others added 20 commits December 19, 2023 08:16
@gmlewis gmlewis changed the title Add Copilot Endpoints Add Copilot endpoints Dec 19, 2023
@gmlewis
Copy link
Collaborator

gmlewis commented Dec 19, 2023

@zetaab - you yourself could move this PR along by performing a review and giving it an LGTM+Approval.

Copy link
Contributor

@zetaab zetaab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find anything else than that one type change. I am currently coding against this PR and it was little bit weird that the type is not similar than other timestamps.

// Assignee can either be a User, Team, or Organization.
Assignee interface{} `json:"assignee"`
AssigningTeam *Team `json:"assigning_team,omitempty"`
PendingCancellationDate *string `json:"pending_cancellation_date,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type should be *Timestamp instead that it works in similar way than other time fields.

}

var copilotSeats *CopilotSeats
resp, err := s.client.Do(ctx, req, &copilotSeats)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pagination works in current version

Copy link
Contributor

@zetaab zetaab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually that PendingCancellationDate field is date so perhaps leave it like this.

LGTM

@gmlewis
Copy link
Collaborator

gmlewis commented Dec 19, 2023

Thank you, @zetaab !
Merging.

@gmlewis gmlewis merged commit 9d6658a into google:master Dec 19, 2023
7 checks passed
@zetaab zetaab mentioned this pull request Dec 19, 2023
@gmlewis gmlewis removed the NeedsReview PR is awaiting a review before merging. label Dec 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for Copilot API