diff --git a/github/apps_marketplace.go b/github/apps_marketplace.go new file mode 100644 index 00000000000..d3e689a146d --- /dev/null +++ b/github/apps_marketplace.go @@ -0,0 +1,180 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// 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 +} + +// MarketplacePlan represents a GitHub Apps Marketplace Listing Plan. +type MarketplacePlan struct { + URL *string `json:"url,omitempty"` + AccountsURL *string `json:"accounts_url,omitempty"` + ID *int `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + MonthlyPriceInCents *int `json:"monthly_price_in_cents,omitempty"` + YearlyPriceInCents *int `json:"yearly_price_in_cents,omitempty"` + PriceModel *string `json:"price_model,omitempty"` + UnitName *string `json:"unit_name,omitempty"` + Bullets *[]string `json:"bullets,omitempty"` +} + +// MarketplacePurchase represents a GitHub Apps Marketplace Purchase. +type MarketplacePurchase struct { + BillingCycle *string `json:"billing_cycle,omitempty"` + NextBillingDate *string `json:"next_billing_date,omitempty"` + UnitCount *int `json:"unit_count,omitempty"` + Plan *MarketplacePlan `json:"plan,omitempty"` + Account *MarketplacePlanAccount `json:"account,omitempty"` +} + +// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan. +type MarketplacePlanAccount struct { + URL *string `json:"url,omitempty"` + Type *string `json:"type,omitempty"` + ID *int `json:"id,omitempty"` + Login *string `json:"login,omitempty"` + Email *string `json:"email,omitempty"` + OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"` + MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` +} + +// ListPlans lists all plans for your Marketplace listing. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing +func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) { + uri := s.marketplaceURI("plans") + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var plans []*MarketplacePlan + resp, err := s.client.Do(ctx, req, &plans) + if err != nil { + return nil, resp, err + } + + return plans, resp, nil +} + +// ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan +func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { + uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID)) + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var accounts []*MarketplacePlanAccount + resp, err := s.client.Do(ctx, req, &accounts) + if err != nil { + return nil, resp, err + } + + return accounts, resp, nil +} + +// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing +func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { + uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID)) + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var accounts []*MarketplacePlanAccount + resp, err := s.client.Do(ctx, req, &accounts) + if err != nil { + return nil, resp, err + } + + return accounts, resp, nil +} + +// ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user. +// +// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases +func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) { + uri := "user/marketplace_purchases" + if s.Stubbed { + uri = "user/marketplace_purchases/stubbed" + } + + u, err := addOptions(uri, opt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeMarketplacePreview) + + var purchases []*MarketplacePurchase + resp, err := s.client.Do(ctx, req, &purchases) + if err != nil { + return nil, resp, err + } + + return purchases, resp, nil +} + +func (s *MarketplaceService) marketplaceURI(endpoint string) string { + url := "marketplace_listing" + if s.Stubbed { + url = "marketplace_listing/stubbed" + } + return url + "/" + endpoint +} diff --git a/github/apps_marketplace_test.go b/github/apps_marketplace_test.go new file mode 100644 index 00000000000..151a72179cb --- /dev/null +++ b/github/apps_marketplace_test.go @@ -0,0 +1,202 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestMarketplaceService_ListPlans(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/plans", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + plans, _, err := client.Marketplace.ListPlans(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListPlans returned error: %v", err) + } + + want := []*MarketplacePlan{{ID: Int(1)}} + if !reflect.DeepEqual(plans, want) { + t.Errorf("Marketplace.ListPlans returned %+v, want %+v", plans, want) + } +} + +func TestMarketplaceService_Stubbed_ListPlans(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/stubbed/plans", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + plans, _, err := client.Marketplace.ListPlans(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListPlans (Stubbed) returned error: %v", err) + } + + want := []*MarketplacePlan{{ID: Int(1)}} + if !reflect.DeepEqual(plans, want) { + t.Errorf("Marketplace.ListPlans (Stubbed) returned %+v, want %+v", plans, want) + } +} + +func TestMarketplaceService_ListPlanAccountsForPlan(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForPlan returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForPlan returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_Stubbed_ListPlanAccountsForPlan(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/stubbed/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_ListPlanAccountsForAccount(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/accounts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForAccount returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForAccount returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_Stubbed_ListPlanAccountsForAccount(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/marketplace_listing/stubbed/accounts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt) + if err != nil { + t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned error: %v", err) + } + + want := []*MarketplacePlanAccount{{ID: Int(1)}} + if !reflect.DeepEqual(accounts, want) { + t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned %+v, want %+v", accounts, want) + } +} + +func TestMarketplaceService_ListMarketplacePurchasesForUser(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/user/marketplace_purchases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"billing_cycle":"monthly"}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = false + purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err) + } + + want := []*MarketplacePurchase{{BillingCycle: String("monthly")}} + if !reflect.DeepEqual(purchases, want) { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want) + } +} + +func TestMarketplaceService_Stubbed_ListMarketplacePurchasesForUser(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/user/marketplace_purchases/stubbed", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMarketplacePreview) + fmt.Fprint(w, `[{"billing_cycle":"monthly"}]`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + client.Marketplace.Stubbed = true + purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt) + if err != nil { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err) + } + + want := []*MarketplacePurchase{{BillingCycle: String("monthly")}} + if !reflect.DeepEqual(purchases, want) { + t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want) + } +} diff --git a/github/github-accessors.go b/github/github-accessors.go index c5b7b6b9e63..821cd83a189 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -500,6 +500,14 @@ func (c *Client) GetLicenses() *LicensesService { return c.Licenses } +// GetMarketplace returns the Marketplace field. +func (c *Client) GetMarketplace() *MarketplaceService { + if c == nil { + return nil + } + return c.Marketplace +} + // GetMigrations returns the Migrations field. func (c *Client) GetMigrations() *MigrationService { if c == nil { @@ -3836,6 +3844,190 @@ func (m *markdownRequest) GetText() string { return *m.Text } +// GetAccountsURL returns the AccountsURL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetAccountsURL() string { + if m == nil || m.AccountsURL == nil { + return "" + } + return *m.AccountsURL +} + +// GetBullets returns the Bullets field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetBullets() []string { + if m == nil || m.Bullets == nil { + return nil + } + return *m.Bullets +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetDescription() string { + if m == nil || m.Description == nil { + return "" + } + return *m.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetID() int { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetMonthlyPriceInCents returns the MonthlyPriceInCents field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetMonthlyPriceInCents() int { + if m == nil || m.MonthlyPriceInCents == nil { + return 0 + } + return *m.MonthlyPriceInCents +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetName() string { + if m == nil || m.Name == nil { + return "" + } + return *m.Name +} + +// GetPriceModel returns the PriceModel field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetPriceModel() string { + if m == nil || m.PriceModel == nil { + return "" + } + return *m.PriceModel +} + +// GetUnitName returns the UnitName field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetUnitName() string { + if m == nil || m.UnitName == nil { + return "" + } + return *m.UnitName +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetYearlyPriceInCents returns the YearlyPriceInCents field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetYearlyPriceInCents() int { + if m == nil || m.YearlyPriceInCents == nil { + return 0 + } + return *m.YearlyPriceInCents +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetEmail() string { + if m == nil || m.Email == nil { + return "" + } + return *m.Email +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetID() int { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetLogin() string { + if m == nil || m.Login == nil { + return "" + } + return *m.Login +} + +// GetMarketplacePurchase returns the MarketplacePurchase field. +func (m *MarketplacePlanAccount) GetMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.MarketplacePurchase +} + +// GetOrganizationBillingEmail returns the OrganizationBillingEmail field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetOrganizationBillingEmail() string { + if m == nil || m.OrganizationBillingEmail == nil { + return "" + } + return *m.OrganizationBillingEmail +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetType() string { + if m == nil || m.Type == nil { + return "" + } + return *m.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetAccount returns the Account field. +func (m *MarketplacePurchase) GetAccount() *MarketplacePlanAccount { + if m == nil { + return nil + } + return m.Account +} + +// GetBillingCycle returns the BillingCycle field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetBillingCycle() string { + if m == nil || m.BillingCycle == nil { + return "" + } + return *m.BillingCycle +} + +// GetNextBillingDate returns the NextBillingDate field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetNextBillingDate() string { + if m == nil || m.NextBillingDate == nil { + return "" + } + return *m.NextBillingDate +} + +// GetPlan returns the Plan field. +func (m *MarketplacePurchase) GetPlan() *MarketplacePlan { + if m == nil { + return nil + } + return m.Plan +} + +// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetUnitCount() int { + if m == nil || m.UnitCount == nil { + return 0 + } + return *m.UnitCount +} + +// Getclient returns the client field. +func (m *MarketplaceService) Getclient() *Client { + if m == nil { + return nil + } + return m.client +} + // GetText returns the Text field if it's non-nil, zero value otherwise. func (m *Match) GetText() string { if m == nil || m.Text == nil { diff --git a/github/github.go b/github/github.go index db06cfb4f6b..3a0727eb69e 100644 --- a/github/github.go +++ b/github/github.go @@ -103,6 +103,9 @@ const ( // https://developer.github.com/changes/2017-07-26-team-review-request-thor-preview/ mediaTypeTeamReviewPreview = "application/vnd.github.thor-preview+json" + // https://developer.github.com/v3/apps/marketplace/ + mediaTypeMarketplacePreview = "application/vnd.github.valkyrie-preview+json" + // https://developer.github.com/changes/2017-08-30-preview-nested-teams/ mediaTypeNestedTeamsPreview = "application/vnd.github.hellcat-preview+json" ) @@ -137,15 +140,16 @@ type Client struct { Git *GitService Gitignores *GitignoresService Issues *IssuesService + Licenses *LicensesService + Marketplace *MarketplaceService + Migrations *MigrationService Organizations *OrganizationsService Projects *ProjectsService PullRequests *PullRequestsService + Reactions *ReactionsService Repositories *RepositoriesService Search *SearchService Users *UsersService - Licenses *LicensesService - Migrations *MigrationService - Reactions *ReactionsService } type service struct { @@ -227,6 +231,7 @@ func NewClient(httpClient *http.Client) *Client { c.Gitignores = (*GitignoresService)(&c.common) c.Issues = (*IssuesService)(&c.common) c.Licenses = (*LicensesService)(&c.common) + c.Marketplace = &MarketplaceService{client: c} c.Migrations = (*MigrationService)(&c.common) c.Organizations = (*OrganizationsService)(&c.common) c.Projects = (*ProjectsService)(&c.common)