Files
grafana/apps/provisioning/pkg/connection/github/client.go
Daniele Stefano Ferru e4b79e2fc8 Provisioning: Add Validation and Mutation for Connection resource (#115596)
* WIP: mutator added, start working on validator

* first validator iteration

* second validator iteration

* wip: working on integration tests

* re-working mutation and validation, using Connection interface

* fixing some rebase things

* fixing integration tests

* formatting

* fixing unit tests

* k8s codegen

* linting

* moving tests which are available only for enterprise

* addressing comments: using repo config for connections, updating tests

* addressing comments: adding some more info in the app and installation

* fixing app data

* addressing comments: updating connection implementation

* addressing comments

* formatting

* fixing tests
2026-01-12 17:52:00 +01:00

94 lines
2.5 KiB
Go

package github
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
"github.com/google/go-github/v70/github"
apierrors "k8s.io/apimachinery/pkg/api/errors"
)
// API errors that we need to convey after parsing real GH errors (or faking them).
var (
//lint:ignore ST1005 this is not punctuation
ErrServiceUnavailable = apierrors.NewServiceUnavailable("github is unavailable")
)
//go:generate mockery --name Client --structname MockClient --inpackage --filename client_mock.go --with-expecter
type Client interface {
// Apps and installations
GetApp(ctx context.Context) (App, error)
GetAppInstallation(ctx context.Context, installationID string) (AppInstallation, error)
}
// App represents a Github App.
type App struct {
// ID represents the GH app ID.
ID int64
// Slug represents the GH app slug.
Slug string
// Owner represents the GH account/org owning the app
Owner string
}
// AppInstallation represents a Github App Installation.
type AppInstallation struct {
// ID represents the GH installation ID.
ID int64
// Whether the installation is enabled or not.
Enabled bool
}
type githubClient struct {
gh *github.Client
}
func NewClient(client *github.Client) Client {
return &githubClient{client}
}
// GetApp gets the app by using the given token.
func (r *githubClient) GetApp(ctx context.Context) (App, error) {
app, _, err := r.gh.Apps.Get(ctx, "")
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) && ghErr.Response.StatusCode == http.StatusServiceUnavailable {
return App{}, ErrServiceUnavailable
}
return App{}, err
}
// TODO(ferruvich): do we need any other info?
return App{
ID: app.GetID(),
Slug: app.GetSlug(),
Owner: app.GetOwner().GetLogin(),
}, nil
}
// GetAppInstallation gets the installation of the app related to the given token.
func (r *githubClient) GetAppInstallation(ctx context.Context, installationID string) (AppInstallation, error) {
id, err := strconv.Atoi(installationID)
if err != nil {
return AppInstallation{}, fmt.Errorf("invalid installation ID: %s", installationID)
}
installation, _, err := r.gh.Apps.GetInstallation(ctx, int64(id))
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) && ghErr.Response.StatusCode == http.StatusServiceUnavailable {
return AppInstallation{}, ErrServiceUnavailable
}
return AppInstallation{}, err
}
// TODO(ferruvich): do we need any other info?
return AppInstallation{
ID: installation.GetID(),
Enabled: installation.GetSuspendedAt().IsZero(),
}, nil
}