Skip to content

Instantly share code, notes, and snippets.

@alexellis
Last active August 8, 2024 18:11
Show Gist options
  • Save alexellis/6212c988189323dbb2806d1c7f7699ab to your computer and use it in GitHub Desktop.
Save alexellis/6212c988189323dbb2806d1c7f7699ab to your computer and use it in GitHub Desktop.
sponsors.go - query whether a GitHub user is your sponsor at a given tier (dollar amount)
// LICENSE for this file: MIT
// Copyright: Alex Ellis, OpenFaaS Ltd 2020
// Do not remove this notice
package handlers
import (
"context"
"encoding/json"
"os"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
)
const minDollars = 25
type SponsorTier struct {
MonthlyPriceInDollars int
}
// SponsorEntity is the User sponsoring
type SponsorEntity struct {
// SponsorUser with an annotation that expands the Union for the User type
SponsorUser SponsorUser `graphql:"... on User"`
}
// Sponsorable is the User receiving the sponsorshop
type Sponsorable struct {
// SponsorUser with an annotation that expands the Union for the User type
SponsorUser SponsorUser `graphql:"... on User"`
// SponsorOrganization was not strictly required, during debug the SponsorUser
// was populated with the org name anyway.
SponsorOrganization SponsorUser `graphql:"... on Organization"`
}
// SponsorUser is a User associated with either side of a
// sponsorship
type SponsorUser struct {
// Login name of the sponsor.
Login string
}
// sponsorships is the query for graphql and it limits
// the search to 100, since most people will not be
// sponsoring more than a few other developers.
type sponsorships struct {
Viewer struct {
Login string
SponsorshipsAsSponsor struct {
Nodes []struct {
SponsorEntity SponsorEntity
Sponsorable Sponsorable
Tier SponsorTier
}
} `graphql:"sponsorshipsAsSponsor(first: 100)"`
}
}
// IsSponsoring returns true if the login for the associated
// accessToken has an active sponsorship for the target login of
// the hard-coded dollar amount.
// The accessToken requires the whole "user" scope to read the
// dollar amount.
func IsSponsoring(login string, targets []string, accessToken string) (bool, error) {
// it's not possible to sponsor oneself
for _, target := range targets {
if target == login {
return true, nil
}
}
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
httpClient := oauth2.NewClient(context.Background(), src)
client := githubv4.NewClient(httpClient)
var q sponsorships
ctx := context.Background()
err := client.Query(ctx, &q, map[string]interface{}{})
if err != nil {
return false, err
}
// log.Printf("Checking %s, for: %v", login, targets)
// printJSON(q.Viewer.SponsorshipsAsSponsor.Nodes)
var users []SponsorUser
for _, node := range q.Viewer.SponsorshipsAsSponsor.Nodes {
sponsor := node.SponsorEntity.SponsorUser
sponsored := node.Sponsorable.SponsorUser.Login
for _, target := range targets {
if sponsored == target &&
node.Tier.MonthlyPriceInDollars >= minDollars {
users = append(users, sponsor)
}
}
}
for _, s := range users {
if s.Login == login {
return true, nil
}
}
return false, nil
}
// printJSON is from the github.com/shurcooL/githubv4 library
// and prints the response for debugging.
func printJSON(v interface{}) {
w := json.NewEncoder(os.Stdout)
w.SetIndent("", "\t")
err := w.Encode(v)
if err != nil {
panic(err)
}
}
@cheshire137
Copy link

Hi there! You can now pass a maintainerLogins argument to sponsorshipsAsSponsor to filter the returned sponsorships. For example:

query { 
  viewer { 
    sponsorshipsAsSponsor(first:100,maintainerLogins:["alexellis", "zkat"]) {
      totalRecurringMonthlyPriceInDollars
      nodes {
        sponsorable {
          ... on User {login}
          ... on Organization {login}
        }
        tier {
          name
        }
      }
    }
  }
}

sponsorshipsAsSponsor query in GraphiQL

-- https://docs.github.com/en/graphql/overview/explorer

@alexellis
Copy link
Author

Thank you @cheshire137 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment