package services import ( "context" "database/sql" "fmt" "net/http" sqlerr "edge-infra.dev/pkg/edge/api/apierror/sql" sqlerror "edge-infra.dev/pkg/edge/api/apierror/sql" "edge-infra.dev/pkg/edge/api/bsl/types" "edge-infra.dev/pkg/edge/api/graph/model" sqlquery "edge-infra.dev/pkg/edge/api/sql" "edge-infra.dev/pkg/edge/bsl" ) const ( effectiveOrganizations = "/provisioning/user-profiles/effective-organizations?pageNumber=%d&pageSize=%d" ) //go:generate mockgen -destination=../mocks/mock_tenant_service.go -package=mocks edge-infra.dev/pkg/edge/api/services TenantService type TenantService interface { Create(ctx context.Context, input *model.TenantInput) (*model.Tenant, error) List(ctx context.Context) ([]*model.Tenant, error) Get(ctx context.Context, id string) (*model.Tenant, error) GetTenantByBannerID(ctx context.Context, id string) (*model.Tenant, error) GetTenantByClusterEdgeID(ctx context.Context, id string) (*model.Tenant, error) GetByName(ctx context.Context, name string) (*model.Tenant, error) GetOktaTenants(ctx context.Context, token string) ([]*model.Tenant, error) } type tenantService struct { SQLDB *sql.DB BSLClient *bsl.Client BSPConfig *types.BSPConfig } func (o *tenantService) GetTenantByBannerID(ctx context.Context, id string) (*model.Tenant, error) { var orgUUID, bslID, name string row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetTenantByBannerEdgeID, id) err := row.Scan(&orgUUID, &bslID, &name) if err != nil { return nil, sqlerror.Wrap(err) } tenant := &model.Tenant{ TenantEdgeID: orgUUID, TenantBSLId: bslID, OrgName: name, } return tenant, nil } func (o *tenantService) GetTenantByClusterEdgeID(ctx context.Context, id string) (*model.Tenant, error) { var orgUUID, bslID, name string row := o.SQLDB.QueryRowContext(ctx, sqlquery.GetTenantByClusterEdgeID, id) err := row.Scan(&orgUUID, &bslID, &name) if err != nil { return nil, sqlerror.Wrap(err) } tenant := &model.Tenant{ TenantEdgeID: orgUUID, TenantBSLId: bslID, OrgName: name, } return tenant, nil } func (o *tenantService) Create(ctx context.Context, input *model.TenantInput) (*model.Tenant, error) { if _, err := o.SQLDB.ExecContext(ctx, sqlquery.TenantInsertQuery, input.TenantBSLId, input.OrgName); err != nil { return nil, sqlerror.Wrap(err) } var tenant model.Tenant row := o.SQLDB.QueryRowContext(ctx, sqlquery.TenantGetNoIDQuery, input.TenantBSLId, input.OrgName) if err := row.Scan(&tenant.TenantEdgeID, &tenant.TenantBSLId, &tenant.OrgName); err != nil { return nil, sqlerror.Wrap(err) } return &tenant, nil } func (o *tenantService) List(ctx context.Context) ([]*model.Tenant, error) { rows, err := o.SQLDB.QueryContext(ctx, sqlquery.TenantListQuery) if err != nil { return nil, sqlerror.Wrap(err) } tenants := []*model.Tenant{} defer rows.Close() for rows.Next() { var tenant model.Tenant if err = rows.Scan(&tenant.TenantEdgeID, &tenant.TenantBSLId, &tenant.OrgName); err != nil { return nil, sqlerror.Wrap(err) } tenants = append(tenants, &tenant) } if err := rows.Err(); err != nil { return nil, sqlerr.Wrap(err) } return tenants, nil } func (o *tenantService) Get(ctx context.Context, id string) (*model.Tenant, error) { var tenant model.Tenant row := o.SQLDB.QueryRowContext(ctx, sqlquery.TenantGetQuery, id) if err := row.Scan(&tenant.TenantEdgeID, &tenant.TenantBSLId, &tenant.OrgName); err != nil { return nil, sqlerror.Wrap(err) } return &tenant, nil } func (o *tenantService) GetByName(ctx context.Context, name string) (*model.Tenant, error) { var tenant model.Tenant row := o.SQLDB.QueryRowContext(ctx, sqlquery.TenantGetByName, name) if err := row.Scan(&tenant.TenantEdgeID, &tenant.TenantBSLId, &tenant.OrgName); err != nil { return nil, sqlerror.Wrap(err) } return &tenant, nil } func (o *tenantService) GetOktaTenants(ctx context.Context, token string) ([]*model.Tenant, error) { tenants := make([]*model.Tenant, 0) uniqueTenants := make(map[string]bool, 0) data := &types.EffectiveOrganizationsResponse{} pageNumber := 0 client := o.BSLClient.WithOktaToken(ctx, token) for !data.LastPage { err := client.JSON(http.MethodGet, fmt.Sprintf(effectiveOrganizations, pageNumber, pageSize), data) if err != nil { return tenants, err } for _, pageContent := range data.PageContent { orgTenant := bsl.GetTenant(o.BSPConfig.Root, pageContent.OrganizationName) if _, exists := uniqueTenants[orgTenant]; !exists { tenant, err := o.GetByName(ctx, orgTenant) if err != nil { return tenants, err } uniqueTenants[tenant.OrgName] = true tenants = append(tenants, tenant) } } pageNumber++ } return tenants, nil } func NewTenantService(sqlDB *sql.DB, bslClient *bsl.Client, bspConfig *types.BSPConfig) *tenantService { //nolint stupid return &tenantService{ SQLDB: sqlDB, BSLClient: bslClient, BSPConfig: bspConfig, } }