package translation import ( "context" "errors" "fmt" "tercul/internal/app/authz" "tercul/internal/domain" platform_auth "tercul/internal/platform/auth" "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"), } } // CreateTranslationInput represents the input for creating a new translation. type CreateTranslationInput struct { Title string Content string Description string Language string Status domain.TranslationStatus TranslatableID uint TranslatableType string TranslatorID *uint IsOriginalLanguage bool } // CreateTranslation creates a new translation. func (c *TranslationCommands) CreateTranslation(ctx context.Context, input CreateTranslationInput) (*domain.Translation, error) { ctx, span := c.tracer.Start(ctx, "CreateTranslation") defer span.End() 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: input.TranslatorID, IsOriginalLanguage: input.IsOriginalLanguage, } err := c.repo.Create(ctx, translation) if err != nil { return nil, err } return translation, nil } // UpdateTranslationInput represents the input for updating an existing translation. type UpdateTranslationInput struct { ID uint Title string Content string Description string Language string Status domain.TranslationStatus } // UpdateTranslation updates an existing translation. func (c *TranslationCommands) UpdateTranslation(ctx context.Context, input UpdateTranslationInput) (*domain.Translation, error) { ctx, span := c.tracer.Start(ctx, "UpdateTranslation") defer span.End() userID, ok := platform_auth.GetUserIDFromContext(ctx) if !ok { return nil, domain.ErrUnauthorized } can, err := c.authzSvc.CanEditTranslation(ctx, userID, input.ID) if err != nil { return nil, err } if !can { return nil, domain.ErrForbidden } translation, err := c.repo.GetByID(ctx, input.ID) if err != nil { if errors.Is(err, domain.ErrEntityNotFound) { return nil, fmt.Errorf("%w: translation with id %d not found", domain.ErrEntityNotFound, input.ID) } return nil, err } translation.Title = input.Title translation.Content = input.Content translation.Description = input.Description translation.Language = input.Language translation.Status = input.Status err = c.repo.Update(ctx, translation) if err != nil { return nil, err } return translation, nil } // DeleteTranslation deletes a translation by ID. func (c *TranslationCommands) DeleteTranslation(ctx context.Context, id uint) error { ctx, span := c.tracer.Start(ctx, "DeleteTranslation") defer span.End() can, err := c.authzSvc.CanDeleteTranslation(ctx) if err != nil { return err } if !can { return domain.ErrForbidden } return c.repo.Delete(ctx, id) }