Skip to content

Multitenancy #231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 47 commits into
base: staging
Choose a base branch
from
Open

Multitenancy #231

wants to merge 47 commits into from

Conversation

thelissimus-work
Copy link

No description provided.

@thelissimus-work thelissimus-work changed the title Sdk 213 Multitenancy Mar 27, 2025
@diyor28 diyor28 requested a review from Copilot March 27, 2025 14:36
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces multitenancy support across multiple modules by adding tenant context propagation, repository, service, and domain logic.

  • Adds tenant context to the authentication middleware
  • Implements tenant service and repository for CRUD operations
  • Updates domain models and repository mappers to incorporate tenant IDs

Reviewed Changes

Copilot reviewed 13 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/middleware/auth.go Adds tenant lookup in authentication flow
pkg/constants/middleware.go Introduces a TenantKey constant
pkg/composables/tenant.go Provides helper methods for using tenant data
modules/core/services/tenant_service.go Implements tenant service methods
modules/core/module.go Registers tenant and tab services; duplicate tab service registration observed
modules/core/infrastructure/persistence/user_repository.go Adds tenant_id handling in user repository records
modules/core/infrastructure/persistence/tenant_repository.go Implements tenant repository with query methods
modules/core/infrastructure/persistence/models/models.go Adds tenant fields to various models
modules/core/infrastructure/persistence/core_mappers.go Maps tenant_id for user entity conversions
modules/core/domain/entities/tenant/* Defines tenant entity and repository interface
modules/core/domain/aggregates/user/user.go Introduces tenantID in the user aggregate
Files not reviewed (7)
  • modules/bichat/infrastructure/persistence/schema/bichat-schema.sql: Language not supported
  • modules/core/infrastructure/persistence/schema/core-schema.sql: Language not supported
  • modules/crm/infrastructure/persistence/schema/crm-schema.sql: Language not supported
  • modules/finance/infrastructure/persistence/schema/finance-schema.sql: Language not supported
  • modules/hrm/infrastructure/persistence/schema/hrm-schema.sql: Language not supported
  • modules/logging/infrastructure/persistence/schema/logging-schema.sql: Language not supported
  • modules/warehouse/infrastructure/persistence/schema/warehouse-schema.sql: Language not supported
Comments suppressed due to low confidence (1)

modules/core/module.go:67

  • Duplicate registration of TabService is detected; consider removing the extra registration to avoid potential unexpected behavior.
services.NewTabService(persistence.NewTabRepository()),

@diyor28
Copy link
Contributor

diyor28 commented Mar 27, 2025

@thelissimus-work Use UUID for tenants table. We will be migrating to UUID pretty soon, so why not start using UUID for new tables already

Copy link

cypress bot commented Mar 31, 2025

Default Project    Run #393

Run Properties:  status check passed Passed #393  •  git commit 6aba8250fd: Merge branch 'staging' into sdk-213
Project Default Project
Branch Review sdk-213
Run status status check passed Passed #393
Run duration 00m 23s
Commit git commit 6aba8250fd: Merge branch 'staging' into sdk-213
Committer Bektosh Madaminov
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 3
View all changes introduced in this branch ↗︎

@bektosh-iota bektosh-iota self-assigned this Apr 1, 2025
@@ -126,8 +137,14 @@ func (g *GormUploadRepository) Count(ctx context.Context) (int64, error) {
if err != nil {
return 0, err
}

tenant, err := composables.UseTenant(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to require tenantId as an explicit parameter in function signatures where it is mandatory, instead of implicitly relying on its presence within a context object (ctx).

  • Error Prevention: If tenantId is mandatory for the function's logic, requiring it in the signature prevents runtime errors that occur when it's missing from the ctx. This shifts error detection earlier in the development cycle.
  • Reduced Boilerplate: This approach eliminates the repetitive code currently needed in many methods and repositories to extract and validate the tenantId from the context.
  • Improved Clarity & Explicitness: Making tenantId an explicit parameter clearly communicates the function's dependency, making the code easier to understand and maintain, thereby reducing the potential for runtime errors.

Handling Multi-Tenant vs. All-Tenant Queries:
For scenarios where a method needs to retrieve records across all tenants (i.e., be tenant-independent), we could handle this by passing a designated value for the tenantId parameter (e.g., an empty string, null, or a specific constant). The method implementation would then check this parameter's value to decide whether or not to include the tenant filtering condition in its database query.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is indeed correct. I pointed this out to Diyor as well, for some reason tenant id propagation is done like this throughout the whole repo.
I'm reworking this whole thing

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mirolimsgd Regarding this issue.
It's been quite some time since this PR is hanging and because it's massive, the longer it halts, the more problems like merge conflicts and it's getting hard maintaining this PR. So maybe I could rework tenant id propagation in a different PR. I'd create a separate issue for it.
It's just that it's taking some time to do this. Besides, speeding up this with AI turned out to be a bit of problem, as it turns out AI still struggles with big refactorings across many files.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bektosh-iota This wasn't implemented by me, so implement it as you see fit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the problem now. Tenant is already part of domain entities and db models, so passing it through context makes no sense

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mirolimsgd Regarding this issue. It's been quite some time since this PR is hanging and because it's massive, the longer it halts, the more problems like merge conflicts and it's getting hard maintaining this PR. So maybe I could rework tenant id propagation in a different PR. I'd create a separate issue for it. It's just that it's taking some time to do this. Besides, speeding up this with AI turned out to be a bit of problem, as it turns out AI still struggles with big refactorings across many files.

It is just suggestions. I am not blocking pr. The problem I see if it is merged it then it will be used and if not immediately fixed will create more work later so I suggest to change it now. It will reduce amount of code in this pr and later there is no need to spend more time.
Both options work for me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mirolimsgd Regarding this issue. It's been quite some time since this PR is hanging and because it's massive, the longer it halts, the more problems like merge conflicts and it's getting hard maintaining this PR. So maybe I could rework tenant id propagation in a different PR. I'd create a separate issue for it. It's just that it's taking some time to do this. Besides, speeding up this with AI turned out to be a bit of problem, as it turns out AI still struggles with big refactorings across many files.

It is just suggestions. I am not blocking pr. The problem I see if it is merged it then it will be used and if not immediately fixed will create more work later so I suggest to change it now. It will reduce amount of code in this pr and later there is no need to spend more time. Both options work for me.

I agree

@bektosh-iota bektosh-iota marked this pull request as ready for review April 14, 2025 11:25
sessionUpdateQuery = `
UPDATE sessions
SET expires_at = $1,
ip = $2,
user_agent = $3
WHERE token = $4`
sessionDeleteQuery = `DELETE FROM sessions WHERE token = $1`
WHERE token = $4 AND tenant_id = $5`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? Token is already a primary key

WHERE token = $4`
sessionDeleteQuery = `DELETE FROM sessions WHERE token = $1`
WHERE token = $4 AND tenant_id = $5`
sessionDeleteQuery = `DELETE FROM sessions WHERE token = $1 AND tenant_id = $2`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well

var count int64
if err := tx.QueryRow(ctx, sessionCountQuery).Scan(&count); err != nil {
if err := tx.QueryRow(ctx, sessionCountQuery+" WHERE tenant_id = $1", tenant.ID).Scan(&count); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For constructing sql queries in the runtime use methods from the repo package. Reference other usages of this package to get an idea on how to use it

"github.com/google/uuid"
)

type Tenant struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason Tenant isn't an interface like other domain entities?

if err := tx.Commit(ctx); err != nil {
t.Fatal(err)
// Rollback instead of commit to ensure clean state
// This is safer as it ensures tests don't affect each other
Copy link
Contributor

@diyor28 diyor28 Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They already don't affect each other since each test runs in a separate db. Committing changes is useful to be able to introspect the db state after a failed test

ErrNoTenantFound = errors.New("no tenant found in context")
)

type Tenant struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this duplicate type for?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants