mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 04:01:34 +00:00
Refactor: In-progress refactoring to fix build.
This commit includes the following changes: - Refactored all data repositories in `internal/data/sql/` to use a consistent `sql` package and to align with the new `domain` models. - Fixed the GraphQL structure by moving the server creation logic from `internal/app` to `cmd/api`, which resolved an import cycle. - Corrected numerous incorrect import paths for packages like `graph`, `linguistics`, `syncjob`, and the legacy `models` package. - Resolved several package and function redeclaration errors. - Removed legacy migration code.
This commit is contained in:
parent
97d53f5491
commit
8797cec718
@ -11,7 +11,10 @@ import (
|
||||
"tercul/internal/platform/log"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/hibiken/asynq"
|
||||
graph "tercul/internal/adapters/graphql"
|
||||
"tercul/internal/platform/auth"
|
||||
)
|
||||
|
||||
// main is the entry point for the Tercul application.
|
||||
@ -39,19 +42,44 @@ func main() {
|
||||
serverFactory := app.NewServerFactory(appBuilder)
|
||||
|
||||
// Create servers
|
||||
graphQLServer, err := serverFactory.CreateGraphQLServer()
|
||||
if err != nil {
|
||||
log.LogFatal("Failed to create GraphQL server",
|
||||
log.F("error", err))
|
||||
}
|
||||
|
||||
backgroundServers, err := serverFactory.CreateBackgroundJobServers()
|
||||
if err != nil {
|
||||
log.LogFatal("Failed to create background job servers",
|
||||
log.F("error", err))
|
||||
}
|
||||
|
||||
playgroundServer := serverFactory.CreatePlaygroundServer()
|
||||
// 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,
|
||||
}
|
||||
|
||||
jwtManager := auth.NewJWTManager()
|
||||
srv := graph.NewServerWithAuth(resolver, jwtManager)
|
||||
graphQLServer := &http.Server{
|
||||
Addr: config.Cfg.ServerPort,
|
||||
Handler: srv,
|
||||
}
|
||||
log.LogInfo("GraphQL server created successfully", log.F("port", config.Cfg.ServerPort))
|
||||
|
||||
// Create GraphQL playground
|
||||
playgroundHandler := playground.Handler("GraphQL", "/query")
|
||||
playgroundServer := &http.Server{
|
||||
Addr: config.Cfg.PlaygroundPort,
|
||||
Handler: playgroundHandler,
|
||||
}
|
||||
log.LogInfo("GraphQL playground created successfully", log.F("port", config.Cfg.PlaygroundPort))
|
||||
|
||||
// Start HTTP servers in goroutines
|
||||
go func() {
|
||||
|
||||
@ -3,73 +3,47 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/enrich"
|
||||
"tercul/internal/store"
|
||||
"tercul/internal/app"
|
||||
"tercul/internal/jobs/linguistics"
|
||||
"tercul/internal/platform/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("Starting enrichment service...")
|
||||
log.Println("Starting enrichment tool...")
|
||||
|
||||
// Create a context that can be cancelled
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Set up signal handling
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
sig := <-sigCh
|
||||
log.Printf("Received signal %v, shutting down...", sig)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
// Load configuration
|
||||
// Load configuration from environment variables
|
||||
config.LoadConfig()
|
||||
|
||||
// Connect to the database
|
||||
dsn := os.Getenv("DATABASE_URL")
|
||||
if dsn == "" {
|
||||
dsn = config.Cfg.GetDSN()
|
||||
}
|
||||
// Initialize structured logger with appropriate log level
|
||||
log.SetDefaultLevel(log.InfoLevel)
|
||||
log.LogInfo("Starting Tercul enrichment tool",
|
||||
log.F("environment", config.Cfg.Environment),
|
||||
log.F("version", "1.0.0"))
|
||||
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
// Build application components
|
||||
appBuilder := app.NewApplicationBuilder()
|
||||
if err := appBuilder.Build(); err != nil {
|
||||
log.LogFatal("Failed to build application",
|
||||
log.F("error", err))
|
||||
}
|
||||
defer appBuilder.Close()
|
||||
|
||||
// Get all works
|
||||
works, err := appBuilder.GetApplication().WorkQueries.ListWorks(context.Background(), 1, 10000) // A bit of a hack, but should work for now
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to database: %v", err)
|
||||
log.LogFatal("Failed to get works",
|
||||
log.F("error", err))
|
||||
}
|
||||
|
||||
// Create a store.DB
|
||||
storeDB := &store.DB{DB: db}
|
||||
|
||||
// Create the enrichment registry
|
||||
registry := enrich.DefaultRegistry()
|
||||
|
||||
// Process pending works
|
||||
if err := store.ProcessPendingWorks(ctx, registry, storeDB); err != nil {
|
||||
log.Fatalf("Failed to process pending works: %v", err)
|
||||
}
|
||||
|
||||
// Set up a ticker to periodically process pending works
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("Shutting down...")
|
||||
return
|
||||
case <-ticker.C:
|
||||
log.Println("Processing pending works...")
|
||||
if err := store.ProcessPendingWorks(ctx, registry, storeDB); err != nil {
|
||||
log.Printf("Failed to process pending works: %v", err)
|
||||
}
|
||||
// Enqueue analysis for each work
|
||||
for _, work := range works.Data {
|
||||
err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynqClient(), work.ID)
|
||||
if err != nil {
|
||||
log.LogError("Failed to enqueue analysis for work",
|
||||
log.F("workID", work.ID),
|
||||
log.F("error", err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Enrichment tool finished.")
|
||||
}
|
||||
|
||||
141
create_repo_interfaces.go
Normal file
141
create_repo_interfaces.go
Normal file
@ -0,0 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sqlDir := "internal/data/sql"
|
||||
domainDir := "internal/domain"
|
||||
|
||||
files, err := ioutil.ReadDir(sqlDir)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading sql directory:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if strings.HasSuffix(file.Name(), "_repository.go") {
|
||||
repoName := strings.TrimSuffix(file.Name(), "_repository.go")
|
||||
repoInterfaceName := strings.Title(repoName) + "Repository"
|
||||
domainPackageName := repoName
|
||||
|
||||
// Create domain directory
|
||||
domainRepoDir := filepath.Join(domainDir, domainPackageName)
|
||||
if err := os.MkdirAll(domainRepoDir, 0755); err != nil {
|
||||
fmt.Printf("Error creating directory %s: %v\n", domainRepoDir, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Read the sql repository file
|
||||
filePath := filepath.Join(sqlDir, file.Name())
|
||||
src, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading file %s: %v\n", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the file
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, "", src, parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing file %s: %v\n", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Find public methods
|
||||
var methods []string
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
if fn, ok := n.(*ast.FuncDecl); ok {
|
||||
if fn.Recv != nil && len(fn.Recv.List) > 0 {
|
||||
if star, ok := fn.Recv.List[0].Type.(*ast.StarExpr); ok {
|
||||
if ident, ok := star.X.(*ast.Ident); ok {
|
||||
if strings.HasSuffix(ident.Name, "Repository") && fn.Name.IsExported() {
|
||||
methods = append(methods, getFuncSignature(fn))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Create the repo.go file
|
||||
repoFilePath := filepath.Join(domainRepoDir, "repo.go")
|
||||
repoFileContent := fmt.Sprintf(`package %s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// %s defines CRUD methods specific to %s.
|
||||
type %s interface {
|
||||
domain.BaseRepository[domain.%s]
|
||||
%s
|
||||
}
|
||||
`, domainPackageName, repoInterfaceName, strings.Title(repoName), repoInterfaceName, strings.Title(repoName), formatMethods(methods))
|
||||
|
||||
if err := ioutil.WriteFile(repoFilePath, []byte(repoFileContent), 0644); err != nil {
|
||||
fmt.Printf("Error writing file %s: %v\n", repoFilePath, err)
|
||||
} else {
|
||||
fmt.Printf("Created %s\n", repoFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFuncSignature(fn *ast.FuncDecl) string {
|
||||
params := ""
|
||||
for _, p := range fn.Type.Params.List {
|
||||
if len(p.Names) > 0 {
|
||||
params += p.Names[0].Name + " "
|
||||
}
|
||||
params += getTypeString(p.Type) + ", "
|
||||
}
|
||||
if len(params) > 0 {
|
||||
params = params[:len(params)-2]
|
||||
}
|
||||
|
||||
results := ""
|
||||
if fn.Type.Results != nil {
|
||||
for _, r := range fn.Type.Results.List {
|
||||
results += getTypeString(r.Type) + ", "
|
||||
}
|
||||
if len(results) > 0 {
|
||||
results = "(" + results[:len(results)-2] + ")"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("\t%s(%s) %s", fn.Name.Name, params, results)
|
||||
}
|
||||
|
||||
func getTypeString(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.SelectorExpr:
|
||||
return getTypeString(t.X) + "." + t.Sel.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + getTypeString(t.X)
|
||||
case *ast.ArrayType:
|
||||
return "[]" + getTypeString(t.Elt)
|
||||
case *ast.InterfaceType:
|
||||
return "interface{}"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func formatMethods(methods []string) string {
|
||||
if len(methods) == 0 {
|
||||
return ""
|
||||
}
|
||||
return "\n" + strings.Join(methods, "\n")
|
||||
}
|
||||
49
fix_domain_repos.go
Normal file
49
fix_domain_repos.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
domainDir := "internal/domain"
|
||||
|
||||
dirs, err := ioutil.ReadDir(domainDir)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading domain directory:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if dir.IsDir() {
|
||||
repoFilePath := filepath.Join(domainDir, dir.Name(), "repo.go")
|
||||
if _, err := os.Stat(repoFilePath); err == nil {
|
||||
content, err := ioutil.ReadFile(repoFilePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading file %s: %v\n", repoFilePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
newContent := strings.Replace(string(content), "domain.Base", "domain.BaseRepository", -1)
|
||||
newContent = strings.Replace(newContent, "domain."+strings.Title(dir.Name()), "domain."+strings.Title(dir.Name()), -1)
|
||||
|
||||
// Fix for names with underscore
|
||||
newContent = strings.Replace(newContent, "domain.Copyright_claim", "domain.CopyrightClaim", -1)
|
||||
newContent = strings.Replace(newContent, "domain.Email_verification", "domain.EmailVerification", -1)
|
||||
newContent = strings.Replace(newContent, "domain.Password_reset", "domain.PasswordReset", -1)
|
||||
newContent = strings.Replace(newContent, "domain.User_profile", "domain.UserProfile", -1)
|
||||
newContent = strings.Replace(newContent, "domain.User_session", "domain.UserSession", -1)
|
||||
|
||||
|
||||
if err := ioutil.WriteFile(repoFilePath, []byte(newContent), 0644); err != nil {
|
||||
fmt.Printf("Error writing file %s: %v\n", repoFilePath, err)
|
||||
} else {
|
||||
fmt.Printf("Fixed repo %s\n", repoFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
fix_sql_imports.go
Normal file
40
fix_sql_imports.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
sqlDir := "internal/data/sql"
|
||||
|
||||
files, err := ioutil.ReadDir(sqlDir)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading sql directory:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if strings.HasSuffix(file.Name(), "_repository.go") {
|
||||
repoName := strings.TrimSuffix(file.Name(), "_repository.go")
|
||||
filePath := filepath.Join(sqlDir, file.Name())
|
||||
|
||||
content, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading file %s: %v\n", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
newContent := strings.Replace(string(content), `"tercul/internal/domain"`, fmt.Sprintf(`"%s"`, filepath.Join("tercul/internal/domain", repoName)), 1)
|
||||
newContent = strings.Replace(newContent, "domain."+strings.Title(repoName)+"Repository", repoName+"."+strings.Title(repoName)+"Repository", 1)
|
||||
|
||||
if err := ioutil.WriteFile(filePath, []byte(newContent), 0644); err != nil {
|
||||
fmt.Printf("Error writing file %s: %v\n", filePath, err)
|
||||
} else {
|
||||
fmt.Printf("Fixed imports in %s\n", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"tercul/internal/platform/db"
|
||||
"tercul/internal/platform/log"
|
||||
auth_platform "tercul/internal/platform/auth"
|
||||
"tercul/linguistics"
|
||||
"tercul/internal/jobs/linguistics"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
|
||||
@ -2,12 +2,12 @@ package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"tercul/internal/jobs/linguistics"
|
||||
syncjob "tercul/internal/jobs/sync"
|
||||
"tercul/internal/platform/auth"
|
||||
"tercul/internal/platform/config"
|
||||
"tercul/graph"
|
||||
"tercul/linguistics"
|
||||
"tercul/internal/platform/log"
|
||||
"tercul/syncjob"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/hibiken/asynq"
|
||||
@ -25,44 +25,6 @@ func NewServerFactory(appBuilder *ApplicationBuilder) *ServerFactory {
|
||||
}
|
||||
}
|
||||
|
||||
// CreateGraphQLServer creates and configures the GraphQL server
|
||||
func (f *ServerFactory) CreateGraphQLServer() (*http.Server, error) {
|
||||
log.LogInfo("Setting up GraphQL server")
|
||||
|
||||
// Create GraphQL resolver with all dependencies
|
||||
resolver := &graph.Resolver{
|
||||
WorkRepo: f.appBuilder.GetRepositories().WorkRepository,
|
||||
UserRepo: f.appBuilder.GetRepositories().UserRepository,
|
||||
AuthorRepo: f.appBuilder.GetRepositories().AuthorRepository,
|
||||
TranslationRepo: f.appBuilder.GetRepositories().TranslationRepository,
|
||||
CommentRepo: f.appBuilder.GetRepositories().CommentRepository,
|
||||
LikeRepo: f.appBuilder.GetRepositories().LikeRepository,
|
||||
BookmarkRepo: f.appBuilder.GetRepositories().BookmarkRepository,
|
||||
CollectionRepo: f.appBuilder.GetRepositories().CollectionRepository,
|
||||
TagRepo: f.appBuilder.GetRepositories().TagRepository,
|
||||
CategoryRepo: f.appBuilder.GetRepositories().CategoryRepository,
|
||||
WorkService: f.appBuilder.GetServices().WorkService,
|
||||
Localization: f.appBuilder.GetServices().LocalizationService,
|
||||
AuthService: f.appBuilder.GetServices().AuthService,
|
||||
}
|
||||
|
||||
// Create JWT manager for authentication
|
||||
jwtManager := auth.NewJWTManager()
|
||||
|
||||
// Create GraphQL server with authentication
|
||||
srv := graph.NewServerWithAuth(resolver, jwtManager)
|
||||
|
||||
// Create HTTP server with middleware
|
||||
httpServer := &http.Server{
|
||||
Addr: config.Cfg.ServerPort,
|
||||
Handler: srv,
|
||||
}
|
||||
|
||||
log.LogInfo("GraphQL server created successfully",
|
||||
log.F("port", config.Cfg.ServerPort))
|
||||
|
||||
return httpServer, nil
|
||||
}
|
||||
|
||||
// CreateBackgroundJobServers creates and configures background job servers
|
||||
func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
|
||||
@ -120,19 +82,3 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
// CreatePlaygroundServer creates the GraphQL playground server
|
||||
func (f *ServerFactory) CreatePlaygroundServer() *http.Server {
|
||||
log.LogInfo("Setting up GraphQL playground")
|
||||
|
||||
playgroundHandler := playground.Handler("GraphQL", "/query")
|
||||
|
||||
playgroundServer := &http.Server{
|
||||
Addr: config.Cfg.PlaygroundPort,
|
||||
Handler: playgroundHandler,
|
||||
}
|
||||
|
||||
log.LogInfo("GraphQL playground created successfully",
|
||||
log.F("port", config.Cfg.PlaygroundPort))
|
||||
|
||||
return playgroundServer
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/author"
|
||||
)
|
||||
|
||||
type authorRepository struct {
|
||||
@ -12,7 +12,7 @@ type authorRepository struct {
|
||||
}
|
||||
|
||||
// NewAuthorRepository creates a new AuthorRepository.
|
||||
func NewAuthorRepository(db *gorm.DB) domain.AuthorRepository {
|
||||
func NewAuthorRepository(db *gorm.DB) author.AuthorRepository {
|
||||
return &authorRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Author](db),
|
||||
db: db,
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/base"
|
||||
"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) domain.BaseRepository[T] {
|
||||
func NewBaseRepositoryImpl[T any](db *gorm.DB) base.BaseRepository[T] {
|
||||
return &BaseRepositoryImpl[T]{db: db}
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/book"
|
||||
)
|
||||
|
||||
type bookRepository struct {
|
||||
@ -13,7 +13,7 @@ type bookRepository struct {
|
||||
}
|
||||
|
||||
// NewBookRepository creates a new BookRepository.
|
||||
func NewBookRepository(db *gorm.DB) domain.BookRepository {
|
||||
func NewBookRepository(db *gorm.DB) book.BookRepository {
|
||||
return &bookRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Book](db),
|
||||
db: db,
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/bookmark"
|
||||
)
|
||||
|
||||
type bookmarkRepository struct {
|
||||
@ -12,7 +12,7 @@ type bookmarkRepository struct {
|
||||
}
|
||||
|
||||
// NewBookmarkRepository creates a new BookmarkRepository.
|
||||
func NewBookmarkRepository(db *gorm.DB) domain.BookmarkRepository {
|
||||
func NewBookmarkRepository(db *gorm.DB) bookmark.BookmarkRepository {
|
||||
return &bookmarkRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Bookmark](db),
|
||||
db: db,
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/category"
|
||||
)
|
||||
|
||||
type categoryRepository struct {
|
||||
@ -13,7 +13,7 @@ type categoryRepository struct {
|
||||
}
|
||||
|
||||
// NewCategoryRepository creates a new CategoryRepository.
|
||||
func NewCategoryRepository(db *gorm.DB) domain.CategoryRepository {
|
||||
func NewCategoryRepository(db *gorm.DB) category.CategoryRepository {
|
||||
return &categoryRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Category](db),
|
||||
db: db,
|
||||
|
||||
@ -1,33 +1,27 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/city"
|
||||
)
|
||||
|
||||
// CityRepository defines CRUD methods specific to City.
|
||||
type CityRepository interface {
|
||||
BaseRepository[models.City]
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]models.City, error)
|
||||
}
|
||||
|
||||
type cityRepository struct {
|
||||
BaseRepository[models.City]
|
||||
domain.BaseRepository[domain.City]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCityRepository creates a new CityRepository.
|
||||
func NewCityRepository(db *gorm.DB) CityRepository {
|
||||
func NewCityRepository(db *gorm.DB) city.CityRepository {
|
||||
return &cityRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.City](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.City](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByCountryID finds cities by country ID
|
||||
func (r *cityRepository) ListByCountryID(ctx context.Context, countryID uint) ([]models.City, error) {
|
||||
var cities []models.City
|
||||
func (r *cityRepository) ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error) {
|
||||
var cities []domain.City
|
||||
if err := r.db.WithContext(ctx).Where("country_id = ?", countryID).Find(&cities).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/collection"
|
||||
)
|
||||
|
||||
type collectionRepository struct {
|
||||
@ -12,7 +12,7 @@ type collectionRepository struct {
|
||||
}
|
||||
|
||||
// NewCollectionRepository creates a new CollectionRepository.
|
||||
func NewCollectionRepository(db *gorm.DB) domain.CollectionRepository {
|
||||
func NewCollectionRepository(db *gorm.DB) collection.CollectionRepository {
|
||||
return &collectionRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Collection](db),
|
||||
db: db,
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/comment"
|
||||
)
|
||||
|
||||
type commentRepository struct {
|
||||
@ -12,7 +12,7 @@ type commentRepository struct {
|
||||
}
|
||||
|
||||
// NewCommentRepository creates a new CommentRepository.
|
||||
func NewCommentRepository(db *gorm.DB) domain.CommentRepository {
|
||||
func NewCommentRepository(db *gorm.DB) comment.CommentRepository {
|
||||
return &commentRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Comment](db),
|
||||
db: db,
|
||||
|
||||
@ -1,37 +1,27 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/contribution"
|
||||
)
|
||||
|
||||
// ContributionRepository defines CRUD methods specific to Contribution.
|
||||
type ContributionRepository interface {
|
||||
BaseRepository[models.Contribution]
|
||||
ListByUserID(ctx context.Context, userID uint) ([]models.Contribution, error)
|
||||
ListByReviewerID(ctx context.Context, reviewerID uint) ([]models.Contribution, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]models.Contribution, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]models.Contribution, error)
|
||||
ListByStatus(ctx context.Context, status string) ([]models.Contribution, error)
|
||||
}
|
||||
|
||||
type contributionRepository struct {
|
||||
BaseRepository[models.Contribution]
|
||||
domain.BaseRepository[domain.Contribution]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewContributionRepository creates a new ContributionRepository.
|
||||
func NewContributionRepository(db *gorm.DB) ContributionRepository {
|
||||
func NewContributionRepository(db *gorm.DB) contribution.ContributionRepository {
|
||||
return &contributionRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Contribution](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Contribution](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByUserID finds contributions by user ID
|
||||
func (r *contributionRepository) ListByUserID(ctx context.Context, userID uint) ([]models.Contribution, error) {
|
||||
var contributions []models.Contribution
|
||||
func (r *contributionRepository) ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error) {
|
||||
var contributions []domain.Contribution
|
||||
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&contributions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -39,8 +29,8 @@ func (r *contributionRepository) ListByUserID(ctx context.Context, userID uint)
|
||||
}
|
||||
|
||||
// ListByReviewerID finds contributions by reviewer ID
|
||||
func (r *contributionRepository) ListByReviewerID(ctx context.Context, reviewerID uint) ([]models.Contribution, error) {
|
||||
var contributions []models.Contribution
|
||||
func (r *contributionRepository) ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error) {
|
||||
var contributions []domain.Contribution
|
||||
if err := r.db.WithContext(ctx).Where("reviewer_id = ?", reviewerID).Find(&contributions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -48,8 +38,8 @@ func (r *contributionRepository) ListByReviewerID(ctx context.Context, reviewerI
|
||||
}
|
||||
|
||||
// ListByWorkID finds contributions by work ID
|
||||
func (r *contributionRepository) ListByWorkID(ctx context.Context, workID uint) ([]models.Contribution, error) {
|
||||
var contributions []models.Contribution
|
||||
func (r *contributionRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Contribution, error) {
|
||||
var contributions []domain.Contribution
|
||||
if err := r.db.WithContext(ctx).Where("work_id = ?", workID).Find(&contributions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -57,8 +47,8 @@ func (r *contributionRepository) ListByWorkID(ctx context.Context, workID uint)
|
||||
}
|
||||
|
||||
// ListByTranslationID finds contributions by translation ID
|
||||
func (r *contributionRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]models.Contribution, error) {
|
||||
var contributions []models.Contribution
|
||||
func (r *contributionRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Contribution, error) {
|
||||
var contributions []domain.Contribution
|
||||
if err := r.db.WithContext(ctx).Where("translation_id = ?", translationID).Find(&contributions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -66,8 +56,8 @@ func (r *contributionRepository) ListByTranslationID(ctx context.Context, transl
|
||||
}
|
||||
|
||||
// ListByStatus finds contributions by status
|
||||
func (r *contributionRepository) ListByStatus(ctx context.Context, status string) ([]models.Contribution, error) {
|
||||
var contributions []models.Contribution
|
||||
func (r *contributionRepository) ListByStatus(ctx context.Context, status string) ([]domain.Contribution, error) {
|
||||
var contributions []domain.Contribution
|
||||
if err := r.db.WithContext(ctx).Where("status = ?", status).Find(&contributions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,34 +1,27 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/copyright_claim"
|
||||
)
|
||||
|
||||
// CopyrightClaimRepository defines CRUD methods specific to CopyrightClaim.
|
||||
type CopyrightClaimRepository interface {
|
||||
BaseRepository[models.CopyrightClaim]
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]models.CopyrightClaim, error)
|
||||
ListByUserID(ctx context.Context, userID uint) ([]models.CopyrightClaim, error)
|
||||
}
|
||||
|
||||
type copyrightClaimRepository struct {
|
||||
BaseRepository[models.CopyrightClaim]
|
||||
domain.BaseRepository[domain.CopyrightClaim]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCopyrightClaimRepository creates a new CopyrightClaimRepository.
|
||||
func NewCopyrightClaimRepository(db *gorm.DB) CopyrightClaimRepository {
|
||||
func NewCopyrightClaimRepository(db *gorm.DB) domain.CopyrightClaimRepository {
|
||||
return ©rightClaimRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.CopyrightClaim](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByWorkID finds claims by work ID
|
||||
func (r *copyrightClaimRepository) ListByWorkID(ctx context.Context, workID uint) ([]models.CopyrightClaim, error) {
|
||||
var claims []models.CopyrightClaim
|
||||
func (r *copyrightClaimRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error) {
|
||||
var claims []domain.CopyrightClaim
|
||||
if err := r.db.WithContext(ctx).Where("work_id = ?", workID).Find(&claims).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -36,8 +29,8 @@ func (r *copyrightClaimRepository) ListByWorkID(ctx context.Context, workID uint
|
||||
}
|
||||
|
||||
// ListByUserID finds claims by user ID
|
||||
func (r *copyrightClaimRepository) ListByUserID(ctx context.Context, userID uint) ([]models.CopyrightClaim, error) {
|
||||
var claims []models.CopyrightClaim
|
||||
func (r *copyrightClaimRepository) ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error) {
|
||||
var claims []domain.CopyrightClaim
|
||||
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&claims).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,42 +1,28 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/copyright"
|
||||
)
|
||||
|
||||
// CopyrightRepository defines CRUD methods specific to Copyright.
|
||||
type CopyrightRepository interface {
|
||||
BaseRepository[models.Copyright]
|
||||
// Polymorphic methods
|
||||
AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error
|
||||
DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error
|
||||
GetByEntity(ctx context.Context, entityID uint, entityType string) ([]models.Copyright, error)
|
||||
GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]models.Copyrightable, error)
|
||||
// Translation methods
|
||||
AddTranslation(ctx context.Context, translation *models.CopyrightTranslation) error
|
||||
GetTranslations(ctx context.Context, copyrightID uint) ([]models.CopyrightTranslation, error)
|
||||
GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*models.CopyrightTranslation, error)
|
||||
}
|
||||
|
||||
type copyrightRepository struct {
|
||||
BaseRepository[models.Copyright]
|
||||
domain.BaseRepository[domain.Copyright]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCopyrightRepository creates a new CopyrightRepository.
|
||||
func NewCopyrightRepository(db *gorm.DB) CopyrightRepository {
|
||||
func NewCopyrightRepository(db *gorm.DB) copyright.CopyrightRepository {
|
||||
return ©rightRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Copyright](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Copyright](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// AttachToEntity attaches a copyright to any entity type
|
||||
func (r *copyrightRepository) AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error {
|
||||
copyrightable := models.Copyrightable{
|
||||
copyrightable := domain.Copyrightable{
|
||||
CopyrightID: copyrightID,
|
||||
CopyrightableID: entityID,
|
||||
CopyrightableType: entityType,
|
||||
@ -47,12 +33,12 @@ func (r *copyrightRepository) AttachToEntity(ctx context.Context, copyrightID ui
|
||||
// DetachFromEntity removes a copyright from an entity
|
||||
func (r *copyrightRepository) DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error {
|
||||
return r.db.WithContext(ctx).Where("copyright_id = ? AND copyrightable_id = ? AND copyrightable_type = ?",
|
||||
copyrightID, entityID, entityType).Delete(&models.Copyrightable{}).Error
|
||||
copyrightID, entityID, entityType).Delete(&domain.Copyrightable{}).Error
|
||||
}
|
||||
|
||||
// GetByEntity gets all copyrights for a specific entity
|
||||
func (r *copyrightRepository) GetByEntity(ctx context.Context, entityID uint, entityType string) ([]models.Copyright, error) {
|
||||
var copyrights []models.Copyright
|
||||
func (r *copyrightRepository) GetByEntity(ctx context.Context, entityID uint, entityType string) ([]domain.Copyright, error) {
|
||||
var copyrights []domain.Copyright
|
||||
err := r.db.WithContext(ctx).Joins("JOIN copyrightables ON copyrightables.copyright_id = copyrights.id").
|
||||
Where("copyrightables.copyrightable_id = ? AND copyrightables.copyrightable_type = ?", entityID, entityType).
|
||||
Preload("Translations").
|
||||
@ -61,27 +47,26 @@ func (r *copyrightRepository) GetByEntity(ctx context.Context, entityID uint, en
|
||||
}
|
||||
|
||||
// GetEntitiesByCopyright gets all entities that have a specific copyright
|
||||
func (r *copyrightRepository) GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]models.Copyrightable, error) {
|
||||
var copyrightables []models.Copyrightable
|
||||
func (r *copyrightRepository) GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]domain.Copyrightable, error) {
|
||||
var copyrightables []domain.Copyrightable
|
||||
err := r.db.WithContext(ctx).Where("copyright_id = ?", copyrightID).Find(©rightables).Error
|
||||
return copyrightables, err
|
||||
}
|
||||
|
||||
// AddTranslation adds a translation to a copyright
|
||||
func (r *copyrightRepository) AddTranslation(ctx context.Context, translation *models.CopyrightTranslation) error {
|
||||
func (r *copyrightRepository) AddTranslation(ctx context.Context, translation *domain.CopyrightTranslation) error {
|
||||
return r.db.WithContext(ctx).Create(translation).Error
|
||||
}
|
||||
|
||||
// GetTranslations gets all translations for a copyright
|
||||
func (r *copyrightRepository) GetTranslations(ctx context.Context, copyrightID uint) ([]models.CopyrightTranslation, error) {
|
||||
var translations []models.CopyrightTranslation
|
||||
func (r *copyrightRepository) GetTranslations(ctx context.Context, copyrightID uint) ([]domain.CopyrightTranslation, error) {
|
||||
var translations []domain.CopyrightTranslation
|
||||
err := r.db.WithContext(ctx).Where("copyright_id = ?", copyrightID).Find(&translations).Error
|
||||
return translations, err
|
||||
}
|
||||
|
||||
// GetTranslationByLanguage gets a specific translation by language code
|
||||
func (r *copyrightRepository) GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*models.CopyrightTranslation, error) {
|
||||
var translation models.CopyrightTranslation
|
||||
func (r *copyrightRepository) GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*domain.CopyrightTranslation, error) {
|
||||
var translation domain.CopyrightTranslation
|
||||
err := r.db.WithContext(ctx).Where("copyright_id = ? AND language_code = ?", copyrightID, languageCode).First(&translation).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
@ -1,35 +1,28 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/country"
|
||||
)
|
||||
|
||||
// CountryRepository defines CRUD methods specific to Country.
|
||||
type CountryRepository interface {
|
||||
BaseRepository[models.Country]
|
||||
GetByCode(ctx context.Context, code string) (*models.Country, error)
|
||||
ListByContinent(ctx context.Context, continent string) ([]models.Country, error)
|
||||
}
|
||||
|
||||
type countryRepository struct {
|
||||
BaseRepository[models.Country]
|
||||
domain.BaseRepository[domain.Country]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCountryRepository creates a new CountryRepository.
|
||||
func NewCountryRepository(db *gorm.DB) CountryRepository {
|
||||
func NewCountryRepository(db *gorm.DB) country.CountryRepository {
|
||||
return &countryRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Country](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Country](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// GetByCode finds a country by code
|
||||
func (r *countryRepository) GetByCode(ctx context.Context, code string) (*models.Country, error) {
|
||||
var country models.Country
|
||||
func (r *countryRepository) GetByCode(ctx context.Context, code string) (*domain.Country, error) {
|
||||
var country domain.Country
|
||||
if err := r.db.WithContext(ctx).Where("code = ?", code).First(&country).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
@ -40,8 +33,8 @@ func (r *countryRepository) GetByCode(ctx context.Context, code string) (*models
|
||||
}
|
||||
|
||||
// ListByContinent finds countries by continent
|
||||
func (r *countryRepository) ListByContinent(ctx context.Context, continent string) ([]models.Country, error) {
|
||||
var countries []models.Country
|
||||
func (r *countryRepository) ListByContinent(ctx context.Context, continent string) ([]domain.Country, error) {
|
||||
var countries []domain.Country
|
||||
if err := r.db.WithContext(ctx).Where("continent = ?", continent).Find(&countries).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,33 +1,27 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/edge"
|
||||
)
|
||||
|
||||
// EdgeRepository defines CRUD operations for the polymorphic edge table.
|
||||
type EdgeRepository interface {
|
||||
BaseRepository[models.Edge]
|
||||
ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]models.Edge, error)
|
||||
}
|
||||
|
||||
type edgeRepository struct {
|
||||
BaseRepository[models.Edge]
|
||||
domain.BaseRepository[domain.Edge]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewEdgeRepository creates a new EdgeRepository.
|
||||
func NewEdgeRepository(db *gorm.DB) EdgeRepository {
|
||||
func NewEdgeRepository(db *gorm.DB) edge.EdgeRepository {
|
||||
return &edgeRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Edge](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Edge](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListBySource finds edges by source table and ID
|
||||
func (r *edgeRepository) ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]models.Edge, error) {
|
||||
var edges []models.Edge
|
||||
func (r *edgeRepository) ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error) {
|
||||
var edges []domain.Edge
|
||||
if err := r.db.WithContext(ctx).Where("source_table = ? AND source_id = ?", sourceTable, sourceID).Find(&edges).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,35 +1,28 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/edition"
|
||||
)
|
||||
|
||||
// EditionRepository defines CRUD methods specific to Edition.
|
||||
type EditionRepository interface {
|
||||
BaseRepository[models.Edition]
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]models.Edition, error)
|
||||
FindByISBN(ctx context.Context, isbn string) (*models.Edition, error)
|
||||
}
|
||||
|
||||
type editionRepository struct {
|
||||
BaseRepository[models.Edition]
|
||||
domain.BaseRepository[domain.Edition]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewEditionRepository creates a new EditionRepository.
|
||||
func NewEditionRepository(db *gorm.DB) EditionRepository {
|
||||
func NewEditionRepository(db *gorm.DB) edition.EditionRepository {
|
||||
return &editionRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Edition](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Edition](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByBookID finds editions by book ID
|
||||
func (r *editionRepository) ListByBookID(ctx context.Context, bookID uint) ([]models.Edition, error) {
|
||||
var editions []models.Edition
|
||||
func (r *editionRepository) ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error) {
|
||||
var editions []domain.Edition
|
||||
if err := r.db.WithContext(ctx).Where("book_id = ?", bookID).Find(&editions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -37,8 +30,8 @@ func (r *editionRepository) ListByBookID(ctx context.Context, bookID uint) ([]mo
|
||||
}
|
||||
|
||||
// FindByISBN finds an edition by ISBN
|
||||
func (r *editionRepository) FindByISBN(ctx context.Context, isbn string) (*models.Edition, error) {
|
||||
var edition models.Edition
|
||||
func (r *editionRepository) FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error) {
|
||||
var edition domain.Edition
|
||||
if err := r.db.WithContext(ctx).Where("isbn = ?", isbn).First(&edition).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
|
||||
@ -1,38 +1,29 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/email_verification"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EmailVerificationRepository defines CRUD methods specific to EmailVerification.
|
||||
type EmailVerificationRepository interface {
|
||||
BaseRepository[models.EmailVerification]
|
||||
GetByToken(ctx context.Context, token string) (*models.EmailVerification, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]models.EmailVerification, error)
|
||||
DeleteExpired(ctx context.Context) error
|
||||
MarkAsUsed(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
type emailVerificationRepository struct {
|
||||
BaseRepository[models.EmailVerification]
|
||||
domain.BaseRepository[domain.EmailVerification]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewEmailVerificationRepository creates a new EmailVerificationRepository.
|
||||
func NewEmailVerificationRepository(db *gorm.DB) EmailVerificationRepository {
|
||||
func NewEmailVerificationRepository(db *gorm.DB) domain.EmailVerificationRepository {
|
||||
return &emailVerificationRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.EmailVerification](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.EmailVerification](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// GetByToken finds a verification by token (only unused and non-expired)
|
||||
func (r *emailVerificationRepository) GetByToken(ctx context.Context, token string) (*models.EmailVerification, error) {
|
||||
var verification models.EmailVerification
|
||||
func (r *emailVerificationRepository) GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error) {
|
||||
var verification domain.EmailVerification
|
||||
if err := r.db.WithContext(ctx).Where("token = ? AND used = ? AND expires_at > ?", token, false, time.Now()).First(&verification).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
@ -43,8 +34,8 @@ func (r *emailVerificationRepository) GetByToken(ctx context.Context, token stri
|
||||
}
|
||||
|
||||
// GetByUserID finds verifications by user ID
|
||||
func (r *emailVerificationRepository) GetByUserID(ctx context.Context, userID uint) ([]models.EmailVerification, error) {
|
||||
var verifications []models.EmailVerification
|
||||
func (r *emailVerificationRepository) GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error) {
|
||||
var verifications []domain.EmailVerification
|
||||
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&verifications).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,7 +44,7 @@ func (r *emailVerificationRepository) GetByUserID(ctx context.Context, userID ui
|
||||
|
||||
// DeleteExpired deletes expired verifications
|
||||
func (r *emailVerificationRepository) DeleteExpired(ctx context.Context) error {
|
||||
if err := r.db.WithContext(ctx).Where("expires_at < ?", time.Now()).Delete(&models.EmailVerification{}).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("expires_at < ?", time.Now()).Delete(&domain.EmailVerification{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -61,7 +52,7 @@ func (r *emailVerificationRepository) DeleteExpired(ctx context.Context) error {
|
||||
|
||||
// MarkAsUsed marks a verification as used
|
||||
func (r *emailVerificationRepository) MarkAsUsed(ctx context.Context, id uint) error {
|
||||
if err := r.db.WithContext(ctx).Model(&models.EmailVerification{}).Where("id = ?", id).Update("used", true).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Model(&domain.EmailVerification{}).Where("id = ?", id).Update("used", true).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/like"
|
||||
)
|
||||
|
||||
type likeRepository struct {
|
||||
@ -12,7 +12,7 @@ type likeRepository struct {
|
||||
}
|
||||
|
||||
// NewLikeRepository creates a new LikeRepository.
|
||||
func NewLikeRepository(db *gorm.DB) domain.LikeRepository {
|
||||
func NewLikeRepository(db *gorm.DB) like.LikeRepository {
|
||||
return &likeRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Like](db),
|
||||
db: db,
|
||||
|
||||
@ -1,35 +1,27 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/monetization"
|
||||
)
|
||||
|
||||
// MonetizationRepository defines CRUD methods specific to Monetization.
|
||||
type MonetizationRepository interface {
|
||||
BaseRepository[models.Monetization]
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]models.Monetization, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]models.Monetization, error)
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]models.Monetization, error)
|
||||
}
|
||||
|
||||
type monetizationRepository struct {
|
||||
BaseRepository[models.Monetization]
|
||||
domain.BaseRepository[domain.Monetization]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewMonetizationRepository creates a new MonetizationRepository.
|
||||
func NewMonetizationRepository(db *gorm.DB) MonetizationRepository {
|
||||
func NewMonetizationRepository(db *gorm.DB) monetization.MonetizationRepository {
|
||||
return &monetizationRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Monetization](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Monetization](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByWorkID finds monetizations by work ID
|
||||
func (r *monetizationRepository) ListByWorkID(ctx context.Context, workID uint) ([]models.Monetization, error) {
|
||||
var monetizations []models.Monetization
|
||||
func (r *monetizationRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error) {
|
||||
var monetizations []domain.Monetization
|
||||
if err := r.db.WithContext(ctx).Where("work_id = ?", workID).Find(&monetizations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -37,8 +29,8 @@ func (r *monetizationRepository) ListByWorkID(ctx context.Context, workID uint)
|
||||
}
|
||||
|
||||
// ListByTranslationID finds monetizations by translation ID
|
||||
func (r *monetizationRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]models.Monetization, error) {
|
||||
var monetizations []models.Monetization
|
||||
func (r *monetizationRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error) {
|
||||
var monetizations []domain.Monetization
|
||||
if err := r.db.WithContext(ctx).Where("translation_id = ?", translationID).Find(&monetizations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -46,8 +38,8 @@ func (r *monetizationRepository) ListByTranslationID(ctx context.Context, transl
|
||||
}
|
||||
|
||||
// ListByBookID finds monetizations by book ID
|
||||
func (r *monetizationRepository) ListByBookID(ctx context.Context, bookID uint) ([]models.Monetization, error) {
|
||||
var monetizations []models.Monetization
|
||||
func (r *monetizationRepository) ListByBookID(ctx context.Context, bookID uint) ([]domain.Monetization, error) {
|
||||
var monetizations []domain.Monetization
|
||||
if err := r.db.WithContext(ctx).Where("book_id = ?", bookID).Find(&monetizations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,38 +1,29 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/password_reset"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PasswordResetRepository defines CRUD methods specific to PasswordReset.
|
||||
type PasswordResetRepository interface {
|
||||
BaseRepository[models.PasswordReset]
|
||||
GetByToken(ctx context.Context, token string) (*models.PasswordReset, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]models.PasswordReset, error)
|
||||
DeleteExpired(ctx context.Context) error
|
||||
MarkAsUsed(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
type passwordResetRepository struct {
|
||||
BaseRepository[models.PasswordReset]
|
||||
domain.BaseRepository[domain.PasswordReset]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewPasswordResetRepository creates a new PasswordResetRepository.
|
||||
func NewPasswordResetRepository(db *gorm.DB) PasswordResetRepository {
|
||||
func NewPasswordResetRepository(db *gorm.DB) domain.PasswordResetRepository {
|
||||
return &passwordResetRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.PasswordReset](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.PasswordReset](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// GetByToken finds a reset by token (only unused and non-expired)
|
||||
func (r *passwordResetRepository) GetByToken(ctx context.Context, token string) (*models.PasswordReset, error) {
|
||||
var reset models.PasswordReset
|
||||
func (r *passwordResetRepository) GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error) {
|
||||
var reset domain.PasswordReset
|
||||
if err := r.db.WithContext(ctx).Where("token = ? AND used = ? AND expires_at > ?", token, false, time.Now()).First(&reset).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
@ -43,8 +34,8 @@ func (r *passwordResetRepository) GetByToken(ctx context.Context, token string)
|
||||
}
|
||||
|
||||
// GetByUserID finds resets by user ID
|
||||
func (r *passwordResetRepository) GetByUserID(ctx context.Context, userID uint) ([]models.PasswordReset, error) {
|
||||
var resets []models.PasswordReset
|
||||
func (r *passwordResetRepository) GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error) {
|
||||
var resets []domain.PasswordReset
|
||||
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&resets).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,7 +44,7 @@ func (r *passwordResetRepository) GetByUserID(ctx context.Context, userID uint)
|
||||
|
||||
// DeleteExpired deletes expired resets
|
||||
func (r *passwordResetRepository) DeleteExpired(ctx context.Context) error {
|
||||
if err := r.db.WithContext(ctx).Where("expires_at < ?", time.Now()).Delete(&models.PasswordReset{}).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("expires_at < ?", time.Now()).Delete(&domain.PasswordReset{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -61,7 +52,7 @@ func (r *passwordResetRepository) DeleteExpired(ctx context.Context) error {
|
||||
|
||||
// MarkAsUsed marks a reset as used
|
||||
func (r *passwordResetRepository) MarkAsUsed(ctx context.Context, id uint) error {
|
||||
if err := r.db.WithContext(ctx).Model(&models.PasswordReset{}).Where("id = ?", id).Update("used", true).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Model(&domain.PasswordReset{}).Where("id = ?", id).Update("used", true).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -1,36 +1,28 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"math"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/place"
|
||||
)
|
||||
|
||||
// PlaceRepository defines CRUD methods specific to Place.
|
||||
type PlaceRepository interface {
|
||||
BaseRepository[models.Place]
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]models.Place, error)
|
||||
ListByCityID(ctx context.Context, cityID uint) ([]models.Place, error)
|
||||
FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]models.Place, error)
|
||||
}
|
||||
|
||||
type placeRepository struct {
|
||||
BaseRepository[models.Place]
|
||||
domain.BaseRepository[domain.Place]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewPlaceRepository creates a new PlaceRepository.
|
||||
func NewPlaceRepository(db *gorm.DB) PlaceRepository {
|
||||
func NewPlaceRepository(db *gorm.DB) place.PlaceRepository {
|
||||
return &placeRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Place](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Place](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByCountryID finds places by country ID
|
||||
func (r *placeRepository) ListByCountryID(ctx context.Context, countryID uint) ([]models.Place, error) {
|
||||
var places []models.Place
|
||||
func (r *placeRepository) ListByCountryID(ctx context.Context, countryID uint) ([]domain.Place, error) {
|
||||
var places []domain.Place
|
||||
if err := r.db.WithContext(ctx).Where("country_id = ?", countryID).Find(&places).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -38,8 +30,8 @@ func (r *placeRepository) ListByCountryID(ctx context.Context, countryID uint) (
|
||||
}
|
||||
|
||||
// ListByCityID finds places by city ID
|
||||
func (r *placeRepository) ListByCityID(ctx context.Context, cityID uint) ([]models.Place, error) {
|
||||
var places []models.Place
|
||||
func (r *placeRepository) ListByCityID(ctx context.Context, cityID uint) ([]domain.Place, error) {
|
||||
var places []domain.Place
|
||||
if err := r.db.WithContext(ctx).Where("city_id = ?", cityID).Find(&places).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -47,10 +39,10 @@ func (r *placeRepository) ListByCityID(ctx context.Context, cityID uint) ([]mode
|
||||
}
|
||||
|
||||
// FindNearby finds places within a certain radius (in kilometers) of a point
|
||||
func (r *placeRepository) FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]models.Place, error) {
|
||||
func (r *placeRepository) FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]domain.Place, error) {
|
||||
// This is a simplified implementation that would need to be replaced with
|
||||
// a proper geospatial query based on the database being used
|
||||
var places []models.Place
|
||||
var places []domain.Place
|
||||
|
||||
// For PostgreSQL with PostGIS, you might use something like:
|
||||
// query := `SELECT * FROM places
|
||||
|
||||
@ -1,33 +1,27 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/publisher"
|
||||
)
|
||||
|
||||
// PublisherRepository defines CRUD methods specific to Publisher.
|
||||
type PublisherRepository interface {
|
||||
BaseRepository[models.Publisher]
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]models.Publisher, error)
|
||||
}
|
||||
|
||||
type publisherRepository struct {
|
||||
BaseRepository[models.Publisher]
|
||||
domain.BaseRepository[domain.Publisher]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewPublisherRepository creates a new PublisherRepository.
|
||||
func NewPublisherRepository(db *gorm.DB) PublisherRepository {
|
||||
func NewPublisherRepository(db *gorm.DB) publisher.PublisherRepository {
|
||||
return &publisherRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Publisher](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Publisher](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByCountryID finds publishers by country ID
|
||||
func (r *publisherRepository) ListByCountryID(ctx context.Context, countryID uint) ([]models.Publisher, error) {
|
||||
var publishers []models.Publisher
|
||||
func (r *publisherRepository) ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error) {
|
||||
var publishers []domain.Publisher
|
||||
if err := r.db.WithContext(ctx).Where("country_id = ?", countryID).Find(&publishers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,35 +1,28 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/source"
|
||||
)
|
||||
|
||||
// SourceRepository defines CRUD methods specific to Source.
|
||||
type SourceRepository interface {
|
||||
BaseRepository[models.Source]
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]models.Source, error)
|
||||
FindByURL(ctx context.Context, url string) (*models.Source, error)
|
||||
}
|
||||
|
||||
type sourceRepository struct {
|
||||
BaseRepository[models.Source]
|
||||
domain.BaseRepository[domain.Source]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewSourceRepository creates a new SourceRepository.
|
||||
func NewSourceRepository(db *gorm.DB) SourceRepository {
|
||||
func NewSourceRepository(db *gorm.DB) source.SourceRepository {
|
||||
return &sourceRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Source](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Source](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ListByWorkID finds sources by work ID
|
||||
func (r *sourceRepository) ListByWorkID(ctx context.Context, workID uint) ([]models.Source, error) {
|
||||
var sources []models.Source
|
||||
func (r *sourceRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error) {
|
||||
var sources []domain.Source
|
||||
if err := r.db.WithContext(ctx).Joins("JOIN work_sources ON work_sources.source_id = sources.id").
|
||||
Where("work_sources.work_id = ?", workID).
|
||||
Find(&sources).Error; err != nil {
|
||||
@ -39,8 +32,8 @@ func (r *sourceRepository) ListByWorkID(ctx context.Context, workID uint) ([]mod
|
||||
}
|
||||
|
||||
// FindByURL finds a source by URL
|
||||
func (r *sourceRepository) FindByURL(ctx context.Context, url string) (*models.Source, error) {
|
||||
var source models.Source
|
||||
func (r *sourceRepository) FindByURL(ctx context.Context, url string) (*domain.Source, error) {
|
||||
var source domain.Source
|
||||
if err := r.db.WithContext(ctx).Where("url = ?", url).First(&source).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/tag"
|
||||
)
|
||||
|
||||
type tagRepository struct {
|
||||
@ -13,7 +13,7 @@ type tagRepository struct {
|
||||
}
|
||||
|
||||
// NewTagRepository creates a new TagRepository.
|
||||
func NewTagRepository(db *gorm.DB) domain.TagRepository {
|
||||
func NewTagRepository(db *gorm.DB) tag.TagRepository {
|
||||
return &tagRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Tag](db),
|
||||
db: db,
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/translation"
|
||||
)
|
||||
|
||||
type translationRepository struct {
|
||||
@ -12,7 +12,7 @@ type translationRepository struct {
|
||||
}
|
||||
|
||||
// NewTranslationRepository creates a new TranslationRepository.
|
||||
func NewTranslationRepository(db *gorm.DB) domain.TranslationRepository {
|
||||
func NewTranslationRepository(db *gorm.DB) translation.TranslationRepository {
|
||||
return &translationRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Translation](db),
|
||||
db: db,
|
||||
|
||||
@ -1,34 +1,28 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/user_profile"
|
||||
)
|
||||
|
||||
// UserProfileRepository defines CRUD methods specific to UserProfile.
|
||||
type UserProfileRepository interface {
|
||||
BaseRepository[models.UserProfile]
|
||||
GetByUserID(ctx context.Context, userID uint) (*models.UserProfile, error)
|
||||
}
|
||||
|
||||
type userProfileRepository struct {
|
||||
BaseRepository[models.UserProfile]
|
||||
domain.BaseRepository[domain.UserProfile]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewUserProfileRepository creates a new UserProfileRepository.
|
||||
func NewUserProfileRepository(db *gorm.DB) UserProfileRepository {
|
||||
func NewUserProfileRepository(db *gorm.DB) domain.UserProfileRepository {
|
||||
return &userProfileRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.UserProfile](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.UserProfile](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// GetByUserID finds a user profile by user ID
|
||||
func (r *userProfileRepository) GetByUserID(ctx context.Context, userID uint) (*models.UserProfile, error) {
|
||||
var profile models.UserProfile
|
||||
func (r *userProfileRepository) GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error) {
|
||||
var profile domain.UserProfile
|
||||
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).First(&profile).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/user"
|
||||
)
|
||||
|
||||
type userRepository struct {
|
||||
@ -13,7 +13,7 @@ type userRepository struct {
|
||||
}
|
||||
|
||||
// NewUserRepository creates a new UserRepository.
|
||||
func NewUserRepository(db *gorm.DB) domain.UserRepository {
|
||||
func NewUserRepository(db *gorm.DB) user.UserRepository {
|
||||
return &userRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.User](db),
|
||||
db: db,
|
||||
|
||||
@ -1,37 +1,29 @@
|
||||
package repositories
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain/user_session"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserSessionRepository defines CRUD methods specific to UserSession.
|
||||
type UserSessionRepository interface {
|
||||
BaseRepository[models.UserSession]
|
||||
GetByToken(ctx context.Context, token string) (*models.UserSession, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]models.UserSession, error)
|
||||
DeleteExpired(ctx context.Context) error
|
||||
}
|
||||
|
||||
type userSessionRepository struct {
|
||||
BaseRepository[models.UserSession]
|
||||
domain.BaseRepository[domain.UserSession]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewUserSessionRepository creates a new UserSessionRepository.
|
||||
func NewUserSessionRepository(db *gorm.DB) UserSessionRepository {
|
||||
func NewUserSessionRepository(db *gorm.DB) domain.UserSessionRepository {
|
||||
return &userSessionRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.UserSession](db),
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.UserSession](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// GetByToken finds a session by token
|
||||
func (r *userSessionRepository) GetByToken(ctx context.Context, token string) (*models.UserSession, error) {
|
||||
var session models.UserSession
|
||||
func (r *userSessionRepository) GetByToken(ctx context.Context, token string) (*domain.UserSession, error) {
|
||||
var session domain.UserSession
|
||||
if err := r.db.WithContext(ctx).Where("token = ?", token).First(&session).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
@ -42,8 +34,8 @@ func (r *userSessionRepository) GetByToken(ctx context.Context, token string) (*
|
||||
}
|
||||
|
||||
// GetByUserID finds sessions by user ID
|
||||
func (r *userSessionRepository) GetByUserID(ctx context.Context, userID uint) ([]models.UserSession, error) {
|
||||
var sessions []models.UserSession
|
||||
func (r *userSessionRepository) GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error) {
|
||||
var sessions []domain.UserSession
|
||||
if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&sessions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -52,7 +44,7 @@ func (r *userSessionRepository) GetByUserID(ctx context.Context, userID uint) ([
|
||||
|
||||
// DeleteExpired deletes expired sessions
|
||||
func (r *userSessionRepository) DeleteExpired(ctx context.Context) error {
|
||||
if err := r.db.WithContext(ctx).Where("expires_at < ?", time.Now()).Delete(&models.UserSession{}).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("expires_at < ?", time.Now()).Delete(&domain.UserSession{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -3,7 +3,7 @@ package sql
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/work"
|
||||
)
|
||||
|
||||
type workRepository struct {
|
||||
@ -12,7 +12,7 @@ type workRepository struct {
|
||||
}
|
||||
|
||||
// NewWorkRepository creates a new WorkRepository.
|
||||
func NewWorkRepository(db *gorm.DB) domain.WorkRepository {
|
||||
func NewWorkRepository(db *gorm.DB) work.WorkRepository {
|
||||
return &workRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[domain.Work](db),
|
||||
db: db,
|
||||
|
||||
15
internal/domain/author/repo.go
Normal file
15
internal/domain/author/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package author
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// AuthorRepository defines CRUD methods specific to Author.
|
||||
type AuthorRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Author]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error)
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error)
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Author, error)
|
||||
}
|
||||
16
internal/domain/book/repo.go
Normal file
16
internal/domain/book/repo.go
Normal file
@ -0,0 +1,16 @@
|
||||
package book
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// BookRepository defines CRUD methods specific to Book.
|
||||
type BookRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Book]
|
||||
|
||||
ListByAuthorID(ctx context.Context, authorID uint) ([]domain.Book, error)
|
||||
ListByPublisherID(ctx context.Context, publisherID uint) ([]domain.Book, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Book, error)
|
||||
FindByISBN(ctx context.Context, isbn string) (*domain.Book, error)
|
||||
}
|
||||
14
internal/domain/bookmark/repo.go
Normal file
14
internal/domain/bookmark/repo.go
Normal file
@ -0,0 +1,14 @@
|
||||
package bookmark
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// BookmarkRepository defines CRUD methods specific to Bookmark.
|
||||
type BookmarkRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Bookmark]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Bookmark, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Bookmark, error)
|
||||
}
|
||||
15
internal/domain/category/repo.go
Normal file
15
internal/domain/category/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package category
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// CategoryRepository defines CRUD methods specific to Category.
|
||||
type CategoryRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Category]
|
||||
|
||||
FindByName(ctx context.Context, name string) (*domain.Category, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Category, error)
|
||||
ListByParentID(ctx context.Context, parentID *uint) ([]domain.Category, error)
|
||||
}
|
||||
13
internal/domain/city/repo.go
Normal file
13
internal/domain/city/repo.go
Normal file
@ -0,0 +1,13 @@
|
||||
package city
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// CityRepository defines CRUD methods specific to City.
|
||||
type CityRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.City]
|
||||
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error)
|
||||
}
|
||||
15
internal/domain/collection/repo.go
Normal file
15
internal/domain/collection/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package collection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// CollectionRepository defines CRUD methods specific to Collection.
|
||||
type CollectionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Collection]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Collection, error)
|
||||
ListPublic(ctx context.Context) ([]domain.Collection, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Collection, error)
|
||||
}
|
||||
16
internal/domain/comment/repo.go
Normal file
16
internal/domain/comment/repo.go
Normal file
@ -0,0 +1,16 @@
|
||||
package comment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// CommentRepository defines CRUD methods specific to Comment.
|
||||
type CommentRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Comment]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Comment, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Comment, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Comment, error)
|
||||
ListByParentID(ctx context.Context, parentID uint) ([]domain.Comment, error)
|
||||
}
|
||||
17
internal/domain/contribution/repo.go
Normal file
17
internal/domain/contribution/repo.go
Normal file
@ -0,0 +1,17 @@
|
||||
package contribution
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// ContributionRepository defines CRUD methods specific to Contribution.
|
||||
type ContributionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Contribution]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error)
|
||||
ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Contribution, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Contribution, error)
|
||||
ListByStatus(ctx context.Context, status string) ([]domain.Contribution, error)
|
||||
}
|
||||
19
internal/domain/copyright/repo.go
Normal file
19
internal/domain/copyright/repo.go
Normal file
@ -0,0 +1,19 @@
|
||||
package copyright
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// CopyrightRepository defines CRUD methods specific to Copyright.
|
||||
type CopyrightRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Copyright]
|
||||
|
||||
AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error)
|
||||
DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error)
|
||||
GetByEntity(ctx context.Context, entityID uint, entityType string) ([]domain.Copyright, error)
|
||||
GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]domain.Copyrightable, error)
|
||||
AddTranslation(ctx context.Context, translation *domain.CopyrightTranslation) (error)
|
||||
GetTranslations(ctx context.Context, copyrightID uint) ([]domain.CopyrightTranslation, error)
|
||||
GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*domain.CopyrightTranslation, error)
|
||||
}
|
||||
14
internal/domain/copyright_claim/repo.go
Normal file
14
internal/domain/copyright_claim/repo.go
Normal file
@ -0,0 +1,14 @@
|
||||
package copyright_claim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// Copyright_claimRepository defines CRUD methods specific to Copyright_claim.
|
||||
type Copyright_claimRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.CopyrightClaim]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error)
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error)
|
||||
}
|
||||
14
internal/domain/country/repo.go
Normal file
14
internal/domain/country/repo.go
Normal file
@ -0,0 +1,14 @@
|
||||
package country
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// CountryRepository defines CRUD methods specific to Country.
|
||||
type CountryRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Country]
|
||||
|
||||
GetByCode(ctx context.Context, code string) (*domain.Country, error)
|
||||
ListByContinent(ctx context.Context, continent string) ([]domain.Country, error)
|
||||
}
|
||||
13
internal/domain/edge/repo.go
Normal file
13
internal/domain/edge/repo.go
Normal file
@ -0,0 +1,13 @@
|
||||
package edge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// EdgeRepository defines CRUD methods specific to Edge.
|
||||
type EdgeRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Edge]
|
||||
|
||||
ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error)
|
||||
}
|
||||
14
internal/domain/edition/repo.go
Normal file
14
internal/domain/edition/repo.go
Normal file
@ -0,0 +1,14 @@
|
||||
package edition
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// EditionRepository defines CRUD methods specific to Edition.
|
||||
type EditionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Edition]
|
||||
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error)
|
||||
FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error)
|
||||
}
|
||||
16
internal/domain/email_verification/repo.go
Normal file
16
internal/domain/email_verification/repo.go
Normal file
@ -0,0 +1,16 @@
|
||||
package email_verification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// Email_verificationRepository defines CRUD methods specific to Email_verification.
|
||||
type Email_verificationRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.EmailVerification]
|
||||
|
||||
GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error)
|
||||
DeleteExpired(ctx context.Context) (error)
|
||||
MarkAsUsed(ctx context.Context, id uint) (error)
|
||||
}
|
||||
16
internal/domain/like/repo.go
Normal file
16
internal/domain/like/repo.go
Normal file
@ -0,0 +1,16 @@
|
||||
package like
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// LikeRepository defines CRUD methods specific to Like.
|
||||
type LikeRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Like]
|
||||
|
||||
ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Like, error)
|
||||
ListByCommentID(ctx context.Context, commentID uint) ([]domain.Like, error)
|
||||
}
|
||||
15
internal/domain/monetization/repo.go
Normal file
15
internal/domain/monetization/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package monetization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// MonetizationRepository defines CRUD methods specific to Monetization.
|
||||
type MonetizationRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Monetization]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error)
|
||||
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error)
|
||||
ListByBookID(ctx context.Context, bookID uint) ([]domain.Monetization, error)
|
||||
}
|
||||
16
internal/domain/password_reset/repo.go
Normal file
16
internal/domain/password_reset/repo.go
Normal file
@ -0,0 +1,16 @@
|
||||
package password_reset
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// Password_resetRepository defines CRUD methods specific to Password_reset.
|
||||
type Password_resetRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.PasswordReset]
|
||||
|
||||
GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error)
|
||||
DeleteExpired(ctx context.Context) (error)
|
||||
MarkAsUsed(ctx context.Context, id uint) (error)
|
||||
}
|
||||
15
internal/domain/place/repo.go
Normal file
15
internal/domain/place/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package place
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// PlaceRepository defines CRUD methods specific to Place.
|
||||
type PlaceRepository interface {
|
||||
domain.BaseRepositoryRepository[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)
|
||||
}
|
||||
13
internal/domain/publisher/repo.go
Normal file
13
internal/domain/publisher/repo.go
Normal file
@ -0,0 +1,13 @@
|
||||
package publisher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// PublisherRepository defines CRUD methods specific to Publisher.
|
||||
type PublisherRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Publisher]
|
||||
|
||||
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error)
|
||||
}
|
||||
14
internal/domain/source/repo.go
Normal file
14
internal/domain/source/repo.go
Normal file
@ -0,0 +1,14 @@
|
||||
package source
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// SourceRepository defines CRUD methods specific to Source.
|
||||
type SourceRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Source]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error)
|
||||
FindByURL(ctx context.Context, url string) (*domain.Source, error)
|
||||
}
|
||||
14
internal/domain/tag/repo.go
Normal file
14
internal/domain/tag/repo.go
Normal file
@ -0,0 +1,14 @@
|
||||
package tag
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// TagRepository defines CRUD methods specific to Tag.
|
||||
type TagRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Tag]
|
||||
|
||||
FindByName(ctx context.Context, name string) (*domain.Tag, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Tag, error)
|
||||
}
|
||||
16
internal/domain/translation/repo.go
Normal file
16
internal/domain/translation/repo.go
Normal file
@ -0,0 +1,16 @@
|
||||
package translation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// TranslationRepository defines CRUD methods specific to Translation.
|
||||
type TranslationRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.Translation]
|
||||
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error)
|
||||
ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error)
|
||||
ListByTranslatorID(ctx context.Context, translatorID uint) ([]domain.Translation, error)
|
||||
ListByStatus(ctx context.Context, status domain.TranslationStatus) ([]domain.Translation, error)
|
||||
}
|
||||
15
internal/domain/user/repo.go
Normal file
15
internal/domain/user/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// UserRepository defines CRUD methods specific to User.
|
||||
type UserRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.User]
|
||||
|
||||
FindByUsername(ctx context.Context, username string) (*domain.User, error)
|
||||
FindByEmail(ctx context.Context, email string) (*domain.User, error)
|
||||
ListByRole(ctx context.Context, role domain.UserRole) ([]domain.User, error)
|
||||
}
|
||||
13
internal/domain/user_profile/repo.go
Normal file
13
internal/domain/user_profile/repo.go
Normal file
@ -0,0 +1,13 @@
|
||||
package user_profile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// User_profileRepository defines CRUD methods specific to User_profile.
|
||||
type User_profileRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.UserProfile]
|
||||
|
||||
GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error)
|
||||
}
|
||||
15
internal/domain/user_session/repo.go
Normal file
15
internal/domain/user_session/repo.go
Normal file
@ -0,0 +1,15 @@
|
||||
package user_session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// User_sessionRepository defines CRUD methods specific to User_session.
|
||||
type User_sessionRepository interface {
|
||||
domain.BaseRepositoryRepository[domain.UserSession]
|
||||
|
||||
GetByToken(ctx context.Context, token string) (*domain.UserSession, error)
|
||||
GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error)
|
||||
DeleteExpired(ctx context.Context) (error)
|
||||
}
|
||||
18
internal/domain/work/repo.go
Normal file
18
internal/domain/work/repo.go
Normal file
@ -0,0 +1,18 @@
|
||||
package work
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// WorkRepository defines CRUD methods specific to Work.
|
||||
type WorkRepository interface {
|
||||
domain.BaseRepositoryRepository[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)
|
||||
GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error)
|
||||
ListWithTranslations(ctx context.Context, page int) (*, error)
|
||||
}
|
||||
@ -11,24 +11,24 @@ type LinguaLanguageDetector struct {
|
||||
}
|
||||
|
||||
// NewLinguaLanguageDetector builds a detector for all supported languages
|
||||
func NewLinguaLanguageDetector() *LinguaLanguageDetector {
|
||||
func NewLinguaLanguageDetector() LanguageDetector {
|
||||
det := lingua.NewLanguageDetectorBuilder().FromAllLanguages().Build()
|
||||
return &LinguaLanguageDetector{detector: det}
|
||||
}
|
||||
|
||||
// DetectLanguage returns a lowercase ISO 639-1 code if possible
|
||||
func (l *LinguaLanguageDetector) DetectLanguage(text string) (string, bool) {
|
||||
func (l *LinguaLanguageDetector) DetectLanguage(text string) (string, error) {
|
||||
lang, ok := l.detector.DetectLanguageOf(text)
|
||||
if !ok {
|
||||
return "", false
|
||||
return "", nil // Or an error if you prefer
|
||||
}
|
||||
// Prefer ISO 639-1 when available else fallback to ISO 639-3
|
||||
if s := lang.IsoCode639_1().String(); s != "" {
|
||||
return s, true
|
||||
return s, nil
|
||||
}
|
||||
if s := lang.IsoCode639_3().String(); s != "" {
|
||||
return s, true
|
||||
return s, nil
|
||||
}
|
||||
// fallback to language name
|
||||
return strings.ToLower(lang.String()), true
|
||||
return strings.ToLower(lang.String()), nil
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ package linguistics
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
models2 "tercul/internal/models"
|
||||
"tercul/internal/domain"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/platform/log"
|
||||
@ -18,14 +18,14 @@ type AnalysisRepository interface {
|
||||
GetWorkContent(ctx context.Context, workID uint, language string) (string, error)
|
||||
|
||||
// StoreWorkAnalysis stores work-specific analysis results
|
||||
StoreWorkAnalysis(ctx context.Context, workID uint, textMetadata *models2.TextMetadata,
|
||||
readabilityScore *models2.ReadabilityScore, languageAnalysis *models2.LanguageAnalysis) error
|
||||
StoreWorkAnalysis(ctx context.Context, workID uint, textMetadata *domain.TextMetadata,
|
||||
readabilityScore *domain.ReadabilityScore, languageAnalysis *domain.LanguageAnalysis) error
|
||||
|
||||
// GetWorkByID fetches a work by ID
|
||||
GetWorkByID(ctx context.Context, workID uint) (*models2.Work, error)
|
||||
GetWorkByID(ctx context.Context, workID uint) (*domain.Work, error)
|
||||
|
||||
// GetAnalysisData fetches persisted analysis data for a work
|
||||
GetAnalysisData(ctx context.Context, workID uint) (*models2.TextMetadata, *models2.ReadabilityScore, *models2.LanguageAnalysis, error)
|
||||
GetAnalysisData(ctx context.Context, workID uint) (*domain.TextMetadata, *domain.ReadabilityScore, *domain.LanguageAnalysis, error)
|
||||
}
|
||||
|
||||
// GORMAnalysisRepository implements AnalysisRepository using GORM
|
||||
@ -45,7 +45,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
||||
}
|
||||
|
||||
// Determine language from the work record to avoid hardcoded defaults
|
||||
var work models2.Work
|
||||
var work domain.Work
|
||||
if err := r.db.WithContext(ctx).First(&work, workID).Error; err != nil {
|
||||
log.LogError("Failed to fetch work for language",
|
||||
log.F("workID", workID),
|
||||
@ -54,7 +54,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
||||
}
|
||||
|
||||
// Create text metadata
|
||||
textMetadata := &models2.TextMetadata{
|
||||
textMetadata := &domain.TextMetadata{
|
||||
WorkID: workID,
|
||||
Language: work.Language,
|
||||
WordCount: result.WordCount,
|
||||
@ -65,7 +65,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
||||
}
|
||||
|
||||
// Create readability score
|
||||
readabilityScore := &models2.ReadabilityScore{
|
||||
readabilityScore := &domain.ReadabilityScore{
|
||||
WorkID: workID,
|
||||
Language: work.Language,
|
||||
Score: result.ReadabilityScore,
|
||||
@ -73,10 +73,10 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
||||
}
|
||||
|
||||
// Create language analysis
|
||||
languageAnalysis := &models2.LanguageAnalysis{
|
||||
languageAnalysis := &domain.LanguageAnalysis{
|
||||
WorkID: workID,
|
||||
Language: work.Language,
|
||||
Analysis: models2.JSONB{
|
||||
Analysis: domain.JSONB{
|
||||
"sentiment": result.Sentiment,
|
||||
"keywords": extractKeywordsAsJSON(result.Keywords),
|
||||
"topics": extractTopicsAsJSON(result.Topics),
|
||||
@ -89,7 +89,7 @@ func (r *GORMAnalysisRepository) StoreAnalysisResults(ctx context.Context, workI
|
||||
// GetWorkContent retrieves content for a work from translations
|
||||
func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint, language string) (string, error) {
|
||||
// First, get the work to determine its language
|
||||
var work models2.Work
|
||||
var work domain.Work
|
||||
if err := r.db.First(&work, workID).Error; err != nil {
|
||||
log.LogError("Failed to fetch work for content retrieval",
|
||||
log.F("workID", workID),
|
||||
@ -102,7 +102,7 @@ func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint
|
||||
// 2. Work's language translation
|
||||
// 3. Any available translation
|
||||
|
||||
var translation models2.Translation
|
||||
var translation domain.Translation
|
||||
|
||||
// Try original language first
|
||||
if err := r.db.Where("translatable_type = ? AND translatable_id = ? AND is_original_language = ?",
|
||||
@ -126,8 +126,8 @@ func (r *GORMAnalysisRepository) GetWorkContent(ctx context.Context, workID uint
|
||||
}
|
||||
|
||||
// GetWorkByID fetches a work by ID
|
||||
func (r *GORMAnalysisRepository) GetWorkByID(ctx context.Context, workID uint) (*models2.Work, error) {
|
||||
var work models2.Work
|
||||
func (r *GORMAnalysisRepository) GetWorkByID(ctx context.Context, workID uint) (*domain.Work, error) {
|
||||
var work domain.Work
|
||||
if err := r.db.WithContext(ctx).First(&work, workID).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch work: %w", err)
|
||||
}
|
||||
@ -135,10 +135,10 @@ func (r *GORMAnalysisRepository) GetWorkByID(ctx context.Context, workID uint) (
|
||||
}
|
||||
|
||||
// GetAnalysisData fetches persisted analysis data for a work
|
||||
func (r *GORMAnalysisRepository) GetAnalysisData(ctx context.Context, workID uint) (*models2.TextMetadata, *models2.ReadabilityScore, *models2.LanguageAnalysis, error) {
|
||||
var textMetadata models2.TextMetadata
|
||||
var readabilityScore models2.ReadabilityScore
|
||||
var languageAnalysis models2.LanguageAnalysis
|
||||
func (r *GORMAnalysisRepository) GetAnalysisData(ctx context.Context, workID uint) (*domain.TextMetadata, *domain.ReadabilityScore, *domain.LanguageAnalysis, error) {
|
||||
var textMetadata domain.TextMetadata
|
||||
var readabilityScore domain.ReadabilityScore
|
||||
var languageAnalysis domain.LanguageAnalysis
|
||||
|
||||
if err := r.db.WithContext(ctx).Where("work_id = ?", workID).First(&textMetadata).Error; err != nil {
|
||||
log.LogWarn("No text metadata found for work",
|
||||
@ -160,14 +160,14 @@ func (r *GORMAnalysisRepository) GetAnalysisData(ctx context.Context, workID uin
|
||||
|
||||
// StoreWorkAnalysis stores work-specific analysis results
|
||||
func (r *GORMAnalysisRepository) StoreWorkAnalysis(ctx context.Context, workID uint,
|
||||
textMetadata *models2.TextMetadata, readabilityScore *models2.ReadabilityScore,
|
||||
languageAnalysis *models2.LanguageAnalysis) error {
|
||||
textMetadata *domain.TextMetadata, readabilityScore *domain.ReadabilityScore,
|
||||
languageAnalysis *domain.LanguageAnalysis) error {
|
||||
|
||||
// Use a transaction to ensure all data is stored atomically
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// Store text metadata
|
||||
if textMetadata != nil {
|
||||
if err := tx.Where("work_id = ?", workID).Delete(&models2.TextMetadata{}).Error; err != nil {
|
||||
if err := tx.Where("work_id = ?", workID).Delete(&domain.TextMetadata{}).Error; err != nil {
|
||||
log.LogError("Failed to delete existing text metadata",
|
||||
log.F("workID", workID),
|
||||
log.F("error", err))
|
||||
@ -184,7 +184,7 @@ func (r *GORMAnalysisRepository) StoreWorkAnalysis(ctx context.Context, workID u
|
||||
|
||||
// Store readability score
|
||||
if readabilityScore != nil {
|
||||
if err := tx.Where("work_id = ?", workID).Delete(&models2.ReadabilityScore{}).Error; err != nil {
|
||||
if err := tx.Where("work_id = ?", workID).Delete(&domain.ReadabilityScore{}).Error; err != nil {
|
||||
log.LogError("Failed to delete existing readability score",
|
||||
log.F("workID", workID),
|
||||
log.F("error", err))
|
||||
@ -201,7 +201,7 @@ func (r *GORMAnalysisRepository) StoreWorkAnalysis(ctx context.Context, workID u
|
||||
|
||||
// Store language analysis
|
||||
if languageAnalysis != nil {
|
||||
if err := tx.Where("work_id = ?", workID).Delete(&models2.LanguageAnalysis{}).Error; err != nil {
|
||||
if err := tx.Where("work_id = ?", workID).Delete(&domain.LanguageAnalysis{}).Error; err != nil {
|
||||
log.LogError("Failed to delete existing language analysis",
|
||||
log.F("workID", workID),
|
||||
log.F("error", err))
|
||||
@ -224,9 +224,9 @@ func (r *GORMAnalysisRepository) StoreWorkAnalysis(ctx context.Context, workID u
|
||||
}
|
||||
|
||||
// Helper functions for data conversion
|
||||
func extractKeywordsAsJSON(keywords []Keyword) models2.JSONB {
|
||||
func extractKeywordsAsJSON(keywords []Keyword) domain.JSONB {
|
||||
if len(keywords) == 0 {
|
||||
return models2.JSONB{}
|
||||
return domain.JSONB{}
|
||||
}
|
||||
|
||||
keywordData := make([]map[string]interface{}, len(keywords))
|
||||
@ -237,12 +237,12 @@ func extractKeywordsAsJSON(keywords []Keyword) models2.JSONB {
|
||||
}
|
||||
}
|
||||
|
||||
return models2.JSONB{"keywords": keywordData}
|
||||
return domain.JSONB{"keywords": keywordData}
|
||||
}
|
||||
|
||||
func extractTopicsAsJSON(topics []Topic) models2.JSONB {
|
||||
func extractTopicsAsJSON(topics []Topic) domain.JSONB {
|
||||
if len(topics) == 0 {
|
||||
return models2.JSONB{}
|
||||
return domain.JSONB{}
|
||||
}
|
||||
|
||||
topicData := make([]map[string]interface{}, len(topics))
|
||||
@ -253,5 +253,5 @@ func extractTopicsAsJSON(topics []Topic) models2.JSONB {
|
||||
}
|
||||
}
|
||||
|
||||
return models2.JSONB{"topics": topicData}
|
||||
return domain.JSONB{"topics": topicData}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ func (e *KeywordExtractor) Extract(text Text) ([]Keyword, error) {
|
||||
|
||||
// Filter out stop words
|
||||
for word := range wordFreq {
|
||||
if isStopWord(word) {
|
||||
if isStopWord(word, text.Language) {
|
||||
delete(wordFreq, word)
|
||||
}
|
||||
}
|
||||
@ -72,36 +72,3 @@ func (e *KeywordExtractor) Extract(text Text) ([]Keyword, error) {
|
||||
return keywords, nil
|
||||
}
|
||||
|
||||
// isStopWord checks if a word is a common stop word
|
||||
func isStopWord(word string) bool {
|
||||
stopWords := map[string]bool{
|
||||
"a": true, "about": true, "above": true, "after": true, "again": true,
|
||||
"against": true, "all": true, "am": true, "an": true, "and": true,
|
||||
"any": true, "are": true, "as": true, "at": true, "be": true,
|
||||
"because": true, "been": true, "before": true, "being": true, "below": true,
|
||||
"between": true, "both": true, "but": true, "by": true, "can": true,
|
||||
"did": true, "do": true, "does": true, "doing": true, "don": true,
|
||||
"down": true, "during": true, "each": true, "few": true, "for": true,
|
||||
"from": true, "further": true, "had": true, "has": true, "have": true,
|
||||
"having": true, "he": true, "her": true, "here": true, "hers": true,
|
||||
"herself": true, "him": true, "himself": true, "his": true, "how": true,
|
||||
"i": true, "if": true, "in": true, "into": true, "is": true,
|
||||
"it": true, "its": true, "itself": true, "just": true, "me": true,
|
||||
"more": true, "most": true, "my": true, "myself": true, "no": true,
|
||||
"nor": true, "not": true, "now": true, "of": true, "off": true,
|
||||
"on": true, "once": true, "only": true, "or": true, "other": true,
|
||||
"our": true, "ours": true, "ourselves": true, "out": true, "over": true,
|
||||
"own": true, "same": true, "she": true, "should": true, "so": true,
|
||||
"some": true, "such": true, "than": true, "that": true, "the": true,
|
||||
"their": true, "theirs": true, "them": true, "themselves": true, "then": true,
|
||||
"there": true, "these": true, "they": true, "this": true, "those": true,
|
||||
"through": true, "to": true, "too": true, "under": true, "until": true,
|
||||
"up": true, "very": true, "was": true, "we": true, "were": true,
|
||||
"what": true, "when": true, "where": true, "which": true, "while": true,
|
||||
"who": true, "whom": true, "why": true, "will": true, "with": true,
|
||||
"would": true, "you": true, "your": true, "yours": true, "yourself": true,
|
||||
"yourselves": true,
|
||||
}
|
||||
|
||||
return stopWords[word]
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package enrich
|
||||
package linguistics
|
||||
|
||||
import "testing"
|
||||
|
||||
|
||||
@ -4,16 +4,16 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LanguageDetector detects the language of a text
|
||||
type LanguageDetector struct{}
|
||||
// languageDetector detects the language of a text
|
||||
type languageDetector struct{}
|
||||
|
||||
// NewLanguageDetector creates a new LanguageDetector
|
||||
func NewLanguageDetector() *LanguageDetector {
|
||||
return &LanguageDetector{}
|
||||
func NewLanguageDetector() *languageDetector {
|
||||
return &languageDetector{}
|
||||
}
|
||||
|
||||
// Detect detects the language of a text and returns the language code, confidence, and error
|
||||
func (d *LanguageDetector) Detect(text Text) (string, float64, error) {
|
||||
func (d *languageDetector) DetectLanguage(text string) (string, error) {
|
||||
// This is a simplified implementation
|
||||
// In a real-world scenario, you would use a library like github.com/pemistahl/lingua-go
|
||||
// or call an external API for language detection
|
||||
|
||||
@ -3,7 +3,7 @@ package linguistics
|
||||
// LanguageDetector defines a provider that can detect the language of a text
|
||||
type LanguageDetector interface {
|
||||
// DetectLanguage returns a BCP-47 or ISO-like code and whether detection was confident
|
||||
DetectLanguage(text string) (string, bool)
|
||||
DetectLanguage(text string) (string, error)
|
||||
}
|
||||
|
||||
// SentimentProvider defines a provider that scores sentiment in [-1, 1]
|
||||
|
||||
@ -26,8 +26,9 @@ func DefaultRegistry() *Registry {
|
||||
|
||||
// Text represents a text to be analyzed
|
||||
type Text struct {
|
||||
ID uint
|
||||
Body string
|
||||
ID uint
|
||||
Body string
|
||||
Language string
|
||||
}
|
||||
|
||||
// Token represents a token in a text
|
||||
@ -38,12 +39,6 @@ type Token struct {
|
||||
Length int
|
||||
}
|
||||
|
||||
// Keyword represents a keyword extracted from a text
|
||||
type Keyword struct {
|
||||
Text string
|
||||
Relevance float64
|
||||
}
|
||||
|
||||
// PoeticMetrics represents metrics from poetic analysis
|
||||
type PoeticMetrics struct {
|
||||
RhymeScheme string
|
||||
|
||||
@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
models2 "tercul/internal/models"
|
||||
"tercul/internal/domain"
|
||||
"time"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
@ -60,7 +60,7 @@ func (j *LinguisticSyncJob) EnqueueAnalysisForAllWorks() error {
|
||||
log.Println("Enqueueing linguistic analysis jobs for all works...")
|
||||
|
||||
var workIDs []uint
|
||||
if err := j.DB.Model(&models2.Work{}).Pluck("id", &workIDs).Error; err != nil {
|
||||
if err := j.DB.Model(&domain.Work{}).Pluck("id", &workIDs).Error; err != nil {
|
||||
return fmt.Errorf("error fetching work IDs: %w", err)
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func (j *LinguisticSyncJob) HandleLinguisticAnalysis(ctx context.Context, t *asy
|
||||
|
||||
// Check if analysis already exists
|
||||
var count int64
|
||||
if err := j.DB.Model(&models2.LanguageAnalysis{}).Where("work_id = ?", payload.WorkID).Count(&count).Error; err != nil {
|
||||
if err := j.DB.Model(&domain.LanguageAnalysis{}).Where("work_id = ?", payload.WorkID).Count(&count).Error; err != nil {
|
||||
return fmt.Errorf("error checking existing analysis: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ package linguistics
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain"
|
||||
"time"
|
||||
|
||||
"tercul/internal/platform/log"
|
||||
@ -206,7 +206,7 @@ func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint)
|
||||
}
|
||||
|
||||
// extractSentimentFromAnalysis extracts sentiment from the Analysis JSONB field
|
||||
func extractSentimentFromAnalysis(analysis models.JSONB) float64 {
|
||||
func extractSentimentFromAnalysis(analysis domain.JSONB) float64 {
|
||||
if analysis == nil {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// SyncAllEdges syncs all edges by enqueueing batch jobs.
|
||||
@ -12,7 +12,7 @@ func (s *SyncJob) SyncAllEdges(ctx context.Context) error {
|
||||
log.Println("Enqueueing edge sync jobs...")
|
||||
|
||||
var count int64
|
||||
if err := s.DB.Model(&models.Edge{}).Count(&count).Error; err != nil {
|
||||
if err := s.DB.Model(&domain.Edge{}).Count(&count).Error; err != nil {
|
||||
return fmt.Errorf("error counting edges: %w", err)
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func (s *SyncJob) SyncAllEdges(ctx context.Context) error {
|
||||
func (s *SyncJob) SyncEdgesBatch(ctx context.Context, batchSize, offset int) error {
|
||||
log.Printf("Syncing edges batch (offset %d, batch size %d)...", offset, batchSize)
|
||||
|
||||
var edges []models.Edge
|
||||
var edges []domain.Edge
|
||||
if err := s.DB.Limit(batchSize).Offset(offset).Find(&edges).Error; err != nil {
|
||||
return fmt.Errorf("error fetching edges batch: %w", err)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
@ -55,7 +55,7 @@ func NewJWTManager() *JWTManager {
|
||||
}
|
||||
|
||||
// GenerateToken generates a new JWT token for a user
|
||||
func (j *JWTManager) GenerateToken(user *models.User) (string, error) {
|
||||
func (j *JWTManager) GenerateToken(user *domain.User) (string, error) {
|
||||
now := time.Now()
|
||||
claims := &Claims{
|
||||
UserID: user.ID,
|
||||
|
||||
@ -73,10 +73,7 @@ func InitDB() (*gorm.DB, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run migrations
|
||||
if err := RunMigrations(db); err != nil {
|
||||
return nil, fmt.Errorf("failed to run migrations: %w", err)
|
||||
}
|
||||
// Migrations are now handled by a separate tool
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
@ -1,331 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
models2 "tercul/internal/models"
|
||||
"tercul/internal/platform/log"
|
||||
)
|
||||
|
||||
// RunMigrations runs all database migrations
|
||||
func RunMigrations(db *gorm.DB) error {
|
||||
log.LogInfo("Running database migrations")
|
||||
|
||||
// First, create all tables using GORM AutoMigrate
|
||||
if err := createTables(db); err != nil {
|
||||
log.LogError("Failed to create tables", log.F("error", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Then add indexes to improve query performance
|
||||
if err := addIndexes(db); err != nil {
|
||||
log.LogError("Failed to add indexes", log.F("error", err))
|
||||
return err
|
||||
}
|
||||
|
||||
log.LogInfo("Database migrations completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTables creates all database tables using GORM AutoMigrate
|
||||
func createTables(db *gorm.DB) error {
|
||||
log.LogInfo("Creating database tables")
|
||||
|
||||
// Enable recommended extensions
|
||||
if err := db.Exec("CREATE EXTENSION IF NOT EXISTS pg_trgm").Error; err != nil {
|
||||
log.LogError("Failed to enable pg_trgm extension", log.F("error", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Create all models/tables
|
||||
err := db.AutoMigrate(
|
||||
// User-related models
|
||||
&models2.User{},
|
||||
&models2.UserProfile{},
|
||||
&models2.UserSession{},
|
||||
&models2.PasswordReset{},
|
||||
&models2.EmailVerification{},
|
||||
|
||||
// Literary models
|
||||
&models2.Work{},
|
||||
&models2.Translation{},
|
||||
&models2.Author{},
|
||||
&models2.Book{},
|
||||
&models2.Publisher{},
|
||||
&models2.Source{},
|
||||
&models2.Edition{},
|
||||
&models2.Series{},
|
||||
&models2.WorkSeries{},
|
||||
|
||||
// Organization models
|
||||
&models2.Tag{},
|
||||
&models2.Category{},
|
||||
|
||||
// Interaction models
|
||||
&models2.Comment{},
|
||||
&models2.Like{},
|
||||
&models2.Bookmark{},
|
||||
&models2.Collection{},
|
||||
&models2.Contribution{},
|
||||
&models2.InteractionEvent{},
|
||||
|
||||
// Location models
|
||||
&models2.Country{},
|
||||
&models2.City{},
|
||||
&models2.Place{},
|
||||
&models2.Address{},
|
||||
&models2.Language{},
|
||||
|
||||
// Linguistic models
|
||||
&models2.ReadabilityScore{},
|
||||
&models2.WritingStyle{},
|
||||
&models2.LinguisticLayer{},
|
||||
&models2.TextMetadata{},
|
||||
&models2.PoeticAnalysis{},
|
||||
&models2.Word{},
|
||||
&models2.Concept{},
|
||||
&models2.LanguageEntity{},
|
||||
&models2.TextBlock{},
|
||||
&models2.WordOccurrence{},
|
||||
&models2.EntityOccurrence{},
|
||||
|
||||
// Relationship models
|
||||
&models2.Edge{},
|
||||
&models2.Embedding{},
|
||||
&models2.Media{},
|
||||
&models2.BookWork{},
|
||||
&models2.AuthorCountry{},
|
||||
&models2.WorkAuthor{},
|
||||
&models2.BookAuthor{},
|
||||
|
||||
// System models
|
||||
&models2.Notification{},
|
||||
&models2.EditorialWorkflow{},
|
||||
&models2.Admin{},
|
||||
&models2.Vote{},
|
||||
&models2.Contributor{},
|
||||
&models2.HybridEntityWork{},
|
||||
&models2.ModerationFlag{},
|
||||
&models2.AuditLog{},
|
||||
|
||||
// Rights models
|
||||
&models2.Copyright{},
|
||||
&models2.CopyrightClaim{},
|
||||
&models2.Monetization{},
|
||||
&models2.License{},
|
||||
|
||||
// Analytics models
|
||||
&models2.WorkStats{},
|
||||
&models2.TranslationStats{},
|
||||
&models2.UserStats{},
|
||||
&models2.BookStats{},
|
||||
&models2.CollectionStats{},
|
||||
&models2.MediaStats{},
|
||||
|
||||
// Metadata models
|
||||
&models2.LanguageAnalysis{},
|
||||
&models2.Gamification{},
|
||||
&models2.Stats{},
|
||||
&models2.SearchDocument{},
|
||||
|
||||
// Psychological models
|
||||
&models2.Emotion{},
|
||||
&models2.Mood{},
|
||||
&models2.TopicCluster{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.LogInfo("Database tables created successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// addIndexes adds indexes to frequently queried columns
|
||||
func addIndexes(db *gorm.DB) error {
|
||||
// Work table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_language ON works(language)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_title ON works(title)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_status ON works(status)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_slug ON works(slug)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Translation table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_work_id ON translations(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_language ON translations(language)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_translator_id ON translations(translator_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS ux_translations_entity_lang ON translations(translatable_type, translatable_id, language)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// User table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_role ON users(role)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Author table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_authors_name ON authors(name)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Category table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_categories_name ON categories(name)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_categories_slug ON categories(slug)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_categories_path ON categories(path)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Tag table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_tags_slug ON tags(slug)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Comment table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_user_id ON comments(user_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_work_id ON comments(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_translation_id ON comments(translation_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Like table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_likes_user_id ON likes(user_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_likes_work_id ON likes(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_likes_translation_id ON likes(translation_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Bookmark table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_bookmarks_user_id ON bookmarks(user_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_bookmarks_work_id ON bookmarks(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Collection table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_collections_user_id ON collections(user_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Contribution table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_contributions_user_id ON contributions(user_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_contributions_work_id ON contributions(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_contributions_status ON contributions(status)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Edge table indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_edges_source_table_id ON edges(source_table, source_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_edges_target_table_id ON edges(target_table, target_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_edges_relation ON edges(relation)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// WorkAuthor unique pair and order index
|
||||
if err := db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS ux_work_authors_pair ON work_authors(work_id, author_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_work_authors_ordinal ON work_authors(ordinal)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// BookAuthor unique pair and order index
|
||||
if err := db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS ux_book_authors_pair ON book_authors(book_id, author_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_book_authors_ordinal ON book_authors(ordinal)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// InteractionEvent indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_interaction_events_target ON interaction_events(target_type, target_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_interaction_events_kind ON interaction_events(kind)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_interaction_events_user ON interaction_events(user_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// SearchDocument indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_search_documents_entity ON search_documents(entity_type, entity_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_search_documents_lang ON search_documents(language_code)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Linguistic analysis indexes
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_text_metadata_work_id ON text_metadata(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_readability_scores_work_id ON readability_scores(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_language_analyses_work_id ON language_analyses(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_poetic_analyses_work_id ON poetic_analyses(work_id)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Timestamps indexes for frequently queried tables
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_created_at ON works(created_at)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_created_at ON translations(created_at)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_created_at ON comments(created_at)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at)").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.LogInfo("Database indexes added successfully")
|
||||
return nil
|
||||
}
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/platform/config"
|
||||
"time"
|
||||
|
||||
@ -27,7 +27,7 @@ func InitWeaviate() {
|
||||
}
|
||||
|
||||
// UpsertWork inserts or updates a Work object in Weaviate
|
||||
func UpsertWork(work models.Work) error {
|
||||
func UpsertWork(work domain.Work) error {
|
||||
// Create a properties map with the fields that exist in the Work model
|
||||
properties := map[string]interface{}{
|
||||
"language": work.Language,
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
)
|
||||
|
||||
// CopyrightRepository defines CRUD methods specific to Copyright.
|
||||
type CopyrightRepository interface {
|
||||
BaseRepository[models.Copyright]
|
||||
// Polymorphic methods
|
||||
AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error
|
||||
DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error
|
||||
GetByEntity(ctx context.Context, entityID uint, entityType string) ([]models.Copyright, error)
|
||||
GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]models.Copyrightable, error)
|
||||
// Translation methods
|
||||
AddTranslation(ctx context.Context, translation *models.CopyrightTranslation) error
|
||||
GetTranslations(ctx context.Context, copyrightID uint) ([]models.CopyrightTranslation, error)
|
||||
GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*models.CopyrightTranslation, error)
|
||||
}
|
||||
|
||||
type copyrightRepository struct {
|
||||
BaseRepository[models.Copyright]
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewCopyrightRepository creates a new CopyrightRepository.
|
||||
func NewCopyrightRepository(db *gorm.DB) CopyrightRepository {
|
||||
return ©rightRepository{
|
||||
BaseRepository: NewBaseRepositoryImpl[models.Copyright](db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// AttachToEntity attaches a copyright to any entity type
|
||||
func (r *copyrightRepository) AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error {
|
||||
copyrightable := models.Copyrightable{
|
||||
CopyrightID: copyrightID,
|
||||
CopyrightableID: entityID,
|
||||
CopyrightableType: entityType,
|
||||
}
|
||||
return r.db.WithContext(ctx).Create(©rightable).Error
|
||||
}
|
||||
|
||||
// DetachFromEntity removes a copyright from an entity
|
||||
func (r *copyrightRepository) DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) error {
|
||||
return r.db.WithContext(ctx).Where("copyright_id = ? AND copyrightable_id = ? AND copyrightable_type = ?",
|
||||
copyrightID, entityID, entityType).Delete(&models.Copyrightable{}).Error
|
||||
}
|
||||
|
||||
// GetByEntity gets all copyrights for a specific entity
|
||||
func (r *copyrightRepository) GetByEntity(ctx context.Context, entityID uint, entityType string) ([]models.Copyright, error) {
|
||||
var copyrights []models.Copyright
|
||||
err := r.db.WithContext(ctx).Joins("JOIN copyrightables ON copyrightables.copyright_id = copyrights.id").
|
||||
Where("copyrightables.copyrightable_id = ? AND copyrightables.copyrightable_type = ?", entityID, entityType).
|
||||
Preload("Translations").
|
||||
Find(©rights).Error
|
||||
return copyrights, err
|
||||
}
|
||||
|
||||
// GetEntitiesByCopyright gets all entities that have a specific copyright
|
||||
func (r *copyrightRepository) GetEntitiesByCopyright(ctx context.Context, copyrightID uint) ([]models.Copyrightable, error) {
|
||||
var copyrightables []models.Copyrightable
|
||||
err := r.db.WithContext(ctx).Where("copyright_id = ?", copyrightID).Find(©rightables).Error
|
||||
return copyrightables, err
|
||||
}
|
||||
|
||||
// AddTranslation adds a translation to a copyright
|
||||
func (r *copyrightRepository) AddTranslation(ctx context.Context, translation *models.CopyrightTranslation) error {
|
||||
return r.db.WithContext(ctx).Create(translation).Error
|
||||
}
|
||||
|
||||
// GetTranslations gets all translations for a copyright
|
||||
func (r *copyrightRepository) GetTranslations(ctx context.Context, copyrightID uint) ([]models.CopyrightTranslation, error) {
|
||||
var translations []models.CopyrightTranslation
|
||||
err := r.db.WithContext(ctx).Where("copyright_id = ?", copyrightID).Find(&translations).Error
|
||||
return translations, err
|
||||
}
|
||||
|
||||
// GetTranslationByLanguage gets a specific translation by language code
|
||||
func (r *copyrightRepository) GetTranslationByLanguage(ctx context.Context, copyrightID uint, languageCode string) (*models.CopyrightTranslation, error) {
|
||||
var translation models.CopyrightTranslation
|
||||
err := r.db.WithContext(ctx).Where("copyright_id = ? AND language_code = ?", copyrightID, languageCode).First(&translation).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrEntityNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &translation, nil
|
||||
}
|
||||
@ -1,146 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
models2 "tercul/internal/models"
|
||||
)
|
||||
|
||||
// DB represents a database connection
|
||||
type DB struct {
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
// Connect creates a new database connection
|
||||
func Connect() *DB {
|
||||
// In a real application, this would use configuration from environment variables
|
||||
// or a configuration file to connect to the database
|
||||
// For this example, we'll assume the DB connection is passed in from main.go
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListPendingWorks returns a list of works that need to be enriched
|
||||
func ListPendingWorks(db *DB) []Work {
|
||||
var works []Work
|
||||
|
||||
// Query for works that haven't been enriched yet
|
||||
var modelWorks []models2.Work
|
||||
db.Where("id NOT IN (SELECT work_id FROM language_analyses)").Find(&modelWorks)
|
||||
|
||||
// Convert to store.Work
|
||||
for _, work := range modelWorks {
|
||||
// Prefer original language translation; fallback to work language; then any
|
||||
var content string
|
||||
var t models2.Translation
|
||||
// Try original
|
||||
if err := db.Where("translatable_type = ? AND translatable_id = ? AND is_original_language = ?", "Work", work.ID, true).
|
||||
First(&t).Error; err == nil {
|
||||
content = t.Content
|
||||
} else {
|
||||
// Try same language
|
||||
if err := db.Where("translatable_type = ? AND translatable_id = ? AND language = ?", "Work", work.ID, work.Language).
|
||||
First(&t).Error; err == nil {
|
||||
content = t.Content
|
||||
} else {
|
||||
// Any translation
|
||||
if err := db.Where("translatable_type = ? AND translatable_id = ?", "Work", work.ID).
|
||||
First(&t).Error; err == nil {
|
||||
content = t.Content
|
||||
}
|
||||
}
|
||||
}
|
||||
works = append(works, Work{
|
||||
ID: work.ID,
|
||||
Body: content,
|
||||
})
|
||||
}
|
||||
|
||||
return works
|
||||
}
|
||||
|
||||
// UpsertWord creates or updates a word in the database
|
||||
func UpsertWord(db *DB, workID uint, text, lemma, pos, phonetic string) error {
|
||||
// Check if the word already exists
|
||||
var word models2.Word
|
||||
result := db.Where("text = ? AND language = ?", text, "auto").First(&word)
|
||||
|
||||
if result.Error != nil && result.Error != gorm.ErrRecordNotFound {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// Create or update the word
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
// Create new word
|
||||
word = models2.Word{
|
||||
Text: text,
|
||||
Language: "auto", // This would be set to the detected language
|
||||
PartOfSpeech: pos,
|
||||
Lemma: lemma,
|
||||
}
|
||||
if err := db.Create(&word).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Update existing word
|
||||
word.PartOfSpeech = pos
|
||||
word.Lemma = lemma
|
||||
if err := db.Save(&word).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Associate the word with the work
|
||||
return db.Exec("INSERT INTO work_words (work_id, word_id) VALUES (?, ?) ON CONFLICT DO NOTHING", workID, word.ID).Error
|
||||
}
|
||||
|
||||
// SaveKeywords saves keywords for a work
|
||||
func SaveKeywords(db *DB, workID uint, keywords []string) error {
|
||||
// Clear existing keywords
|
||||
if err := db.Exec("DELETE FROM work_topic_clusters WHERE work_id = ?", workID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a topic cluster for the keywords
|
||||
cluster := models2.TopicCluster{
|
||||
Name: "Auto-generated",
|
||||
Description: "Automatically generated keywords",
|
||||
Keywords: strings.Join(keywords, ", "),
|
||||
}
|
||||
|
||||
if err := db.Create(&cluster).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Associate the cluster with the work
|
||||
return db.Exec("INSERT INTO work_topic_clusters (work_id, topic_cluster_id) VALUES (?, ?)", workID, cluster.ID).Error
|
||||
}
|
||||
|
||||
// SavePoetics saves poetic analysis for a work
|
||||
func SavePoetics(db *DB, workID uint, metrics PoeticMetrics) error {
|
||||
poetics := models2.PoeticAnalysis{
|
||||
WorkID: workID,
|
||||
Language: "auto", // This would be set to the detected language
|
||||
RhymeScheme: metrics.RhymeScheme,
|
||||
MeterType: metrics.MeterType,
|
||||
StanzaCount: metrics.StanzaCount,
|
||||
LineCount: metrics.LineCount,
|
||||
Structure: metrics.Structure,
|
||||
}
|
||||
|
||||
return db.Create(&poetics).Error
|
||||
}
|
||||
|
||||
// MarkEnriched marks a work as enriched with the detected language
|
||||
func MarkEnriched(db *DB, workID uint, language string) error {
|
||||
// Create a language analysis record to mark the work as processed
|
||||
analysis := models2.LanguageAnalysis{
|
||||
WorkID: workID,
|
||||
Language: language,
|
||||
Analysis: models2.JSONB{
|
||||
"enriched": true,
|
||||
"language": language,
|
||||
},
|
||||
}
|
||||
|
||||
return db.Create(&analysis).Error
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package store
|
||||
|
||||
// Work represents a work to be processed
|
||||
type Work struct {
|
||||
ID uint
|
||||
Body string
|
||||
}
|
||||
|
||||
// PoeticMetrics represents metrics from poetic analysis
|
||||
type PoeticMetrics struct {
|
||||
RhymeScheme string
|
||||
MeterType string
|
||||
StanzaCount int
|
||||
LineCount int
|
||||
Structure string
|
||||
}
|
||||
@ -1,119 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"tercul/internal/enrich"
|
||||
)
|
||||
|
||||
// ProcessWork processes a work using the enrichment registry and stores the results
|
||||
func ProcessWork(ctx context.Context, reg *enrich.Registry, db *DB, work Work) error {
|
||||
log.Printf("Processing work ID %d", work.ID)
|
||||
|
||||
// Create a text object for the enrichment services
|
||||
text := enrich.Text{ID: work.ID, Body: work.Body}
|
||||
|
||||
// Detect language
|
||||
lang, confidence, err := reg.Lang.Detect(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Detected language: %s (confidence: %.2f)", lang, confidence)
|
||||
|
||||
// Tokenize text
|
||||
tokens, err := reg.Tok.Tokenize(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Tokenized text into %d tokens", len(tokens))
|
||||
|
||||
// Tag parts of speech
|
||||
pos, err := reg.Pos.Tag(tokens)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Tagged %d tokens with parts of speech", len(pos))
|
||||
|
||||
// Process each token
|
||||
for i, token := range tokens {
|
||||
// Get lemma
|
||||
lemma, err := reg.Lem.Lemma(token.Text, lang)
|
||||
if err != nil {
|
||||
log.Printf("Error getting lemma for token %s: %v", token.Text, err)
|
||||
lemma = token.Text // Use the original text as fallback
|
||||
}
|
||||
|
||||
// Get phonetic encoding
|
||||
phonetic := reg.Phon.Encode(token.Text)
|
||||
|
||||
// Store the word
|
||||
if err := UpsertWord(db, work.ID, token.Text, lemma, pos[i], phonetic); err != nil {
|
||||
log.Printf("Error storing word %s: %v", token.Text, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract keywords
|
||||
keywords, err := reg.Key.Extract(text)
|
||||
if err != nil {
|
||||
log.Printf("Error extracting keywords: %v", err)
|
||||
} else {
|
||||
// Convert keywords to strings
|
||||
keywordStrings := make([]string, len(keywords))
|
||||
for i, kw := range keywords {
|
||||
keywordStrings[i] = kw.Text
|
||||
}
|
||||
|
||||
// Save keywords
|
||||
if err := SaveKeywords(db, work.ID, keywordStrings); err != nil {
|
||||
log.Printf("Error saving keywords: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze poetics
|
||||
enrichMetrics, err := reg.Poet.Analyse(text)
|
||||
if err != nil {
|
||||
log.Printf("Error analyzing poetics: %v", err)
|
||||
} else {
|
||||
// Convert to store.PoeticMetrics
|
||||
metrics := PoeticMetrics{
|
||||
RhymeScheme: enrichMetrics.RhymeScheme,
|
||||
MeterType: enrichMetrics.MeterType,
|
||||
StanzaCount: enrichMetrics.StanzaCount,
|
||||
LineCount: enrichMetrics.LineCount,
|
||||
Structure: enrichMetrics.Structure,
|
||||
}
|
||||
|
||||
// Save poetics
|
||||
if err := SavePoetics(db, work.ID, metrics); err != nil {
|
||||
log.Printf("Error saving poetics: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the work as enriched
|
||||
if err := MarkEnriched(db, work.ID, lang); err != nil {
|
||||
log.Printf("Error marking work as enriched: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Successfully processed work ID %d", work.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessPendingWorks processes all pending works
|
||||
func ProcessPendingWorks(ctx context.Context, reg *enrich.Registry, db *DB) error {
|
||||
log.Println("Processing pending works...")
|
||||
|
||||
// Get pending works
|
||||
works := ListPendingWorks(db)
|
||||
log.Printf("Found %d pending works", len(works))
|
||||
|
||||
// Process each work
|
||||
for _, work := range works {
|
||||
if err := ProcessWork(ctx, reg, db, work); err != nil {
|
||||
log.Printf("Error processing work ID %d: %v", work.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Finished processing pending works")
|
||||
return nil
|
||||
}
|
||||
@ -13,37 +13,41 @@ import (
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/repositories"
|
||||
"tercul/services"
|
||||
"tercul/graph"
|
||||
|
||||
graph "tercul/internal/adapters/graphql"
|
||||
"tercul/internal/app/auth"
|
||||
"tercul/internal/app/localization"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/data/sql"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// IntegrationTestSuite provides a comprehensive test environment with either in-memory SQLite or mock repositories
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
DB *gorm.DB
|
||||
WorkRepo repositories.WorkRepository
|
||||
UserRepo repositories.UserRepository
|
||||
AuthorRepo repositories.AuthorRepository
|
||||
TranslationRepo repositories.TranslationRepository
|
||||
CommentRepo repositories.CommentRepository
|
||||
LikeRepo repositories.LikeRepository
|
||||
BookmarkRepo repositories.BookmarkRepository
|
||||
CollectionRepo repositories.CollectionRepository
|
||||
TagRepo repositories.TagRepository
|
||||
CategoryRepo repositories.CategoryRepository
|
||||
|
||||
DB *gorm.DB
|
||||
WorkRepo domain.WorkRepository
|
||||
UserRepo domain.UserRepository
|
||||
AuthorRepo domain.AuthorRepository
|
||||
TranslationRepo domain.TranslationRepository
|
||||
CommentRepo domain.CommentRepository
|
||||
LikeRepo domain.LikeRepository
|
||||
BookmarkRepo domain.BookmarkRepository
|
||||
CollectionRepo domain.CollectionRepository
|
||||
TagRepo domain.TagRepository
|
||||
CategoryRepo domain.CategoryRepository
|
||||
|
||||
// Services
|
||||
WorkService services.WorkService
|
||||
Localization services.LocalizationService
|
||||
AuthService services.AuthService
|
||||
|
||||
WorkCommands *work.WorkCommands
|
||||
WorkQueries *work.WorkQueries
|
||||
Localization localization.Service
|
||||
AuthService auth.Service
|
||||
|
||||
// Test data
|
||||
TestWorks []*models.Work
|
||||
TestUsers []*models.User
|
||||
TestAuthors []*models.Author
|
||||
TestTranslations []*models.Translation
|
||||
TestWorks []*domain.Work
|
||||
TestUsers []*domain.User
|
||||
TestAuthors []*domain.Author
|
||||
TestTranslations []*domain.Translation
|
||||
}
|
||||
|
||||
// TestConfig holds configuration for the test environment
|
||||
@ -115,53 +119,53 @@ func (s *IntegrationTestSuite) setupInMemoryDB(config *TestConfig) {
|
||||
|
||||
// Run migrations
|
||||
if err := db.AutoMigrate(
|
||||
&models.Work{},
|
||||
&models.User{},
|
||||
&models.Author{},
|
||||
&models.Translation{},
|
||||
&models.Comment{},
|
||||
&models.Like{},
|
||||
&models.Bookmark{},
|
||||
&models.Collection{},
|
||||
&models.Tag{},
|
||||
&models.Category{},
|
||||
&models.Country{},
|
||||
&models.City{},
|
||||
&models.Place{},
|
||||
&models.Address{},
|
||||
&models.Copyright{},
|
||||
&models.CopyrightClaim{},
|
||||
&models.Monetization{},
|
||||
&models.Book{},
|
||||
&models.Publisher{},
|
||||
&models.Source{},
|
||||
// &models.WorkAnalytics{}, // Commented out as it's not in models package
|
||||
&models.ReadabilityScore{},
|
||||
&models.WritingStyle{},
|
||||
&models.Emotion{},
|
||||
&models.TopicCluster{},
|
||||
&models.Mood{},
|
||||
&models.Concept{},
|
||||
&models.LinguisticLayer{},
|
||||
&models.WorkStats{},
|
||||
&models.TextMetadata{},
|
||||
&models.PoeticAnalysis{},
|
||||
&models.TranslationField{},
|
||||
&domain.Work{},
|
||||
&domain.User{},
|
||||
&domain.Author{},
|
||||
&domain.Translation{},
|
||||
&domain.Comment{},
|
||||
&domain.Like{},
|
||||
&domain.Bookmark{},
|
||||
&domain.Collection{},
|
||||
&domain.Tag{},
|
||||
&domain.Category{},
|
||||
&domain.Country{},
|
||||
&domain.City{},
|
||||
&domain.Place{},
|
||||
&domain.Address{},
|
||||
&domain.Copyright{},
|
||||
&domain.CopyrightClaim{},
|
||||
&domain.Monetization{},
|
||||
&domain.Book{},
|
||||
&domain.Publisher{},
|
||||
&domain.Source{},
|
||||
// &domain.WorkAnalytics{}, // Commented out as it's not in models package
|
||||
&domain.ReadabilityScore{},
|
||||
&domain.WritingStyle{},
|
||||
&domain.Emotion{},
|
||||
&domain.TopicCluster{},
|
||||
&domain.Mood{},
|
||||
&domain.Concept{},
|
||||
&domain.LinguisticLayer{},
|
||||
&domain.WorkStats{},
|
||||
&domain.TextMetadata{},
|
||||
&domain.PoeticAnalysis{},
|
||||
&domain.TranslationField{},
|
||||
); err != nil {
|
||||
s.T().Fatalf("Failed to run migrations: %v", err)
|
||||
}
|
||||
|
||||
// Create repository instances
|
||||
s.WorkRepo = repositories.NewWorkRepository(db)
|
||||
s.UserRepo = repositories.NewUserRepository(db)
|
||||
s.AuthorRepo = repositories.NewAuthorRepository(db)
|
||||
s.TranslationRepo = repositories.NewTranslationRepository(db)
|
||||
s.CommentRepo = repositories.NewCommentRepository(db)
|
||||
s.LikeRepo = repositories.NewLikeRepository(db)
|
||||
s.BookmarkRepo = repositories.NewBookmarkRepository(db)
|
||||
s.CollectionRepo = repositories.NewCollectionRepository(db)
|
||||
s.TagRepo = repositories.NewTagRepository(db)
|
||||
s.CategoryRepo = repositories.NewCategoryRepository(db)
|
||||
s.WorkRepo = sql.NewWorkRepository(db)
|
||||
s.UserRepo = sql.NewUserRepository(db)
|
||||
s.AuthorRepo = sql.NewAuthorRepository(db)
|
||||
s.TranslationRepo = sql.NewTranslationRepository(db)
|
||||
s.CommentRepo = sql.NewCommentRepository(db)
|
||||
s.LikeRepo = sql.NewLikeRepository(db)
|
||||
s.BookmarkRepo = sql.NewBookmarkRepository(db)
|
||||
s.CollectionRepo = sql.NewCollectionRepository(db)
|
||||
s.TagRepo = sql.NewTagRepository(db)
|
||||
s.CategoryRepo = sql.NewCategoryRepository(db)
|
||||
}
|
||||
|
||||
// setupMockRepositories sets up mock repositories for testing
|
||||
@ -181,16 +185,17 @@ func (s *IntegrationTestSuite) setupMockRepositories() {
|
||||
|
||||
// setupServices sets up service instances
|
||||
func (s *IntegrationTestSuite) setupServices() {
|
||||
s.WorkService = services.NewWorkService(s.WorkRepo, nil)
|
||||
// Temporarily comment out services that depend on problematic repositories
|
||||
// s.Localization = services.NewLocalizationService(s.TranslationRepo)
|
||||
// s.AuthService = services.NewAuthService(s.UserRepo, "test-secret-key")
|
||||
mockAnalyzer := &MockAnalyzer{}
|
||||
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")
|
||||
}
|
||||
|
||||
// setupTestData creates initial test data
|
||||
func (s *IntegrationTestSuite) setupTestData() {
|
||||
// Create test users
|
||||
s.TestUsers = []*models.User{
|
||||
s.TestUsers = []*domain.User{
|
||||
{Username: "testuser1", Email: "test1@example.com", FirstName: "Test", LastName: "User1"},
|
||||
{Username: "testuser2", Email: "test2@example.com", FirstName: "Test", LastName: "User2"},
|
||||
}
|
||||
@ -202,7 +207,7 @@ func (s *IntegrationTestSuite) setupTestData() {
|
||||
}
|
||||
|
||||
// Create test authors
|
||||
s.TestAuthors = []*models.Author{
|
||||
s.TestAuthors = []*domain.Author{
|
||||
{Name: "Test Author 1", Language: "en"},
|
||||
{Name: "Test Author 2", Language: "fr"},
|
||||
}
|
||||
@ -214,7 +219,7 @@ func (s *IntegrationTestSuite) setupTestData() {
|
||||
}
|
||||
|
||||
// Create test works
|
||||
s.TestWorks = []*models.Work{
|
||||
s.TestWorks = []*domain.Work{
|
||||
{Title: "Test Work 1", Language: "en"},
|
||||
{Title: "Test Work 2", Language: "en"},
|
||||
{Title: "Test Work 3", Language: "fr"},
|
||||
@ -227,7 +232,7 @@ func (s *IntegrationTestSuite) setupTestData() {
|
||||
}
|
||||
|
||||
// Create test translations
|
||||
s.TestTranslations = []*models.Translation{
|
||||
s.TestTranslations = []*domain.Translation{
|
||||
{
|
||||
Title: "Test Work 1",
|
||||
Content: "Test content for work 1",
|
||||
@ -292,35 +297,23 @@ func (s *IntegrationTestSuite) SetupTest() {
|
||||
// GetResolver returns a properly configured GraphQL resolver for testing
|
||||
func (s *IntegrationTestSuite) GetResolver() *graph.Resolver {
|
||||
return &graph.Resolver{
|
||||
WorkRepo: s.WorkRepo,
|
||||
UserRepo: s.UserRepo,
|
||||
AuthorRepo: s.AuthorRepo,
|
||||
TranslationRepo: s.TranslationRepo,
|
||||
CommentRepo: s.CommentRepo,
|
||||
LikeRepo: s.LikeRepo,
|
||||
BookmarkRepo: s.BookmarkRepo,
|
||||
CollectionRepo: s.CollectionRepo,
|
||||
TagRepo: s.TagRepo,
|
||||
CategoryRepo: s.CategoryRepo,
|
||||
WorkService: s.WorkService,
|
||||
Localization: s.Localization,
|
||||
AuthService: s.AuthService,
|
||||
// This needs to be updated to reflect the new resolver structure
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTestWork creates a test work with optional content
|
||||
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *models.Work {
|
||||
work := &models.Work{
|
||||
Title: title,
|
||||
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
||||
work := &domain.Work{
|
||||
Title: title,
|
||||
Language: language,
|
||||
}
|
||||
work.Language = language
|
||||
|
||||
if err := s.WorkRepo.Create(context.Background(), work); err != nil {
|
||||
s.T().Fatalf("Failed to create test work: %v", err)
|
||||
}
|
||||
|
||||
if content != "" {
|
||||
translation := &models.Translation{
|
||||
translation := &domain.Translation{
|
||||
Title: title,
|
||||
Content: content,
|
||||
Language: language,
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tercul/internal/repositories"
|
||||
"tercul/internal/domain"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -40,17 +40,17 @@ func (m *MockBaseRepository[T]) DeleteInTx(ctx context.Context, tx *gorm.DB, id
|
||||
}
|
||||
|
||||
// GetByIDWithOptions retrieves an entity by its ID with query options (mock implementation)
|
||||
func (m *MockBaseRepository[T]) GetByIDWithOptions(ctx context.Context, id uint, options *repositories.QueryOptions) (*T, error) {
|
||||
func (m *MockBaseRepository[T]) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*T, error) {
|
||||
return nil, fmt.Errorf("GetByIDWithOptions not implemented in mock repository")
|
||||
}
|
||||
|
||||
// ListWithOptions returns entities with query options (mock implementation)
|
||||
func (m *MockBaseRepository[T]) ListWithOptions(ctx context.Context, options *repositories.QueryOptions) ([]T, error) {
|
||||
func (m *MockBaseRepository[T]) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]T, error) {
|
||||
return nil, fmt.Errorf("ListWithOptions not implemented in mock repository")
|
||||
}
|
||||
|
||||
// CountWithOptions returns the count with query options (mock implementation)
|
||||
func (m *MockBaseRepository[T]) CountWithOptions(ctx context.Context, options *repositories.QueryOptions) (int64, error) {
|
||||
func (m *MockBaseRepository[T]) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||
return 0, fmt.Errorf("CountWithOptions not implemented in mock repository")
|
||||
}
|
||||
|
||||
|
||||
@ -4,23 +4,22 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
models2 "tercul/internal/models"
|
||||
repositories2 "tercul/internal/repositories"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// MockTranslationRepository is an in-memory implementation of TranslationRepository
|
||||
type MockTranslationRepository struct {
|
||||
items []models2.Translation
|
||||
items []domain.Translation
|
||||
}
|
||||
|
||||
func NewMockTranslationRepository() *MockTranslationRepository {
|
||||
return &MockTranslationRepository{items: []models2.Translation{}}
|
||||
return &MockTranslationRepository{items: []domain.Translation{}}
|
||||
}
|
||||
|
||||
var _ repositories2.TranslationRepository = (*MockTranslationRepository)(nil)
|
||||
var _ domain.TranslationRepository = (*MockTranslationRepository)(nil)
|
||||
|
||||
// BaseRepository methods with context support
|
||||
func (m *MockTranslationRepository) Create(ctx context.Context, t *models2.Translation) error {
|
||||
func (m *MockTranslationRepository) Create(ctx context.Context, t *domain.Translation) error {
|
||||
if t == nil {
|
||||
return errors.New("nil translation")
|
||||
}
|
||||
@ -29,24 +28,24 @@ func (m *MockTranslationRepository) Create(ctx context.Context, t *models2.Trans
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) GetByID(ctx context.Context, id uint) (*models2.Translation, error) {
|
||||
func (m *MockTranslationRepository) GetByID(ctx context.Context, id uint) (*domain.Translation, error) {
|
||||
for i := range m.items {
|
||||
if m.items[i].ID == id {
|
||||
cp := m.items[i]
|
||||
return &cp, nil
|
||||
}
|
||||
}
|
||||
return nil, repositories2.ErrEntityNotFound
|
||||
return nil, domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) Update(ctx context.Context, t *models2.Translation) error {
|
||||
func (m *MockTranslationRepository) Update(ctx context.Context, t *domain.Translation) error {
|
||||
for i := range m.items {
|
||||
if m.items[i].ID == t.ID {
|
||||
m.items[i] = *t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return repositories2.ErrEntityNotFound
|
||||
return domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error {
|
||||
@ -56,57 +55,57 @@ func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return repositories2.ErrEntityNotFound
|
||||
return domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) List(ctx context.Context, page, pageSize int) (*repositories2.PaginatedResult[models2.Translation], error) {
|
||||
all := append([]models2.Translation(nil), m.items...)
|
||||
func (m *MockTranslationRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) {
|
||||
all := append([]domain.Translation(nil), m.items...)
|
||||
total := int64(len(all))
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start > len(all) {
|
||||
return &repositories2.PaginatedResult[models2.Translation]{Items: []models2.Translation{}, TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Translation]{Items: []domain.Translation{}, TotalCount: total}, nil
|
||||
}
|
||||
if end > len(all) {
|
||||
end = len(all)
|
||||
}
|
||||
return &repositories2.PaginatedResult[models2.Translation]{Items: all[start:end], TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Translation]{Items: all[start:end], TotalCount: total}, nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) ListAll(ctx context.Context) ([]models2.Translation, error) {
|
||||
return append([]models2.Translation(nil), m.items...), nil
|
||||
func (m *MockTranslationRepository) ListAll(ctx context.Context) ([]domain.Translation, error) {
|
||||
return append([]domain.Translation(nil), m.items...), nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) Count(ctx context.Context) (int64, error) {
|
||||
return int64(len(m.items)), nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*models2.Translation, error) {
|
||||
func (m *MockTranslationRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Translation, error) {
|
||||
return m.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]models2.Translation, error) {
|
||||
all := append([]models2.Translation(nil), m.items...)
|
||||
func (m *MockTranslationRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Translation, error) {
|
||||
all := append([]domain.Translation(nil), m.items...)
|
||||
end := offset + batchSize
|
||||
if end > len(all) {
|
||||
end = len(all)
|
||||
}
|
||||
if offset > len(all) {
|
||||
return []models2.Translation{}, nil
|
||||
return []domain.Translation{}, nil
|
||||
}
|
||||
return all[offset:end], nil
|
||||
}
|
||||
|
||||
// New BaseRepository methods
|
||||
func (m *MockTranslationRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *models2.Translation) error {
|
||||
func (m *MockTranslationRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Translation) error {
|
||||
return m.Create(ctx, entity)
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) GetByIDWithOptions(ctx context.Context, id uint, options *repositories2.QueryOptions) (*models2.Translation, error) {
|
||||
func (m *MockTranslationRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Translation, error) {
|
||||
return m.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *models2.Translation) error {
|
||||
func (m *MockTranslationRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Translation) error {
|
||||
return m.Update(ctx, entity)
|
||||
}
|
||||
|
||||
@ -114,7 +113,7 @@ func (m *MockTranslationRepository) DeleteInTx(ctx context.Context, tx *gorm.DB,
|
||||
return m.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) ListWithOptions(ctx context.Context, options *repositories2.QueryOptions) ([]models2.Translation, error) {
|
||||
func (m *MockTranslationRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Translation, error) {
|
||||
result, err := m.List(ctx, 1, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -122,7 +121,7 @@ func (m *MockTranslationRepository) ListWithOptions(ctx context.Context, options
|
||||
return result.Items, nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) CountWithOptions(ctx context.Context, options *repositories2.QueryOptions) (int64, error) {
|
||||
func (m *MockTranslationRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||
return m.Count(ctx)
|
||||
}
|
||||
|
||||
@ -140,12 +139,12 @@ func (m *MockTranslationRepository) WithTx(ctx context.Context, fn func(tx *gorm
|
||||
}
|
||||
|
||||
// TranslationRepository specific methods
|
||||
func (m *MockTranslationRepository) ListByWorkID(ctx context.Context, workID uint) ([]models2.Translation, error) {
|
||||
func (m *MockTranslationRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error) {
|
||||
return m.ListByEntity(ctx, "Work", workID)
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) ListByEntity(ctx context.Context, entityType string, entityID uint) ([]models2.Translation, error) {
|
||||
var out []models2.Translation
|
||||
func (m *MockTranslationRepository) ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error) {
|
||||
var out []domain.Translation
|
||||
for i := range m.items {
|
||||
tr := m.items[i]
|
||||
if tr.TranslatableType == entityType && tr.TranslatableID == entityID {
|
||||
@ -155,8 +154,8 @@ func (m *MockTranslationRepository) ListByEntity(ctx context.Context, entityType
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) ListByTranslatorID(ctx context.Context, translatorID uint) ([]models2.Translation, error) {
|
||||
var out []models2.Translation
|
||||
func (m *MockTranslationRepository) ListByTranslatorID(ctx context.Context, translatorID uint) ([]domain.Translation, error) {
|
||||
var out []domain.Translation
|
||||
for i := range m.items {
|
||||
if m.items[i].TranslatorID != nil && *m.items[i].TranslatorID == translatorID {
|
||||
out = append(out, m.items[i])
|
||||
@ -165,8 +164,8 @@ func (m *MockTranslationRepository) ListByTranslatorID(ctx context.Context, tran
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *MockTranslationRepository) ListByStatus(ctx context.Context, status models2.TranslationStatus) ([]models2.Translation, error) {
|
||||
var out []models2.Translation
|
||||
func (m *MockTranslationRepository) ListByStatus(ctx context.Context, status domain.TranslationStatus) ([]domain.Translation, error) {
|
||||
var out []domain.Translation
|
||||
for i := range m.items {
|
||||
if m.items[i].Status == status {
|
||||
out = append(out, m.items[i])
|
||||
@ -177,12 +176,12 @@ func (m *MockTranslationRepository) ListByStatus(ctx context.Context, status mod
|
||||
|
||||
// Test helper: add a translation for a Work
|
||||
func (m *MockTranslationRepository) AddTranslationForWork(workID uint, language string, content string, isOriginal bool) {
|
||||
m.Create(context.Background(), &models2.Translation{
|
||||
m.Create(context.Background(), &domain.Translation{
|
||||
Title: "",
|
||||
Content: content,
|
||||
Description: "",
|
||||
Language: language,
|
||||
Status: models2.TranslationStatusPublished,
|
||||
Status: domain.TranslationStatusPublished,
|
||||
TranslatableID: workID,
|
||||
TranslatableType: "Work",
|
||||
IsOriginalLanguage: isOriginal,
|
||||
|
||||
@ -3,22 +3,21 @@ package testutil
|
||||
import (
|
||||
"context"
|
||||
"gorm.io/gorm"
|
||||
"tercul/internal/models"
|
||||
"tercul/internal/repositories"
|
||||
"tercul/internal/domain"
|
||||
)
|
||||
|
||||
// UnifiedMockWorkRepository is a shared mock for WorkRepository tests
|
||||
// Implements all required methods and uses an in-memory slice
|
||||
|
||||
type UnifiedMockWorkRepository struct {
|
||||
Works []*models.Work
|
||||
Works []*domain.Work
|
||||
}
|
||||
|
||||
func NewUnifiedMockWorkRepository() *UnifiedMockWorkRepository {
|
||||
return &UnifiedMockWorkRepository{Works: []*models.Work{}}
|
||||
return &UnifiedMockWorkRepository{Works: []*domain.Work{}}
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) AddWork(work *models.Work) {
|
||||
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
|
||||
@ -27,28 +26,28 @@ func (m *UnifiedMockWorkRepository) AddWork(work *models.Work) {
|
||||
}
|
||||
|
||||
// BaseRepository methods with context support
|
||||
func (m *UnifiedMockWorkRepository) Create(ctx context.Context, entity *models.Work) error {
|
||||
func (m *UnifiedMockWorkRepository) Create(ctx context.Context, entity *domain.Work) error {
|
||||
m.AddWork(entity)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) GetByID(ctx context.Context, id uint) (*models.Work, error) {
|
||||
func (m *UnifiedMockWorkRepository) GetByID(ctx context.Context, id uint) (*domain.Work, error) {
|
||||
for _, w := range m.Works {
|
||||
if w.ID == id {
|
||||
return w, nil
|
||||
}
|
||||
}
|
||||
return nil, repositories.ErrEntityNotFound
|
||||
return nil, domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *models.Work) error {
|
||||
func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *domain.Work) error {
|
||||
for i, w := range m.Works {
|
||||
if w.ID == entity.ID {
|
||||
m.Works[i] = entity
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return repositories.ErrEntityNotFound
|
||||
return domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error {
|
||||
@ -58,11 +57,11 @@ func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return repositories.ErrEntityNotFound
|
||||
return domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) List(ctx context.Context, page, pageSize int) (*repositories.PaginatedResult[models.Work], error) {
|
||||
var all []models.Work
|
||||
func (m *UnifiedMockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
||||
var all []domain.Work
|
||||
for _, w := range m.Works {
|
||||
if w != nil {
|
||||
all = append(all, *w)
|
||||
@ -72,16 +71,16 @@ func (m *UnifiedMockWorkRepository) List(ctx context.Context, page, pageSize int
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start > len(all) {
|
||||
return &repositories.PaginatedResult[models.Work]{Items: []models.Work{}, TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Work]{Items: []domain.Work{}, TotalCount: total}, nil
|
||||
}
|
||||
if end > len(all) {
|
||||
end = len(all)
|
||||
}
|
||||
return &repositories.PaginatedResult[models.Work]{Items: all[start:end], TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Work]{Items: all[start:end], TotalCount: total}, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) ListAll(ctx context.Context) ([]models.Work, error) {
|
||||
var all []models.Work
|
||||
func (m *UnifiedMockWorkRepository) ListAll(ctx context.Context) ([]domain.Work, error) {
|
||||
var all []domain.Work
|
||||
for _, w := range m.Works {
|
||||
if w != nil {
|
||||
all = append(all, *w)
|
||||
@ -94,17 +93,17 @@ func (m *UnifiedMockWorkRepository) Count(ctx context.Context) (int64, error) {
|
||||
return int64(len(m.Works)), nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*models.Work, error) {
|
||||
func (m *UnifiedMockWorkRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Work, error) {
|
||||
for _, w := range m.Works {
|
||||
if w.ID == id {
|
||||
return w, nil
|
||||
}
|
||||
}
|
||||
return nil, repositories.ErrEntityNotFound
|
||||
return nil, domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]models.Work, error) {
|
||||
var result []models.Work
|
||||
func (m *UnifiedMockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Work, error) {
|
||||
var result []domain.Work
|
||||
end := offset + batchSize
|
||||
if end > len(m.Works) {
|
||||
end = len(m.Works)
|
||||
@ -118,15 +117,15 @@ func (m *UnifiedMockWorkRepository) GetAllForSync(ctx context.Context, batchSize
|
||||
}
|
||||
|
||||
// New BaseRepository methods
|
||||
func (m *UnifiedMockWorkRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *models.Work) error {
|
||||
func (m *UnifiedMockWorkRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Work) error {
|
||||
return m.Create(ctx, entity)
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *repositories.QueryOptions) (*models.Work, error) {
|
||||
func (m *UnifiedMockWorkRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Work, error) {
|
||||
return m.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *models.Work) error {
|
||||
func (m *UnifiedMockWorkRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Work) error {
|
||||
return m.Update(ctx, entity)
|
||||
}
|
||||
|
||||
@ -134,7 +133,7 @@ func (m *UnifiedMockWorkRepository) DeleteInTx(ctx context.Context, tx *gorm.DB,
|
||||
return m.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) ListWithOptions(ctx context.Context, options *repositories.QueryOptions) ([]models.Work, error) {
|
||||
func (m *UnifiedMockWorkRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Work, error) {
|
||||
result, err := m.List(ctx, 1, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -142,7 +141,7 @@ func (m *UnifiedMockWorkRepository) ListWithOptions(ctx context.Context, options
|
||||
return result.Items, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) CountWithOptions(ctx context.Context, options *repositories.QueryOptions) (int64, error) {
|
||||
func (m *UnifiedMockWorkRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||
return m.Count(ctx)
|
||||
}
|
||||
|
||||
@ -160,8 +159,8 @@ func (m *UnifiedMockWorkRepository) WithTx(ctx context.Context, fn func(tx *gorm
|
||||
}
|
||||
|
||||
// WorkRepository specific methods
|
||||
func (m *UnifiedMockWorkRepository) FindByTitle(ctx context.Context, title string) ([]models.Work, error) {
|
||||
var result []models.Work
|
||||
func (m *UnifiedMockWorkRepository) FindByTitle(ctx context.Context, title string) ([]domain.Work, error) {
|
||||
var result []domain.Work
|
||||
for _, w := range m.Works {
|
||||
if len(title) == 0 || (len(w.Title) >= len(title) && w.Title[:len(title)] == title) {
|
||||
result = append(result, *w)
|
||||
@ -170,8 +169,8 @@ func (m *UnifiedMockWorkRepository) FindByTitle(ctx context.Context, title strin
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*repositories.PaginatedResult[models.Work], error) {
|
||||
var filtered []models.Work
|
||||
func (m *UnifiedMockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
||||
var filtered []domain.Work
|
||||
for _, w := range m.Works {
|
||||
if w.Language == language {
|
||||
filtered = append(filtered, *w)
|
||||
@ -181,16 +180,16 @@ func (m *UnifiedMockWorkRepository) FindByLanguage(ctx context.Context, language
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start > len(filtered) {
|
||||
return &repositories.PaginatedResult[models.Work]{Items: []models.Work{}, TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Work]{Items: []domain.Work{}, TotalCount: total}, nil
|
||||
}
|
||||
if end > len(filtered) {
|
||||
end = len(filtered)
|
||||
}
|
||||
return &repositories.PaginatedResult[models.Work]{Items: filtered[start:end], TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Work]{Items: filtered[start:end], TotalCount: total}, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]models.Work, error) {
|
||||
result := make([]models.Work, len(m.Works))
|
||||
func (m *UnifiedMockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error) {
|
||||
result := make([]domain.Work, len(m.Works))
|
||||
for i, w := range m.Works {
|
||||
if w != nil {
|
||||
result[i] = *w
|
||||
@ -199,8 +198,8 @@ func (m *UnifiedMockWorkRepository) FindByAuthor(ctx context.Context, authorID u
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]models.Work, error) {
|
||||
result := make([]models.Work, len(m.Works))
|
||||
func (m *UnifiedMockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error) {
|
||||
result := make([]domain.Work, len(m.Works))
|
||||
for i, w := range m.Works {
|
||||
if w != nil {
|
||||
result[i] = *w
|
||||
@ -209,17 +208,17 @@ func (m *UnifiedMockWorkRepository) FindByCategory(ctx context.Context, category
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*models.Work, error) {
|
||||
func (m *UnifiedMockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error) {
|
||||
for _, w := range m.Works {
|
||||
if w.ID == id {
|
||||
return w, nil
|
||||
}
|
||||
}
|
||||
return nil, repositories.ErrEntityNotFound
|
||||
return nil, domain.ErrEntityNotFound
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*repositories.PaginatedResult[models.Work], error) {
|
||||
var all []models.Work
|
||||
func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
|
||||
var all []domain.Work
|
||||
for _, w := range m.Works {
|
||||
if w != nil {
|
||||
all = append(all, *w)
|
||||
@ -229,16 +228,16 @@ func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, pa
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start > len(all) {
|
||||
return &repositories.PaginatedResult[models.Work]{Items: []models.Work{}, TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Work]{Items: []domain.Work{}, TotalCount: total}, nil
|
||||
}
|
||||
if end > len(all) {
|
||||
end = len(all)
|
||||
}
|
||||
return &repositories.PaginatedResult[models.Work]{Items: all[start:end], TotalCount: total}, nil
|
||||
return &domain.PaginatedResult[domain.Work]{Items: all[start:end], TotalCount: total}, nil
|
||||
}
|
||||
|
||||
func (m *UnifiedMockWorkRepository) Reset() {
|
||||
m.Works = []*models.Work{}
|
||||
m.Works = []*domain.Work{}
|
||||
}
|
||||
|
||||
// Add helper to get GraphQL-style Work with Name mapped from Title
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"tercul/graph"
|
||||
"tercul/internal/models"
|
||||
"tercul/services"
|
||||
"context"
|
||||
graph "tercul/internal/adapters/graphql"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/domain"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
@ -11,14 +12,26 @@ import (
|
||||
// SimpleTestSuite provides a minimal test environment with just the essentials
|
||||
type SimpleTestSuite struct {
|
||||
suite.Suite
|
||||
WorkRepo *UnifiedMockWorkRepository
|
||||
WorkService services.WorkService
|
||||
WorkRepo *UnifiedMockWorkRepository
|
||||
WorkCommands *work.WorkCommands
|
||||
WorkQueries *work.WorkQueries
|
||||
MockAnalyzer *MockAnalyzer
|
||||
}
|
||||
|
||||
// MockAnalyzer is a mock implementation of the analyzer interface.
|
||||
type MockAnalyzer struct{}
|
||||
|
||||
// AnalyzeWork is the mock implementation of the AnalyzeWork method.
|
||||
func (m *MockAnalyzer) AnalyzeWork(ctx context.Context, workID uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupSuite sets up the test suite
|
||||
func (s *SimpleTestSuite) SetupSuite() {
|
||||
s.WorkRepo = NewUnifiedMockWorkRepository()
|
||||
s.WorkService = services.NewWorkService(s.WorkRepo, nil)
|
||||
s.MockAnalyzer = &MockAnalyzer{}
|
||||
s.WorkCommands = work.NewWorkCommands(s.WorkRepo, s.MockAnalyzer)
|
||||
s.WorkQueries = work.NewWorkQueries(s.WorkRepo)
|
||||
}
|
||||
|
||||
// SetupTest resets test data for each test
|
||||
@ -28,19 +41,20 @@ 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,
|
||||
WorkService: s.WorkService,
|
||||
// Other fields will be nil, but that's okay for basic tests
|
||||
// WorkRepo: s.WorkRepo, // This should be removed from resolver
|
||||
// WorkService: s.WorkService, // This is replaced by commands/queries
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTestWork creates a test work with optional content
|
||||
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *models.Work {
|
||||
work := &models.Work{
|
||||
Title: title,
|
||||
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
|
||||
work := &domain.Work{
|
||||
Title: title,
|
||||
Language: language,
|
||||
}
|
||||
work.Language = language
|
||||
|
||||
// Add work to the mock repository
|
||||
s.WorkRepo.AddWork(work)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user