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

#720 Initial commit for listing Plans and Plan Accounts #732

Merged
merged 14 commits into from
Nov 14, 2017

Conversation

lackerman
Copy link
Contributor

  • Includes basic testing for Listing Plans and the Accounts linked to a Plan (including stubs)

NOTE This is currently awaiting scrutiny as it's my first contribution to the project. I would appreciate any feedback (good or bad) and the 👍 if it's ok to carry on with the rest of the implementation for #720

* Includes basic testing for Listing Plans and the Accounts linked to a Plan (including stubs)
@googlebot
Copy link

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If your company signed a CLA, they designated a Point of Contact who decides which employees are authorized to participate. You may need to contact the Point of Contact for your company and ask to be added to the group of authorized contributors. If you don't know who your Point of Contact is, direct the project maintainer to go/cla#troubleshoot.
  • In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again.

Copy link
Member

@dmitshur dmitshur left a comment

Choose a reason for hiding this comment

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

@lackerman, this is a fantastic start!

It seems that we'll need to make some new decisions for this issue, because these endpoints offer something no other GitHub API endpoints have offered in the past: the alternative stubbed endpoints. /cc @gmlewis

I will think a little more about this, but so far, I think we should factor out that stubbed bool argument from each method. Perhaps, instead, it should be a new option that we add to github.Client. What do you think about that?

Aside from that, I spotted and pointed out some minor issues with missing periods at ends of sentences, and missing ,omitempty JSON tag options.

Plan *MarketplacePlan `json:"plan"`
}

// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan
Copy link
Member

Choose a reason for hiding this comment

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

In general, I think we should strive to have all sentences in Go documentation to have no spelling or grammar mistakes, and to include periods at ends of sentences. So, I'll point out that you're missing a period at the end of this sentence.

MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase"`
}

// ListPlans lists all plans for your Marketplace listing
Copy link
Member

Choose a reason for hiding this comment

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

Also missing a period at end of this sentence.

return i, resp, nil
}

// ListPlanAccounts lists all GitHub accounts (user or organization) on a specific plan
Copy link
Member

Choose a reason for hiding this comment

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

And this one.

YearlyPriceInCents *int `json:"yearly_price_in_cents"`
PriceModel *string `json:"price_model"`
UnitName *string `json:"unit_name"`
Bullets *[]string `json:"bullets"`
Copy link
Member

Choose a reason for hiding this comment

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

For consistency with other structs that we map to GitHub responses, all of these should have ,omitempty JSON tag option. E.g., see:

type Issue struct {
ID *int `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Locked *bool `json:"locked,omitempty"`
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
Labels []Label `json:"labels,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Comments *int `json:"comments,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedBy *User `json:"closed_by,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"`
Repository *Repository `json:"repository,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
// TextMatches is only populated from search results that request text matches
// See: search.go and https://developer.github.com/v3/search/#text-match-metadata
TextMatches []TextMatch `json:"text_matches,omitempty"`
}

It doesn't make a difference for unmarshaling JSON, but it does for marshaling. I don't think these will be marshaled (since we're receiving data from GitHub API, not sending these upstream). So, the only reason to do it is to maintain consistency.

See issue #537 for some more background information on this.

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 was wondering why they were there. I didn't realise that they had no impact on unmarshalling (but it makes perfect sense). Happy to maintain the consistency.

@lackerman
Copy link
Contributor Author

I signed it!

@googlebot
Copy link

CLAs look good, thanks!

@googlebot googlebot added cla: yes Indication that the PR author has signed a Google Contributor License Agreement. and removed cla: no labels Sep 28, 2017
@lackerman
Copy link
Contributor Author

@shurcooL Regarding

...I think we should factor out that stubbed bool argument from each method. Perhaps, instead, it should be a new option that we add to github.Client. What do you think about that?...

I'm so happy you said that. The flag felt like suck a hack. Not sure I have enough context or experience to comment on the preferred solution, but I'm happy to implement it ;).

@lackerman
Copy link
Contributor Author

lackerman commented Oct 1, 2017

Hi @shurcooL, could you perhaps tell me why the build is failing? I see it's during the go generate step, but I'm not sure how the code I've written has resulted in a build failure.

@elliott-beach
Copy link
Contributor

@lackerman You need to run the command go generate and commit and push, this will generate some accessors for structs.

@dmitshur
Copy link
Member

dmitshur commented Oct 3, 2017

Nice work on resolving the build failure.

I'm so happy you said that. The flag felt like suck a hack. Not sure I have enough context or experience to comment on the preferred solution, but I'm happy to implement it ;).

Great! The next step here, as far as I can tell, is to factor out that stubbed bool argument from all the new methods.

Looking at it a bit closer, I think we can move it into MarketplaceService struct rather than into the github.Client struct. Perhaps it can look something like this:

// MarketplaceService handles communication with the marketplace related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
type MarketplaceService struct {
	client *Client

	// Stubbed controls whether endpoints that return stubbed data are used
	// instead of production endpoints. Stubbed data is fake data that's useful
	// for testing your GitHub Apps. Stubbed data is hard-coded and will not
	// change based on actual subscriptions.
	//
	// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
	Stubbed bool
}

Then, users can set it to true if they wish:

client := github.NewClient(nil)
client.MarketplaceService.Stubbed = true
// ...

@lackerman
Copy link
Contributor Author

Hi @shurcooL what about using a Options wrapper?

// MarketplaceOptions specifies the optional parameters to the Marketplace service methods
type MarketplaceOptions struct {
	// Stubbed controls whether endpoints that return stubbed data are used
	// instead of production endpoints. Stubbed data is fake data that's useful
	// for testing your GitHub Apps. Stubbed data is hard-coded and will not
	// change based on actual subscriptions.
	//
	// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
	Stubbed bool

	ListOptions
}

And then only passing ListOptions to addOptions

uri := marketplaceURI(opt, "plans")
u, err := addOptions(uri, opt.ListOptions)

Or is this pattern specifically used for optional query parameters in the Url?

@dmitshur
Copy link
Member

MarketplaceOptions would work too. So it's just a design decision that's up to us to make.

I think it largely depends on how often one wants to set Stubbed to true. From my current understanding how it's meant to be used, it makes more sense to me to set it once for the entire service while doing testing, rather than for each individual endpoint via the options.

So, unless I'm not understanding the use case properly, I still prefer my client.MarketplaceService.Stubbed = true suggestion above.

@gmlewis @elliott-beach Do you have thoughts/preferences on how to deal with the stubbed parameter?

@lackerman
Copy link
Contributor Author

Hi @shurcooL, this is probably due to my own ignorance of the language, but when I try your suggested implementation, I get:

# github.com/google/go-github/github
./github.go:231:39: cannot convert &c.common (type *service) to 
type *MarketplaceService

since the Service is of type service by default but now it's being declared as type struct. Is this because I'm missing something that will allow it to implement the service interface?

@dmitshur
Copy link
Member

@lackerman You wouldn't be able to reuse &c.common struct anymore, because *MarketplaceService will have an extra field. You'll have to create a new *MarketplaceService struct, like so:

-c.Marketplace = (*MarketplaceService)(&c.common)
+c.Marketplace = &MarketplaceService{client: c}

@lackerman
Copy link
Contributor Author

lackerman commented Oct 17, 2017

Thanks for the help @shurcooL. Please could I ask you to explain the difference between the following two calling conventions:
(*MarketplaceService)(&c.common) and &MarketplaceService{client: c}
The second one makes sense but I'm not seeing how (*XXX)(...) creates a valid instance.

@dmitshur
Copy link
Member

dmitshur commented Oct 17, 2017

@lackerman It works for other services because they're defined as service, e.g.:

type ActivityService service

So *service is convertible to *ActivityService. E.g., see here.

You can find more information about this in #389 and #390 that introduced it. Note, this is a pretty advanced trick, it's not commonly done, and the benefits it offers are nice but not critical.

@lackerman
Copy link
Contributor Author

Ah, a colleague pointed out that it's just a type cast. Due to the syntax (*type)(...), I thought it was some kind or function call/syntactic sugar for new or &.

@dmitshur
Copy link
Member

Yes, it is. Although if you want to be precise about terms, it's a "type conversion". See https://golang.org/ref/spec#Conversions:

Conversions are expressions of the form T(x) where T is a type and x is an expression that can be converted to type T.

If the type starts with the operator * or <-, or if the type starts with the keyword func and has no result list, it must be parenthesized when necessary to avoid ambiguity

Hence (*T)(x).

…tead of service

Added a flag, `stubbed` to the MarketplaceService struct to set whether or not the Service should called the stubbed endpoints
@lackerman
Copy link
Contributor Author

Hi @shurcooL, I've completed the unit tests and performed a basic test to my account on one of the stubbed endpoints and it appears to work as expected.

@dmitshur
Copy link
Member

dmitshur commented Oct 24, 2017

Good to hear!

I'm not seeing any new commits. Did you mean to push something that implements the changes we discussed above?

@lackerman
Copy link
Contributor Author

@shurcooL, sad panda...yeah, I forgot to push. It's pushed now.

// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
Stubbed bool

ListOptions
Copy link
Member

@dmitshur dmitshur Oct 24, 2017

Choose a reason for hiding this comment

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

ListOptions probably doesn't belong here in a service struct, does it? I'm guessing we forgot to remove it from back when we considered a MarketplaceOptions wrapper, yeah?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yurp, pushed a fix.

@lackerman
Copy link
Contributor Author

@shurcooL fixed the issue you pointed out.

@gmlewis
Copy link
Collaborator

gmlewis commented Oct 26, 2017

Sorry for the delay - yes, I like the MarketplaceService.Stubbed solution that @shurcooL proposed in the comment above.
I'll now review the PR.

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, @lackerman for doing this! I have just a few things to fix if you don't mind.

@@ -0,0 +1,180 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/2013/2017/

// instead of production endpoints. Stubbed data is fake data that's useful
// for testing your GitHub Apps. Stubbed data is hard-coded and will not
// change based on actual subscriptions.
//
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: lines 23-24 are unnecessary due to line 16 above. Please remove them.

Copy link
Member

Choose a reason for hiding this comment

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

@gmlewis See https://github.com/google/go-github/pull/732/files#r148168072 for why I think it's still helpful to include them. It just so happens the URL matches the one for the service, I wish we could link to a more specific section, but I don't see one...

// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan.
type MarketplacePlanAccount struct {
URL *string `json:"url,omitempty"`
AccountType *string `json:"type,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason to not call this Type (instead of AccountType)?
I say this because it seems stuttery to me... e.g. account.AccountType vs account.Type.

NextBillingDate *string `json:"next_billing_date,omitempty"`
UnitCount *int `json:"unit_count,omitempty"`
Plan *MarketplacePlan `json:"plan,omitempty"`
MarketplacePlanAccount *MarketplacePlanAccount `json:"account,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/MarketPlanAccount/Account/
Let's keep the names matching as closely to the JSON tag as possible.

// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMarketplacePreview)

var i []*MarketplacePlan
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/i/plans/
i usually connotes an index and since this variable is significant and not just any ordinary temp value, I think giving it a meaningful name here improves readability.

// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMarketplacePreview)

var i []*MarketplacePlanAccount
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/i/accounts/

// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMarketplacePreview)

var i []*MarketplacePurchase
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/i/purchases/

@@ -0,0 +1,202 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

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

s/2013/2017/

})

opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = false
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm... I wish we didn't use a package global client for unit testing, as this will prevent unit tests from all being run in parallel in the future.

Ideally, we would have something like:

client, teardown := setup()
defer teardown()

for each test. No action is needed in this PR, but I'll file a new issue and point here for motivation.

github/github.go Outdated
@@ -143,6 +146,7 @@ type Client struct {
Licenses *LicensesService
Migrations *MigrationService
Reactions *ReactionsService
Marketplace *MarketplaceService
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's also move this one two lines up to keep this list sorted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have no problem moving it up, but note that there are items higher in the list which are not sorted.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would you mind sorting them while you are here, please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@lackerman
Copy link
Contributor Author

Thanks @gmlewis for the extra pair of eyes, really appreciate the suggestions.

@@ -162,13 +160,13 @@ func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMarketplacePreview)

var i []*MarketplacePurchase
resp, err := s.client.Do(ctx, req, &i)
var purchaces []*MarketplacePurchase
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: s/purchaces/purchases/

github/github.go Outdated
@@ -136,16 +139,17 @@ type Client struct {
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
Licenses *LicensesService
Copy link
Collaborator

Choose a reason for hiding this comment

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

Licenses after Issues, please. 😄

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 it ever end... :P Done

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, @lackerman!
LGTM.
Awaiting second LGTM before merging.

@gmlewis gmlewis requested a review from elliott-beach October 31, 2017 21:45
// instead of production endpoints. Stubbed data is fake data that's useful
// for testing your GitHub Apps. Stubbed data is hard-coded and will not
// change based on actual subscriptions.
Stubbed bool
Copy link
Member

@dmitshur dmitshur Nov 1, 2017

Choose a reason for hiding this comment

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

This Stubbed field is quite unusual and may cause people to seek more information about it. I think it's worth it to include a link to GitHub API documentation for it as well here, as I suggested in #732 (comment):

 // Stubbed controls whether endpoints that return stubbed data are used
 // instead of production endpoints. Stubbed data is fake data that's useful
 // for testing your GitHub Apps. Stubbed data is hard-coded and will not
 // change based on actual subscriptions.
+//
+// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
 Stubbed bool

This is also a way of us hinting that it's not our own invention specific to go-github Go library, but a GitHub API feature.

Copy link
Member

@dmitshur dmitshur Nov 12, 2017

Choose a reason for hiding this comment

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

@gmlewis Does that sound good to you?

If so, I can apply it and merge. Edit: @lackerman has already pushed ecac90f.

Copy link
Member

@dmitshur dmitshur left a comment

Choose a reason for hiding this comment

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

Just one suggested change about Stubbed field documentation, see comment above. Otherwise LGTM.

@gmlewis gmlewis removed the request for review from elliott-beach November 1, 2017 16:29
@lackerman
Copy link
Contributor Author

@shurcooL, updated

@lackerman
Copy link
Contributor Author

@shurcooL not sure why the build failed after adding a comment line

@dmitshur
Copy link
Member

We have some issues with gen-accessors right now, I've filed #778 for it. But fixing that is out of scope of this PR, so just re-generate the package, and it should be fine.

@lackerman
Copy link
Contributor Author

@shurcooL ready to be merged

@dmitshur dmitshur merged commit 8b3951c into google:master Nov 14, 2017
@dmitshur
Copy link
Member

Merged. Thank you @lackerman!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Indication that the PR author has signed a Google Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants