tercul-backend/internal/app/translation/commands.go
Damir Mukimov d50722dad5
Some checks failed
Test / Integration Tests (push) Successful in 4s
Build / Build Binary (push) Failing after 2m9s
Docker Build / Build Docker Image (push) Failing after 2m32s
Test / Unit Tests (push) Failing after 3m12s
Lint / Go Lint (push) Failing after 1m0s
Refactor ID handling to use UUIDs across the application
- Updated database models and repositories to replace uint IDs with UUIDs.
- Modified test fixtures to generate and use UUIDs for authors, translations, users, and works.
- Adjusted mock implementations to align with the new UUID structure.
- Ensured all relevant functions and methods are updated to handle UUIDs correctly.
- Added necessary imports for UUID handling in various files.
2025-12-27 00:33:34 +01:00

120 lines
3.4 KiB
Go

package translation
import (
"context"
"fmt"
"tercul/internal/app/authz"
"tercul/internal/domain"
platform_auth "tercul/internal/platform/auth"
"github.com/google/uuid"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// TranslationCommands contains the command handlers for the translation aggregate.
type TranslationCommands struct {
repo domain.TranslationRepository
authzSvc *authz.Service
tracer trace.Tracer
}
// NewTranslationCommands creates a new TranslationCommands handler.
func NewTranslationCommands(repo domain.TranslationRepository, authzSvc *authz.Service) *TranslationCommands {
return &TranslationCommands{
repo: repo,
authzSvc: authzSvc,
tracer: otel.Tracer("translation.commands"),
}
}
// CreateOrUpdateTranslationInput represents the input for creating or updating a translation.
type CreateOrUpdateTranslationInput struct {
Title string
Content string
Description string
Language string
Status domain.TranslationStatus
TranslatableID uuid.UUID
TranslatableType string
TranslatorID *uuid.UUID
IsOriginalLanguage bool
}
// CreateOrUpdateTranslation creates a new translation or updates an existing one.
func (c *TranslationCommands) CreateOrUpdateTranslation(ctx context.Context, input CreateOrUpdateTranslationInput) (*domain.Translation, error) {
ctx, span := c.tracer.Start(ctx, "CreateOrUpdateTranslation")
defer span.End()
// Validate input first
if input.Language == "" {
return nil, fmt.Errorf("%w: language cannot be empty", domain.ErrValidation)
}
if input.TranslatableID == uuid.Nil {
return nil, fmt.Errorf("%w: translatable ID cannot be nil", domain.ErrValidation)
}
if input.TranslatableType == "" {
return nil, fmt.Errorf("%w: translatable type cannot be empty", domain.ErrValidation)
}
userID, ok := platform_auth.GetUserIDFromContext(ctx)
if !ok {
return nil, domain.ErrUnauthorized
}
// Authorize that the user can edit the parent entity.
can, err := c.authzSvc.CanEditEntity(ctx, userID, input.TranslatableType, input.TranslatableID)
if err != nil {
return nil, fmt.Errorf("authorization check failed: %w", err)
}
if !can {
return nil, domain.ErrForbidden
}
var translatorID uuid.UUID
if input.TranslatorID != nil {
translatorID = *input.TranslatorID
} else {
translatorID = userID
}
translation := &domain.Translation{
Title: input.Title,
Content: input.Content,
Description: input.Description,
Language: input.Language,
Status: input.Status,
TranslatableID: input.TranslatableID,
TranslatableType: input.TranslatableType,
TranslatorID: &translatorID,
IsOriginalLanguage: input.IsOriginalLanguage,
}
if err := c.repo.Upsert(ctx, translation); err != nil {
return nil, fmt.Errorf("failed to upsert translation: %w", err)
}
return translation, nil
}
// DeleteTranslation deletes a translation by ID.
func (c *TranslationCommands) DeleteTranslation(ctx context.Context, id uuid.UUID) error {
ctx, span := c.tracer.Start(ctx, "DeleteTranslation")
defer span.End()
userID, ok := platform_auth.GetUserIDFromContext(ctx)
if !ok {
return domain.ErrUnauthorized
}
can, err := c.authzSvc.CanDeleteTranslation(ctx, userID, id)
if err != nil {
return err
}
if !can {
return domain.ErrForbidden
}
return c.repo.Delete(ctx, id)
}