mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
Merge pull request #8 from SamyRai/refactor-work-aggregate
Refactor: Isolate Work aggregate into its own package
This commit is contained in:
commit
9bc1e221e6
@ -17,6 +17,7 @@ import (
|
|||||||
"tercul/internal/app/like"
|
"tercul/internal/app/like"
|
||||||
"tercul/internal/app/translation"
|
"tercul/internal/app/translation"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
platform_auth "tercul/internal/platform/auth"
|
platform_auth "tercul/internal/platform/auth"
|
||||||
"tercul/internal/testutil"
|
"tercul/internal/testutil"
|
||||||
|
|
||||||
@ -966,8 +967,8 @@ func (s *GraphQLIntegrationSuite) TestTrendingWorksQuery() {
|
|||||||
// Arrange
|
// Arrange
|
||||||
work1 := s.CreateTestWork("Work 1", "en", "content")
|
work1 := s.CreateTestWork("Work 1", "en", "content")
|
||||||
work2 := s.CreateTestWork("Work 2", "en", "content")
|
work2 := s.CreateTestWork("Work 2", "en", "content")
|
||||||
s.DB.Create(&domain.WorkStats{WorkID: work1.ID, Views: 100, Likes: 10, Comments: 1})
|
s.DB.Create(&work.WorkStats{WorkID: work1.ID, Views: 100, Likes: 10, Comments: 1})
|
||||||
s.DB.Create(&domain.WorkStats{WorkID: work2.ID, Views: 10, Likes: 100, Comments: 10})
|
s.DB.Create(&work.WorkStats{WorkID: work2.ID, Views: 10, Likes: 100, Comments: 10})
|
||||||
s.Require().NoError(s.App.Analytics.UpdateTrending(context.Background()))
|
s.Require().NoError(s.App.Analytics.UpdateTrending(context.Background()))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"tercul/internal/app/like"
|
"tercul/internal/app/like"
|
||||||
"tercul/internal/app/translation"
|
"tercul/internal/app/translation"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
platform_auth "tercul/internal/platform/auth"
|
platform_auth "tercul/internal/platform/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput
|
|||||||
return nil, fmt.Errorf("%w: %v", ErrValidation, err)
|
return nil, fmt.Errorf("%w: %v", ErrValidation, err)
|
||||||
}
|
}
|
||||||
// Create domain model
|
// Create domain model
|
||||||
work := &domain.Work{
|
workModel := &work.Work{
|
||||||
Title: input.Name,
|
Title: input.Name,
|
||||||
TranslatableModel: domain.TranslatableModel{Language: input.Language},
|
TranslatableModel: domain.TranslatableModel{Language: input.Language},
|
||||||
// Description: *input.Description,
|
// Description: *input.Description,
|
||||||
@ -99,11 +100,10 @@ func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call work service
|
// Call work service
|
||||||
createdWork, err := r.App.Work.Commands.CreateWork(ctx, work)
|
createdWork, err := r.App.Work.Commands.CreateWork(ctx, workModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
work = createdWork
|
|
||||||
|
|
||||||
if input.Content != nil && *input.Content != "" {
|
if input.Content != nil && *input.Content != "" {
|
||||||
translationInput := translation.CreateTranslationInput{
|
translationInput := translation.CreateTranslationInput{
|
||||||
@ -140,7 +140,7 @@ func (r *mutationResolver) UpdateWork(ctx context.Context, id string, input mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create domain model
|
// Create domain model
|
||||||
work := &domain.Work{
|
workModel := &work.Work{
|
||||||
TranslatableModel: domain.TranslatableModel{
|
TranslatableModel: domain.TranslatableModel{
|
||||||
BaseModel: domain.BaseModel{ID: uint(workID)},
|
BaseModel: domain.BaseModel{ID: uint(workID)},
|
||||||
Language: input.Language,
|
Language: input.Language,
|
||||||
@ -149,7 +149,7 @@ func (r *mutationResolver) UpdateWork(ctx context.Context, id string, input mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call work service
|
// Call work service
|
||||||
err = r.App.Work.Commands.UpdateWork(ctx, work)
|
err = r.App.Work.Commands.UpdateWork(ctx, workModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -157,8 +157,8 @@ func (r *mutationResolver) UpdateWork(ctx context.Context, id string, input mode
|
|||||||
// Convert to GraphQL model
|
// Convert to GraphQL model
|
||||||
return &model.Work{
|
return &model.Work{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: work.Title,
|
Name: workModel.Title,
|
||||||
Language: work.Language,
|
Language: workModel.Language,
|
||||||
Content: input.Content,
|
Content: input.Content,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -929,20 +929,20 @@ func (r *queryResolver) Work(ctx context.Context, id string) (*model.Work, error
|
|||||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
work, err := r.App.Work.Queries.GetWorkByID(ctx, uint(workID))
|
workRecord, err := r.App.Work.Queries.GetWorkByID(ctx, uint(workID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if work == nil {
|
if workRecord == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
content := r.resolveWorkContent(ctx, work.ID, work.Language)
|
content := r.resolveWorkContent(ctx, workRecord.ID, workRecord.Language)
|
||||||
|
|
||||||
return &model.Work{
|
return &model.Work{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: work.Title,
|
Name: workRecord.Title,
|
||||||
Language: work.Language,
|
Language: workRecord.Language,
|
||||||
Content: content,
|
Content: content,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -1239,13 +1239,13 @@ func (r *queryResolver) TrendingWorks(ctx context.Context, timePeriod *string, l
|
|||||||
l = int(*limit)
|
l = int(*limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
works, err := r.App.Analytics.GetTrendingWorks(ctx, tp, l)
|
workRecords, err := r.App.Analytics.GetTrendingWorks(ctx, tp, l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var result []*model.Work
|
var result []*model.Work
|
||||||
for _, w := range works {
|
for _, w := range workRecords {
|
||||||
result = append(result, &model.Work{
|
result = append(result, &model.Work{
|
||||||
ID: fmt.Sprintf("%d", w.ID),
|
ID: fmt.Sprintf("%d", w.ID),
|
||||||
Name: w.Title,
|
Name: w.Title,
|
||||||
|
|||||||
22
internal/app/analytics/interfaces.go
Normal file
22
internal/app/analytics/interfaces.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package analytics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnalyticsRepository defines the data access layer for analytics.
|
||||||
|
type Repository interface {
|
||||||
|
IncrementWorkCounter(ctx context.Context, workID uint, field string, value int) error
|
||||||
|
IncrementTranslationCounter(ctx context.Context, translationID uint, field string, value int) error
|
||||||
|
UpdateWorkStats(ctx context.Context, workID uint, stats work.WorkStats) error
|
||||||
|
UpdateTranslationStats(ctx context.Context, translationID uint, stats domain.TranslationStats) error
|
||||||
|
GetOrCreateWorkStats(ctx context.Context, workID uint) (*work.WorkStats, error)
|
||||||
|
GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error)
|
||||||
|
GetOrCreateUserEngagement(ctx context.Context, userID uint, date time.Time) (*domain.UserEngagement, error)
|
||||||
|
UpdateUserEngagement(ctx context.Context, userEngagement *domain.UserEngagement) error
|
||||||
|
UpdateTrendingWorks(ctx context.Context, timePeriod string, trending []*domain.Trending) error
|
||||||
|
GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*work.Work, error)
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/jobs/linguistics"
|
"tercul/internal/jobs/linguistics"
|
||||||
"tercul/internal/platform/log"
|
"tercul/internal/platform/log"
|
||||||
"time"
|
"time"
|
||||||
@ -23,7 +24,7 @@ type Service interface {
|
|||||||
IncrementTranslationLikes(ctx context.Context, translationID uint) error
|
IncrementTranslationLikes(ctx context.Context, translationID uint) error
|
||||||
IncrementTranslationComments(ctx context.Context, translationID uint) error
|
IncrementTranslationComments(ctx context.Context, translationID uint) error
|
||||||
IncrementTranslationShares(ctx context.Context, translationID uint) error
|
IncrementTranslationShares(ctx context.Context, translationID uint) error
|
||||||
GetOrCreateWorkStats(ctx context.Context, workID uint) (*domain.WorkStats, error)
|
GetOrCreateWorkStats(ctx context.Context, workID uint) (*work.WorkStats, error)
|
||||||
GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error)
|
GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error)
|
||||||
|
|
||||||
UpdateWorkReadingTime(ctx context.Context, workID uint) error
|
UpdateWorkReadingTime(ctx context.Context, workID uint) error
|
||||||
@ -34,18 +35,18 @@ type Service interface {
|
|||||||
|
|
||||||
UpdateUserEngagement(ctx context.Context, userID uint, eventType string) error
|
UpdateUserEngagement(ctx context.Context, userID uint, eventType string) error
|
||||||
UpdateTrending(ctx context.Context) error
|
UpdateTrending(ctx context.Context) error
|
||||||
GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*domain.Work, error)
|
GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*work.Work, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
repo domain.AnalyticsRepository
|
repo Repository
|
||||||
analysisRepo linguistics.AnalysisRepository
|
analysisRepo linguistics.AnalysisRepository
|
||||||
translationRepo domain.TranslationRepository
|
translationRepo domain.TranslationRepository
|
||||||
workRepo domain.WorkRepository
|
workRepo work.WorkRepository
|
||||||
sentimentProvider linguistics.SentimentProvider
|
sentimentProvider linguistics.SentimentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(repo domain.AnalyticsRepository, analysisRepo linguistics.AnalysisRepository, translationRepo domain.TranslationRepository, workRepo domain.WorkRepository, sentimentProvider linguistics.SentimentProvider) Service {
|
func NewService(repo Repository, analysisRepo linguistics.AnalysisRepository, translationRepo domain.TranslationRepository, workRepo work.WorkRepository, sentimentProvider linguistics.SentimentProvider) Service {
|
||||||
return &service{
|
return &service{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
analysisRepo: analysisRepo,
|
analysisRepo: analysisRepo,
|
||||||
@ -95,7 +96,7 @@ func (s *service) IncrementTranslationShares(ctx context.Context, translationID
|
|||||||
return s.repo.IncrementTranslationCounter(ctx, translationID, "shares", 1)
|
return s.repo.IncrementTranslationCounter(ctx, translationID, "shares", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) GetOrCreateWorkStats(ctx context.Context, workID uint) (*domain.WorkStats, error) {
|
func (s *service) GetOrCreateWorkStats(ctx context.Context, workID uint) (*work.WorkStats, error) {
|
||||||
return s.repo.GetOrCreateWorkStats(ctx, workID)
|
return s.repo.GetOrCreateWorkStats(ctx, workID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +252,7 @@ func (s *service) UpdateUserEngagement(ctx context.Context, userID uint, eventTy
|
|||||||
return s.repo.UpdateUserEngagement(ctx, engagement)
|
return s.repo.UpdateUserEngagement(ctx, engagement)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*domain.Work, error) {
|
func (s *service) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*work.Work, error) {
|
||||||
return s.repo.GetTrendingWorks(ctx, timePeriod, limit)
|
return s.repo.GetTrendingWorks(ctx, timePeriod, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,10 +265,10 @@ func (s *service) UpdateTrending(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trendingWorks := make([]*domain.Trending, 0, len(works))
|
trendingWorks := make([]*domain.Trending, 0, len(works))
|
||||||
for _, work := range works {
|
for _, aWork := range works {
|
||||||
stats, err := s.repo.GetOrCreateWorkStats(ctx, work.ID)
|
stats, err := s.repo.GetOrCreateWorkStats(ctx, aWork.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.LogWarn("failed to get work stats", log.F("workID", work.ID), log.F("error", err))
|
log.LogWarn("failed to get work stats", log.F("workID", aWork.ID), log.F("error", err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +276,7 @@ func (s *service) UpdateTrending(ctx context.Context) error {
|
|||||||
|
|
||||||
trendingWorks = append(trendingWorks, &domain.Trending{
|
trendingWorks = append(trendingWorks, &domain.Trending{
|
||||||
EntityType: "Work",
|
EntityType: "Work",
|
||||||
EntityID: work.ID,
|
EntityID: aWork.ID,
|
||||||
Score: score,
|
Score: score,
|
||||||
TimePeriod: "daily", // Hardcoded for now
|
TimePeriod: "daily", // Hardcoded for now
|
||||||
Date: time.Now().UTC(),
|
Date: time.Now().UTC(),
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"tercul/internal/app/analytics"
|
"tercul/internal/app/analytics"
|
||||||
"tercul/internal/data/sql"
|
"tercul/internal/data/sql"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/jobs/linguistics"
|
"tercul/internal/jobs/linguistics"
|
||||||
"tercul/internal/testutil"
|
"tercul/internal/testutil"
|
||||||
|
|
||||||
@ -239,8 +240,8 @@ func (s *AnalyticsServiceTestSuite) TestUpdateTrending() {
|
|||||||
// Arrange
|
// Arrange
|
||||||
work1 := s.CreateTestWork("Work 1", "en", "content")
|
work1 := s.CreateTestWork("Work 1", "en", "content")
|
||||||
work2 := s.CreateTestWork("Work 2", "en", "content")
|
work2 := s.CreateTestWork("Work 2", "en", "content")
|
||||||
s.DB.Create(&domain.WorkStats{WorkID: work1.ID, Views: 100, Likes: 10, Comments: 1})
|
s.DB.Create(&work.WorkStats{WorkID: work1.ID, Views: 100, Likes: 10, Comments: 1})
|
||||||
s.DB.Create(&domain.WorkStats{WorkID: work2.ID, Views: 10, Likes: 100, Comments: 10})
|
s.DB.Create(&work.WorkStats{WorkID: work2.ID, Views: 10, Likes: 100, Comments: 10})
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
err := s.service.UpdateTrending(context.Background())
|
err := s.service.UpdateTrending(context.Background())
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockCopyrightRepository struct {
|
type mockCopyrightRepository struct {
|
||||||
@ -172,10 +173,11 @@ func (m *mockCopyrightRepository) WithTx(ctx context.Context, fn func(tx *gorm.D
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockWorkRepository struct {
|
type mockWorkRepository struct {
|
||||||
domain.WorkRepository
|
work.WorkRepository
|
||||||
getByIDWithOptionsFunc func(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error)
|
getByIDWithOptionsFunc func(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error)
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
|
||||||
|
func (m *mockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
if m.getByIDWithOptionsFunc != nil {
|
if m.getByIDWithOptionsFunc != nil {
|
||||||
return m.getByIDWithOptionsFunc(ctx, id, options)
|
return m.getByIDWithOptionsFunc(ctx, id, options)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/platform/log"
|
"tercul/internal/platform/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyrightQueries contains the query handlers for copyright.
|
// CopyrightQueries contains the query handlers for copyright.
|
||||||
type CopyrightQueries struct {
|
type CopyrightQueries struct {
|
||||||
repo domain.CopyrightRepository
|
repo domain.CopyrightRepository
|
||||||
workRepo domain.WorkRepository
|
workRepo work.WorkRepository
|
||||||
authorRepo domain.AuthorRepository
|
authorRepo domain.AuthorRepository
|
||||||
bookRepo domain.BookRepository
|
bookRepo domain.BookRepository
|
||||||
publisherRepo domain.PublisherRepository
|
publisherRepo domain.PublisherRepository
|
||||||
@ -18,7 +19,7 @@ type CopyrightQueries struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewCopyrightQueries creates a new CopyrightQueries handler.
|
// NewCopyrightQueries creates a new CopyrightQueries handler.
|
||||||
func NewCopyrightQueries(repo domain.CopyrightRepository, workRepo domain.WorkRepository, authorRepo domain.AuthorRepository, bookRepo domain.BookRepository, publisherRepo domain.PublisherRepository, sourceRepo domain.SourceRepository) *CopyrightQueries {
|
func NewCopyrightQueries(repo domain.CopyrightRepository, workRepo work.WorkRepository, authorRepo domain.AuthorRepository, bookRepo domain.BookRepository, publisherRepo domain.PublisherRepository, sourceRepo domain.SourceRepository) *CopyrightQueries {
|
||||||
return &CopyrightQueries{repo: repo, workRepo: workRepo, authorRepo: authorRepo, bookRepo: bookRepo, publisherRepo: publisherRepo, sourceRepo: sourceRepo}
|
return &CopyrightQueries{repo: repo, workRepo: workRepo, authorRepo: authorRepo, bookRepo: bookRepo, publisherRepo: publisherRepo, sourceRepo: sourceRepo}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,11 +43,11 @@ func (q *CopyrightQueries) ListCopyrights(ctx context.Context) ([]domain.Copyrig
|
|||||||
// GetCopyrightsForWork gets all copyrights for a specific work.
|
// GetCopyrightsForWork gets all copyrights for a specific work.
|
||||||
func (q *CopyrightQueries) GetCopyrightsForWork(ctx context.Context, workID uint) ([]*domain.Copyright, error) {
|
func (q *CopyrightQueries) GetCopyrightsForWork(ctx context.Context, workID uint) ([]*domain.Copyright, error) {
|
||||||
log.LogDebug("Getting copyrights for work", log.F("work_id", workID))
|
log.LogDebug("Getting copyrights for work", log.F("work_id", workID))
|
||||||
work, err := q.workRepo.GetByIDWithOptions(ctx, workID, &domain.QueryOptions{Preloads: []string{"Copyrights"}})
|
workRecord, err := q.workRepo.GetByIDWithOptions(ctx, workID, &domain.QueryOptions{Preloads: []string{"Copyrights"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return work.Copyrights, nil
|
return workRecord.Copyrights, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCopyrightsForAuthor gets all copyrights for a specific author.
|
// GetCopyrightsForAuthor gets all copyrights for a specific author.
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -99,8 +100,8 @@ func (s *CopyrightQueriesSuite) TestGetCopyrightsForSource_RepoError() {
|
|||||||
|
|
||||||
func (s *CopyrightQueriesSuite) TestGetCopyrightsForWork_Success() {
|
func (s *CopyrightQueriesSuite) TestGetCopyrightsForWork_Success() {
|
||||||
copyrights := []*domain.Copyright{{Name: "Test Copyright"}}
|
copyrights := []*domain.Copyright{{Name: "Test Copyright"}}
|
||||||
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
return &domain.Work{Copyrights: copyrights}, nil
|
return &work.Work{Copyrights: copyrights}, nil
|
||||||
}
|
}
|
||||||
c, err := s.queries.GetCopyrightsForWork(context.Background(), 1)
|
c, err := s.queries.GetCopyrightsForWork(context.Background(), 1)
|
||||||
assert.NoError(s.T(), err)
|
assert.NoError(s.T(), err)
|
||||||
@ -108,7 +109,7 @@ func (s *CopyrightQueriesSuite) TestGetCopyrightsForWork_Success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CopyrightQueriesSuite) TestGetCopyrightsForWork_RepoError() {
|
func (s *CopyrightQueriesSuite) TestGetCopyrightsForWork_RepoError() {
|
||||||
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
return nil, errors.New("db error")
|
return nil, errors.New("db error")
|
||||||
}
|
}
|
||||||
c, err := s.queries.GetCopyrightsForWork(context.Background(), 1)
|
c, err := s.queries.GetCopyrightsForWork(context.Background(), 1)
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package monetization
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockMonetizationRepository struct {
|
type mockMonetizationRepository struct {
|
||||||
@ -97,10 +98,11 @@ func (m *mockMonetizationRepository) RemoveMonetizationFromSource(ctx context.Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockWorkRepository struct {
|
type mockWorkRepository struct {
|
||||||
domain.WorkRepository
|
work.WorkRepository
|
||||||
getByIDWithOptionsFunc func(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error)
|
getByIDWithOptionsFunc func(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error)
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
|
||||||
|
func (m *mockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
if m.getByIDWithOptionsFunc != nil {
|
if m.getByIDWithOptionsFunc != nil {
|
||||||
return m.getByIDWithOptionsFunc(ctx, id, options)
|
return m.getByIDWithOptionsFunc(ctx, id, options)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/platform/log"
|
"tercul/internal/platform/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MonetizationQueries contains the query handlers for monetization.
|
// MonetizationQueries contains the query handlers for monetization.
|
||||||
type MonetizationQueries struct {
|
type MonetizationQueries struct {
|
||||||
repo domain.MonetizationRepository
|
repo domain.MonetizationRepository
|
||||||
workRepo domain.WorkRepository
|
workRepo work.WorkRepository
|
||||||
authorRepo domain.AuthorRepository
|
authorRepo domain.AuthorRepository
|
||||||
bookRepo domain.BookRepository
|
bookRepo domain.BookRepository
|
||||||
publisherRepo domain.PublisherRepository
|
publisherRepo domain.PublisherRepository
|
||||||
@ -18,7 +19,7 @@ type MonetizationQueries struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewMonetizationQueries creates a new MonetizationQueries handler.
|
// NewMonetizationQueries creates a new MonetizationQueries handler.
|
||||||
func NewMonetizationQueries(repo domain.MonetizationRepository, workRepo domain.WorkRepository, authorRepo domain.AuthorRepository, bookRepo domain.BookRepository, publisherRepo domain.PublisherRepository, sourceRepo domain.SourceRepository) *MonetizationQueries {
|
func NewMonetizationQueries(repo domain.MonetizationRepository, workRepo work.WorkRepository, authorRepo domain.AuthorRepository, bookRepo domain.BookRepository, publisherRepo domain.PublisherRepository, sourceRepo domain.SourceRepository) *MonetizationQueries {
|
||||||
return &MonetizationQueries{repo: repo, workRepo: workRepo, authorRepo: authorRepo, bookRepo: bookRepo, publisherRepo: publisherRepo, sourceRepo: sourceRepo}
|
return &MonetizationQueries{repo: repo, workRepo: workRepo, authorRepo: authorRepo, bookRepo: bookRepo, publisherRepo: publisherRepo, sourceRepo: sourceRepo}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,11 +40,11 @@ func (q *MonetizationQueries) ListMonetizations(ctx context.Context) ([]domain.M
|
|||||||
|
|
||||||
func (q *MonetizationQueries) GetMonetizationsForWork(ctx context.Context, workID uint) ([]*domain.Monetization, error) {
|
func (q *MonetizationQueries) GetMonetizationsForWork(ctx context.Context, workID uint) ([]*domain.Monetization, error) {
|
||||||
log.LogDebug("Getting monetizations for work", log.F("work_id", workID))
|
log.LogDebug("Getting monetizations for work", log.F("work_id", workID))
|
||||||
work, err := q.workRepo.GetByIDWithOptions(ctx, workID, &domain.QueryOptions{Preloads: []string{"Monetizations"}})
|
workRecord, err := q.workRepo.GetByIDWithOptions(ctx, workID, &domain.QueryOptions{Preloads: []string{"Monetizations"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return work.Monetizations, nil
|
return workRecord.Monetizations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *MonetizationQueries) GetMonetizationsForAuthor(ctx context.Context, authorID uint) ([]*domain.Monetization, error) {
|
func (q *MonetizationQueries) GetMonetizationsForAuthor(ctx context.Context, authorID uint) ([]*domain.Monetization, error) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,8 +82,8 @@ func (s *MonetizationQueriesSuite) TestListMonetizations_Success() {
|
|||||||
|
|
||||||
func (s *MonetizationQueriesSuite) TestGetMonetizationsForWork_Success() {
|
func (s *MonetizationQueriesSuite) TestGetMonetizationsForWork_Success() {
|
||||||
monetizations := []*domain.Monetization{{Amount: 10.0}}
|
monetizations := []*domain.Monetization{{Amount: 10.0}}
|
||||||
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
return &domain.Work{Monetizations: monetizations}, nil
|
return &work.Work{Monetizations: monetizations}, nil
|
||||||
}
|
}
|
||||||
m, err := s.queries.GetMonetizationsForWork(context.Background(), 1)
|
m, err := s.queries.GetMonetizationsForWork(context.Background(), 1)
|
||||||
assert.NoError(s.T(), err)
|
assert.NoError(s.T(), err)
|
||||||
@ -90,7 +91,7 @@ func (s *MonetizationQueriesSuite) TestGetMonetizationsForWork_Success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MonetizationQueriesSuite) TestGetMonetizationsForWork_RepoError() {
|
func (s *MonetizationQueriesSuite) TestGetMonetizationsForWork_RepoError() {
|
||||||
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
s.workRepo.getByIDWithOptionsFunc = func(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
return nil, errors.New("db error")
|
return nil, errors.New("db error")
|
||||||
}
|
}
|
||||||
m, err := s.queries.GetMonetizationsForWork(context.Background(), 1)
|
m, err := s.queries.GetMonetizationsForWork(context.Background(), 1)
|
||||||
|
|||||||
@ -4,14 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"tercul/internal/app/localization"
|
"tercul/internal/app/localization"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/platform/log"
|
"tercul/internal/platform/log"
|
||||||
"tercul/internal/platform/search"
|
"tercul/internal/platform/search"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IndexService pushes localized snapshots into Weaviate for search
|
// IndexService pushes localized snapshots into Weaviate for search
|
||||||
type IndexService interface {
|
type IndexService interface {
|
||||||
IndexWork(ctx context.Context, work domain.Work) error
|
IndexWork(ctx context.Context, work work.Work) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type indexService struct {
|
type indexService struct {
|
||||||
@ -23,7 +23,7 @@ func NewIndexService(localization *localization.Service, weaviate search.Weaviat
|
|||||||
return &indexService{localization: localization, weaviate: weaviate}
|
return &indexService{localization: localization, weaviate: weaviate}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *indexService) IndexWork(ctx context.Context, work domain.Work) error {
|
func (s *indexService) IndexWork(ctx context.Context, work work.Work) error {
|
||||||
log.LogDebug("Indexing work", log.F("work_id", work.ID))
|
log.LogDebug("Indexing work", log.F("work_id", work.ID))
|
||||||
// TODO: Get content from translation service
|
// TODO: Get content from translation service
|
||||||
content := ""
|
content := ""
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"tercul/internal/app/localization"
|
"tercul/internal/app/localization"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockLocalizationRepository struct {
|
type mockLocalizationRepository struct {
|
||||||
@ -36,7 +37,7 @@ type mockWeaviateWrapper struct {
|
|||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockWeaviateWrapper) IndexWork(ctx context.Context, work *domain.Work, content string) error {
|
func (m *mockWeaviateWrapper) IndexWork(ctx context.Context, work *work.Work, content string) error {
|
||||||
args := m.Called(ctx, work, content)
|
args := m.Called(ctx, work, content)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
@ -48,7 +49,7 @@ func TestIndexService_IndexWork(t *testing.T) {
|
|||||||
service := NewIndexService(localizationService, weaviateWrapper)
|
service := NewIndexService(localizationService, weaviateWrapper)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
work := domain.Work{
|
work := work.Work{
|
||||||
TranslatableModel: domain.TranslatableModel{
|
TranslatableModel: domain.TranslatableModel{
|
||||||
BaseModel: domain.BaseModel{ID: 1},
|
BaseModel: domain.BaseModel{ID: 1},
|
||||||
Language: "en",
|
Language: "en",
|
||||||
|
|||||||
@ -3,18 +3,18 @@ package work
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"tercul/internal/domain"
|
|
||||||
"tercul/internal/domain/search"
|
"tercul/internal/domain/search"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WorkCommands contains the command handlers for the work aggregate.
|
// WorkCommands contains the command handlers for the work aggregate.
|
||||||
type WorkCommands struct {
|
type WorkCommands struct {
|
||||||
repo domain.WorkRepository
|
repo work.WorkRepository
|
||||||
searchClient search.SearchClient
|
searchClient search.SearchClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorkCommands creates a new WorkCommands handler.
|
// NewWorkCommands creates a new WorkCommands handler.
|
||||||
func NewWorkCommands(repo domain.WorkRepository, searchClient search.SearchClient) *WorkCommands {
|
func NewWorkCommands(repo work.WorkRepository, searchClient search.SearchClient) *WorkCommands {
|
||||||
return &WorkCommands{
|
return &WorkCommands{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
searchClient: searchClient,
|
searchClient: searchClient,
|
||||||
@ -22,7 +22,7 @@ func NewWorkCommands(repo domain.WorkRepository, searchClient search.SearchClien
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateWork creates a new work.
|
// CreateWork creates a new work.
|
||||||
func (c *WorkCommands) CreateWork(ctx context.Context, work *domain.Work) (*domain.Work, error) {
|
func (c *WorkCommands) CreateWork(ctx context.Context, work *work.Work) (*work.Work, error) {
|
||||||
if work == nil {
|
if work == nil {
|
||||||
return nil, errors.New("work cannot be nil")
|
return nil, errors.New("work cannot be nil")
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ func (c *WorkCommands) CreateWork(ctx context.Context, work *domain.Work) (*doma
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateWork updates an existing work.
|
// UpdateWork updates an existing work.
|
||||||
func (c *WorkCommands) UpdateWork(ctx context.Context, work *domain.Work) error {
|
func (c *WorkCommands) UpdateWork(ctx context.Context, work *work.Work) error {
|
||||||
if work == nil {
|
if work == nil {
|
||||||
return errors.New("work cannot be nil")
|
return errors.New("work cannot be nil")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
workdomain "tercul/internal/domain/work"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ func TestWorkCommandsSuite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestCreateWork_Success() {
|
func (s *WorkCommandsSuite) TestCreateWork_Success() {
|
||||||
work := &domain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
_, err := s.commands.CreateWork(context.Background(), work)
|
_, err := s.commands.CreateWork(context.Background(), work)
|
||||||
assert.NoError(s.T(), err)
|
assert.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
@ -38,20 +39,20 @@ func (s *WorkCommandsSuite) TestCreateWork_Nil() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestCreateWork_EmptyTitle() {
|
func (s *WorkCommandsSuite) TestCreateWork_EmptyTitle() {
|
||||||
work := &domain.Work{TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
_, err := s.commands.CreateWork(context.Background(), work)
|
_, err := s.commands.CreateWork(context.Background(), work)
|
||||||
assert.Error(s.T(), err)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestCreateWork_EmptyLanguage() {
|
func (s *WorkCommandsSuite) TestCreateWork_EmptyLanguage() {
|
||||||
work := &domain.Work{Title: "Test Work"}
|
work := &workdomain.Work{Title: "Test Work"}
|
||||||
_, err := s.commands.CreateWork(context.Background(), work)
|
_, err := s.commands.CreateWork(context.Background(), work)
|
||||||
assert.Error(s.T(), err)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestCreateWork_RepoError() {
|
func (s *WorkCommandsSuite) TestCreateWork_RepoError() {
|
||||||
work := &domain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
s.repo.createFunc = func(ctx context.Context, w *domain.Work) error {
|
s.repo.createFunc = func(ctx context.Context, w *workdomain.Work) error {
|
||||||
return errors.New("db error")
|
return errors.New("db error")
|
||||||
}
|
}
|
||||||
_, err := s.commands.CreateWork(context.Background(), work)
|
_, err := s.commands.CreateWork(context.Background(), work)
|
||||||
@ -59,7 +60,7 @@ func (s *WorkCommandsSuite) TestCreateWork_RepoError() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestUpdateWork_Success() {
|
func (s *WorkCommandsSuite) TestUpdateWork_Success() {
|
||||||
work := &domain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
work.ID = 1
|
work.ID = 1
|
||||||
err := s.commands.UpdateWork(context.Background(), work)
|
err := s.commands.UpdateWork(context.Background(), work)
|
||||||
assert.NoError(s.T(), err)
|
assert.NoError(s.T(), err)
|
||||||
@ -71,29 +72,29 @@ func (s *WorkCommandsSuite) TestUpdateWork_Nil() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestUpdateWork_ZeroID() {
|
func (s *WorkCommandsSuite) TestUpdateWork_ZeroID() {
|
||||||
work := &domain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
err := s.commands.UpdateWork(context.Background(), work)
|
err := s.commands.UpdateWork(context.Background(), work)
|
||||||
assert.Error(s.T(), err)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestUpdateWork_EmptyTitle() {
|
func (s *WorkCommandsSuite) TestUpdateWork_EmptyTitle() {
|
||||||
work := &domain.Work{TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
work.ID = 1
|
work.ID = 1
|
||||||
err := s.commands.UpdateWork(context.Background(), work)
|
err := s.commands.UpdateWork(context.Background(), work)
|
||||||
assert.Error(s.T(), err)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestUpdateWork_EmptyLanguage() {
|
func (s *WorkCommandsSuite) TestUpdateWork_EmptyLanguage() {
|
||||||
work := &domain.Work{Title: "Test Work"}
|
work := &workdomain.Work{Title: "Test Work"}
|
||||||
work.ID = 1
|
work.ID = 1
|
||||||
err := s.commands.UpdateWork(context.Background(), work)
|
err := s.commands.UpdateWork(context.Background(), work)
|
||||||
assert.Error(s.T(), err)
|
assert.Error(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkCommandsSuite) TestUpdateWork_RepoError() {
|
func (s *WorkCommandsSuite) TestUpdateWork_RepoError() {
|
||||||
work := &domain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
work := &workdomain.Work{Title: "Test Work", TranslatableModel: domain.TranslatableModel{Language: "en"}}
|
||||||
work.ID = 1
|
work.ID = 1
|
||||||
s.repo.updateFunc = func(ctx context.Context, w *domain.Work) error {
|
s.repo.updateFunc = func(ctx context.Context, w *workdomain.Work) error {
|
||||||
return errors.New("db error")
|
return errors.New("db error")
|
||||||
}
|
}
|
||||||
err := s.commands.UpdateWork(context.Background(), work)
|
err := s.commands.UpdateWork(context.Background(), work)
|
||||||
|
|||||||
@ -3,29 +3,30 @@ package work
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockWorkRepository struct {
|
type mockWorkRepository struct {
|
||||||
domain.WorkRepository
|
work.WorkRepository
|
||||||
createFunc func(ctx context.Context, work *domain.Work) error
|
createFunc func(ctx context.Context, work *work.Work) error
|
||||||
updateFunc func(ctx context.Context, work *domain.Work) error
|
updateFunc func(ctx context.Context, work *work.Work) error
|
||||||
deleteFunc func(ctx context.Context, id uint) error
|
deleteFunc func(ctx context.Context, id uint) error
|
||||||
getByIDFunc func(ctx context.Context, id uint) (*domain.Work, error)
|
getByIDFunc func(ctx context.Context, id uint) (*work.Work, error)
|
||||||
listFunc func(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
|
listFunc func(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error)
|
||||||
getWithTranslationsFunc func(ctx context.Context, id uint) (*domain.Work, error)
|
getWithTranslationsFunc func(ctx context.Context, id uint) (*work.Work, error)
|
||||||
findByTitleFunc func(ctx context.Context, title string) ([]domain.Work, error)
|
findByTitleFunc func(ctx context.Context, title string) ([]work.Work, error)
|
||||||
findByAuthorFunc func(ctx context.Context, authorID uint) ([]domain.Work, error)
|
findByAuthorFunc func(ctx context.Context, authorID uint) ([]work.Work, error)
|
||||||
findByCategoryFunc func(ctx context.Context, categoryID uint) ([]domain.Work, error)
|
findByCategoryFunc func(ctx context.Context, categoryID uint) ([]work.Work, error)
|
||||||
findByLanguageFunc func(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
|
findByLanguageFunc func(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockWorkRepository) Create(ctx context.Context, work *domain.Work) error {
|
func (m *mockWorkRepository) Create(ctx context.Context, work *work.Work) error {
|
||||||
if m.createFunc != nil {
|
if m.createFunc != nil {
|
||||||
return m.createFunc(ctx, work)
|
return m.createFunc(ctx, work)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) Update(ctx context.Context, work *domain.Work) error {
|
func (m *mockWorkRepository) Update(ctx context.Context, work *work.Work) error {
|
||||||
if m.updateFunc != nil {
|
if m.updateFunc != nil {
|
||||||
return m.updateFunc(ctx, work)
|
return m.updateFunc(ctx, work)
|
||||||
}
|
}
|
||||||
@ -37,43 +38,43 @@ func (m *mockWorkRepository) Delete(ctx context.Context, id uint) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) GetByID(ctx context.Context, id uint) (*domain.Work, error) {
|
func (m *mockWorkRepository) GetByID(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
if m.getByIDFunc != nil {
|
if m.getByIDFunc != nil {
|
||||||
return m.getByIDFunc(ctx, id)
|
return m.getByIDFunc(ctx, id)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (m *mockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
if m.listFunc != nil {
|
if m.listFunc != nil {
|
||||||
return m.listFunc(ctx, page, pageSize)
|
return m.listFunc(ctx, page, pageSize)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) {
|
func (m *mockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
if m.getWithTranslationsFunc != nil {
|
if m.getWithTranslationsFunc != nil {
|
||||||
return m.getWithTranslationsFunc(ctx, id)
|
return m.getWithTranslationsFunc(ctx, id)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) FindByTitle(ctx context.Context, title string) ([]domain.Work, error) {
|
func (m *mockWorkRepository) FindByTitle(ctx context.Context, title string) ([]work.Work, error) {
|
||||||
if m.findByTitleFunc != nil {
|
if m.findByTitleFunc != nil {
|
||||||
return m.findByTitleFunc(ctx, title)
|
return m.findByTitleFunc(ctx, title)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) {
|
func (m *mockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]work.Work, error) {
|
||||||
if m.findByAuthorFunc != nil {
|
if m.findByAuthorFunc != nil {
|
||||||
return m.findByAuthorFunc(ctx, authorID)
|
return m.findByAuthorFunc(ctx, authorID)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) {
|
func (m *mockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]work.Work, error) {
|
||||||
if m.findByCategoryFunc != nil {
|
if m.findByCategoryFunc != nil {
|
||||||
return m.findByCategoryFunc(ctx, categoryID)
|
return m.findByCategoryFunc(ctx, categoryID)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (m *mockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (m *mockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
if m.findByLanguageFunc != nil {
|
if m.findByLanguageFunc != nil {
|
||||||
return m.findByLanguageFunc(ctx, language, page, pageSize)
|
return m.findByLanguageFunc(ctx, language, page, pageSize)
|
||||||
}
|
}
|
||||||
@ -81,10 +82,10 @@ func (m *mockWorkRepository) FindByLanguage(ctx context.Context, language string
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockSearchClient struct {
|
type mockSearchClient struct {
|
||||||
indexWorkFunc func(ctx context.Context, work *domain.Work, pipeline string) error
|
indexWorkFunc func(ctx context.Context, work *work.Work, pipeline string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockSearchClient) IndexWork(ctx context.Context, work *domain.Work, pipeline string) error {
|
func (m *mockSearchClient) IndexWork(ctx context.Context, work *work.Work, pipeline string) error {
|
||||||
if m.indexWorkFunc != nil {
|
if m.indexWorkFunc != nil {
|
||||||
return m.indexWorkFunc(ctx, work, pipeline)
|
return m.indexWorkFunc(ctx, work, pipeline)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WorkAnalytics contains analytics data for a work
|
// WorkAnalytics contains analytics data for a work
|
||||||
@ -30,18 +31,18 @@ type TranslationAnalytics struct {
|
|||||||
|
|
||||||
// WorkQueries contains the query handlers for the work aggregate.
|
// WorkQueries contains the query handlers for the work aggregate.
|
||||||
type WorkQueries struct {
|
type WorkQueries struct {
|
||||||
repo domain.WorkRepository
|
repo work.WorkRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorkQueries creates a new WorkQueries handler.
|
// NewWorkQueries creates a new WorkQueries handler.
|
||||||
func NewWorkQueries(repo domain.WorkRepository) *WorkQueries {
|
func NewWorkQueries(repo work.WorkRepository) *WorkQueries {
|
||||||
return &WorkQueries{
|
return &WorkQueries{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWorkByID retrieves a work by ID.
|
// GetWorkByID retrieves a work by ID.
|
||||||
func (q *WorkQueries) GetWorkByID(ctx context.Context, id uint) (*domain.Work, error) {
|
func (q *WorkQueries) GetWorkByID(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
return nil, errors.New("invalid work ID")
|
return nil, errors.New("invalid work ID")
|
||||||
}
|
}
|
||||||
@ -49,12 +50,12 @@ func (q *WorkQueries) GetWorkByID(ctx context.Context, id uint) (*domain.Work, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListWorks returns a paginated list of works.
|
// ListWorks returns a paginated list of works.
|
||||||
func (q *WorkQueries) ListWorks(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (q *WorkQueries) ListWorks(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
return q.repo.List(ctx, page, pageSize)
|
return q.repo.List(ctx, page, pageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWorkWithTranslations retrieves a work with its translations.
|
// GetWorkWithTranslations retrieves a work with its translations.
|
||||||
func (q *WorkQueries) GetWorkWithTranslations(ctx context.Context, id uint) (*domain.Work, error) {
|
func (q *WorkQueries) GetWorkWithTranslations(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
return nil, errors.New("invalid work ID")
|
return nil, errors.New("invalid work ID")
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ func (q *WorkQueries) GetWorkWithTranslations(ctx context.Context, id uint) (*do
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindWorksByTitle finds works by title.
|
// FindWorksByTitle finds works by title.
|
||||||
func (q *WorkQueries) FindWorksByTitle(ctx context.Context, title string) ([]domain.Work, error) {
|
func (q *WorkQueries) FindWorksByTitle(ctx context.Context, title string) ([]work.Work, error) {
|
||||||
if title == "" {
|
if title == "" {
|
||||||
return nil, errors.New("title cannot be empty")
|
return nil, errors.New("title cannot be empty")
|
||||||
}
|
}
|
||||||
@ -70,7 +71,7 @@ func (q *WorkQueries) FindWorksByTitle(ctx context.Context, title string) ([]dom
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindWorksByAuthor finds works by author ID.
|
// FindWorksByAuthor finds works by author ID.
|
||||||
func (q *WorkQueries) FindWorksByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) {
|
func (q *WorkQueries) FindWorksByAuthor(ctx context.Context, authorID uint) ([]work.Work, error) {
|
||||||
if authorID == 0 {
|
if authorID == 0 {
|
||||||
return nil, errors.New("invalid author ID")
|
return nil, errors.New("invalid author ID")
|
||||||
}
|
}
|
||||||
@ -78,7 +79,7 @@ func (q *WorkQueries) FindWorksByAuthor(ctx context.Context, authorID uint) ([]d
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindWorksByCategory finds works by category ID.
|
// FindWorksByCategory finds works by category ID.
|
||||||
func (q *WorkQueries) FindWorksByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) {
|
func (q *WorkQueries) FindWorksByCategory(ctx context.Context, categoryID uint) ([]work.Work, error) {
|
||||||
if categoryID == 0 {
|
if categoryID == 0 {
|
||||||
return nil, errors.New("invalid category ID")
|
return nil, errors.New("invalid category ID")
|
||||||
}
|
}
|
||||||
@ -86,7 +87,7 @@ func (q *WorkQueries) FindWorksByCategory(ctx context.Context, categoryID uint)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindWorksByLanguage finds works by language.
|
// FindWorksByLanguage finds works by language.
|
||||||
func (q *WorkQueries) FindWorksByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (q *WorkQueries) FindWorksByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
if language == "" {
|
if language == "" {
|
||||||
return nil, errors.New("language cannot be empty")
|
return nil, errors.New("language cannot be empty")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
workdomain "tercul/internal/domain/work"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,9 +25,9 @@ func TestWorkQueriesSuite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestGetWorkByID_Success() {
|
func (s *WorkQueriesSuite) TestGetWorkByID_Success() {
|
||||||
work := &domain.Work{Title: "Test Work"}
|
work := &workdomain.Work{Title: "Test Work"}
|
||||||
work.ID = 1
|
work.ID = 1
|
||||||
s.repo.getByIDFunc = func(ctx context.Context, id uint) (*domain.Work, error) {
|
s.repo.getByIDFunc = func(ctx context.Context, id uint) (*workdomain.Work, error) {
|
||||||
return work, nil
|
return work, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.GetWorkByID(context.Background(), 1)
|
w, err := s.queries.GetWorkByID(context.Background(), 1)
|
||||||
@ -41,8 +42,8 @@ func (s *WorkQueriesSuite) TestGetWorkByID_ZeroID() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestListWorks_Success() {
|
func (s *WorkQueriesSuite) TestListWorks_Success() {
|
||||||
works := &domain.PaginatedResult[domain.Work]{}
|
works := &domain.PaginatedResult[workdomain.Work]{}
|
||||||
s.repo.listFunc = func(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
s.repo.listFunc = func(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[workdomain.Work], error) {
|
||||||
return works, nil
|
return works, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.ListWorks(context.Background(), 1, 10)
|
w, err := s.queries.ListWorks(context.Background(), 1, 10)
|
||||||
@ -51,9 +52,9 @@ func (s *WorkQueriesSuite) TestListWorks_Success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestGetWorkWithTranslations_Success() {
|
func (s *WorkQueriesSuite) TestGetWorkWithTranslations_Success() {
|
||||||
work := &domain.Work{Title: "Test Work"}
|
work := &workdomain.Work{Title: "Test Work"}
|
||||||
work.ID = 1
|
work.ID = 1
|
||||||
s.repo.getWithTranslationsFunc = func(ctx context.Context, id uint) (*domain.Work, error) {
|
s.repo.getWithTranslationsFunc = func(ctx context.Context, id uint) (*workdomain.Work, error) {
|
||||||
return work, nil
|
return work, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.GetWorkWithTranslations(context.Background(), 1)
|
w, err := s.queries.GetWorkWithTranslations(context.Background(), 1)
|
||||||
@ -68,8 +69,8 @@ func (s *WorkQueriesSuite) TestGetWorkWithTranslations_ZeroID() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestFindWorksByTitle_Success() {
|
func (s *WorkQueriesSuite) TestFindWorksByTitle_Success() {
|
||||||
works := []domain.Work{{Title: "Test Work"}}
|
works := []workdomain.Work{{Title: "Test Work"}}
|
||||||
s.repo.findByTitleFunc = func(ctx context.Context, title string) ([]domain.Work, error) {
|
s.repo.findByTitleFunc = func(ctx context.Context, title string) ([]workdomain.Work, error) {
|
||||||
return works, nil
|
return works, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.FindWorksByTitle(context.Background(), "Test")
|
w, err := s.queries.FindWorksByTitle(context.Background(), "Test")
|
||||||
@ -84,8 +85,8 @@ func (s *WorkQueriesSuite) TestFindWorksByTitle_Empty() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestFindWorksByAuthor_Success() {
|
func (s *WorkQueriesSuite) TestFindWorksByAuthor_Success() {
|
||||||
works := []domain.Work{{Title: "Test Work"}}
|
works := []workdomain.Work{{Title: "Test Work"}}
|
||||||
s.repo.findByAuthorFunc = func(ctx context.Context, authorID uint) ([]domain.Work, error) {
|
s.repo.findByAuthorFunc = func(ctx context.Context, authorID uint) ([]workdomain.Work, error) {
|
||||||
return works, nil
|
return works, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.FindWorksByAuthor(context.Background(), 1)
|
w, err := s.queries.FindWorksByAuthor(context.Background(), 1)
|
||||||
@ -100,8 +101,8 @@ func (s *WorkQueriesSuite) TestFindWorksByAuthor_ZeroID() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestFindWorksByCategory_Success() {
|
func (s *WorkQueriesSuite) TestFindWorksByCategory_Success() {
|
||||||
works := []domain.Work{{Title: "Test Work"}}
|
works := []workdomain.Work{{Title: "Test Work"}}
|
||||||
s.repo.findByCategoryFunc = func(ctx context.Context, categoryID uint) ([]domain.Work, error) {
|
s.repo.findByCategoryFunc = func(ctx context.Context, categoryID uint) ([]workdomain.Work, error) {
|
||||||
return works, nil
|
return works, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.FindWorksByCategory(context.Background(), 1)
|
w, err := s.queries.FindWorksByCategory(context.Background(), 1)
|
||||||
@ -116,8 +117,8 @@ func (s *WorkQueriesSuite) TestFindWorksByCategory_ZeroID() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkQueriesSuite) TestFindWorksByLanguage_Success() {
|
func (s *WorkQueriesSuite) TestFindWorksByLanguage_Success() {
|
||||||
works := &domain.PaginatedResult[domain.Work]{}
|
works := &domain.PaginatedResult[workdomain.Work]{}
|
||||||
s.repo.findByLanguageFunc = func(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
s.repo.findByLanguageFunc = func(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[workdomain.Work], error) {
|
||||||
return works, nil
|
return works, nil
|
||||||
}
|
}
|
||||||
w, err := s.queries.FindWorksByLanguage(context.Background(), "en", 1, 10)
|
w, err := s.queries.FindWorksByLanguage(context.Background(), "en", 1, 10)
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
package work
|
package work
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"tercul/internal/domain"
|
|
||||||
"tercul/internal/domain/search"
|
"tercul/internal/domain/search"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service is the application service for the work aggregate.
|
// Service is the application service for the work aggregate.
|
||||||
@ -12,7 +12,7 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewService creates a new work Service.
|
// NewService creates a new work Service.
|
||||||
func NewService(repo domain.WorkRepository, searchClient search.SearchClient) *Service {
|
func NewService(repo work.WorkRepository, searchClient search.SearchClient) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
Commands: NewWorkCommands(repo, searchClient),
|
Commands: NewWorkCommands(repo, searchClient),
|
||||||
Queries: NewWorkQueries(repo),
|
Queries: NewWorkQueries(repo),
|
||||||
|
|||||||
@ -3,7 +3,9 @@ package sql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"tercul/internal/app/analytics"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -13,7 +15,7 @@ type analyticsRepository struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAnalyticsRepository(db *gorm.DB) domain.AnalyticsRepository {
|
func NewAnalyticsRepository(db *gorm.DB) analytics.Repository {
|
||||||
return &analyticsRepository{db: db}
|
return &analyticsRepository{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ func (r *analyticsRepository) IncrementWorkCounter(ctx context.Context, workID u
|
|||||||
// Using a transaction to ensure atomicity
|
// Using a transaction to ensure atomicity
|
||||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
// First, try to update the existing record
|
// First, try to update the existing record
|
||||||
result := tx.Model(&domain.WorkStats{}).Where("work_id = ?", workID).UpdateColumn(field, gorm.Expr(fmt.Sprintf("%s + ?", field), value))
|
result := tx.Model(&work.WorkStats{}).Where("work_id = ?", workID).UpdateColumn(field, gorm.Expr(fmt.Sprintf("%s + ?", field), value))
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return result.Error
|
return result.Error
|
||||||
}
|
}
|
||||||
@ -49,14 +51,14 @@ func (r *analyticsRepository) IncrementWorkCounter(ctx context.Context, workID u
|
|||||||
// If no rows were affected, the record does not exist, so create it
|
// If no rows were affected, the record does not exist, so create it
|
||||||
if result.RowsAffected == 0 {
|
if result.RowsAffected == 0 {
|
||||||
initialData := map[string]interface{}{"work_id": workID, field: value}
|
initialData := map[string]interface{}{"work_id": workID, field: value}
|
||||||
return tx.Model(&domain.WorkStats{}).Create(initialData).Error
|
return tx.Model(&work.WorkStats{}).Create(initialData).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *analyticsRepository) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*domain.Work, error) {
|
func (r *analyticsRepository) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*work.Work, error) {
|
||||||
var trendingWorks []*domain.Trending
|
var trendingWorks []*domain.Trending
|
||||||
err := r.db.WithContext(ctx).
|
err := r.db.WithContext(ctx).
|
||||||
Where("entity_type = ? AND time_period = ?", "Work", timePeriod).
|
Where("entity_type = ? AND time_period = ?", "Work", timePeriod).
|
||||||
@ -68,7 +70,7 @@ func (r *analyticsRepository) GetTrendingWorks(ctx context.Context, timePeriod s
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(trendingWorks) == 0 {
|
if len(trendingWorks) == 0 {
|
||||||
return []*domain.Work{}, nil
|
return []*work.Work{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
workIDs := make([]uint, len(trendingWorks))
|
workIDs := make([]uint, len(trendingWorks))
|
||||||
@ -76,22 +78,22 @@ func (r *analyticsRepository) GetTrendingWorks(ctx context.Context, timePeriod s
|
|||||||
workIDs[i] = tw.EntityID
|
workIDs[i] = tw.EntityID
|
||||||
}
|
}
|
||||||
|
|
||||||
var works []*domain.Work
|
var works []*work.Work
|
||||||
err = r.db.WithContext(ctx).
|
err = r.db.WithContext(ctx).
|
||||||
Where("id IN ?", workIDs).
|
Where("id IN ?", workIDs).
|
||||||
Find(&works).Error
|
Find(&works).Error
|
||||||
|
|
||||||
// This part is tricky because the order from the IN clause is not guaranteed.
|
// This part is tricky because the order from the IN clause is not guaranteed.
|
||||||
// We need to re-order the works based on the trending rank.
|
// We need to re-order the works based on the trending rank.
|
||||||
workMap := make(map[uint]*domain.Work)
|
workMap := make(map[uint]*work.Work)
|
||||||
for _, work := range works {
|
for _, w := range works {
|
||||||
workMap[work.ID] = work
|
workMap[w.ID] = w
|
||||||
}
|
}
|
||||||
|
|
||||||
orderedWorks := make([]*domain.Work, len(workIDs))
|
orderedWorks := make([]*work.Work, len(workIDs))
|
||||||
for i, id := range workIDs {
|
for i, id := range workIDs {
|
||||||
if work, ok := workMap[id]; ok {
|
if w, ok := workMap[id]; ok {
|
||||||
orderedWorks[i] = work
|
orderedWorks[i] = w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,17 +120,17 @@ func (r *analyticsRepository) IncrementTranslationCounter(ctx context.Context, t
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *analyticsRepository) UpdateWorkStats(ctx context.Context, workID uint, stats domain.WorkStats) error {
|
func (r *analyticsRepository) UpdateWorkStats(ctx context.Context, workID uint, stats work.WorkStats) error {
|
||||||
return r.db.WithContext(ctx).Model(&domain.WorkStats{}).Where("work_id = ?", workID).Updates(stats).Error
|
return r.db.WithContext(ctx).Model(&work.WorkStats{}).Where("work_id = ?", workID).Updates(stats).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *analyticsRepository) UpdateTranslationStats(ctx context.Context, translationID uint, stats domain.TranslationStats) error {
|
func (r *analyticsRepository) UpdateTranslationStats(ctx context.Context, translationID uint, stats domain.TranslationStats) error {
|
||||||
return r.db.WithContext(ctx).Model(&domain.TranslationStats{}).Where("translation_id = ?", translationID).Updates(stats).Error
|
return r.db.WithContext(ctx).Model(&domain.TranslationStats{}).Where("translation_id = ?", translationID).Updates(stats).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *analyticsRepository) GetOrCreateWorkStats(ctx context.Context, workID uint) (*domain.WorkStats, error) {
|
func (r *analyticsRepository) GetOrCreateWorkStats(ctx context.Context, workID uint) (*work.WorkStats, error) {
|
||||||
var stats domain.WorkStats
|
var stats work.WorkStats
|
||||||
err := r.db.WithContext(ctx).Where(domain.WorkStats{WorkID: workID}).FirstOrCreate(&stats).Error
|
err := r.db.WithContext(ctx).Where(work.WorkStats{WorkID: workID}).FirstOrCreate(&stats).Error
|
||||||
return &stats, err
|
return &stats, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package sql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -21,15 +22,15 @@ func NewMonetizationRepository(db *gorm.DB) domain.MonetizationRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *monetizationRepository) AddMonetizationToWork(ctx context.Context, workID uint, monetizationID uint) error {
|
func (r *monetizationRepository) AddMonetizationToWork(ctx context.Context, workID uint, monetizationID uint) error {
|
||||||
work := &domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}}
|
workRecord := &work.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}}
|
||||||
monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}}
|
monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}}
|
||||||
return r.db.WithContext(ctx).Model(work).Association("Monetizations").Append(monetization)
|
return r.db.WithContext(ctx).Model(workRecord).Association("Monetizations").Append(monetization)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *monetizationRepository) RemoveMonetizationFromWork(ctx context.Context, workID uint, monetizationID uint) error {
|
func (r *monetizationRepository) RemoveMonetizationFromWork(ctx context.Context, workID uint, monetizationID uint) error {
|
||||||
work := &domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}}
|
workRecord := &work.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}}
|
||||||
monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}}
|
monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}}
|
||||||
return r.db.WithContext(ctx).Model(work).Association("Monetizations").Delete(monetization)
|
return r.db.WithContext(ctx).Model(workRecord).Association("Monetizations").Delete(monetization)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *monetizationRepository) AddMonetizationToAuthor(ctx context.Context, authorID uint, monetizationID uint) error {
|
func (r *monetizationRepository) AddMonetizationToAuthor(ctx context.Context, authorID uint, monetizationID uint) error {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"tercul/internal/data/sql"
|
"tercul/internal/data/sql"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
workdomain "tercul/internal/domain/work"
|
||||||
"tercul/internal/testutil"
|
"tercul/internal/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
@ -40,7 +41,7 @@ func (s *MonetizationRepositoryTestSuite) TestAddMonetizationToWork() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Verify that the association was created in the database
|
// Verify that the association was created in the database
|
||||||
var foundWork domain.Work
|
var foundWork workdomain.Work
|
||||||
err = s.DB.Preload("Monetizations").First(&foundWork, work.ID).Error
|
err = s.DB.Preload("Monetizations").First(&foundWork, work.ID).Error
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Len(foundWork.Monetizations, 1)
|
s.Require().Len(foundWork.Monetizations, 1)
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
package sql
|
package sql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"tercul/internal/app/analytics"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
"tercul/internal/domain/auth"
|
"tercul/internal/domain/auth"
|
||||||
"tercul/internal/domain/localization"
|
"tercul/internal/domain/localization"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Repositories struct {
|
type Repositories struct {
|
||||||
Work domain.WorkRepository
|
Work work.WorkRepository
|
||||||
User domain.UserRepository
|
User domain.UserRepository
|
||||||
Author domain.AuthorRepository
|
Author domain.AuthorRepository
|
||||||
Translation domain.TranslationRepository
|
Translation domain.TranslationRepository
|
||||||
@ -24,7 +26,7 @@ type Repositories struct {
|
|||||||
Source domain.SourceRepository
|
Source domain.SourceRepository
|
||||||
Copyright domain.CopyrightRepository
|
Copyright domain.CopyrightRepository
|
||||||
Monetization domain.MonetizationRepository
|
Monetization domain.MonetizationRepository
|
||||||
Analytics domain.AnalyticsRepository
|
Analytics analytics.Repository
|
||||||
Auth auth.AuthRepository
|
Auth auth.AuthRepository
|
||||||
Localization localization.LocalizationRepository
|
Localization localization.LocalizationRepository
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,26 +3,27 @@ package sql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type workRepository struct {
|
type workRepository struct {
|
||||||
domain.BaseRepository[domain.Work]
|
domain.BaseRepository[work.Work]
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorkRepository creates a new WorkRepository.
|
// NewWorkRepository creates a new WorkRepository.
|
||||||
func NewWorkRepository(db *gorm.DB) domain.WorkRepository {
|
func NewWorkRepository(db *gorm.DB) work.WorkRepository {
|
||||||
return &workRepository{
|
return &workRepository{
|
||||||
BaseRepository: NewBaseRepositoryImpl[domain.Work](db),
|
BaseRepository: NewBaseRepositoryImpl[work.Work](db),
|
||||||
db: db,
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByTitle finds works by title (partial match)
|
// FindByTitle finds works by title (partial match)
|
||||||
func (r *workRepository) FindByTitle(ctx context.Context, title string) ([]domain.Work, error) {
|
func (r *workRepository) FindByTitle(ctx context.Context, title string) ([]work.Work, error) {
|
||||||
var works []domain.Work
|
var works []work.Work
|
||||||
if err := r.db.WithContext(ctx).Where("title LIKE ?", "%"+title+"%").Find(&works).Error; err != nil {
|
if err := r.db.WithContext(ctx).Where("title LIKE ?", "%"+title+"%").Find(&works).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -30,8 +31,8 @@ func (r *workRepository) FindByTitle(ctx context.Context, title string) ([]domai
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindByAuthor finds works by author ID
|
// FindByAuthor finds works by author ID
|
||||||
func (r *workRepository) FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) {
|
func (r *workRepository) FindByAuthor(ctx context.Context, authorID uint) ([]work.Work, error) {
|
||||||
var works []domain.Work
|
var works []work.Work
|
||||||
if err := r.db.WithContext(ctx).Joins("JOIN work_authors ON work_authors.work_id = works.id").
|
if err := r.db.WithContext(ctx).Joins("JOIN work_authors ON work_authors.work_id = works.id").
|
||||||
Where("work_authors.author_id = ?", authorID).
|
Where("work_authors.author_id = ?", authorID).
|
||||||
Find(&works).Error; err != nil {
|
Find(&works).Error; err != nil {
|
||||||
@ -41,8 +42,8 @@ func (r *workRepository) FindByAuthor(ctx context.Context, authorID uint) ([]dom
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindByCategory finds works by category ID
|
// FindByCategory finds works by category ID
|
||||||
func (r *workRepository) FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) {
|
func (r *workRepository) FindByCategory(ctx context.Context, categoryID uint) ([]work.Work, error) {
|
||||||
var works []domain.Work
|
var works []work.Work
|
||||||
if err := r.db.WithContext(ctx).Joins("JOIN work_categories ON work_categories.work_id = works.id").
|
if err := r.db.WithContext(ctx).Joins("JOIN work_categories ON work_categories.work_id = works.id").
|
||||||
Where("work_categories.category_id = ?", categoryID).
|
Where("work_categories.category_id = ?", categoryID).
|
||||||
Find(&works).Error; err != nil {
|
Find(&works).Error; err != nil {
|
||||||
@ -52,7 +53,7 @@ func (r *workRepository) FindByCategory(ctx context.Context, categoryID uint) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindByLanguage finds works by language with pagination
|
// FindByLanguage finds works by language with pagination
|
||||||
func (r *workRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (r *workRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
@ -61,11 +62,11 @@ func (r *workRepository) FindByLanguage(ctx context.Context, language string, pa
|
|||||||
pageSize = 20
|
pageSize = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
var works []domain.Work
|
var works []work.Work
|
||||||
var totalCount int64
|
var totalCount int64
|
||||||
|
|
||||||
// Get total count
|
// Get total count
|
||||||
if err := r.db.WithContext(ctx).Model(&domain.Work{}).Where("language = ?", language).Count(&totalCount).Error; err != nil {
|
if err := r.db.WithContext(ctx).Model(&work.Work{}).Where("language = ?", language).Count(&totalCount).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ func (r *workRepository) FindByLanguage(ctx context.Context, language string, pa
|
|||||||
hasNext := page < totalPages
|
hasNext := page < totalPages
|
||||||
hasPrev := page > 1
|
hasPrev := page > 1
|
||||||
|
|
||||||
return &domain.PaginatedResult[domain.Work]{
|
return &domain.PaginatedResult[work.Work]{
|
||||||
Items: works,
|
Items: works,
|
||||||
TotalCount: totalCount,
|
TotalCount: totalCount,
|
||||||
Page: page,
|
Page: page,
|
||||||
@ -99,23 +100,15 @@ func (r *workRepository) FindByLanguage(ctx context.Context, language string, pa
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Delete removes a work and its associations
|
// Delete removes a work and its associations
|
||||||
func (r *workRepository) Delete(ctx context.Context, id uint) error {
|
func (r *workRepository) Delete(ctx context.Context, id uint) error {
|
||||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
// Manually delete associations
|
// Manually delete associations
|
||||||
if err := tx.Select("Copyrights", "Monetizations", "Authors", "Tags", "Categories").Delete(&domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: id}}}).Error; err != nil {
|
if err := tx.Select("Copyrights", "Monetizations", "Authors", "Tags", "Categories").Delete(&work.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: id}}}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also delete the work itself
|
// Also delete the work itself
|
||||||
if err := tx.Delete(&domain.Work{}, id).Error; err != nil {
|
if err := tx.Delete(&work.Work{}, id).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -123,12 +116,12 @@ func (r *workRepository) Delete(ctx context.Context, id uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetWithTranslations gets a work with its translations
|
// GetWithTranslations gets a work with its translations
|
||||||
func (r *workRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) {
|
func (r *workRepository) GetWithTranslations(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
return r.FindWithPreload(ctx, []string{"Translations"}, id)
|
return r.FindWithPreload(ctx, []string{"Translations"}, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListWithTranslations lists works with their translations
|
// ListWithTranslations lists works with their translations
|
||||||
func (r *workRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (r *workRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
@ -137,11 +130,11 @@ func (r *workRepository) ListWithTranslations(ctx context.Context, page, pageSiz
|
|||||||
pageSize = 20
|
pageSize = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
var works []domain.Work
|
var works []work.Work
|
||||||
var totalCount int64
|
var totalCount int64
|
||||||
|
|
||||||
// Get total count
|
// Get total count
|
||||||
if err := r.db.WithContext(ctx).Model(&domain.Work{}).Count(&totalCount).Error; err != nil {
|
if err := r.db.WithContext(ctx).Model(&work.Work{}).Count(&totalCount).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +157,7 @@ func (r *workRepository) ListWithTranslations(ctx context.Context, page, pageSiz
|
|||||||
hasNext := page < totalPages
|
hasNext := page < totalPages
|
||||||
hasPrev := page > 1
|
hasPrev := page > 1
|
||||||
|
|
||||||
return &domain.PaginatedResult[domain.Work]{
|
return &domain.PaginatedResult[work.Work]{
|
||||||
Items: works,
|
Items: works,
|
||||||
TotalCount: totalCount,
|
TotalCount: totalCount,
|
||||||
Page: page,
|
Page: page,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"tercul/internal/data/sql"
|
"tercul/internal/data/sql"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/testutil"
|
"tercul/internal/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
@ -12,7 +13,7 @@ import (
|
|||||||
|
|
||||||
type WorkRepositoryTestSuite struct {
|
type WorkRepositoryTestSuite struct {
|
||||||
testutil.IntegrationTestSuite
|
testutil.IntegrationTestSuite
|
||||||
WorkRepo domain.WorkRepository
|
WorkRepo work.WorkRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WorkRepositoryTestSuite) SetupSuite() {
|
func (s *WorkRepositoryTestSuite) SetupSuite() {
|
||||||
@ -29,7 +30,7 @@ func (s *WorkRepositoryTestSuite) TestCreateWork() {
|
|||||||
}
|
}
|
||||||
s.Require().NoError(s.DB.Create(copyright).Error)
|
s.Require().NoError(s.DB.Create(copyright).Error)
|
||||||
|
|
||||||
work := &domain.Work{
|
workModel := &work.Work{
|
||||||
Title: "New Test Work",
|
Title: "New Test Work",
|
||||||
TranslatableModel: domain.TranslatableModel{
|
TranslatableModel: domain.TranslatableModel{
|
||||||
Language: "en",
|
Language: "en",
|
||||||
@ -38,15 +39,15 @@ func (s *WorkRepositoryTestSuite) TestCreateWork() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
err := s.WorkRepo.Create(context.Background(), work)
|
err := s.WorkRepo.Create(context.Background(), workModel)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.NotZero(work.ID)
|
s.NotZero(workModel.ID)
|
||||||
|
|
||||||
// Verify that the work was actually created in the database
|
// Verify that the work was actually created in the database
|
||||||
var foundWork domain.Work
|
var foundWork work.Work
|
||||||
err = s.DB.Preload("Copyrights").First(&foundWork, work.ID).Error
|
err = s.DB.Preload("Copyrights").First(&foundWork, workModel.ID).Error
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Equal("New Test Work", foundWork.Title)
|
s.Equal("New Test Work", foundWork.Title)
|
||||||
s.Equal("en", foundWork.Language)
|
s.Equal("en", foundWork.Language)
|
||||||
@ -64,16 +65,16 @@ func (s *WorkRepositoryTestSuite) TestGetWorkByID() {
|
|||||||
}
|
}
|
||||||
s.Require().NoError(s.DB.Create(copyright).Error)
|
s.Require().NoError(s.DB.Create(copyright).Error)
|
||||||
|
|
||||||
work := s.CreateTestWork("Test Work", "en", "Test content")
|
workModel := s.CreateTestWork("Test Work", "en", "Test content")
|
||||||
s.Require().NoError(s.DB.Model(work).Association("Copyrights").Append(copyright))
|
s.Require().NoError(s.DB.Model(workModel).Association("Copyrights").Append(copyright))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
foundWork, err := s.WorkRepo.GetByID(context.Background(), work.ID)
|
foundWork, err := s.WorkRepo.GetByID(context.Background(), workModel.ID)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotNil(foundWork)
|
s.Require().NotNil(foundWork)
|
||||||
s.Equal(work.ID, foundWork.ID)
|
s.Equal(workModel.ID, foundWork.ID)
|
||||||
s.Equal("Test Work", foundWork.Title)
|
s.Equal("Test Work", foundWork.Title)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -95,21 +96,21 @@ func (s *WorkRepositoryTestSuite) TestUpdateWork() {
|
|||||||
s.Require().NoError(s.DB.Create(©right1).Error)
|
s.Require().NoError(s.DB.Create(©right1).Error)
|
||||||
s.Require().NoError(s.DB.Create(©right2).Error)
|
s.Require().NoError(s.DB.Create(©right2).Error)
|
||||||
|
|
||||||
work := s.CreateTestWork("Original Title", "en", "Original content")
|
workModel := s.CreateTestWork("Original Title", "en", "Original content")
|
||||||
s.Require().NoError(s.DB.Model(work).Association("Copyrights").Append(copyright1))
|
s.Require().NoError(s.DB.Model(workModel).Association("Copyrights").Append(copyright1))
|
||||||
|
|
||||||
work.Title = "Updated Title"
|
workModel.Title = "Updated Title"
|
||||||
s.Require().NoError(s.DB.Model(work).Association("Copyrights").Replace(copyright2))
|
s.Require().NoError(s.DB.Model(workModel).Association("Copyrights").Replace(copyright2))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
err := s.WorkRepo.Update(context.Background(), work)
|
err := s.WorkRepo.Update(context.Background(), workModel)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Verify that the work was actually updated in the database
|
// Verify that the work was actually updated in the database
|
||||||
var foundWork domain.Work
|
var foundWork work.Work
|
||||||
err = s.DB.Preload("Copyrights").First(&foundWork, work.ID).Error
|
err = s.DB.Preload("Copyrights").First(&foundWork, workModel.ID).Error
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Equal("Updated Title", foundWork.Title)
|
s.Equal("Updated Title", foundWork.Title)
|
||||||
s.Require().Len(foundWork.Copyrights, 1)
|
s.Require().Len(foundWork.Copyrights, 1)
|
||||||
@ -120,25 +121,25 @@ func (s *WorkRepositoryTestSuite) TestUpdateWork() {
|
|||||||
func (s *WorkRepositoryTestSuite) TestDeleteWork() {
|
func (s *WorkRepositoryTestSuite) TestDeleteWork() {
|
||||||
s.Run("should delete an existing work and its associations", func() {
|
s.Run("should delete an existing work and its associations", func() {
|
||||||
// Arrange
|
// Arrange
|
||||||
work := s.CreateTestWork("To Be Deleted", "en", "Content")
|
workModel := s.CreateTestWork("To Be Deleted", "en", "Content")
|
||||||
copyright := &domain.Copyright{Name: "C1", Identificator: "C1"}
|
copyright := &domain.Copyright{Name: "C1", Identificator: "C1"}
|
||||||
s.Require().NoError(s.DB.Create(copyright).Error)
|
s.Require().NoError(s.DB.Create(copyright).Error)
|
||||||
s.Require().NoError(s.DB.Model(work).Association("Copyrights").Append(copyright))
|
s.Require().NoError(s.DB.Model(workModel).Association("Copyrights").Append(copyright))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
err := s.WorkRepo.Delete(context.Background(), work.ID)
|
err := s.WorkRepo.Delete(context.Background(), workModel.ID)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Verify that the work was actually deleted from the database
|
// Verify that the work was actually deleted from the database
|
||||||
var foundWork domain.Work
|
var foundWork work.Work
|
||||||
err = s.DB.First(&foundWork, work.ID).Error
|
err = s.DB.First(&foundWork, workModel.ID).Error
|
||||||
s.Require().Error(err)
|
s.Require().Error(err)
|
||||||
|
|
||||||
// Verify that the association in the join table is also deleted
|
// Verify that the association in the join table is also deleted
|
||||||
var count int64
|
var count int64
|
||||||
s.DB.Table("work_copyrights").Where("work_id = ?", work.ID).Count(&count)
|
s.DB.Table("work_copyrights").Where("work_id = ?", workModel.ID).Count(&count)
|
||||||
s.Zero(count)
|
s.Zero(count)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
package domain
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type AnalyticsRepository interface {
|
|
||||||
IncrementWorkCounter(ctx context.Context, workID uint, field string, value int) error
|
|
||||||
IncrementTranslationCounter(ctx context.Context, translationID uint, field string, value int) error
|
|
||||||
UpdateWorkStats(ctx context.Context, workID uint, stats WorkStats) error
|
|
||||||
UpdateTranslationStats(ctx context.Context, translationID uint, stats TranslationStats) error
|
|
||||||
GetOrCreateWorkStats(ctx context.Context, workID uint) (*WorkStats, error)
|
|
||||||
GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*TranslationStats, error)
|
|
||||||
GetOrCreateUserEngagement(ctx context.Context, userID uint, date time.Time) (*UserEngagement, error)
|
|
||||||
UpdateUserEngagement(ctx context.Context, userEngagement *UserEngagement) error
|
|
||||||
UpdateTrendingWorks(ctx context.Context, timePeriod string, trending []*Trending) error
|
|
||||||
GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*Work, error)
|
|
||||||
}
|
|
||||||
@ -182,40 +182,6 @@ func (u *User) CheckPassword(password string) bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkStatus string
|
|
||||||
const (
|
|
||||||
WorkStatusDraft WorkStatus = "draft"
|
|
||||||
WorkStatusPublished WorkStatus = "published"
|
|
||||||
WorkStatusArchived WorkStatus = "archived"
|
|
||||||
WorkStatusDeleted WorkStatus = "deleted"
|
|
||||||
)
|
|
||||||
type WorkType string
|
|
||||||
const (
|
|
||||||
WorkTypePoetry WorkType = "poetry"
|
|
||||||
WorkTypeProse WorkType = "prose"
|
|
||||||
WorkTypeDrama WorkType = "drama"
|
|
||||||
WorkTypeEssay WorkType = "essay"
|
|
||||||
WorkTypeNovel WorkType = "novel"
|
|
||||||
WorkTypeShortStory WorkType = "short_story"
|
|
||||||
WorkTypeNovella WorkType = "novella"
|
|
||||||
WorkTypePlay WorkType = "play"
|
|
||||||
WorkTypeScript WorkType = "script"
|
|
||||||
WorkTypeOther WorkType = "other"
|
|
||||||
)
|
|
||||||
type Work struct {
|
|
||||||
TranslatableModel
|
|
||||||
Title string `gorm:"size:255;not null"`
|
|
||||||
Description string `gorm:"type:text"`
|
|
||||||
Type WorkType `gorm:"size:50;default:'other'"`
|
|
||||||
Status WorkStatus `gorm:"size:50;default:'draft'"`
|
|
||||||
PublishedAt *time.Time
|
|
||||||
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
|
||||||
Authors []*Author `gorm:"many2many:work_authors"`
|
|
||||||
Tags []*Tag `gorm:"many2many:work_tags"`
|
|
||||||
Categories []*Category `gorm:"many2many:work_categories"`
|
|
||||||
Copyrights []*Copyright `gorm:"many2many:work_copyrights;constraint:OnDelete:CASCADE"`
|
|
||||||
Monetizations []*Monetization `gorm:"many2many:work_monetizations;constraint:OnDelete:CASCADE"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthorStatus string
|
type AuthorStatus string
|
||||||
const (
|
const (
|
||||||
@ -229,7 +195,6 @@ type Author struct {
|
|||||||
Status AuthorStatus `gorm:"size:50;default:'active'"`
|
Status AuthorStatus `gorm:"size:50;default:'active'"`
|
||||||
BirthDate *time.Time
|
BirthDate *time.Time
|
||||||
DeathDate *time.Time
|
DeathDate *time.Time
|
||||||
Works []*Work `gorm:"many2many:work_authors"`
|
|
||||||
Books []*Book `gorm:"many2many:book_authors"`
|
Books []*Book `gorm:"many2many:book_authors"`
|
||||||
CountryID *uint
|
CountryID *uint
|
||||||
Country *Country `gorm:"foreignKey:CountryID"`
|
Country *Country `gorm:"foreignKey:CountryID"`
|
||||||
@ -267,7 +232,6 @@ type Book struct {
|
|||||||
Format BookFormat `gorm:"size:50;default:'paperback'"`
|
Format BookFormat `gorm:"size:50;default:'paperback'"`
|
||||||
Status BookStatus `gorm:"size:50;default:'draft'"`
|
Status BookStatus `gorm:"size:50;default:'draft'"`
|
||||||
PublishedAt *time.Time
|
PublishedAt *time.Time
|
||||||
Works []*Work `gorm:"many2many:book_works"`
|
|
||||||
Authors []*Author `gorm:"many2many:book_authors"`
|
Authors []*Author `gorm:"many2many:book_authors"`
|
||||||
PublisherID *uint
|
PublisherID *uint
|
||||||
Publisher *Publisher `gorm:"foreignKey:PublisherID"`
|
Publisher *Publisher `gorm:"foreignKey:PublisherID"`
|
||||||
@ -307,7 +271,6 @@ type Source struct {
|
|||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
URL string `gorm:"size:512"`
|
URL string `gorm:"size:512"`
|
||||||
Status SourceStatus `gorm:"size:50;default:'active'"`
|
Status SourceStatus `gorm:"size:50;default:'active'"`
|
||||||
Works []*Work `gorm:"many2many:work_sources"`
|
|
||||||
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
Translations []*Translation `gorm:"polymorphic:Translatable"`
|
||||||
Copyrights []*Copyright `gorm:"many2many:source_copyrights;constraint:OnDelete:CASCADE"`
|
Copyrights []*Copyright `gorm:"many2many:source_copyrights;constraint:OnDelete:CASCADE"`
|
||||||
Monetizations []*Monetization `gorm:"many2many:source_monetizations;constraint:OnDelete:CASCADE"`
|
Monetizations []*Monetization `gorm:"many2many:source_monetizations;constraint:OnDelete:CASCADE"`
|
||||||
@ -333,12 +296,6 @@ type Edition struct {
|
|||||||
Book *Book `gorm:"foreignKey:BookID"`
|
Book *Book `gorm:"foreignKey:BookID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Work) BeforeSave(tx *gorm.DB) error {
|
|
||||||
if w.Title == "" {
|
|
||||||
w.Title = "Untitled Work"
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (a *Author) BeforeSave(tx *gorm.DB) error {
|
func (a *Author) BeforeSave(tx *gorm.DB) error {
|
||||||
if a.Name == "" {
|
if a.Name == "" {
|
||||||
a.Name = "Unknown Author"
|
a.Name = "Unknown Author"
|
||||||
@ -364,7 +321,6 @@ type Comment struct {
|
|||||||
UserID uint
|
UserID uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
LineNumber *int `gorm:"index"`
|
LineNumber *int `gorm:"index"`
|
||||||
@ -380,7 +336,6 @@ type Like struct {
|
|||||||
UserID uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
UserID uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
WorkID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
TranslationID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
CommentID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
CommentID *uint `gorm:"index;uniqueIndex:uniq_like_user_target"`
|
||||||
@ -392,7 +347,6 @@ type Bookmark struct {
|
|||||||
UserID uint `gorm:"index;uniqueIndex:uniq_bookmark_user_work"`
|
UserID uint `gorm:"index;uniqueIndex:uniq_bookmark_user_work"`
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID uint `gorm:"index;uniqueIndex:uniq_bookmark_user_work"`
|
WorkID uint `gorm:"index;uniqueIndex:uniq_bookmark_user_work"`
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
Notes string `gorm:"type:text"`
|
Notes string `gorm:"type:text"`
|
||||||
LastReadAt *time.Time
|
LastReadAt *time.Time
|
||||||
Progress int `gorm:"default:0"`
|
Progress int `gorm:"default:0"`
|
||||||
@ -403,10 +357,14 @@ type Collection struct {
|
|||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
UserID uint
|
UserID uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
Works []*Work `gorm:"many2many:collection_works"`
|
|
||||||
IsPublic bool `gorm:"default:true"`
|
IsPublic bool `gorm:"default:true"`
|
||||||
CoverImageURL string `gorm:"size:255"`
|
CoverImageURL string `gorm:"size:255"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CollectionWork struct {
|
||||||
|
CollectionID uint `gorm:"primaryKey"`
|
||||||
|
WorkID uint `gorm:"primaryKey"`
|
||||||
|
}
|
||||||
type Contribution struct {
|
type Contribution struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `gorm:"size:100;not null"`
|
Name string `gorm:"size:100;not null"`
|
||||||
@ -414,7 +372,6 @@ type Contribution struct {
|
|||||||
UserID uint
|
UserID uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
ReviewerID *uint
|
ReviewerID *uint
|
||||||
@ -477,7 +434,6 @@ type Tag struct {
|
|||||||
BaseModel
|
BaseModel
|
||||||
Name string `gorm:"size:100;not null;uniqueIndex"`
|
Name string `gorm:"size:100;not null;uniqueIndex"`
|
||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
Works []*Work `gorm:"many2many:work_tags"`
|
|
||||||
Slug string `gorm:"size:255;index"`
|
Slug string `gorm:"size:255;index"`
|
||||||
}
|
}
|
||||||
type Category struct {
|
type Category struct {
|
||||||
@ -487,7 +443,6 @@ type Category struct {
|
|||||||
ParentID *uint
|
ParentID *uint
|
||||||
Parent *Category `gorm:"foreignKey:ParentID"`
|
Parent *Category `gorm:"foreignKey:ParentID"`
|
||||||
Children []*Category `gorm:"foreignKey:ParentID"`
|
Children []*Category `gorm:"foreignKey:ParentID"`
|
||||||
Works []*Work `gorm:"many2many:work_categories"`
|
|
||||||
Path string `gorm:"size:1024;index"`
|
Path string `gorm:"size:1024;index"`
|
||||||
Slug string `gorm:"size:255;index"`
|
Slug string `gorm:"size:255;index"`
|
||||||
}
|
}
|
||||||
@ -496,14 +451,6 @@ type Series struct {
|
|||||||
Name string `gorm:"size:255;not null;uniqueIndex"`
|
Name string `gorm:"size:255;not null;uniqueIndex"`
|
||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
}
|
}
|
||||||
type WorkSeries struct {
|
|
||||||
BaseModel
|
|
||||||
WorkID uint `gorm:"index;uniqueIndex:uniq_work_series"`
|
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
SeriesID uint `gorm:"index;uniqueIndex:uniq_work_series"`
|
|
||||||
Series *Series `gorm:"foreignKey:SeriesID"`
|
|
||||||
NumberInSeries int `gorm:"default:0"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Translation struct {
|
type Translation struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -554,9 +501,6 @@ func (t *Translation) BeforeSave(tx *gorm.DB) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (w *Work) GetID() uint { return w.ID }
|
|
||||||
func (w *Work) GetType() string { return "Work" }
|
|
||||||
func (w *Work) GetDefaultLanguage() string { return w.Language }
|
|
||||||
func (a *Author) GetID() uint { return a.ID }
|
func (a *Author) GetID() uint { return a.ID }
|
||||||
func (a *Author) GetType() string { return "Author" }
|
func (a *Author) GetType() string { return "Author" }
|
||||||
func (a *Author) GetDefaultLanguage() string { return a.Language }
|
func (a *Author) GetDefaultLanguage() string { return a.Language }
|
||||||
@ -583,13 +527,6 @@ type Copyright struct {
|
|||||||
EndDate *time.Time
|
EndDate *time.Time
|
||||||
Translations []CopyrightTranslation `gorm:"foreignKey:CopyrightID"`
|
Translations []CopyrightTranslation `gorm:"foreignKey:CopyrightID"`
|
||||||
}
|
}
|
||||||
type WorkCopyright struct {
|
|
||||||
WorkID uint `gorm:"primaryKey;index"`
|
|
||||||
CopyrightID uint `gorm:"primaryKey;index"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (WorkCopyright) TableName() string { return "work_copyrights" }
|
|
||||||
|
|
||||||
type AuthorCopyright struct {
|
type AuthorCopyright struct {
|
||||||
AuthorID uint `gorm:"primaryKey;index"`
|
AuthorID uint `gorm:"primaryKey;index"`
|
||||||
@ -660,13 +597,6 @@ const (
|
|||||||
MonetizationStatusInactive MonetizationStatus = "inactive"
|
MonetizationStatusInactive MonetizationStatus = "inactive"
|
||||||
MonetizationStatusPending MonetizationStatus = "pending"
|
MonetizationStatusPending MonetizationStatus = "pending"
|
||||||
)
|
)
|
||||||
type WorkMonetization struct {
|
|
||||||
WorkID uint `gorm:"primaryKey;index"`
|
|
||||||
MonetizationID uint `gorm:"primaryKey;index"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (WorkMonetization) TableName() string { return "work_monetizations" }
|
|
||||||
|
|
||||||
type AuthorMonetization struct {
|
type AuthorMonetization struct {
|
||||||
AuthorID uint `gorm:"primaryKey;index"`
|
AuthorID uint `gorm:"primaryKey;index"`
|
||||||
@ -742,20 +672,6 @@ type AuditLog struct {
|
|||||||
// This is getting very long, but it's the correct approach.
|
// This is getting very long, but it's the correct approach.
|
||||||
// I will just paste the rest of the structs here.
|
// I will just paste the rest of the structs here.
|
||||||
|
|
||||||
type WorkStats struct {
|
|
||||||
BaseModel
|
|
||||||
Views int64 `gorm:"default:0"`
|
|
||||||
Likes int64 `gorm:"default:0"`
|
|
||||||
Comments int64 `gorm:"default:0"`
|
|
||||||
Bookmarks int64 `gorm:"default:0"`
|
|
||||||
Shares int64 `gorm:"default:0"`
|
|
||||||
TranslationCount int64 `gorm:"default:0"`
|
|
||||||
ReadingTime int `gorm:"default:0"`
|
|
||||||
Complexity float64 `gorm:"type:decimal(5,2);default:0.0"`
|
|
||||||
Sentiment float64 `gorm:"type:decimal(5,2);default:0.0"`
|
|
||||||
WorkID uint `gorm:"uniqueIndex;index"`
|
|
||||||
Work *Work `gorm:"foreignKey:WorkID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
|
||||||
}
|
|
||||||
type TranslationStats struct {
|
type TranslationStats struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Views int64 `gorm:"default:0"`
|
Views int64 `gorm:"default:0"`
|
||||||
@ -815,14 +731,6 @@ type MediaStats struct {
|
|||||||
Media interface{} `gorm:"-"`
|
Media interface{} `gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BookWork struct {
|
|
||||||
BaseModel
|
|
||||||
BookID uint `gorm:"index;uniqueIndex:uniq_book_work"`
|
|
||||||
Book *Book `gorm:"foreignKey:BookID"`
|
|
||||||
WorkID uint `gorm:"index;uniqueIndex:uniq_book_work"`
|
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
Order int `gorm:"default:0"`
|
|
||||||
}
|
|
||||||
type AuthorCountry struct {
|
type AuthorCountry struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
AuthorID uint `gorm:"index;uniqueIndex:uniq_author_country"`
|
AuthorID uint `gorm:"index;uniqueIndex:uniq_author_country"`
|
||||||
@ -830,15 +738,6 @@ type AuthorCountry struct {
|
|||||||
CountryID uint `gorm:"index;uniqueIndex:uniq_author_country"`
|
CountryID uint `gorm:"index;uniqueIndex:uniq_author_country"`
|
||||||
Country *Country `gorm:"foreignKey:CountryID"`
|
Country *Country `gorm:"foreignKey:CountryID"`
|
||||||
}
|
}
|
||||||
type WorkAuthor struct {
|
|
||||||
BaseModel
|
|
||||||
WorkID uint `gorm:"index;uniqueIndex:uniq_work_author_role"`
|
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
AuthorID uint `gorm:"index;uniqueIndex:uniq_work_author_role"`
|
|
||||||
Author *Author `gorm:"foreignKey:AuthorID"`
|
|
||||||
Role string `gorm:"size:50;default:'author';uniqueIndex:uniq_work_author_role"`
|
|
||||||
Ordinal int `gorm:"default:0"`
|
|
||||||
}
|
|
||||||
type BookAuthor struct {
|
type BookAuthor struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
BookID uint `gorm:"index;uniqueIndex:uniq_book_author_role"`
|
BookID uint `gorm:"index;uniqueIndex:uniq_book_author_role"`
|
||||||
@ -855,7 +754,6 @@ type ReadabilityScore struct {
|
|||||||
Language string `gorm:"size:50;not null"`
|
Language string `gorm:"size:50;not null"`
|
||||||
Method string `gorm:"size:50"`
|
Method string `gorm:"size:50"`
|
||||||
WorkID uint
|
WorkID uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
}
|
}
|
||||||
type WritingStyle struct {
|
type WritingStyle struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -863,7 +761,6 @@ type WritingStyle struct {
|
|||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
Language string `gorm:"size:50;not null"`
|
Language string `gorm:"size:50;not null"`
|
||||||
WorkID uint
|
WorkID uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
}
|
}
|
||||||
type LinguisticLayer struct {
|
type LinguisticLayer struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -872,13 +769,11 @@ type LinguisticLayer struct {
|
|||||||
Language string `gorm:"size:50;not null"`
|
Language string `gorm:"size:50;not null"`
|
||||||
Type string `gorm:"size:50"`
|
Type string `gorm:"size:50"`
|
||||||
WorkID uint
|
WorkID uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
Data JSONB `gorm:"type:jsonb;default:'{}'"`
|
Data JSONB `gorm:"type:jsonb;default:'{}'"`
|
||||||
}
|
}
|
||||||
type TextBlock struct {
|
type TextBlock struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
Index int `gorm:"index"`
|
Index int `gorm:"index"`
|
||||||
@ -897,7 +792,6 @@ type TextMetadata struct {
|
|||||||
AverageWordLength float64 `gorm:"type:decimal(5,2)"`
|
AverageWordLength float64 `gorm:"type:decimal(5,2)"`
|
||||||
AverageSentenceLength float64 `gorm:"type:decimal(5,2)"`
|
AverageSentenceLength float64 `gorm:"type:decimal(5,2)"`
|
||||||
WorkID uint
|
WorkID uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
}
|
}
|
||||||
type PoeticAnalysis struct {
|
type PoeticAnalysis struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -908,7 +802,6 @@ type PoeticAnalysis struct {
|
|||||||
StanzaCount int `gorm:"default:0"`
|
StanzaCount int `gorm:"default:0"`
|
||||||
LineCount int `gorm:"default:0"`
|
LineCount int `gorm:"default:0"`
|
||||||
WorkID uint
|
WorkID uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
}
|
}
|
||||||
type Word struct {
|
type Word struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -918,7 +811,6 @@ type Word struct {
|
|||||||
Lemma string `gorm:"size:100"`
|
Lemma string `gorm:"size:100"`
|
||||||
ConceptID *uint
|
ConceptID *uint
|
||||||
Concept *Concept `gorm:"foreignKey:ConceptID"`
|
Concept *Concept `gorm:"foreignKey:ConceptID"`
|
||||||
Works []*Work `gorm:"many2many:work_words"`
|
|
||||||
}
|
}
|
||||||
type WordOccurrence struct {
|
type WordOccurrence struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -936,14 +828,12 @@ type Concept struct {
|
|||||||
Name string `gorm:"size:100;not null"`
|
Name string `gorm:"size:100;not null"`
|
||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
Words []*Word `gorm:"foreignKey:ConceptID"`
|
Words []*Word `gorm:"foreignKey:ConceptID"`
|
||||||
Works []*Work `gorm:"many2many:work_concepts"`
|
|
||||||
}
|
}
|
||||||
type LanguageEntity struct {
|
type LanguageEntity struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `gorm:"size:100;not null"`
|
Name string `gorm:"size:100;not null"`
|
||||||
Type string `gorm:"size:50"`
|
Type string `gorm:"size:50"`
|
||||||
Language string `gorm:"size:50;not null"`
|
Language string `gorm:"size:50;not null"`
|
||||||
Works []*Work `gorm:"many2many:work_language_entities"`
|
|
||||||
}
|
}
|
||||||
type EntityOccurrence struct {
|
type EntityOccurrence struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -960,7 +850,6 @@ type LanguageAnalysis struct {
|
|||||||
Language string `gorm:"size:50;not null;uniqueIndex:uniq_work_language_analysis"`
|
Language string `gorm:"size:50;not null;uniqueIndex:uniq_work_language_analysis"`
|
||||||
Analysis JSONB `gorm:"type:jsonb;default:'{}'"`
|
Analysis JSONB `gorm:"type:jsonb;default:'{}'"`
|
||||||
WorkID uint `gorm:"index;uniqueIndex:uniq_work_language_analysis"`
|
WorkID uint `gorm:"index;uniqueIndex:uniq_work_language_analysis"`
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
}
|
}
|
||||||
type Gamification struct {
|
type Gamification struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -981,7 +870,6 @@ type Stats struct {
|
|||||||
UserID *uint
|
UserID *uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
}
|
}
|
||||||
type SearchDocument struct {
|
type SearchDocument struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@ -1002,7 +890,6 @@ type Emotion struct {
|
|||||||
UserID *uint
|
UserID *uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
CollectionID *uint
|
CollectionID *uint
|
||||||
Collection *Collection `gorm:"foreignKey:CollectionID"`
|
Collection *Collection `gorm:"foreignKey:CollectionID"`
|
||||||
}
|
}
|
||||||
@ -1011,14 +898,12 @@ type Mood struct {
|
|||||||
Name string `gorm:"size:100;not null"`
|
Name string `gorm:"size:100;not null"`
|
||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
Language string `gorm:"size:50;not null"`
|
Language string `gorm:"size:50;not null"`
|
||||||
Works []*Work `gorm:"many2many:work_moods"`
|
|
||||||
}
|
}
|
||||||
type TopicCluster struct {
|
type TopicCluster struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Name string `gorm:"size:100;not null"`
|
Name string `gorm:"size:100;not null"`
|
||||||
Description string `gorm:"type:text"`
|
Description string `gorm:"type:text"`
|
||||||
Keywords string `gorm:"type:text"`
|
Keywords string `gorm:"type:text"`
|
||||||
Works []*Work `gorm:"many2many:work_topic_clusters"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Edge struct {
|
type Edge struct {
|
||||||
@ -1039,7 +924,6 @@ type Embedding struct {
|
|||||||
Model string `gorm:"size:50;not null;uniqueIndex:uniq_embedding"`
|
Model string `gorm:"size:50;not null;uniqueIndex:uniq_embedding"`
|
||||||
Dim int `gorm:"default:0"`
|
Dim int `gorm:"default:0"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
}
|
}
|
||||||
@ -1086,7 +970,6 @@ type EditorialWorkflow struct {
|
|||||||
Notes string `gorm:"type:text"`
|
Notes string `gorm:"type:text"`
|
||||||
Language string `gorm:"size:50;not null"`
|
Language string `gorm:"size:50;not null"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
UserID uint
|
UserID uint
|
||||||
@ -1109,7 +992,6 @@ type Vote struct {
|
|||||||
UserID uint
|
UserID uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
CommentID *uint
|
CommentID *uint
|
||||||
@ -1122,7 +1004,6 @@ type Contributor struct {
|
|||||||
UserID *uint
|
UserID *uint
|
||||||
User *User `gorm:"foreignKey:UserID"`
|
User *User `gorm:"foreignKey:UserID"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
}
|
}
|
||||||
@ -1140,7 +1021,6 @@ type HybridEntityWork struct {
|
|||||||
Name string `gorm:"size:100;not null"`
|
Name string `gorm:"size:100;not null"`
|
||||||
Type string `gorm:"size:50"`
|
Type string `gorm:"size:50"`
|
||||||
WorkID *uint
|
WorkID *uint
|
||||||
Work *Work `gorm:"foreignKey:WorkID"`
|
|
||||||
TranslationID *uint
|
TranslationID *uint
|
||||||
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
Translation *Translation `gorm:"foreignKey:TranslationID"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -234,16 +234,6 @@ type BaseRepository[T any] interface {
|
|||||||
WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error
|
WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// WorkRepository defines methods specific to Work.
|
|
||||||
type WorkRepository interface {
|
|
||||||
BaseRepository[Work]
|
|
||||||
FindByTitle(ctx context.Context, title string) ([]Work, error)
|
|
||||||
FindByAuthor(ctx context.Context, authorID uint) ([]Work, error)
|
|
||||||
FindByCategory(ctx context.Context, categoryID uint) ([]Work, error)
|
|
||||||
FindByLanguage(ctx context.Context, language string, page, pageSize int) (*PaginatedResult[Work], error)
|
|
||||||
GetWithTranslations(ctx context.Context, id uint) (*Work, error)
|
|
||||||
ListWithTranslations(ctx context.Context, page, pageSize int) (*PaginatedResult[Work], error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthorRepository defines CRUD methods specific to Author.
|
// AuthorRepository defines CRUD methods specific to Author.
|
||||||
type AuthorRepository interface {
|
type AuthorRepository interface {
|
||||||
|
|||||||
@ -2,10 +2,10 @@ package search
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SearchClient defines the interface for a search client.
|
// SearchClient defines the interface for a search client.
|
||||||
type SearchClient interface {
|
type SearchClient interface {
|
||||||
IndexWork(ctx context.Context, work *domain.Work, pipeline string) error
|
IndexWork(ctx context.Context, work *work.Work, pipeline string) error
|
||||||
}
|
}
|
||||||
116
internal/domain/work/entity.go
Normal file
116
internal/domain/work/entity.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package work
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"tercul/internal/domain"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WorkStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
WorkStatusDraft WorkStatus = "draft"
|
||||||
|
WorkStatusPublished WorkStatus = "published"
|
||||||
|
WorkStatusArchived WorkStatus = "archived"
|
||||||
|
WorkStatusDeleted WorkStatus = "deleted"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WorkType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
WorkTypePoetry WorkType = "poetry"
|
||||||
|
WorkTypeProse WorkType = "prose"
|
||||||
|
WorkTypeDrama WorkType = "drama"
|
||||||
|
WorkTypeEssay WorkType = "essay"
|
||||||
|
WorkTypeNovel WorkType = "novel"
|
||||||
|
WorkTypeShortStory WorkType = "short_story"
|
||||||
|
WorkTypeNovella WorkType = "novella"
|
||||||
|
WorkTypePlay WorkType = "play"
|
||||||
|
WorkTypeScript WorkType = "script"
|
||||||
|
WorkTypeOther WorkType = "other"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Work struct {
|
||||||
|
domain.TranslatableModel
|
||||||
|
Title string `gorm:"size:255;not null"`
|
||||||
|
Description string `gorm:"type:text"`
|
||||||
|
Type WorkType `gorm:"size:50;default:'other'"`
|
||||||
|
Status WorkStatus `gorm:"size:50;default:'draft'"`
|
||||||
|
PublishedAt *time.Time
|
||||||
|
Translations []*domain.Translation `gorm:"polymorphic:Translatable"`
|
||||||
|
Authors []*domain.Author `gorm:"many2many:work_authors"`
|
||||||
|
Tags []*domain.Tag `gorm:"many2many:work_tags"`
|
||||||
|
Categories []*domain.Category `gorm:"many2many:work_categories"`
|
||||||
|
Copyrights []*domain.Copyright `gorm:"many2many:work_copyrights;constraint:OnDelete:CASCADE"`
|
||||||
|
Monetizations []*domain.Monetization `gorm:"many2many:work_monetizations;constraint:OnDelete:CASCADE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Work) BeforeSave(tx *gorm.DB) error {
|
||||||
|
if w.Title == "" {
|
||||||
|
w.Title = "Untitled Work"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Work) GetID() uint { return w.ID }
|
||||||
|
func (w *Work) GetType() string { return "Work" }
|
||||||
|
func (w *Work) GetDefaultLanguage() string { return w.Language }
|
||||||
|
|
||||||
|
type WorkStats struct {
|
||||||
|
domain.BaseModel
|
||||||
|
Views int64 `gorm:"default:0"`
|
||||||
|
Likes int64 `gorm:"default:0"`
|
||||||
|
Comments int64 `gorm:"default:0"`
|
||||||
|
Bookmarks int64 `gorm:"default:0"`
|
||||||
|
Shares int64 `gorm:"default:0"`
|
||||||
|
TranslationCount int64 `gorm:"default:0"`
|
||||||
|
ReadingTime int `gorm:"default:0"`
|
||||||
|
Complexity float64 `gorm:"type:decimal(5,2);default:0.0"`
|
||||||
|
Sentiment float64 `gorm:"type:decimal(5,2);default:0.0"`
|
||||||
|
WorkID uint `gorm:"uniqueIndex;index"`
|
||||||
|
Work *Work `gorm:"foreignKey:WorkID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkSeries struct {
|
||||||
|
domain.BaseModel
|
||||||
|
WorkID uint `gorm:"index;uniqueIndex:uniq_work_series"`
|
||||||
|
Work *Work `gorm:"foreignKey:WorkID"`
|
||||||
|
SeriesID uint `gorm:"index;uniqueIndex:uniq_work_series"`
|
||||||
|
Series *domain.Series `gorm:"foreignKey:SeriesID"`
|
||||||
|
NumberInSeries int `gorm:"default:0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BookWork struct {
|
||||||
|
domain.BaseModel
|
||||||
|
BookID uint `gorm:"index;uniqueIndex:uniq_book_work"`
|
||||||
|
Book *domain.Book `gorm:"foreignKey:BookID"`
|
||||||
|
WorkID uint `gorm:"index;uniqueIndex:uniq_book_work"`
|
||||||
|
Work *Work `gorm:"foreignKey:WorkID"`
|
||||||
|
Order int `gorm:"default:0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkAuthor struct {
|
||||||
|
domain.BaseModel
|
||||||
|
WorkID uint `gorm:"index;uniqueIndex:uniq_work_author_role"`
|
||||||
|
Work *Work `gorm:"foreignKey:WorkID"`
|
||||||
|
AuthorID uint `gorm:"index;uniqueIndex:uniq_work_author_role"`
|
||||||
|
Author *domain.Author `gorm:"foreignKey:AuthorID"`
|
||||||
|
Role string `gorm:"size:50;default:'author';uniqueIndex:uniq_work_author_role"`
|
||||||
|
Ordinal int `gorm:"default:0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkCopyright struct {
|
||||||
|
WorkID uint `gorm:"primaryKey;index"`
|
||||||
|
CopyrightID uint `gorm:"primaryKey;index"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (WorkCopyright) TableName() string { return "work_copyrights" }
|
||||||
|
|
||||||
|
type WorkMonetization struct {
|
||||||
|
WorkID uint `gorm:"primaryKey;index"`
|
||||||
|
MonetizationID uint `gorm:"primaryKey;index"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (WorkMonetization) TableName() string { return "work_monetizations" }
|
||||||
17
internal/domain/work/repo.go
Normal file
17
internal/domain/work/repo.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package work
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"tercul/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WorkRepository defines methods specific to Work.
|
||||||
|
type WorkRepository interface {
|
||||||
|
domain.BaseRepository[Work]
|
||||||
|
FindByTitle(ctx context.Context, title string) ([]Work, error)
|
||||||
|
FindByAuthor(ctx context.Context, authorID uint) ([]Work, error)
|
||||||
|
FindByCategory(ctx context.Context, categoryID uint) ([]Work, error)
|
||||||
|
FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[Work], error)
|
||||||
|
GetWithTranslations(ctx context.Context, id uint) (*Work, error)
|
||||||
|
ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[Work], error)
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"tercul/internal/platform/log"
|
"tercul/internal/platform/log"
|
||||||
@ -22,7 +23,7 @@ type AnalysisRepository interface {
|
|||||||
readabilityScore *domain.ReadabilityScore, languageAnalysis *domain.LanguageAnalysis) error
|
readabilityScore *domain.ReadabilityScore, languageAnalysis *domain.LanguageAnalysis) error
|
||||||
|
|
||||||
// GetWorkByID fetches a work by ID
|
// GetWorkByID fetches a work by ID
|
||||||
GetWorkByID(ctx context.Context, workID uint) (*domain.Work, error)
|
GetWorkByID(ctx context.Context, workID uint) (*work.Work, error)
|
||||||
|
|
||||||
// GetAnalysisData fetches persisted analysis data for a work
|
// GetAnalysisData fetches persisted analysis data for a work
|
||||||
GetAnalysisData(ctx context.Context, workID uint) (*domain.TextMetadata, *domain.ReadabilityScore, *domain.LanguageAnalysis, error)
|
GetAnalysisData(ctx context.Context, workID uint) (*domain.TextMetadata, *domain.ReadabilityScore, *domain.LanguageAnalysis, error)
|
||||||
@ -45,8 +46,8 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine language from the work record to avoid hardcoded defaults
|
// Determine language from the work record to avoid hardcoded defaults
|
||||||
var work domain.Work
|
var workRecord work.Work
|
||||||
if err := r.db.WithContext(ctx).First(&work, workID).Error; err != nil {
|
if err := r.db.WithContext(ctx).First(&workRecord, workID).Error; err != nil {
|
||||||
log.LogError("Failed to fetch work for language",
|
log.LogError("Failed to fetch work for language",
|
||||||
log.F("workID", workID),
|
log.F("workID", workID),
|
||||||
log.F("error", err))
|
log.F("error", err))
|
||||||
@ -56,7 +57,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
|||||||
// Create text metadata
|
// Create text metadata
|
||||||
textMetadata := &domain.TextMetadata{
|
textMetadata := &domain.TextMetadata{
|
||||||
WorkID: workID,
|
WorkID: workID,
|
||||||
Language: work.Language,
|
Language: workRecord.Language,
|
||||||
WordCount: result.WordCount,
|
WordCount: result.WordCount,
|
||||||
SentenceCount: result.SentenceCount,
|
SentenceCount: result.SentenceCount,
|
||||||
ParagraphCount: result.ParagraphCount,
|
ParagraphCount: result.ParagraphCount,
|
||||||
@ -67,7 +68,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
|||||||
// Create readability score
|
// Create readability score
|
||||||
readabilityScore := &domain.ReadabilityScore{
|
readabilityScore := &domain.ReadabilityScore{
|
||||||
WorkID: workID,
|
WorkID: workID,
|
||||||
Language: work.Language,
|
Language: workRecord.Language,
|
||||||
Score: result.ReadabilityScore,
|
Score: result.ReadabilityScore,
|
||||||
Method: result.ReadabilityMethod,
|
Method: result.ReadabilityMethod,
|
||||||
}
|
}
|
||||||
@ -75,7 +76,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
|||||||
// Create language analysis
|
// Create language analysis
|
||||||
languageAnalysis := &domain.LanguageAnalysis{
|
languageAnalysis := &domain.LanguageAnalysis{
|
||||||
WorkID: workID,
|
WorkID: workID,
|
||||||
Language: work.Language,
|
Language: workRecord.Language,
|
||||||
Analysis: domain.JSONB{
|
Analysis: domain.JSONB{
|
||||||
"sentiment": result.Sentiment,
|
"sentiment": result.Sentiment,
|
||||||
"keywords": extractKeywordsAsJSON(result.Keywords),
|
"keywords": extractKeywordsAsJSON(result.Keywords),
|
||||||
@ -89,8 +90,8 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
|||||||
// GetWorkContent retrieves content for a work from translations
|
// GetWorkContent retrieves content for a work from translations
|
||||||
func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint, language string) (string, error) {
|
func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint, language string) (string, error) {
|
||||||
// First, get the work to determine its language
|
// First, get the work to determine its language
|
||||||
var work domain.Work
|
var workRecord work.Work
|
||||||
if err := r.db.First(&work, workID).Error; err != nil {
|
if err := r.db.First(&workRecord, workID).Error; err != nil {
|
||||||
log.LogError("Failed to fetch work for content retrieval",
|
log.LogError("Failed to fetch work for content retrieval",
|
||||||
log.F("workID", workID),
|
log.F("workID", workID),
|
||||||
log.F("error", err))
|
log.F("error", err))
|
||||||
@ -112,7 +113,7 @@ func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint
|
|||||||
|
|
||||||
// Try work's language
|
// Try work's language
|
||||||
if err := r.db.Where("translatable_type = ? AND translatable_id = ? AND language = ?",
|
if err := r.db.Where("translatable_type = ? AND translatable_id = ? AND language = ?",
|
||||||
"Work", workID, work.Language).First(&translation).Error; err == nil {
|
"Work", workID, workRecord.Language).First(&translation).Error; err == nil {
|
||||||
return translation.Content, nil
|
return translation.Content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,12 +127,12 @@ func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetWorkByID fetches a work by ID
|
// GetWorkByID fetches a work by ID
|
||||||
func (r *GORMAnalysisRepository) GetWorkByID(ctx context.Context, workID uint) (*domain.Work, error) {
|
func (r *GORMAnalysisRepository) GetWorkByID(ctx context.Context, workID uint) (*work.Work, error) {
|
||||||
var work domain.Work
|
var workRecord work.Work
|
||||||
if err := r.db.WithContext(ctx).First(&work, workID).Error; err != nil {
|
if err := r.db.WithContext(ctx).First(&workRecord, workID).Error; err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch work: %w", err)
|
return nil, fmt.Errorf("failed to fetch work: %w", err)
|
||||||
}
|
}
|
||||||
return &work, nil
|
return &workRecord, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAnalysisData fetches persisted analysis data for a work
|
// GetAnalysisData fetches persisted analysis data for a work
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hibiken/asynq"
|
"github.com/hibiken/asynq"
|
||||||
@ -60,7 +61,7 @@ func (j *LinguisticSyncJob) EnqueueAnalysisForAllWorks() error {
|
|||||||
log.Println("Enqueueing linguistic analysis jobs for all works...")
|
log.Println("Enqueueing linguistic analysis jobs for all works...")
|
||||||
|
|
||||||
var workIDs []uint
|
var workIDs []uint
|
||||||
if err := j.DB.Model(&domain.Work{}).Pluck("id", &workIDs).Error; err != nil {
|
if err := j.DB.Model(&work.Work{}).Pluck("id", &workIDs).Error; err != nil {
|
||||||
return fmt.Errorf("error fetching work IDs: %w", err)
|
return fmt.Errorf("error fetching work IDs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain/work"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
||||||
@ -13,7 +13,7 @@ import (
|
|||||||
var Client *weaviate.Client
|
var Client *weaviate.Client
|
||||||
|
|
||||||
// UpsertWork inserts or updates a Work object in Weaviate
|
// UpsertWork inserts or updates a Work object in Weaviate
|
||||||
func UpsertWork(client *weaviate.Client, work domain.Work) error {
|
func UpsertWork(client *weaviate.Client, work work.Work) error {
|
||||||
// Create a properties map with the fields that exist in the Work model
|
// Create a properties map with the fields that exist in the Work model
|
||||||
properties := map[string]interface{}{
|
properties := map[string]interface{}{
|
||||||
"language": work.Language,
|
"language": work.Language,
|
||||||
|
|||||||
@ -3,14 +3,14 @@ package search
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain/work"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WeaviateWrapper interface {
|
type WeaviateWrapper interface {
|
||||||
IndexWork(ctx context.Context, work *domain.Work, content string) error
|
IndexWork(ctx context.Context, work *work.Work, content string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type weaviateWrapper struct {
|
type weaviateWrapper struct {
|
||||||
@ -21,7 +21,7 @@ func NewWeaviateWrapper(client *weaviate.Client) WeaviateWrapper {
|
|||||||
return &weaviateWrapper{client: client}
|
return &weaviateWrapper{client: client}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *weaviateWrapper) IndexWork(ctx context.Context, work *domain.Work, content string) error {
|
func (w *weaviateWrapper) IndexWork(ctx context.Context, work *work.Work, content string) error {
|
||||||
properties := map[string]interface{}{
|
properties := map[string]interface{}{
|
||||||
"language": work.Language,
|
"language": work.Language,
|
||||||
"title": work.Title,
|
"title": work.Title,
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"tercul/internal/data/sql"
|
"tercul/internal/data/sql"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
"tercul/internal/domain/search"
|
"tercul/internal/domain/search"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"tercul/internal/jobs/linguistics"
|
"tercul/internal/jobs/linguistics"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ import (
|
|||||||
// mockSearchClient is a mock implementation of the SearchClient interface.
|
// mockSearchClient is a mock implementation of the SearchClient interface.
|
||||||
type mockSearchClient struct{}
|
type mockSearchClient struct{}
|
||||||
|
|
||||||
func (m *mockSearchClient) IndexWork(ctx context.Context, work *domain.Work, pipeline string) error {
|
func (m *mockSearchClient) IndexWork(ctx context.Context, work *work.Work, pipeline string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +55,8 @@ func (m *mockAnalyticsService) IncrementTranslationComments(ctx context.Context,
|
|||||||
func (m *mockAnalyticsService) IncrementTranslationShares(ctx context.Context, translationID uint) error {
|
func (m *mockAnalyticsService) IncrementTranslationShares(ctx context.Context, translationID uint) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (m *mockAnalyticsService) GetOrCreateWorkStats(ctx context.Context, workID uint) (*domain.WorkStats, error) {
|
func (m *mockAnalyticsService) GetOrCreateWorkStats(ctx context.Context, workID uint) (*work.WorkStats, error) {
|
||||||
return &domain.WorkStats{}, nil
|
return &work.WorkStats{}, nil
|
||||||
}
|
}
|
||||||
func (m *mockAnalyticsService) GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error) {
|
func (m *mockAnalyticsService) GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error) {
|
||||||
return &domain.TranslationStats{}, nil
|
return &domain.TranslationStats{}, nil
|
||||||
@ -73,7 +74,7 @@ func (m *mockAnalyticsService) UpdateUserEngagement(ctx context.Context, userID
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (m *mockAnalyticsService) UpdateTrending(ctx context.Context) error { return nil }
|
func (m *mockAnalyticsService) UpdateTrending(ctx context.Context) error { return nil }
|
||||||
func (m *mockAnalyticsService) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*domain.Work, error) {
|
func (m *mockAnalyticsService) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*work.Work, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,13 +140,13 @@ func (s *IntegrationTestSuite) SetupSuite(config *TestConfig) {
|
|||||||
|
|
||||||
s.DB = db
|
s.DB = db
|
||||||
db.AutoMigrate(
|
db.AutoMigrate(
|
||||||
&domain.Work{}, &domain.User{}, &domain.Author{}, &domain.Translation{},
|
&work.Work{}, &domain.User{}, &domain.Author{}, &domain.Translation{},
|
||||||
&domain.Comment{}, &domain.Like{}, &domain.Bookmark{}, &domain.Collection{},
|
&domain.Comment{}, &domain.Like{}, &domain.Bookmark{}, &domain.Collection{},
|
||||||
&domain.Tag{}, &domain.Category{}, &domain.Book{}, &domain.Publisher{},
|
&domain.Tag{}, &domain.Category{}, &domain.Book{}, &domain.Publisher{},
|
||||||
&domain.Source{}, &domain.Copyright{}, &domain.Monetization{},
|
&domain.Source{}, &domain.Copyright{}, &domain.Monetization{},
|
||||||
&domain.WorkStats{}, &domain.Trending{}, &domain.UserSession{}, &domain.Localization{},
|
&work.WorkStats{}, &domain.Trending{}, &domain.UserSession{}, &domain.Localization{},
|
||||||
&domain.LanguageAnalysis{}, &domain.TextMetadata{}, &domain.ReadabilityScore{},
|
&domain.LanguageAnalysis{}, &domain.TextMetadata{}, &domain.ReadabilityScore{},
|
||||||
&domain.TranslationStats{}, &TestEntity{},
|
&domain.TranslationStats{}, &TestEntity{}, &domain.CollectionWork{},
|
||||||
)
|
)
|
||||||
|
|
||||||
repos := sql.NewRepositories(s.DB)
|
repos := sql.NewRepositories(s.DB)
|
||||||
@ -184,8 +185,8 @@ func (s *IntegrationTestSuite) SetupTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateTestWork creates a test work with optional content
|
// CreateTestWork creates a test work with optional content
|
||||||
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *work.Work {
|
||||||
work := &domain.Work{
|
work := &work.Work{
|
||||||
Title: title,
|
Title: title,
|
||||||
TranslatableModel: domain.TranslatableModel{
|
TranslatableModel: domain.TranslatableModel{
|
||||||
Language: language,
|
Language: language,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package testutil
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@ -13,12 +14,12 @@ type MockAnalyticsService struct {
|
|||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockAnalyticsService) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*domain.Work, error) {
|
func (m *MockAnalyticsService) GetTrendingWorks(ctx context.Context, timePeriod string, limit int) ([]*work.Work, error) {
|
||||||
args := m.Called(ctx, timePeriod, limit)
|
args := m.Called(ctx, timePeriod, limit)
|
||||||
if args.Get(0) == nil {
|
if args.Get(0) == nil {
|
||||||
return nil, args.Error(1)
|
return nil, args.Error(1)
|
||||||
}
|
}
|
||||||
return args.Get(0).([]*domain.Work), args.Error(1)
|
return args.Get(0).([]*work.Work), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockAnalyticsService) UpdateTrending(ctx context.Context) error {
|
func (m *MockAnalyticsService) UpdateTrending(ctx context.Context) error {
|
||||||
@ -46,12 +47,12 @@ func (m *MockAnalyticsService) IncrementWorkBookmarks(ctx context.Context, workI
|
|||||||
m.Called(ctx, workID)
|
m.Called(ctx, workID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockAnalyticsService) GetOrCreateWorkStats(ctx context.Context, workID uint) (*domain.WorkStats, error) {
|
func (m *MockAnalyticsService) GetOrCreateWorkStats(ctx context.Context, workID uint) (*work.WorkStats, error) {
|
||||||
args := m.Called(ctx, workID)
|
args := m.Called(ctx, workID)
|
||||||
if args.Get(0) == nil {
|
if args.Get(0) == nil {
|
||||||
return nil, args.Error(1)
|
return nil, args.Error(1)
|
||||||
}
|
}
|
||||||
return args.Get(0).(*domain.WorkStats), args.Error(1)
|
return args.Get(0).(*work.WorkStats), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockAnalyticsService) GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error) {
|
func (m *MockAnalyticsService) GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error) {
|
||||||
@ -85,7 +86,7 @@ func (m *MockAnalyticsService) IncrementTranslationCounter(ctx context.Context,
|
|||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockAnalyticsService) UpdateWorkStats(ctx context.Context, workID uint, stats domain.WorkStats) error {
|
func (m *MockAnalyticsService) UpdateWorkStats(ctx context.Context, workID uint, stats work.WorkStats) error {
|
||||||
args := m.Called(ctx, workID, stats)
|
args := m.Called(ctx, workID, stats)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,14 @@ package testutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockWeaviateWrapper struct {
|
type MockWeaviateWrapper struct {
|
||||||
IndexWorkFunc func(ctx context.Context, work *domain.Work, content string) error
|
IndexWorkFunc func(ctx context.Context, work *work.Work, content string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWeaviateWrapper) IndexWork(ctx context.Context, work *domain.Work, content string) error {
|
func (m *MockWeaviateWrapper) IndexWork(ctx context.Context, work *work.Work, content string) error {
|
||||||
if m.IndexWorkFunc != nil {
|
if m.IndexWorkFunc != nil {
|
||||||
return m.IndexWorkFunc(ctx, work, content)
|
return m.IndexWorkFunc(ctx, work, content)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package testutil
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
|
"tercul/internal/domain/work"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -11,23 +12,23 @@ import (
|
|||||||
// MockWorkRepository is a mock implementation of the WorkRepository interface.
|
// MockWorkRepository is a mock implementation of the WorkRepository interface.
|
||||||
type MockWorkRepository struct {
|
type MockWorkRepository struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
Works []*domain.Work
|
Works []*work.Work
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMockWorkRepository creates a new MockWorkRepository.
|
// NewMockWorkRepository creates a new MockWorkRepository.
|
||||||
func NewMockWorkRepository() *MockWorkRepository {
|
func NewMockWorkRepository() *MockWorkRepository {
|
||||||
return &MockWorkRepository{Works: []*domain.Work{}}
|
return &MockWorkRepository{Works: []*work.Work{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create adds a new work to the mock repository.
|
// Create adds a new work to the mock repository.
|
||||||
func (m *MockWorkRepository) Create(ctx context.Context, work *domain.Work) error {
|
func (m *MockWorkRepository) Create(ctx context.Context, work *work.Work) error {
|
||||||
work.ID = uint(len(m.Works) + 1)
|
work.ID = uint(len(m.Works) + 1)
|
||||||
m.Works = append(m.Works, work)
|
m.Works = append(m.Works, work)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByID retrieves a work by its ID from the mock repository.
|
// GetByID retrieves a work by its ID from the mock repository.
|
||||||
func (m *MockWorkRepository) GetByID(ctx context.Context, id uint) (*domain.Work, error) {
|
func (m *MockWorkRepository) GetByID(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
for _, w := range m.Works {
|
for _, w := range m.Works {
|
||||||
if w.ID == id {
|
if w.ID == id {
|
||||||
return w, nil
|
return w, nil
|
||||||
@ -43,31 +44,31 @@ func (m *MockWorkRepository) Exists(ctx context.Context, id uint) (bool, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The rest of the WorkRepository and BaseRepository methods can be stubbed out.
|
// The rest of the WorkRepository and BaseRepository methods can be stubbed out.
|
||||||
func (m *MockWorkRepository) FindByTitle(ctx context.Context, title string) ([]domain.Work, error) {
|
func (m *MockWorkRepository) FindByTitle(ctx context.Context, title string) ([]work.Work, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) {
|
func (m *MockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]work.Work, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) {
|
func (m *MockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]work.Work, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (m *MockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) {
|
func (m *MockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*work.Work, error) {
|
||||||
return m.GetByID(ctx, id)
|
return m.GetByID(ctx, id)
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (m *MockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Work) error {
|
func (m *MockWorkRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *work.Work) error {
|
||||||
return m.Create(ctx, entity)
|
return m.Create(ctx, entity)
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
func (m *MockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*work.Work, error) {
|
||||||
return m.GetByID(ctx, id)
|
return m.GetByID(ctx, id)
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) Update(ctx context.Context, entity *domain.Work) error {
|
func (m *MockWorkRepository) Update(ctx context.Context, entity *work.Work) error {
|
||||||
for i, w := range m.Works {
|
for i, w := range m.Works {
|
||||||
if w.ID == entity.ID {
|
if w.ID == entity.ID {
|
||||||
m.Works[i] = entity
|
m.Works[i] = entity
|
||||||
@ -76,7 +77,7 @@ func (m *MockWorkRepository) Update(ctx context.Context, entity *domain.Work) er
|
|||||||
}
|
}
|
||||||
return gorm.ErrRecordNotFound
|
return gorm.ErrRecordNotFound
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Work) error {
|
func (m *MockWorkRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *work.Work) error {
|
||||||
return m.Update(ctx, entity)
|
return m.Update(ctx, entity)
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) Delete(ctx context.Context, id uint) error {
|
func (m *MockWorkRepository) Delete(ctx context.Context, id uint) error {
|
||||||
@ -91,14 +92,14 @@ func (m *MockWorkRepository) Delete(ctx context.Context, id uint) error {
|
|||||||
func (m *MockWorkRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uint) error {
|
func (m *MockWorkRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uint) error {
|
||||||
return m.Delete(ctx, id)
|
return m.Delete(ctx, id)
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
func (m *MockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Work, error) {
|
func (m *MockWorkRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]work.Work, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) ListAll(ctx context.Context) ([]domain.Work, error) {
|
func (m *MockWorkRepository) ListAll(ctx context.Context) ([]work.Work, error) {
|
||||||
var works []domain.Work
|
var works []work.Work
|
||||||
for _, w := range m.Works {
|
for _, w := range m.Works {
|
||||||
works = append(works, *w)
|
works = append(works, *w)
|
||||||
}
|
}
|
||||||
@ -110,10 +111,10 @@ func (m *MockWorkRepository) Count(ctx context.Context) (int64, error) {
|
|||||||
func (m *MockWorkRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
func (m *MockWorkRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Work, error) {
|
func (m *MockWorkRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*work.Work, error) {
|
||||||
return m.GetByID(ctx, id)
|
return m.GetByID(ctx, id)
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Work, error) {
|
func (m *MockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]work.Work, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
func (m *MockWorkRepository) BeginTx(ctx context.Context) (*gorm.DB, error) {
|
func (m *MockWorkRepository) BeginTx(ctx context.Context) (*gorm.DB, error) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"tercul/internal/app/work"
|
"tercul/internal/app/work"
|
||||||
"tercul/internal/domain"
|
"tercul/internal/domain"
|
||||||
domain_localization "tercul/internal/domain/localization"
|
domain_localization "tercul/internal/domain/localization"
|
||||||
|
domain_work "tercul/internal/domain/work"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
@ -24,7 +25,7 @@ type SimpleTestSuite struct {
|
|||||||
type MockSearchClient struct{}
|
type MockSearchClient struct{}
|
||||||
|
|
||||||
// IndexWork is the mock implementation of the IndexWork method.
|
// IndexWork is the mock implementation of the IndexWork method.
|
||||||
func (m *MockSearchClient) IndexWork(ctx context.Context, work *domain.Work, pipeline string) error {
|
func (m *MockSearchClient) IndexWork(ctx context.Context, work *domain_work.Work, pipeline string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,8 +75,8 @@ func (s *SimpleTestSuite) GetResolver() *graph.Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateTestWork creates a test work with optional content
|
// CreateTestWork creates a test work with optional content
|
||||||
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain_work.Work {
|
||||||
work := &domain.Work{
|
work := &domain_work.Work{
|
||||||
Title: title,
|
Title: title,
|
||||||
TranslatableModel: domain.TranslatableModel{Language: language},
|
TranslatableModel: domain.TranslatableModel{Language: language},
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user