mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
Fix build issues and refactor for maintainability
This commit is contained in:
parent
8797cec718
commit
52348462a6
@ -50,23 +50,11 @@ func main() {
|
||||
|
||||
// Create GraphQL server
|
||||
resolver := &graph.Resolver{
|
||||
WorkRepo: appBuilder.GetRepositories().WorkRepository,
|
||||
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,
|
||||
App: appBuilder.GetApplication(),
|
||||
}
|
||||
|
||||
jwtManager := auth.NewJWTManager()
|
||||
srv := graph.NewServerWithAuth(resolver, jwtManager)
|
||||
srv := NewServerWithAuth(resolver, jwtManager)
|
||||
graphQLServer := &http.Server{
|
||||
Addr: config.Cfg.ServerPort,
|
||||
Handler: srv,
|
||||
|
||||
@ -2,15 +2,15 @@ package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"tercul/internal/adapters/graphql"
|
||||
"tercul/internal/platform/auth"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
)
|
||||
|
||||
// NewServer creates a new GraphQL server with the given resolver
|
||||
func NewServer(resolver *Resolver) http.Handler {
|
||||
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))
|
||||
func NewServer(resolver *graphql.Resolver) http.Handler {
|
||||
srv := handler.NewDefaultServer(graphql.NewExecutableSchema(graphql.Config{Resolvers: resolver}))
|
||||
|
||||
// Create a mux to handle GraphQL endpoint only (no playground here; served separately in production)
|
||||
mux := http.NewServeMux()
|
||||
@ -20,8 +20,8 @@ func NewServer(resolver *Resolver) http.Handler {
|
||||
}
|
||||
|
||||
// NewServerWithAuth creates a new GraphQL server with authentication middleware
|
||||
func NewServerWithAuth(resolver *Resolver, jwtManager *auth.JWTManager) http.Handler {
|
||||
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))
|
||||
func NewServerWithAuth(resolver *graphql.Resolver, jwtManager *auth.JWTManager) http.Handler {
|
||||
srv := handler.NewDefaultServer(graphql.NewExecutableSchema(graphql.Config{Resolvers: resolver}))
|
||||
|
||||
// Apply authentication middleware to GraphQL endpoint
|
||||
authHandler := auth.GraphQLAuthMiddleware(jwtManager)(srv)
|
||||
|
||||
@ -2,14 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"tercul/internal/app"
|
||||
"tercul/internal/jobs/linguistics"
|
||||
"tercul/internal/platform/config"
|
||||
log "tercul/internal/platform/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Starting enrichment tool...")
|
||||
log.LogInfo("Starting enrichment tool...")
|
||||
|
||||
// Load configuration from environment variables
|
||||
config.LoadConfig()
|
||||
@ -36,8 +36,8 @@ func main() {
|
||||
}
|
||||
|
||||
// Enqueue analysis for each work
|
||||
for _, work := range works.Data {
|
||||
err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynqClient(), work.ID)
|
||||
for _, work := range works.Items {
|
||||
err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynq(), work.ID)
|
||||
if err != nil {
|
||||
log.LogError("Failed to enqueue analysis for work",
|
||||
log.F("workID", work.ID),
|
||||
@ -45,5 +45,5 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Enrichment tool finished.")
|
||||
log.LogInfo("Enrichment tool finished.")
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//go:build tools
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//go:build tools
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
//go:build tools
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@ -4,10 +4,10 @@ import "context"
|
||||
|
||||
// resolveWorkContent uses Localization service to fetch preferred content
|
||||
func (r *queryResolver) resolveWorkContent(ctx context.Context, workID uint, preferredLanguage string) *string {
|
||||
if r.Localization == nil {
|
||||
if r.App.Localization == nil {
|
||||
return nil
|
||||
}
|
||||
content, err := r.Localization.GetWorkContent(ctx, workID, preferredLanguage)
|
||||
content, err := r.App.Localization.GetWorkContent(ctx, workID, preferredLanguage)
|
||||
if err != nil || content == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"tercul/internal/adapters/graphql"
|
||||
graph "tercul/internal/adapters/graphql"
|
||||
"tercul/internal/testutil"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
@ -140,9 +140,9 @@ func (s *GraphQLIntegrationSuite) TestQueryWork() {
|
||||
// TestQueryWorks tests the works query
|
||||
func (s *GraphQLIntegrationSuite) TestQueryWorks() {
|
||||
// Create test works
|
||||
work1 := s.CreateTestWork("Test Work 1", "en", "Test content for work 1")
|
||||
work2 := 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 1", "en", "Test content for work 1")
|
||||
s.CreateTestWork("Test Work 2", "en", "Test content for work 2")
|
||||
s.CreateTestWork("Test Work 3", "fr", "Test content for work 3")
|
||||
|
||||
// Define the query
|
||||
query := `
|
||||
|
||||
@ -7,6 +7,7 @@ package graphql
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"tercul/internal/adapters/graphql/model"
|
||||
"tercul/internal/app/auth"
|
||||
@ -82,9 +83,9 @@ func (r *mutationResolver) Login(ctx context.Context, email string, password str
|
||||
func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput) (*model.Work, error) {
|
||||
// Create domain model
|
||||
work := &domain.Work{
|
||||
Title: input.Name,
|
||||
Description: *input.Description,
|
||||
Language: input.Language,
|
||||
Title: input.Name,
|
||||
TranslatableModel: domain.TranslatableModel{Language: input.Language},
|
||||
// Description: *input.Description,
|
||||
// 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.
|
||||
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
|
||||
|
||||
if countryID != nil {
|
||||
@ -385,9 +386,9 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authors, err = r.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint))
|
||||
authors, err = r.App.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint))
|
||||
} 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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -402,8 +403,8 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
|
||||
var result []*model.Author
|
||||
for _, a := range authors {
|
||||
var bio *string
|
||||
if r.Localization != nil {
|
||||
if b, err := r.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && b != "" {
|
||||
if r.App.Localization != nil {
|
||||
if b, err := r.App.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && 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.
|
||||
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
|
||||
|
||||
if role != nil {
|
||||
// Convert GraphQL role to model role
|
||||
var modelRole models2.UserRole
|
||||
var modelRole domain.UserRole
|
||||
switch *role {
|
||||
case model.UserRoleReader:
|
||||
modelRole = models2.UserRoleReader
|
||||
modelRole = domain.UserRoleReader
|
||||
case model.UserRoleContributor:
|
||||
modelRole = models2.UserRoleContributor
|
||||
modelRole = domain.UserRoleContributor
|
||||
case model.UserRoleReviewer:
|
||||
modelRole = models2.UserRoleReviewer
|
||||
modelRole = domain.UserRoleReviewer
|
||||
case model.UserRoleEditor:
|
||||
modelRole = models2.UserRoleEditor
|
||||
modelRole = domain.UserRoleEditor
|
||||
case model.UserRoleAdmin:
|
||||
modelRole = models2.UserRoleAdmin
|
||||
modelRole = domain.UserRoleAdmin
|
||||
default:
|
||||
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 {
|
||||
result, err := r.UserRepo.List(ctx, 1, 1000) // Use pagination
|
||||
result, err := r.App.UserRepo.List(ctx, 1, 1000) // Use pagination
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -474,15 +475,15 @@ func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32,
|
||||
// Convert model role to GraphQL role
|
||||
var graphqlRole model.UserRole
|
||||
switch u.Role {
|
||||
case models2.UserRoleReader:
|
||||
case domain.UserRoleReader:
|
||||
graphqlRole = model.UserRoleReader
|
||||
case models2.UserRoleContributor:
|
||||
case domain.UserRoleContributor:
|
||||
graphqlRole = model.UserRoleContributor
|
||||
case models2.UserRoleReviewer:
|
||||
case domain.UserRoleReviewer:
|
||||
graphqlRole = model.UserRoleReviewer
|
||||
case models2.UserRoleEditor:
|
||||
case domain.UserRoleEditor:
|
||||
graphqlRole = model.UserRoleEditor
|
||||
case models2.UserRoleAdmin:
|
||||
case domain.UserRoleAdmin:
|
||||
graphqlRole = model.UserRoleAdmin
|
||||
default:
|
||||
graphqlRole = model.UserRoleReader
|
||||
@ -526,7 +527,7 @@ func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := r.TagRepo.GetByID(ctx, uint(tagID))
|
||||
tag, err := r.App.TagRepo.GetByID(ctx, uint(tagID))
|
||||
if err != nil {
|
||||
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.
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -563,7 +564,7 @@ func (r *queryResolver) Category(ctx context.Context, id string) (*model.Categor
|
||||
return nil, err
|
||||
}
|
||||
|
||||
category, err := r.CategoryRepo.GetByID(ctx, uint(categoryID))
|
||||
category, err := r.App.CategoryRepo.GetByID(ctx, uint(categoryID))
|
||||
if err != nil {
|
||||
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.
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"tercul/internal/app/localization"
|
||||
"tercul/internal/app/search"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// Application is a container for all the application-layer services.
|
||||
@ -19,4 +20,10 @@ type Application struct {
|
||||
Search search.IndexService
|
||||
WorkCommands *work.WorkCommands
|
||||
WorkQueries *work.WorkQueries
|
||||
|
||||
// Repositories - to be refactored into app services
|
||||
AuthorRepo domain.AuthorRepository
|
||||
UserRepo domain.UserRepository
|
||||
TagRepo domain.TagRepository
|
||||
CategoryRepo domain.CategoryRepository
|
||||
}
|
||||
|
||||
@ -7,14 +7,12 @@ import (
|
||||
"tercul/internal/app/search"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/data/sql"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/platform/cache"
|
||||
"tercul/internal/platform/config"
|
||||
"tercul/internal/platform/db"
|
||||
"tercul/internal/platform/log"
|
||||
auth_platform "tercul/internal/platform/auth"
|
||||
"tercul/internal/jobs/linguistics"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"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.
|
||||
translationRepo := sql.NewTranslationRepository(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
|
||||
@ -136,6 +137,10 @@ func (b *ApplicationBuilder) BuildApplication() error {
|
||||
CopyrightQueries: copyrightQueries,
|
||||
Localization: localizationService,
|
||||
Search: searchService,
|
||||
AuthorRepo: authorRepo,
|
||||
UserRepo: userRepo,
|
||||
TagRepo: tagRepo,
|
||||
CategoryRepo: categoryRepo,
|
||||
}
|
||||
|
||||
log.LogInfo("Application layer initialized successfully")
|
||||
@ -159,6 +164,21 @@ func (b *ApplicationBuilder) GetApplication() *Application {
|
||||
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
|
||||
func (b *ApplicationBuilder) Close() error {
|
||||
if b.asynqClient != nil {
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"tercul/internal/jobs/linguistics"
|
||||
syncjob "tercul/internal/jobs/sync"
|
||||
"tercul/internal/platform/auth"
|
||||
"tercul/internal/platform/config"
|
||||
"tercul/internal/platform/log"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
@ -46,8 +42,8 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
|
||||
|
||||
// Create sync job instance
|
||||
syncJobInstance := syncjob.NewSyncJob(
|
||||
f.appBuilder.GetDatabase(),
|
||||
f.appBuilder.GetAsynqClient(),
|
||||
f.appBuilder.GetDB(),
|
||||
f.appBuilder.GetAsynq(),
|
||||
)
|
||||
|
||||
// Register sync job handlers
|
||||
@ -60,9 +56,9 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
|
||||
|
||||
// Create linguistic sync job
|
||||
linguisticSyncJob := linguistics.NewLinguisticSyncJob(
|
||||
f.appBuilder.GetDatabase(),
|
||||
f.appBuilder.GetLinguistics().GetAnalyzer(),
|
||||
f.appBuilder.GetAsynqClient(),
|
||||
f.appBuilder.GetDB(),
|
||||
f.appBuilder.GetLinguisticsFactory().GetAnalyzer(),
|
||||
f.appBuilder.GetAsynq(),
|
||||
)
|
||||
|
||||
// Create linguistic server and register handlers
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/author"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type authorRepository struct {
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tercul/internal/domain/base"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/platform/config"
|
||||
"tercul/internal/platform/log"
|
||||
"time"
|
||||
@ -28,7 +28,7 @@ type BaseRepositoryImpl[T any] struct {
|
||||
}
|
||||
|
||||
// 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}
|
||||
}
|
||||
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/book"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type bookRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/bookmark"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type bookmarkRepository struct {
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/category"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type categoryRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/city"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type cityRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/collection"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type collectionRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/comment"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type commentRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/contribution"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type contributionRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/copyright_claim"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type copyrightClaimRepository struct {
|
||||
@ -12,7 +14,7 @@ type copyrightClaimRepository struct {
|
||||
}
|
||||
|
||||
// NewCopyrightClaimRepository creates a new CopyrightClaimRepository.
|
||||
func NewCopyrightClaimRepository(db *gorm.DB) domain.CopyrightClaimRepository {
|
||||
func NewCopyrightClaimRepository(db *gorm.DB) copyright_claim.Copyright_claimRepository {
|
||||
return ©rightClaimRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db),
|
||||
db: db,
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/copyright"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type copyrightRepository struct {
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/country"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type countryRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/edge"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type edgeRepository struct {
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/edition"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type editionRepository struct {
|
||||
|
||||
@ -3,9 +3,11 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/email_verification"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type emailVerificationRepository struct {
|
||||
@ -14,7 +16,7 @@ type emailVerificationRepository struct {
|
||||
}
|
||||
|
||||
// NewEmailVerificationRepository creates a new EmailVerificationRepository.
|
||||
func NewEmailVerificationRepository(db *gorm.DB) domain.EmailVerificationRepository {
|
||||
func NewEmailVerificationRepository(db *gorm.DB) email_verification.Email_verificationRepository {
|
||||
return &emailVerificationRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.EmailVerification](db),
|
||||
db: db,
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/like"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type likeRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/monetization"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type monetizationRepository struct {
|
||||
|
||||
@ -3,9 +3,11 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/password_reset"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type passwordResetRepository struct {
|
||||
@ -14,7 +16,7 @@ type passwordResetRepository struct {
|
||||
}
|
||||
|
||||
// NewPasswordResetRepository creates a new PasswordResetRepository.
|
||||
func NewPasswordResetRepository(db *gorm.DB) domain.PasswordResetRepository {
|
||||
func NewPasswordResetRepository(db *gorm.DB) password_reset.Password_resetRepository {
|
||||
return &passwordResetRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.PasswordReset](db),
|
||||
db: db,
|
||||
|
||||
@ -2,9 +2,11 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"math"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/place"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type placeRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/publisher"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type publisherRepository struct {
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/source"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type sourceRepository struct {
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/tag"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type tagRepository struct {
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/translation"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type translationRepository struct {
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/user_profile"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type userProfileRepository struct {
|
||||
@ -13,7 +15,7 @@ type userProfileRepository struct {
|
||||
}
|
||||
|
||||
// NewUserProfileRepository creates a new UserProfileRepository.
|
||||
func NewUserProfileRepository(db *gorm.DB) domain.UserProfileRepository {
|
||||
func NewUserProfileRepository(db *gorm.DB) user_profile.User_profileRepository {
|
||||
return &userProfileRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.UserProfile](db),
|
||||
db: db,
|
||||
|
||||
@ -3,8 +3,10 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/user"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type userRepository struct {
|
||||
|
||||
@ -3,9 +3,11 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/user_session"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type userSessionRepository struct {
|
||||
@ -14,7 +16,7 @@ type userSessionRepository struct {
|
||||
}
|
||||
|
||||
// NewUserSessionRepository creates a new UserSessionRepository.
|
||||
func NewUserSessionRepository(db *gorm.DB) domain.UserSessionRepository {
|
||||
func NewUserSessionRepository(db *gorm.DB) user_session.User_sessionRepository {
|
||||
return &userSessionRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.UserSession](db),
|
||||
db: db,
|
||||
|
||||
@ -2,8 +2,10 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/work"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type workRepository struct {
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// AuthorRepository defines CRUD methods specific to Author.
|
||||
type AuthorRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Author]
|
||||
domain.BaseRepository[domain.Author]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error)
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// BookRepository defines CRUD methods specific to Book.
|
||||
type BookRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Book]
|
||||
domain.BaseRepository[domain.Book]
|
||||
|
||||
ListByAuthorID(ctx context.Context, authorID uint) ([]domain.Book, error)
|
||||
ListByPublisherID(ctx context.Context, publisherID uint) ([]domain.Book, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// BookmarkRepository defines CRUD methods specific to Bookmark.
|
||||
type BookmarkRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Bookmark]
|
||||
domain.BaseRepository[domain.Bookmark]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Bookmark, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Bookmark, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// CategoryRepository defines CRUD methods specific to Category.
|
||||
type CategoryRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Category]
|
||||
domain.BaseRepository[domain.Category]
|
||||
|
||||
FindByName(ctx context.Context, name string) (*domain.Category, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Category, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// CityRepository defines CRUD methods specific to City.
|
||||
type CityRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.City]
|
||||
domain.BaseRepository[domain.City]
|
||||
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// CollectionRepository defines CRUD methods specific to Collection.
|
||||
type CollectionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Collection]
|
||||
domain.BaseRepository[domain.Collection]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Collection, error)
|
||||
ListPublic(ctx context.Context) ([]domain.Collection, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// CommentRepository defines CRUD methods specific to Comment.
|
||||
type CommentRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Comment]
|
||||
domain.BaseRepository[domain.Comment]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Comment, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Comment, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// ContributionRepository defines CRUD methods specific to Contribution.
|
||||
type ContributionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Contribution]
|
||||
domain.BaseRepository[domain.Contribution]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error)
|
||||
ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// CopyrightRepository defines CRUD methods specific to Copyright.
|
||||
type CopyrightRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Copyright]
|
||||
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)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// Copyright_claimRepository defines CRUD methods specific to Copyright_claim.
|
||||
type Copyright_claimRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.CopyrightClaim]
|
||||
domain.BaseRepository[domain.CopyrightClaim]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error)
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// CountryRepository defines CRUD methods specific to Country.
|
||||
type CountryRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Country]
|
||||
domain.BaseRepository[domain.Country]
|
||||
|
||||
GetByCode(ctx context.Context, code string) (*domain.Country, error)
|
||||
ListByContinent(ctx context.Context, continent string) ([]domain.Country, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// EdgeRepository defines CRUD methods specific to Edge.
|
||||
type EdgeRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Edge]
|
||||
domain.BaseRepository[domain.Edge]
|
||||
|
||||
ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// EditionRepository defines CRUD methods specific to Edition.
|
||||
type EditionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Edition]
|
||||
domain.BaseRepository[domain.Edition]
|
||||
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error)
|
||||
FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// Email_verificationRepository defines CRUD methods specific to Email_verification.
|
||||
type Email_verificationRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.EmailVerification]
|
||||
domain.BaseRepository[domain.EmailVerification]
|
||||
|
||||
GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// LikeRepository defines CRUD methods specific to Like.
|
||||
type LikeRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Like]
|
||||
domain.BaseRepository[domain.Like]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// MonetizationRepository defines CRUD methods specific to Monetization.
|
||||
type MonetizationRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Monetization]
|
||||
domain.BaseRepository[domain.Monetization]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// Password_resetRepository defines CRUD methods specific to Password_reset.
|
||||
type Password_resetRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.PasswordReset]
|
||||
domain.BaseRepository[domain.PasswordReset]
|
||||
|
||||
GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error)
|
||||
|
||||
@ -7,9 +7,9 @@ import (
|
||||
|
||||
// PlaceRepository defines CRUD methods specific to Place.
|
||||
type PlaceRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Place]
|
||||
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 float64, radiusKm float64) ([]domain.Place, error)
|
||||
FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]domain.Place, error)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// PublisherRepository defines CRUD methods specific to Publisher.
|
||||
type PublisherRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Publisher]
|
||||
domain.BaseRepository[domain.Publisher]
|
||||
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// SourceRepository defines CRUD methods specific to Source.
|
||||
type SourceRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Source]
|
||||
domain.BaseRepository[domain.Source]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error)
|
||||
FindByURL(ctx context.Context, url string) (*domain.Source, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// TagRepository defines CRUD methods specific to Tag.
|
||||
type TagRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Tag]
|
||||
domain.BaseRepository[domain.Tag]
|
||||
|
||||
FindByName(ctx context.Context, name string) (*domain.Tag, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Tag, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// TranslationRepository defines CRUD methods specific to Translation.
|
||||
type TranslationRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Translation]
|
||||
domain.BaseRepository[domain.Translation]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error)
|
||||
ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// UserRepository defines CRUD methods specific to User.
|
||||
type UserRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.User]
|
||||
domain.BaseRepository[domain.User]
|
||||
|
||||
FindByUsername(ctx context.Context, username string) (*domain.User, error)
|
||||
FindByEmail(ctx context.Context, email string) (*domain.User, error)
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// User_profileRepository defines CRUD methods specific to User_profile.
|
||||
type User_profileRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.UserProfile]
|
||||
domain.BaseRepository[domain.UserProfile]
|
||||
|
||||
GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
// User_sessionRepository defines CRUD methods specific to User_session.
|
||||
type User_sessionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.UserSession]
|
||||
domain.BaseRepository[domain.UserSession]
|
||||
|
||||
GetByToken(ctx context.Context, token string) (*domain.UserSession, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error)
|
||||
|
||||
@ -7,12 +7,12 @@ import (
|
||||
|
||||
// WorkRepository defines CRUD methods specific to Work.
|
||||
type WorkRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Work]
|
||||
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 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)
|
||||
ListWithTranslations(ctx context.Context, page int) (*, error)
|
||||
ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
func TestLinguaLanguageDetector_DetectLanguage(t *testing.T) {
|
||||
d := NewLinguaLanguageDetector()
|
||||
code, ok := d.DetectLanguage("This is an English sentence.")
|
||||
require.True(t, ok)
|
||||
code, err := d.DetectLanguage("This is an English sentence.")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, code)
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ func (d *languageDetector) DetectLanguage(text string) (string, error) {
|
||||
// or call an external API for language detection
|
||||
|
||||
// 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
|
||||
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
|
||||
if englishCount > spanishCount && englishCount > frenchCount {
|
||||
return "en", 0.7, nil
|
||||
return "en", nil
|
||||
} else if spanishCount > englishCount && spanishCount > frenchCount {
|
||||
return "es", 0.7, nil
|
||||
return "es", nil
|
||||
} else if frenchCount > englishCount && frenchCount > spanishCount {
|
||||
return "fr", 0.7, nil
|
||||
return "fr", nil
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@ -4,21 +4,21 @@ import "testing"
|
||||
|
||||
func TestLanguageDetector_Detect_EN(t *testing.T) {
|
||||
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 {
|
||||
t.Fatalf("Detect returned error: %v", err)
|
||||
t.Fatalf("DetectLanguage returned error: %v", err)
|
||||
}
|
||||
if lang != "en" {
|
||||
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) {
|
||||
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" {
|
||||
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) {
|
||||
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" {
|
||||
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) {
|
||||
d := NewLanguageDetector()
|
||||
// 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 {
|
||||
t.Fatalf("Detect returned error: %v", err)
|
||||
t.Fatalf("DetectLanguage returned error: %v", err)
|
||||
}
|
||||
if lang != "en" {
|
||||
t.Fatalf("expected default language 'en', got %q", lang)
|
||||
}
|
||||
if conf != 0.5 {
|
||||
t.Errorf("expected default confidence 0.5, got %f", conf)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package linguistics
|
||||
|
||||
// Registry holds all the text analysis services
|
||||
type Registry struct {
|
||||
Lang *LanguageDetector
|
||||
Lang LanguageDetector
|
||||
Tok *Tokenizer
|
||||
Pos *POSTagger
|
||||
Lem *Lemmatizer
|
||||
|
||||
@ -52,7 +52,7 @@ func (a *BasicTextAnalyzer) AnalyzeText(ctx context.Context, text string, langua
|
||||
|
||||
// Auto-detect language if not provided and a detector exists
|
||||
if language == "" && a.langDetector != nil {
|
||||
if detected, ok := a.langDetector.DetectLanguage(text); ok {
|
||||
if detected, err := a.langDetector.DetectLanguage(text); err == nil {
|
||||
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
|
||||
if language == "" && a.langDetector != nil {
|
||||
if detected, ok := a.langDetector.DetectLanguage(text); ok {
|
||||
if detected, err := a.langDetector.DetectLanguage(text); err == nil {
|
||||
language = detected
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
|
||||
// Mocks for provider interfaces
|
||||
|
||||
type mockLangDetector struct{ lang string; ok bool }
|
||||
func (m mockLangDetector) DetectLanguage(text string) (string, bool) { return m.lang, m.ok }
|
||||
type mockLangDetector struct{ lang string; err error }
|
||||
func (m mockLangDetector) DetectLanguage(text string) (string, error) { return m.lang, m.err }
|
||||
|
||||
type mockSentimentProvider struct{ score float64; err error }
|
||||
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) {
|
||||
// Arrange
|
||||
a := NewBasicTextAnalyzer().
|
||||
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}).
|
||||
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
|
||||
WithSentimentProvider(mockSentimentProvider{score: 0.75}).
|
||||
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
|
||||
kw := []Keyword{{Text: "constant", Relevance: 0.3}}
|
||||
a := NewBasicTextAnalyzer().
|
||||
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}).
|
||||
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
|
||||
WithSentimentProvider(mockSentimentProvider{score: 0.5}).
|
||||
WithKeywordProvider(mockKeywordProvider{kws: kw})
|
||||
|
||||
@ -119,7 +119,7 @@ func TestAnalyzeTextConcurrently_AggregatesWithProviders(t *testing.T) {
|
||||
|
||||
func TestAnalyzeTextConcurrently_ContextCanceled(t *testing.T) {
|
||||
a := NewBasicTextAnalyzer().
|
||||
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}).
|
||||
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
|
||||
WithSentimentProvider(mockSentimentProvider{score: 0.9}).
|
||||
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "x", Relevance: 0.1}}})
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ var (
|
||||
"do": {}, "does": {}, "did": {}, "will": {}, "would": {}, "could": {},
|
||||
"should": {}, "may": {}, "might": {}, "can": {}, "this": {}, "that": {},
|
||||
"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": {},
|
||||
"our": {}, "their": {},
|
||||
}
|
||||
|
||||
@ -2,11 +2,9 @@ package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -16,6 +14,7 @@ import (
|
||||
|
||||
graph "tercul/internal/adapters/graphql"
|
||||
"tercul/internal/app/auth"
|
||||
auth_platform "tercul/internal/platform/auth"
|
||||
"tercul/internal/app/localization"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/data/sql"
|
||||
@ -41,7 +40,8 @@ type IntegrationTestSuite struct {
|
||||
WorkCommands *work.WorkCommands
|
||||
WorkQueries *work.WorkQueries
|
||||
Localization localization.Service
|
||||
AuthService auth.Service
|
||||
AuthCommands *auth.AuthCommands
|
||||
AuthQueries *auth.AuthQueries
|
||||
|
||||
// Test data
|
||||
TestWorks []*domain.Work
|
||||
@ -189,7 +189,9 @@ func (s *IntegrationTestSuite) setupServices() {
|
||||
s.WorkCommands = work.NewWorkCommands(s.WorkRepo, mockAnalyzer)
|
||||
s.WorkQueries = work.NewWorkQueries(s.WorkRepo)
|
||||
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
|
||||
@ -208,8 +210,8 @@ func (s *IntegrationTestSuite) setupTestData() {
|
||||
|
||||
// Create test authors
|
||||
s.TestAuthors = []*domain.Author{
|
||||
{Name: "Test Author 1", Language: "en"},
|
||||
{Name: "Test Author 2", Language: "fr"},
|
||||
{Name: "Test Author 1", TranslatableModel: domain.TranslatableModel{Language: "en"}},
|
||||
{Name: "Test Author 2", TranslatableModel: domain.TranslatableModel{Language: "fr"}},
|
||||
}
|
||||
|
||||
for _, author := range s.TestAuthors {
|
||||
@ -220,9 +222,9 @@ func (s *IntegrationTestSuite) setupTestData() {
|
||||
|
||||
// Create test works
|
||||
s.TestWorks = []*domain.Work{
|
||||
{Title: "Test Work 1", Language: "en"},
|
||||
{Title: "Test Work 2", Language: "en"},
|
||||
{Title: "Test Work 3", Language: "fr"},
|
||||
{Title: "Test Work 1", TranslatableModel: domain.TranslatableModel{Language: "en"}},
|
||||
{Title: "Test Work 2", TranslatableModel: domain.TranslatableModel{Language: "en"}},
|
||||
{Title: "Test Work 3", TranslatableModel: domain.TranslatableModel{Language: "fr"}},
|
||||
}
|
||||
|
||||
for _, work := range s.TestWorks {
|
||||
@ -304,8 +306,8 @@ func (s *IntegrationTestSuite) GetResolver() *graph.Resolver {
|
||||
// CreateTestWork creates a test work with optional content
|
||||
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
||||
work := &domain.Work{
|
||||
Title: title,
|
||||
Language: language,
|
||||
Title: title,
|
||||
TranslatableModel: domain.TranslatableModel{Language: language},
|
||||
}
|
||||
|
||||
if err := s.WorkRepo.Create(context.Background(), work); err != nil {
|
||||
|
||||
@ -2,7 +2,6 @@ package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tercul/internal/domain"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@ -35,7 +35,7 @@ func (m *MockTranslationRepository) GetByID(ctx context.Context, id uint) (*doma
|
||||
return &cp, nil
|
||||
}
|
||||
}
|
||||
return nil, domain.ErrEntityNotFound
|
||||
return nil, ErrEntityNotFound
|
||||
}
|
||||
|
||||
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 domain.ErrEntityNotFound
|
||||
return ErrEntityNotFound
|
||||
}
|
||||
|
||||
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 domain.ErrEntityNotFound
|
||||
return ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) {
|
||||
|
||||
@ -19,9 +19,6 @@ func NewUnifiedMockWorkRepository() *UnifiedMockWorkRepository {
|
||||
|
||||
func (m *UnifiedMockWorkRepository) AddWork(work *domain.Work) {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -37,7 +34,7 @@ func (m *UnifiedMockWorkRepository) GetByID(ctx context.Context, id uint) (*doma
|
||||
return w, nil
|
||||
}
|
||||
}
|
||||
return nil, domain.ErrEntityNotFound
|
||||
return nil, ErrEntityNotFound
|
||||
}
|
||||
|
||||
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 domain.ErrEntityNotFound
|
||||
return ErrEntityNotFound
|
||||
}
|
||||
|
||||
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 domain.ErrEntityNotFound
|
||||
return ErrEntityNotFound
|
||||
}
|
||||
|
||||
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 nil, domain.ErrEntityNotFound
|
||||
return nil, ErrEntityNotFound
|
||||
}
|
||||
|
||||
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 nil, domain.ErrEntityNotFound
|
||||
return nil, ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
||||
|
||||
@ -3,6 +3,7 @@ package testutil
|
||||
import (
|
||||
"context"
|
||||
graph "tercul/internal/adapters/graphql"
|
||||
"tercul/internal/app"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/domain"
|
||||
|
||||
@ -41,19 +42,30 @@ func (s *SimpleTestSuite) SetupTest() {
|
||||
|
||||
// GetResolver returns a minimal GraphQL resolver for testing
|
||||
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{
|
||||
// WorkRepo: s.WorkRepo, // This should be removed from resolver
|
||||
// WorkService: s.WorkService, // This is replaced by commands/queries
|
||||
App: &app.Application{
|
||||
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
|
||||
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
||||
work := &domain.Work{
|
||||
Title: title,
|
||||
Language: language,
|
||||
Title: title,
|
||||
TranslatableModel: domain.TranslatableModel{Language: language},
|
||||
}
|
||||
|
||||
// Add work to the mock repository
|
||||
|
||||
@ -2,6 +2,7 @@ package testutil
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@ -15,6 +16,8 @@ import (
|
||||
"tercul/internal/platform/config"
|
||||
)
|
||||
|
||||
var ErrEntityNotFound = errors.New("entity not found")
|
||||
|
||||
// TestDB holds the test database connection
|
||||
var TestDB *gorm.DB
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user