Skip to content

Commit e57bffe

Browse files
FEAT[DXTA-297]: Create workflow for github installation data provisioning
1 parent 7d6f423 commit e57bffe

12 files changed

Lines changed: 407 additions & 20 deletions

File tree

cmd/internal-api/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func main() {
129129

130130
defer temporalClient.Close()
131131

132-
usersHandler := handler.NewUsers(temporalClient, *cfg)
132+
temporalHandler := handler.SetupOnboardingTemporal(temporalClient, *cfg)
133133

134134
r.Route("/tenant", func(r chi.Router) {
135135
if os.Getenv("ENABLE_JWT_AUTH") == "true" {
@@ -147,8 +147,8 @@ func main() {
147147
r.Post("/teams", handler.CreateTeam)
148148
r.Post("/teams/{team_id}/members/{member_id}", handler.AddMemberToTeam)
149149
r.Post("/members", handler.CreateMember)
150+
r.Get("/provision-github-installations/{installation_id}", temporalHandler.ProvisionGithubInstallationData)
150151
})
151-
152152
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
153153
w.Write([]byte(`OK`))
154154
})
@@ -157,7 +157,7 @@ func main() {
157157
w.Write([]byte(`OK`))
158158
})
159159

160-
r.Get("/users-count", usersHandler.UsersCount)
160+
r.Get("/users-count", temporalHandler.UsersCount)
161161

162162
go func() {
163163
log.Printf("Listening on %s\n", srv.Addr)

cmd/onboarding-worker/main.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"log"
66

7+
"github.com/dxta-dev/app/internal/internal_api/data"
78
"github.com/dxta-dev/app/internal/onboarding"
89
"github.com/dxta-dev/app/internal/onboarding/activity"
910
"github.com/dxta-dev/app/internal/onboarding/workflow"
@@ -17,6 +18,12 @@ func main() {
1718
log.Fatalln("Failed to load configuration:", err)
1819
}
1920

21+
err = data.LoadGithubConfig()
22+
23+
if err != nil {
24+
log.Fatalln("Failed to load github configuration:", err)
25+
}
26+
2027
temporalClient, err := client.Dial(client.Options{
2128
HostPort: cfg.TemporalHostPort,
2229
Namespace: cfg.TemporalOnboardingNamespace,
@@ -26,12 +33,19 @@ func main() {
2633
}
2734
defer temporalClient.Close()
2835

36+
err = data.InitAppClient()
37+
38+
if err != nil {
39+
log.Fatalf("Unable to init app client: %v", err)
40+
}
41+
2942
err = onboarding.RegisterNamespace(
3043
context.Background(),
3144
cfg.TemporalHostPort,
3245
cfg.TemporalOnboardingNamespace,
3346
30,
3447
)
48+
3549
if err != nil {
3650
log.Fatalln("Failed to register Temporal namespace:", err)
3751
}
@@ -41,6 +55,9 @@ func main() {
4155
w.RegisterWorkflow(workflow.CountUsersWorkflow)
4256
w.RegisterActivity(activity.CountUsersActivity)
4357

58+
w.RegisterWorkflow(workflow.ProvisionGithubInstallationData)
59+
w.RegisterActivity(activity.GetGithubInstallation)
60+
4461
if err := w.Run(worker.InterruptCh()); err != nil {
4562
log.Fatalln("Worker failed to start", err)
4663
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020

2121
require (
2222
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect
23+
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 // indirect
2324
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
2425
github.com/davecgh/go-spew v1.1.1 // indirect
2526
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
@@ -29,8 +30,12 @@ require (
2930
github.com/go-logr/logr v1.4.2 // indirect
3031
github.com/go-logr/stdr v1.2.2 // indirect
3132
github.com/goccy/go-json v0.10.3 // indirect
33+
github.com/gofri/go-github-ratelimit/v2 v2.0.2 // indirect
3234
github.com/gogo/protobuf v1.3.2 // indirect
35+
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
3336
github.com/golang/mock v1.6.0 // indirect
37+
github.com/google/go-github/v72 v72.0.0 // indirect
38+
github.com/google/go-querystring v1.1.0 // indirect
3439
github.com/google/uuid v1.6.0 // indirect
3540
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
3641
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ github.com/XSAM/otelsql v0.39.0/go.mod h1:uMOXLUX+wkuAuP0AR3B45NXX7E9lJS2mERa8gq
55
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
66
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
77
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
8+
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 h1:B91r9bHtXp/+XRgS5aZm6ZzTdz3ahgJYmkt4xZkgDz8=
9+
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0/go.mod h1:OeVe5ggFzoBnmgitZe/A+BqGOnv1DvU/0uiLQi1wutM=
810
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
911
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
1012
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -54,8 +56,12 @@ github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
5456
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
5557
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
5658
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
59+
github.com/gofri/go-github-ratelimit/v2 v2.0.2 h1:gS8wAS1jTmlWGdTjAM7KIpsLjwY1S0S/gKK5hthfSXM=
60+
github.com/gofri/go-github-ratelimit/v2 v2.0.2/go.mod h1:YBQt4gTbdcbMjJFT05YFEaECwH78P5b0IwrnbLiHGdE=
5761
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
5862
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
63+
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
64+
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
5965
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
6066
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
6167
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
@@ -68,9 +74,14 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
6874
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
6975
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
7076
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
77+
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
7178
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
7279
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
7380
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
81+
github.com/google/go-github/v72 v72.0.0 h1:FcIO37BLoVPBO9igQQ6tStsv2asG4IPcYFi655PPvBM=
82+
github.com/google/go-github/v72 v72.0.0/go.mod h1:WWtw8GMRiL62mvIquf1kO3onRHeWWKmK01qdCY8c5fg=
83+
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
84+
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
7485
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
7586
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
7687
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package data
2+
3+
import (
4+
"encoding/base64"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
"os"
9+
"strconv"
10+
11+
"github.com/bradleyfalzon/ghinstallation/v2"
12+
"github.com/gofri/go-github-ratelimit/v2/github_ratelimit"
13+
"github.com/gofri/go-github-ratelimit/v2/github_ratelimit/github_primary_ratelimit"
14+
"github.com/gofri/go-github-ratelimit/v2/github_ratelimit/github_secondary_ratelimit"
15+
"github.com/google/go-github/v72/github"
16+
)
17+
18+
var GithubConfig *GithubCfg
19+
20+
type GithubCfg struct {
21+
GithubAppId int64
22+
GithubAppPrivateKey []byte
23+
GithubAppClient *github.Client
24+
RoundTripper http.RoundTripper
25+
}
26+
27+
func LoadGithubConfig() error {
28+
appIdStr := os.Getenv("GITHUB_APP_ID")
29+
appPrivateKeyStr := os.Getenv("GITHUB_APP_PRIVATE_KEY")
30+
31+
if appIdStr == "" {
32+
return errors.New("GITHUB_APP_ID not set")
33+
}
34+
35+
if appPrivateKeyStr == "" {
36+
return errors.New("GITHUB_APP_PRIVATE_KEY not set")
37+
}
38+
39+
appId, err := strconv.ParseInt(appIdStr, 10, 64)
40+
41+
if err != nil {
42+
return errors.New("could not parse app id string to int64")
43+
}
44+
45+
appPrivateKey, err := base64.StdEncoding.DecodeString(appPrivateKeyStr)
46+
47+
if err != nil {
48+
return errors.New("failed to decode base64 string")
49+
}
50+
51+
GithubConfig = &GithubCfg{
52+
GithubAppId: appId,
53+
GithubAppPrivateKey: appPrivateKey,
54+
GithubAppClient: nil,
55+
RoundTripper: http.DefaultTransport,
56+
}
57+
58+
return nil
59+
}
60+
61+
func getInstallationTransport(tr http.RoundTripper, installationId int64) (http.RoundTripper, error) {
62+
itt, err := ghinstallation.New(tr, GithubConfig.GithubAppId, installationId, GithubConfig.GithubAppPrivateKey)
63+
64+
if err != nil {
65+
return nil, fmt.Errorf("failed to create apps transport: %w", err)
66+
}
67+
68+
return itt, nil
69+
}
70+
71+
func getAppTransport(tr http.RoundTripper) (http.RoundTripper, error) {
72+
atr, err := ghinstallation.NewAppsTransport(tr, GithubConfig.GithubAppId, GithubConfig.GithubAppPrivateKey)
73+
74+
if err != nil {
75+
return nil, fmt.Errorf("failed to create apps transport: %w", err)
76+
}
77+
78+
return atr, nil
79+
}
80+
81+
func createLimiter(tr http.RoundTripper) http.RoundTripper {
82+
return github_ratelimit.New(tr,
83+
github_primary_ratelimit.WithLimitDetectedCallback(func(ctx *github_primary_ratelimit.CallbackContext) {
84+
fmt.Printf("Primary rate limit detected: category %s, reset time: %v\n", ctx.Category, ctx.ResetTime)
85+
}),
86+
github_secondary_ratelimit.WithLimitDetectedCallback(func(ctx *github_secondary_ratelimit.CallbackContext) {
87+
fmt.Printf("Secondary rate limit detected: reset time: %v, total sleep time: %v\n", ctx.ResetTime, ctx.TotalSleepTime)
88+
}),
89+
)
90+
}
91+
92+
func NewInstallationClient(installationId int64) (*github.Client, error) {
93+
tr := GithubConfig.RoundTripper
94+
tr, err := getInstallationTransport(tr, installationId)
95+
96+
if err != nil {
97+
return nil, err
98+
}
99+
100+
tr = createLimiter(tr)
101+
102+
return github.NewClient(&http.Client{Transport: tr}), nil
103+
}
104+
105+
func InitAppClient() error {
106+
tr := GithubConfig.RoundTripper
107+
tr, err := getAppTransport(tr)
108+
109+
if err != nil {
110+
return err
111+
}
112+
113+
tr = createLimiter(tr)
114+
115+
GithubConfig.GithubAppClient = github.NewClient(&http.Client{Transport: tr})
116+
117+
return nil
118+
}
119+
120+
type NewAppClient struct {
121+
client *github.Client
122+
}
123+
124+
func AppClient(client *github.Client) *NewAppClient {
125+
return &NewAppClient{
126+
client,
127+
}
128+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package data
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/go-github/v72/github"
8+
)
9+
10+
func (cfg GithubCfg) GetGithubInstallation(installationId int64, ctx context.Context) (*github.Installation, error) {
11+
githubAppClient := cfg.GithubAppClient
12+
13+
installation, _, err := githubAppClient.Apps.GetInstallation(ctx, installationId)
14+
15+
if err != nil {
16+
fmt.Printf("Could not retrieve installation. Error: %v", err.Error())
17+
return nil, err
18+
}
19+
20+
return installation, nil
21+
}
22+
23+
func (d TenantDB) SyncGithubInstallationDataToTenant(
24+
installationId int64,
25+
installationOrgName string,
26+
organizationId string,
27+
ctx context.Context,
28+
) error {
29+
tx, err := d.DB.BeginTx(ctx, nil)
30+
31+
if err != nil {
32+
return err
33+
}
34+
35+
_, err = tx.Exec(`
36+
INSERT INTO github_organizations
37+
(github_app_installation_id, name)
38+
VALUES
39+
(?, ?);`,
40+
installationId, installationOrgName)
41+
42+
if err != nil {
43+
_ = tx.Rollback()
44+
return err
45+
}
46+
47+
_, err = tx.Exec(`
48+
INSERT INTO organizations
49+
(external_id)
50+
VALUES
51+
(?)
52+
ON CONFLICT
53+
(external_id)
54+
DO NOTHING;`,
55+
organizationId)
56+
57+
if err != nil {
58+
_ = tx.Rollback()
59+
return err
60+
}
61+
62+
_, err = tx.Exec(`
63+
INSERT INTO 'organizations_github_organizations'
64+
('organization_id', 'github_app_installation_id')
65+
VALUES
66+
(?, ?);`,
67+
organizationId, installationId)
68+
69+
if err != nil {
70+
_ = tx.Rollback()
71+
return err
72+
}
73+
74+
return nil
75+
}

0 commit comments

Comments
 (0)