Skip to content

Commit

Permalink
Expose TokenSource through ClientCreators
Browse files Browse the repository at this point in the history
This installation token retrievable from TokenSource can be used to clone repositories.
  • Loading branch information
hermanbanken committed Apr 15, 2021
1 parent 933ee56 commit 1e9f88c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 30 deletions.
2 changes: 1 addition & 1 deletion example/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (h *PRCommentHandler) Handle(ctx context.Context, eventType, deliveryID str
return nil
}

client, err := h.NewInstallationClient(installationID)
client, _, err := h.NewInstallationClient(installationID)
if err != nil {
return err
}
Expand Down
38 changes: 24 additions & 14 deletions githubapp/caching_client_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ const (
DefaultCachingClientCapacity = 64
)

type clientTokenSource struct {
client *github.Client
tokenSource TokenSource
}

type clientV4TokenSource struct {
client *githubv4.Client
tokenSource TokenSource
}

// NewDefaultCachingClientCreator returns a ClientCreator using values from the
// configuration or other defaults.
func NewDefaultCachingClientCreator(c Config, opts ...ClientOption) (ClientCreator, error) {
Expand Down Expand Up @@ -70,42 +80,42 @@ func (c *cachingClientCreator) NewAppV4Client() (*githubv4.Client, error) {
return c.delegate.NewAppV4Client()
}

func (c *cachingClientCreator) NewInstallationClient(installationID int64) (*github.Client, error) {
func (c *cachingClientCreator) NewInstallationClient(installationID int64) (*github.Client, TokenSource, error) {
// if client is in cache, return it
key := c.toCacheKey("v3", installationID)
val, ok := c.cachedClients.Get(key)
if ok {
if client, ok := val.(*github.Client); ok {
return client, nil
if cts, ok := val.(clientTokenSource); ok {
return cts.client, cts.tokenSource, nil
}
}

// otherwise, create and return
client, err := c.delegate.NewInstallationClient(installationID)
client, ts, err := c.delegate.NewInstallationClient(installationID)
if err != nil {
return nil, err
return nil, nil, err
}
c.cachedClients.Add(key, client)
return client, nil
c.cachedClients.Add(key, clientTokenSource{client, ts})
return client, ts, nil
}

func (c *cachingClientCreator) NewInstallationV4Client(installationID int64) (*githubv4.Client, error) {
func (c *cachingClientCreator) NewInstallationV4Client(installationID int64) (*githubv4.Client, TokenSource, error) {
// if client is in cache, return it
key := c.toCacheKey("v4", installationID)
val, ok := c.cachedClients.Get(key)
if ok {
if client, ok := val.(*githubv4.Client); ok {
return client, nil
if cts, ok := val.(clientV4TokenSource); ok {
return cts.client, cts.tokenSource, nil
}
}

// otherwise, create and return
client, err := c.delegate.NewInstallationV4Client(installationID)
client, ts, err := c.delegate.NewInstallationV4Client(installationID)
if err != nil {
return nil, err
return nil, nil, err
}
c.cachedClients.Add(key, client)
return client, nil
c.cachedClients.Add(key, clientV4TokenSource{client, ts})
return client, ts, nil
}

func (c *cachingClientCreator) NewTokenClient(token string) (*github.Client, error) {
Expand Down
37 changes: 22 additions & 15 deletions githubapp/client_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ type ClientCreator interface {
// * the installation ID is the ID that is shown in the URL of https://{githubURL}/settings/installations/{#}
// (navigate to the "installations" page without the # and go to the app's page to see the number)
// * the key bytes must be a PEM-encoded PKCS1 or PKCS8 private key for the application
NewInstallationClient(installationID int64) (*github.Client, error)
NewInstallationClient(installationID int64) (*github.Client, TokenSource, error)

// NewInstallationV4Client returns an installation-authenticated v4 API client, similar to NewInstallationClient.
NewInstallationV4Client(installationID int64) (*githubv4.Client, error)
NewInstallationV4Client(installationID int64) (*githubv4.Client, TokenSource, error)

// NewTokenClient returns a *github.Client that uses the passed in OAuth token for authentication.
NewTokenClient(token string) (*github.Client, error)
Expand Down Expand Up @@ -209,9 +209,9 @@ func (c *clientCreator) NewAppV4Client() (*githubv4.Client, error) {
return client, nil
}

func (c *clientCreator) NewInstallationClient(installationID int64) (*github.Client, error) {
func (c *clientCreator) NewInstallationClient(installationID int64) (*github.Client, TokenSource, error) {
base := c.newHTTPClient()
installation, transportError := newInstallation(c.integrationID, installationID, c.privKeyBytes, c.v3BaseURL)
installation, ghTransport, transportError := newInstallation(c.integrationID, installationID, c.privKeyBytes, c.v3BaseURL)

middleware := []ClientMiddleware{installation}
if c.cacheFunc != nil {
Expand All @@ -220,30 +220,34 @@ func (c *clientCreator) NewInstallationClient(installationID int64) (*github.Cli

client, err := c.newClient(base, middleware, fmt.Sprintf("installation: %d", installationID), installationID)
if err != nil {
return nil, err
return nil, nil, err
}
if *transportError != nil {
return nil, *transportError
return nil, nil, *transportError
}
return client, nil
return client, *ghTransport, nil
}

func (c *clientCreator) NewInstallationV4Client(installationID int64) (*githubv4.Client, error) {
func (c *clientCreator) NewInstallationV4Client(installationID int64) (*githubv4.Client, TokenSource, error) {
base := c.newHTTPClient()
installation, transportError := newInstallation(c.integrationID, installationID, c.privKeyBytes, c.v3BaseURL)
installation, ghTransport, transportError := newInstallation(c.integrationID, installationID, c.privKeyBytes, c.v3BaseURL)

// The v4 API primarily uses POST requests (except for introspection queries)
// which we cannot cache, so don't construct the middleware
middleware := []ClientMiddleware{installation}

client, err := c.newV4Client(base, middleware, fmt.Sprintf("installation: %d", installationID))
if err != nil {
return nil, err
return nil, nil, err
}
if *transportError != nil {
return nil, *transportError
return nil, nil, *transportError
}
return client, nil
return client, *ghTransport, nil
}

type TokenSource interface {
Token(ctx context.Context) (string, error)
}

func (c *clientCreator) NewTokenClient(token string) (*github.Client, error) {
Expand Down Expand Up @@ -334,19 +338,22 @@ func newAppInstallation(integrationID int64, privKeyBytes []byte, v3BaseURL stri
return installation, &transportError
}

func newInstallation(integrationID, installationID int64, privKeyBytes []byte, v3BaseURL string) (ClientMiddleware, *error) {
func newInstallation(integrationID, installationID int64, privKeyBytes []byte, v3BaseURL string) (ClientMiddleware, **ghinstallation.Transport, *error) {
var transportError error
var itr *ghinstallation.Transport
installation := func(next http.RoundTripper) http.RoundTripper {
itr, err := ghinstallation.New(next, integrationID, installationID, privKeyBytes)
var err error
itr, err = ghinstallation.New(next, integrationID, installationID, privKeyBytes)
if err != nil {
transportError = err

return next
}
// leaving the v3 URL since this is used to refresh the token, not make queries
itr.BaseURL = strings.TrimSuffix(v3BaseURL, "/")
return itr
}
return installation, &transportError
return installation, &itr, &transportError
}

func cache(cacheFunc func() httpcache.Cache) ClientMiddleware {
Expand Down

0 comments on commit 1e9f88c

Please sign in to comment.