Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/onboarding-worker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"log"
"sync"

"github.com/dxta-dev/app/internal/onboarding"
"github.com/dxta-dev/app/internal/onboarding/activity"
Expand Down Expand Up @@ -48,17 +49,21 @@ func main() {
log.Fatalln("Failed to register Temporal namespace:", err)
}

tenantDBConnections := sync.Map{}

w := worker.New(temporalClient, cfg.TemporalOnboardingQueueName, worker.Options{})

userActivities := activity.NewUserActivites(
*cfg,
)
githubInstallationActivities := activity.NewGithubInstallationActivities(*githubAppClient)
tenantActivities := activity.NewTenantActivities(&tenantDBConnections)

w.RegisterWorkflow(workflow.CountUsers)
w.RegisterWorkflow(workflow.AfterGithubInstallationWorkflow)
w.RegisterActivity(userActivities)
w.RegisterActivity(githubInstallationActivities)
w.RegisterActivity(tenantActivities)

if err := w.Run(worker.InterruptCh()); err != nil {
log.Fatalln("Worker failed to start", err)
Expand Down
10 changes: 5 additions & 5 deletions db/migrations/tenant/migrations/1750420632_init.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ CREATE TABLE "teams" (
"created_at" DATETIME NOT NULL DEFAULT (datetime('now')),
"updated_at" DATETIME NOT NULL DEFAULT (datetime('now')),
"deleted_at" DATETIME DEFAULT NULL,
FOREIGN KEY ("organization_id") references "organizations" ("id")
FOREIGN KEY ("organization_id") REFERENCES "organizations" ("id")
);

CREATE TABLE "members" (
Expand All @@ -49,7 +49,7 @@ CREATE TABLE "teams__members" (

CREATE TABLE "github_organizations" (
"id" INTEGER PRIMARY KEY NOT NULL,
"external_id" INTEGER NOT NULL,
"external_id" INTEGER NOT NULL UNIQUE,
"name" TEXT NOT NULL,
"github_app_installation_id" INTEGER NOT NULL,
"created_at" DATETIME NOT NULL DEFAULT (datetime('now')),
Expand All @@ -69,7 +69,7 @@ CREATE TABLE "organizations__github_organizations" (

CREATE TABLE "github_members" (
"id" INTEGER PRIMARY KEY NOT NULL,
"external_id" INTEGER NOT NULL,
"external_id" INTEGER NOT NULL UNIQUE,
"username" TEXT NOT NULL,
"email" TEXT DEFAULT NULL,
"member_id" INTEGER NULL,
Expand All @@ -81,7 +81,7 @@ CREATE TABLE "github_members" (

CREATE TABLE "github_teams" (
"id" INTEGER PRIMARY KEY NOT NULL,
"external_id" INTEGER NOT NULL,
"external_id" INTEGER NOT NULL UNIQUE,
"name" TEXT NOT NULL,
"github_organization_id" INTEGER NOT NULL,
"created_at" DATETIME NOT NULL DEFAULT (datetime('now')),
Expand Down Expand Up @@ -161,4 +161,4 @@ BEGIN
UPDATE "github_teams"
SET updated_at = datetime('now')
WHERE id = OLD.id;
END;
END;
6 changes: 3 additions & 3 deletions db/migrations/tenant/migrations/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ CREATE TABLE "organizations" ("id" INTEGER PRIMARY KEY NOT NULL, "name" TEXT NOT
CREATE TABLE "teams" ("id" INTEGER PRIMARY KEY NOT NULL, "name" TEXT NOT NULL, "organization_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL, FOREIGN KEY ("organization_id") REFERENCES "organizations" ("id"));
CREATE TABLE "members" ("id" INTEGER PRIMARY KEY NOT NULL, "name" TEXT NOT NULL, "email" TEXT DEFAULT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL);
CREATE TABLE "teams__members" ("team_id" INTEGER NOT NULL, "member_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), PRIMARY KEY ("team_id", "member_id"), FOREIGN KEY ("team_id") REFERENCES "teams" ("id"), FOREIGN KEY ("member_id") REFERENCES "members" ("id"));
CREATE TABLE "github_organizations" ("id" INTEGER PRIMARY KEY NOT NULL, "external_id" INTEGER NOT NULL, "name" TEXT NOT NULL, "github_app_installation_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL);
CREATE TABLE "github_organizations" ("id" INTEGER PRIMARY KEY NOT NULL, "external_id" INTEGER NOT NULL UNIQUE, "name" TEXT NOT NULL, "github_app_installation_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL);
CREATE TABLE "organizations__github_organizations" ("organization_id" INTEGER NOT NULL, "github_organization_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), PRIMARY KEY ("organization_id", "github_organization_id"), FOREIGN KEY ("organization_id") REFERENCES "organizations" ("id"), FOREIGN KEY ("github_organization_id") REFERENCES "github_organizations" ("id"));
CREATE TABLE "github_members" ("id" INTEGER PRIMARY KEY NOT NULL, "external_id" INTEGER NOT NULL, "username" TEXT NOT NULL, "email" TEXT DEFAULT NULL, "member_id" INTEGER NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL, FOREIGN KEY ("member_id") REFERENCES "members" ("id"));
CREATE TABLE "github_teams" ("id" INTEGER PRIMARY KEY NOT NULL, "external_id" INTEGER NOT NULL, "name" TEXT NOT NULL, "github_organization_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL, FOREIGN KEY ("github_organization_id") REFERENCES "github_organizations" ("id"));
CREATE TABLE "github_members" ("id" INTEGER PRIMARY KEY NOT NULL, "external_id" INTEGER NOT NULL UNIQUE, "username" TEXT NOT NULL, "email" TEXT DEFAULT NULL, "member_id" INTEGER NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL, FOREIGN KEY ("member_id") REFERENCES "members" ("id"));
CREATE TABLE "github_teams" ("id" INTEGER PRIMARY KEY NOT NULL, "external_id" INTEGER NOT NULL UNIQUE, "name" TEXT NOT NULL, "github_organization_id" INTEGER NOT NULL, "created_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "updated_at" DATETIME NOT NULL DEFAULT (datetime ('now')), "deleted_at" DATETIME DEFAULT NULL, FOREIGN KEY ("github_organization_id") REFERENCES "github_organizations" ("id"));
CREATE TABLE "github_teams__github_members" ("github_team_id" INTEGER NOT NULL, "github_member_id" INTEGER NOT NULL, PRIMARY KEY ("github_team_id", "github_member_id"), FOREIGN KEY ("github_team_id") REFERENCES "github_teams" ("id"), FOREIGN KEY ("github_member_id") REFERENCES "github_members" ("id"));
CREATE TRIGGER "settings_set_updated_at" AFTER UPDATE ON "settings" FOR EACH ROW BEGIN
UPDATE "settings" SET updated_at = datetime ('now') WHERE id = OLD.id;
Expand Down
16 changes: 8 additions & 8 deletions internal/internal-api/data/tenantDB.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package data

import (
"context"
"database/sql"
"fmt"
"errors"
"os"

"github.com/dxta-dev/app/internal/otel"
Expand All @@ -12,7 +13,7 @@ type TenantDB struct {
DB *sql.DB
}

func NewTenantDB(dbUrl string) (TenantDB, error) {
func NewTenantDB(dbUrl string, ctx context.Context) (TenantDB, error) {
driverName := otel.GetDriverName()
devToken := os.Getenv("DXTA_DEV_GROUP_TOKEN")

Expand All @@ -22,12 +23,11 @@ func NewTenantDB(dbUrl string) (TenantDB, error) {
)

if err != nil {
fmt.Printf(
"Issue while opening tenant database connection. DBUrl: %s Error: %s",
dbUrl,
err.Error(),
)
return TenantDB{}, err
return TenantDB{}, errors.New("failed to open tenant db connection " + err.Error())
}

if err := tenantDB.PingContext(ctx); err != nil {
return TenantDB{}, errors.New("failed to verify tenant db connection " + err.Error())
}

return TenantDB{
Expand Down
4 changes: 2 additions & 2 deletions internal/internal-api/internal-api.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ func GetTenantDBUrlByAuthId(ctx context.Context, authId string) (TenantDBData, e
return tenantData, nil
}

func InternalApiState(dbUrl string, r *http.Request) (State, error) {
tenantDB, err := data.NewTenantDB(dbUrl)
func InternalApiState(ctx context.Context, dbUrl string, r *http.Request) (State, error) {
tenantDB, err := data.NewTenantDB(dbUrl, ctx)

if err != nil {
return State{}, err
Expand Down
18 changes: 13 additions & 5 deletions internal/onboarding/activity/github_installation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@ func NewGithubInstallationActivities(GithubAppClient onboarding.GithubAppClient)
}
}

func (gia *GithubInstallationActivities) GetGithubInstallation(
type GithubInstallationOrganization struct {
OrganizationID int64
OrganizationLogin string
}

func (gia *GithubInstallationActivities) GetInstallationOrganization(
ctx context.Context,
installationId int64,
) (string, error) {
login, err := gia.githubAppClient.GetOrganizationLogin(ctx, installationId)
) (*GithubInstallationOrganization, error) {
account, err := gia.githubAppClient.GetInstallationAccount(ctx, installationId)
Comment thread
stefanskoricdev marked this conversation as resolved.

if err != nil {
fmt.Printf("Could not retrieve installation. Error: %v", err.Error())
return "", err
return nil, err
}

return login, nil
return &GithubInstallationOrganization{
OrganizationID: account.ID,
OrganizationLogin: account.Login,
}, nil
}
70 changes: 70 additions & 0 deletions internal/onboarding/activity/github_organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package activity

import (
"context"
"errors"

"github.com/dxta-dev/app/internal/onboarding"
)

func (ta *TenantActivities) UpsertGithubOrganization(
ctx context.Context,
DBURL string,
installationId int64,
installationOrgName string,
installationOrgId int64,
organizationId int64,
) (res int64, err error) {
db, err := onboarding.GetCachedTenantDB(ta.DBConnections, DBURL, ctx)

if err != nil {
return 0, err
}

tx, err := db.BeginTx(ctx, nil)

if err != nil {
return 0, err
}

defer func() {
if err != nil {
_ = tx.Rollback()
}
}()

rows := tx.QueryRowContext(ctx, `
INSERT INTO github_organizations
(github_app_installation_id, name, external_id)
VALUES
(?, ?, ?)
ON CONFLICT (external_id)
DO UPDATE SET
github_app_installation_id = excluded.github_app_installation_id,
name = excluded.name
RETURNING id`,
installationId, installationOrgName, installationOrgId)

var githubOrganizationId int64

if err = rows.Scan(&githubOrganizationId); err != nil {
return 0, errors.New("failed to upsert github_organizations: " + err.Error())
}

_, err = tx.Exec(`
INSERT OR IGNORE INTO 'organizations__github_organizations'
('organization_id', 'github_organization_id')
VALUES
(?, ?)`,
organizationId, githubOrganizationId)

if err != nil {
return 0, errors.New("failed to upsert organizations__github_organizations:" + err.Error())
}

if err := tx.Commit(); err != nil {
return 0, errors.New("failed to commit github organization upsert tx: " + err.Error())
}

return githubOrganizationId, nil
}
40 changes: 40 additions & 0 deletions internal/onboarding/activity/organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package activity

import (
"context"
"errors"
"sync"

"github.com/dxta-dev/app/internal/onboarding"
)

type TenantActivities struct {
DBConnections *sync.Map
}

func NewTenantActivities(DBConnections *sync.Map) *TenantActivities {
return &TenantActivities{DBConnections}
}

func (ta *TenantActivities) GetOrganizationIDByAuthID(ctx context.Context, authID string, DBURL string) (int64, error) {
db, err := onboarding.GetCachedTenantDB(ta.DBConnections, DBURL, ctx)

if err != nil {
return 0, err
}

var organizationId int64

if err = db.QueryRowContext(ctx, `
SELECT
id
FROM
organizations
WHERE
auth_id = ?;`,
authID).Scan(&organizationId); err != nil {
return 0, errors.New("failed to retrieve organization: " + err.Error())
}

return organizationId, nil
}
22 changes: 15 additions & 7 deletions internal/onboarding/github_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
}, nil
}

func getInstallationTransport(

Check failure on line 56 in internal/onboarding/github_config.go

View workflow job for this annotation

GitHub Actions / test

unreachable func: getInstallationTransport
tr http.RoundTripper,
installationId int64,
appId int64,
Expand Down Expand Up @@ -106,7 +106,7 @@
)
}

func NewInstallationClient(

Check failure on line 109 in internal/onboarding/github_config.go

View workflow job for this annotation

GitHub Actions / test

unreachable func: NewInstallationClient
installationId int64,
tr http.RoundTripper,
cfg GithubConfig,
Expand All @@ -131,25 +131,33 @@
client *github.Client
}

func (gac *GithubAppClient) GetOrganizationLogin(
type Account struct {
ID int64
Login string
}

func (gac *GithubAppClient) GetInstallationAccount(
ctx context.Context,
installationID int64,
) (string, error) {
) (*Account, error) {
installation, _, error := gac.client.Apps.GetInstallation(ctx, installationID)
if error != nil {
return "", errors.New("failed to get installation: " + error.Error())
return nil, errors.New("failed to get installation: " + error.Error())

}

if installation.Account == nil || installation.Account.Login == nil {
return "", errors.New("installation account or login is nil")
return nil, errors.New("installation account or login is nil")
}

if installation.TargetType == nil || *installation.TargetType != "organization" {
return "", errors.New("installation is not for an organization")
if installation.TargetType == nil || *installation.TargetType != "Organization" {
return nil, errors.New("installation is not for an organization")
}

return *installation.Account.Login, nil
return &Account{
ID: *installation.Account.ID,
Login: *installation.Account.Login,
}, nil
}

func NewAppClient(cfg GithubConfig) (*GithubAppClient, error) {
Expand Down
27 changes: 27 additions & 0 deletions internal/onboarding/tenant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package onboarding

import (
"context"
"database/sql"
"errors"
"sync"

internal_api_data "github.com/dxta-dev/app/internal/internal-api/data"
)

func GetCachedTenantDB(store *sync.Map, dbUrl string, ctx context.Context) (*sql.DB, error) {
db, ok := store.Load(dbUrl)

if !ok {
tenantDB, err := internal_api_data.NewTenantDB(dbUrl, ctx)

if err != nil {
return nil, errors.New("failed to create tenant db connection: " + err.Error())
}

db = tenantDB.DB
store.Store(dbUrl, db)
}

return db.(*sql.DB), nil
}
Loading