Fix build issues and refactor for maintainability

This commit is contained in:
google-labs-jules[bot] 2025-09-05 21:37:42 +00:00
parent 8797cec718
commit 52348462a6
78 changed files with 269 additions and 186 deletions

View File

@ -50,23 +50,11 @@ func main() {
// Create GraphQL server // Create GraphQL server
resolver := &graph.Resolver{ resolver := &graph.Resolver{
WorkRepo: appBuilder.GetRepositories().WorkRepository, App: appBuilder.GetApplication(),
UserRepo: appBuilder.GetRepositories().UserRepository,
AuthorRepo: appBuilder.GetRepositories().AuthorRepository,
TranslationRepo: appBuilder.GetRepositories().TranslationRepository,
CommentRepo: appBuilder.GetRepositories().CommentRepository,
LikeRepo: appBuilder.GetRepositories().LikeRepository,
BookmarkRepo: appBuilder.GetRepositories().BookmarkRepository,
CollectionRepo: appBuilder.GetRepositories().CollectionRepository,
TagRepo: appBuilder.GetRepositories().TagRepository,
CategoryRepo: appBuilder.GetRepositories().CategoryRepository,
WorkService: appBuilder.GetServices().WorkService,
Localization: appBuilder.GetServices().LocalizationService,
AuthService: appBuilder.GetServices().AuthService,
} }
jwtManager := auth.NewJWTManager() jwtManager := auth.NewJWTManager()
srv := graph.NewServerWithAuth(resolver, jwtManager) srv := NewServerWithAuth(resolver, jwtManager)
graphQLServer := &http.Server{ graphQLServer := &http.Server{
Addr: config.Cfg.ServerPort, Addr: config.Cfg.ServerPort,
Handler: srv, Handler: srv,

View File

@ -2,15 +2,15 @@ package main
import ( import (
"net/http" "net/http"
"tercul/internal/adapters/graphql"
"tercul/internal/platform/auth" "tercul/internal/platform/auth"
"github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler"
) )
// NewServer creates a new GraphQL server with the given resolver // NewServer creates a new GraphQL server with the given resolver
func NewServer(resolver *Resolver) http.Handler { func NewServer(resolver *graphql.Resolver) http.Handler {
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver})) srv := handler.NewDefaultServer(graphql.NewExecutableSchema(graphql.Config{Resolvers: resolver}))
// Create a mux to handle GraphQL endpoint only (no playground here; served separately in production) // Create a mux to handle GraphQL endpoint only (no playground here; served separately in production)
mux := http.NewServeMux() mux := http.NewServeMux()
@ -20,8 +20,8 @@ func NewServer(resolver *Resolver) http.Handler {
} }
// NewServerWithAuth creates a new GraphQL server with authentication middleware // NewServerWithAuth creates a new GraphQL server with authentication middleware
func NewServerWithAuth(resolver *Resolver, jwtManager *auth.JWTManager) http.Handler { func NewServerWithAuth(resolver *graphql.Resolver, jwtManager *auth.JWTManager) http.Handler {
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver})) srv := handler.NewDefaultServer(graphql.NewExecutableSchema(graphql.Config{Resolvers: resolver}))
// Apply authentication middleware to GraphQL endpoint // Apply authentication middleware to GraphQL endpoint
authHandler := auth.GraphQLAuthMiddleware(jwtManager)(srv) authHandler := auth.GraphQLAuthMiddleware(jwtManager)(srv)

View File

@ -2,14 +2,14 @@ package main
import ( import (
"context" "context"
"log"
"tercul/internal/app" "tercul/internal/app"
"tercul/internal/jobs/linguistics" "tercul/internal/jobs/linguistics"
"tercul/internal/platform/config" "tercul/internal/platform/config"
log "tercul/internal/platform/log"
) )
func main() { func main() {
log.Println("Starting enrichment tool...") log.LogInfo("Starting enrichment tool...")
// Load configuration from environment variables // Load configuration from environment variables
config.LoadConfig() config.LoadConfig()
@ -36,8 +36,8 @@ func main() {
} }
// Enqueue analysis for each work // Enqueue analysis for each work
for _, work := range works.Data { for _, work := range works.Items {
err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynqClient(), work.ID) err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynq(), work.ID)
if err != nil { if err != nil {
log.LogError("Failed to enqueue analysis for work", log.LogError("Failed to enqueue analysis for work",
log.F("workID", work.ID), log.F("workID", work.ID),
@ -45,5 +45,5 @@ func main() {
} }
} }
log.Println("Enrichment tool finished.") log.LogInfo("Enrichment tool finished.")
} }

View File

@ -1,3 +1,5 @@
//go:build tools
package main package main
import ( import (

View File

@ -1,3 +1,5 @@
//go:build tools
package main package main
import ( import (

View File

@ -1,3 +1,5 @@
//go:build tools
package main package main
import ( import (

View File

@ -4,10 +4,10 @@ import "context"
// resolveWorkContent uses Localization service to fetch preferred content // resolveWorkContent uses Localization service to fetch preferred content
func (r *queryResolver) resolveWorkContent(ctx context.Context, workID uint, preferredLanguage string) *string { func (r *queryResolver) resolveWorkContent(ctx context.Context, workID uint, preferredLanguage string) *string {
if r.Localization == nil { if r.App.Localization == nil {
return nil return nil
} }
content, err := r.Localization.GetWorkContent(ctx, workID, preferredLanguage) content, err := r.App.Localization.GetWorkContent(ctx, workID, preferredLanguage)
if err != nil || content == "" { if err != nil || content == "" {
return nil return nil
} }

View File

@ -9,7 +9,7 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"tercul/internal/adapters/graphql" graph "tercul/internal/adapters/graphql"
"tercul/internal/testutil" "tercul/internal/testutil"
"github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler"
@ -140,9 +140,9 @@ func (s *GraphQLIntegrationSuite) TestQueryWork() {
// TestQueryWorks tests the works query // TestQueryWorks tests the works query
func (s *GraphQLIntegrationSuite) TestQueryWorks() { func (s *GraphQLIntegrationSuite) TestQueryWorks() {
// Create test works // Create test works
work1 := s.CreateTestWork("Test Work 1", "en", "Test content for work 1") s.CreateTestWork("Test Work 1", "en", "Test content for work 1")
work2 := s.CreateTestWork("Test Work 2", "en", "Test content for work 2") s.CreateTestWork("Test Work 2", "en", "Test content for work 2")
work3 := s.CreateTestWork("Test Work 3", "fr", "Test content for work 3") s.CreateTestWork("Test Work 3", "fr", "Test content for work 3")
// Define the query // Define the query
query := ` query := `

View File

@ -7,6 +7,7 @@ package graphql
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"strconv" "strconv"
"tercul/internal/adapters/graphql/model" "tercul/internal/adapters/graphql/model"
"tercul/internal/app/auth" "tercul/internal/app/auth"
@ -83,8 +84,8 @@ func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput
// Create domain model // Create domain model
work := &domain.Work{ work := &domain.Work{
Title: input.Name, Title: input.Name,
Description: *input.Description, TranslatableModel: domain.TranslatableModel{Language: input.Language},
Language: input.Language, // Description: *input.Description,
// Other fields can be set here // Other fields can be set here
} }
@ -377,7 +378,7 @@ func (r *queryResolver) Author(ctx context.Context, id string) (*model.Author, e
// Authors is the resolver for the authors field. // Authors is the resolver for the authors field.
func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32, search *string, countryID *string) ([]*model.Author, error) { func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32, search *string, countryID *string) ([]*model.Author, error) {
var authors []models2.Author var authors []domain.Author
var err error var err error
if countryID != nil { if countryID != nil {
@ -385,9 +386,9 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
if err != nil { if err != nil {
return nil, err return nil, err
} }
authors, err = r.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint)) authors, err = r.App.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint))
} else { } else {
result, err := r.AuthorRepo.List(ctx, 1, 1000) // Use pagination result, err := r.App.AuthorRepo.List(ctx, 1, 1000) // Use pagination
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -402,8 +403,8 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
var result []*model.Author var result []*model.Author
for _, a := range authors { for _, a := range authors {
var bio *string var bio *string
if r.Localization != nil { if r.App.Localization != nil {
if b, err := r.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && b != "" { if b, err := r.App.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && b != "" {
bio = &b bio = &b
} }
} }
@ -435,29 +436,29 @@ func (r *queryResolver) UserByUsername(ctx context.Context, username string) (*m
// Users is the resolver for the users field. // Users is the resolver for the users field.
func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32, role *model.UserRole) ([]*model.User, error) { func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32, role *model.UserRole) ([]*model.User, error) {
var users []models2.User var users []domain.User
var err error var err error
if role != nil { if role != nil {
// Convert GraphQL role to model role // Convert GraphQL role to model role
var modelRole models2.UserRole var modelRole domain.UserRole
switch *role { switch *role {
case model.UserRoleReader: case model.UserRoleReader:
modelRole = models2.UserRoleReader modelRole = domain.UserRoleReader
case model.UserRoleContributor: case model.UserRoleContributor:
modelRole = models2.UserRoleContributor modelRole = domain.UserRoleContributor
case model.UserRoleReviewer: case model.UserRoleReviewer:
modelRole = models2.UserRoleReviewer modelRole = domain.UserRoleReviewer
case model.UserRoleEditor: case model.UserRoleEditor:
modelRole = models2.UserRoleEditor modelRole = domain.UserRoleEditor
case model.UserRoleAdmin: case model.UserRoleAdmin:
modelRole = models2.UserRoleAdmin modelRole = domain.UserRoleAdmin
default: default:
return nil, fmt.Errorf("invalid user role: %s", *role) return nil, fmt.Errorf("invalid user role: %s", *role)
} }
users, err = r.UserRepo.ListByRole(ctx, modelRole) users, err = r.App.UserRepo.ListByRole(ctx, modelRole)
} else { } else {
result, err := r.UserRepo.List(ctx, 1, 1000) // Use pagination result, err := r.App.UserRepo.List(ctx, 1, 1000) // Use pagination
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -474,15 +475,15 @@ func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32,
// Convert model role to GraphQL role // Convert model role to GraphQL role
var graphqlRole model.UserRole var graphqlRole model.UserRole
switch u.Role { switch u.Role {
case models2.UserRoleReader: case domain.UserRoleReader:
graphqlRole = model.UserRoleReader graphqlRole = model.UserRoleReader
case models2.UserRoleContributor: case domain.UserRoleContributor:
graphqlRole = model.UserRoleContributor graphqlRole = model.UserRoleContributor
case models2.UserRoleReviewer: case domain.UserRoleReviewer:
graphqlRole = model.UserRoleReviewer graphqlRole = model.UserRoleReviewer
case models2.UserRoleEditor: case domain.UserRoleEditor:
graphqlRole = model.UserRoleEditor graphqlRole = model.UserRoleEditor
case models2.UserRoleAdmin: case domain.UserRoleAdmin:
graphqlRole = model.UserRoleAdmin graphqlRole = model.UserRoleAdmin
default: default:
graphqlRole = model.UserRoleReader graphqlRole = model.UserRoleReader
@ -526,7 +527,7 @@ func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error)
return nil, err return nil, err
} }
tag, err := r.TagRepo.GetByID(ctx, uint(tagID)) tag, err := r.App.TagRepo.GetByID(ctx, uint(tagID))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -539,7 +540,7 @@ func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error)
// Tags is the resolver for the tags field. // Tags is the resolver for the tags field.
func (r *queryResolver) Tags(ctx context.Context, limit *int32, offset *int32) ([]*model.Tag, error) { func (r *queryResolver) Tags(ctx context.Context, limit *int32, offset *int32) ([]*model.Tag, error) {
paginatedResult, err := r.TagRepo.List(ctx, 1, 1000) // Use pagination paginatedResult, err := r.App.TagRepo.List(ctx, 1, 1000) // Use pagination
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -563,7 +564,7 @@ func (r *queryResolver) Category(ctx context.Context, id string) (*model.Categor
return nil, err return nil, err
} }
category, err := r.CategoryRepo.GetByID(ctx, uint(categoryID)) category, err := r.App.CategoryRepo.GetByID(ctx, uint(categoryID))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -576,7 +577,7 @@ func (r *queryResolver) Category(ctx context.Context, id string) (*model.Categor
// Categories is the resolver for the categories field. // Categories is the resolver for the categories field.
func (r *queryResolver) Categories(ctx context.Context, limit *int32, offset *int32) ([]*model.Category, error) { func (r *queryResolver) Categories(ctx context.Context, limit *int32, offset *int32) ([]*model.Category, error) {
paginatedResult, err := r.CategoryRepo.List(ctx, 1, 1000) paginatedResult, err := r.App.CategoryRepo.List(ctx, 1, 1000)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -6,6 +6,7 @@ import (
"tercul/internal/app/localization" "tercul/internal/app/localization"
"tercul/internal/app/search" "tercul/internal/app/search"
"tercul/internal/app/work" "tercul/internal/app/work"
"tercul/internal/domain"
) )
// Application is a container for all the application-layer services. // Application is a container for all the application-layer services.
@ -19,4 +20,10 @@ type Application struct {
Search search.IndexService Search search.IndexService
WorkCommands *work.WorkCommands WorkCommands *work.WorkCommands
WorkQueries *work.WorkQueries WorkQueries *work.WorkQueries
// Repositories - to be refactored into app services
AuthorRepo domain.AuthorRepository
UserRepo domain.UserRepository
TagRepo domain.TagRepository
CategoryRepo domain.CategoryRepository
} }

View File

@ -7,14 +7,12 @@ import (
"tercul/internal/app/search" "tercul/internal/app/search"
"tercul/internal/app/work" "tercul/internal/app/work"
"tercul/internal/data/sql" "tercul/internal/data/sql"
"tercul/internal/domain"
"tercul/internal/platform/cache" "tercul/internal/platform/cache"
"tercul/internal/platform/config" "tercul/internal/platform/config"
"tercul/internal/platform/db" "tercul/internal/platform/db"
"tercul/internal/platform/log" "tercul/internal/platform/log"
auth_platform "tercul/internal/platform/auth" auth_platform "tercul/internal/platform/auth"
"tercul/internal/jobs/linguistics" "tercul/internal/jobs/linguistics"
"time"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"github.com/weaviate/weaviate-go-client/v5/weaviate" "github.com/weaviate/weaviate-go-client/v5/weaviate"
@ -110,6 +108,9 @@ func (b *ApplicationBuilder) BuildApplication() error {
// I need to add all the other repos here. For now, I'll just add the ones I need for the services. // I need to add all the other repos here. For now, I'll just add the ones I need for the services.
translationRepo := sql.NewTranslationRepository(b.dbConn) translationRepo := sql.NewTranslationRepository(b.dbConn)
copyrightRepo := sql.NewCopyrightRepository(b.dbConn) copyrightRepo := sql.NewCopyrightRepository(b.dbConn)
authorRepo := sql.NewAuthorRepository(b.dbConn)
tagRepo := sql.NewTagRepository(b.dbConn)
categoryRepo := sql.NewCategoryRepository(b.dbConn)
// Initialize application services // Initialize application services
@ -136,6 +137,10 @@ func (b *ApplicationBuilder) BuildApplication() error {
CopyrightQueries: copyrightQueries, CopyrightQueries: copyrightQueries,
Localization: localizationService, Localization: localizationService,
Search: searchService, Search: searchService,
AuthorRepo: authorRepo,
UserRepo: userRepo,
TagRepo: tagRepo,
CategoryRepo: categoryRepo,
} }
log.LogInfo("Application layer initialized successfully") log.LogInfo("Application layer initialized successfully")
@ -159,6 +164,21 @@ func (b *ApplicationBuilder) GetApplication() *Application {
return b.App return b.App
} }
// GetDB returns the database connection
func (b *ApplicationBuilder) GetDB() *gorm.DB {
return b.dbConn
}
// GetAsynq returns the Asynq client
func (b *ApplicationBuilder) GetAsynq() *asynq.Client {
return b.asynqClient
}
// GetLinguisticsFactory returns the linguistics factory
func (b *ApplicationBuilder) GetLinguisticsFactory() *linguistics.LinguisticsFactory {
return b.linguistics
}
// Close closes all resources // Close closes all resources
func (b *ApplicationBuilder) Close() error { func (b *ApplicationBuilder) Close() error {
if b.asynqClient != nil { if b.asynqClient != nil {

View File

@ -1,15 +1,11 @@
package app package app
import ( import (
"net/http"
"tercul/internal/jobs/linguistics" "tercul/internal/jobs/linguistics"
syncjob "tercul/internal/jobs/sync" syncjob "tercul/internal/jobs/sync"
"tercul/internal/platform/auth"
"tercul/internal/platform/config" "tercul/internal/platform/config"
"tercul/internal/platform/log" "tercul/internal/platform/log"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
) )
@ -46,8 +42,8 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
// Create sync job instance // Create sync job instance
syncJobInstance := syncjob.NewSyncJob( syncJobInstance := syncjob.NewSyncJob(
f.appBuilder.GetDatabase(), f.appBuilder.GetDB(),
f.appBuilder.GetAsynqClient(), f.appBuilder.GetAsynq(),
) )
// Register sync job handlers // Register sync job handlers
@ -60,9 +56,9 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
// Create linguistic sync job // Create linguistic sync job
linguisticSyncJob := linguistics.NewLinguisticSyncJob( linguisticSyncJob := linguistics.NewLinguisticSyncJob(
f.appBuilder.GetDatabase(), f.appBuilder.GetDB(),
f.appBuilder.GetLinguistics().GetAnalyzer(), f.appBuilder.GetLinguisticsFactory().GetAnalyzer(),
f.appBuilder.GetAsynqClient(), f.appBuilder.GetAsynq(),
) )
// Create linguistic server and register handlers // Create linguistic server and register handlers

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/author" "tercul/internal/domain/author"
"gorm.io/gorm"
) )
type authorRepository struct { type authorRepository struct {

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"tercul/internal/domain/base" "tercul/internal/domain"
"tercul/internal/platform/config" "tercul/internal/platform/config"
"tercul/internal/platform/log" "tercul/internal/platform/log"
"time" "time"
@ -28,7 +28,7 @@ type BaseRepositoryImpl[T any] struct {
} }
// NewBaseRepositoryImpl creates a new BaseRepositoryImpl // NewBaseRepositoryImpl creates a new BaseRepositoryImpl
func NewBaseRepositoryImpl[T any](db *gorm.DB) base.BaseRepository[T] { func NewBaseRepositoryImpl[T any](db *gorm.DB) domain.BaseRepository[T] {
return &BaseRepositoryImpl[T]{db: db} return &BaseRepositoryImpl[T]{db: db}
} }

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/book" "tercul/internal/domain/book"
"gorm.io/gorm"
) )
type bookRepository struct { type bookRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/bookmark" "tercul/internal/domain/bookmark"
"gorm.io/gorm"
) )
type bookmarkRepository struct { type bookmarkRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/category" "tercul/internal/domain/category"
"gorm.io/gorm"
) )
type categoryRepository struct { type categoryRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/city" "tercul/internal/domain/city"
"gorm.io/gorm"
) )
type cityRepository struct { type cityRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/collection" "tercul/internal/domain/collection"
"gorm.io/gorm"
) )
type collectionRepository struct { type collectionRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/comment" "tercul/internal/domain/comment"
"gorm.io/gorm"
) )
type commentRepository struct { type commentRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/contribution" "tercul/internal/domain/contribution"
"gorm.io/gorm"
) )
type contributionRepository struct { type contributionRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/copyright_claim" "tercul/internal/domain/copyright_claim"
"gorm.io/gorm"
) )
type copyrightClaimRepository struct { type copyrightClaimRepository struct {
@ -12,7 +14,7 @@ type copyrightClaimRepository struct {
} }
// NewCopyrightClaimRepository creates a new CopyrightClaimRepository. // NewCopyrightClaimRepository creates a new CopyrightClaimRepository.
func NewCopyrightClaimRepository(db *gorm.DB) domain.CopyrightClaimRepository { func NewCopyrightClaimRepository(db *gorm.DB) copyright_claim.Copyright_claimRepository {
return &copyrightClaimRepository{ return &copyrightClaimRepository{
BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db), BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db),
db: db, db: db,

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/copyright" "tercul/internal/domain/copyright"
"gorm.io/gorm"
) )
type copyrightRepository struct { type copyrightRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/country" "tercul/internal/domain/country"
"gorm.io/gorm"
) )
type countryRepository struct { type countryRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/edge" "tercul/internal/domain/edge"
"gorm.io/gorm"
) )
type edgeRepository struct { type edgeRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/edition" "tercul/internal/domain/edition"
"gorm.io/gorm"
) )
type editionRepository struct { type editionRepository struct {

View File

@ -3,9 +3,11 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/email_verification" "tercul/internal/domain/email_verification"
"time" "time"
"gorm.io/gorm"
) )
type emailVerificationRepository struct { type emailVerificationRepository struct {
@ -14,7 +16,7 @@ type emailVerificationRepository struct {
} }
// NewEmailVerificationRepository creates a new EmailVerificationRepository. // NewEmailVerificationRepository creates a new EmailVerificationRepository.
func NewEmailVerificationRepository(db *gorm.DB) domain.EmailVerificationRepository { func NewEmailVerificationRepository(db *gorm.DB) email_verification.Email_verificationRepository {
return &emailVerificationRepository{ return &emailVerificationRepository{
BaseRepository: NewBaseRepositoryImpl[domain.EmailVerification](db), BaseRepository: NewBaseRepositoryImpl[domain.EmailVerification](db),
db: db, db: db,

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/like" "tercul/internal/domain/like"
"gorm.io/gorm"
) )
type likeRepository struct { type likeRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/monetization" "tercul/internal/domain/monetization"
"gorm.io/gorm"
) )
type monetizationRepository struct { type monetizationRepository struct {

View File

@ -3,9 +3,11 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/password_reset" "tercul/internal/domain/password_reset"
"time" "time"
"gorm.io/gorm"
) )
type passwordResetRepository struct { type passwordResetRepository struct {
@ -14,7 +16,7 @@ type passwordResetRepository struct {
} }
// NewPasswordResetRepository creates a new PasswordResetRepository. // NewPasswordResetRepository creates a new PasswordResetRepository.
func NewPasswordResetRepository(db *gorm.DB) domain.PasswordResetRepository { func NewPasswordResetRepository(db *gorm.DB) password_reset.Password_resetRepository {
return &passwordResetRepository{ return &passwordResetRepository{
BaseRepository: NewBaseRepositoryImpl[domain.PasswordReset](db), BaseRepository: NewBaseRepositoryImpl[domain.PasswordReset](db),
db: db, db: db,

View File

@ -2,9 +2,11 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm"
"math" "math"
"tercul/internal/domain"
"tercul/internal/domain/place" "tercul/internal/domain/place"
"gorm.io/gorm"
) )
type placeRepository struct { type placeRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/publisher" "tercul/internal/domain/publisher"
"gorm.io/gorm"
) )
type publisherRepository struct { type publisherRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/source" "tercul/internal/domain/source"
"gorm.io/gorm"
) )
type sourceRepository struct { type sourceRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/tag" "tercul/internal/domain/tag"
"gorm.io/gorm"
) )
type tagRepository struct { type tagRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/translation" "tercul/internal/domain/translation"
"gorm.io/gorm"
) )
type translationRepository struct { type translationRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/user_profile" "tercul/internal/domain/user_profile"
"gorm.io/gorm"
) )
type userProfileRepository struct { type userProfileRepository struct {
@ -13,7 +15,7 @@ type userProfileRepository struct {
} }
// NewUserProfileRepository creates a new UserProfileRepository. // NewUserProfileRepository creates a new UserProfileRepository.
func NewUserProfileRepository(db *gorm.DB) domain.UserProfileRepository { func NewUserProfileRepository(db *gorm.DB) user_profile.User_profileRepository {
return &userProfileRepository{ return &userProfileRepository{
BaseRepository: NewBaseRepositoryImpl[domain.UserProfile](db), BaseRepository: NewBaseRepositoryImpl[domain.UserProfile](db),
db: db, db: db,

View File

@ -3,8 +3,10 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/user" "tercul/internal/domain/user"
"gorm.io/gorm"
) )
type userRepository struct { type userRepository struct {

View File

@ -3,9 +3,11 @@ package sql
import ( import (
"context" "context"
"errors" "errors"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/user_session" "tercul/internal/domain/user_session"
"time" "time"
"gorm.io/gorm"
) )
type userSessionRepository struct { type userSessionRepository struct {
@ -14,7 +16,7 @@ type userSessionRepository struct {
} }
// NewUserSessionRepository creates a new UserSessionRepository. // NewUserSessionRepository creates a new UserSessionRepository.
func NewUserSessionRepository(db *gorm.DB) domain.UserSessionRepository { func NewUserSessionRepository(db *gorm.DB) user_session.User_sessionRepository {
return &userSessionRepository{ return &userSessionRepository{
BaseRepository: NewBaseRepositoryImpl[domain.UserSession](db), BaseRepository: NewBaseRepositoryImpl[domain.UserSession](db),
db: db, db: db,

View File

@ -2,8 +2,10 @@ package sql
import ( import (
"context" "context"
"gorm.io/gorm" "tercul/internal/domain"
"tercul/internal/domain/work" "tercul/internal/domain/work"
"gorm.io/gorm"
) )
type workRepository struct { type workRepository struct {

View File

@ -7,7 +7,7 @@ import (
// AuthorRepository defines CRUD methods specific to Author. // AuthorRepository defines CRUD methods specific to Author.
type AuthorRepository interface { type AuthorRepository interface {
domain.BaseRepositoryRepository[domain.Author] domain.BaseRepository[domain.Author]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error)
ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error) ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error)

View File

@ -7,7 +7,7 @@ import (
// BookRepository defines CRUD methods specific to Book. // BookRepository defines CRUD methods specific to Book.
type BookRepository interface { type BookRepository interface {
domain.BaseRepositoryRepository[domain.Book] domain.BaseRepository[domain.Book]
ListByAuthorID(ctx context.Context, authorID uint) ([]domain.Book, error) ListByAuthorID(ctx context.Context, authorID uint) ([]domain.Book, error)
ListByPublisherID(ctx context.Context, publisherID uint) ([]domain.Book, error) ListByPublisherID(ctx context.Context, publisherID uint) ([]domain.Book, error)

View File

@ -7,7 +7,7 @@ import (
// BookmarkRepository defines CRUD methods specific to Bookmark. // BookmarkRepository defines CRUD methods specific to Bookmark.
type BookmarkRepository interface { type BookmarkRepository interface {
domain.BaseRepositoryRepository[domain.Bookmark] domain.BaseRepository[domain.Bookmark]
ListByUserID(ctx context.Context, userID uint) ([]domain.Bookmark, error) ListByUserID(ctx context.Context, userID uint) ([]domain.Bookmark, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Bookmark, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Bookmark, error)

View File

@ -7,7 +7,7 @@ import (
// CategoryRepository defines CRUD methods specific to Category. // CategoryRepository defines CRUD methods specific to Category.
type CategoryRepository interface { type CategoryRepository interface {
domain.BaseRepositoryRepository[domain.Category] domain.BaseRepository[domain.Category]
FindByName(ctx context.Context, name string) (*domain.Category, error) FindByName(ctx context.Context, name string) (*domain.Category, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Category, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Category, error)

View File

@ -7,7 +7,7 @@ import (
// CityRepository defines CRUD methods specific to City. // CityRepository defines CRUD methods specific to City.
type CityRepository interface { type CityRepository interface {
domain.BaseRepositoryRepository[domain.City] domain.BaseRepository[domain.City]
ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error) ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error)
} }

View File

@ -7,7 +7,7 @@ import (
// CollectionRepository defines CRUD methods specific to Collection. // CollectionRepository defines CRUD methods specific to Collection.
type CollectionRepository interface { type CollectionRepository interface {
domain.BaseRepositoryRepository[domain.Collection] domain.BaseRepository[domain.Collection]
ListByUserID(ctx context.Context, userID uint) ([]domain.Collection, error) ListByUserID(ctx context.Context, userID uint) ([]domain.Collection, error)
ListPublic(ctx context.Context) ([]domain.Collection, error) ListPublic(ctx context.Context) ([]domain.Collection, error)

View File

@ -7,7 +7,7 @@ import (
// CommentRepository defines CRUD methods specific to Comment. // CommentRepository defines CRUD methods specific to Comment.
type CommentRepository interface { type CommentRepository interface {
domain.BaseRepositoryRepository[domain.Comment] domain.BaseRepository[domain.Comment]
ListByUserID(ctx context.Context, userID uint) ([]domain.Comment, error) ListByUserID(ctx context.Context, userID uint) ([]domain.Comment, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Comment, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Comment, error)

View File

@ -7,7 +7,7 @@ import (
// ContributionRepository defines CRUD methods specific to Contribution. // ContributionRepository defines CRUD methods specific to Contribution.
type ContributionRepository interface { type ContributionRepository interface {
domain.BaseRepositoryRepository[domain.Contribution] domain.BaseRepository[domain.Contribution]
ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error) ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error)
ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error) ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error)

View File

@ -7,7 +7,7 @@ import (
// CopyrightRepository defines CRUD methods specific to Copyright. // CopyrightRepository defines CRUD methods specific to Copyright.
type CopyrightRepository interface { type CopyrightRepository interface {
domain.BaseRepositoryRepository[domain.Copyright] domain.BaseRepository[domain.Copyright]
AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error) AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error)
DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error) DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error)

View File

@ -7,7 +7,7 @@ import (
// Copyright_claimRepository defines CRUD methods specific to Copyright_claim. // Copyright_claimRepository defines CRUD methods specific to Copyright_claim.
type Copyright_claimRepository interface { type Copyright_claimRepository interface {
domain.BaseRepositoryRepository[domain.CopyrightClaim] domain.BaseRepository[domain.CopyrightClaim]
ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error)
ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error) ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error)

View File

@ -7,7 +7,7 @@ import (
// CountryRepository defines CRUD methods specific to Country. // CountryRepository defines CRUD methods specific to Country.
type CountryRepository interface { type CountryRepository interface {
domain.BaseRepositoryRepository[domain.Country] domain.BaseRepository[domain.Country]
GetByCode(ctx context.Context, code string) (*domain.Country, error) GetByCode(ctx context.Context, code string) (*domain.Country, error)
ListByContinent(ctx context.Context, continent string) ([]domain.Country, error) ListByContinent(ctx context.Context, continent string) ([]domain.Country, error)

View File

@ -7,7 +7,7 @@ import (
// EdgeRepository defines CRUD methods specific to Edge. // EdgeRepository defines CRUD methods specific to Edge.
type EdgeRepository interface { type EdgeRepository interface {
domain.BaseRepositoryRepository[domain.Edge] domain.BaseRepository[domain.Edge]
ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error) ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error)
} }

View File

@ -7,7 +7,7 @@ import (
// EditionRepository defines CRUD methods specific to Edition. // EditionRepository defines CRUD methods specific to Edition.
type EditionRepository interface { type EditionRepository interface {
domain.BaseRepositoryRepository[domain.Edition] domain.BaseRepository[domain.Edition]
ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error) ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error)
FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error) FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error)

View File

@ -7,7 +7,7 @@ import (
// Email_verificationRepository defines CRUD methods specific to Email_verification. // Email_verificationRepository defines CRUD methods specific to Email_verification.
type Email_verificationRepository interface { type Email_verificationRepository interface {
domain.BaseRepositoryRepository[domain.EmailVerification] domain.BaseRepository[domain.EmailVerification]
GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error) GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error)
GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error) GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error)

View File

@ -7,7 +7,7 @@ import (
// LikeRepository defines CRUD methods specific to Like. // LikeRepository defines CRUD methods specific to Like.
type LikeRepository interface { type LikeRepository interface {
domain.BaseRepositoryRepository[domain.Like] domain.BaseRepository[domain.Like]
ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error) ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error)

View File

@ -7,7 +7,7 @@ import (
// MonetizationRepository defines CRUD methods specific to Monetization. // MonetizationRepository defines CRUD methods specific to Monetization.
type MonetizationRepository interface { type MonetizationRepository interface {
domain.BaseRepositoryRepository[domain.Monetization] domain.BaseRepository[domain.Monetization]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error)
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error) ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error)

View File

@ -7,7 +7,7 @@ import (
// Password_resetRepository defines CRUD methods specific to Password_reset. // Password_resetRepository defines CRUD methods specific to Password_reset.
type Password_resetRepository interface { type Password_resetRepository interface {
domain.BaseRepositoryRepository[domain.PasswordReset] domain.BaseRepository[domain.PasswordReset]
GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error) GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error)
GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error) GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error)

View File

@ -7,9 +7,9 @@ import (
// PlaceRepository defines CRUD methods specific to Place. // PlaceRepository defines CRUD methods specific to Place.
type PlaceRepository interface { type PlaceRepository interface {
domain.BaseRepositoryRepository[domain.Place] domain.BaseRepository[domain.Place]
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Place, error) ListByCountryID(ctx context.Context, countryID uint) ([]domain.Place, error)
ListByCityID(ctx context.Context, cityID uint) ([]domain.Place, error) ListByCityID(ctx context.Context, cityID uint) ([]domain.Place, error)
FindNearby(ctx context.Context, latitude float64, radiusKm float64) ([]domain.Place, error) FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]domain.Place, error)
} }

View File

@ -7,7 +7,7 @@ import (
// PublisherRepository defines CRUD methods specific to Publisher. // PublisherRepository defines CRUD methods specific to Publisher.
type PublisherRepository interface { type PublisherRepository interface {
domain.BaseRepositoryRepository[domain.Publisher] domain.BaseRepository[domain.Publisher]
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error) ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error)
} }

View File

@ -7,7 +7,7 @@ import (
// SourceRepository defines CRUD methods specific to Source. // SourceRepository defines CRUD methods specific to Source.
type SourceRepository interface { type SourceRepository interface {
domain.BaseRepositoryRepository[domain.Source] domain.BaseRepository[domain.Source]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error)
FindByURL(ctx context.Context, url string) (*domain.Source, error) FindByURL(ctx context.Context, url string) (*domain.Source, error)

View File

@ -7,7 +7,7 @@ import (
// TagRepository defines CRUD methods specific to Tag. // TagRepository defines CRUD methods specific to Tag.
type TagRepository interface { type TagRepository interface {
domain.BaseRepositoryRepository[domain.Tag] domain.BaseRepository[domain.Tag]
FindByName(ctx context.Context, name string) (*domain.Tag, error) FindByName(ctx context.Context, name string) (*domain.Tag, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Tag, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Tag, error)

View File

@ -7,7 +7,7 @@ import (
// TranslationRepository defines CRUD methods specific to Translation. // TranslationRepository defines CRUD methods specific to Translation.
type TranslationRepository interface { type TranslationRepository interface {
domain.BaseRepositoryRepository[domain.Translation] domain.BaseRepository[domain.Translation]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error) ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error)
ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error) ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error)

View File

@ -7,7 +7,7 @@ import (
// UserRepository defines CRUD methods specific to User. // UserRepository defines CRUD methods specific to User.
type UserRepository interface { type UserRepository interface {
domain.BaseRepositoryRepository[domain.User] domain.BaseRepository[domain.User]
FindByUsername(ctx context.Context, username string) (*domain.User, error) FindByUsername(ctx context.Context, username string) (*domain.User, error)
FindByEmail(ctx context.Context, email string) (*domain.User, error) FindByEmail(ctx context.Context, email string) (*domain.User, error)

View File

@ -7,7 +7,7 @@ import (
// User_profileRepository defines CRUD methods specific to User_profile. // User_profileRepository defines CRUD methods specific to User_profile.
type User_profileRepository interface { type User_profileRepository interface {
domain.BaseRepositoryRepository[domain.UserProfile] domain.BaseRepository[domain.UserProfile]
GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error) GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error)
} }

View File

@ -7,7 +7,7 @@ import (
// User_sessionRepository defines CRUD methods specific to User_session. // User_sessionRepository defines CRUD methods specific to User_session.
type User_sessionRepository interface { type User_sessionRepository interface {
domain.BaseRepositoryRepository[domain.UserSession] domain.BaseRepository[domain.UserSession]
GetByToken(ctx context.Context, token string) (*domain.UserSession, error) GetByToken(ctx context.Context, token string) (*domain.UserSession, error)
GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error) GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error)

View File

@ -7,12 +7,12 @@ import (
// WorkRepository defines CRUD methods specific to Work. // WorkRepository defines CRUD methods specific to Work.
type WorkRepository interface { type WorkRepository interface {
domain.BaseRepositoryRepository[domain.Work] domain.BaseRepository[domain.Work]
FindByTitle(ctx context.Context, title string) ([]domain.Work, error) FindByTitle(ctx context.Context, title string) ([]domain.Work, error)
FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error)
FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error)
FindByLanguage(ctx context.Context, language string, page int) (*, error) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error)
ListWithTranslations(ctx context.Context, page int) (*, error) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
} }

View File

@ -7,7 +7,7 @@ import (
func TestLinguaLanguageDetector_DetectLanguage(t *testing.T) { func TestLinguaLanguageDetector_DetectLanguage(t *testing.T) {
d := NewLinguaLanguageDetector() d := NewLinguaLanguageDetector()
code, ok := d.DetectLanguage("This is an English sentence.") code, err := d.DetectLanguage("This is an English sentence.")
require.True(t, ok) require.NoError(t, err)
require.NotEmpty(t, code) require.NotEmpty(t, code)
} }

View File

@ -19,7 +19,7 @@ func (d *languageDetector) DetectLanguage(text string) (string, error) {
// or call an external API for language detection // or call an external API for language detection
// For demonstration purposes, we'll use a simple heuristic based on common words // For demonstration purposes, we'll use a simple heuristic based on common words
content := strings.ToLower(text.Body) content := strings.ToLower(text)
// Check for English // Check for English
englishWords := []string{"the", "and", "is", "in", "to", "of", "that", "for"} englishWords := []string{"the", "and", "is", "in", "to", "of", "that", "for"}
@ -35,15 +35,15 @@ func (d *languageDetector) DetectLanguage(text string) (string, error) {
// Determine the most likely language // Determine the most likely language
if englishCount > spanishCount && englishCount > frenchCount { if englishCount > spanishCount && englishCount > frenchCount {
return "en", 0.7, nil return "en", nil
} else if spanishCount > englishCount && spanishCount > frenchCount { } else if spanishCount > englishCount && spanishCount > frenchCount {
return "es", 0.7, nil return "es", nil
} else if frenchCount > englishCount && frenchCount > spanishCount { } else if frenchCount > englishCount && frenchCount > spanishCount {
return "fr", 0.7, nil return "fr", nil
} }
// Default to English if we can't determine the language // Default to English if we can't determine the language
return "en", 0.5, nil return "en", nil
} }
// countWords counts the occurrences of words in a text // countWords counts the occurrences of words in a text

View File

@ -4,21 +4,21 @@ import "testing"
func TestLanguageDetector_Detect_EN(t *testing.T) { func TestLanguageDetector_Detect_EN(t *testing.T) {
d := NewLanguageDetector() d := NewLanguageDetector()
lang, conf, err := d.Detect(Text{Body: " the and is in to of that for the "}) lang, err := d.DetectLanguage(" the and is in to of that for the ")
if err != nil { if err != nil {
t.Fatalf("Detect returned error: %v", err) t.Fatalf("DetectLanguage returned error: %v", err)
} }
if lang != "en" { if lang != "en" {
t.Fatalf("expected language 'en', got %q", lang) t.Fatalf("expected language 'en', got %q", lang)
} }
if conf <= 0 {
t.Errorf("expected positive confidence, got %f", conf)
}
} }
func TestLanguageDetector_Detect_ES(t *testing.T) { func TestLanguageDetector_Detect_ES(t *testing.T) {
d := NewLanguageDetector() d := NewLanguageDetector()
lang, _, _ := d.Detect(Text{Body: " el la es en de que por para el "}) lang, err := d.DetectLanguage(" el la es en de que por para el ")
if err != nil {
t.Fatalf("DetectLanguage returned error: %v", err)
}
if lang != "es" { if lang != "es" {
t.Fatalf("expected language 'es', got %q", lang) t.Fatalf("expected language 'es', got %q", lang)
} }
@ -26,7 +26,10 @@ func TestLanguageDetector_Detect_ES(t *testing.T) {
func TestLanguageDetector_Detect_FR(t *testing.T) { func TestLanguageDetector_Detect_FR(t *testing.T) {
d := NewLanguageDetector() d := NewLanguageDetector()
lang, _, _ := d.Detect(Text{Body: " le la est en de que pour dans le "}) lang, err := d.DetectLanguage(" le la est en de que pour dans le ")
if err != nil {
t.Fatalf("DetectLanguage returned error: %v", err)
}
if lang != "fr" { if lang != "fr" {
t.Fatalf("expected language 'fr', got %q", lang) t.Fatalf("expected language 'fr', got %q", lang)
} }
@ -35,14 +38,11 @@ func TestLanguageDetector_Detect_FR(t *testing.T) {
func TestLanguageDetector_Detect_DefaultEnglish(t *testing.T) { func TestLanguageDetector_Detect_DefaultEnglish(t *testing.T) {
d := NewLanguageDetector() d := NewLanguageDetector()
// Balanced/unknown should default to English per implementation // Balanced/unknown should default to English per implementation
lang, conf, err := d.Detect(Text{Body: " lorem ipsum dolor sit amet "}) lang, err := d.DetectLanguage(" lorem ipsum dolor sit amet ")
if err != nil { if err != nil {
t.Fatalf("Detect returned error: %v", err) t.Fatalf("DetectLanguage returned error: %v", err)
} }
if lang != "en" { if lang != "en" {
t.Fatalf("expected default language 'en', got %q", lang) t.Fatalf("expected default language 'en', got %q", lang)
} }
if conf != 0.5 {
t.Errorf("expected default confidence 0.5, got %f", conf)
}
} }

View File

@ -2,7 +2,7 @@ package linguistics
// Registry holds all the text analysis services // Registry holds all the text analysis services
type Registry struct { type Registry struct {
Lang *LanguageDetector Lang LanguageDetector
Tok *Tokenizer Tok *Tokenizer
Pos *POSTagger Pos *POSTagger
Lem *Lemmatizer Lem *Lemmatizer

View File

@ -52,7 +52,7 @@ func (a *BasicTextAnalyzer) AnalyzeText(ctx context.Context, text string, langua
// Auto-detect language if not provided and a detector exists // Auto-detect language if not provided and a detector exists
if language == "" && a.langDetector != nil { if language == "" && a.langDetector != nil {
if detected, ok := a.langDetector.DetectLanguage(text); ok { if detected, err := a.langDetector.DetectLanguage(text); err == nil {
language = detected language = detected
} }
} }
@ -114,7 +114,7 @@ func (a *BasicTextAnalyzer) AnalyzeTextConcurrently(ctx context.Context, text st
// Auto-detect language if not provided and a detector exists // Auto-detect language if not provided and a detector exists
if language == "" && a.langDetector != nil { if language == "" && a.langDetector != nil {
if detected, ok := a.langDetector.DetectLanguage(text); ok { if detected, err := a.langDetector.DetectLanguage(text); err == nil {
language = detected language = detected
} }
} }

View File

@ -11,8 +11,8 @@ import (
// Mocks for provider interfaces // Mocks for provider interfaces
type mockLangDetector struct{ lang string; ok bool } type mockLangDetector struct{ lang string; err error }
func (m mockLangDetector) DetectLanguage(text string) (string, bool) { return m.lang, m.ok } func (m mockLangDetector) DetectLanguage(text string) (string, error) { return m.lang, m.err }
type mockSentimentProvider struct{ score float64; err error } type mockSentimentProvider struct{ score float64; err error }
func (m mockSentimentProvider) Score(text string, language string) (float64, error) { return m.score, m.err } func (m mockSentimentProvider) Score(text string, language string) (float64, error) { return m.score, m.err }
@ -34,7 +34,7 @@ func TestAnalyzeText_Empty(t *testing.T) {
func TestAnalyzeText_ProvidersAndLangDetection(t *testing.T) { func TestAnalyzeText_ProvidersAndLangDetection(t *testing.T) {
// Arrange // Arrange
a := NewBasicTextAnalyzer(). a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}). WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.75}). WithSentimentProvider(mockSentimentProvider{score: 0.75}).
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "golang", Relevance: 0.42}}}) WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "golang", Relevance: 0.42}}})
@ -84,7 +84,7 @@ func TestAnalyzeTextConcurrently_AggregatesWithProviders(t *testing.T) {
// Providers return consistent values regardless of input // Providers return consistent values regardless of input
kw := []Keyword{{Text: "constant", Relevance: 0.3}} kw := []Keyword{{Text: "constant", Relevance: 0.3}}
a := NewBasicTextAnalyzer(). a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}). WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.5}). WithSentimentProvider(mockSentimentProvider{score: 0.5}).
WithKeywordProvider(mockKeywordProvider{kws: kw}) WithKeywordProvider(mockKeywordProvider{kws: kw})
@ -119,7 +119,7 @@ func TestAnalyzeTextConcurrently_AggregatesWithProviders(t *testing.T) {
func TestAnalyzeTextConcurrently_ContextCanceled(t *testing.T) { func TestAnalyzeTextConcurrently_ContextCanceled(t *testing.T) {
a := NewBasicTextAnalyzer(). a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}). WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.9}). WithSentimentProvider(mockSentimentProvider{score: 0.9}).
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "x", Relevance: 0.1}}}) WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "x", Relevance: 0.1}}})

View File

@ -16,7 +16,7 @@ var (
"do": {}, "does": {}, "did": {}, "will": {}, "would": {}, "could": {}, "do": {}, "does": {}, "did": {}, "will": {}, "would": {}, "could": {},
"should": {}, "may": {}, "might": {}, "can": {}, "this": {}, "that": {}, "should": {}, "may": {}, "might": {}, "can": {}, "this": {}, "that": {},
"these": {}, "those": {}, "i": {}, "you": {}, "he": {}, "she": {}, "these": {}, "those": {}, "i": {}, "you": {}, "he": {}, "she": {},
"it": {}, "we": {}, "they": {}, "me": {}, "him": {}, "hers": {}, "it": {}, "we": {}, "they": {}, "me": {}, "him": {}, "hers": {}, "over": {},
"us": {}, "them": {}, "my": {}, "your": {}, "his": {}, "its": {}, "us": {}, "them": {}, "my": {}, "your": {}, "his": {}, "its": {},
"our": {}, "their": {}, "our": {}, "their": {},
} }

View File

@ -2,11 +2,9 @@ package testutil
import ( import (
"context" "context"
"fmt"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"testing"
"time" "time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -16,6 +14,7 @@ import (
graph "tercul/internal/adapters/graphql" graph "tercul/internal/adapters/graphql"
"tercul/internal/app/auth" "tercul/internal/app/auth"
auth_platform "tercul/internal/platform/auth"
"tercul/internal/app/localization" "tercul/internal/app/localization"
"tercul/internal/app/work" "tercul/internal/app/work"
"tercul/internal/data/sql" "tercul/internal/data/sql"
@ -41,7 +40,8 @@ type IntegrationTestSuite struct {
WorkCommands *work.WorkCommands WorkCommands *work.WorkCommands
WorkQueries *work.WorkQueries WorkQueries *work.WorkQueries
Localization localization.Service Localization localization.Service
AuthService auth.Service AuthCommands *auth.AuthCommands
AuthQueries *auth.AuthQueries
// Test data // Test data
TestWorks []*domain.Work TestWorks []*domain.Work
@ -189,7 +189,9 @@ func (s *IntegrationTestSuite) setupServices() {
s.WorkCommands = work.NewWorkCommands(s.WorkRepo, mockAnalyzer) s.WorkCommands = work.NewWorkCommands(s.WorkRepo, mockAnalyzer)
s.WorkQueries = work.NewWorkQueries(s.WorkRepo) s.WorkQueries = work.NewWorkQueries(s.WorkRepo)
s.Localization = localization.NewService(s.TranslationRepo) s.Localization = localization.NewService(s.TranslationRepo)
s.AuthService = auth.NewService(s.UserRepo, "test-secret-key") jwtManager := auth_platform.NewJWTManager()
s.AuthCommands = auth.NewAuthCommands(s.UserRepo, jwtManager)
s.AuthQueries = auth.NewAuthQueries(s.UserRepo, jwtManager)
} }
// setupTestData creates initial test data // setupTestData creates initial test data
@ -208,8 +210,8 @@ func (s *IntegrationTestSuite) setupTestData() {
// Create test authors // Create test authors
s.TestAuthors = []*domain.Author{ s.TestAuthors = []*domain.Author{
{Name: "Test Author 1", Language: "en"}, {Name: "Test Author 1", TranslatableModel: domain.TranslatableModel{Language: "en"}},
{Name: "Test Author 2", Language: "fr"}, {Name: "Test Author 2", TranslatableModel: domain.TranslatableModel{Language: "fr"}},
} }
for _, author := range s.TestAuthors { for _, author := range s.TestAuthors {
@ -220,9 +222,9 @@ func (s *IntegrationTestSuite) setupTestData() {
// Create test works // Create test works
s.TestWorks = []*domain.Work{ s.TestWorks = []*domain.Work{
{Title: "Test Work 1", Language: "en"}, {Title: "Test Work 1", TranslatableModel: domain.TranslatableModel{Language: "en"}},
{Title: "Test Work 2", Language: "en"}, {Title: "Test Work 2", TranslatableModel: domain.TranslatableModel{Language: "en"}},
{Title: "Test Work 3", Language: "fr"}, {Title: "Test Work 3", TranslatableModel: domain.TranslatableModel{Language: "fr"}},
} }
for _, work := range s.TestWorks { for _, work := range s.TestWorks {
@ -305,7 +307,7 @@ func (s *IntegrationTestSuite) GetResolver() *graph.Resolver {
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work { func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
work := &domain.Work{ work := &domain.Work{
Title: title, Title: title,
Language: language, TranslatableModel: domain.TranslatableModel{Language: language},
} }
if err := s.WorkRepo.Create(context.Background(), work); err != nil { if err := s.WorkRepo.Create(context.Background(), work); err != nil {

View File

@ -2,7 +2,6 @@ package testutil
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"tercul/internal/domain" "tercul/internal/domain"
"gorm.io/gorm" "gorm.io/gorm"

View File

@ -35,7 +35,7 @@ func (m *MockTranslationRepository) GetByID(ctx context.Context, id uint) (*doma
return &cp, nil return &cp, nil
} }
} }
return nil, domain.ErrEntityNotFound return nil, ErrEntityNotFound
} }
func (m *MockTranslationRepository) Update(ctx context.Context, t *domain.Translation) error { func (m *MockTranslationRepository) Update(ctx context.Context, t *domain.Translation) error {
@ -45,7 +45,7 @@ func (m *MockTranslationRepository) Update(ctx context.Context, t *domain.Transl
return nil return nil
} }
} }
return domain.ErrEntityNotFound return ErrEntityNotFound
} }
func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error { func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error {
@ -55,7 +55,7 @@ func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error {
return nil return nil
} }
} }
return domain.ErrEntityNotFound return ErrEntityNotFound
} }
func (m *MockTranslationRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) { func (m *MockTranslationRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) {

View File

@ -19,9 +19,6 @@ func NewUnifiedMockWorkRepository() *UnifiedMockWorkRepository {
func (m *UnifiedMockWorkRepository) AddWork(work *domain.Work) { func (m *UnifiedMockWorkRepository) AddWork(work *domain.Work) {
work.ID = uint(len(m.Works) + 1) work.ID = uint(len(m.Works) + 1)
if work.Language == "" {
work.Language = "en" // default for tests, can be set by caller
}
m.Works = append(m.Works, work) m.Works = append(m.Works, work)
} }
@ -37,7 +34,7 @@ func (m *UnifiedMockWorkRepository) GetByID(ctx context.Context, id uint) (*doma
return w, nil return w, nil
} }
} }
return nil, domain.ErrEntityNotFound return nil, ErrEntityNotFound
} }
func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *domain.Work) error { func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *domain.Work) error {
@ -47,7 +44,7 @@ func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *domain.W
return nil return nil
} }
} }
return domain.ErrEntityNotFound return ErrEntityNotFound
} }
func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error { func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error {
@ -57,7 +54,7 @@ func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error {
return nil return nil
} }
} }
return domain.ErrEntityNotFound return ErrEntityNotFound
} }
func (m *UnifiedMockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) { func (m *UnifiedMockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
@ -99,7 +96,7 @@ func (m *UnifiedMockWorkRepository) FindWithPreload(ctx context.Context, preload
return w, nil return w, nil
} }
} }
return nil, domain.ErrEntityNotFound return nil, ErrEntityNotFound
} }
func (m *UnifiedMockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Work, error) { func (m *UnifiedMockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Work, error) {
@ -214,7 +211,7 @@ func (m *UnifiedMockWorkRepository) GetWithTranslations(ctx context.Context, id
return w, nil return w, nil
} }
} }
return nil, domain.ErrEntityNotFound return nil, ErrEntityNotFound
} }
func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) { func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {

View File

@ -3,6 +3,7 @@ package testutil
import ( import (
"context" "context"
graph "tercul/internal/adapters/graphql" graph "tercul/internal/adapters/graphql"
"tercul/internal/app"
"tercul/internal/app/work" "tercul/internal/app/work"
"tercul/internal/domain" "tercul/internal/domain"
@ -41,19 +42,30 @@ func (s *SimpleTestSuite) SetupTest() {
// GetResolver returns a minimal GraphQL resolver for testing // GetResolver returns a minimal GraphQL resolver for testing
func (s *SimpleTestSuite) GetResolver() *graph.Resolver { func (s *SimpleTestSuite) GetResolver() *graph.Resolver {
// This needs to be updated to reflect the new resolver structure
// For now, we'll return a resolver with the work commands and queries
return &graph.Resolver{ return &graph.Resolver{
// WorkRepo: s.WorkRepo, // This should be removed from resolver App: &app.Application{
// WorkService: s.WorkService, // This is replaced by commands/queries WorkCommands: s.WorkCommands,
WorkQueries: s.WorkQueries,
Localization: &MockLocalization{},
},
} }
} }
type MockLocalization struct{}
func (m *MockLocalization) GetWorkContent(ctx context.Context, workID uint, preferredLanguage string) (string, error) {
return "Test content for work", nil
}
func (m *MockLocalization) GetAuthorBiography(ctx context.Context, authorID uint, preferredLanguage string) (string, error) {
return "Test biography", nil
}
// CreateTestWork creates a test work with optional content // CreateTestWork creates a test work with optional content
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain.Work { func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
work := &domain.Work{ work := &domain.Work{
Title: title, Title: title,
Language: language, TranslatableModel: domain.TranslatableModel{Language: language},
} }
// Add work to the mock repository // Add work to the mock repository

View File

@ -2,6 +2,7 @@ package testutil
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -15,6 +16,8 @@ import (
"tercul/internal/platform/config" "tercul/internal/platform/config"
) )
var ErrEntityNotFound = errors.New("entity not found")
// TestDB holds the test database connection // TestDB holds the test database connection
var TestDB *gorm.DB var TestDB *gorm.DB