mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-26 22:21:33 +00:00
feat: Refactor GORM relations and implement mutations
This commit includes a major refactoring of the GORM many-to-many relationships to use explicit join tables, improving stability and compatibility with GORM's features. It also implements a large number of previously unimplemented GraphQL mutations for core entities like Collections, Comments, Likes, and Bookmarks. Key changes: - Refactored polymorphic many-to-many relationships for Copyright and Monetization to use standard many-to-many with explicit join tables. - Implemented GraphQL mutations for Collection, Comment, Like, and Bookmark entities, including input validation and authorization checks. - Added comprehensive integration tests for all new features and refactored code. - Refactored the GraphQL integration test suite to be type-safe, using generics for response handling as requested. - Updated repository interfaces and implementations to support the new data model. - Updated the TODO.md file to reflect the completed work.
This commit is contained in:
parent
d536c3acb5
commit
0395df3ff0
26
TODO.md
26
TODO.md
@ -8,7 +8,7 @@
|
||||
|
||||
## [ ] Security Enhancements
|
||||
|
||||
- [ ] Add comprehensive input validation for all GraphQL mutations (High, 2d)
|
||||
- [x] Add comprehensive input validation for all GraphQL mutations (High, 2d) - *Partially complete. Core mutations are validated.*
|
||||
|
||||
## [ ] Code Quality & Architecture
|
||||
|
||||
@ -17,29 +17,29 @@
|
||||
|
||||
## [ ] Architecture Refactor (DDD-lite)
|
||||
|
||||
- [ ] Create skeleton packages: `cmd/`, `internal/platform/`, `internal/domain/`, `internal/app/`, `internal/data/`, `internal/adapters/graphql/`, `internal/jobs/`
|
||||
- [x] Create skeleton packages: `cmd/`, `internal/platform/`, `internal/domain/`, `internal/app/`, `internal/data/`, `internal/adapters/graphql/`, `internal/jobs/`
|
||||
- [x] Move infra to `internal/platform/*` (`config`, `db`, `cache`, `auth`, `http`, `log`, `search`)
|
||||
- [ ] Wire DI in `cmd/api/main.go` and expose an `Application` facade to adapters
|
||||
- [ ] Unify GraphQL under `internal/adapters/graphql` and update `gqlgen.yml`; move `schema.graphqls` and resolvers
|
||||
- [x] Wire DI in `cmd/api/main.go` and expose an `Application` facade to adapters
|
||||
- [x] Unify GraphQL under `internal/adapters/graphql` and update `gqlgen.yml`; move `schema.graphqls` and resolvers
|
||||
- [ ] Resolvers call application services only; add dataloaders per aggregate
|
||||
- [ ] Introduce Unit-of-Work: `platform/db.WithTx(ctx, func(ctx) error)` and repo factory for `*sql.DB` / `*sql.Tx`
|
||||
- [ ] Split write vs read paths for `work` (commands.go, queries.go); make read models cacheable
|
||||
- [x] Introduce Unit-of-Work: `platform/db.WithTx(ctx, func(ctx) error)` and repo factory for `*sql.DB` / `*sql.Tx`
|
||||
- [x] Split write vs read paths for `work` (commands.go, queries.go); make read models cacheable
|
||||
- [ ] Replace bespoke cached repositories with decorators in `internal/data/cache` (reads only; deterministic invalidation)
|
||||
- [ ] Restructure `models/*` into domain aggregates with constructors and invariants
|
||||
- [x] Restructure `models/*` into domain aggregates with constructors and invariants
|
||||
- [ ] Adopt migrations tool (goose/atlas/migrate); move SQL to `internal/data/migrations`; delete `migrations.go`
|
||||
- [ ] Observability: centralize logging; add Prometheus metrics and OpenTelemetry tracing; request IDs
|
||||
- [ ] Config: replace ad-hoc config with env parsing + validation (e.g., koanf/envconfig); no globals
|
||||
- [ ] Security: move JWT/middleware to `internal/platform/auth`; add authz policy helpers (e.g., `CanEditWork`)
|
||||
- [ ] Search: move Weaviate client/schema to `internal/platform/search`, optional domain interface
|
||||
- [ ] Background jobs: move to `cmd/worker` and `internal/jobs/*`; ensure idempotency and lease
|
||||
- [ ] Python ops: move scripts to `/ops/migration` and `/ops/analysis`; keep outputs under `/ops/migration/outputs/`
|
||||
- [ ] Cleanup: delete dead packages (`store`, duplicate `repositories`); consolidate to `internal/data/sql`
|
||||
- [x] Security: move JWT/middleware to `internal/platform/auth`; add authz policy helpers (e.g., `CanEditWork`)
|
||||
- [x] Search: move Weaviate client/schema to `internal/platform/search`, optional domain interface
|
||||
- [x] Background jobs: move to `cmd/worker` and `internal/jobs/*`; ensure idempotency and lease
|
||||
- [x] Python ops: move scripts to `/ops/migration` and `/ops/analysis`; keep outputs under `/ops/migration/outputs/`
|
||||
- [x] Cleanup: delete dead packages (`store`, duplicate `repositories`); consolidate to `internal/data/sql`
|
||||
- [ ] CI: add `make lint test test-integration` and integration tests with Docker compose
|
||||
|
||||
## [ ] Testing
|
||||
|
||||
- [ ] Add unit tests for all models, repositories, and services (High, 3d)
|
||||
- [ ] Add integration tests for GraphQL API and background jobs (High, 3d)
|
||||
- [x] Add integration tests for GraphQL API and background jobs (High, 3d) - *Partially complete. Core mutations are tested.*
|
||||
- [ ] Add performance benchmarks for critical paths (Medium, 2d)
|
||||
- [ ] Add benchmarks for text analysis (sequential vs concurrent) and cache hit/miss rates
|
||||
|
||||
|
||||
51
go.mod
51
go.mod
@ -1,26 +1,24 @@
|
||||
module tercul
|
||||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.2
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.17.72
|
||||
github.com/99designs/gqlgen v0.17.78
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/hibiken/asynq v0.25.1
|
||||
github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc
|
||||
github.com/pemistahl/lingua-go v1.4.0
|
||||
github.com/redis/go-redis/v9 v9.8.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/vektah/gqlparser/v2 v2.5.26
|
||||
github.com/weaviate/weaviate v1.30.2
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.1.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
github.com/redis/go-redis/v9 v9.13.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/vektah/gqlparser/v2 v2.5.30
|
||||
github.com/weaviate/weaviate v1.32.6
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.4.1
|
||||
golang.org/x/crypto v0.41.0
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
gorm.io/gorm v1.30.0
|
||||
gorm.io/gorm v1.30.3
|
||||
)
|
||||
|
||||
require (
|
||||
@ -39,12 +37,12 @@ require (
|
||||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.4 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
@ -60,23 +58,22 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sosodev/duration v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.6 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.7 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
gonum.org/v1/gonum v0.15.1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
|
||||
google.golang.org/grpc v1.69.4 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
116
go.sum
116
go.sum
@ -1,5 +1,5 @@
|
||||
github.com/99designs/gqlgen v0.17.72 h1:2JDAuutIYtAN26BAtigfLZFnTN53fpYbIENL8bVgAKY=
|
||||
github.com/99designs/gqlgen v0.17.72/go.mod h1:BoL4C3j9W2f95JeWMrSArdDNGWmZB9MOS2EMHJDZmUc=
|
||||
github.com/99designs/gqlgen v0.17.78 h1:bhIi7ynrc3js2O8wu1sMQj1YHPENDt3jQGyifoBvoVI=
|
||||
github.com/99designs/gqlgen v0.17.78/go.mod h1:yI/o31IauG2kX0IsskM4R894OCCG1jXJORhtLQqB7Oc=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
@ -81,8 +81,8 @@ github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3Bum
|
||||
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
|
||||
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
|
||||
@ -114,8 +114,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@ -130,8 +130,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
@ -186,8 +186,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI=
|
||||
github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/redis/go-redis/v9 v9.13.0 h1:PpmlVykE0ODh8P43U0HqC+2NXHXwG+GUtQyz+MPKGRg=
|
||||
github.com/redis/go-redis/v9 v9.13.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@ -206,8 +206,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
|
||||
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -218,17 +218,17 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
|
||||
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/juiTobN4=
|
||||
github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
||||
github.com/weaviate/weaviate v1.30.2 h1:zJjhXR4EwCK3v8bO3OgQCIAoQRbFJM3C6imR33rM3i8=
|
||||
github.com/weaviate/weaviate v1.30.2/go.mod h1:FQJsD9pckNolW1C+S+P88okIX6DEOLJwf7aqFvgYgSQ=
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.1.0 h1:3wSf4fktKLvspPHwDYnn07u0sKfDAhrA5JeRe+R4ENg=
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.1.0/go.mod h1:gg5qyiHk53+HMZW2ynkrgm+cMQDD2Ewyma84rBeChz4=
|
||||
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
|
||||
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
|
||||
github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE=
|
||||
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
||||
github.com/weaviate/weaviate v1.32.6 h1:N0MRjuqZT9l2un4xFeV4fXZ9dkLbqrijC5JIfr759Os=
|
||||
github.com/weaviate/weaviate v1.32.6/go.mod h1:hzzhAOYxgKe+B2jxZJtaWMIdElcXXn+RQyQ7ccQORNg=
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.4.1 h1:hfKocGPe11IUr4XsLp3q9hJYck0I2yIHGlFBpLqb/F4=
|
||||
github.com/weaviate/weaviate-go-client/v5 v5.4.1/go.mod h1:l72EnmCLj9LCQkR8S7nN7Y1VqGMmL3Um8exhFkMmfwk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||
@ -242,16 +242,16 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@ -259,30 +259,30 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
|
||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -295,8 +295,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -304,10 +304,10 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -315,8 +315,8 @@ golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
@ -324,10 +324,10 @@ gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
|
||||
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
|
||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -346,10 +346,10 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
gorm.io/gorm v1.30.3 h1:QiG8upl0Sg9ba2Zatfjy0fy4It2iNBL2/eMdvEkdXNs=
|
||||
gorm.io/gorm v1.30.3/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@ -116,7 +116,7 @@ call_argument_directives_with_null: true
|
||||
|
||||
# gqlgen will search for any type names in the schema in these go packages
|
||||
# if they match it will use them, otherwise it will generate them.
|
||||
autobind:
|
||||
# autobind:
|
||||
# - "tercul/internal/adapters/graphql/model"
|
||||
|
||||
# This section declares type mapping between the GraphQL and go type systems
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -45,8 +45,8 @@ type Author struct {
|
||||
}
|
||||
|
||||
type AuthorInput struct {
|
||||
Name string `json:"name"`
|
||||
Language string `json:"language"`
|
||||
Name string `json:"name" valid:"required,length(3|255)"`
|
||||
Language string `json:"language" valid:"required,length(2|2)"`
|
||||
Biography *string `json:"biography,omitempty"`
|
||||
BirthDate *string `json:"birthDate,omitempty"`
|
||||
DeathDate *string `json:"deathDate,omitempty"`
|
||||
@ -87,7 +87,7 @@ type Bookmark struct {
|
||||
|
||||
type BookmarkInput struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
WorkID string `json:"workId"`
|
||||
WorkID string `json:"workId" valid:"required"`
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
@ -121,7 +121,7 @@ type Collection struct {
|
||||
}
|
||||
|
||||
type CollectionInput struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name" valid:"required,length(3|255)"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
WorkIds []string `json:"workIds,omitempty"`
|
||||
}
|
||||
@ -149,7 +149,7 @@ type Comment struct {
|
||||
}
|
||||
|
||||
type CommentInput struct {
|
||||
Text string `json:"text"`
|
||||
Text string `json:"text" valid:"required,length(1|4096)"`
|
||||
WorkID *string `json:"workId,omitempty"`
|
||||
TranslationID *string `json:"translationId,omitempty"`
|
||||
LineNumber *int32 `json:"lineNumber,omitempty"`
|
||||
@ -268,6 +268,11 @@ type LinguisticLayer struct {
|
||||
Works []*Work `json:"works,omitempty"`
|
||||
}
|
||||
|
||||
type LoginInput struct {
|
||||
Email string `json:"email" valid:"required,email"`
|
||||
Password string `json:"password" valid:"required,length(6|255)"`
|
||||
}
|
||||
|
||||
type Mood struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@ -313,11 +318,11 @@ type ReadabilityScore struct {
|
||||
}
|
||||
|
||||
type RegisterInput struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Username string `json:"username" valid:"required,alphanum,length(3|50)"`
|
||||
Email string `json:"email" valid:"required,email"`
|
||||
Password string `json:"password" valid:"required,length(6|255)"`
|
||||
FirstName string `json:"firstName" valid:"required,alpha,length(2|50)"`
|
||||
LastName string `json:"lastName" valid:"required,alpha,length(2|50)"`
|
||||
}
|
||||
|
||||
type SearchFilters struct {
|
||||
@ -390,10 +395,10 @@ type Translation struct {
|
||||
}
|
||||
|
||||
type TranslationInput struct {
|
||||
Name string `json:"name"`
|
||||
Language string `json:"language"`
|
||||
Name string `json:"name" valid:"required,length(3|255)"`
|
||||
Language string `json:"language" valid:"required,length(2|2)"`
|
||||
Content *string `json:"content,omitempty"`
|
||||
WorkID string `json:"workId"`
|
||||
WorkID string `json:"workId" valid:"required,uuid"`
|
||||
}
|
||||
|
||||
type TranslationStats struct {
|
||||
@ -511,8 +516,8 @@ type Work struct {
|
||||
}
|
||||
|
||||
type WorkInput struct {
|
||||
Name string `json:"name"`
|
||||
Language string `json:"language"`
|
||||
Name string `json:"name" valid:"required,length(3|255)"`
|
||||
Language string `json:"language" valid:"required,length(2|2)"`
|
||||
Content *string `json:"content,omitempty"`
|
||||
AuthorIds []string `json:"authorIds,omitempty"`
|
||||
TagIds []string `json:"tagIds,omitempty"`
|
||||
|
||||
@ -539,7 +539,7 @@ type SearchResults {
|
||||
type Mutation {
|
||||
# Authentication
|
||||
register(input: RegisterInput!): AuthPayload!
|
||||
login(email: String!, password: String!): AuthPayload!
|
||||
login(input: LoginInput!): AuthPayload!
|
||||
|
||||
# Work mutations
|
||||
createWork(input: WorkInput!): Work!
|
||||
@ -600,6 +600,11 @@ type Mutation {
|
||||
}
|
||||
|
||||
# Input types
|
||||
input LoginInput {
|
||||
email: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
input RegisterInput {
|
||||
username: String!
|
||||
email: String!
|
||||
|
||||
@ -2,7 +2,7 @@ package graphql
|
||||
|
||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.72
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.78
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -12,10 +12,18 @@ import (
|
||||
"tercul/internal/adapters/graphql/model"
|
||||
"tercul/internal/app/auth"
|
||||
"tercul/internal/domain"
|
||||
platform_auth "tercul/internal/platform/auth"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
)
|
||||
|
||||
// Register is the resolver for the register field.
|
||||
func (r *mutationResolver) Register(ctx context.Context, input model.RegisterInput) (*model.AuthPayload, error) {
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Convert GraphQL input to service input
|
||||
registerInput := auth.RegisterInput{
|
||||
Username: input.Username,
|
||||
@ -49,11 +57,16 @@ func (r *mutationResolver) Register(ctx context.Context, input model.RegisterInp
|
||||
}
|
||||
|
||||
// Login is the resolver for the login field.
|
||||
func (r *mutationResolver) Login(ctx context.Context, email string, password string) (*model.AuthPayload, error) {
|
||||
func (r *mutationResolver) Login(ctx context.Context, input model.LoginInput) (*model.AuthPayload, error) {
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Convert GraphQL input to service input
|
||||
loginInput := auth.LoginInput{
|
||||
Email: email,
|
||||
Password: password,
|
||||
Email: input.Email,
|
||||
Password: input.Password,
|
||||
}
|
||||
|
||||
// Call auth service
|
||||
@ -81,6 +94,11 @@ func (r *mutationResolver) Login(ctx context.Context, email string, password str
|
||||
|
||||
// CreateWork is the resolver for the createWork field.
|
||||
func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput) (*model.Work, error) {
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
work := &domain.Work{
|
||||
Title: input.Name,
|
||||
@ -130,42 +148,231 @@ func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput
|
||||
|
||||
// UpdateWork is the resolver for the updateWork field.
|
||||
func (r *mutationResolver) UpdateWork(ctx context.Context, id string, input model.WorkInput) (*model.Work, error) {
|
||||
panic(fmt.Errorf("not implemented: UpdateWork - updateWork"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
workID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
work := &domain.Work{
|
||||
TranslatableModel: domain.TranslatableModel{
|
||||
BaseModel: domain.BaseModel{ID: uint(workID)},
|
||||
Language: input.Language,
|
||||
},
|
||||
Title: input.Name,
|
||||
}
|
||||
|
||||
// Call work service
|
||||
err = r.App.WorkCommands.UpdateWork(ctx, work)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Work{
|
||||
ID: id,
|
||||
Name: work.Title,
|
||||
Language: work.Language,
|
||||
Content: input.Content,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteWork is the resolver for the deleteWork field.
|
||||
func (r *mutationResolver) DeleteWork(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteWork - deleteWork"))
|
||||
workID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
err = r.App.WorkCommands.DeleteWork(ctx, uint(workID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CreateTranslation is the resolver for the createTranslation field.
|
||||
func (r *mutationResolver) CreateTranslation(ctx context.Context, input model.TranslationInput) (*model.Translation, error) {
|
||||
panic(fmt.Errorf("not implemented: CreateTranslation - createTranslation"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
workID, err := strconv.ParseUint(input.WorkID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
translation := &domain.Translation{
|
||||
Title: input.Name,
|
||||
Language: input.Language,
|
||||
TranslatableID: uint(workID),
|
||||
TranslatableType: "Work",
|
||||
}
|
||||
if input.Content != nil {
|
||||
translation.Content = *input.Content
|
||||
}
|
||||
|
||||
// Call translation service
|
||||
err = r.App.TranslationRepo.Create(ctx, translation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Translation{
|
||||
ID: fmt.Sprintf("%d", translation.ID),
|
||||
Name: translation.Title,
|
||||
Language: translation.Language,
|
||||
Content: &translation.Content,
|
||||
WorkID: input.WorkID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateTranslation is the resolver for the updateTranslation field.
|
||||
func (r *mutationResolver) UpdateTranslation(ctx context.Context, id string, input model.TranslationInput) (*model.Translation, error) {
|
||||
panic(fmt.Errorf("not implemented: UpdateTranslation - updateTranslation"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
translationID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid translation ID: %v", err)
|
||||
}
|
||||
|
||||
workID, err := strconv.ParseUint(input.WorkID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
translation := &domain.Translation{
|
||||
BaseModel: domain.BaseModel{ID: uint(translationID)},
|
||||
Title: input.Name,
|
||||
Language: input.Language,
|
||||
TranslatableID: uint(workID),
|
||||
TranslatableType: "Work",
|
||||
}
|
||||
if input.Content != nil {
|
||||
translation.Content = *input.Content
|
||||
}
|
||||
|
||||
// Call translation service
|
||||
err = r.App.TranslationRepo.Update(ctx, translation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Translation{
|
||||
ID: id,
|
||||
Name: translation.Title,
|
||||
Language: translation.Language,
|
||||
Content: &translation.Content,
|
||||
WorkID: input.WorkID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteTranslation is the resolver for the deleteTranslation field.
|
||||
func (r *mutationResolver) DeleteTranslation(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteTranslation - deleteTranslation"))
|
||||
translationID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid translation ID: %v", err)
|
||||
}
|
||||
|
||||
err = r.App.TranslationRepo.Delete(ctx, uint(translationID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CreateAuthor is the resolver for the createAuthor field.
|
||||
func (r *mutationResolver) CreateAuthor(ctx context.Context, input model.AuthorInput) (*model.Author, error) {
|
||||
panic(fmt.Errorf("not implemented: CreateAuthor - createAuthor"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
author := &domain.Author{
|
||||
Name: input.Name,
|
||||
TranslatableModel: domain.TranslatableModel{
|
||||
Language: input.Language,
|
||||
},
|
||||
}
|
||||
|
||||
// Call author service
|
||||
err := r.App.AuthorRepo.Create(ctx, author)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Author{
|
||||
ID: fmt.Sprintf("%d", author.ID),
|
||||
Name: author.Name,
|
||||
Language: author.Language,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateAuthor is the resolver for the updateAuthor field.
|
||||
func (r *mutationResolver) UpdateAuthor(ctx context.Context, id string, input model.AuthorInput) (*model.Author, error) {
|
||||
panic(fmt.Errorf("not implemented: UpdateAuthor - updateAuthor"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
authorID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid author ID: %v", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
author := &domain.Author{
|
||||
TranslatableModel: domain.TranslatableModel{
|
||||
BaseModel: domain.BaseModel{ID: uint(authorID)},
|
||||
Language: input.Language,
|
||||
},
|
||||
Name: input.Name,
|
||||
}
|
||||
|
||||
// Call author service
|
||||
err = r.App.AuthorRepo.Update(ctx, author)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Author{
|
||||
ID: id,
|
||||
Name: author.Name,
|
||||
Language: author.Language,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteAuthor is the resolver for the deleteAuthor field.
|
||||
func (r *mutationResolver) DeleteAuthor(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteAuthor - deleteAuthor"))
|
||||
authorID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid author ID: %v", err)
|
||||
}
|
||||
|
||||
err = r.App.AuthorRepo.Delete(ctx, uint(authorID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// UpdateUser is the resolver for the updateUser field.
|
||||
@ -180,62 +387,566 @@ func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, err
|
||||
|
||||
// CreateCollection is the resolver for the createCollection field.
|
||||
func (r *mutationResolver) CreateCollection(ctx context.Context, input model.CollectionInput) (*model.Collection, error) {
|
||||
panic(fmt.Errorf("not implemented: CreateCollection - createCollection"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
collection := &domain.Collection{
|
||||
Name: input.Name,
|
||||
UserID: userID,
|
||||
}
|
||||
if input.Description != nil {
|
||||
collection.Description = *input.Description
|
||||
}
|
||||
|
||||
// Call collection repository
|
||||
err := r.App.CollectionRepo.Create(ctx, collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Collection{
|
||||
ID: fmt.Sprintf("%d", collection.ID),
|
||||
Name: collection.Name,
|
||||
Description: &collection.Description,
|
||||
User: &model.User{
|
||||
ID: fmt.Sprintf("%d", userID),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateCollection is the resolver for the updateCollection field.
|
||||
func (r *mutationResolver) UpdateCollection(ctx context.Context, id string, input model.CollectionInput) (*model.Collection, error) {
|
||||
panic(fmt.Errorf("not implemented: UpdateCollection - updateCollection"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse collection ID
|
||||
collectionID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collection ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing collection
|
||||
collection, err := r.App.CollectionRepo.GetByID(ctx, uint(collectionID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if collection == nil {
|
||||
return nil, fmt.Errorf("collection not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if collection.UserID != userID {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Update fields
|
||||
collection.Name = input.Name
|
||||
if input.Description != nil {
|
||||
collection.Description = *input.Description
|
||||
}
|
||||
|
||||
// Call collection repository
|
||||
err = r.App.CollectionRepo.Update(ctx, collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Collection{
|
||||
ID: id,
|
||||
Name: collection.Name,
|
||||
Description: &collection.Description,
|
||||
User: &model.User{
|
||||
ID: fmt.Sprintf("%d", userID),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteCollection is the resolver for the deleteCollection field.
|
||||
func (r *mutationResolver) DeleteCollection(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteCollection - deleteCollection"))
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse collection ID
|
||||
collectionID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid collection ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing collection
|
||||
collection, err := r.App.CollectionRepo.GetByID(ctx, uint(collectionID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if collection == nil {
|
||||
return false, fmt.Errorf("collection not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if collection.UserID != userID {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Call collection repository
|
||||
err = r.App.CollectionRepo.Delete(ctx, uint(collectionID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// AddWorkToCollection is the resolver for the addWorkToCollection field.
|
||||
func (r *mutationResolver) AddWorkToCollection(ctx context.Context, collectionID string, workID string) (*model.Collection, error) {
|
||||
panic(fmt.Errorf("not implemented: AddWorkToCollection - addWorkToCollection"))
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse IDs
|
||||
collID, err := strconv.ParseUint(collectionID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collection ID: %v", err)
|
||||
}
|
||||
wID, err := strconv.ParseUint(workID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing collection
|
||||
collection, err := r.App.CollectionRepo.GetByID(ctx, uint(collID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if collection == nil {
|
||||
return nil, fmt.Errorf("collection not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if collection.UserID != userID {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Add work to collection
|
||||
err = r.App.CollectionRepo.AddWorkToCollection(ctx, uint(collID), uint(wID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetch the updated collection to return it
|
||||
updatedCollection, err := r.App.CollectionRepo.GetByID(ctx, uint(collID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Collection{
|
||||
ID: collectionID,
|
||||
Name: updatedCollection.Name,
|
||||
Description: &updatedCollection.Description,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RemoveWorkFromCollection is the resolver for the removeWorkFromCollection field.
|
||||
func (r *mutationResolver) RemoveWorkFromCollection(ctx context.Context, collectionID string, workID string) (*model.Collection, error) {
|
||||
panic(fmt.Errorf("not implemented: RemoveWorkFromCollection - removeWorkFromCollection"))
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse IDs
|
||||
collID, err := strconv.ParseUint(collectionID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid collection ID: %v", err)
|
||||
}
|
||||
wID, err := strconv.ParseUint(workID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing collection
|
||||
collection, err := r.App.CollectionRepo.GetByID(ctx, uint(collID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if collection == nil {
|
||||
return nil, fmt.Errorf("collection not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if collection.UserID != userID {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Remove work from collection
|
||||
err = r.App.CollectionRepo.RemoveWorkFromCollection(ctx, uint(collID), uint(wID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetch the updated collection to return it
|
||||
updatedCollection, err := r.App.CollectionRepo.GetByID(ctx, uint(collID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Collection{
|
||||
ID: collectionID,
|
||||
Name: updatedCollection.Name,
|
||||
Description: &updatedCollection.Description,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateComment is the resolver for the createComment field.
|
||||
func (r *mutationResolver) CreateComment(ctx context.Context, input model.CommentInput) (*model.Comment, error) {
|
||||
panic(fmt.Errorf("not implemented: CreateComment - createComment"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Custom validation
|
||||
if (input.WorkID == nil && input.TranslationID == nil) || (input.WorkID != nil && input.TranslationID != nil) {
|
||||
return nil, fmt.Errorf("must provide either workId or translationId, but not both")
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
comment := &domain.Comment{
|
||||
Text: input.Text,
|
||||
UserID: userID,
|
||||
}
|
||||
if input.WorkID != nil {
|
||||
workID, err := strconv.ParseUint(*input.WorkID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
wID := uint(workID)
|
||||
comment.WorkID = &wID
|
||||
}
|
||||
if input.TranslationID != nil {
|
||||
translationID, err := strconv.ParseUint(*input.TranslationID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid translation ID: %v", err)
|
||||
}
|
||||
tID := uint(translationID)
|
||||
comment.TranslationID = &tID
|
||||
}
|
||||
if input.ParentCommentID != nil {
|
||||
parentCommentID, err := strconv.ParseUint(*input.ParentCommentID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid parent comment ID: %v", err)
|
||||
}
|
||||
pID := uint(parentCommentID)
|
||||
comment.ParentID = &pID
|
||||
}
|
||||
|
||||
// Call comment repository
|
||||
err := r.App.CommentRepo.Create(ctx, comment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Comment{
|
||||
ID: fmt.Sprintf("%d", comment.ID),
|
||||
Text: comment.Text,
|
||||
User: &model.User{
|
||||
ID: fmt.Sprintf("%d", userID),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateComment is the resolver for the updateComment field.
|
||||
func (r *mutationResolver) UpdateComment(ctx context.Context, id string, input model.CommentInput) (*model.Comment, error) {
|
||||
panic(fmt.Errorf("not implemented: UpdateComment - updateComment"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse comment ID
|
||||
commentID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid comment ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing comment
|
||||
comment, err := r.App.CommentRepo.GetByID(ctx, uint(commentID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if comment == nil {
|
||||
return nil, fmt.Errorf("comment not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if comment.UserID != userID {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Update fields
|
||||
comment.Text = input.Text
|
||||
|
||||
// Call comment repository
|
||||
err = r.App.CommentRepo.Update(ctx, comment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Comment{
|
||||
ID: id,
|
||||
Text: comment.Text,
|
||||
User: &model.User{
|
||||
ID: fmt.Sprintf("%d", userID),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteComment is the resolver for the deleteComment field.
|
||||
func (r *mutationResolver) DeleteComment(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteComment - deleteComment"))
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse comment ID
|
||||
commentID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid comment ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing comment
|
||||
comment, err := r.App.CommentRepo.GetByID(ctx, uint(commentID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if comment == nil {
|
||||
return false, fmt.Errorf("comment not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if comment.UserID != userID {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Call comment repository
|
||||
err = r.App.CommentRepo.Delete(ctx, uint(commentID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CreateLike is the resolver for the createLike field.
|
||||
func (r *mutationResolver) CreateLike(ctx context.Context, input model.LikeInput) (*model.Like, error) {
|
||||
panic(fmt.Errorf("not implemented: CreateLike - createLike"))
|
||||
// Custom validation
|
||||
if (input.WorkID == nil && input.TranslationID == nil && input.CommentID == nil) ||
|
||||
(input.WorkID != nil && input.TranslationID != nil) ||
|
||||
(input.WorkID != nil && input.CommentID != nil) ||
|
||||
(input.TranslationID != nil && input.CommentID != nil) {
|
||||
return nil, fmt.Errorf("must provide exactly one of workId, translationId, or commentId")
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
like := &domain.Like{
|
||||
UserID: userID,
|
||||
}
|
||||
if input.WorkID != nil {
|
||||
workID, err := strconv.ParseUint(*input.WorkID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
wID := uint(workID)
|
||||
like.WorkID = &wID
|
||||
}
|
||||
if input.TranslationID != nil {
|
||||
translationID, err := strconv.ParseUint(*input.TranslationID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid translation ID: %v", err)
|
||||
}
|
||||
tID := uint(translationID)
|
||||
like.TranslationID = &tID
|
||||
}
|
||||
if input.CommentID != nil {
|
||||
commentID, err := strconv.ParseUint(*input.CommentID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid comment ID: %v", err)
|
||||
}
|
||||
cID := uint(commentID)
|
||||
like.CommentID = &cID
|
||||
}
|
||||
|
||||
// Call like repository
|
||||
err := r.App.LikeRepo.Create(ctx, like)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Like{
|
||||
ID: fmt.Sprintf("%d", like.ID),
|
||||
User: &model.User{ID: fmt.Sprintf("%d", userID)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteLike is the resolver for the deleteLike field.
|
||||
func (r *mutationResolver) DeleteLike(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteLike - deleteLike"))
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse like ID
|
||||
likeID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid like ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing like
|
||||
like, err := r.App.LikeRepo.GetByID(ctx, uint(likeID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if like == nil {
|
||||
return false, fmt.Errorf("like not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if like.UserID != userID {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Call like repository
|
||||
err = r.App.LikeRepo.Delete(ctx, uint(likeID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CreateBookmark is the resolver for the createBookmark field.
|
||||
func (r *mutationResolver) CreateBookmark(ctx context.Context, input model.BookmarkInput) (*model.Bookmark, error) {
|
||||
panic(fmt.Errorf("not implemented: CreateBookmark - createBookmark"))
|
||||
// Validate input
|
||||
if _, err := govalidator.ValidateStruct(input); err != nil {
|
||||
return nil, fmt.Errorf("invalid input: %w", err)
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse work ID
|
||||
workID, err := strconv.ParseUint(input.WorkID, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid work ID: %v", err)
|
||||
}
|
||||
|
||||
// Create domain model
|
||||
bookmark := &domain.Bookmark{
|
||||
UserID: userID,
|
||||
WorkID: uint(workID),
|
||||
}
|
||||
if input.Name != nil {
|
||||
bookmark.Name = *input.Name
|
||||
}
|
||||
|
||||
// Call bookmark repository
|
||||
err = r.App.BookmarkRepo.Create(ctx, bookmark)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to GraphQL model
|
||||
return &model.Bookmark{
|
||||
ID: fmt.Sprintf("%d", bookmark.ID),
|
||||
Name: &bookmark.Name,
|
||||
User: &model.User{ID: fmt.Sprintf("%d", userID)},
|
||||
Work: &model.Work{ID: fmt.Sprintf("%d", workID)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteBookmark is the resolver for the deleteBookmark field.
|
||||
func (r *mutationResolver) DeleteBookmark(ctx context.Context, id string) (bool, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteBookmark - deleteBookmark"))
|
||||
// Get user ID from context
|
||||
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Parse bookmark ID
|
||||
bookmarkID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid bookmark ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the existing bookmark
|
||||
bookmark, err := r.App.BookmarkRepo.GetByID(ctx, uint(bookmarkID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if bookmark == nil {
|
||||
return false, fmt.Errorf("bookmark not found")
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
if bookmark.UserID != userID {
|
||||
return false, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
// Call bookmark repository
|
||||
err = r.App.BookmarkRepo.Delete(ctx, uint(bookmarkID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CreateContribution is the resolver for the createContribution field.
|
||||
|
||||
@ -32,4 +32,11 @@ type Application struct {
|
||||
SourceRepo domain.SourceRepository
|
||||
MonetizationQueries *monetization.MonetizationQueries
|
||||
MonetizationCommands *monetization.MonetizationCommands
|
||||
TranslationRepo domain.TranslationRepository
|
||||
CopyrightRepo domain.CopyrightRepository
|
||||
MonetizationRepo domain.MonetizationRepository
|
||||
CommentRepo domain.CommentRepository
|
||||
LikeRepo domain.LikeRepository
|
||||
BookmarkRepo domain.BookmarkRepository
|
||||
CollectionRepo domain.CollectionRepository
|
||||
}
|
||||
|
||||
@ -148,7 +148,14 @@ func (b *ApplicationBuilder) BuildApplication() error {
|
||||
BookRepo: sql.NewBookRepository(b.dbConn),
|
||||
PublisherRepo: sql.NewPublisherRepository(b.dbConn),
|
||||
SourceRepo: sql.NewSourceRepository(b.dbConn),
|
||||
TranslationRepo: translationRepo,
|
||||
MonetizationQueries: monetization.NewMonetizationQueries(sql.NewMonetizationRepository(b.dbConn), workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo),
|
||||
CopyrightRepo: copyrightRepo,
|
||||
MonetizationRepo: sql.NewMonetizationRepository(b.dbConn),
|
||||
CommentRepo: sql.NewCommentRepository(b.dbConn),
|
||||
LikeRepo: sql.NewLikeRepository(b.dbConn),
|
||||
BookmarkRepo: sql.NewBookmarkRepository(b.dbConn),
|
||||
CollectionRepo: sql.NewCollectionRepository(b.dbConn),
|
||||
}
|
||||
|
||||
log.LogInfo("Application layer initialized successfully")
|
||||
|
||||
@ -29,6 +29,24 @@ func (r *collectionRepository) ListByUserID(ctx context.Context, userID uint) ([
|
||||
return collections, nil
|
||||
}
|
||||
|
||||
// AddWorkToCollection adds a work to a collection
|
||||
func (r *collectionRepository) AddWorkToCollection(ctx context.Context, collectionID uint, workID uint) error {
|
||||
collection := &domain.Collection{}
|
||||
collection.ID = collectionID
|
||||
work := &domain.Work{}
|
||||
work.ID = workID
|
||||
return r.db.WithContext(ctx).Model(collection).Association("Works").Append(work)
|
||||
}
|
||||
|
||||
// RemoveWorkFromCollection removes a work from a collection
|
||||
func (r *collectionRepository) RemoveWorkFromCollection(ctx context.Context, collectionID uint, workID uint) error {
|
||||
collection := &domain.Collection{}
|
||||
collection.ID = collectionID
|
||||
work := &domain.Work{}
|
||||
work.ID = workID
|
||||
return r.db.WithContext(ctx).Model(collection).Association("Works").Delete(work)
|
||||
}
|
||||
|
||||
// ListPublic finds public collections
|
||||
func (r *collectionRepository) ListPublic(ctx context.Context) ([]domain.Collection, error) {
|
||||
var collections []domain.Collection
|
||||
|
||||
@ -80,6 +80,8 @@ type CollectionRepository interface {
|
||||
ListByUserID(ctx context.Context, userID uint) ([]Collection, error)
|
||||
ListPublic(ctx context.Context) ([]Collection, error)
|
||||
ListByWorkID(ctx context.Context, workID uint) ([]Collection, error)
|
||||
AddWorkToCollection(ctx context.Context, collectionID uint, workID uint) error
|
||||
RemoveWorkFromCollection(ctx context.Context, collectionID uint, workID uint) error
|
||||
}
|
||||
|
||||
// CommentRepository defines CRUD methods specific to Comment.
|
||||
|
||||
@ -28,6 +28,7 @@ import (
|
||||
// IntegrationTestSuite provides a comprehensive test environment with either in-memory SQLite or mock repositories
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
App *app.Application
|
||||
DB *gorm.DB
|
||||
WorkRepo domain.WorkRepository
|
||||
UserRepo domain.UserRepository
|
||||
@ -216,6 +217,39 @@ func (s *IntegrationTestSuite) setupServices() {
|
||||
jwtManager := auth_platform.NewJWTManager()
|
||||
s.AuthCommands = auth.NewAuthCommands(s.UserRepo, jwtManager)
|
||||
s.AuthQueries = auth.NewAuthQueries(s.UserRepo, jwtManager)
|
||||
|
||||
copyrightCommands := copyright.NewCopyrightCommands(s.CopyrightRepo)
|
||||
copyrightQueries := copyright.NewCopyrightQueries(s.CopyrightRepo, s.WorkRepo, s.AuthorRepo, s.BookRepo, s.PublisherRepo, s.SourceRepo)
|
||||
|
||||
monetizationCommands := monetization.NewMonetizationCommands(s.MonetizationRepo)
|
||||
monetizationQueries := monetization.NewMonetizationQueries(s.MonetizationRepo, s.WorkRepo, s.AuthorRepo, s.BookRepo, s.PublisherRepo, s.SourceRepo)
|
||||
|
||||
s.App = &app.Application{
|
||||
WorkCommands: s.WorkCommands,
|
||||
WorkQueries: s.WorkQueries,
|
||||
AuthCommands: s.AuthCommands,
|
||||
AuthQueries: s.AuthQueries,
|
||||
CopyrightCommands: copyrightCommands,
|
||||
CopyrightQueries: copyrightQueries,
|
||||
Localization: s.Localization,
|
||||
Search: search.NewIndexService(s.Localization, s.TranslationRepo),
|
||||
MonetizationCommands: monetizationCommands,
|
||||
MonetizationQueries: monetizationQueries,
|
||||
AuthorRepo: s.AuthorRepo,
|
||||
UserRepo: s.UserRepo,
|
||||
TagRepo: s.TagRepo,
|
||||
CategoryRepo: s.CategoryRepo,
|
||||
BookRepo: s.BookRepo,
|
||||
PublisherRepo: s.PublisherRepo,
|
||||
SourceRepo: s.SourceRepo,
|
||||
TranslationRepo: s.TranslationRepo,
|
||||
CopyrightRepo: s.CopyrightRepo,
|
||||
MonetizationRepo: s.MonetizationRepo,
|
||||
CommentRepo: s.CommentRepo,
|
||||
LikeRepo: s.LikeRepo,
|
||||
BookmarkRepo: s.BookmarkRepo,
|
||||
CollectionRepo: s.CollectionRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// setupTestData creates initial test data
|
||||
@ -322,48 +356,8 @@ func (s *IntegrationTestSuite) SetupTest() {
|
||||
|
||||
// GetResolver returns a properly configured GraphQL resolver for testing
|
||||
func (s *IntegrationTestSuite) GetResolver() *graph.Resolver {
|
||||
// Initialize repositories
|
||||
workRepo := sql.NewWorkRepository(s.DB)
|
||||
userRepo := sql.NewUserRepository(s.DB)
|
||||
authorRepo := sql.NewAuthorRepository(s.DB)
|
||||
translationRepo := sql.NewTranslationRepository(s.DB)
|
||||
copyrightRepo := sql.NewCopyrightRepository(s.DB)
|
||||
bookRepo := sql.NewBookRepository(s.DB)
|
||||
publisherRepo := sql.NewPublisherRepository(s.DB)
|
||||
sourceRepo := sql.NewSourceRepository(s.DB)
|
||||
monetizationRepo := sql.NewMonetizationRepository(s.DB)
|
||||
|
||||
// Initialize application services
|
||||
workCommands := work.NewWorkCommands(workRepo, &MockAnalyzer{})
|
||||
workQueries := work.NewWorkQueries(workRepo)
|
||||
|
||||
jwtManager := auth_platform.NewJWTManager()
|
||||
authCommands := auth.NewAuthCommands(userRepo, jwtManager)
|
||||
authQueries := auth.NewAuthQueries(userRepo, jwtManager)
|
||||
|
||||
copyrightCommands := copyright.NewCopyrightCommands(copyrightRepo)
|
||||
copyrightQueries := copyright.NewCopyrightQueries(copyrightRepo, workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo)
|
||||
|
||||
localizationService := localization.NewService(translationRepo)
|
||||
|
||||
searchService := search.NewIndexService(localizationService, translationRepo)
|
||||
|
||||
monetizationCommands := monetization.NewMonetizationCommands(monetizationRepo)
|
||||
monetizationQueries := monetization.NewMonetizationQueries(monetizationRepo, workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo)
|
||||
|
||||
return &graph.Resolver{
|
||||
App: &app.Application{
|
||||
WorkCommands: workCommands,
|
||||
WorkQueries: workQueries,
|
||||
AuthCommands: authCommands,
|
||||
AuthQueries: authQueries,
|
||||
CopyrightCommands: copyrightCommands,
|
||||
CopyrightQueries: copyrightQueries,
|
||||
Localization: localizationService,
|
||||
Search: searchService,
|
||||
MonetizationCommands: monetizationCommands,
|
||||
MonetizationQueries: monetizationQueries,
|
||||
},
|
||||
App: s.App,
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,3 +399,21 @@ func (s *IntegrationTestSuite) CleanupTestData() {
|
||||
s.DB.Exec("DELETE FROM users")
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAuthenticatedUser creates a user and returns the user and an auth token
|
||||
func (s *IntegrationTestSuite) CreateAuthenticatedUser(username, email string, role domain.UserRole) (*domain.User, string) {
|
||||
user := &domain.User{
|
||||
Username: username,
|
||||
Email: email,
|
||||
Role: role,
|
||||
Password: "password", // Not used for token generation, but good to have
|
||||
}
|
||||
err := s.UserRepo.Create(context.Background(), user)
|
||||
s.Require().NoError(err)
|
||||
|
||||
jwtManager := auth_platform.NewJWTManager()
|
||||
token, err := jwtManager.GenerateToken(user)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return user, token
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user