diff --git a/internal/app/app.go b/internal/app/app.go index ae0a7bc..e3c5a02 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -4,6 +4,7 @@ import ( "tercul/internal/app/auth" "tercul/internal/app/copyright" "tercul/internal/app/localization" + "tercul/internal/app/monetization" "tercul/internal/app/search" "tercul/internal/app/work" "tercul/internal/domain" @@ -26,4 +27,8 @@ type Application struct { UserRepo domain.UserRepository TagRepo domain.TagRepository CategoryRepo domain.CategoryRepository + BookRepo domain.BookRepository + PublisherRepo domain.PublisherRepository + SourceRepo domain.SourceRepository + MonetizationQueries *monetization.MonetizationQueries } diff --git a/internal/app/application_builder.go b/internal/app/application_builder.go index bb28709..9cb24c4 100644 --- a/internal/app/application_builder.go +++ b/internal/app/application_builder.go @@ -4,6 +4,7 @@ import ( "tercul/internal/app/auth" "tercul/internal/app/copyright" "tercul/internal/app/localization" + "tercul/internal/app/monetization" "tercul/internal/app/search" "tercul/internal/app/work" "tercul/internal/data/sql" @@ -122,7 +123,10 @@ func (b *ApplicationBuilder) BuildApplication() error { authQueries := auth.NewAuthQueries(userRepo, jwtManager) copyrightCommands := copyright.NewCopyrightCommands(copyrightRepo) - copyrightQueries := copyright.NewCopyrightQueries(copyrightRepo) + bookRepo := sql.NewBookRepository(b.dbConn) + publisherRepo := sql.NewPublisherRepository(b.dbConn) + sourceRepo := sql.NewSourceRepository(b.dbConn) + copyrightQueries := copyright.NewCopyrightQueries(copyrightRepo, workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo) localizationService := localization.NewService(translationRepo) @@ -141,6 +145,10 @@ func (b *ApplicationBuilder) BuildApplication() error { UserRepo: userRepo, TagRepo: tagRepo, CategoryRepo: categoryRepo, + BookRepo: sql.NewBookRepository(b.dbConn), + PublisherRepo: sql.NewPublisherRepository(b.dbConn), + SourceRepo: sql.NewSourceRepository(b.dbConn), + MonetizationQueries: monetization.NewMonetizationQueries(sql.NewMonetizationRepository(b.dbConn), workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo), } log.LogInfo("Application layer initialized successfully") diff --git a/internal/app/copyright/commands.go b/internal/app/copyright/commands.go index 261a282..991de27 100644 --- a/internal/app/copyright/commands.go +++ b/internal/app/copyright/commands.go @@ -55,26 +55,85 @@ func (c *CopyrightCommands) DeleteCopyright(ctx context.Context, id uint) error return c.repo.Delete(ctx, id) } -// AttachCopyrightToEntity attaches a copyright to any entity type. -func (c *CopyrightCommands) AttachCopyrightToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error { - if copyrightID == 0 || entityID == 0 { - return errors.New("invalid copyright ID or entity ID") + +// AddCopyrightToWork adds a copyright to a work. +func (c *CopyrightCommands) AddCopyrightToWork(ctx context.Context, workID uint, copyrightID uint) error { + if workID == 0 || copyrightID == 0 { + return errors.New("invalid work ID or copyright ID") } - if entityType == "" { - return errors.New("entity type cannot be empty") - } - return c.repo.AttachToEntity(ctx, copyrightID, entityID, entityType) + return c.repo.AddCopyrightToWork(ctx, workID, copyrightID) } -// DetachCopyrightFromEntity removes a copyright from an entity. -func (c *CopyrightCommands) DetachCopyrightFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error { - if copyrightID == 0 || entityID == 0 { - return errors.New("invalid copyright ID or entity ID") +// RemoveCopyrightFromWork removes a copyright from a work. +func (c *CopyrightCommands) RemoveCopyrightFromWork(ctx context.Context, workID uint, copyrightID uint) error { + if workID == 0 || copyrightID == 0 { + return errors.New("invalid work ID or copyright ID") } - if entityType == "" { - return errors.New("entity type cannot be empty") + return c.repo.RemoveCopyrightFromWork(ctx, workID, copyrightID) +} + +// AddCopyrightToAuthor adds a copyright to an author. +func (c *CopyrightCommands) AddCopyrightToAuthor(ctx context.Context, authorID uint, copyrightID uint) error { + if authorID == 0 || copyrightID == 0 { + return errors.New("invalid author ID or copyright ID") } - return c.repo.DetachFromEntity(ctx, copyrightID, entityID, entityType) + return c.repo.AddCopyrightToAuthor(ctx, authorID, copyrightID) +} + +// RemoveCopyrightFromAuthor removes a copyright from an author. +func (c *CopyrightCommands) RemoveCopyrightFromAuthor(ctx context.Context, authorID uint, copyrightID uint) error { + if authorID == 0 || copyrightID == 0 { + return errors.New("invalid author ID or copyright ID") + } + return c.repo.RemoveCopyrightFromAuthor(ctx, authorID, copyrightID) +} + +// AddCopyrightToBook adds a copyright to a book. +func (c *CopyrightCommands) AddCopyrightToBook(ctx context.Context, bookID uint, copyrightID uint) error { + if bookID == 0 || copyrightID == 0 { + return errors.New("invalid book ID or copyright ID") + } + return c.repo.AddCopyrightToBook(ctx, bookID, copyrightID) +} + +// RemoveCopyrightFromBook removes a copyright from a book. +func (c *CopyrightCommands) RemoveCopyrightFromBook(ctx context.Context, bookID uint, copyrightID uint) error { + if bookID == 0 || copyrightID == 0 { + return errors.New("invalid book ID or copyright ID") + } + return c.repo.RemoveCopyrightFromBook(ctx, bookID, copyrightID) +} + +// AddCopyrightToPublisher adds a copyright to a publisher. +func (c *CopyrightCommands) AddCopyrightToPublisher(ctx context.Context, publisherID uint, copyrightID uint) error { + if publisherID == 0 || copyrightID == 0 { + return errors.New("invalid publisher ID or copyright ID") + } + return c.repo.AddCopyrightToPublisher(ctx, publisherID, copyrightID) +} + +// RemoveCopyrightFromPublisher removes a copyright from a publisher. +func (c *CopyrightCommands) RemoveCopyrightFromPublisher(ctx context.Context, publisherID uint, copyrightID uint) error { + if publisherID == 0 || copyrightID == 0 { + return errors.New("invalid publisher ID or copyright ID") + } + return c.repo.RemoveCopyrightFromPublisher(ctx, publisherID, copyrightID) +} + +// AddCopyrightToSource adds a copyright to a source. +func (c *CopyrightCommands) AddCopyrightToSource(ctx context.Context, sourceID uint, copyrightID uint) error { + if sourceID == 0 || copyrightID == 0 { + return errors.New("invalid source ID or copyright ID") + } + return c.repo.AddCopyrightToSource(ctx, sourceID, copyrightID) +} + +// RemoveCopyrightFromSource removes a copyright from a source. +func (c *CopyrightCommands) RemoveCopyrightFromSource(ctx context.Context, sourceID uint, copyrightID uint) error { + if sourceID == 0 || copyrightID == 0 { + return errors.New("invalid source ID or copyright ID") + } + return c.repo.RemoveCopyrightFromSource(ctx, sourceID, copyrightID) } // AddTranslation adds a translation to a copyright. diff --git a/internal/app/copyright/commands_test.go b/internal/app/copyright/commands_test.go new file mode 100644 index 0000000..72f7402 --- /dev/null +++ b/internal/app/copyright/commands_test.go @@ -0,0 +1,237 @@ +package copyright_test + +import ( + "context" + "testing" + "tercul/internal/app/copyright" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type CopyrightCommandsTestSuite struct { + testutil.IntegrationTestSuite + commands *copyright.CopyrightCommands +} + +func (s *CopyrightCommandsTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) + s.commands = copyright.NewCopyrightCommands(s.CopyrightRepo) +} + +func (s *CopyrightCommandsTestSuite) TestAddCopyrightToWork() { + s.Run("should add a copyright to a work", func() { + // Arrange + work := s.CreateTestWork("Test Work", "en", "Test content") + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + + // Act + err := s.commands.AddCopyrightToWork(context.Background(), work.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + + // Verify that the association was created in the database + var foundWork domain.Work + err = s.DB.Preload("Copyrights").First(&foundWork, work.ID).Error + s.Require().NoError(err) + s.Require().Len(foundWork.Copyrights, 1) + s.Equal(copyright.ID, foundWork.Copyrights[0].ID) + }) +} + +func (s *CopyrightCommandsTestSuite) TestRemoveCopyrightFromWork() { + s.Run("should remove a copyright from a work", func() { + // Arrange + work := s.CreateTestWork("Test Work", "en", "Test content") + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + s.Require().NoError(s.commands.AddCopyrightToWork(context.Background(), work.ID, copyright.ID)) + + // Act + err := s.commands.RemoveCopyrightFromWork(context.Background(), work.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + + // Verify that the association was removed from the database + var foundWork domain.Work + err = s.DB.Preload("Copyrights").First(&foundWork, work.ID).Error + s.Require().NoError(err) + s.Require().Len(foundWork.Copyrights, 0) + }) +} + +func (s *CopyrightCommandsTestSuite) TestAddCopyrightToAuthor() { + s.Run("should add a copyright to an author", func() { + // Arrange + author := &domain.Author{Name: "Test Author"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + + // Act + err := s.commands.AddCopyrightToAuthor(context.Background(), author.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundAuthor domain.Author + err = s.DB.Preload("Copyrights").First(&foundAuthor, author.ID).Error + s.Require().NoError(err) + s.Require().Len(foundAuthor.Copyrights, 1) + s.Equal(copyright.ID, foundAuthor.Copyrights[0].ID) + }) +} + +func (s *CopyrightCommandsTestSuite) TestRemoveCopyrightFromAuthor() { + s.Run("should remove a copyright from an author", func() { + // Arrange + author := &domain.Author{Name: "Test Author"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + s.Require().NoError(s.commands.AddCopyrightToAuthor(context.Background(), author.ID, copyright.ID)) + + // Act + err := s.commands.RemoveCopyrightFromAuthor(context.Background(), author.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundAuthor domain.Author + err = s.DB.Preload("Copyrights").First(&foundAuthor, author.ID).Error + s.Require().NoError(err) + s.Require().Len(foundAuthor.Copyrights, 0) + }) +} + +func (s *CopyrightCommandsTestSuite) TestAddCopyrightToBook() { + s.Run("should add a copyright to a book", func() { + // Arrange + book := &domain.Book{Title: "Test Book"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + + // Act + err := s.commands.AddCopyrightToBook(context.Background(), book.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundBook domain.Book + err = s.DB.Preload("Copyrights").First(&foundBook, book.ID).Error + s.Require().NoError(err) + s.Require().Len(foundBook.Copyrights, 1) + s.Equal(copyright.ID, foundBook.Copyrights[0].ID) + }) +} + +func (s *CopyrightCommandsTestSuite) TestRemoveCopyrightFromBook() { + s.Run("should remove a copyright from a book", func() { + // Arrange + book := &domain.Book{Title: "Test Book"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + s.Require().NoError(s.commands.AddCopyrightToBook(context.Background(), book.ID, copyright.ID)) + + // Act + err := s.commands.RemoveCopyrightFromBook(context.Background(), book.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundBook domain.Book + err = s.DB.Preload("Copyrights").First(&foundBook, book.ID).Error + s.Require().NoError(err) + s.Require().Len(foundBook.Copyrights, 0) + }) +} + +func (s *CopyrightCommandsTestSuite) TestAddCopyrightToPublisher() { + s.Run("should add a copyright to a publisher", func() { + // Arrange + publisher := &domain.Publisher{Name: "Test Publisher"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + + // Act + err := s.commands.AddCopyrightToPublisher(context.Background(), publisher.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundPublisher domain.Publisher + err = s.DB.Preload("Copyrights").First(&foundPublisher, publisher.ID).Error + s.Require().NoError(err) + s.Require().Len(foundPublisher.Copyrights, 1) + s.Equal(copyright.ID, foundPublisher.Copyrights[0].ID) + }) +} + +func (s *CopyrightCommandsTestSuite) TestRemoveCopyrightFromPublisher() { + s.Run("should remove a copyright from a publisher", func() { + // Arrange + publisher := &domain.Publisher{Name: "Test Publisher"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + s.Require().NoError(s.commands.AddCopyrightToPublisher(context.Background(), publisher.ID, copyright.ID)) + + // Act + err := s.commands.RemoveCopyrightFromPublisher(context.Background(), publisher.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundPublisher domain.Publisher + err = s.DB.Preload("Copyrights").First(&foundPublisher, publisher.ID).Error + s.Require().NoError(err) + s.Require().Len(foundPublisher.Copyrights, 0) + }) +} + +func (s *CopyrightCommandsTestSuite) TestAddCopyrightToSource() { + s.Run("should add a copyright to a source", func() { + // Arrange + source := &domain.Source{Name: "Test Source"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + + // Act + err := s.commands.AddCopyrightToSource(context.Background(), source.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundSource domain.Source + err = s.DB.Preload("Copyrights").First(&foundSource, source.ID).Error + s.Require().NoError(err) + s.Require().Len(foundSource.Copyrights, 1) + s.Equal(copyright.ID, foundSource.Copyrights[0].ID) + }) +} + +func (s *CopyrightCommandsTestSuite) TestRemoveCopyrightFromSource() { + s.Run("should remove a copyright from a source", func() { + // Arrange + source := &domain.Source{Name: "Test Source"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + copyright := &domain.Copyright{Name: "Test Copyright", Identificator: "TC-123"} + s.Require().NoError(s.CopyrightRepo.Create(context.Background(), copyright)) + s.Require().NoError(s.commands.AddCopyrightToSource(context.Background(), source.ID, copyright.ID)) + + // Act + err := s.commands.RemoveCopyrightFromSource(context.Background(), source.ID, copyright.ID) + + // Assert + s.Require().NoError(err) + var foundSource domain.Source + err = s.DB.Preload("Copyrights").First(&foundSource, source.ID).Error + s.Require().NoError(err) + s.Require().Len(foundSource.Copyrights, 0) + }) +} + +func TestCopyrightCommands(t *testing.T) { + suite.Run(t, new(CopyrightCommandsTestSuite)) +} diff --git a/internal/app/copyright/queries.go b/internal/app/copyright/queries.go index a91c797..b2660d6 100644 --- a/internal/app/copyright/queries.go +++ b/internal/app/copyright/queries.go @@ -8,12 +8,17 @@ import ( // CopyrightQueries contains the query handlers for copyright. type CopyrightQueries struct { - repo domain.CopyrightRepository + repo domain.CopyrightRepository + workRepo domain.WorkRepository + authorRepo domain.AuthorRepository + bookRepo domain.BookRepository + publisherRepo domain.PublisherRepository + sourceRepo domain.SourceRepository } // NewCopyrightQueries creates a new CopyrightQueries handler. -func NewCopyrightQueries(repo domain.CopyrightRepository) *CopyrightQueries { - return &CopyrightQueries{repo: repo} +func NewCopyrightQueries(repo domain.CopyrightRepository, workRepo domain.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} } // GetCopyrightByID retrieves a copyright by ID. @@ -31,23 +36,51 @@ func (q *CopyrightQueries) ListCopyrights(ctx context.Context) ([]domain.Copyrig return q.repo.ListAll(ctx) } -// GetCopyrightsForEntity gets all copyrights for a specific entity. -func (q *CopyrightQueries) GetCopyrightsForEntity(ctx context.Context, entityID uint, entityType string) ([]domain.Copyright, error) { - if entityID == 0 { - return nil, errors.New("invalid entity ID") + + +// GetCopyrightsForWork gets all copyrights for a specific work. +func (q *CopyrightQueries) GetCopyrightsForWork(ctx context.Context, workID uint) ([]*domain.Copyright, error) { + work, err := q.workRepo.GetByIDWithOptions(ctx, workID, &domain.QueryOptions{Preloads: []string{"Copyrights"}}) + if err != nil { + return nil, err } - if entityType == "" { - return nil, errors.New("entity type cannot be empty") - } - return q.repo.GetByEntity(ctx, entityID, entityType) + return work.Copyrights, nil } -// GetEntitiesByCopyright gets all entities that have a specific copyright. -func (q *CopyrightQueries) GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]domain.Copyrightable, error) { - if copyrightID == 0 { - return nil, errors.New("invalid copyright ID") +// GetCopyrightsForAuthor gets all copyrights for a specific author. +func (q *CopyrightQueries) GetCopyrightsForAuthor(ctx context.Context, authorID uint) ([]*domain.Copyright, error) { + author, err := q.authorRepo.GetByIDWithOptions(ctx, authorID, &domain.QueryOptions{Preloads: []string{"Copyrights"}}) + if err != nil { + return nil, err } - return q.repo.GetEntitiesByCopyright(ctx, copyrightID) + return author.Copyrights, nil +} + +// GetCopyrightsForBook gets all copyrights for a specific book. +func (q *CopyrightQueries) GetCopyrightsForBook(ctx context.Context, bookID uint) ([]*domain.Copyright, error) { + book, err := q.bookRepo.GetByIDWithOptions(ctx, bookID, &domain.QueryOptions{Preloads: []string{"Copyrights"}}) + if err != nil { + return nil, err + } + return book.Copyrights, nil +} + +// GetCopyrightsForPublisher gets all copyrights for a specific publisher. +func (q *CopyrightQueries) GetCopyrightsForPublisher(ctx context.Context, publisherID uint) ([]*domain.Copyright, error) { + publisher, err := q.publisherRepo.GetByIDWithOptions(ctx, publisherID, &domain.QueryOptions{Preloads: []string{"Copyrights"}}) + if err != nil { + return nil, err + } + return publisher.Copyrights, nil +} + +// GetCopyrightsForSource gets all copyrights for a specific source. +func (q *CopyrightQueries) GetCopyrightsForSource(ctx context.Context, sourceID uint) ([]*domain.Copyright, error) { + source, err := q.sourceRepo.GetByIDWithOptions(ctx, sourceID, &domain.QueryOptions{Preloads: []string{"Copyrights"}}) + if err != nil { + return nil, err + } + return source.Copyrights, nil } // GetTranslations gets all translations for a copyright. diff --git a/internal/app/monetization/commands.go b/internal/app/monetization/commands.go new file mode 100644 index 0000000..939ee55 --- /dev/null +++ b/internal/app/monetization/commands.go @@ -0,0 +1,89 @@ +package monetization + +import ( + "context" + "errors" + "tercul/internal/domain" +) + +// MonetizationCommands contains the command handlers for monetization. +type MonetizationCommands struct { + repo domain.MonetizationRepository +} + +// NewMonetizationCommands creates a new MonetizationCommands handler. +func NewMonetizationCommands(repo domain.MonetizationRepository) *MonetizationCommands { + return &MonetizationCommands{repo: repo} +} + +// AddMonetizationToWork adds a monetization to a work. +func (c *MonetizationCommands) AddMonetizationToWork(ctx context.Context, workID uint, monetizationID uint) error { + if workID == 0 || monetizationID == 0 { + return errors.New("invalid work ID or monetization ID") + } + return c.repo.AddMonetizationToWork(ctx, workID, monetizationID) +} + +// RemoveMonetizationFromWork removes a monetization from a work. +func (c *MonetizationCommands) RemoveMonetizationFromWork(ctx context.Context, workID uint, monetizationID uint) error { + if workID == 0 || monetizationID == 0 { + return errors.New("invalid work ID or monetization ID") + } + return c.repo.RemoveMonetizationFromWork(ctx, workID, monetizationID) +} + +func (c *MonetizationCommands) AddMonetizationToAuthor(ctx context.Context, authorID uint, monetizationID uint) error { + if authorID == 0 || monetizationID == 0 { + return errors.New("invalid author ID or monetization ID") + } + return c.repo.AddMonetizationToAuthor(ctx, authorID, monetizationID) +} + +func (c *MonetizationCommands) RemoveMonetizationFromAuthor(ctx context.Context, authorID uint, monetizationID uint) error { + if authorID == 0 || monetizationID == 0 { + return errors.New("invalid author ID or monetization ID") + } + return c.repo.RemoveMonetizationFromAuthor(ctx, authorID, monetizationID) +} + +func (c *MonetizationCommands) AddMonetizationToBook(ctx context.Context, bookID uint, monetizationID uint) error { + if bookID == 0 || monetizationID == 0 { + return errors.New("invalid book ID or monetization ID") + } + return c.repo.AddMonetizationToBook(ctx, bookID, monetizationID) +} + +func (c *MonetizationCommands) RemoveMonetizationFromBook(ctx context.Context, bookID uint, monetizationID uint) error { + if bookID == 0 || monetizationID == 0 { + return errors.New("invalid book ID or monetization ID") + } + return c.repo.RemoveMonetizationFromBook(ctx, bookID, monetizationID) +} + +func (c *MonetizationCommands) AddMonetizationToPublisher(ctx context.Context, publisherID uint, monetizationID uint) error { + if publisherID == 0 || monetizationID == 0 { + return errors.New("invalid publisher ID or monetization ID") + } + return c.repo.AddMonetizationToPublisher(ctx, publisherID, monetizationID) +} + +func (c *MonetizationCommands) RemoveMonetizationFromPublisher(ctx context.Context, publisherID uint, monetizationID uint) error { + if publisherID == 0 || monetizationID == 0 { + return errors.New("invalid publisher ID or monetization ID") + } + return c.repo.RemoveMonetizationFromPublisher(ctx, publisherID, monetizationID) +} + +func (c *MonetizationCommands) AddMonetizationToSource(ctx context.Context, sourceID uint, monetizationID uint) error { + if sourceID == 0 || monetizationID == 0 { + return errors.New("invalid source ID or monetization ID") + } + return c.repo.AddMonetizationToSource(ctx, sourceID, monetizationID) +} + +func (c *MonetizationCommands) RemoveMonetizationFromSource(ctx context.Context, sourceID uint, monetizationID uint) error { + if sourceID == 0 || monetizationID == 0 { + return errors.New("invalid source ID or monetization ID") + } + return c.repo.RemoveMonetizationFromSource(ctx, sourceID, monetizationID) +} diff --git a/internal/app/monetization/commands_test.go b/internal/app/monetization/commands_test.go new file mode 100644 index 0000000..58f9115 --- /dev/null +++ b/internal/app/monetization/commands_test.go @@ -0,0 +1,215 @@ +package monetization_test + +import ( + "context" + "testing" + "tercul/internal/app/monetization" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type MonetizationCommandsTestSuite struct { + testutil.IntegrationTestSuite + commands *monetization.MonetizationCommands +} + +func (s *MonetizationCommandsTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) + s.commands = monetization.NewMonetizationCommands(s.MonetizationRepo) +} + +func (s *MonetizationCommandsTestSuite) TestAddMonetizationToWork() { + s.Run("should add a monetization to a work", func() { + // Arrange + work := s.CreateTestWork("Test Work", "en", "Test content") + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + + // Act + err := s.commands.AddMonetizationToWork(context.Background(), work.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + + // Verify that the association was created in the database + var foundWork domain.Work + err = s.DB.Preload("Monetizations").First(&foundWork, work.ID).Error + s.Require().NoError(err) + s.Require().Len(foundWork.Monetizations, 1) + s.Equal(monetization.ID, foundWork.Monetizations[0].ID) + }) +} + +func (s *MonetizationCommandsTestSuite) TestAddMonetizationToAuthor() { + s.Run("should add a monetization to an author", func() { + // Arrange + author := &domain.Author{Name: "Test Author"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + + // Act + err := s.commands.AddMonetizationToAuthor(context.Background(), author.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundAuthor domain.Author + err = s.DB.Preload("Monetizations").First(&foundAuthor, author.ID).Error + s.Require().NoError(err) + s.Require().Len(foundAuthor.Monetizations, 1) + s.Equal(monetization.ID, foundAuthor.Monetizations[0].ID) + }) +} + +func (s *MonetizationCommandsTestSuite) TestRemoveMonetizationFromAuthor() { + s.Run("should remove a monetization from an author", func() { + // Arrange + author := &domain.Author{Name: "Test Author"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + s.Require().NoError(s.commands.AddMonetizationToAuthor(context.Background(), author.ID, monetization.ID)) + + // Act + err := s.commands.RemoveMonetizationFromAuthor(context.Background(), author.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundAuthor domain.Author + err = s.DB.Preload("Monetizations").First(&foundAuthor, author.ID).Error + s.Require().NoError(err) + s.Require().Len(foundAuthor.Monetizations, 0) + }) +} + +func (s *MonetizationCommandsTestSuite) TestAddMonetizationToBook() { + s.Run("should add a monetization to a book", func() { + // Arrange + book := &domain.Book{Title: "Test Book"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + + // Act + err := s.commands.AddMonetizationToBook(context.Background(), book.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundBook domain.Book + err = s.DB.Preload("Monetizations").First(&foundBook, book.ID).Error + s.Require().NoError(err) + s.Require().Len(foundBook.Monetizations, 1) + s.Equal(monetization.ID, foundBook.Monetizations[0].ID) + }) +} + +func (s *MonetizationCommandsTestSuite) TestRemoveMonetizationFromBook() { + s.Run("should remove a monetization from a book", func() { + // Arrange + book := &domain.Book{Title: "Test Book"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + s.Require().NoError(s.commands.AddMonetizationToBook(context.Background(), book.ID, monetization.ID)) + + // Act + err := s.commands.RemoveMonetizationFromBook(context.Background(), book.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundBook domain.Book + err = s.DB.Preload("Monetizations").First(&foundBook, book.ID).Error + s.Require().NoError(err) + s.Require().Len(foundBook.Monetizations, 0) + }) +} + +func (s *MonetizationCommandsTestSuite) TestAddMonetizationToPublisher() { + s.Run("should add a monetization to a publisher", func() { + // Arrange + publisher := &domain.Publisher{Name: "Test Publisher"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + + // Act + err := s.commands.AddMonetizationToPublisher(context.Background(), publisher.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundPublisher domain.Publisher + err = s.DB.Preload("Monetizations").First(&foundPublisher, publisher.ID).Error + s.Require().NoError(err) + s.Require().Len(foundPublisher.Monetizations, 1) + s.Equal(monetization.ID, foundPublisher.Monetizations[0].ID) + }) +} + +func (s *MonetizationCommandsTestSuite) TestRemoveMonetizationFromPublisher() { + s.Run("should remove a monetization from a publisher", func() { + // Arrange + publisher := &domain.Publisher{Name: "Test Publisher"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + s.Require().NoError(s.commands.AddMonetizationToPublisher(context.Background(), publisher.ID, monetization.ID)) + + // Act + err := s.commands.RemoveMonetizationFromPublisher(context.Background(), publisher.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundPublisher domain.Publisher + err = s.DB.Preload("Monetizations").First(&foundPublisher, publisher.ID).Error + s.Require().NoError(err) + s.Require().Len(foundPublisher.Monetizations, 0) + }) +} + +func (s *MonetizationCommandsTestSuite) TestAddMonetizationToSource() { + s.Run("should add a monetization to a source", func() { + // Arrange + source := &domain.Source{Name: "Test Source"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + + // Act + err := s.commands.AddMonetizationToSource(context.Background(), source.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundSource domain.Source + err = s.DB.Preload("Monetizations").First(&foundSource, source.ID).Error + s.Require().NoError(err) + s.Require().Len(foundSource.Monetizations, 1) + s.Equal(monetization.ID, foundSource.Monetizations[0].ID) + }) +} + +func (s *MonetizationCommandsTestSuite) TestRemoveMonetizationFromSource() { + s.Run("should remove a monetization from a source", func() { + // Arrange + source := &domain.Source{Name: "Test Source"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + s.Require().NoError(s.commands.AddMonetizationToSource(context.Background(), source.ID, monetization.ID)) + + // Act + err := s.commands.RemoveMonetizationFromSource(context.Background(), source.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + var foundSource domain.Source + err = s.DB.Preload("Monetizations").First(&foundSource, source.ID).Error + s.Require().NoError(err) + s.Require().Len(foundSource.Monetizations, 0) + }) +} + +func TestMonetizationCommands(t *testing.T) { + suite.Run(t, new(MonetizationCommandsTestSuite)) +} diff --git a/internal/app/monetization/queries.go b/internal/app/monetization/queries.go new file mode 100644 index 0000000..4e5f57f --- /dev/null +++ b/internal/app/monetization/queries.go @@ -0,0 +1,75 @@ +package monetization + +import ( + "context" + "errors" + "tercul/internal/domain" +) + +// MonetizationQueries contains the query handlers for monetization. +type MonetizationQueries struct { + repo domain.MonetizationRepository + workRepo domain.WorkRepository + authorRepo domain.AuthorRepository + bookRepo domain.BookRepository + publisherRepo domain.PublisherRepository + sourceRepo domain.SourceRepository +} + +// 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 { + return &MonetizationQueries{repo: repo, workRepo: workRepo, authorRepo: authorRepo, bookRepo: bookRepo, publisherRepo: publisherRepo, sourceRepo: sourceRepo} +} + +// GetMonetizationByID retrieves a monetization by ID. +func (q *MonetizationQueries) GetMonetizationByID(ctx context.Context, id uint) (*domain.Monetization, error) { + if id == 0 { + return nil, errors.New("invalid monetization ID") + } + return q.repo.GetByID(ctx, id) +} + +// ListMonetizations retrieves all monetizations. +func (q *MonetizationQueries) ListMonetizations(ctx context.Context) ([]domain.Monetization, error) { + return q.repo.ListAll(ctx) +} + +func (q *MonetizationQueries) GetMonetizationsForWork(ctx context.Context, workID uint) ([]*domain.Monetization, error) { + work, err := q.workRepo.GetByIDWithOptions(ctx, workID, &domain.QueryOptions{Preloads: []string{"Monetizations"}}) + if err != nil { + return nil, err + } + return work.Monetizations, nil +} + +func (q *MonetizationQueries) GetMonetizationsForAuthor(ctx context.Context, authorID uint) ([]*domain.Monetization, error) { + author, err := q.authorRepo.GetByIDWithOptions(ctx, authorID, &domain.QueryOptions{Preloads: []string{"Monetizations"}}) + if err != nil { + return nil, err + } + return author.Monetizations, nil +} + +func (q *MonetizationQueries) GetMonetizationsForBook(ctx context.Context, bookID uint) ([]*domain.Monetization, error) { + book, err := q.bookRepo.GetByIDWithOptions(ctx, bookID, &domain.QueryOptions{Preloads: []string{"Monetizations"}}) + if err != nil { + return nil, err + } + return book.Monetizations, nil +} + +func (q *MonetizationQueries) GetMonetizationsForPublisher(ctx context.Context, publisherID uint) ([]*domain.Monetization, error) { + publisher, err := q.publisherRepo.GetByIDWithOptions(ctx, publisherID, &domain.QueryOptions{Preloads: []string{"Monetizations"}}) + if err != nil { + return nil, err + } + return publisher.Monetizations, nil +} + +func (q *MonetizationQueries) GetMonetizationsForSource(ctx context.Context, sourceID uint) ([]*domain.Monetization, error) { + source, err := q.sourceRepo.GetByIDWithOptions(ctx, sourceID, &domain.QueryOptions{Preloads: []string{"Monetizations"}}) + if err != nil { + return nil, err + } + return source.Monetizations, nil +} diff --git a/internal/data/sql/author_repository.go b/internal/data/sql/author_repository.go index 8394e9a..b8cf5e1 100644 --- a/internal/data/sql/author_repository.go +++ b/internal/data/sql/author_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/author" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type authorRepository struct { } // NewAuthorRepository creates a new AuthorRepository. -func NewAuthorRepository(db *gorm.DB) author.AuthorRepository { +func NewAuthorRepository(db *gorm.DB) domain.AuthorRepository { return &authorRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Author](db), db: db, diff --git a/internal/data/sql/author_repository_test.go b/internal/data/sql/author_repository_test.go new file mode 100644 index 0000000..ef3c44c --- /dev/null +++ b/internal/data/sql/author_repository_test.go @@ -0,0 +1,120 @@ +package sql_test + +import ( + "context" + "testing" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type AuthorRepositoryTestSuite struct { + testutil.IntegrationTestSuite +} + +func (s *AuthorRepositoryTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) +} + +func (s *AuthorRepositoryTestSuite) TestCreateAuthor() { + s.Run("should create a new author", func() { + // Arrange + author := &domain.Author{ + Name: "New Test Author", + TranslatableModel: domain.TranslatableModel{ + Language: "en", + }, + } + + // Act + err := s.AuthorRepo.Create(context.Background(), author) + + // Assert + s.Require().NoError(err) + s.NotZero(author.ID) + + // Verify that the author was actually created in the database + var foundAuthor domain.Author + err = s.DB.First(&foundAuthor, author.ID).Error + s.Require().NoError(err) + s.Equal("New Test Author", foundAuthor.Name) + s.Equal("en", foundAuthor.Language) + }) +} + +func (s *AuthorRepositoryTestSuite) TestGetAuthorByID() { + s.Run("should return an author by ID", func() { + // Arrange + author := &domain.Author{Name: "Test Author"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + + // Act + foundAuthor, err := s.AuthorRepo.GetByID(context.Background(), author.ID) + + // Assert + s.Require().NoError(err) + s.Require().NotNil(foundAuthor) + s.Equal(author.ID, foundAuthor.ID) + s.Equal("Test Author", foundAuthor.Name) + }) +} + +func (s *AuthorRepositoryTestSuite) TestUpdateAuthor() { + s.Run("should update an existing author", func() { + // Arrange + author := &domain.Author{Name: "Original Name"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + author.Name = "Updated Name" + + // Act + err := s.AuthorRepo.Update(context.Background(), author) + + // Assert + s.Require().NoError(err) + var foundAuthor domain.Author + err = s.DB.First(&foundAuthor, author.ID).Error + s.Require().NoError(err) + s.Equal("Updated Name", foundAuthor.Name) + }) +} + +func (s *AuthorRepositoryTestSuite) TestDeleteAuthor() { + s.Run("should delete an existing author", func() { + // Arrange + author := &domain.Author{Name: "To Be Deleted"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author)) + + // Act + err := s.AuthorRepo.Delete(context.Background(), author.ID) + + // Assert + s.Require().NoError(err) + var foundAuthor domain.Author + err = s.DB.First(&foundAuthor, author.ID).Error + s.Require().Error(err) + }) +} + +func (s *AuthorRepositoryTestSuite) TestListByWorkID() { + s.Run("should return all authors for a given work", func() { + // Arrange + work := s.CreateTestWork("Test Work", "en", "Test content") + author1 := &domain.Author{Name: "Author 1"} + author2 := &domain.Author{Name: "Author 2"} + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author1)) + s.Require().NoError(s.AuthorRepo.Create(context.Background(), author2)) + s.Require().NoError(s.DB.Model(&work).Association("Authors").Append([]*domain.Author{author1, author2})) + + // Act + authors, err := s.AuthorRepo.ListByWorkID(context.Background(), work.ID) + + // Assert + s.Require().NoError(err) + s.Len(authors, 2) + }) +} + +func TestAuthorRepository(t *testing.T) { + suite.Run(t, new(AuthorRepositoryTestSuite)) +} diff --git a/internal/data/sql/book_repository.go b/internal/data/sql/book_repository.go index 6538c5f..6e1dbf2 100644 --- a/internal/data/sql/book_repository.go +++ b/internal/data/sql/book_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/book" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type bookRepository struct { } // NewBookRepository creates a new BookRepository. -func NewBookRepository(db *gorm.DB) book.BookRepository { +func NewBookRepository(db *gorm.DB) domain.BookRepository { return &bookRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Book](db), db: db, diff --git a/internal/data/sql/book_repository_test.go b/internal/data/sql/book_repository_test.go new file mode 100644 index 0000000..be8ff6a --- /dev/null +++ b/internal/data/sql/book_repository_test.go @@ -0,0 +1,117 @@ +package sql_test + +import ( + "context" + "testing" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type BookRepositoryTestSuite struct { + testutil.IntegrationTestSuite +} + +func (s *BookRepositoryTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) +} + +func (s *BookRepositoryTestSuite) TestCreateBook() { + s.Run("should create a new book", func() { + // Arrange + book := &domain.Book{ + Title: "New Test Book", + TranslatableModel: domain.TranslatableModel{ + Language: "en", + }, + } + + // Act + err := s.BookRepo.Create(context.Background(), book) + + // Assert + s.Require().NoError(err) + s.NotZero(book.ID) + + // Verify that the book was actually created in the database + var foundBook domain.Book + err = s.DB.First(&foundBook, book.ID).Error + s.Require().NoError(err) + s.Equal("New Test Book", foundBook.Title) + s.Equal("en", foundBook.Language) + }) +} + +func (s *BookRepositoryTestSuite) TestGetBookByID() { + s.Run("should return a book by ID", func() { + // Arrange + book := &domain.Book{Title: "Test Book"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + + // Act + foundBook, err := s.BookRepo.GetByID(context.Background(), book.ID) + + // Assert + s.Require().NoError(err) + s.Require().NotNil(foundBook) + s.Equal(book.ID, foundBook.ID) + s.Equal("Test Book", foundBook.Title) + }) +} + +func (s *BookRepositoryTestSuite) TestUpdateBook() { + s.Run("should update an existing book", func() { + // Arrange + book := &domain.Book{Title: "Original Title"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + book.Title = "Updated Title" + + // Act + err := s.BookRepo.Update(context.Background(), book) + + // Assert + s.Require().NoError(err) + var foundBook domain.Book + err = s.DB.First(&foundBook, book.ID).Error + s.Require().NoError(err) + s.Equal("Updated Title", foundBook.Title) + }) +} + +func (s *BookRepositoryTestSuite) TestDeleteBook() { + s.Run("should delete an existing book", func() { + // Arrange + book := &domain.Book{Title: "To Be Deleted"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + + // Act + err := s.BookRepo.Delete(context.Background(), book.ID) + + // Assert + s.Require().NoError(err) + var foundBook domain.Book + err = s.DB.First(&foundBook, book.ID).Error + s.Require().Error(err) + }) +} + +func (s *BookRepositoryTestSuite) TestFindByISBN() { + s.Run("should return a book by ISBN", func() { + // Arrange + book := &domain.Book{Title: "Test Book", ISBN: "1234567890"} + s.Require().NoError(s.BookRepo.Create(context.Background(), book)) + + // Act + foundBook, err := s.BookRepo.FindByISBN(context.Background(), "1234567890") + + // Assert + s.Require().NoError(err) + s.Require().NotNil(foundBook) + s.Equal(book.ID, foundBook.ID) + }) +} + +func TestBookRepository(t *testing.T) { + suite.Run(t, new(BookRepositoryTestSuite)) +} diff --git a/internal/data/sql/bookmark_repository.go b/internal/data/sql/bookmark_repository.go index 3ce840f..3cb9117 100644 --- a/internal/data/sql/bookmark_repository.go +++ b/internal/data/sql/bookmark_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/bookmark" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type bookmarkRepository struct { } // NewBookmarkRepository creates a new BookmarkRepository. -func NewBookmarkRepository(db *gorm.DB) bookmark.BookmarkRepository { +func NewBookmarkRepository(db *gorm.DB) domain.BookmarkRepository { return &bookmarkRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Bookmark](db), db: db, diff --git a/internal/data/sql/category_repository.go b/internal/data/sql/category_repository.go index fa057bb..d696404 100644 --- a/internal/data/sql/category_repository.go +++ b/internal/data/sql/category_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/category" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type categoryRepository struct { } // NewCategoryRepository creates a new CategoryRepository. -func NewCategoryRepository(db *gorm.DB) category.CategoryRepository { +func NewCategoryRepository(db *gorm.DB) domain.CategoryRepository { return &categoryRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Category](db), db: db, diff --git a/internal/data/sql/city_repository.go b/internal/data/sql/city_repository.go index 9d61aa2..e042b41 100644 --- a/internal/data/sql/city_repository.go +++ b/internal/data/sql/city_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/city" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type cityRepository struct { } // NewCityRepository creates a new CityRepository. -func NewCityRepository(db *gorm.DB) city.CityRepository { +func NewCityRepository(db *gorm.DB) domain.CityRepository { return &cityRepository{ BaseRepository: NewBaseRepositoryImpl[domain.City](db), db: db, diff --git a/internal/data/sql/collection_repository.go b/internal/data/sql/collection_repository.go index 03e5046..3c278b3 100644 --- a/internal/data/sql/collection_repository.go +++ b/internal/data/sql/collection_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/collection" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type collectionRepository struct { } // NewCollectionRepository creates a new CollectionRepository. -func NewCollectionRepository(db *gorm.DB) collection.CollectionRepository { +func NewCollectionRepository(db *gorm.DB) domain.CollectionRepository { return &collectionRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Collection](db), db: db, diff --git a/internal/data/sql/comment_repository.go b/internal/data/sql/comment_repository.go index dad9bc1..582bb8c 100644 --- a/internal/data/sql/comment_repository.go +++ b/internal/data/sql/comment_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/comment" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type commentRepository struct { } // NewCommentRepository creates a new CommentRepository. -func NewCommentRepository(db *gorm.DB) comment.CommentRepository { +func NewCommentRepository(db *gorm.DB) domain.CommentRepository { return &commentRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Comment](db), db: db, diff --git a/internal/data/sql/contribution_repository.go b/internal/data/sql/contribution_repository.go index 03607a9..36aa0a0 100644 --- a/internal/data/sql/contribution_repository.go +++ b/internal/data/sql/contribution_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/contribution" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type contributionRepository struct { } // NewContributionRepository creates a new ContributionRepository. -func NewContributionRepository(db *gorm.DB) contribution.ContributionRepository { +func NewContributionRepository(db *gorm.DB) domain.ContributionRepository { return &contributionRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Contribution](db), db: db, diff --git a/internal/data/sql/copyright_claim_repository.go b/internal/data/sql/copyright_claim_repository.go index 53e2132..9efc0d4 100644 --- a/internal/data/sql/copyright_claim_repository.go +++ b/internal/data/sql/copyright_claim_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/copyright_claim" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type copyrightClaimRepository struct { } // NewCopyrightClaimRepository creates a new CopyrightClaimRepository. -func NewCopyrightClaimRepository(db *gorm.DB) copyright_claim.Copyright_claimRepository { +func NewCopyrightClaimRepository(db *gorm.DB) domain.CopyrightClaimRepository { return ©rightClaimRepository{ BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db), db: db, diff --git a/internal/data/sql/copyright_repository.go b/internal/data/sql/copyright_repository.go index 6582abd..89b9705 100644 --- a/internal/data/sql/copyright_repository.go +++ b/internal/data/sql/copyright_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/copyright" "gorm.io/gorm" ) @@ -15,45 +14,14 @@ type copyrightRepository struct { } // NewCopyrightRepository creates a new CopyrightRepository. -func NewCopyrightRepository(db *gorm.DB) copyright.CopyrightRepository { +func NewCopyrightRepository(db *gorm.DB) domain.CopyrightRepository { return ©rightRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Copyright](db), db: db, } } -// AttachToEntity attaches a copyright to any entity type -func (r *copyrightRepository) AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error { - copyrightable := domain.Copyrightable{ - CopyrightID: copyrightID, - CopyrightableID: entityID, - CopyrightableType: entityType, - } - return r.db.WithContext(ctx).Create(©rightable).Error -} -// DetachFromEntity removes a copyright from an entity -func (r *copyrightRepository) DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error { - return r.db.WithContext(ctx).Where("copyright_id = ? AND copyrightable_id = ? AND copyrightable_type = ?", - copyrightID, entityID, entityType).Delete(&domain.Copyrightable{}).Error -} - -// GetByEntity gets all copyrights for a specific entity -func (r *copyrightRepository) GetByEntity(ctx context.Context, entityID uint, entityType string) ([]domain.Copyright, error) { - var copyrights []domain.Copyright - err := r.db.WithContext(ctx).Joins("JOIN copyrightables ON copyrightables.copyright_id = copyrights.id"). - Where("copyrightables.copyrightable_id = ? AND copyrightables.copyrightable_type = ?", entityID, entityType). - Preload("Translations"). - Find(©rights).Error - return copyrights, err -} - -// GetEntitiesByCopyright gets all entities that have a specific copyright -func (r *copyrightRepository) GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]domain.Copyrightable, error) { - var copyrightables []domain.Copyrightable - err := r.db.WithContext(ctx).Where("copyright_id = ?", copyrightID).Find(©rightables).Error - return copyrightables, err -} // AddTranslation adds a translation to a copyright func (r *copyrightRepository) AddTranslation(ctx context.Context, translation *domain.CopyrightTranslation) error { return r.db.WithContext(ctx).Create(translation).Error @@ -78,3 +46,63 @@ func (r *copyrightRepository) GetTranslationByLanguage(ctx context.Context, copy } return &translation, nil } + +func (r *copyrightRepository) AddCopyrightToWork(ctx context.Context, workID uint, copyrightID uint) error { + work := &domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(work).Association("Copyrights").Append(copyright) +} + +func (r *copyrightRepository) RemoveCopyrightFromWork(ctx context.Context, workID uint, copyrightID uint) error { + work := &domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(work).Association("Copyrights").Delete(copyright) +} + +func (r *copyrightRepository) AddCopyrightToAuthor(ctx context.Context, authorID uint, copyrightID uint) error { + author := &domain.Author{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: authorID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(author).Association("Copyrights").Append(copyright) +} + +func (r *copyrightRepository) RemoveCopyrightFromAuthor(ctx context.Context, authorID uint, copyrightID uint) error { + author := &domain.Author{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: authorID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(author).Association("Copyrights").Delete(copyright) +} + +func (r *copyrightRepository) AddCopyrightToBook(ctx context.Context, bookID uint, copyrightID uint) error { + book := &domain.Book{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: bookID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(book).Association("Copyrights").Append(copyright) +} + +func (r *copyrightRepository) RemoveCopyrightFromBook(ctx context.Context, bookID uint, copyrightID uint) error { + book := &domain.Book{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: bookID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(book).Association("Copyrights").Delete(copyright) +} + +func (r *copyrightRepository) AddCopyrightToPublisher(ctx context.Context, publisherID uint, copyrightID uint) error { + publisher := &domain.Publisher{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: publisherID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(publisher).Association("Copyrights").Append(copyright) +} + +func (r *copyrightRepository) RemoveCopyrightFromPublisher(ctx context.Context, publisherID uint, copyrightID uint) error { + publisher := &domain.Publisher{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: publisherID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(publisher).Association("Copyrights").Delete(copyright) +} + +func (r *copyrightRepository) AddCopyrightToSource(ctx context.Context, sourceID uint, copyrightID uint) error { + source := &domain.Source{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: sourceID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(source).Association("Copyrights").Append(copyright) +} + +func (r *copyrightRepository) RemoveCopyrightFromSource(ctx context.Context, sourceID uint, copyrightID uint) error { + source := &domain.Source{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: sourceID}}} + copyright := &domain.Copyright{BaseModel: domain.BaseModel{ID: copyrightID}} + return r.db.WithContext(ctx).Model(source).Association("Copyrights").Delete(copyright) +} diff --git a/internal/data/sql/country_repository.go b/internal/data/sql/country_repository.go index e448c49..8f4f809 100644 --- a/internal/data/sql/country_repository.go +++ b/internal/data/sql/country_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/country" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type countryRepository struct { } // NewCountryRepository creates a new CountryRepository. -func NewCountryRepository(db *gorm.DB) country.CountryRepository { +func NewCountryRepository(db *gorm.DB) domain.CountryRepository { return &countryRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Country](db), db: db, diff --git a/internal/data/sql/edge_repository.go b/internal/data/sql/edge_repository.go index c987cd4..f49badc 100644 --- a/internal/data/sql/edge_repository.go +++ b/internal/data/sql/edge_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/edge" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type edgeRepository struct { } // NewEdgeRepository creates a new EdgeRepository. -func NewEdgeRepository(db *gorm.DB) edge.EdgeRepository { +func NewEdgeRepository(db *gorm.DB) domain.EdgeRepository { return &edgeRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Edge](db), db: db, diff --git a/internal/data/sql/edition_repository.go b/internal/data/sql/edition_repository.go index 968b186..f732ca5 100644 --- a/internal/data/sql/edition_repository.go +++ b/internal/data/sql/edition_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/edition" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type editionRepository struct { } // NewEditionRepository creates a new EditionRepository. -func NewEditionRepository(db *gorm.DB) edition.EditionRepository { +func NewEditionRepository(db *gorm.DB) domain.EditionRepository { return &editionRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Edition](db), db: db, diff --git a/internal/data/sql/email_verification_repository.go b/internal/data/sql/email_verification_repository.go index 3a52534..3a250e0 100644 --- a/internal/data/sql/email_verification_repository.go +++ b/internal/data/sql/email_verification_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/email_verification" "time" "gorm.io/gorm" @@ -16,7 +15,7 @@ type emailVerificationRepository struct { } // NewEmailVerificationRepository creates a new EmailVerificationRepository. -func NewEmailVerificationRepository(db *gorm.DB) email_verification.Email_verificationRepository { +func NewEmailVerificationRepository(db *gorm.DB) domain.EmailVerificationRepository { return &emailVerificationRepository{ BaseRepository: NewBaseRepositoryImpl[domain.EmailVerification](db), db: db, diff --git a/internal/data/sql/like_repository.go b/internal/data/sql/like_repository.go index 6688932..c644a2f 100644 --- a/internal/data/sql/like_repository.go +++ b/internal/data/sql/like_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/like" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type likeRepository struct { } // NewLikeRepository creates a new LikeRepository. -func NewLikeRepository(db *gorm.DB) like.LikeRepository { +func NewLikeRepository(db *gorm.DB) domain.LikeRepository { return &likeRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Like](db), db: db, diff --git a/internal/data/sql/monetization_repository.go b/internal/data/sql/monetization_repository.go index 7fa2d62..2485ae5 100644 --- a/internal/data/sql/monetization_repository.go +++ b/internal/data/sql/monetization_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/monetization" "gorm.io/gorm" ) @@ -14,36 +13,69 @@ type monetizationRepository struct { } // NewMonetizationRepository creates a new MonetizationRepository. -func NewMonetizationRepository(db *gorm.DB) monetization.MonetizationRepository { +func NewMonetizationRepository(db *gorm.DB) domain.MonetizationRepository { return &monetizationRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Monetization](db), db: db, } } -// ListByWorkID finds monetizations by work ID -func (r *monetizationRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error) { - var monetizations []domain.Monetization - if err := r.db.WithContext(ctx).Where("work_id = ?", workID).Find(&monetizations).Error; err != nil { - return nil, err - } - return monetizations, nil +func (r *monetizationRepository) AddMonetizationToWork(ctx context.Context, workID uint, monetizationID uint) error { + work := &domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(work).Association("Monetizations").Append(monetization) } -// ListByTranslationID finds monetizations by translation ID -func (r *monetizationRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error) { - var monetizations []domain.Monetization - if err := r.db.WithContext(ctx).Where("translation_id = ?", translationID).Find(&monetizations).Error; err != nil { - return nil, err - } - return monetizations, nil +func (r *monetizationRepository) RemoveMonetizationFromWork(ctx context.Context, workID uint, monetizationID uint) error { + work := &domain.Work{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: workID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(work).Association("Monetizations").Delete(monetization) } -// ListByBookID finds monetizations by book ID -func (r *monetizationRepository) ListByBookID(ctx context.Context, bookID uint) ([]domain.Monetization, error) { - var monetizations []domain.Monetization - if err := r.db.WithContext(ctx).Where("book_id = ?", bookID).Find(&monetizations).Error; err != nil { - return nil, err - } - return monetizations, nil +func (r *monetizationRepository) AddMonetizationToAuthor(ctx context.Context, authorID uint, monetizationID uint) error { + author := &domain.Author{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: authorID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(author).Association("Monetizations").Append(monetization) +} + +func (r *monetizationRepository) RemoveMonetizationFromAuthor(ctx context.Context, authorID uint, monetizationID uint) error { + author := &domain.Author{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: authorID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(author).Association("Monetizations").Delete(monetization) +} + +func (r *monetizationRepository) AddMonetizationToBook(ctx context.Context, bookID uint, monetizationID uint) error { + book := &domain.Book{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: bookID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(book).Association("Monetizations").Append(monetization) +} + +func (r *monetizationRepository) RemoveMonetizationFromBook(ctx context.Context, bookID uint, monetizationID uint) error { + book := &domain.Book{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: bookID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(book).Association("Monetizations").Delete(monetization) +} + +func (r *monetizationRepository) AddMonetizationToPublisher(ctx context.Context, publisherID uint, monetizationID uint) error { + publisher := &domain.Publisher{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: publisherID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(publisher).Association("Monetizations").Append(monetization) +} + +func (r *monetizationRepository) RemoveMonetizationFromPublisher(ctx context.Context, publisherID uint, monetizationID uint) error { + publisher := &domain.Publisher{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: publisherID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(publisher).Association("Monetizations").Delete(monetization) +} + +func (r *monetizationRepository) AddMonetizationToSource(ctx context.Context, sourceID uint, monetizationID uint) error { + source := &domain.Source{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: sourceID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(source).Association("Monetizations").Append(monetization) +} + +func (r *monetizationRepository) RemoveMonetizationFromSource(ctx context.Context, sourceID uint, monetizationID uint) error { + source := &domain.Source{TranslatableModel: domain.TranslatableModel{BaseModel: domain.BaseModel{ID: sourceID}}} + monetization := &domain.Monetization{BaseModel: domain.BaseModel{ID: monetizationID}} + return r.db.WithContext(ctx).Model(source).Association("Monetizations").Delete(monetization) } diff --git a/internal/data/sql/monetization_repository_test.go b/internal/data/sql/monetization_repository_test.go new file mode 100644 index 0000000..b66352b --- /dev/null +++ b/internal/data/sql/monetization_repository_test.go @@ -0,0 +1,44 @@ +package sql_test + +import ( + "context" + "testing" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type MonetizationRepositoryTestSuite struct { + testutil.IntegrationTestSuite +} + +func (s *MonetizationRepositoryTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) +} + +func (s *MonetizationRepositoryTestSuite) TestAddMonetizationToWork() { + s.Run("should add a monetization to a work", func() { + // Arrange + work := s.CreateTestWork("Test Work", "en", "Test content") + monetization := &domain.Monetization{Amount: 10.0} + s.Require().NoError(s.DB.Create(monetization).Error) + + // Act + err := s.MonetizationRepo.AddMonetizationToWork(context.Background(), work.ID, monetization.ID) + + // Assert + s.Require().NoError(err) + + // Verify that the association was created in the database + var foundWork domain.Work + err = s.DB.Preload("Monetizations").First(&foundWork, work.ID).Error + s.Require().NoError(err) + s.Require().Len(foundWork.Monetizations, 1) + s.Equal(monetization.ID, foundWork.Monetizations[0].ID) + }) +} + +func TestMonetizationRepository(t *testing.T) { + suite.Run(t, new(MonetizationRepositoryTestSuite)) +} diff --git a/internal/data/sql/password_reset_repository.go b/internal/data/sql/password_reset_repository.go index 00740ea..dc91705 100644 --- a/internal/data/sql/password_reset_repository.go +++ b/internal/data/sql/password_reset_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/password_reset" "time" "gorm.io/gorm" @@ -16,7 +15,7 @@ type passwordResetRepository struct { } // NewPasswordResetRepository creates a new PasswordResetRepository. -func NewPasswordResetRepository(db *gorm.DB) password_reset.Password_resetRepository { +func NewPasswordResetRepository(db *gorm.DB) domain.PasswordResetRepository { return &passwordResetRepository{ BaseRepository: NewBaseRepositoryImpl[domain.PasswordReset](db), db: db, diff --git a/internal/data/sql/place_repository.go b/internal/data/sql/place_repository.go index 992cad8..f082f0b 100644 --- a/internal/data/sql/place_repository.go +++ b/internal/data/sql/place_repository.go @@ -4,7 +4,6 @@ import ( "context" "math" "tercul/internal/domain" - "tercul/internal/domain/place" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type placeRepository struct { } // NewPlaceRepository creates a new PlaceRepository. -func NewPlaceRepository(db *gorm.DB) place.PlaceRepository { +func NewPlaceRepository(db *gorm.DB) domain.PlaceRepository { return &placeRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Place](db), db: db, diff --git a/internal/data/sql/publisher_repository.go b/internal/data/sql/publisher_repository.go index e96af2b..c00dc08 100644 --- a/internal/data/sql/publisher_repository.go +++ b/internal/data/sql/publisher_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/publisher" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type publisherRepository struct { } // NewPublisherRepository creates a new PublisherRepository. -func NewPublisherRepository(db *gorm.DB) publisher.PublisherRepository { +func NewPublisherRepository(db *gorm.DB) domain.PublisherRepository { return &publisherRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Publisher](db), db: db, diff --git a/internal/data/sql/publisher_repository_test.go b/internal/data/sql/publisher_repository_test.go new file mode 100644 index 0000000..424cf45 --- /dev/null +++ b/internal/data/sql/publisher_repository_test.go @@ -0,0 +1,101 @@ +package sql_test + +import ( + "context" + "testing" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type PublisherRepositoryTestSuite struct { + testutil.IntegrationTestSuite +} + +func (s *PublisherRepositoryTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) +} + +func (s *PublisherRepositoryTestSuite) TestCreatePublisher() { + s.Run("should create a new publisher", func() { + // Arrange + publisher := &domain.Publisher{ + Name: "New Test Publisher", + TranslatableModel: domain.TranslatableModel{ + Language: "en", + }, + } + + // Act + err := s.PublisherRepo.Create(context.Background(), publisher) + + // Assert + s.Require().NoError(err) + s.NotZero(publisher.ID) + + // Verify that the publisher was actually created in the database + var foundPublisher domain.Publisher + err = s.DB.First(&foundPublisher, publisher.ID).Error + s.Require().NoError(err) + s.Equal("New Test Publisher", foundPublisher.Name) + s.Equal("en", foundPublisher.Language) + }) +} + +func (s *PublisherRepositoryTestSuite) TestGetPublisherByID() { + s.Run("should return a publisher by ID", func() { + // Arrange + publisher := &domain.Publisher{Name: "Test Publisher"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + + // Act + foundPublisher, err := s.PublisherRepo.GetByID(context.Background(), publisher.ID) + + // Assert + s.Require().NoError(err) + s.Require().NotNil(foundPublisher) + s.Equal(publisher.ID, foundPublisher.ID) + s.Equal("Test Publisher", foundPublisher.Name) + }) +} + +func (s *PublisherRepositoryTestSuite) TestUpdatePublisher() { + s.Run("should update an existing publisher", func() { + // Arrange + publisher := &domain.Publisher{Name: "Original Name"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + publisher.Name = "Updated Name" + + // Act + err := s.PublisherRepo.Update(context.Background(), publisher) + + // Assert + s.Require().NoError(err) + var foundPublisher domain.Publisher + err = s.DB.First(&foundPublisher, publisher.ID).Error + s.Require().NoError(err) + s.Equal("Updated Name", foundPublisher.Name) + }) +} + +func (s *PublisherRepositoryTestSuite) TestDeletePublisher() { + s.Run("should delete an existing publisher", func() { + // Arrange + publisher := &domain.Publisher{Name: "To Be Deleted"} + s.Require().NoError(s.PublisherRepo.Create(context.Background(), publisher)) + + // Act + err := s.PublisherRepo.Delete(context.Background(), publisher.ID) + + // Assert + s.Require().NoError(err) + var foundPublisher domain.Publisher + err = s.DB.First(&foundPublisher, publisher.ID).Error + s.Require().Error(err) + }) +} + +func TestPublisherRepository(t *testing.T) { + suite.Run(t, new(PublisherRepositoryTestSuite)) +} diff --git a/internal/data/sql/source_repository.go b/internal/data/sql/source_repository.go index e9b19ee..e18962d 100644 --- a/internal/data/sql/source_repository.go +++ b/internal/data/sql/source_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/source" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type sourceRepository struct { } // NewSourceRepository creates a new SourceRepository. -func NewSourceRepository(db *gorm.DB) source.SourceRepository { +func NewSourceRepository(db *gorm.DB) domain.SourceRepository { return &sourceRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Source](db), db: db, diff --git a/internal/data/sql/source_repository_test.go b/internal/data/sql/source_repository_test.go new file mode 100644 index 0000000..736180d --- /dev/null +++ b/internal/data/sql/source_repository_test.go @@ -0,0 +1,101 @@ +package sql_test + +import ( + "context" + "testing" + "tercul/internal/domain" + "tercul/internal/testutil" + + "github.com/stretchr/testify/suite" +) + +type SourceRepositoryTestSuite struct { + testutil.IntegrationTestSuite +} + +func (s *SourceRepositoryTestSuite) SetupSuite() { + s.IntegrationTestSuite.SetupSuite(testutil.DefaultTestConfig()) +} + +func (s *SourceRepositoryTestSuite) TestCreateSource() { + s.Run("should create a new source", func() { + // Arrange + source := &domain.Source{ + Name: "New Test Source", + TranslatableModel: domain.TranslatableModel{ + Language: "en", + }, + } + + // Act + err := s.SourceRepo.Create(context.Background(), source) + + // Assert + s.Require().NoError(err) + s.NotZero(source.ID) + + // Verify that the source was actually created in the database + var foundSource domain.Source + err = s.DB.First(&foundSource, source.ID).Error + s.Require().NoError(err) + s.Equal("New Test Source", foundSource.Name) + s.Equal("en", foundSource.Language) + }) +} + +func (s *SourceRepositoryTestSuite) TestGetSourceByID() { + s.Run("should return a source by ID", func() { + // Arrange + source := &domain.Source{Name: "Test Source"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + + // Act + foundSource, err := s.SourceRepo.GetByID(context.Background(), source.ID) + + // Assert + s.Require().NoError(err) + s.Require().NotNil(foundSource) + s.Equal(source.ID, foundSource.ID) + s.Equal("Test Source", foundSource.Name) + }) +} + +func (s *SourceRepositoryTestSuite) TestUpdateSource() { + s.Run("should update an existing source", func() { + // Arrange + source := &domain.Source{Name: "Original Name"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + source.Name = "Updated Name" + + // Act + err := s.SourceRepo.Update(context.Background(), source) + + // Assert + s.Require().NoError(err) + var foundSource domain.Source + err = s.DB.First(&foundSource, source.ID).Error + s.Require().NoError(err) + s.Equal("Updated Name", foundSource.Name) + }) +} + +func (s *SourceRepositoryTestSuite) TestDeleteSource() { + s.Run("should delete an existing source", func() { + // Arrange + source := &domain.Source{Name: "To Be Deleted"} + s.Require().NoError(s.SourceRepo.Create(context.Background(), source)) + + // Act + err := s.SourceRepo.Delete(context.Background(), source.ID) + + // Assert + s.Require().NoError(err) + var foundSource domain.Source + err = s.DB.First(&foundSource, source.ID).Error + s.Require().Error(err) + }) +} + +func TestSourceRepository(t *testing.T) { + suite.Run(t, new(SourceRepositoryTestSuite)) +} diff --git a/internal/data/sql/tag_repository.go b/internal/data/sql/tag_repository.go index 96ae1c2..82a90bb 100644 --- a/internal/data/sql/tag_repository.go +++ b/internal/data/sql/tag_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/tag" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type tagRepository struct { } // NewTagRepository creates a new TagRepository. -func NewTagRepository(db *gorm.DB) tag.TagRepository { +func NewTagRepository(db *gorm.DB) domain.TagRepository { return &tagRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Tag](db), db: db, diff --git a/internal/data/sql/translation_repository.go b/internal/data/sql/translation_repository.go index b7d5a7c..28e332e 100644 --- a/internal/data/sql/translation_repository.go +++ b/internal/data/sql/translation_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/translation" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type translationRepository struct { } // NewTranslationRepository creates a new TranslationRepository. -func NewTranslationRepository(db *gorm.DB) translation.TranslationRepository { +func NewTranslationRepository(db *gorm.DB) domain.TranslationRepository { return &translationRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Translation](db), db: db, diff --git a/internal/data/sql/user_profile_repository.go b/internal/data/sql/user_profile_repository.go index d624a70..d8c0700 100644 --- a/internal/data/sql/user_profile_repository.go +++ b/internal/data/sql/user_profile_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/user_profile" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type userProfileRepository struct { } // NewUserProfileRepository creates a new UserProfileRepository. -func NewUserProfileRepository(db *gorm.DB) user_profile.User_profileRepository { +func NewUserProfileRepository(db *gorm.DB) domain.UserProfileRepository { return &userProfileRepository{ BaseRepository: NewBaseRepositoryImpl[domain.UserProfile](db), db: db, diff --git a/internal/data/sql/user_repository.go b/internal/data/sql/user_repository.go index 158795d..a409e60 100644 --- a/internal/data/sql/user_repository.go +++ b/internal/data/sql/user_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/user" "gorm.io/gorm" ) @@ -15,7 +14,7 @@ type userRepository struct { } // NewUserRepository creates a new UserRepository. -func NewUserRepository(db *gorm.DB) user.UserRepository { +func NewUserRepository(db *gorm.DB) domain.UserRepository { return &userRepository{ BaseRepository: NewBaseRepositoryImpl[domain.User](db), db: db, diff --git a/internal/data/sql/user_session_repository.go b/internal/data/sql/user_session_repository.go index f8961e6..f7265f5 100644 --- a/internal/data/sql/user_session_repository.go +++ b/internal/data/sql/user_session_repository.go @@ -4,7 +4,6 @@ import ( "context" "errors" "tercul/internal/domain" - "tercul/internal/domain/user_session" "time" "gorm.io/gorm" @@ -16,7 +15,7 @@ type userSessionRepository struct { } // NewUserSessionRepository creates a new UserSessionRepository. -func NewUserSessionRepository(db *gorm.DB) user_session.User_sessionRepository { +func NewUserSessionRepository(db *gorm.DB) domain.UserSessionRepository { return &userSessionRepository{ BaseRepository: NewBaseRepositoryImpl[domain.UserSession](db), db: db, diff --git a/internal/data/sql/work_repository.go b/internal/data/sql/work_repository.go index a0b71f2..88a2289 100644 --- a/internal/data/sql/work_repository.go +++ b/internal/data/sql/work_repository.go @@ -3,7 +3,6 @@ package sql import ( "context" "tercul/internal/domain" - "tercul/internal/domain/work" "gorm.io/gorm" ) @@ -14,7 +13,7 @@ type workRepository struct { } // NewWorkRepository creates a new WorkRepository. -func NewWorkRepository(db *gorm.DB) work.WorkRepository { +func NewWorkRepository(db *gorm.DB) domain.WorkRepository { return &workRepository{ BaseRepository: NewBaseRepositoryImpl[domain.Work](db), db: db, @@ -100,6 +99,28 @@ func (r *workRepository) FindByLanguage(ctx context.Context, language string, pa }, nil } + + + + + + + +// Delete removes a work and its associations +func (r *workRepository) Delete(ctx context.Context, id uint) error { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + // 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 { + return err + } + // Also delete the work itself + if err := tx.Delete(&domain.Work{}, id).Error; err != nil { + return err + } + return nil + }) +} + // GetWithTranslations gets a work with its translations func (r *workRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) { return r.FindWithPreload(ctx, []string{"Translations"}, id) diff --git a/internal/data/sql/work_repository_test.go b/internal/data/sql/work_repository_test.go index 2f7367f..33dff3c 100644 --- a/internal/data/sql/work_repository_test.go +++ b/internal/data/sql/work_repository_test.go @@ -18,13 +18,20 @@ func (s *WorkRepositoryTestSuite) SetupSuite() { } func (s *WorkRepositoryTestSuite) TestCreateWork() { - s.Run("should create a new work", func() { + s.Run("should create a new work with a copyright", func() { // Arrange + copyright := &domain.Copyright{ + Name: "Test Copyright", + Identificator: "TC-123", + } + s.Require().NoError(s.DB.Create(copyright).Error) + work := &domain.Work{ Title: "New Test Work", TranslatableModel: domain.TranslatableModel{ Language: "en", }, + Copyrights: []*domain.Copyright{copyright}, } // Act @@ -36,17 +43,26 @@ func (s *WorkRepositoryTestSuite) TestCreateWork() { // Verify that the work was actually created in the database var foundWork domain.Work - err = s.DB.First(&foundWork, work.ID).Error + err = s.DB.Preload("Copyrights").First(&foundWork, work.ID).Error s.Require().NoError(err) s.Equal("New Test Work", foundWork.Title) s.Equal("en", foundWork.Language) + s.Require().Len(foundWork.Copyrights, 1) + s.Equal("Test Copyright", foundWork.Copyrights[0].Name) }) } func (s *WorkRepositoryTestSuite) TestGetWorkByID() { - s.Run("should return a work by ID", func() { + s.Run("should return a work by ID with copyrights", func() { // Arrange + copyright := &domain.Copyright{ + Name: "Test Copyright", + Identificator: "TC-123", + } + s.Require().NoError(s.DB.Create(copyright).Error) + work := s.CreateTestWork("Test Work", "en", "Test content") + s.Require().NoError(s.DB.Model(work).Association("Copyrights").Append(copyright)) // Act foundWork, err := s.WorkRepo.GetByID(context.Background(), work.ID) @@ -69,10 +85,18 @@ func (s *WorkRepositoryTestSuite) TestGetWorkByID() { } func (s *WorkRepositoryTestSuite) TestUpdateWork() { - s.Run("should update an existing work", func() { + s.Run("should update an existing work and its copyrights", func() { // Arrange + copyright1 := &domain.Copyright{Name: "C1", Identificator: "C1"} + copyright2 := &domain.Copyright{Name: "C2", Identificator: "C2"} + s.Require().NoError(s.DB.Create(©right1).Error) + s.Require().NoError(s.DB.Create(©right2).Error) + work := s.CreateTestWork("Original Title", "en", "Original content") + s.Require().NoError(s.DB.Model(work).Association("Copyrights").Append(copyright1)) + work.Title = "Updated Title" + s.Require().NoError(s.DB.Model(work).Association("Copyrights").Replace(copyright2)) // Act err := s.WorkRepo.Update(context.Background(), work) @@ -82,16 +106,21 @@ func (s *WorkRepositoryTestSuite) TestUpdateWork() { // Verify that the work was actually updated in the database var foundWork domain.Work - err = s.DB.First(&foundWork, work.ID).Error + err = s.DB.Preload("Copyrights").First(&foundWork, work.ID).Error s.Require().NoError(err) s.Equal("Updated Title", foundWork.Title) + s.Require().Len(foundWork.Copyrights, 1) + s.Equal("C2", foundWork.Copyrights[0].Name) }) } func (s *WorkRepositoryTestSuite) TestDeleteWork() { - s.Run("should delete an existing work", func() { + s.Run("should delete an existing work and its associations", func() { // Arrange work := s.CreateTestWork("To Be Deleted", "en", "Content") + copyright := &domain.Copyright{Name: "C1", Identificator: "C1"} + s.Require().NoError(s.DB.Create(copyright).Error) + s.Require().NoError(s.DB.Model(work).Association("Copyrights").Append(copyright)) // Act err := s.WorkRepo.Delete(context.Background(), work.ID) @@ -103,6 +132,11 @@ func (s *WorkRepositoryTestSuite) TestDeleteWork() { var foundWork domain.Work err = s.DB.First(&foundWork, work.ID).Error s.Require().Error(err) + + // Verify that the association in the join table is also deleted + var count int64 + s.DB.Table("work_copyrights").Where("work_id = ?", work.ID).Count(&count) + s.Zero(count) }) } diff --git a/internal/domain/author/repo.go b/internal/domain/author/repo.go deleted file mode 100644 index 4016138..0000000 --- a/internal/domain/author/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package author - -import ( - "context" - "tercul/internal/domain" -) - -// AuthorRepository defines CRUD methods specific to Author. -type AuthorRepository interface { - domain.BaseRepository[domain.Author] - - ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error) - ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error) - ListByCountryID(ctx context.Context, countryID uint) ([]domain.Author, error) -} diff --git a/internal/domain/book/repo.go b/internal/domain/book/repo.go deleted file mode 100644 index c24fd8d..0000000 --- a/internal/domain/book/repo.go +++ /dev/null @@ -1,16 +0,0 @@ -package book - -import ( - "context" - "tercul/internal/domain" -) - -// BookRepository defines CRUD methods specific to Book. -type BookRepository interface { - domain.BaseRepository[domain.Book] - - ListByAuthorID(ctx context.Context, authorID uint) ([]domain.Book, error) - ListByPublisherID(ctx context.Context, publisherID uint) ([]domain.Book, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Book, error) - FindByISBN(ctx context.Context, isbn string) (*domain.Book, error) -} diff --git a/internal/domain/bookmark/repo.go b/internal/domain/bookmark/repo.go deleted file mode 100644 index 68d3656..0000000 --- a/internal/domain/bookmark/repo.go +++ /dev/null @@ -1,14 +0,0 @@ -package bookmark - -import ( - "context" - "tercul/internal/domain" -) - -// BookmarkRepository defines CRUD methods specific to Bookmark. -type BookmarkRepository interface { - domain.BaseRepository[domain.Bookmark] - - ListByUserID(ctx context.Context, userID uint) ([]domain.Bookmark, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Bookmark, error) -} diff --git a/internal/domain/category/repo.go b/internal/domain/category/repo.go deleted file mode 100644 index ee5dc1a..0000000 --- a/internal/domain/category/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package category - -import ( - "context" - "tercul/internal/domain" -) - -// CategoryRepository defines CRUD methods specific to Category. -type CategoryRepository interface { - domain.BaseRepository[domain.Category] - - FindByName(ctx context.Context, name string) (*domain.Category, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Category, error) - ListByParentID(ctx context.Context, parentID *uint) ([]domain.Category, error) -} diff --git a/internal/domain/city/repo.go b/internal/domain/city/repo.go deleted file mode 100644 index 9b306ba..0000000 --- a/internal/domain/city/repo.go +++ /dev/null @@ -1,13 +0,0 @@ -package city - -import ( - "context" - "tercul/internal/domain" -) - -// CityRepository defines CRUD methods specific to City. -type CityRepository interface { - domain.BaseRepository[domain.City] - - ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error) -} diff --git a/internal/domain/collection/repo.go b/internal/domain/collection/repo.go deleted file mode 100644 index 4ed84f9..0000000 --- a/internal/domain/collection/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package collection - -import ( - "context" - "tercul/internal/domain" -) - -// CollectionRepository defines CRUD methods specific to Collection. -type CollectionRepository interface { - domain.BaseRepository[domain.Collection] - - ListByUserID(ctx context.Context, userID uint) ([]domain.Collection, error) - ListPublic(ctx context.Context) ([]domain.Collection, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Collection, error) -} diff --git a/internal/domain/comment/repo.go b/internal/domain/comment/repo.go deleted file mode 100644 index a65177d..0000000 --- a/internal/domain/comment/repo.go +++ /dev/null @@ -1,16 +0,0 @@ -package comment - -import ( - "context" - "tercul/internal/domain" -) - -// CommentRepository defines CRUD methods specific to Comment. -type CommentRepository interface { - domain.BaseRepository[domain.Comment] - - ListByUserID(ctx context.Context, userID uint) ([]domain.Comment, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Comment, error) - ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Comment, error) - ListByParentID(ctx context.Context, parentID uint) ([]domain.Comment, error) -} diff --git a/internal/domain/contribution/repo.go b/internal/domain/contribution/repo.go deleted file mode 100644 index 180d800..0000000 --- a/internal/domain/contribution/repo.go +++ /dev/null @@ -1,17 +0,0 @@ -package contribution - -import ( - "context" - "tercul/internal/domain" -) - -// ContributionRepository defines CRUD methods specific to Contribution. -type ContributionRepository interface { - domain.BaseRepository[domain.Contribution] - - ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error) - ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Contribution, error) - ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Contribution, error) - ListByStatus(ctx context.Context, status string) ([]domain.Contribution, error) -} diff --git a/internal/domain/copyright/repo.go b/internal/domain/copyright/repo.go deleted file mode 100644 index 51b56ba..0000000 --- a/internal/domain/copyright/repo.go +++ /dev/null @@ -1,19 +0,0 @@ -package copyright - -import ( - "context" - "tercul/internal/domain" -) - -// CopyrightRepository defines CRUD methods specific to Copyright. -type CopyrightRepository interface { - domain.BaseRepository[domain.Copyright] - - AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error) - DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error) - GetByEntity(ctx context.Context, entityID uint, entityType string) ([]domain.Copyright, error) - GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]domain.Copyrightable, error) - AddTranslation(ctx context.Context, translation *domain.CopyrightTranslation) (error) - GetTranslations(ctx context.Context, copyrightID uint) ([]domain.CopyrightTranslation, error) - GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*domain.CopyrightTranslation, error) -} diff --git a/internal/domain/copyright_claim/repo.go b/internal/domain/copyright_claim/repo.go deleted file mode 100644 index 17a4795..0000000 --- a/internal/domain/copyright_claim/repo.go +++ /dev/null @@ -1,14 +0,0 @@ -package copyright_claim - -import ( - "context" - "tercul/internal/domain" -) - -// Copyright_claimRepository defines CRUD methods specific to Copyright_claim. -type Copyright_claimRepository interface { - domain.BaseRepository[domain.CopyrightClaim] - - ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error) - ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error) -} diff --git a/internal/domain/country/repo.go b/internal/domain/country/repo.go deleted file mode 100644 index 265d95a..0000000 --- a/internal/domain/country/repo.go +++ /dev/null @@ -1,14 +0,0 @@ -package country - -import ( - "context" - "tercul/internal/domain" -) - -// CountryRepository defines CRUD methods specific to Country. -type CountryRepository interface { - domain.BaseRepository[domain.Country] - - GetByCode(ctx context.Context, code string) (*domain.Country, error) - ListByContinent(ctx context.Context, continent string) ([]domain.Country, error) -} diff --git a/internal/domain/edge/repo.go b/internal/domain/edge/repo.go deleted file mode 100644 index d49578c..0000000 --- a/internal/domain/edge/repo.go +++ /dev/null @@ -1,13 +0,0 @@ -package edge - -import ( - "context" - "tercul/internal/domain" -) - -// EdgeRepository defines CRUD methods specific to Edge. -type EdgeRepository interface { - domain.BaseRepository[domain.Edge] - - ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error) -} diff --git a/internal/domain/edition/repo.go b/internal/domain/edition/repo.go deleted file mode 100644 index a5eef63..0000000 --- a/internal/domain/edition/repo.go +++ /dev/null @@ -1,14 +0,0 @@ -package edition - -import ( - "context" - "tercul/internal/domain" -) - -// EditionRepository defines CRUD methods specific to Edition. -type EditionRepository interface { - domain.BaseRepository[domain.Edition] - - ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error) - FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error) -} diff --git a/internal/domain/email_verification/repo.go b/internal/domain/email_verification/repo.go deleted file mode 100644 index bc4d969..0000000 --- a/internal/domain/email_verification/repo.go +++ /dev/null @@ -1,16 +0,0 @@ -package email_verification - -import ( - "context" - "tercul/internal/domain" -) - -// Email_verificationRepository defines CRUD methods specific to Email_verification. -type Email_verificationRepository interface { - domain.BaseRepository[domain.EmailVerification] - - GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error) - GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error) - DeleteExpired(ctx context.Context) (error) - MarkAsUsed(ctx context.Context, id uint) (error) -} diff --git a/internal/domain/entities.go b/internal/domain/entities.go index 8a3d874..e92cc6c 100644 --- a/internal/domain/entities.go +++ b/internal/domain/entities.go @@ -206,8 +206,8 @@ type Work struct { Authors []*Author `gorm:"many2many:work_authors"` Tags []*Tag `gorm:"many2many:work_tags"` Categories []*Category `gorm:"many2many:work_categories"` - Copyrights []Copyright `gorm:"-"` - Monetizations []Monetization `gorm:"-"` + Copyrights []*Copyright `gorm:"many2many:work_copyrights;constraint:OnDelete:CASCADE"` + Monetizations []*Monetization `gorm:"many2many:work_monetizations;constraint:OnDelete:CASCADE"` } type AuthorStatus string @@ -233,8 +233,8 @@ type Author struct { AddressID *uint Address *Address `gorm:"foreignKey:AddressID"` Translations []Translation `gorm:"polymorphic:Translatable"` - Copyrights []Copyright `gorm:"-"` - Monetizations []Monetization `gorm:"-"` + Copyrights []*Copyright `gorm:"many2many:author_copyrights;constraint:OnDelete:CASCADE"` + Monetizations []*Monetization `gorm:"many2many:author_monetizations;constraint:OnDelete:CASCADE"` } type BookStatus string @@ -265,8 +265,8 @@ type Book struct { PublisherID *uint Publisher *Publisher `gorm:"foreignKey:PublisherID"` Translations []Translation `gorm:"polymorphic:Translatable"` - Copyrights []Copyright `gorm:"-"` - Monetizations []Monetization `gorm:"-"` + Copyrights []*Copyright `gorm:"many2many:book_copyrights;constraint:OnDelete:CASCADE"` + Monetizations []*Monetization `gorm:"many2many:book_monetizations;constraint:OnDelete:CASCADE"` } type PublisherStatus string @@ -284,8 +284,8 @@ type Publisher struct { CountryID *uint Country *Country `gorm:"foreignKey:CountryID"` Translations []Translation `gorm:"polymorphic:Translatable"` - Copyrights []Copyright `gorm:"-"` - Monetizations []Monetization `gorm:"-"` + Copyrights []*Copyright `gorm:"many2many:publisher_copyrights;constraint:OnDelete:CASCADE"` + Monetizations []*Monetization `gorm:"many2many:publisher_monetizations;constraint:OnDelete:CASCADE"` } type SourceStatus string @@ -302,8 +302,8 @@ type Source struct { Status SourceStatus `gorm:"size:50;default:'active'"` Works []*Work `gorm:"many2many:work_sources"` Translations []Translation `gorm:"polymorphic:Translatable"` - Copyrights []Copyright `gorm:"-"` - Monetizations []Monetization `gorm:"-"` + Copyrights []*Copyright `gorm:"many2many:source_copyrights;constraint:OnDelete:CASCADE"` + Monetizations []*Monetization `gorm:"many2many:source_monetizations;constraint:OnDelete:CASCADE"` } type EditionStatus string @@ -574,16 +574,47 @@ type Copyright struct { License string `gorm:"size:100"` StartDate *time.Time EndDate *time.Time - Copyrightables []Copyrightable `gorm:"-"` Translations []CopyrightTranslation `gorm:"foreignKey:CopyrightID"` } -type Copyrightable struct { - BaseModel - CopyrightID uint - Copyright *Copyright `gorm:"foreignKey:CopyrightID"` - CopyrightableID uint - CopyrightableType string +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 { + AuthorID uint `gorm:"primaryKey;index"` + CopyrightID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (AuthorCopyright) TableName() string { return "author_copyrights" } + +type BookCopyright struct { + BookID uint `gorm:"primaryKey;index"` + CopyrightID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (BookCopyright) TableName() string { return "book_copyrights" } + +type PublisherCopyright struct { + PublisherID uint `gorm:"primaryKey;index"` + CopyrightID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (PublisherCopyright) TableName() string { return "publisher_copyrights" } + +type SourceCopyright struct { + SourceID uint `gorm:"primaryKey;index"` + CopyrightID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (SourceCopyright) TableName() string { return "source_copyrights" } type CopyrightTranslation struct { BaseModel CopyrightID uint @@ -607,7 +638,6 @@ type CopyrightClaim struct { ResolvedAt *time.Time UserID *uint User *User `gorm:"foreignKey:UserID"` - Claimables []Copyrightable `gorm:"-"` } type MonetizationType string const ( @@ -623,13 +653,45 @@ const ( MonetizationStatusInactive MonetizationStatus = "inactive" MonetizationStatusPending MonetizationStatus = "pending" ) -type Monetizable struct { - BaseModel - MonetizationID uint - Monetization *Monetization `gorm:"foreignKey:MonetizationID"` - MonetizableID uint - MonetizableType string +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 { + AuthorID uint `gorm:"primaryKey;index"` + MonetizationID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (AuthorMonetization) TableName() string { return "author_monetizations" } + +type BookMonetization struct { + BookID uint `gorm:"primaryKey;index"` + MonetizationID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (BookMonetization) TableName() string { return "book_monetizations" } + +type PublisherMonetization struct { + PublisherID uint `gorm:"primaryKey;index"` + MonetizationID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (PublisherMonetization) TableName() string { return "publisher_monetizations" } + +type SourceMonetization struct { + SourceID uint `gorm:"primaryKey;index"` + MonetizationID uint `gorm:"primaryKey;index"` + CreatedAt time.Time +} + +func (SourceMonetization) TableName() string { return "source_monetizations" } type Monetization struct { BaseModel Amount float64 `gorm:"type:decimal(10,2);default:0.0"` @@ -639,7 +701,6 @@ type Monetization struct { StartDate *time.Time EndDate *time.Time Language string `gorm:"size:50;not null"` - Monetizables []Monetizable `gorm:"-"` } type License struct { BaseModel diff --git a/internal/domain/interfaces.go b/internal/domain/interfaces.go index d442d35..1b7c231 100644 --- a/internal/domain/interfaces.go +++ b/internal/domain/interfaces.go @@ -16,6 +16,191 @@ type PaginatedResult[T any] struct { HasPrev bool `json:"hasPrev"` } +// MonetizationRepository defines CRUD methods specific to Monetization. +type MonetizationRepository interface { + BaseRepository[Monetization] + AddMonetizationToWork(ctx context.Context, workID uint, monetizationID uint) error + RemoveMonetizationFromWork(ctx context.Context, workID uint, monetizationID uint) error + AddMonetizationToAuthor(ctx context.Context, authorID uint, monetizationID uint) error + RemoveMonetizationFromAuthor(ctx context.Context, authorID uint, monetizationID uint) error + AddMonetizationToBook(ctx context.Context, bookID uint, monetizationID uint) error + RemoveMonetizationFromBook(ctx context.Context, bookID uint, monetizationID uint) error + AddMonetizationToPublisher(ctx context.Context, publisherID uint, monetizationID uint) error + RemoveMonetizationFromPublisher(ctx context.Context, publisherID uint, monetizationID uint) error + AddMonetizationToSource(ctx context.Context, sourceID uint, monetizationID uint) error + RemoveMonetizationFromSource(ctx context.Context, sourceID uint, monetizationID uint) error +} + +// PublisherRepository defines CRUD methods specific to Publisher. +type PublisherRepository interface { + BaseRepository[Publisher] + ListByCountryID(ctx context.Context, countryID uint) ([]Publisher, error) +} + +// SourceRepository defines CRUD methods specific to Source. +type SourceRepository interface { + BaseRepository[Source] + ListByWorkID(ctx context.Context, workID uint) ([]Source, error) + FindByURL(ctx context.Context, url string) (*Source, error) +} + +// BookRepository defines CRUD methods specific to Book. +type BookRepository interface { + BaseRepository[Book] + ListByAuthorID(ctx context.Context, authorID uint) ([]Book, error) + ListByPublisherID(ctx context.Context, publisherID uint) ([]Book, error) + ListByWorkID(ctx context.Context, workID uint) ([]Book, error) + FindByISBN(ctx context.Context, isbn string) (*Book, error) +} + +// BookmarkRepository defines CRUD methods specific to Bookmark. +type BookmarkRepository interface { + BaseRepository[Bookmark] + ListByUserID(ctx context.Context, userID uint) ([]Bookmark, error) + ListByWorkID(ctx context.Context, workID uint) ([]Bookmark, error) +} + +// CategoryRepository defines CRUD methods specific to Category. +type CategoryRepository interface { + BaseRepository[Category] + FindByName(ctx context.Context, name string) (*Category, error) + ListByWorkID(ctx context.Context, workID uint) ([]Category, error) + ListByParentID(ctx context.Context, parentID *uint) ([]Category, error) +} + +// CityRepository defines CRUD methods specific to City. +type CityRepository interface { + BaseRepository[City] + ListByCountryID(ctx context.Context, countryID uint) ([]City, error) +} + +// CollectionRepository defines CRUD methods specific to Collection. +type CollectionRepository interface { + BaseRepository[Collection] + ListByUserID(ctx context.Context, userID uint) ([]Collection, error) + ListPublic(ctx context.Context) ([]Collection, error) + ListByWorkID(ctx context.Context, workID uint) ([]Collection, error) +} + +// CommentRepository defines CRUD methods specific to Comment. +type CommentRepository interface { + BaseRepository[Comment] + ListByUserID(ctx context.Context, userID uint) ([]Comment, error) + ListByWorkID(ctx context.Context, workID uint) ([]Comment, error) + ListByTranslationID(ctx context.Context, translationID uint) ([]Comment, error) + ListByParentID(ctx context.Context, parentID uint) ([]Comment, error) +} + +// ContributionRepository defines CRUD methods specific to Contribution. +type ContributionRepository interface { + BaseRepository[Contribution] + ListByUserID(ctx context.Context, userID uint) ([]Contribution, error) + ListByReviewerID(ctx context.Context, reviewerID uint) ([]Contribution, error) + ListByWorkID(ctx context.Context, workID uint) ([]Contribution, error) + ListByTranslationID(ctx context.Context, translationID uint) ([]Contribution, error) + ListByStatus(ctx context.Context, status string) ([]Contribution, error) +} + +// CopyrightClaimRepository defines CRUD methods specific to CopyrightClaim. +type CopyrightClaimRepository interface { + BaseRepository[CopyrightClaim] + ListByWorkID(ctx context.Context, workID uint) ([]CopyrightClaim, error) + ListByUserID(ctx context.Context, userID uint) ([]CopyrightClaim, error) +} + +// CountryRepository defines CRUD methods specific to Country. +type CountryRepository interface { + BaseRepository[Country] + GetByCode(ctx context.Context, code string) (*Country, error) + ListByContinent(ctx context.Context, continent string) ([]Country, error) +} + +// EdgeRepository defines CRUD methods specific to Edge. +type EdgeRepository interface { + BaseRepository[Edge] + ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]Edge, error) +} + +// EditionRepository defines CRUD methods specific to Edition. +type EditionRepository interface { + BaseRepository[Edition] + ListByBookID(ctx context.Context, bookID uint) ([]Edition, error) + FindByISBN(ctx context.Context, isbn string) (*Edition, error) +} + +// EmailVerificationRepository defines CRUD methods specific to EmailVerification. +type EmailVerificationRepository interface { + BaseRepository[EmailVerification] + GetByToken(ctx context.Context, token string) (*EmailVerification, error) + GetByUserID(ctx context.Context, userID uint) ([]EmailVerification, error) + DeleteExpired(ctx context.Context) error + MarkAsUsed(ctx context.Context, id uint) error +} + +// LikeRepository defines CRUD methods specific to Like. +type LikeRepository interface { + BaseRepository[Like] + ListByUserID(ctx context.Context, userID uint) ([]Like, error) + ListByWorkID(ctx context.Context, workID uint) ([]Like, error) + ListByTranslationID(ctx context.Context, translationID uint) ([]Like, error) + ListByCommentID(ctx context.Context, commentID uint) ([]Like, error) +} + +// PasswordResetRepository defines CRUD methods specific to PasswordReset. +type PasswordResetRepository interface { + BaseRepository[PasswordReset] + GetByToken(ctx context.Context, token string) (*PasswordReset, error) + GetByUserID(ctx context.Context, userID uint) ([]PasswordReset, error) + DeleteExpired(ctx context.Context) error + MarkAsUsed(ctx context.Context, id uint) error +} + +// PlaceRepository defines CRUD methods specific to Place. +type PlaceRepository interface { + BaseRepository[Place] + ListByCountryID(ctx context.Context, countryID uint) ([]Place, error) + ListByCityID(ctx context.Context, cityID uint) ([]Place, error) + FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]Place, error) +} + +// TagRepository defines CRUD methods specific to Tag. +type TagRepository interface { + BaseRepository[Tag] + FindByName(ctx context.Context, name string) (*Tag, error) + ListByWorkID(ctx context.Context, workID uint) ([]Tag, error) +} + +// TranslationRepository defines CRUD methods specific to Translation. +type TranslationRepository interface { + BaseRepository[Translation] + ListByWorkID(ctx context.Context, workID uint) ([]Translation, error) + ListByEntity(ctx context.Context, entityType string, entityID uint) ([]Translation, error) + ListByTranslatorID(ctx context.Context, translatorID uint) ([]Translation, error) + ListByStatus(ctx context.Context, status TranslationStatus) ([]Translation, error) +} + +// UserRepository defines CRUD methods specific to User. +type UserRepository interface { + BaseRepository[User] + FindByUsername(ctx context.Context, username string) (*User, error) + FindByEmail(ctx context.Context, email string) (*User, error) + ListByRole(ctx context.Context, role UserRole) ([]User, error) +} + +// UserProfileRepository defines CRUD methods specific to UserProfile. +type UserProfileRepository interface { + BaseRepository[UserProfile] + GetByUserID(ctx context.Context, userID uint) (*UserProfile, error) +} + +// UserSessionRepository defines CRUD methods specific to UserSession. +type UserSessionRepository interface { + BaseRepository[UserSession] + GetByToken(ctx context.Context, token string) (*UserSession, error) + GetByUserID(ctx context.Context, userID uint) ([]UserSession, error) + DeleteExpired(ctx context.Context) error +} + // QueryOptions provides options for repository queries type QueryOptions struct { Preloads []string @@ -66,87 +251,20 @@ type AuthorRepository interface { ListByCountryID(ctx context.Context, countryID uint) ([]Author, error) } -// BookRepository defines CRUD methods specific to Book. -type BookRepository interface { - BaseRepository[Book] - ListByAuthorID(ctx context.Context, authorID uint) ([]Book, error) - ListByPublisherID(ctx context.Context, publisherID uint) ([]Book, error) - ListByWorkID(ctx context.Context, workID uint) ([]Book, error) - FindByISBN(ctx context.Context, isbn string) (*Book, error) -} - -// UserRepository defines CRUD methods specific to User. -type UserRepository interface { - BaseRepository[User] - FindByUsername(ctx context.Context, username string) (*User, error) - FindByEmail(ctx context.Context, email string) (*User, error) - ListByRole(ctx context.Context, role UserRole) ([]User, error) -} - -// TranslationRepository defines CRUD methods specific to Translation. -type TranslationRepository interface { - BaseRepository[Translation] - ListByWorkID(ctx context.Context, workID uint) ([]Translation, error) - ListByEntity(ctx context.Context, entityType string, entityID uint) ([]Translation, error) - ListByTranslatorID(ctx context.Context, translatorID uint) ([]Translation, error) - ListByStatus(ctx context.Context, status TranslationStatus) ([]Translation, error) -} - -// CommentRepository defines CRUD methods specific to Comment. -type CommentRepository interface { - BaseRepository[Comment] - ListByUserID(ctx context.Context, userID uint) ([]Comment, error) - ListByWorkID(ctx context.Context, workID uint) ([]Comment, error) - ListByTranslationID(ctx context.Context, translationID uint) ([]Comment, error) - ListByParentID(ctx context.Context, parentID uint) ([]Comment, error) -} - -// LikeRepository defines CRUD methods specific to Like. -type LikeRepository interface { - BaseRepository[Like] - ListByUserID(ctx context.Context, userID uint) ([]Like, error) - ListByWorkID(ctx context.Context, workID uint) ([]Like, error) - ListByTranslationID(ctx context.Context, translationID uint) ([]Like, error) - ListByCommentID(ctx context.Context, commentID uint) ([]Like, error) -} - -// BookmarkRepository defines CRUD methods specific to Bookmark. -type BookmarkRepository interface { - BaseRepository[Bookmark] - ListByUserID(ctx context.Context, userID uint) ([]Bookmark, error) - ListByWorkID(ctx context.Context, workID uint) ([]Bookmark, error) -} - -// CollectionRepository defines CRUD methods specific to Collection. -type CollectionRepository interface { - BaseRepository[Collection] - ListByUserID(ctx context.Context, userID uint) ([]Collection, error) - ListPublic(ctx context.Context) ([]Collection, error) - ListByWorkID(ctx context.Context, workID uint) ([]Collection, error) -} - -// TagRepository defines CRUD methods specific to Tag. -type TagRepository interface { - BaseRepository[Tag] - FindByName(ctx context.Context, name string) (*Tag, error) - ListByWorkID(ctx context.Context, workID uint) ([]Tag, error) -} - -// CategoryRepository defines CRUD methods specific to Category. -type CategoryRepository interface { - BaseRepository[Category] - FindByName(ctx context.Context, name string) (*Category, error) - ListByWorkID(ctx context.Context, workID uint) ([]Category, error) - ListByParentID(ctx context.Context, parentID *uint) ([]Category, error) -} // CopyrightRepository defines CRUD methods specific to Copyright. type CopyrightRepository interface { BaseRepository[Copyright] - AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error - DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error - GetByEntity(ctx context.Context, entityID uint, entityType string) ([]Copyright, error) - GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]Copyrightable, error) + AddCopyrightToWork(ctx context.Context, workID uint, copyrightID uint) error + RemoveCopyrightFromWork(ctx context.Context, workID uint, copyrightID uint) error + AddCopyrightToAuthor(ctx context.Context, authorID uint, copyrightID uint) error + RemoveCopyrightFromAuthor(ctx context.Context, authorID uint, copyrightID uint) error + AddCopyrightToBook(ctx context.Context, bookID uint, copyrightID uint) error + RemoveCopyrightFromBook(ctx context.Context, bookID uint, copyrightID uint) error + AddCopyrightToPublisher(ctx context.Context, publisherID uint, copyrightID uint) error + RemoveCopyrightFromPublisher(ctx context.Context, publisherID uint, copyrightID uint) error + AddCopyrightToSource(ctx context.Context, sourceID uint, copyrightID uint) error + RemoveCopyrightFromSource(ctx context.Context, sourceID uint, copyrightID uint) error AddTranslation(ctx context.Context, translation *CopyrightTranslation) error GetTranslations(ctx context.Context, copyrightID uint) ([]CopyrightTranslation, error) GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*CopyrightTranslation, error) diff --git a/internal/domain/like/repo.go b/internal/domain/like/repo.go deleted file mode 100644 index cc80ddf..0000000 --- a/internal/domain/like/repo.go +++ /dev/null @@ -1,16 +0,0 @@ -package like - -import ( - "context" - "tercul/internal/domain" -) - -// LikeRepository defines CRUD methods specific to Like. -type LikeRepository interface { - domain.BaseRepository[domain.Like] - - ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error) - ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Like, error) - ListByCommentID(ctx context.Context, commentID uint) ([]domain.Like, error) -} diff --git a/internal/domain/monetization/repo.go b/internal/domain/monetization/repo.go deleted file mode 100644 index c4e4233..0000000 --- a/internal/domain/monetization/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package monetization - -import ( - "context" - "tercul/internal/domain" -) - -// MonetizationRepository defines CRUD methods specific to Monetization. -type MonetizationRepository interface { - domain.BaseRepository[domain.Monetization] - - ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error) - ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error) - ListByBookID(ctx context.Context, bookID uint) ([]domain.Monetization, error) -} diff --git a/internal/domain/password_reset/repo.go b/internal/domain/password_reset/repo.go deleted file mode 100644 index 0601c62..0000000 --- a/internal/domain/password_reset/repo.go +++ /dev/null @@ -1,16 +0,0 @@ -package password_reset - -import ( - "context" - "tercul/internal/domain" -) - -// Password_resetRepository defines CRUD methods specific to Password_reset. -type Password_resetRepository interface { - domain.BaseRepository[domain.PasswordReset] - - GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error) - GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error) - DeleteExpired(ctx context.Context) (error) - MarkAsUsed(ctx context.Context, id uint) (error) -} diff --git a/internal/domain/place/repo.go b/internal/domain/place/repo.go deleted file mode 100644 index 7605be5..0000000 --- a/internal/domain/place/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package place - -import ( - "context" - "tercul/internal/domain" -) - -// PlaceRepository defines CRUD methods specific to Place. -type PlaceRepository interface { - domain.BaseRepository[domain.Place] - - ListByCountryID(ctx context.Context, countryID uint) ([]domain.Place, error) - ListByCityID(ctx context.Context, cityID uint) ([]domain.Place, error) - FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]domain.Place, error) -} diff --git a/internal/domain/publisher/repo.go b/internal/domain/publisher/repo.go deleted file mode 100644 index 53b5874..0000000 --- a/internal/domain/publisher/repo.go +++ /dev/null @@ -1,13 +0,0 @@ -package publisher - -import ( - "context" - "tercul/internal/domain" -) - -// PublisherRepository defines CRUD methods specific to Publisher. -type PublisherRepository interface { - domain.BaseRepository[domain.Publisher] - - ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error) -} diff --git a/internal/domain/source/repo.go b/internal/domain/source/repo.go deleted file mode 100644 index 9e65c81..0000000 --- a/internal/domain/source/repo.go +++ /dev/null @@ -1,14 +0,0 @@ -package source - -import ( - "context" - "tercul/internal/domain" -) - -// SourceRepository defines CRUD methods specific to Source. -type SourceRepository interface { - domain.BaseRepository[domain.Source] - - ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error) - FindByURL(ctx context.Context, url string) (*domain.Source, error) -} diff --git a/internal/domain/tag/repo.go b/internal/domain/tag/repo.go deleted file mode 100644 index f42f921..0000000 --- a/internal/domain/tag/repo.go +++ /dev/null @@ -1,14 +0,0 @@ -package tag - -import ( - "context" - "tercul/internal/domain" -) - -// TagRepository defines CRUD methods specific to Tag. -type TagRepository interface { - domain.BaseRepository[domain.Tag] - - FindByName(ctx context.Context, name string) (*domain.Tag, error) - ListByWorkID(ctx context.Context, workID uint) ([]domain.Tag, error) -} diff --git a/internal/domain/translation/repo.go b/internal/domain/translation/repo.go deleted file mode 100644 index d99de99..0000000 --- a/internal/domain/translation/repo.go +++ /dev/null @@ -1,16 +0,0 @@ -package translation - -import ( - "context" - "tercul/internal/domain" -) - -// TranslationRepository defines CRUD methods specific to Translation. -type TranslationRepository interface { - domain.BaseRepository[domain.Translation] - - ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error) - ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error) - ListByTranslatorID(ctx context.Context, translatorID uint) ([]domain.Translation, error) - ListByStatus(ctx context.Context, status domain.TranslationStatus) ([]domain.Translation, error) -} diff --git a/internal/domain/user/repo.go b/internal/domain/user/repo.go deleted file mode 100644 index 88e0412..0000000 --- a/internal/domain/user/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package user - -import ( - "context" - "tercul/internal/domain" -) - -// UserRepository defines CRUD methods specific to User. -type UserRepository interface { - domain.BaseRepository[domain.User] - - FindByUsername(ctx context.Context, username string) (*domain.User, error) - FindByEmail(ctx context.Context, email string) (*domain.User, error) - ListByRole(ctx context.Context, role domain.UserRole) ([]domain.User, error) -} diff --git a/internal/domain/user_profile/repo.go b/internal/domain/user_profile/repo.go deleted file mode 100644 index 81d406e..0000000 --- a/internal/domain/user_profile/repo.go +++ /dev/null @@ -1,13 +0,0 @@ -package user_profile - -import ( - "context" - "tercul/internal/domain" -) - -// User_profileRepository defines CRUD methods specific to User_profile. -type User_profileRepository interface { - domain.BaseRepository[domain.UserProfile] - - GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error) -} diff --git a/internal/domain/user_session/repo.go b/internal/domain/user_session/repo.go deleted file mode 100644 index 49df108..0000000 --- a/internal/domain/user_session/repo.go +++ /dev/null @@ -1,15 +0,0 @@ -package user_session - -import ( - "context" - "tercul/internal/domain" -) - -// User_sessionRepository defines CRUD methods specific to User_session. -type User_sessionRepository interface { - domain.BaseRepository[domain.UserSession] - - GetByToken(ctx context.Context, token string) (*domain.UserSession, error) - GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error) - DeleteExpired(ctx context.Context) (error) -} diff --git a/internal/domain/work/repo.go b/internal/domain/work/repo.go deleted file mode 100644 index c215ab3..0000000 --- a/internal/domain/work/repo.go +++ /dev/null @@ -1,18 +0,0 @@ -package work - -import ( - "context" - "tercul/internal/domain" -) - -// WorkRepository defines CRUD methods specific to Work. -type WorkRepository interface { - domain.BaseRepository[domain.Work] - - FindByTitle(ctx context.Context, title string) ([]domain.Work, error) - FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) - FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) - FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) - GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) - ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) -} diff --git a/internal/testutil/integration_test_utils.go b/internal/testutil/integration_test_utils.go index 295dbb5..ecbab82 100644 --- a/internal/testutil/integration_test_utils.go +++ b/internal/testutil/integration_test_utils.go @@ -35,6 +35,11 @@ type IntegrationTestSuite struct { CollectionRepo domain.CollectionRepository TagRepo domain.TagRepository CategoryRepo domain.CategoryRepository + BookRepo domain.BookRepository + MonetizationRepo domain.MonetizationRepository + PublisherRepo domain.PublisherRepository + SourceRepo domain.SourceRepository + CopyrightRepo domain.CopyrightRepository // Services WorkCommands *work.WorkCommands @@ -139,6 +144,16 @@ func (s *IntegrationTestSuite) setupInMemoryDB(config *TestConfig) { &domain.Book{}, &domain.Publisher{}, &domain.Source{}, + &domain.WorkCopyright{}, + &domain.AuthorCopyright{}, + &domain.BookCopyright{}, + &domain.PublisherCopyright{}, + &domain.SourceCopyright{}, + &domain.WorkMonetization{}, + &domain.AuthorMonetization{}, + &domain.BookMonetization{}, + &domain.PublisherMonetization{}, + &domain.SourceMonetization{}, // &domain.WorkAnalytics{}, // Commented out as it's not in models package &domain.ReadabilityScore{}, &domain.WritingStyle{}, @@ -166,6 +181,11 @@ func (s *IntegrationTestSuite) setupInMemoryDB(config *TestConfig) { s.CollectionRepo = sql.NewCollectionRepository(db) s.TagRepo = sql.NewTagRepository(db) s.CategoryRepo = sql.NewCategoryRepository(db) + s.BookRepo = sql.NewBookRepository(db) + s.MonetizationRepo = sql.NewMonetizationRepository(db) + s.PublisherRepo = sql.NewPublisherRepository(db) + s.SourceRepo = sql.NewSourceRepository(db) + s.CopyrightRepo = sql.NewCopyrightRepository(db) } // setupMockRepositories sets up mock repositories for testing