package workspaces import ( "context" "database/sql" "errors" "fmt" "strings" "inbox/internal/base/slug" "inbox/internal/domain/workspace" ) type Repository interface { ListProjects(ctx context.Context) ([]workspace.Project, error) CreateProject(ctx context.Context, value workspace.Project) (workspace.Project, error) ListWorkspaces(ctx context.Context, projectID string) ([]workspace.Workspace, error) CreateWorkspace(ctx context.Context, value workspace.Workspace) (workspace.Workspace, error) GetWorkspace(ctx context.Context, workspaceID string) (workspace.Workspace, error) GetWorkspaceBySlugOrName(ctx context.Context, value string) (workspace.Workspace, error) UpdateWorkspace(ctx context.Context, value workspace.Workspace) error } type Service struct { repo Repository } func NewService(repo Repository) *Service { return &Service{ repo: repo, } } func (s *Service) ListProjects(ctx context.Context) ([]workspace.Project, error) { return s.repo.ListProjects(ctx) } func (s *Service) CreateProject(ctx context.Context, value workspace.Project) (workspace.Project, error) { value.Slug = normalizeSlug(value.Slug, value.Name) value = workspace.NormalizeProjectForCreate(value) return s.repo.CreateProject(ctx, value) } func (s *Service) ListWorkspaces(ctx context.Context, projectID string) ([]workspace.Workspace, error) { return s.repo.ListWorkspaces(ctx, strings.TrimSpace(projectID)) } func (s *Service) CreateWorkspace(ctx context.Context, value workspace.Workspace) (workspace.Workspace, error) { value.Slug = normalizeSlug(value.Slug, value.Name) value.RuntimeBackend = workspace.ManagedRuntimeBackend value = workspace.NormalizeWorkspaceForCreate(value) return s.repo.CreateWorkspace(ctx, value) } func (s *Service) Get(ctx context.Context, workspaceID string) (workspace.Workspace, error) { return s.repo.GetWorkspace(ctx, workspaceID) } func (s *Service) GetBySlugOrName(ctx context.Context, value string) (workspace.Workspace, error) { return s.repo.GetWorkspaceBySlugOrName(ctx, strings.TrimSpace(value)) } func (s *Service) Resolve(ctx context.Context, workspaceID, workspaceValue string) (workspace.Workspace, error) { if workspaceID = strings.TrimSpace(workspaceID); workspaceID != "" { item, err := s.repo.GetWorkspace(ctx, workspaceID) if err != nil { if errors.Is(err, sql.ErrNoRows) { return workspace.Workspace{}, fmt.Errorf("workspace not found: %s: %w", workspaceID, sql.ErrNoRows) } return workspace.Workspace{}, err } return item, nil } if workspaceValue = strings.TrimSpace(workspaceValue); workspaceValue != "" { item, err := s.repo.GetWorkspaceBySlugOrName(ctx, workspaceValue) if err != nil { if errors.Is(err, sql.ErrNoRows) { return workspace.Workspace{}, fmt.Errorf("workspace not found: %s: %w", workspaceValue, sql.ErrNoRows) } return workspace.Workspace{}, err } return item, nil } return workspace.Workspace{}, fmt.Errorf("workspace is required") } func normalizeSlug(explicit, fallback string) string { if value := slug.Normalize(explicit); value != "" { return value } return slug.Normalize(fallback) }