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.78 import ( "context" "fmt" "log" "strconv" "tercul/internal/adapters/graphql/model" "tercul/internal/app/auth" "tercul/internal/app/author" "tercul/internal/app/collection" "tercul/internal/app/comment" "tercul/internal/app/bookmark" "tercul/internal/app/like" "tercul/internal/app/translation" "tercul/internal/domain" platform_auth "tercul/internal/platform/auth" ) // Register is the resolver for the register field. func (r *mutationResolver) Register(ctx context.Context, input model.RegisterInput) (*model.AuthPayload, error) { // Convert GraphQL input to service input registerInput := auth.RegisterInput{ Username: input.Username, Email: input.Email, Password: input.Password, FirstName: input.FirstName, LastName: input.LastName, } // Call auth service authResponse, err := r.App.AuthCommands.Register(ctx, registerInput) if err != nil { return nil, err } // Convert service response to GraphQL response return &model.AuthPayload{ Token: authResponse.Token, User: &model.User{ ID: fmt.Sprintf("%d", authResponse.User.ID), Username: authResponse.User.Username, Email: authResponse.User.Email, FirstName: &authResponse.User.FirstName, LastName: &authResponse.User.LastName, DisplayName: &authResponse.User.DisplayName, Role: model.UserRole(authResponse.User.Role), Verified: authResponse.User.Verified, Active: authResponse.User.Active, }, }, nil } // Login is the resolver for the login field. func (r *mutationResolver) Login(ctx context.Context, input model.LoginInput) (*model.AuthPayload, error) { // Convert GraphQL input to service input loginInput := auth.LoginInput{ Email: input.Email, Password: input.Password, } // Call auth service authResponse, err := r.App.AuthCommands.Login(ctx, loginInput) if err != nil { return nil, err } // Convert service response to GraphQL response return &model.AuthPayload{ Token: authResponse.Token, User: &model.User{ ID: fmt.Sprintf("%d", authResponse.User.ID), Username: authResponse.User.Username, Email: authResponse.User.Email, FirstName: &authResponse.User.FirstName, LastName: &authResponse.User.LastName, DisplayName: &authResponse.User.DisplayName, Role: model.UserRole(authResponse.User.Role), Verified: authResponse.User.Verified, Active: authResponse.User.Active, }, }, nil } // CreateWork is the resolver for the createWork field. func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput) (*model.Work, error) { if err := validateWorkInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, err) } // Create domain model work := &domain.Work{ Title: input.Name, TranslatableModel: domain.TranslatableModel{Language: input.Language}, // Description: *input.Description, // Other fields can be set here } // Call work service err := r.App.WorkCommands.CreateWork(ctx, work) if err != nil { return nil, err } // The logic for creating a translation should probably be in the app layer as well, // but for now, we'll leave it here to match the old logic. // This will be refactored later. if input.Content != nil && *input.Content != "" { // This part needs a translation repository, which is not in the App struct. // I will have to add it. // For now, I will comment this out. /* translation := &domain.Translation{ Title: input.Name, Content: *input.Content, Language: input.Language, TranslatableID: work.ID, TranslatableType: "Work", IsOriginalLanguage: true, } // This needs a translation repo, which should be part of a translation service. // err = r.App.TranslationRepo.Create(ctx, translation) // if err != nil { // return nil, fmt.Errorf("failed to create translation: %v", err) // } */ } // Convert to GraphQL model return &model.Work{ ID: fmt.Sprintf("%d", work.ID), Name: work.Title, Language: work.Language, Content: input.Content, }, nil } // UpdateWork is the resolver for the updateWork field. func (r *mutationResolver) UpdateWork(ctx context.Context, id string, input model.WorkInput) (*model.Work, error) { if err := validateWorkInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, 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) { 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) { if err := validateTranslationInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, err) } workID, err := strconv.ParseUint(input.WorkID, 10, 32) if err != nil { return nil, fmt.Errorf("invalid work ID: %v", err) } var content string if input.Content != nil { content = *input.Content } createInput := translation.CreateTranslationInput{ Title: input.Name, Language: input.Language, Content: content, WorkID: uint(workID), } // Call translation service newTranslation, err := r.App.TranslationCommands.CreateTranslation(ctx, createInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Translation{ ID: fmt.Sprintf("%d", newTranslation.ID), Name: newTranslation.Title, Language: newTranslation.Language, Content: &newTranslation.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) { if err := validateTranslationInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, err) } translationID, err := strconv.ParseUint(id, 10, 32) if err != nil { return nil, fmt.Errorf("invalid translation ID: %v", err) } var content string if input.Content != nil { content = *input.Content } updateInput := translation.UpdateTranslationInput{ ID: uint(translationID), Title: input.Name, Language: input.Language, Content: content, } // Call translation service updatedTranslation, err := r.App.TranslationCommands.UpdateTranslation(ctx, updateInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Translation{ ID: id, Name: updatedTranslation.Title, Language: updatedTranslation.Language, Content: &updatedTranslation.Content, WorkID: input.WorkID, }, nil } // DeleteTranslation is the resolver for the deleteTranslation field. func (r *mutationResolver) DeleteTranslation(ctx context.Context, id string) (bool, error) { translationID, err := strconv.ParseUint(id, 10, 32) if err != nil { return false, fmt.Errorf("invalid translation ID: %v", err) } err = r.App.TranslationCommands.DeleteTranslation(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) { if err := validateAuthorInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, err) } createInput := author.CreateAuthorInput{ Name: input.Name, Language: input.Language, } // Call author service newAuthor, err := r.App.AuthorCommands.CreateAuthor(ctx, createInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Author{ ID: fmt.Sprintf("%d", newAuthor.ID), Name: newAuthor.Name, Language: newAuthor.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) { if err := validateAuthorInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, err) } authorID, err := strconv.ParseUint(id, 10, 32) if err != nil { return nil, fmt.Errorf("invalid author ID: %v", err) } updateInput := author.UpdateAuthorInput{ ID: uint(authorID), Name: input.Name, Language: input.Language, } // Call author service updatedAuthor, err := r.App.AuthorCommands.UpdateAuthor(ctx, updateInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Author{ ID: id, Name: updatedAuthor.Name, Language: updatedAuthor.Language, }, nil } // DeleteAuthor is the resolver for the deleteAuthor field. func (r *mutationResolver) DeleteAuthor(ctx context.Context, id string) (bool, error) { authorID, err := strconv.ParseUint(id, 10, 32) if err != nil { return false, fmt.Errorf("invalid author ID: %v", err) } err = r.App.AuthorCommands.DeleteAuthor(ctx, uint(authorID)) if err != nil { return false, err } return true, nil } // UpdateUser is the resolver for the updateUser field. func (r *mutationResolver) UpdateUser(ctx context.Context, id string, input model.UserInput) (*model.User, error) { panic(fmt.Errorf("not implemented: UpdateUser - updateUser")) } // DeleteUser is the resolver for the deleteUser field. func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, error) { panic(fmt.Errorf("not implemented: DeleteUser - deleteUser")) } // CreateCollection is the resolver for the createCollection field. func (r *mutationResolver) CreateCollection(ctx context.Context, input model.CollectionInput) (*model.Collection, error) { // Get user ID from context userID, ok := platform_auth.GetUserIDFromContext(ctx) if !ok { return nil, fmt.Errorf("unauthorized") } var description string if input.Description != nil { description = *input.Description } createInput := collection.CreateCollectionInput{ Name: input.Name, Description: description, UserID: userID, } // Call collection service newCollection, err := r.App.CollectionCommands.CreateCollection(ctx, createInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Collection{ ID: fmt.Sprintf("%d", newCollection.ID), Name: newCollection.Name, Description: &newCollection.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) { // 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) } var description string if input.Description != nil { description = *input.Description } updateInput := collection.UpdateCollectionInput{ ID: uint(collectionID), Name: input.Name, Description: description, UserID: userID, } // Call collection service updatedCollection, err := r.App.CollectionCommands.UpdateCollection(ctx, updateInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Collection{ ID: id, Name: updatedCollection.Name, Description: &updatedCollection.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) { // 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) } deleteInput := collection.DeleteCollectionInput{ ID: uint(collectionID), UserID: userID, } // Call collection service err = r.App.CollectionCommands.DeleteCollection(ctx, deleteInput) 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) { // 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) } addInput := collection.AddWorkToCollectionInput{ CollectionID: uint(collID), WorkID: uint(wID), UserID: userID, } // Add work to collection err = r.App.CollectionCommands.AddWorkToCollection(ctx, addInput) if err != nil { return nil, err } // Fetch the updated collection to return it updatedCollection, err := r.App.CollectionQueries.GetCollectionByID(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) { // 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) } removeInput := collection.RemoveWorkFromCollectionInput{ CollectionID: uint(collID), WorkID: uint(wID), UserID: userID, } // Remove work from collection err = r.App.CollectionCommands.RemoveWorkFromCollection(ctx, removeInput) if err != nil { return nil, err } // Fetch the updated collection to return it updatedCollection, err := r.App.CollectionQueries.GetCollectionByID(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) { // 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") } createInput := comment.CreateCommentInput{ 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) createInput.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) createInput.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) createInput.ParentID = &pID } // Call comment service newComment, err := r.App.CommentCommands.CreateComment(ctx, createInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Comment{ ID: fmt.Sprintf("%d", newComment.ID), Text: newComment.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) { // 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) } updateInput := comment.UpdateCommentInput{ ID: uint(commentID), Text: input.Text, UserID: userID, } // Call comment service updatedComment, err := r.App.CommentCommands.UpdateComment(ctx, updateInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Comment{ ID: id, Text: updatedComment.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) { // 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) } deleteInput := comment.DeleteCommentInput{ ID: uint(commentID), UserID: userID, } // Call comment service err = r.App.CommentCommands.DeleteComment(ctx, deleteInput) 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) { // 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") } createInput := like.CreateLikeInput{ 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) createInput.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) createInput.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) createInput.CommentID = &cID } // Call like service newLike, err := r.App.LikeCommands.CreateLike(ctx, createInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Like{ ID: fmt.Sprintf("%d", newLike.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) { // 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) } deleteInput := like.DeleteLikeInput{ ID: uint(likeID), UserID: userID, } // Call like service err = r.App.LikeCommands.DeleteLike(ctx, deleteInput) 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) { // 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) } createInput := bookmark.CreateBookmarkInput{ UserID: userID, WorkID: uint(workID), Name: input.Name, } // Call bookmark service newBookmark, err := r.App.BookmarkCommands.CreateBookmark(ctx, createInput) if err != nil { return nil, err } // Convert to GraphQL model return &model.Bookmark{ ID: fmt.Sprintf("%d", newBookmark.ID), Name: &newBookmark.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) { // 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) } deleteInput := bookmark.DeleteBookmarkInput{ ID: uint(bookmarkID), UserID: userID, } // Call bookmark service err = r.App.BookmarkCommands.DeleteBookmark(ctx, deleteInput) if err != nil { return false, err } return true, nil } // CreateContribution is the resolver for the createContribution field. func (r *mutationResolver) CreateContribution(ctx context.Context, input model.ContributionInput) (*model.Contribution, error) { panic(fmt.Errorf("not implemented: CreateContribution - createContribution")) } // UpdateContribution is the resolver for the updateContribution field. func (r *mutationResolver) UpdateContribution(ctx context.Context, id string, input model.ContributionInput) (*model.Contribution, error) { panic(fmt.Errorf("not implemented: UpdateContribution - updateContribution")) } // DeleteContribution is the resolver for the deleteContribution field. func (r *mutationResolver) DeleteContribution(ctx context.Context, id string) (bool, error) { panic(fmt.Errorf("not implemented: DeleteContribution - deleteContribution")) } // ReviewContribution is the resolver for the reviewContribution field. func (r *mutationResolver) ReviewContribution(ctx context.Context, id string, status model.ContributionStatus, feedback *string) (*model.Contribution, error) { panic(fmt.Errorf("not implemented: ReviewContribution - reviewContribution")) } // Logout is the resolver for the logout field. func (r *mutationResolver) Logout(ctx context.Context) (bool, error) { panic(fmt.Errorf("not implemented: Logout - logout")) } // RefreshToken is the resolver for the refreshToken field. func (r *mutationResolver) RefreshToken(ctx context.Context) (*model.AuthPayload, error) { panic(fmt.Errorf("not implemented: RefreshToken - refreshToken")) } // ForgotPassword is the resolver for the forgotPassword field. func (r *mutationResolver) ForgotPassword(ctx context.Context, email string) (bool, error) { panic(fmt.Errorf("not implemented: ForgotPassword - forgotPassword")) } // ResetPassword is the resolver for the resetPassword field. func (r *mutationResolver) ResetPassword(ctx context.Context, token string, newPassword string) (bool, error) { panic(fmt.Errorf("not implemented: ResetPassword - resetPassword")) } // VerifyEmail is the resolver for the verifyEmail field. func (r *mutationResolver) VerifyEmail(ctx context.Context, token string) (bool, error) { panic(fmt.Errorf("not implemented: VerifyEmail - verifyEmail")) } // ResendVerificationEmail is the resolver for the resendVerificationEmail field. func (r *mutationResolver) ResendVerificationEmail(ctx context.Context, email string) (bool, error) { panic(fmt.Errorf("not implemented: ResendVerificationEmail - resendVerificationEmail")) } // UpdateProfile is the resolver for the updateProfile field. func (r *mutationResolver) UpdateProfile(ctx context.Context, input model.UserInput) (*model.User, error) { panic(fmt.Errorf("not implemented: UpdateProfile - updateProfile")) } // ChangePassword is the resolver for the changePassword field. func (r *mutationResolver) ChangePassword(ctx context.Context, currentPassword string, newPassword string) (bool, error) { panic(fmt.Errorf("not implemented: ChangePassword - changePassword")) } // Work is the resolver for the work field. func (r *queryResolver) Work(ctx context.Context, id string) (*model.Work, error) { workID, err := strconv.ParseUint(id, 10, 32) if err != nil { return nil, fmt.Errorf("invalid work ID: %v", err) } work, err := r.App.WorkQueries.GetWorkByID(ctx, uint(workID)) if err != nil { return nil, err } if work == nil { return nil, nil } // Content resolved via Localization service content, err := r.App.Localization.GetWorkContent(ctx, work.ID, work.Language) if err != nil { // Log error but don't fail the request log.Printf("could not resolve content for work %d: %v", work.ID, err) } authorIDs := make([]string, len(work.AuthorIDs)) for i, authorID := range work.AuthorIDs { authorIDs[i] = fmt.Sprintf("%d", authorID) } return &model.Work{ ID: id, Name: work.Title, Language: work.Language, Content: &content, AuthorIDs: authorIDs, }, nil } // Works is the resolver for the works field. func (r *queryResolver) Works(ctx context.Context, limit *int32, offset *int32, language *string, authorID *string, categoryID *string, tagID *string, search *string) ([]*model.Work, error) { // This resolver has complex logic that should be moved to the application layer. // For now, I will just call the ListWorks query. // A proper implementation would have specific query methods for each filter. page := 1 pageSize := 20 if limit != nil { pageSize = int(*limit) } if offset != nil { page = int(*offset)/pageSize + 1 } paginatedResult, err := r.App.WorkQueries.ListWorks(ctx, page, pageSize) if err != nil { return nil, err } // Convert to GraphQL model var result []*model.Work for _, w := range paginatedResult.Items { content, _ := r.App.Localization.GetWorkContent(ctx, w.ID, w.Language) result = append(result, &model.Work{ ID: fmt.Sprintf("%d", w.ID), Name: w.Title, Language: w.Language, Content: &content, }) } return result, nil } // Translation is the resolver for the translation field. func (r *queryResolver) Translation(ctx context.Context, id string) (*model.Translation, error) { panic(fmt.Errorf("not implemented: Translation - translation")) } // Translations is the resolver for the translations field. func (r *queryResolver) Translations(ctx context.Context, workID string, language *string, limit *int32, offset *int32) ([]*model.Translation, error) { panic(fmt.Errorf("not implemented: Translations - translations")) } // Author is the resolver for the author field. func (r *queryResolver) Author(ctx context.Context, id string) (*model.Author, error) { panic(fmt.Errorf("not implemented: Author - author")) } // Authors is the resolver for the authors field. func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32, search *string, countryID *string) ([]*model.Author, error) { var authors []domain.Author var err error if countryID != nil { countryIDUint, err := strconv.ParseUint(*countryID, 10, 32) if err != nil { return nil, err } authors, err = r.App.AuthorQueries.ListAuthorsByCountryID(ctx, uint(countryIDUint)) } else { page := 1 pageSize := 1000 if limit != nil { pageSize = int(*limit) } if offset != nil { page = int(*offset)/pageSize + 1 } result, err := r.App.AuthorQueries.ListAuthors(ctx, page, pageSize) if err != nil { return nil, err } authors = result.Items } if err != nil { return nil, err } // Convert to GraphQL model; resolve biography via Localization service var result []*model.Author for _, a := range authors { var bio *string if r.App.Localization != nil { if b, err := r.App.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && b != "" { bio = &b } } result = append(result, &model.Author{ ID: fmt.Sprintf("%d", a.ID), Name: a.Name, Language: a.Language, Biography: bio, }) } return result, nil } // User is the resolver for the user field. func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) { panic(fmt.Errorf("not implemented: User - user")) } // UserByEmail is the resolver for the userByEmail field. func (r *queryResolver) UserByEmail(ctx context.Context, email string) (*model.User, error) { panic(fmt.Errorf("not implemented: UserByEmail - userByEmail")) } // UserByUsername is the resolver for the userByUsername field. func (r *queryResolver) UserByUsername(ctx context.Context, username string) (*model.User, error) { panic(fmt.Errorf("not implemented: UserByUsername - userByUsername")) } // Users is the resolver for the users field. func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32, role *model.UserRole) ([]*model.User, error) { var users []domain.User var err error if role != nil { // Convert GraphQL role to model role var modelRole domain.UserRole switch *role { case model.UserRoleReader: modelRole = domain.UserRoleReader case model.UserRoleContributor: modelRole = domain.UserRoleContributor case model.UserRoleReviewer: modelRole = domain.UserRoleReviewer case model.UserRoleEditor: modelRole = domain.UserRoleEditor case model.UserRoleAdmin: modelRole = domain.UserRoleAdmin default: return nil, fmt.Errorf("invalid user role: %s", *role) } users, err = r.App.UserQueries.ListUsersByRole(ctx, modelRole) } else { page := 1 pageSize := 1000 if limit != nil { pageSize = int(*limit) } if offset != nil { page = int(*offset)/pageSize + 1 } result, err := r.App.UserQueries.ListUsers(ctx, page, pageSize) if err != nil { return nil, err } users = result.Items } if err != nil { return nil, err } // Convert to GraphQL model var result []*model.User for _, u := range users { // Convert model role to GraphQL role var graphqlRole model.UserRole switch u.Role { case domain.UserRoleReader: graphqlRole = model.UserRoleReader case domain.UserRoleContributor: graphqlRole = model.UserRoleContributor case domain.UserRoleReviewer: graphqlRole = model.UserRoleReviewer case domain.UserRoleEditor: graphqlRole = model.UserRoleEditor case domain.UserRoleAdmin: graphqlRole = model.UserRoleAdmin default: graphqlRole = model.UserRoleReader } result = append(result, &model.User{ ID: fmt.Sprintf("%d", u.ID), Username: u.Username, Email: u.Email, Role: graphqlRole, }) } return result, nil } // Me is the resolver for the me field. func (r *queryResolver) Me(ctx context.Context) (*model.User, error) { panic(fmt.Errorf("not implemented: Me - me")) } // UserProfile is the resolver for the userProfile field. func (r *queryResolver) UserProfile(ctx context.Context, userID string) (*model.UserProfile, error) { panic(fmt.Errorf("not implemented: UserProfile - userProfile")) } // Collection is the resolver for the collection field. func (r *queryResolver) Collection(ctx context.Context, id string) (*model.Collection, error) { panic(fmt.Errorf("not implemented: Collection - collection")) } // Collections is the resolver for the collections field. func (r *queryResolver) Collections(ctx context.Context, userID *string, limit *int32, offset *int32) ([]*model.Collection, error) { panic(fmt.Errorf("not implemented: Collections - collections")) } // Tag is the resolver for the tag field. func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error) { tagID, err := strconv.ParseUint(id, 10, 32) if err != nil { return nil, err } tag, err := r.App.TagQueries.GetTagByID(ctx, uint(tagID)) if err != nil { return nil, err } return &model.Tag{ ID: fmt.Sprintf("%d", tag.ID), Name: tag.Name, }, nil } // Tags is the resolver for the tags field. func (r *queryResolver) Tags(ctx context.Context, limit *int32, offset *int32) ([]*model.Tag, error) { page := 1 pageSize := 1000 if limit != nil { pageSize = int(*limit) } if offset != nil { page = int(*offset)/pageSize + 1 } paginatedResult, err := r.App.TagQueries.ListTags(ctx, page, pageSize) if err != nil { return nil, err } // Convert to GraphQL model var result []*model.Tag for _, t := range paginatedResult.Items { result = append(result, &model.Tag{ ID: fmt.Sprintf("%d", t.ID), Name: t.Name, }) } return result, nil } // Category is the resolver for the category field. func (r *queryResolver) Category(ctx context.Context, id string) (*model.Category, error) { categoryID, err := strconv.ParseUint(id, 10, 32) if err != nil { return nil, err } category, err := r.App.CategoryQueries.GetCategoryByID(ctx, uint(categoryID)) if err != nil { return nil, err } return &model.Category{ ID: fmt.Sprintf("%d", category.ID), Name: category.Name, }, nil } // Categories is the resolver for the categories field. func (r *queryResolver) Categories(ctx context.Context, limit *int32, offset *int32) ([]*model.Category, error) { page := 1 pageSize := 1000 if limit != nil { pageSize = int(*limit) } if offset != nil { page = int(*offset)/pageSize + 1 } paginatedResult, err := r.App.CategoryQueries.ListCategories(ctx, page, pageSize) if err != nil { return nil, err } // Convert to GraphQL model var result []*model.Category for _, c := range paginatedResult.Items { result = append(result, &model.Category{ ID: fmt.Sprintf("%d", c.ID), Name: c.Name, }) } return result, nil } // Comment is the resolver for the comment field. func (r *queryResolver) Comment(ctx context.Context, id string) (*model.Comment, error) { panic(fmt.Errorf("not implemented: Comment - comment")) } // Comments is the resolver for the comments field. func (r *queryResolver) Comments(ctx context.Context, workID *string, translationID *string, userID *string, limit *int32, offset *int32) ([]*model.Comment, error) { panic(fmt.Errorf("not implemented: Comments - comments")) } // Search is the resolver for the search field. func (r *queryResolver) Search(ctx context.Context, query string, limit *int32, offset *int32, filters *model.SearchFilters) (*model.SearchResults, error) { panic(fmt.Errorf("not implemented: Search - search")) } // TrendingWorks is the resolver for the trendingWorks field. func (r *queryResolver) TrendingWorks(ctx context.Context, timePeriod *string, limit *int32) ([]*model.Work, error) { tp := "daily" if timePeriod != nil { tp = *timePeriod } l := 10 if limit != nil { l = int(*limit) } works, err := r.App.AnalyticsService.GetTrendingWorks(ctx, tp, l) if err != nil { return nil, err } var result []*model.Work for _, w := range works { result = append(result, &model.Work{ ID: fmt.Sprintf("%d", w.ID), Name: w.Title, Language: w.Language, }) } return result, nil } // Mutation returns MutationResolver implementation. func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } // Work returns WorkResolver implementation. func (r *Resolver) Work() WorkResolver { return &workResolver{r} } func (r *workResolver) Authors(ctx context.Context, obj *model.Work) ([]*model.Author, error) { thunk := For(ctx).AuthorLoader.LoadMany(ctx, obj.AuthorIDs) results, errs := thunk() if len(errs) > 0 { // handle errors return nil, errs[0] } modelAuthors := make([]*model.Author, len(results)) for i, author := range results { modelAuthors[i] = &model.Author{ ID: fmt.Sprintf("%d", author.ID), Name: author.Name, Language: author.Language, } } return modelAuthors, nil } // Categories is the resolver for the categories field. func (r *workResolver) Categories(ctx context.Context, obj *model.Work) ([]*model.Category, error) { panic(fmt.Errorf("not implemented: Categories - categories")) } // Tags is the resolver for the tags field. func (r *workResolver) Tags(ctx context.Context, obj *model.Work) ([]*model.Tag, error) { panic(fmt.Errorf("not implemented: Tags - tags")) } // Translation returns TranslationResolver implementation. func (r *Resolver) Translation() TranslationResolver { return &translationResolver{r} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } type workResolver struct{ *Resolver } type translationResolver struct{ *Resolver } // Work is the resolver for the work field. func (r *translationResolver) Work(ctx context.Context, obj *model.Translation) (*model.Work, error) { panic(fmt.Errorf("not implemented: Work - work")) } // Translator is the resolver for the translator field. func (r *translationResolver) Translator(ctx context.Context, obj *model.Translation) (*model.User, error) { panic(fmt.Errorf("not implemented: Translator - translator")) } func (r *Resolver) Category() CategoryResolver { return &categoryResolver{r} } func (r *Resolver) Tag() TagResolver { return &tagResolver{r} } func (r *Resolver) User() UserResolver { return &userResolver{r} } type categoryResolver struct{ *Resolver } // Works is the resolver for the works field. func (r *categoryResolver) Works(ctx context.Context, obj *model.Category) ([]*model.Work, error) { panic(fmt.Errorf("not implemented: Works - works")) } type tagResolver struct{ *Resolver } // Works is the resolver for the works field. func (r *tagResolver) Works(ctx context.Context, obj *model.Tag) ([]*model.Work, error) { panic(fmt.Errorf("not implemented: Works - works")) } type userResolver struct{ *Resolver } // Collections is the resolver for the collections field. func (r *userResolver) Collections(ctx context.Context, obj *model.User) ([]*model.Collection, error) { panic(fmt.Errorf("not implemented: Collections - collections")) } // !!! WARNING !!! // The code below was going to be deleted when updating resolvers. It has been copied here so you have // one last chance to move it out of harms way if you want. There are two reasons this happens: // - When renaming or deleting a resolver the old code will be put in here. You can safely delete // it when you're done. // - You have helper methods in this file. Move them out to keep these resolver files clean. /* func (r *Resolver) Translation() TranslationResolver { return &translationResolver{r} } type translationResolver struct{ *Resolver } func toInt32(i int64) *int { val := int(i) return &val } func toInt(i int) *int { return &i } func (r *workResolver) Stats(ctx context.Context, obj *model.Work) (*model.WorkStats, error) { workID, err := strconv.ParseUint(obj.ID, 10, 32) if err != nil { return nil, fmt.Errorf("invalid work ID: %v", err) } stats, err := r.App.AnalyticsService.GetOrCreateWorkStats(ctx, uint(workID)) if err != nil { return nil, err } // Convert domain model to GraphQL model return &model.WorkStats{ ID: fmt.Sprintf("%d", stats.ID), Views: toInt32(stats.Views), Likes: toInt32(stats.Likes), Comments: toInt32(stats.Comments), Bookmarks: toInt32(stats.Bookmarks), Shares: toInt32(stats.Shares), TranslationCount: toInt32(stats.TranslationCount), ReadingTime: toInt(stats.ReadingTime), Complexity: &stats.Complexity, Sentiment: &stats.Sentiment, }, nil } func (r *translationResolver) Stats(ctx context.Context, obj *model.Translation) (*model.TranslationStats, error) { translationID, err := strconv.ParseUint(obj.ID, 10, 32) if err != nil { return nil, fmt.Errorf("invalid translation ID: %v", err) } stats, err := r.App.AnalyticsService.GetOrCreateTranslationStats(ctx, uint(translationID)) if err != nil { return nil, err } // Convert domain model to GraphQL model return &model.TranslationStats{ ID: fmt.Sprintf("%d", stats.ID), Views: toInt32(stats.Views), Likes: toInt32(stats.Likes), Comments: toInt32(stats.Comments), Shares: toInt32(stats.Shares), ReadingTime: toInt(stats.ReadingTime), Sentiment: &stats.Sentiment, }, nil } */