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
|
// 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,
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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.")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
//go:build tools
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
//go:build tools
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
//go:build tools
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 := `
|
||||||
|
|||||||
@ -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"
|
||||||
@ -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) {
|
func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput) (*model.Work, error) {
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 ©rightClaimRepository{
|
return ©rightClaimRepository{
|
||||||
BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db),
|
BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db),
|
||||||
db: db,
|
db: db,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}}})
|
||||||
|
|
||||||
|
|||||||
@ -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": {},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
@ -304,8 +306,8 @@ func (s *IntegrationTestSuite) GetResolver() *graph.Resolver {
|
|||||||
// CreateTestWork creates a test work with optional content
|
// CreateTestWork creates a test work with optional content
|
||||||
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *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 {
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user