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/analytics" "tercul/internal/app/auth" "tercul/internal/domain" platform_auth "tercul/internal/platform/auth" "time" ) // 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) } // 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) { 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) } 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) { 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) { if err := validateAuthorInput(input); err != nil { return nil, fmt.Errorf("%w: %v", ErrValidation, 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) { 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) } // 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) { 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. 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") } // 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) { // 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) { // 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) { // 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) { // 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) { // 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 } // Publish analytics event if comment.WorkID != nil { event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeWorkCommented, WorkID: comment.WorkID, UserID: &userID, Timestamp: time.Now(), } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish work commented event: %v", err) } } if comment.TranslationID != nil { event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeTranslationCommented, TranslationID: comment.TranslationID, UserID: &userID, Timestamp: time.Now(), } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish translation commented event: %v", 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) { // 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) { // 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) { // 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 } // Publish analytics event if like.WorkID != nil { event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeWorkLiked, WorkID: like.WorkID, UserID: &userID, Timestamp: time.Now(), } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish work liked event: %v", err) } } if like.TranslationID != nil { event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeTranslationLiked, TranslationID: like.TranslationID, UserID: &userID, Timestamp: time.Now(), } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish translation liked event: %v", 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) { // 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) { // 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 } // Publish analytics event wID := uint(workID) event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeWorkBookmarked, WorkID: &wID, UserID: &userID, Timestamp: time.Now(), } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish work bookmarked event: %v", 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) { // 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. 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 } // Publish analytics event for work view wID := uint(workID) event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeWorkViewed, WorkID: &wID, Timestamp: time.Now(), } if userID, ok := platform_auth.GetUserIDFromContext(ctx); ok { event.UserID = &userID } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish work viewed event: %v", err) } // 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) } return &model.Work{ ID: id, Name: work.Title, Language: work.Language, Content: &content, }, 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) { translationID, err := strconv.ParseUint(id, 10, 32) if err != nil { return nil, fmt.Errorf("invalid translation ID: %v", err) } translation, err := r.App.TranslationRepo.GetByID(ctx, uint(translationID)) if err != nil { return nil, err } if translation == nil { return nil, nil } // Publish analytics event for translation view tID := uint(translationID) event := analytics.AnalyticsEvent{ EventType: analytics.EventTypeTranslationViewed, TranslationID: &tID, Timestamp: time.Now(), } if userID, ok := platform_auth.GetUserIDFromContext(ctx); ok { event.UserID = &userID } if err := r.App.AnalyticsPublisher.Publish(ctx, event); err != nil { log.Printf("failed to publish translation viewed event: %v", err) } return &model.Translation{ ID: fmt.Sprintf("%d", translation.ID), Name: translation.Title, Language: translation.Language, Content: &translation.Content, WorkID: fmt.Sprintf("%d", translation.TranslatableID), }, nil } // 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.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint)) } else { result, err := r.App.AuthorRepo.List(ctx, 1, 1000) // Use pagination 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.UserRepo.ListByRole(ctx, modelRole) } else { result, err := r.App.UserRepo.List(ctx, 1, 1000) // Use pagination 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.TagRepo.GetByID(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) { paginatedResult, err := r.App.TagRepo.List(ctx, 1, 1000) // Use pagination 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.CategoryRepo.GetByID(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) { paginatedResult, err := r.App.CategoryRepo.List(ctx, 1, 1000) 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")) } // PopularTranslations is the resolver for the popularTranslations field. func (r *queryResolver) PopularTranslations(ctx context.Context, workID string, limit *int) ([]*model.Translation, error) { wID, err := strconv.ParseUint(workID, 10, 32) if err != nil { return nil, fmt.Errorf("invalid work ID: %v", err) } l := 10 // default limit if limit != nil { l = *limit } translations, err := r.App.AnalyticsService.GetPopularTranslations(ctx, uint(wID), l) if err != nil { return nil, err } var result []*model.Translation for _, t := range translations { result = append(result, &model.Translation{ ID: fmt.Sprintf("%d", t.ID), Name: t.Title, Language: t.Language, Content: &t.Content, WorkID: workID, }) } return result, nil } // 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} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } // !!! 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) Work() WorkResolver { return &workResolver{r} } func (r *Resolver) Translation() TranslationResolver { return &translationResolver{r} } type workResolver struct{ *Resolver } 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 } */