From f1b0d8247afd84bb1625ffac1f1d51e87e6332bf Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Mon, 13 Nov 2023 15:29:32 -0300 Subject: [PATCH 01/13] feat: tenant server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cecilia Romão --- cmd/serve.go | 28 +++++ cmd/serve_test.go | 18 +++- dev/docker-compose-sdp-anchor.yml | 2 + internal/serve/serve.go | 3 - .../internal/httphandler/health_handler.go | 20 ++++ .../httphandler/health_handler_test.go | 30 ++++++ stellar-multitenant/pkg/serve/serve.go | 100 ++++++++++++++++++ stellar-multitenant/pkg/serve/serve_test.go | 63 +++++++++++ 8 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 stellar-multitenant/pkg/internal/httphandler/health_handler.go create mode 100644 stellar-multitenant/pkg/internal/httphandler/health_handler_test.go create mode 100644 stellar-multitenant/pkg/serve/serve.go create mode 100644 stellar-multitenant/pkg/serve/serve_test.go diff --git a/cmd/serve.go b/cmd/serve.go index c040a1e24..0261dd4b5 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -21,6 +21,7 @@ import ( "github.com/stellar/go/support/log" "github.com/stellar/stellar-disbursement-platform-backend/internal/monitor" "github.com/stellar/stellar-disbursement-platform-backend/internal/serve" + serveTenants "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/serve" ) type ServeCommand struct{} @@ -28,6 +29,7 @@ type ServeCommand struct{} type ServerServiceInterface interface { StartServe(opts serve.ServeOptions, httpServer serve.HTTPServerInterface) StartMetricsServe(opts serve.MetricsServeOptions, httpServer serve.HTTPServerInterface) + StartTenantServe(opts serveTenants.ServeOptions, httpServer serveTenants.HTTPServerInterface) GetSchedulerJobRegistrars(ctx context.Context, serveOpts serve.ServeOptions, schedulerOptions scheduler.SchedulerOptions, apAPIService anchorplatform.AnchorPlatformAPIServiceInterface) ([]scheduler.SchedulerJobRegisterOption, error) } @@ -50,6 +52,13 @@ func (s *ServerService) StartMetricsServe(opts serve.MetricsServeOptions, httpSe } } +func (s *ServerService) StartTenantServe(opts serveTenants.ServeOptions, httpServer serveTenants.HTTPServerInterface) { + err := serveTenants.StartServe(opts, httpServer) + if err != nil { + log.Fatalf("Error starting metrics server: %s", err.Error()) + } +} + func (s *ServerService) GetSchedulerJobRegistrars(ctx context.Context, serveOpts serve.ServeOptions, schedulerOptions scheduler.SchedulerOptions, apAPIService anchorplatform.AnchorPlatformAPIServiceInterface) ([]scheduler.SchedulerJobRegisterOption, error) { // TODO: inject these in the server options, to do the Dependency Injection properly. dbConnectionPool, err := db.OpenDBConnectionPool(globalOptions.databaseURL) @@ -80,6 +89,7 @@ func (s *ServerService) GetSchedulerJobRegistrars(ctx context.Context, serveOpts func (c *ServeCommand) Command(serverService ServerServiceInterface, monitorService monitor.MonitorServiceInterface) *cobra.Command { serveOpts := serve.ServeOptions{} metricsServeOpts := serve.MetricsServeOptions{} + tenantServeOpts := serveTenants.ServeOptions{} schedulerOptions := scheduler.SchedulerOptions{} crashTrackerOptions := crashtracker.CrashTrackerOptions{} @@ -109,6 +119,14 @@ func (c *ServeCommand) Command(serverService ServerServiceInterface, monitorServ FlagDefault: 8002, Required: true, }, + { + Name: "tenant-serve-port", + Usage: "Port where the tenant server will be listening on", + OptType: types.Int, + ConfigKey: &tenantServeOpts.Port, + FlagDefault: 8003, + Required: true, + }, { Name: "crash-tracker-type", Usage: `Crash tracker type. Options: "SENTRY", "DRY_RUN"`, @@ -342,6 +360,13 @@ func (c *ServeCommand) Command(serverService ServerServiceInterface, monitorServ // Inject metrics server dependencies metricsServeOpts.MonitorService = monitorService metricsServeOpts.Environment = globalOptions.environment + + // Inject tenant server dependencies + tenantServeOpts.DatabaseDSN = globalOptions.databaseURL + tenantServeOpts.Environment = globalOptions.environment + tenantServeOpts.GitCommit = globalOptions.gitCommit + tenantServeOpts.Version = globalOptions.version + tenantServeOpts.NetworkPassphrase = globalOptions.networkPassphrase }, Run: func(cmd *cobra.Command, _ []string) { ctx := cmd.Context() @@ -386,6 +411,9 @@ func (c *ServeCommand) Command(serverService ServerServiceInterface, monitorServ log.Ctx(ctx).Info("Starting Metrics Server...") go serverService.StartMetricsServe(metricsServeOpts, &serve.HTTPServer{}) + log.Ctx(ctx).Info("Starting Tenant Server...") + go serverService.StartTenantServe(tenantServeOpts, &serveTenants.HTTPServer{}) + // Starting Application Server log.Ctx(ctx).Info("Starting Application Server...") serverService.StartServe(serveOpts, &serve.HTTPServer{}) diff --git a/cmd/serve_test.go b/cmd/serve_test.go index 51dfeca3d..d36afaef5 100644 --- a/cmd/serve_test.go +++ b/cmd/serve_test.go @@ -18,6 +18,7 @@ import ( "github.com/stellar/stellar-disbursement-platform-backend/internal/scheduler" "github.com/stellar/stellar-disbursement-platform-backend/internal/serve" "github.com/stellar/stellar-disbursement-platform-backend/internal/serve/httpclient" + serveTenants "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/serve" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -41,6 +42,11 @@ func (m *mockServer) StartMetricsServe(opts serve.MetricsServeOptions, httpServe m.wg.Done() } +func (m *mockServer) StartTenantServe(opts serveTenants.ServeOptions, httpServer serveTenants.HTTPServerInterface) { + m.Called(opts, httpServer) + m.wg.Done() +} + func (m *mockServer) GetSchedulerJobRegistrars(ctx context.Context, serveOpts serve.ServeOptions, schedulerOptions scheduler.SchedulerOptions, apAPIService anchorplatform.AnchorPlatformAPIServiceInterface) ([]scheduler.SchedulerJobRegisterOption, error) { args := m.Called(ctx, serveOpts, schedulerOptions, apAPIService) if args.Get(0) == nil { @@ -146,6 +152,15 @@ func Test_serve(t *testing.T) { MonitorService: &mMonitorService, } + serveTenantOpts := serveTenants.ServeOptions{ + Environment: "test", + DatabaseDSN: randomDatabaseDSN, + GitCommit: "1234567890abcdef", + NetworkPassphrase: network.TestNetworkPassphrase, + Port: 8003, + Version: "x.y.z", + } + schedulerOptions := scheduler.SchedulerOptions{ MaxInvitationSMSResendAttempts: 3, } @@ -154,11 +169,12 @@ func Test_serve(t *testing.T) { mServer := mockServer{} mServer.On("StartMetricsServe", serveMetricOpts, mock.AnythingOfType("*serve.HTTPServer")).Once() mServer.On("StartServe", serveOpts, mock.AnythingOfType("*serve.HTTPServer")).Once() + mServer.On("StartTenantServe", serveTenantOpts, mock.AnythingOfType("*serve.HTTPServer")).Once() mServer. On("GetSchedulerJobRegistrars", mock.AnythingOfType("*context.emptyCtx"), serveOpts, schedulerOptions, mock.Anything). Return([]scheduler.SchedulerJobRegisterOption{}, nil). Once() - mServer.wg.Add(1) + mServer.wg.Add(2) // SetupCLI and replace the serve command with one containing a mocked server rootCmd := SetupCLI("x.y.z", "1234567890abcdef") diff --git a/dev/docker-compose-sdp-anchor.yml b/dev/docker-compose-sdp-anchor.yml index 2c66bfb53..e12c04898 100644 --- a/dev/docker-compose-sdp-anchor.yml +++ b/dev/docker-compose-sdp-anchor.yml @@ -20,6 +20,8 @@ services: dockerfile: Dockerfile ports: - "8000:8000" + - "8002:8002" + - "8003:8003" environment: BASE_URL: http://localhost:8000 DATABASE_URL: postgres://postgres@db:5432/postgres?sslmode=disable diff --git a/internal/serve/serve.go b/internal/serve/serve.go index 800d485c9..57e2072ef 100644 --- a/internal/serve/serve.go +++ b/internal/serve/serve.go @@ -31,7 +31,6 @@ import ( txnsubmitterutils "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils" "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" "github.com/stellar/stellar-disbursement-platform-backend/stellar-auth/pkg/auth" - "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" ) const ServiceID = "serve" @@ -95,8 +94,6 @@ func (opts *ServeOptions) SetupDependencies() error { // Set crash tracker LogAndReportErrors as DefaultReportErrorFunc httperror.SetDefaultReportErrorFunc(opts.CrashTrackerClient.LogAndReportErrors) - _ = tenant.Tenant{} - // Setup Database: dbConnectionPool, err := db.OpenDBConnectionPoolWithMetrics(opts.DatabaseDSN, opts.MonitorService) if err != nil { diff --git a/stellar-multitenant/pkg/internal/httphandler/health_handler.go b/stellar-multitenant/pkg/internal/httphandler/health_handler.go new file mode 100644 index 000000000..f3bf31e0d --- /dev/null +++ b/stellar-multitenant/pkg/internal/httphandler/health_handler.go @@ -0,0 +1,20 @@ +package httphandler + +import ( + "net/http" + + "github.com/stellar/go/support/render/httpjson" +) + +type HealthHandler struct { + GitCommit string + Version string +} + +func (h HealthHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + httpjson.RenderStatus(rw, http.StatusOK, map[string]string{ + "status": "pass", + "release_id": h.GitCommit, + "version": h.Version, + }, httpjson.JSON) +} diff --git a/stellar-multitenant/pkg/internal/httphandler/health_handler_test.go b/stellar-multitenant/pkg/internal/httphandler/health_handler_test.go new file mode 100644 index 000000000..af5644bf3 --- /dev/null +++ b/stellar-multitenant/pkg/internal/httphandler/health_handler_test.go @@ -0,0 +1,30 @@ +package httphandler + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_HealthHandler(t *testing.T) { + // test + req, err := http.NewRequest(http.MethodGet, "/health", nil) + require.NoError(t, err) + rr := httptest.NewRecorder() + http.HandlerFunc(HealthHandler{ + GitCommit: "1234567890abcdef", + Version: "x.y.z", + }.ServeHTTP).ServeHTTP(rr, req) + + // assert response + assert.Equal(t, http.StatusOK, rr.Code) + wantJson := `{ + "status": "pass", + "version": "x.y.z", + "release_id": "1234567890abcdef" + }` + assert.JSONEq(t, wantJson, rr.Body.String()) +} diff --git a/stellar-multitenant/pkg/serve/serve.go b/stellar-multitenant/pkg/serve/serve.go new file mode 100644 index 000000000..e13654788 --- /dev/null +++ b/stellar-multitenant/pkg/serve/serve.go @@ -0,0 +1,100 @@ +package serve + +import ( + "fmt" + "time" + + "github.com/go-chi/chi/v5" + chimiddleware "github.com/go-chi/chi/v5/middleware" + supporthttp "github.com/stellar/go/support/http" + "github.com/stellar/go/support/log" + "github.com/stellar/stellar-disbursement-platform-backend/db" + "github.com/stellar/stellar-disbursement-platform-backend/internal/serve/middleware" + "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/internal/httphandler" + "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" +) + +type HTTPServerInterface interface { + Run(conf supporthttp.Config) +} + +type HTTPServer struct{} + +func (h *HTTPServer) Run(conf supporthttp.Config) { + supporthttp.Run(conf) +} + +type ServeOptions struct { + Environment string + DatabaseDSN string + dbConnectionPool db.DBConnectionPool + GitCommit string + NetworkPassphrase string + Port int + tenantManager *tenant.Manager + Version string +} + +// SetupDependencies uses the serve options to setup the dependencies for the server. +func (opts *ServeOptions) SetupDependencies() error { + // Setup Database: + dbConnectionPool, err := db.OpenDBConnectionPool(opts.DatabaseDSN) + if err != nil { + return fmt.Errorf("error connecting to the database: %w", err) + } + + opts.dbConnectionPool = dbConnectionPool + + opts.tenantManager = tenant.NewManager(tenant.WithDatabase(dbConnectionPool)) + + return nil +} + +func StartServe(opts ServeOptions, httpServer HTTPServerInterface) error { + if err := opts.SetupDependencies(); err != nil { + return fmt.Errorf("error starting dependencies: %w", err) + } + + // Start the server + listenAddr := fmt.Sprintf(":%d", opts.Port) + serverConfig := supporthttp.Config{ + ListenAddr: listenAddr, + Handler: handleHTTP(&opts), + TCPKeepAlive: time.Minute * 3, + ShutdownGracePeriod: time.Second * 50, + ReadTimeout: time.Second * 5, + WriteTimeout: time.Second * 35, + IdleTimeout: time.Minute * 2, + OnStarting: func() { + log.Info("Starting Tenant Server") + log.Infof("Listening on %s", listenAddr) + }, + OnStopping: func() { + log.Info("Closing the database connection...") + err := opts.dbConnectionPool.Close() + if err != nil { + log.Errorf("error closing database connection: %s", err.Error()) + } + + log.Info("Stopping Tenant Server") + }, + } + httpServer.Run(serverConfig) + return nil +} + +func handleHTTP(opts *ServeOptions) *chi.Mux { + mux := chi.NewMux() + + mux.Use(chimiddleware.RequestID) + mux.Use(chimiddleware.RealIP) + mux.Use(supporthttp.LoggingMiddleware) + mux.Use(middleware.RecoverHandler) + + mux.Get("/health", httphandler.HealthHandler{ + GitCommit: opts.GitCommit, + Version: opts.Version, + }.ServeHTTP) + + return mux +} diff --git a/stellar-multitenant/pkg/serve/serve_test.go b/stellar-multitenant/pkg/serve/serve_test.go new file mode 100644 index 000000000..1fe192ada --- /dev/null +++ b/stellar-multitenant/pkg/serve/serve_test.go @@ -0,0 +1,63 @@ +package serve + +import ( + "testing" + "time" + + "github.com/stellar/go/network" + supporthttp "github.com/stellar/go/support/http" + "github.com/stellar/stellar-disbursement-platform-backend/db" + "github.com/stellar/stellar-disbursement-platform-backend/db/dbtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type mockHTTPServer struct { + mock.Mock +} + +func (m *mockHTTPServer) Run(conf supporthttp.Config) { + m.Called(conf) +} + +var _ HTTPServerInterface = new(mockHTTPServer) + +func Test_Serve(t *testing.T) { + dbt := dbtest.OpenWithoutMigrations(t) + defer dbt.Close() + + dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, err) + defer dbConnectionPool.Close() + + opts := ServeOptions{ + DatabaseDSN: dbt.DSN, + Environment: "test", + GitCommit: "1234567890abcdef", + Port: 8003, + Version: "x.y.z", + NetworkPassphrase: network.TestNetworkPassphrase, + } + + // Mock supportHTTPRun + mHTTPServer := mockHTTPServer{} + mHTTPServer.On("Run", mock.AnythingOfType("http.Config")).Run(func(args mock.Arguments) { + conf, ok := args.Get(0).(supporthttp.Config) + require.True(t, ok, "should be of type supporthttp.Config") + assert.Equal(t, ":8003", conf.ListenAddr) + assert.Equal(t, time.Minute*3, conf.TCPKeepAlive) + assert.Equal(t, time.Second*50, conf.ShutdownGracePeriod) + assert.Equal(t, time.Second*5, conf.ReadTimeout) + assert.Equal(t, time.Second*35, conf.WriteTimeout) + assert.Equal(t, time.Minute*2, conf.IdleTimeout) + assert.Nil(t, conf.TLS) + assert.ObjectsAreEqualValues(handleHTTP(&opts), conf.Handler) + conf.OnStopping() + }).Once() + + // test and assert + err = StartServe(opts, &mHTTPServer) + require.NoError(t, err) + mHTTPServer.AssertExpectations(t) +} From 565c8def0d9d12d3791c986b56e2f1157417282d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 19:25:13 -0300 Subject: [PATCH 02/13] [tenant] feat: get tenants queries --- stellar-multitenant/pkg/tenant/manager.go | 106 ++++++++++++++++++ .../pkg/tenant/manager_test.go | 80 +++++++++++++ 2 files changed, 186 insertions(+) diff --git a/stellar-multitenant/pkg/tenant/manager.go b/stellar-multitenant/pkg/tenant/manager.go index 510a614df..e64e1eb2c 100644 --- a/stellar-multitenant/pkg/tenant/manager.go +++ b/stellar-multitenant/pkg/tenant/manager.go @@ -125,6 +125,112 @@ func (m *Manager) ProvisionNewTenant( return t, nil } +func (m *Manager) GetDSNForTenant(ctx context.Context, tenantName string) (string, error) { + dataSourceName, err := m.db.DSN(ctx) + if err != nil { + return "", fmt.Errorf("getting database DSN: %w", err) + } + u, err := url.Parse(dataSourceName) + if err != nil { + return "", fmt.Errorf("parsing database DSN: %w", err) + } + q := u.Query() + schemaName := fmt.Sprintf("sdp_%s", tenantName) + q.Set("search_path", schemaName) + u.RawQuery = q.Encode() + return u.String(), nil +} + +// GetAllTenants returns all tenants in the database. +func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { + var tnts []Tenant + query := ` + SELECT + t.id, + t.name, + t.status, + t.email_sender_type, + t.sms_sender_type, + t.enable_mfa, + t.enable_recaptcha, + t.created_at, + t.updated_at + FROM + tenants t + ORDER BY + t.name ASC + ` + + err := m.db.SelectContext(ctx, &tnts, query) + if err != nil { + return nil, fmt.Errorf("getting all tenants: %w", err) + } + + return tnts, nil +} + +// GetTenantByID returns the tenant with the given id. +func (m *Manager) GetTenantByID(ctx context.Context, id string) (*Tenant, error) { + var tnt Tenant + query := ` + SELECT + t.id, + t.name, + t.status, + t.email_sender_type, + t.sms_sender_type, + t.enable_mfa, + t.enable_recaptcha, + t.created_at, + t.updated_at + FROM + tenants t + WHERE + t.id = $1 + ` + + err := m.db.GetContext(ctx, &tnt, query, id) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrTenantDoesNotExist + } + return nil, fmt.Errorf("getting tenant id %s: %w", id, err) + } + + return &tnt, nil +} + +// GetTenantByName returns the tenant with the given name. +func (m *Manager) GetTenantByName(ctx context.Context, name string) (*Tenant, error) { + var tnt Tenant + query := ` + SELECT + t.id, + t.name, + t.status, + t.email_sender_type, + t.sms_sender_type, + t.enable_mfa, + t.enable_recaptcha, + t.created_at, + t.updated_at + FROM + tenants t + WHERE + t.name = $1 + ` + + err := m.db.GetContext(ctx, &tnt, query, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrTenantDoesNotExist + } + return nil, fmt.Errorf("getting tenant name %s: %w", name, err) + } + + return &tnt, nil +} + func (m *Manager) AddTenant(ctx context.Context, name string) (*Tenant, error) { if name == "" { return nil, ErrEmptyTenantName diff --git a/stellar-multitenant/pkg/tenant/manager_test.go b/stellar-multitenant/pkg/tenant/manager_test.go index 13366a46b..6ea373f3e 100644 --- a/stellar-multitenant/pkg/tenant/manager_test.go +++ b/stellar-multitenant/pkg/tenant/manager_test.go @@ -359,3 +359,83 @@ func Test_Manager_RunMigrationsForTenant(t *testing.T) { TenantSchemaHasTablesFixture(t, ctx, dbConnectionPool, tnt2SchemaName, expectedTablesAfterMigrationsApplied) } + +func Test_Manager_GetAllTenants(t *testing.T) { + dbt := dbtest.OpenWithTenantMigrationsOnly(t) + defer dbt.Close() + + dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, err) + defer dbConnectionPool.Close() + + ctx := context.Background() + + m := NewManager(WithDatabase(dbConnectionPool)) + tnt1, err := m.AddTenant(ctx, "myorg1") + require.NoError(t, err) + tnt2, err := m.AddTenant(ctx, "myorg2") + require.NoError(t, err) + + tenants, err := m.GetAllTenants(ctx) + require.NoError(t, err) + + assert.ElementsMatch(t, tenants, []Tenant{*tnt1, *tnt2}) +} + +func Test_Manager_GetTenantByID(t *testing.T) { + dbt := dbtest.OpenWithTenantMigrationsOnly(t) + defer dbt.Close() + + dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, err) + defer dbConnectionPool.Close() + + ctx := context.Background() + + m := NewManager(WithDatabase(dbConnectionPool)) + _, err = m.AddTenant(ctx, "myorg1") + require.NoError(t, err) + tnt2, err := m.AddTenant(ctx, "myorg2") + require.NoError(t, err) + + t.Run("gets tenant successfully", func(t *testing.T) { + tntDB, err := m.GetTenantByID(ctx, tnt2.ID) + require.NoError(t, err) + assert.Equal(t, tnt2, tntDB) + }) + + t.Run("returns error when tenant is not found", func(t *testing.T) { + tntDB, err := m.GetTenantByID(ctx, "unknown") + assert.ErrorIs(t, err, ErrTenantDoesNotExist) + assert.Nil(t, tntDB) + }) +} + +func Test_Manager_GetTenantByName(t *testing.T) { + dbt := dbtest.OpenWithTenantMigrationsOnly(t) + defer dbt.Close() + + dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, err) + defer dbConnectionPool.Close() + + ctx := context.Background() + + m := NewManager(WithDatabase(dbConnectionPool)) + _, err = m.AddTenant(ctx, "myorg1") + require.NoError(t, err) + tnt2, err := m.AddTenant(ctx, "myorg2") + require.NoError(t, err) + + t.Run("gets tenant successfully", func(t *testing.T) { + tntDB, err := m.GetTenantByName(ctx, "myorg2") + require.NoError(t, err) + assert.Equal(t, tnt2, tntDB) + }) + + t.Run("returns error when tenant is not found", func(t *testing.T) { + tntDB, err := m.GetTenantByName(ctx, "unknown") + assert.ErrorIs(t, err, ErrTenantDoesNotExist) + assert.Nil(t, tntDB) + }) +} From b97dc02bb11bc911eed1a3af85af4e76e92337c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 19:25:44 -0300 Subject: [PATCH 03/13] [serve] feat: get tenant routes --- stellar-multitenant/pkg/serve/serve.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stellar-multitenant/pkg/serve/serve.go b/stellar-multitenant/pkg/serve/serve.go index e13654788..dd38b3b7f 100644 --- a/stellar-multitenant/pkg/serve/serve.go +++ b/stellar-multitenant/pkg/serve/serve.go @@ -96,5 +96,14 @@ func handleHTTP(opts *ServeOptions) *chi.Mux { Version: opts.Version, }.ServeHTTP) + mux.Group(func(r chi.Router) { + r.Route("/tenants", func(r chi.Router) { + tenantsHandler := httphandler.TenantsHandler{Manager: opts.tenantManager} + r.Get("/", tenantsHandler.GetAll) + r.Get("/{id}", tenantsHandler.GetByID) + r.Get("/{name}", tenantsHandler.GetByName) + }) + }) + return mux } From 056f71959e3b112603d4fed6bb97f75af1e76c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 19:26:15 -0300 Subject: [PATCH 04/13] feat: create tenant fixture --- stellar-multitenant/pkg/tenant/fixtures.go | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/stellar-multitenant/pkg/tenant/fixtures.go b/stellar-multitenant/pkg/tenant/fixtures.go index 979de1dc3..7b983e648 100644 --- a/stellar-multitenant/pkg/tenant/fixtures.go +++ b/stellar-multitenant/pkg/tenant/fixtures.go @@ -6,6 +6,7 @@ import ( "github.com/lib/pq" "github.com/stellar/stellar-disbursement-platform-backend/db" + "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -101,3 +102,30 @@ func AssertRegisteredUser(t *testing.T, ctx context.Context, dbConnectionPool db assert.Equal(t, pq.StringArray{"owner"}, user.Roles) assert.True(t, user.IsOwner) } + +func CreateTenantFixture(t *testing.T, ctx context.Context, sqlExec db.SQLExecuter, name string) *Tenant { + tenantName := name + if name == "" { + name, err := utils.RandomString(56) + require.NoError(t, err) + tenantName = name + } + + const query = ` + INSERT + INTO tenants (name) + VALUES ($1) + RETURNING + id, name, status, email_sender_type, sms_sender_type, + enable_mfa, enable_recaptcha, created_at, updated_at + ` + + tnt := &Tenant{ + Name: tenantName, + } + + err := sqlExec.QueryRowxContext(ctx, query, tnt.Name).Scan(&tnt.ID, &tnt.Name, &tnt.Status, &tnt.EmailSenderType, &tnt.SMSSenderType, &tnt.EnableMFA, &tnt.EnableReCAPTCHA, &tnt.CreatedAt, &tnt.UpdatedAt) + require.NoError(t, err) + + return tnt +} From bcf04cccdce822d03ea004551f5f8d0bc7a000ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 19:26:41 -0300 Subject: [PATCH 05/13] feat: tenant handler --- .../internal/httphandler/tenants_handler.go | 64 ++++++++++++++ .../httphandler/tenants_handler_test.go | 86 +++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 stellar-multitenant/pkg/internal/httphandler/tenants_handler.go create mode 100644 stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go new file mode 100644 index 000000000..f6ba39c51 --- /dev/null +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go @@ -0,0 +1,64 @@ +package httphandler + +import ( + "errors" + "fmt" + "net/http" + + "github.com/go-chi/chi" + "github.com/stellar/go/support/render/httpjson" + "github.com/stellar/stellar-disbursement-platform-backend/internal/serve/httperror" + "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" +) + +type TenantsHandler struct { + Manager *tenant.Manager +} + +func (t TenantsHandler) GetAll(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + tnts, err := t.Manager.GetAllTenants(ctx) + if err != nil { + httperror.InternalError(ctx, "Cannot get tenants", err, nil).Render(w) + return + } + + httpjson.RenderStatus(w, http.StatusOK, tnts, httpjson.JSON) +} + +func (t TenantsHandler) GetByID(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + tenantID := chi.URLParam(r, "id") + + tnt, err := t.Manager.GetTenantByID(ctx, tenantID) + if err != nil { + if errors.Is(tenant.ErrTenantDoesNotExist, err) { + errorMsg := fmt.Sprintf("a tenant with the id %s does not exist", tenantID) + httperror.NotFound(errorMsg, err, nil).Render(w) + return + } + httperror.InternalError(ctx, "Cannot get tenant by ID", err, nil).Render(w) + return + } + + httpjson.RenderStatus(w, http.StatusOK, tnt, httpjson.JSON) +} + +func (t TenantsHandler) GetByName(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + tenantName := chi.URLParam(r, "name") + + tnt, err := t.Manager.GetTenantByName(ctx, tenantName) + if err != nil { + if errors.Is(tenant.ErrTenantDoesNotExist, err) { + errorMsg := fmt.Sprintf("a tenant with the name %s does not exist", tenantName) + httperror.NotFound(errorMsg, err, nil).Render(w) + return + } + httperror.InternalError(ctx, "Cannot get tenant by ID", err, nil).Render(w) + return + } + + httpjson.RenderStatus(w, http.StatusOK, tnt, httpjson.JSON) +} diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go new file mode 100644 index 000000000..a821be4c2 --- /dev/null +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go @@ -0,0 +1,86 @@ +package httphandler + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stellar/stellar-disbursement-platform-backend/db" + "github.com/stellar/stellar-disbursement-platform-backend/db/dbtest" + "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_GetTenant(t *testing.T) { + dbt := dbtest.OpenWithTenantMigrationsOnly(t) + defer dbt.Close() + + dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) + require.NoError(t, err) + defer dbConnectionPool.Close() + + ctx := context.Background() + + handler := &TenantsHandler{ + Manager: &tenant.Manager{}, + } + + tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") + tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") + + tnt1JSON, err := json.Marshal(tnt1) + require.NoError(t, err) + tnt2JSON, err := json.Marshal(tnt2) + require.NoError(t, err) + + t.Run("successfully returns a list of all tenants", func(t *testing.T) { + expectedJSON := fmt.Sprintf("[%s, %s]", tnt1JSON, tnt2JSON) + + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/tenants/", nil) + http.HandlerFunc(handler.GetAll).ServeHTTP(rr, req) + + resp := rr.Result() + + respBody, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.JSONEq(t, expectedJSON, string(respBody)) + }) + + t.Run("successfully returns a tenant by ID", func(t *testing.T) { + url := fmt.Sprintf("/tenants/%s", tnt1.ID) + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", url, nil) + http.HandlerFunc(handler.GetAll).ServeHTTP(rr, req) + + resp := rr.Result() + + respBody, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.JSONEq(t, string(tnt1JSON), string(respBody)) + }) + + t.Run("successfully returns a tenant by name", func(t *testing.T) { + url := fmt.Sprintf("/tenants/%s", tnt2.Name) + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", url, nil) + http.HandlerFunc(handler.GetAll).ServeHTTP(rr, req) + + resp := rr.Result() + + respBody, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.JSONEq(t, string(tnt2JSON), string(respBody)) + }) +} From 1a474b64eb8236598eb8c37e7d560f760718a8d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 20:06:37 -0300 Subject: [PATCH 06/13] improve: select query --- stellar-multitenant/pkg/tenant/manager.go | 70 +++++++---------------- 1 file changed, 21 insertions(+), 49 deletions(-) diff --git a/stellar-multitenant/pkg/tenant/manager.go b/stellar-multitenant/pkg/tenant/manager.go index e64e1eb2c..def960d9d 100644 --- a/stellar-multitenant/pkg/tenant/manager.go +++ b/stellar-multitenant/pkg/tenant/manager.go @@ -141,25 +141,27 @@ func (m *Manager) GetDSNForTenant(ctx context.Context, tenantName string) (strin return u.String(), nil } +var selectQuery string = ` + SELECT + t.id, + t.name, + t.status, + t.email_sender_type, + t.sms_sender_type, + t.enable_mfa, + t.enable_recaptcha, + t.created_at, + t.updated_at + FROM + tenants t + %s +` + // GetAllTenants returns all tenants in the database. func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { var tnts []Tenant - query := ` - SELECT - t.id, - t.name, - t.status, - t.email_sender_type, - t.sms_sender_type, - t.enable_mfa, - t.enable_recaptcha, - t.created_at, - t.updated_at - FROM - tenants t - ORDER BY - t.name ASC - ` + + query := fmt.Sprintf(selectQuery, "ORDER BY t.name ASC") err := m.db.SelectContext(ctx, &tnts, query) if err != nil { @@ -171,23 +173,8 @@ func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { // GetTenantByID returns the tenant with the given id. func (m *Manager) GetTenantByID(ctx context.Context, id string) (*Tenant, error) { - var tnt Tenant - query := ` - SELECT - t.id, - t.name, - t.status, - t.email_sender_type, - t.sms_sender_type, - t.enable_mfa, - t.enable_recaptcha, - t.created_at, - t.updated_at - FROM - tenants t - WHERE - t.id = $1 - ` + var tnt Tenant + query := fmt.Sprintf(selectQuery, "WHERE t.id = $1") err := m.db.GetContext(ctx, &tnt, query, id) if err != nil { @@ -203,22 +190,7 @@ func (m *Manager) GetTenantByID(ctx context.Context, id string) (*Tenant, error) // GetTenantByName returns the tenant with the given name. func (m *Manager) GetTenantByName(ctx context.Context, name string) (*Tenant, error) { var tnt Tenant - query := ` - SELECT - t.id, - t.name, - t.status, - t.email_sender_type, - t.sms_sender_type, - t.enable_mfa, - t.enable_recaptcha, - t.created_at, - t.updated_at - FROM - tenants t - WHERE - t.name = $1 - ` + query := fmt.Sprintf(selectQuery, "WHERE t.name = $1") err := m.db.GetContext(ctx, &tnt, query, name) if err != nil { From 9c175b7fbe7646660f8bc87f755d6775b8a7fa10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 20:06:52 -0300 Subject: [PATCH 07/13] feat: DeleteAllTenantFixtures --- stellar-multitenant/pkg/tenant/fixtures.go | 34 +++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/stellar-multitenant/pkg/tenant/fixtures.go b/stellar-multitenant/pkg/tenant/fixtures.go index 7b983e648..f19dad0fb 100644 --- a/stellar-multitenant/pkg/tenant/fixtures.go +++ b/stellar-multitenant/pkg/tenant/fixtures.go @@ -103,6 +103,12 @@ func AssertRegisteredUser(t *testing.T, ctx context.Context, dbConnectionPool db assert.True(t, user.IsOwner) } +func DeleteAllTenantFixtures(t *testing.T, ctx context.Context, sqlExec db.SQLExecuter) { + const query = "DELETE FROM tenants" + _, err := sqlExec.ExecContext(ctx, query) + require.NoError(t, err) +} + func CreateTenantFixture(t *testing.T, ctx context.Context, sqlExec db.SQLExecuter, name string) *Tenant { tenantName := name if name == "" { @@ -112,12 +118,26 @@ func CreateTenantFixture(t *testing.T, ctx context.Context, sqlExec db.SQLExecut } const query = ` - INSERT - INTO tenants (name) - VALUES ($1) - RETURNING - id, name, status, email_sender_type, sms_sender_type, - enable_mfa, enable_recaptcha, created_at, updated_at + WITH create_tenant AS ( + INSERT INTO tenants + (name) + VALUES + ($1) + ON CONFLICT DO NOTHING + RETURNING * + ) + SELECT + ct.id, + ct.name, + ct.status, + ct.email_sender_type, + ct.sms_sender_type, + ct.enable_mfa, + ct.enable_recaptcha, + ct.created_at, + ct.updated_at + FROM + create_tenant ct ` tnt := &Tenant{ @@ -126,6 +146,6 @@ func CreateTenantFixture(t *testing.T, ctx context.Context, sqlExec db.SQLExecut err := sqlExec.QueryRowxContext(ctx, query, tnt.Name).Scan(&tnt.ID, &tnt.Name, &tnt.Status, &tnt.EmailSenderType, &tnt.SMSSenderType, &tnt.EnableMFA, &tnt.EnableReCAPTCHA, &tnt.CreatedAt, &tnt.UpdatedAt) require.NoError(t, err) - + return tnt } From dfbcbc32b3feeb999011f9ea21f4147dab1ee958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Tue, 14 Nov 2023 20:07:00 -0300 Subject: [PATCH 08/13] Update tenants_handler_test.go --- .../httphandler/tenants_handler_test.go | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go index a821be4c2..5a01fc9be 100644 --- a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go @@ -16,7 +16,7 @@ import ( "github.com/stretchr/testify/require" ) -func Test_GetTenant(t *testing.T) { +func Test_Get(t *testing.T) { dbt := dbtest.OpenWithTenantMigrationsOnly(t) defer dbt.Close() @@ -30,15 +30,17 @@ func Test_GetTenant(t *testing.T) { Manager: &tenant.Manager{}, } - tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") - tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") + t.Run("GetAll successfully returns a list of all tenants", func(t *testing.T) { + tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) - tnt1JSON, err := json.Marshal(tnt1) - require.NoError(t, err) - tnt2JSON, err := json.Marshal(tnt2) - require.NoError(t, err) + tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") + tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") + + tnt1JSON, err := json.Marshal(tnt1) + require.NoError(t, err) + tnt2JSON, err := json.Marshal(tnt2) + require.NoError(t, err) - t.Run("successfully returns a list of all tenants", func(t *testing.T) { expectedJSON := fmt.Sprintf("[%s, %s]", tnt1JSON, tnt2JSON) rr := httptest.NewRecorder() @@ -54,11 +56,18 @@ func Test_GetTenant(t *testing.T) { assert.JSONEq(t, expectedJSON, string(respBody)) }) - t.Run("successfully returns a tenant by ID", func(t *testing.T) { + t.Run("GetByID successfully returns a tenant by ID", func(t *testing.T) { + tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) + + tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") + _ = tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") + tnt1JSON, err := json.Marshal(tnt1) + require.NoError(t, err) + url := fmt.Sprintf("/tenants/%s", tnt1.ID) rr := httptest.NewRecorder() req, _ := http.NewRequest("GET", url, nil) - http.HandlerFunc(handler.GetAll).ServeHTTP(rr, req) + http.HandlerFunc(handler.GetByID).ServeHTTP(rr, req) resp := rr.Result() @@ -69,11 +78,18 @@ func Test_GetTenant(t *testing.T) { assert.JSONEq(t, string(tnt1JSON), string(respBody)) }) - t.Run("successfully returns a tenant by name", func(t *testing.T) { + t.Run("GetByName successfully returns a tenant by name", func(t *testing.T) { + tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) + + _ = tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") + tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") + tnt2JSON, err := json.Marshal(tnt2) + require.NoError(t, err) + url := fmt.Sprintf("/tenants/%s", tnt2.Name) rr := httptest.NewRecorder() req, _ := http.NewRequest("GET", url, nil) - http.HandlerFunc(handler.GetAll).ServeHTTP(rr, req) + http.HandlerFunc(handler.GetByName).ServeHTTP(rr, req) resp := rr.Result() From 2094c522329370064027d9bee03466b2808d360b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Wed, 15 Nov 2023 14:35:11 -0300 Subject: [PATCH 09/13] fix: tenants handler test --- .../internal/httphandler/tenants_handler.go | 2 +- .../httphandler/tenants_handler_test.go | 48 ++++++++----------- stellar-multitenant/pkg/serve/serve.go | 4 +- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go index f6ba39c51..382c1d7db 100644 --- a/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - "github.com/go-chi/chi" + "github.com/go-chi/chi/v5" "github.com/stellar/go/support/render/httpjson" "github.com/stellar/stellar-disbursement-platform-backend/internal/serve/httperror" "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go index 5a01fc9be..9b58aa302 100644 --- a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go @@ -9,6 +9,7 @@ import ( "net/http/httptest" "testing" + "github.com/go-chi/chi/v5" "github.com/stellar/stellar-disbursement-platform-backend/db" "github.com/stellar/stellar-disbursement-platform-backend/db/dbtest" "github.com/stellar/stellar-disbursement-platform-backend/stellar-multitenant/pkg/tenant" @@ -26,25 +27,28 @@ func Test_Get(t *testing.T) { ctx := context.Background() - handler := &TenantsHandler{ - Manager: &tenant.Manager{}, + handler := TenantsHandler{ + Manager: tenant.NewManager(tenant.WithDatabase(dbConnectionPool)), } - t.Run("GetAll successfully returns a list of all tenants", func(t *testing.T) { - tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) + r := chi.NewRouter() + r.Get("/tenants/{id:^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$}", handler.GetByID) + r.Get("/tenants/{name:^[a-z-]+$}", handler.GetByName) - tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") - tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") + tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) + tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "org-one") + tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "org-two") - tnt1JSON, err := json.Marshal(tnt1) - require.NoError(t, err) - tnt2JSON, err := json.Marshal(tnt2) - require.NoError(t, err) + tnt1JSON, err := json.Marshal(tnt1) + require.NoError(t, err) + tnt2JSON, err := json.Marshal(tnt2) + require.NoError(t, err) + t.Run("GetAll successfully returns a list of all tenants", func(t *testing.T) { expectedJSON := fmt.Sprintf("[%s, %s]", tnt1JSON, tnt2JSON) rr := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/tenants/", nil) + req, _ := http.NewRequest("GET", "/tenants", nil) http.HandlerFunc(handler.GetAll).ServeHTTP(rr, req) resp := rr.Result() @@ -57,20 +61,13 @@ func Test_Get(t *testing.T) { }) t.Run("GetByID successfully returns a tenant by ID", func(t *testing.T) { - tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) - - tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") - _ = tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") - tnt1JSON, err := json.Marshal(tnt1) - require.NoError(t, err) - url := fmt.Sprintf("/tenants/%s", tnt1.ID) rr := httptest.NewRecorder() - req, _ := http.NewRequest("GET", url, nil) - http.HandlerFunc(handler.GetByID).ServeHTTP(rr, req) + req, err := http.NewRequest("GET", url, nil) + require.NoError(t, err) + r.ServeHTTP(rr, req) resp := rr.Result() - respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) @@ -79,17 +76,10 @@ func Test_Get(t *testing.T) { }) t.Run("GetByName successfully returns a tenant by name", func(t *testing.T) { - tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) - - _ = tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") - tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") - tnt2JSON, err := json.Marshal(tnt2) - require.NoError(t, err) - url := fmt.Sprintf("/tenants/%s", tnt2.Name) rr := httptest.NewRecorder() req, _ := http.NewRequest("GET", url, nil) - http.HandlerFunc(handler.GetByName).ServeHTTP(rr, req) + r.ServeHTTP(rr, req) resp := rr.Result() diff --git a/stellar-multitenant/pkg/serve/serve.go b/stellar-multitenant/pkg/serve/serve.go index dd38b3b7f..ddf1409a9 100644 --- a/stellar-multitenant/pkg/serve/serve.go +++ b/stellar-multitenant/pkg/serve/serve.go @@ -100,8 +100,8 @@ func handleHTTP(opts *ServeOptions) *chi.Mux { r.Route("/tenants", func(r chi.Router) { tenantsHandler := httphandler.TenantsHandler{Manager: opts.tenantManager} r.Get("/", tenantsHandler.GetAll) - r.Get("/{id}", tenantsHandler.GetByID) - r.Get("/{name}", tenantsHandler.GetByName) + r.Get("/{name:^[a-z-]+$}", tenantsHandler.GetByName) + r.Get("/{id:^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$}", tenantsHandler.GetByID) }) }) From b36c70576608428408a352e0a13e0bcac27585c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Wed, 15 Nov 2023 15:30:11 -0300 Subject: [PATCH 10/13] fix: single func to get tenant by id or name --- .../internal/httphandler/tenants_handler.go | 28 +++---------- .../httphandler/tenants_handler_test.go | 11 +++--- stellar-multitenant/pkg/serve/serve.go | 3 +- stellar-multitenant/pkg/tenant/manager.go | 26 +++---------- .../pkg/tenant/manager_test.go | 39 ++++--------------- 5 files changed, 24 insertions(+), 83 deletions(-) diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go index 382c1d7db..128b6ef35 100644 --- a/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler.go @@ -27,36 +27,18 @@ func (t TenantsHandler) GetAll(w http.ResponseWriter, r *http.Request) { httpjson.RenderStatus(w, http.StatusOK, tnts, httpjson.JSON) } -func (t TenantsHandler) GetByID(w http.ResponseWriter, r *http.Request) { +func (t TenantsHandler) GetByIDOrName(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - tenantID := chi.URLParam(r, "id") + arg := chi.URLParam(r, "arg") - tnt, err := t.Manager.GetTenantByID(ctx, tenantID) + tnt, err := t.Manager.GetTenantByIDOrName(ctx, arg) if err != nil { if errors.Is(tenant.ErrTenantDoesNotExist, err) { - errorMsg := fmt.Sprintf("a tenant with the id %s does not exist", tenantID) + errorMsg := fmt.Sprintf("tenant %s does not exist", arg) httperror.NotFound(errorMsg, err, nil).Render(w) return } - httperror.InternalError(ctx, "Cannot get tenant by ID", err, nil).Render(w) - return - } - - httpjson.RenderStatus(w, http.StatusOK, tnt, httpjson.JSON) -} - -func (t TenantsHandler) GetByName(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - tenantName := chi.URLParam(r, "name") - - tnt, err := t.Manager.GetTenantByName(ctx, tenantName) - if err != nil { - if errors.Is(tenant.ErrTenantDoesNotExist, err) { - errorMsg := fmt.Sprintf("a tenant with the name %s does not exist", tenantName) - httperror.NotFound(errorMsg, err, nil).Render(w) - return - } - httperror.InternalError(ctx, "Cannot get tenant by ID", err, nil).Render(w) + httperror.InternalError(ctx, "Cannot get tenant by ID or name", err, nil).Render(w) return } diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go index 9b58aa302..19261381a 100644 --- a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go @@ -32,12 +32,11 @@ func Test_Get(t *testing.T) { } r := chi.NewRouter() - r.Get("/tenants/{id:^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$}", handler.GetByID) - r.Get("/tenants/{name:^[a-z-]+$}", handler.GetByName) + r.Get("/tenants/{arg}", handler.GetByIDOrName) tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) - tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "org-one") - tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "org-two") + tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") + tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") tnt1JSON, err := json.Marshal(tnt1) require.NoError(t, err) @@ -60,7 +59,7 @@ func Test_Get(t *testing.T) { assert.JSONEq(t, expectedJSON, string(respBody)) }) - t.Run("GetByID successfully returns a tenant by ID", func(t *testing.T) { + t.Run("successfully returns a tenant by ID", func(t *testing.T) { url := fmt.Sprintf("/tenants/%s", tnt1.ID) rr := httptest.NewRecorder() req, err := http.NewRequest("GET", url, nil) @@ -75,7 +74,7 @@ func Test_Get(t *testing.T) { assert.JSONEq(t, string(tnt1JSON), string(respBody)) }) - t.Run("GetByName successfully returns a tenant by name", func(t *testing.T) { + t.Run("successfully returns a tenant by name", func(t *testing.T) { url := fmt.Sprintf("/tenants/%s", tnt2.Name) rr := httptest.NewRecorder() req, _ := http.NewRequest("GET", url, nil) diff --git a/stellar-multitenant/pkg/serve/serve.go b/stellar-multitenant/pkg/serve/serve.go index ddf1409a9..80a29f1a0 100644 --- a/stellar-multitenant/pkg/serve/serve.go +++ b/stellar-multitenant/pkg/serve/serve.go @@ -100,8 +100,7 @@ func handleHTTP(opts *ServeOptions) *chi.Mux { r.Route("/tenants", func(r chi.Router) { tenantsHandler := httphandler.TenantsHandler{Manager: opts.tenantManager} r.Get("/", tenantsHandler.GetAll) - r.Get("/{name:^[a-z-]+$}", tenantsHandler.GetByName) - r.Get("/{id:^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$}", tenantsHandler.GetByID) + r.Get("/{arg}", tenantsHandler.GetByIDOrName) }) }) diff --git a/stellar-multitenant/pkg/tenant/manager.go b/stellar-multitenant/pkg/tenant/manager.go index def960d9d..8169d5a1f 100644 --- a/stellar-multitenant/pkg/tenant/manager.go +++ b/stellar-multitenant/pkg/tenant/manager.go @@ -171,33 +171,17 @@ func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { return tnts, nil } -// GetTenantByID returns the tenant with the given id. -func (m *Manager) GetTenantByID(ctx context.Context, id string) (*Tenant, error) { +// GetTenantByIDOrName returns the tenant with a given id or name. +func (m *Manager) GetTenantByIDOrName(ctx context.Context, arg string) (*Tenant, error) { var tnt Tenant - query := fmt.Sprintf(selectQuery, "WHERE t.id = $1") + query := fmt.Sprintf(selectQuery, "WHERE t.id = $1 OR t.name = $1") - err := m.db.GetContext(ctx, &tnt, query, id) + err := m.db.GetContext(ctx, &tnt, query, arg) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrTenantDoesNotExist } - return nil, fmt.Errorf("getting tenant id %s: %w", id, err) - } - - return &tnt, nil -} - -// GetTenantByName returns the tenant with the given name. -func (m *Manager) GetTenantByName(ctx context.Context, name string) (*Tenant, error) { - var tnt Tenant - query := fmt.Sprintf(selectQuery, "WHERE t.name = $1") - - err := m.db.GetContext(ctx, &tnt, query, name) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, ErrTenantDoesNotExist - } - return nil, fmt.Errorf("getting tenant name %s: %w", name, err) + return nil, fmt.Errorf("getting tenant %s: %w", arg, err) } return &tnt, nil diff --git a/stellar-multitenant/pkg/tenant/manager_test.go b/stellar-multitenant/pkg/tenant/manager_test.go index 6ea373f3e..0721927e2 100644 --- a/stellar-multitenant/pkg/tenant/manager_test.go +++ b/stellar-multitenant/pkg/tenant/manager_test.go @@ -382,7 +382,7 @@ func Test_Manager_GetAllTenants(t *testing.T) { assert.ElementsMatch(t, tenants, []Tenant{*tnt1, *tnt2}) } -func Test_Manager_GetTenantByID(t *testing.T) { +func Test_Manager_GetTenantByIDOrName(t *testing.T) { dbt := dbtest.OpenWithTenantMigrationsOnly(t) defer dbt.Close() @@ -393,48 +393,25 @@ func Test_Manager_GetTenantByID(t *testing.T) { ctx := context.Background() m := NewManager(WithDatabase(dbConnectionPool)) - _, err = m.AddTenant(ctx, "myorg1") + tnt1, err := m.AddTenant(ctx, "myorg1") require.NoError(t, err) tnt2, err := m.AddTenant(ctx, "myorg2") require.NoError(t, err) - t.Run("gets tenant successfully", func(t *testing.T) { - tntDB, err := m.GetTenantByID(ctx, tnt2.ID) + t.Run("gets tenant by ID successfully", func(t *testing.T) { + tntDB, err := m.GetTenantByIDOrName(ctx, tnt1.ID) require.NoError(t, err) - assert.Equal(t, tnt2, tntDB) + assert.Equal(t, tnt1, tntDB) }) - t.Run("returns error when tenant is not found", func(t *testing.T) { - tntDB, err := m.GetTenantByID(ctx, "unknown") - assert.ErrorIs(t, err, ErrTenantDoesNotExist) - assert.Nil(t, tntDB) - }) -} - -func Test_Manager_GetTenantByName(t *testing.T) { - dbt := dbtest.OpenWithTenantMigrationsOnly(t) - defer dbt.Close() - - dbConnectionPool, err := db.OpenDBConnectionPool(dbt.DSN) - require.NoError(t, err) - defer dbConnectionPool.Close() - - ctx := context.Background() - - m := NewManager(WithDatabase(dbConnectionPool)) - _, err = m.AddTenant(ctx, "myorg1") - require.NoError(t, err) - tnt2, err := m.AddTenant(ctx, "myorg2") - require.NoError(t, err) - - t.Run("gets tenant successfully", func(t *testing.T) { - tntDB, err := m.GetTenantByName(ctx, "myorg2") + t.Run("gets tenant by name successfully", func(t *testing.T) { + tntDB, err := m.GetTenantByIDOrName(ctx, tnt2.Name) require.NoError(t, err) assert.Equal(t, tnt2, tntDB) }) t.Run("returns error when tenant is not found", func(t *testing.T) { - tntDB, err := m.GetTenantByName(ctx, "unknown") + tntDB, err := m.GetTenantByIDOrName(ctx, "unknown") assert.ErrorIs(t, err, ErrTenantDoesNotExist) assert.Nil(t, tntDB) }) From 1ea40a8f97649deec858a9ca09d1009b8e0cfeca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Wed, 15 Nov 2023 15:41:49 -0300 Subject: [PATCH 11/13] Update manager.go --- stellar-multitenant/pkg/tenant/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stellar-multitenant/pkg/tenant/manager.go b/stellar-multitenant/pkg/tenant/manager.go index 8169d5a1f..c99e23eb2 100644 --- a/stellar-multitenant/pkg/tenant/manager.go +++ b/stellar-multitenant/pkg/tenant/manager.go @@ -173,7 +173,7 @@ func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { // GetTenantByIDOrName returns the tenant with a given id or name. func (m *Manager) GetTenantByIDOrName(ctx context.Context, arg string) (*Tenant, error) { - var tnt Tenant + var tnt Tenant query := fmt.Sprintf(selectQuery, "WHERE t.id = $1 OR t.name = $1") err := m.db.GetContext(ctx, &tnt, query, arg) From 07c8d33f390974e5ea8f629f276493aace3ea82e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Thu, 16 Nov 2023 15:28:33 -0300 Subject: [PATCH 12/13] fix: manager and mocks --- stellar-multitenant/pkg/tenant/manager.go | 73 ++++++------------- .../pkg/tenant/manager_test.go | 23 ++++-- stellar-multitenant/pkg/tenant/mocks.go | 12 ++- 3 files changed, 46 insertions(+), 62 deletions(-) diff --git a/stellar-multitenant/pkg/tenant/manager.go b/stellar-multitenant/pkg/tenant/manager.go index 66a269f10..7c40afc75 100644 --- a/stellar-multitenant/pkg/tenant/manager.go +++ b/stellar-multitenant/pkg/tenant/manager.go @@ -22,8 +22,10 @@ type tenantContextKey struct{} type ManagerInterface interface { GetDSNForTenant(ctx context.Context, tenantName string) (string, error) + GetAllTenants(ctx context.Context) ([]Tenant, error) GetTenantByID(ctx context.Context, id string) (*Tenant, error) GetTenantByName(ctx context.Context, name string) (*Tenant, error) + GetTenantByIDOrName(ctx context.Context, arg string) (*Tenant, error) AddTenant(ctx context.Context, name string) (*Tenant, error) UpdateTenantConfig(ctx context.Context, tu *TenantUpdate) (*Tenant, error) } @@ -48,17 +50,30 @@ func (m *Manager) GetDSNForTenant(ctx context.Context, tenantName string) (strin return u.String(), nil } +var selectQuery string = ` + SELECT + * + FROM + tenants t + %s +` + +// GetAllTenants returns all tenants in the database. func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { - const q = `SELECT * FROM tenants` - var tenants []Tenant - if err := m.db.SelectContext(ctx, &tenants, q); err != nil { + var tnts []Tenant + + query := fmt.Sprintf(selectQuery, "ORDER BY t.name ASC") + + err := m.db.SelectContext(ctx, &tnts, query) + if err != nil { return nil, fmt.Errorf("getting all tenants: %w", err) } - return tenants, nil + + return tnts, nil } func (m *Manager) GetTenantByID(ctx context.Context, id string) (*Tenant, error) { - const q = "SELECT * FROM tenants WHERE id = $1" + q := fmt.Sprintf(selectQuery, "WHERE t.id = $1") var t Tenant if err := m.db.GetContext(ctx, &t, q, id); err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -70,7 +85,7 @@ func (m *Manager) GetTenantByID(ctx context.Context, id string) (*Tenant, error) } func (m *Manager) GetTenantByName(ctx context.Context, name string) (*Tenant, error) { - const q = "SELECT * FROM tenants WHERE name = $1" + q := fmt.Sprintf(selectQuery, "WHERE t.name = $1") var t Tenant if err := m.db.GetContext(ctx, &t, q, name); err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -81,52 +96,6 @@ func (m *Manager) GetTenantByName(ctx context.Context, name string) (*Tenant, er return &t, nil } -func (m *Manager) GetDSNForTenant(ctx context.Context, tenantName string) (string, error) { - dataSourceName, err := m.db.DSN(ctx) - if err != nil { - return "", fmt.Errorf("getting database DSN: %w", err) - } - u, err := url.Parse(dataSourceName) - if err != nil { - return "", fmt.Errorf("parsing database DSN: %w", err) - } - q := u.Query() - schemaName := fmt.Sprintf("sdp_%s", tenantName) - q.Set("search_path", schemaName) - u.RawQuery = q.Encode() - return u.String(), nil -} - -var selectQuery string = ` - SELECT - t.id, - t.name, - t.status, - t.email_sender_type, - t.sms_sender_type, - t.enable_mfa, - t.enable_recaptcha, - t.created_at, - t.updated_at - FROM - tenants t - %s -` - -// GetAllTenants returns all tenants in the database. -func (m *Manager) GetAllTenants(ctx context.Context) ([]Tenant, error) { - var tnts []Tenant - - query := fmt.Sprintf(selectQuery, "ORDER BY t.name ASC") - - err := m.db.SelectContext(ctx, &tnts, query) - if err != nil { - return nil, fmt.Errorf("getting all tenants: %w", err) - } - - return tnts, nil -} - // GetTenantByIDOrName returns the tenant with a given id or name. func (m *Manager) GetTenantByIDOrName(ctx context.Context, arg string) (*Tenant, error) { var tnt Tenant diff --git a/stellar-multitenant/pkg/tenant/manager_test.go b/stellar-multitenant/pkg/tenant/manager_test.go index 72d5b5269..0dbebbbd0 100644 --- a/stellar-multitenant/pkg/tenant/manager_test.go +++ b/stellar-multitenant/pkg/tenant/manager_test.go @@ -160,7 +160,7 @@ func Test_Manager_GetAllTenants(t *testing.T) { assert.ElementsMatch(t, tenants, []Tenant{*tnt1, *tnt2}) } -func Test_Manager_GetTenantByName(t *testing.T) { +func Test_Manager_GetTenantByID(t *testing.T) { dbt := dbtest.OpenWithTenantMigrationsOnly(t) defer dbt.Close() @@ -177,19 +177,19 @@ func Test_Manager_GetTenantByName(t *testing.T) { require.NoError(t, err) t.Run("gets tenant successfully", func(t *testing.T) { - tntDB, err := m.GetTenantByName(ctx, "myorg2") + tntDB, err := m.GetTenantByID(ctx, tnt2.ID) require.NoError(t, err) assert.Equal(t, tnt2, tntDB) }) t.Run("returns error when tenant is not found", func(t *testing.T) { - tntDB, err := m.GetTenantByName(ctx, "unknown") + tntDB, err := m.GetTenantByID(ctx, "unknown") assert.ErrorIs(t, err, ErrTenantDoesNotExist) assert.Nil(t, tntDB) }) } -func Test_Manager_GetAllTenants(t *testing.T) { +func Test_Manager_GetTenantByName(t *testing.T) { dbt := dbtest.OpenWithTenantMigrationsOnly(t) defer dbt.Close() @@ -200,15 +200,22 @@ func Test_Manager_GetAllTenants(t *testing.T) { ctx := context.Background() m := NewManager(WithDatabase(dbConnectionPool)) - tnt1, err := m.AddTenant(ctx, "myorg1") + _, err = m.AddTenant(ctx, "myorg1") require.NoError(t, err) tnt2, err := m.AddTenant(ctx, "myorg2") require.NoError(t, err) - tenants, err := m.GetAllTenants(ctx) - require.NoError(t, err) + t.Run("gets tenant successfully", func(t *testing.T) { + tntDB, err := m.GetTenantByName(ctx, "myorg2") + require.NoError(t, err) + assert.Equal(t, tnt2, tntDB) + }) - assert.ElementsMatch(t, tenants, []Tenant{*tnt1, *tnt2}) + t.Run("returns error when tenant is not found", func(t *testing.T) { + tntDB, err := m.GetTenantByName(ctx, "unknown") + assert.ErrorIs(t, err, ErrTenantDoesNotExist) + assert.Nil(t, tntDB) + }) } func Test_Manager_GetTenantByIDOrName(t *testing.T) { diff --git a/stellar-multitenant/pkg/tenant/mocks.go b/stellar-multitenant/pkg/tenant/mocks.go index bcaba79a3..b9af7c7fd 100644 --- a/stellar-multitenant/pkg/tenant/mocks.go +++ b/stellar-multitenant/pkg/tenant/mocks.go @@ -15,12 +15,12 @@ func (m *TenantManagerMock) GetDSNForTenant(ctx context.Context, tenantName stri return args.String(0), args.Error(1) } -func (m *TenantManagerMock) GetTenant(ctx context.Context) (*Tenant, error) { +func (m *TenantManagerMock) GetAllTenants(ctx context.Context) ([]Tenant, error) { args := m.Called(ctx) if args.Get(0) == nil { return nil, args.Error(1) } - return args.Get(0).(*Tenant), args.Error(1) + return args.Get(0).([]Tenant), args.Error(1) } func (m *TenantManagerMock) GetTenantByName(ctx context.Context, name string) (*Tenant, error) { @@ -39,6 +39,14 @@ func (m *TenantManagerMock) GetTenantByID(ctx context.Context, id string) (*Tena return args.Get(0).(*Tenant), args.Error(1) } +func (m *TenantManagerMock) GetTenantByIDOrName(ctx context.Context, arg string) (*Tenant, error) { + args := m.Called(ctx, arg) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).(*Tenant), args.Error(1) +} + func (m *TenantManagerMock) AddTenant(ctx context.Context, name string) (*Tenant, error) { args := m.Called(ctx, name) if args.Get(0) == nil { From d54c7a6aeae5de301ecada096cd118f2d48637e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Thu, 16 Nov 2023 15:47:25 -0300 Subject: [PATCH 13/13] fix: fixture --- .../pkg/internal/httphandler/tenants_handler_test.go | 2 +- stellar-multitenant/pkg/tenant/fixtures.go | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go index 19261381a..7a8080696 100644 --- a/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go +++ b/stellar-multitenant/pkg/internal/httphandler/tenants_handler_test.go @@ -34,7 +34,7 @@ func Test_Get(t *testing.T) { r := chi.NewRouter() r.Get("/tenants/{arg}", handler.GetByIDOrName) - tenant.DeleteAllTenantFixtures(t, ctx, dbConnectionPool) + tenant.DeleteAllTenantsFixture(t, ctx, dbConnectionPool) tnt1 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg1") tnt2 := tenant.CreateTenantFixture(t, ctx, dbConnectionPool, "myorg2") diff --git a/stellar-multitenant/pkg/tenant/fixtures.go b/stellar-multitenant/pkg/tenant/fixtures.go index db0d3252b..241bec35f 100644 --- a/stellar-multitenant/pkg/tenant/fixtures.go +++ b/stellar-multitenant/pkg/tenant/fixtures.go @@ -8,9 +8,9 @@ import ( "github.com/lib/pq" migrate "github.com/rubenv/sql-migrate" "github.com/stellar/stellar-disbursement-platform-backend/db" - "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" authmigrations "github.com/stellar/stellar-disbursement-platform-backend/db/migrations/auth-migrations" sdpmigrations "github.com/stellar/stellar-disbursement-platform-backend/db/migrations/sdp-migrations" + "github.com/stellar/stellar-disbursement-platform-backend/internal/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -93,12 +93,6 @@ func AssertRegisteredUser(t *testing.T, ctx context.Context, dbConnectionPool db assert.True(t, user.IsOwner) } -func DeleteAllTenantFixtures(t *testing.T, ctx context.Context, sqlExec db.SQLExecuter) { - const query = "DELETE FROM tenants" - _, err := sqlExec.ExecContext(ctx, query) - require.NoError(t, err) -} - func CreateTenantFixture(t *testing.T, ctx context.Context, sqlExec db.SQLExecuter, name string) *Tenant { tenantName := name if name == "" {