This commit implements the following core GraphQL query resolvers that were previously panicking:

- `Me`: Fetches the details of the currently authenticated user from the request context.
- `User`: Fetches the public details of a user by their ID.
- `Author`: Fetches the details of an author by their ID, including their biography from the localization service.

These changes are part of the larger effort to complete the unimplemented resolvers in the GraphQL API.
This commit is contained in:
google-labs-jules[bot] 2025-10-05 13:31:12 +00:00
parent 20da2199ba
commit ef4077b5d6
2 changed files with 84 additions and 9 deletions

View File

@ -8,7 +8,7 @@ This document is the single source of truth for all outstanding development task
### Stabilize Core Logic (Prevent Panics) ### Stabilize Core Logic (Prevent Panics)
- [ ] **Fix Background Job Panic:** The background job queue in `internal/jobs/sync/queue.go` can panic on error. This must be refactored to handle errors gracefully. - [x] **Fix Background Job Panic:** The background job queue in `internal/jobs/sync/queue.go` can panic on error. This has been refactored to handle errors gracefully by using `log.Printf` instead of `log.Fatalf`.
--- ---
@ -54,10 +54,10 @@ This document is the single source of truth for all outstanding development task
### EPIC: Robust Testing Framework ### EPIC: Robust Testing Framework
- [ ] **Refactor Testing Utilities:** Decouple our tests from a live database to make them faster and more reliable. - [x] **Refactor Testing Utilities:** Decouple our tests from a live database to make them faster and more reliable.
- [ ] Remove all database connection logic from `internal/testutil/testutil.go`. - [x] Verified that `internal/testutil/testutil.go` is already database-agnostic.
- [ ] **Implement Mock Repositories:** The test mocks are incomplete and `panic`. - [x] **Implement Mock Repositories:** The test mocks that were incomplete and causing `panic`s have been implemented.
- [ ] Implement the `panic("not implemented")` methods in `internal/adapters/graphql/like_repo_mock_test.go`, `internal/adapters/graphql/work_repo_mock_test.go`, and `internal/testutil/mock_user_repository.go`. - [x] Implemented the `panic("not implemented")` methods in `internal/adapters/graphql/like_repo_mock_test.go`, `internal/adapters/graphql/work_repo_mock_test.go`, and `internal/testutil/mock_user_repository.go`.
--- ---
@ -75,7 +75,7 @@ This document is the single source of truth for all outstanding development task
### EPIC: Further Architectural Improvements ### EPIC: Further Architectural Improvements
- [ ] **Refactor Caching:** Replace the bespoke cached repositories with a decorator pattern in `internal/data/cache`. - [ ] **Refactor Caching:** Replace the bespoke cached repositories with a decorator pattern in `internal/data/cache`.
- [ ] **Consolidate Duplicated Structs:** The `WorkAnalytics` and `TranslationAnalytics` structs are defined in two different packages. Consolidate them. - [x] **Consolidate Duplicated Structs:** The duplicated `WorkAnalytics` and `TranslationAnalytics` structs have been consolidated into a new `internal/domain/analytics` package.
--- ---

View File

@ -1219,7 +1219,31 @@ func (r *queryResolver) Books(ctx context.Context, limit *int32, offset *int32)
// Author is the resolver for the author field. // Author is the resolver for the author field.
func (r *queryResolver) Author(ctx context.Context, id string) (*model.Author, error) { func (r *queryResolver) Author(ctx context.Context, id string) (*model.Author, error) {
panic(fmt.Errorf("not implemented: Author - author")) authorID, err := strconv.ParseUint(id, 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid author ID: %v", err)
}
authorRecord, err := r.App.Author.Queries.Author(ctx, uint(authorID))
if err != nil {
return nil, err
}
if authorRecord == nil {
return nil, nil // Or return a "not found" error
}
var bio *string
biography, err := r.App.Localization.Queries.GetAuthorBiography(ctx, authorRecord.ID, authorRecord.Language)
if err == nil && biography != "" {
bio = &biography
}
return &model.Author{
ID: fmt.Sprintf("%d", authorRecord.ID),
Name: authorRecord.Name,
Language: authorRecord.Language,
Biography: bio,
}, nil
} }
// Authors is the resolver for the authors field. // Authors is the resolver for the authors field.
@ -1267,7 +1291,33 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
// User is the resolver for the user field. // User is the resolver for the user field.
func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) { func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) {
panic(fmt.Errorf("not implemented: User - user")) userID, err := strconv.ParseUint(id, 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid user ID: %v", err)
}
user, err := r.App.User.Queries.User(ctx, uint(userID))
if err != nil {
return nil, err
}
if user == nil {
return nil, nil // Or return a "not found" error
}
// Convert to GraphQL model
return &model.User{
ID: fmt.Sprintf("%d", user.ID),
Username: user.Username,
Email: user.Email,
FirstName: &user.FirstName,
LastName: &user.LastName,
DisplayName: &user.DisplayName,
Bio: &user.Bio,
AvatarURL: &user.AvatarURL,
Role: model.UserRole(user.Role),
Verified: user.Verified,
Active: user.Active,
}, nil
} }
// UserByEmail is the resolver for the userByEmail field. // UserByEmail is the resolver for the userByEmail field.
@ -1344,7 +1394,32 @@ func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32,
// Me is the resolver for the me field. // Me is the resolver for the me field.
func (r *queryResolver) Me(ctx context.Context) (*model.User, error) { func (r *queryResolver) Me(ctx context.Context) (*model.User, error) {
panic(fmt.Errorf("not implemented: Me - me")) // Get user ID from context
userID, ok := platform_auth.GetUserIDFromContext(ctx)
if !ok {
return nil, domain.ErrUnauthorized
}
// Fetch user details
user, err := r.App.User.Queries.User(ctx, userID)
if err != nil {
return nil, err
}
// Convert to GraphQL model
return &model.User{
ID: fmt.Sprintf("%d", user.ID),
Username: user.Username,
Email: user.Email,
FirstName: &user.FirstName,
LastName: &user.LastName,
DisplayName: &user.DisplayName,
Bio: &user.Bio,
AvatarURL: &user.AvatarURL,
Role: model.UserRole(user.Role),
Verified: user.Verified,
Active: user.Active,
}, nil
} }
// UserProfile is the resolver for the userProfile field. // UserProfile is the resolver for the userProfile field.