mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
- Merged main branch improvements (better search structure with SearchResultItem) - Added SearchAlpha config parameter for hybrid search tuning (default: 0.7) - Updated NewWeaviateWrapper to accept host and searchAlpha parameters - Fixed all type mismatches in mocks and tests - Updated GraphQL resolver to use new SearchResults structure - All tests and vet checks passing
2103 lines
57 KiB
Go
2103 lines
57 KiB
Go
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
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"tercul/internal/adapters/graphql/model"
|
|
"tercul/internal/app/auth"
|
|
"tercul/internal/app/author"
|
|
"tercul/internal/app/book"
|
|
"tercul/internal/app/bookmark"
|
|
"tercul/internal/app/collection"
|
|
"tercul/internal/app/comment"
|
|
"tercul/internal/app/contribution"
|
|
"tercul/internal/app/like"
|
|
"tercul/internal/app/translation"
|
|
"tercul/internal/app/user"
|
|
"tercul/internal/domain"
|
|
domainsearch "tercul/internal/domain/search"
|
|
platform_auth "tercul/internal/platform/auth"
|
|
"tercul/internal/platform/log"
|
|
"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.Auth.Commands.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.Auth.Commands.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 := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
// Create domain model
|
|
workModel := &domain.Work{
|
|
Title: input.Name,
|
|
TranslatableModel: domain.TranslatableModel{Language: input.Language},
|
|
}
|
|
|
|
// Call work service
|
|
createdWork, err := r.App.Work.Commands.CreateWork(ctx, workModel)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if input.Content != nil && *input.Content != "" {
|
|
translationInput := translation.CreateOrUpdateTranslationInput{
|
|
Title: input.Name,
|
|
Content: *input.Content,
|
|
Language: input.Language,
|
|
TranslatableID: createdWork.ID,
|
|
TranslatableType: "works",
|
|
IsOriginalLanguage: true,
|
|
}
|
|
_, err := r.App.Translation.Commands.CreateOrUpdateTranslation(ctx, translationInput)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create initial translation: %w", err)
|
|
}
|
|
}
|
|
|
|
// Convert to GraphQL model
|
|
return &model.Work{
|
|
ID: fmt.Sprintf("%d", createdWork.ID),
|
|
Name: createdWork.Title,
|
|
Language: createdWork.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 := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
workID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid work ID", domain.ErrValidation)
|
|
}
|
|
|
|
// Create domain model
|
|
workModel := &domain.Work{
|
|
TranslatableModel: domain.TranslatableModel{
|
|
BaseModel: domain.BaseModel{ID: uint(workID)},
|
|
Language: input.Language,
|
|
},
|
|
Title: input.Name,
|
|
}
|
|
|
|
// Call work service
|
|
err = r.App.Work.Commands.UpdateWork(ctx, workModel)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert to GraphQL model
|
|
return &model.Work{
|
|
ID: id,
|
|
Name: workModel.Title,
|
|
Language: workModel.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("%w: invalid work ID", domain.ErrValidation)
|
|
}
|
|
|
|
err = r.App.Work.Commands.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 := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
workID, err := strconv.ParseUint(input.WorkID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid work ID", domain.ErrValidation)
|
|
}
|
|
|
|
var content string
|
|
if input.Content != nil {
|
|
content = *input.Content
|
|
}
|
|
|
|
createInput := translation.CreateOrUpdateTranslationInput{
|
|
Title: input.Name,
|
|
Content: content,
|
|
Language: input.Language,
|
|
TranslatableID: uint(workID),
|
|
TranslatableType: "works",
|
|
}
|
|
createdTranslation, err := r.App.Translation.Commands.CreateOrUpdateTranslation(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
go func() {
|
|
if err := r.App.Analytics.IncrementWorkTranslationCount(context.Background(), uint(workID)); err != nil {
|
|
log.Error(err, "failed to increment work translation count")
|
|
}
|
|
}()
|
|
|
|
return &model.Translation{
|
|
ID: fmt.Sprintf("%d", createdTranslation.ID),
|
|
Name: createdTranslation.Title,
|
|
Language: createdTranslation.Language,
|
|
Content: &createdTranslation.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 := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
workID, err := strconv.ParseUint(input.WorkID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid work ID", domain.ErrValidation)
|
|
}
|
|
|
|
var content string
|
|
if input.Content != nil {
|
|
content = *input.Content
|
|
}
|
|
|
|
updateInput := translation.CreateOrUpdateTranslationInput{
|
|
Title: input.Name,
|
|
Content: content,
|
|
Language: input.Language,
|
|
TranslatableID: uint(workID),
|
|
TranslatableType: "works",
|
|
}
|
|
updatedTranslation, err := r.App.Translation.Commands.CreateOrUpdateTranslation(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Translation{
|
|
ID: fmt.Sprintf("%d", updatedTranslation.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.Translation.Commands.DeleteTranslation(ctx, uint(translationID))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// CreateBook is the resolver for the createBook field.
|
|
func (r *mutationResolver) CreateBook(ctx context.Context, input model.BookInput) (*model.Book, error) {
|
|
if err := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
createInput := book.CreateBookInput{
|
|
Title: input.Name,
|
|
Description: *input.Description,
|
|
Language: input.Language,
|
|
ISBN: input.Isbn,
|
|
}
|
|
|
|
createdBook, err := r.App.Book.Commands.CreateBook(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Book{
|
|
ID: fmt.Sprintf("%d", createdBook.ID),
|
|
Name: createdBook.Title,
|
|
Language: createdBook.Language,
|
|
Description: &createdBook.Description,
|
|
Isbn: &createdBook.ISBN,
|
|
}, nil
|
|
}
|
|
|
|
// UpdateBook is the resolver for the updateBook field.
|
|
func (r *mutationResolver) UpdateBook(ctx context.Context, id string, input model.BookInput) (*model.Book, error) {
|
|
if err := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bookID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid book ID", domain.ErrValidation)
|
|
}
|
|
|
|
updateInput := book.UpdateBookInput{
|
|
ID: uint(bookID),
|
|
Title: &input.Name,
|
|
Description: input.Description,
|
|
Language: &input.Language,
|
|
ISBN: input.Isbn,
|
|
}
|
|
|
|
updatedBook, err := r.App.Book.Commands.UpdateBook(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Book{
|
|
ID: id,
|
|
Name: updatedBook.Title,
|
|
Language: updatedBook.Language,
|
|
Description: &updatedBook.Description,
|
|
Isbn: &updatedBook.ISBN,
|
|
}, nil
|
|
}
|
|
|
|
// DeleteBook is the resolver for the deleteBook field.
|
|
func (r *mutationResolver) DeleteBook(ctx context.Context, id string) (bool, error) {
|
|
bookID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("%w: invalid book ID", domain.ErrValidation)
|
|
}
|
|
|
|
err = r.App.Book.Commands.DeleteBook(ctx, uint(bookID))
|
|
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 := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
createInput := author.CreateAuthorInput{
|
|
Name: input.Name,
|
|
}
|
|
createdAuthor, err := r.App.Author.Commands.CreateAuthor(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Author{
|
|
ID: fmt.Sprintf("%d", createdAuthor.ID),
|
|
Name: createdAuthor.Name,
|
|
Language: createdAuthor.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 := Validate(input); err != nil {
|
|
return nil, 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,
|
|
}
|
|
updatedAuthor, err := r.App.Author.Commands.UpdateAuthor(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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.Author.Commands.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) {
|
|
if err := Validate(input); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
userID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid user ID: %v", err)
|
|
}
|
|
|
|
updateInput := user.UpdateUserInput{
|
|
ID: uint(userID),
|
|
Username: input.Username,
|
|
Email: input.Email,
|
|
Password: input.Password,
|
|
FirstName: input.FirstName,
|
|
LastName: input.LastName,
|
|
DisplayName: input.DisplayName,
|
|
Bio: input.Bio,
|
|
AvatarURL: input.AvatarURL,
|
|
Verified: input.Verified,
|
|
Active: input.Active,
|
|
}
|
|
|
|
if input.Role != nil {
|
|
role := domain.UserRole(input.Role.String())
|
|
updateInput.Role = &role
|
|
}
|
|
|
|
if input.CountryID != nil {
|
|
countryID, err := strconv.ParseUint(*input.CountryID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid country ID: %v", err)
|
|
}
|
|
uid := uint(countryID)
|
|
updateInput.CountryID = &uid
|
|
}
|
|
if input.CityID != nil {
|
|
cityID, err := strconv.ParseUint(*input.CityID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid city ID: %v", err)
|
|
}
|
|
uid := uint(cityID)
|
|
updateInput.CityID = &uid
|
|
}
|
|
if input.AddressID != nil {
|
|
addressID, err := strconv.ParseUint(*input.AddressID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid address ID: %v", err)
|
|
}
|
|
uid := uint(addressID)
|
|
updateInput.AddressID = &uid
|
|
}
|
|
|
|
updatedUser, err := r.App.User.Commands.UpdateUser(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.User{
|
|
ID: fmt.Sprintf("%d", updatedUser.ID),
|
|
Username: updatedUser.Username,
|
|
Email: updatedUser.Email,
|
|
FirstName: &updatedUser.FirstName,
|
|
LastName: &updatedUser.LastName,
|
|
DisplayName: &updatedUser.DisplayName,
|
|
Bio: &updatedUser.Bio,
|
|
AvatarURL: &updatedUser.AvatarURL,
|
|
Role: model.UserRole(updatedUser.Role),
|
|
Verified: updatedUser.Verified,
|
|
Active: updatedUser.Active,
|
|
}, nil
|
|
}
|
|
|
|
// DeleteUser is the resolver for the deleteUser field.
|
|
func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, error) {
|
|
userID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("%w: invalid user ID", domain.ErrValidation)
|
|
}
|
|
|
|
err = r.App.User.Commands.DeleteUser(ctx, uint(userID))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// CreateCollection is the resolver for the createCollection field.
|
|
func (r *mutationResolver) CreateCollection(ctx context.Context, input model.CollectionInput) (*model.Collection, error) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
createInput := collection.CreateCollectionInput{
|
|
Name: input.Name,
|
|
UserID: userID,
|
|
}
|
|
if input.Description != nil {
|
|
createInput.Description = *input.Description
|
|
}
|
|
createdCollection, err := r.App.Collection.Commands.CreateCollection(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Collection{
|
|
ID: fmt.Sprintf("%d", createdCollection.ID),
|
|
Name: createdCollection.Name,
|
|
Description: &createdCollection.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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
collectionID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid collection ID: %v", err)
|
|
}
|
|
|
|
updateInput := collection.UpdateCollectionInput{
|
|
ID: uint(collectionID),
|
|
Name: input.Name,
|
|
UserID: userID,
|
|
}
|
|
if input.Description != nil {
|
|
updateInput.Description = *input.Description
|
|
}
|
|
updatedCollection, err := r.App.Collection.Commands.UpdateCollection(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
collectionID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("invalid collection ID: %v", err)
|
|
}
|
|
|
|
err = r.App.Collection.Commands.DeleteCollection(ctx, uint(collectionID), userID)
|
|
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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
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,
|
|
}
|
|
err = r.App.Collection.Commands.AddWorkToCollection(ctx, addInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
updatedCollection, err := r.App.Collection.Queries.Collection(ctx, uint(collID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
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,
|
|
}
|
|
err = r.App.Collection.Commands.RemoveWorkFromCollection(ctx, removeInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
updatedCollection, err := r.App.Collection.Queries.Collection(ctx, uint(collID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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) {
|
|
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")
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
createdComment, err := r.App.Comment.Commands.CreateComment(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if createdComment.WorkID != nil {
|
|
if err := r.App.Analytics.IncrementWorkComments(ctx, *createdComment.WorkID); err != nil {
|
|
log.FromContext(ctx).Error(err, "failed to increment work comments")
|
|
}
|
|
}
|
|
if createdComment.TranslationID != nil {
|
|
if err := r.App.Analytics.IncrementTranslationComments(ctx, *createdComment.TranslationID); err != nil {
|
|
log.FromContext(ctx).Error(err, "failed to increment translation comments")
|
|
}
|
|
}
|
|
|
|
return &model.Comment{
|
|
ID: fmt.Sprintf("%d", createdComment.ID),
|
|
Text: createdComment.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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
commentID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid comment ID: %v", err)
|
|
}
|
|
|
|
commentModel, err := r.App.Comment.Queries.Comment(ctx, uint(commentID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if commentModel == nil {
|
|
return nil, fmt.Errorf("comment not found")
|
|
}
|
|
|
|
if commentModel.UserID != userID {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
updateInput := comment.UpdateCommentInput{
|
|
ID: uint(commentID),
|
|
Text: input.Text,
|
|
}
|
|
updatedComment, err := r.App.Comment.Commands.UpdateComment(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
commentID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("invalid comment ID: %v", err)
|
|
}
|
|
|
|
comment, err := r.App.Comment.Queries.Comment(ctx, uint(commentID))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if comment == nil {
|
|
return false, fmt.Errorf("comment not found")
|
|
}
|
|
|
|
if comment.UserID != userID {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
err = r.App.Comment.Commands.DeleteComment(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) {
|
|
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")
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
createdLike, err := r.App.Like.Commands.CreateLike(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if createdLike.WorkID != nil {
|
|
if err := r.App.Analytics.IncrementWorkLikes(ctx, *createdLike.WorkID); err != nil {
|
|
log.FromContext(ctx).Error(err, "failed to increment work likes")
|
|
}
|
|
}
|
|
if createdLike.TranslationID != nil {
|
|
if err := r.App.Analytics.IncrementTranslationLikes(ctx, *createdLike.TranslationID); err != nil {
|
|
log.FromContext(ctx).Error(err, "failed to increment translation likes")
|
|
}
|
|
}
|
|
|
|
return &model.Like{
|
|
ID: fmt.Sprintf("%d", createdLike.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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
likeID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("invalid like ID: %v", err)
|
|
}
|
|
|
|
like, err := r.App.Like.Queries.Like(ctx, uint(likeID))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if like == nil {
|
|
return false, fmt.Errorf("like not found")
|
|
}
|
|
|
|
if like.UserID != userID {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
err = r.App.Like.Commands.DeleteLike(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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
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),
|
|
}
|
|
if input.Name != nil {
|
|
createInput.Name = *input.Name
|
|
}
|
|
|
|
createdBookmark, err := r.App.Bookmark.Commands.CreateBookmark(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := r.App.Analytics.IncrementWorkBookmarks(ctx, uint(workID)); err != nil {
|
|
log.FromContext(ctx).Error(err, "failed to increment work bookmarks")
|
|
}
|
|
|
|
return &model.Bookmark{
|
|
ID: fmt.Sprintf("%d", createdBookmark.ID),
|
|
Name: &createdBookmark.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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
bookmarkID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("invalid bookmark ID: %v", err)
|
|
}
|
|
|
|
bookmark, err := r.App.Bookmark.Queries.Bookmark(ctx, uint(bookmarkID))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if bookmark == nil {
|
|
return false, fmt.Errorf("bookmark not found")
|
|
}
|
|
|
|
if bookmark.UserID != userID {
|
|
return false, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
err = r.App.Bookmark.Commands.DeleteBookmark(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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
createInput := contribution.CreateContributionInput{
|
|
Name: input.Name,
|
|
}
|
|
|
|
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.Status != nil {
|
|
createInput.Status = input.Status.String()
|
|
} else {
|
|
createInput.Status = "DRAFT" // Default status
|
|
}
|
|
|
|
createdContribution, err := r.App.Contribution.Commands.CreateContribution(ctx, createInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Contribution{
|
|
ID: fmt.Sprintf("%d", createdContribution.ID),
|
|
Name: createdContribution.Name,
|
|
Status: model.ContributionStatus(createdContribution.Status),
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", userID),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// UpdateContribution is the resolver for the updateContribution field.
|
|
func (r *mutationResolver) UpdateContribution(ctx context.Context, id string, input model.ContributionInput) (*model.Contribution, error) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, domain.ErrUnauthorized
|
|
}
|
|
|
|
contributionID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid contribution ID", domain.ErrValidation)
|
|
}
|
|
|
|
updateInput := contribution.UpdateContributionInput{
|
|
ID: uint(contributionID),
|
|
UserID: userID,
|
|
Name: &input.Name,
|
|
}
|
|
|
|
if input.Status != nil {
|
|
status := input.Status.String()
|
|
updateInput.Status = &status
|
|
}
|
|
|
|
updatedContribution, err := r.App.Contribution.Commands.UpdateContribution(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Contribution{
|
|
ID: fmt.Sprintf("%d", updatedContribution.ID),
|
|
Name: updatedContribution.Name,
|
|
Status: model.ContributionStatus(updatedContribution.Status),
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", updatedContribution.UserID),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// DeleteContribution is the resolver for the deleteContribution field.
|
|
func (r *mutationResolver) DeleteContribution(ctx context.Context, id string) (bool, error) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return false, domain.ErrUnauthorized
|
|
}
|
|
|
|
contributionID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return false, fmt.Errorf("%w: invalid contribution ID", domain.ErrValidation)
|
|
}
|
|
|
|
err = r.App.Contribution.Commands.DeleteContribution(ctx, uint(contributionID), userID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// 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) {
|
|
contributionID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid contribution ID", domain.ErrValidation)
|
|
}
|
|
|
|
reviewInput := contribution.ReviewContributionInput{
|
|
ID: uint(contributionID),
|
|
Status: status.String(),
|
|
Feedback: feedback,
|
|
}
|
|
|
|
reviewedContribution, err := r.App.Contribution.Commands.ReviewContribution(ctx, reviewInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.Contribution{
|
|
ID: fmt.Sprintf("%d", reviewedContribution.ID),
|
|
Name: reviewedContribution.Name,
|
|
Status: model.ContributionStatus(reviewedContribution.Status),
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", reviewedContribution.UserID),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Logout is the resolver for the logout field.
|
|
func (r *mutationResolver) Logout(ctx context.Context) (bool, error) {
|
|
err := r.App.Auth.Commands.Logout(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// RefreshToken is the resolver for the refreshToken field.
|
|
func (r *mutationResolver) RefreshToken(ctx context.Context) (*model.AuthPayload, error) {
|
|
authResponse, err := r.App.Auth.Commands.RefreshToken(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// ForgotPassword is the resolver for the forgotPassword field.
|
|
func (r *mutationResolver) ForgotPassword(ctx context.Context, email string) (bool, error) {
|
|
err := r.App.Auth.Commands.ForgotPassword(ctx, email)
|
|
if err != nil {
|
|
return true, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// ResetPassword is the resolver for the resetPassword field.
|
|
func (r *mutationResolver) ResetPassword(ctx context.Context, token string, newPassword string) (bool, error) {
|
|
resetInput := auth.ResetPasswordInput{
|
|
Token: token,
|
|
NewPassword: newPassword,
|
|
}
|
|
err := r.App.Auth.Commands.ResetPassword(ctx, resetInput)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// VerifyEmail is the resolver for the verifyEmail field.
|
|
func (r *mutationResolver) VerifyEmail(ctx context.Context, token string) (bool, error) {
|
|
err := r.App.Auth.Commands.VerifyEmail(ctx, token)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// ResendVerificationEmail is the resolver for the resendVerificationEmail field.
|
|
func (r *mutationResolver) ResendVerificationEmail(ctx context.Context, email string) (bool, error) {
|
|
err := r.App.Auth.Commands.ResendVerificationEmail(ctx, email)
|
|
if err != nil {
|
|
return true, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// UpdateProfile is the resolver for the updateProfile field.
|
|
func (r *mutationResolver) UpdateProfile(ctx context.Context, input model.UserInput) (*model.User, error) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, domain.ErrUnauthorized
|
|
}
|
|
|
|
updateInput := user.UpdateUserInput{
|
|
ID: userID,
|
|
FirstName: input.FirstName,
|
|
LastName: input.LastName,
|
|
DisplayName: input.DisplayName,
|
|
Bio: input.Bio,
|
|
AvatarURL: input.AvatarURL,
|
|
}
|
|
|
|
if input.CountryID != nil {
|
|
countryID, err := strconv.ParseUint(*input.CountryID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid country ID: %v", err)
|
|
}
|
|
uid := uint(countryID)
|
|
updateInput.CountryID = &uid
|
|
}
|
|
if input.CityID != nil {
|
|
cityID, err := strconv.ParseUint(*input.CityID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid city ID: %v", err)
|
|
}
|
|
uid := uint(cityID)
|
|
updateInput.CityID = &uid
|
|
}
|
|
if input.AddressID != nil {
|
|
addressID, err := strconv.ParseUint(*input.AddressID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid address ID: %v", err)
|
|
}
|
|
uid := uint(addressID)
|
|
updateInput.AddressID = &uid
|
|
}
|
|
|
|
updatedUser, err := r.App.User.Commands.UpdateUser(ctx, updateInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &model.User{
|
|
ID: fmt.Sprintf("%d", updatedUser.ID),
|
|
Username: updatedUser.Username,
|
|
Email: updatedUser.Email,
|
|
FirstName: &updatedUser.FirstName,
|
|
LastName: &updatedUser.LastName,
|
|
DisplayName: &updatedUser.DisplayName,
|
|
Bio: &updatedUser.Bio,
|
|
AvatarURL: &updatedUser.AvatarURL,
|
|
Role: model.UserRole(updatedUser.Role),
|
|
Verified: updatedUser.Verified,
|
|
Active: updatedUser.Active,
|
|
}, nil
|
|
}
|
|
|
|
// ChangePassword is the resolver for the changePassword field.
|
|
func (r *mutationResolver) ChangePassword(ctx context.Context, currentPassword string, newPassword string) (bool, error) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return false, domain.ErrUnauthorized
|
|
}
|
|
|
|
changeInput := auth.ChangePasswordInput{
|
|
UserID: userID,
|
|
CurrentPassword: currentPassword,
|
|
NewPassword: newPassword,
|
|
}
|
|
|
|
err := r.App.Auth.Commands.ChangePassword(ctx, changeInput)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
workDTO, err := r.App.Work.Queries.GetWorkByID(ctx, uint(workID))
|
|
if err != nil {
|
|
if errors.Is(err, domain.ErrEntityNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
go func() {
|
|
if err := r.App.Analytics.IncrementWorkViews(context.Background(), uint(workID)); err != nil {
|
|
log.Error(err, "failed to increment work views")
|
|
}
|
|
}()
|
|
|
|
content := r.resolveWorkContent(ctx, workDTO.ID, workDTO.Language)
|
|
|
|
return &model.Work{
|
|
ID: id,
|
|
Name: workDTO.Title,
|
|
Language: workDTO.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) {
|
|
page := 1
|
|
pageSize := 20
|
|
if limit != nil {
|
|
pageSize = int(*limit)
|
|
}
|
|
if offset != nil {
|
|
page = int(*offset)/pageSize + 1
|
|
}
|
|
|
|
paginatedResult, err := r.App.Work.Queries.ListWorks(ctx, page, pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Work
|
|
for _, w := range paginatedResult.Items {
|
|
content := r.resolveWorkContent(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)
|
|
}
|
|
|
|
translationDTO, err := r.App.Translation.Queries.Translation(ctx, uint(translationID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if translationDTO == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
go func() {
|
|
if err := r.App.Analytics.IncrementTranslationViews(context.Background(), uint(translationID)); err != nil {
|
|
log.Error(err, "failed to increment translation views")
|
|
}
|
|
}()
|
|
|
|
return &model.Translation{
|
|
ID: id,
|
|
Name: translationDTO.Title,
|
|
Language: translationDTO.Language,
|
|
Content: &translationDTO.Content,
|
|
WorkID: fmt.Sprintf("%d", translationDTO.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) {
|
|
wID, err := strconv.ParseUint(workID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid work ID", domain.ErrValidation)
|
|
}
|
|
|
|
page := 1
|
|
pageSize := 20
|
|
if limit != nil {
|
|
pageSize = int(*limit)
|
|
}
|
|
if offset != nil {
|
|
page = int(*offset)/pageSize + 1
|
|
}
|
|
|
|
paginatedResult, err := r.App.Translation.Queries.ListTranslations(ctx, uint(wID), language, page, pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Translation
|
|
for _, t := range paginatedResult.Items {
|
|
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
|
|
}
|
|
|
|
// Book is the resolver for the book field.
|
|
func (r *queryResolver) Book(ctx context.Context, id string) (*model.Book, error) {
|
|
bookID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid book ID", domain.ErrValidation)
|
|
}
|
|
|
|
bookRecord, err := r.App.Book.Queries.Book(ctx, uint(bookID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if bookRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &model.Book{
|
|
ID: fmt.Sprintf("%d", bookRecord.ID),
|
|
Name: bookRecord.Title,
|
|
Language: bookRecord.Language,
|
|
Description: &bookRecord.Description,
|
|
Isbn: &bookRecord.ISBN,
|
|
}, nil
|
|
}
|
|
|
|
// Books is the resolver for the books field.
|
|
func (r *queryResolver) Books(ctx context.Context, limit *int32, offset *int32) ([]*model.Book, error) {
|
|
books, err := r.App.Book.Queries.Books(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Book
|
|
for _, b := range books {
|
|
result = append(result, &model.Book{
|
|
ID: fmt.Sprintf("%d", b.ID),
|
|
Name: b.Title,
|
|
Language: b.Language,
|
|
Description: &b.Description,
|
|
Isbn: &b.ISBN,
|
|
})
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Author is the resolver for the author field.
|
|
func (r *queryResolver) Author(ctx context.Context, id string) (*model.Author, error) {
|
|
authorID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid author ID", domain.ErrValidation)
|
|
}
|
|
|
|
authorRecord, err := r.App.Author.Queries.Author(ctx, uint(authorID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if authorRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var bio *string
|
|
authorWithTranslations, err := r.App.Author.Queries.AuthorWithTranslations(ctx, authorRecord.ID)
|
|
if err == nil && authorWithTranslations != nil {
|
|
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.
|
|
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
|
|
var countryIDUint *uint
|
|
|
|
if countryID != nil {
|
|
parsedID, err := strconv.ParseUint(*countryID, 10, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
uid := uint(parsedID)
|
|
countryIDUint = &uid
|
|
}
|
|
|
|
authors, err = r.App.Author.Queries.Authors(ctx, countryIDUint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Author
|
|
for _, a := range authors {
|
|
var bio *string
|
|
authorWithTranslations, err := r.App.Author.Queries.AuthorWithTranslations(ctx, a.ID)
|
|
if err == nil && authorWithTranslations != nil {
|
|
biography, err := r.App.Localization.Queries.GetAuthorBiography(ctx, a.ID, a.Language)
|
|
if err == nil && biography != "" {
|
|
bio = &biography
|
|
}
|
|
}
|
|
|
|
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) {
|
|
userID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid user ID", domain.ErrValidation)
|
|
}
|
|
|
|
userRecord, err := r.App.User.Queries.User(ctx, uint(userID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &model.User{
|
|
ID: fmt.Sprintf("%d", userRecord.ID),
|
|
Username: userRecord.Username,
|
|
Email: userRecord.Email,
|
|
FirstName: &userRecord.FirstName,
|
|
LastName: &userRecord.LastName,
|
|
DisplayName: &userRecord.DisplayName,
|
|
Bio: &userRecord.Bio,
|
|
AvatarURL: &userRecord.AvatarURL,
|
|
Role: model.UserRole(userRecord.Role),
|
|
Verified: userRecord.Verified,
|
|
Active: userRecord.Active,
|
|
}, nil
|
|
}
|
|
|
|
// UserByEmail is the resolver for the userByEmail field.
|
|
func (r *queryResolver) UserByEmail(ctx context.Context, email string) (*model.User, error) {
|
|
userRecord, err := r.App.User.Queries.UserByEmail(ctx, email)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &model.User{
|
|
ID: fmt.Sprintf("%d", userRecord.ID),
|
|
Username: userRecord.Username,
|
|
Email: userRecord.Email,
|
|
FirstName: &userRecord.FirstName,
|
|
LastName: &userRecord.LastName,
|
|
DisplayName: &userRecord.DisplayName,
|
|
Bio: &userRecord.Bio,
|
|
AvatarURL: &userRecord.AvatarURL,
|
|
Role: model.UserRole(userRecord.Role),
|
|
Verified: userRecord.Verified,
|
|
Active: userRecord.Active,
|
|
}, nil
|
|
}
|
|
|
|
// UserByUsername is the resolver for the userByUsername field.
|
|
func (r *queryResolver) UserByUsername(ctx context.Context, username string) (*model.User, error) {
|
|
userRecord, err := r.App.User.Queries.UserByUsername(ctx, username)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &model.User{
|
|
ID: fmt.Sprintf("%d", userRecord.ID),
|
|
Username: userRecord.Username,
|
|
Email: userRecord.Email,
|
|
FirstName: &userRecord.FirstName,
|
|
LastName: &userRecord.LastName,
|
|
DisplayName: &userRecord.DisplayName,
|
|
Bio: &userRecord.Bio,
|
|
AvatarURL: &userRecord.AvatarURL,
|
|
Role: model.UserRole(userRecord.Role),
|
|
Verified: userRecord.Verified,
|
|
Active: userRecord.Active,
|
|
}, nil
|
|
}
|
|
|
|
// 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 {
|
|
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.User.Queries.UsersByRole(ctx, modelRole)
|
|
} else {
|
|
users, err = r.App.User.Queries.Users(ctx)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.User
|
|
for _, u := range users {
|
|
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) {
|
|
userID, ok := platform_auth.GetUserIDFromContext(ctx)
|
|
if !ok {
|
|
return nil, domain.ErrUnauthorized
|
|
}
|
|
|
|
userRecord, err := r.App.User.Queries.User(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if userRecord == nil {
|
|
return nil, domain.ErrUserNotFound
|
|
}
|
|
|
|
return &model.User{
|
|
ID: fmt.Sprintf("%d", userRecord.ID),
|
|
Username: userRecord.Username,
|
|
Email: userRecord.Email,
|
|
FirstName: &userRecord.FirstName,
|
|
LastName: &userRecord.LastName,
|
|
DisplayName: &userRecord.DisplayName,
|
|
Bio: &userRecord.Bio,
|
|
AvatarURL: &userRecord.AvatarURL,
|
|
Role: model.UserRole(userRecord.Role),
|
|
Verified: userRecord.Verified,
|
|
Active: userRecord.Active,
|
|
}, nil
|
|
}
|
|
|
|
// UserProfile is the resolver for the userProfile field.
|
|
func (r *queryResolver) UserProfile(ctx context.Context, userID string) (*model.UserProfile, error) {
|
|
uID, err := strconv.ParseUint(userID, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid user ID", domain.ErrValidation)
|
|
}
|
|
|
|
profile, err := r.App.User.Queries.UserProfile(ctx, uint(uID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if profile == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
user, err := r.App.User.Queries.User(ctx, uint(uID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if user == nil {
|
|
return nil, fmt.Errorf("user not found for profile %d", profile.ID)
|
|
}
|
|
|
|
return &model.UserProfile{
|
|
ID: fmt.Sprintf("%d", profile.ID),
|
|
UserID: userID,
|
|
User: &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,
|
|
},
|
|
PhoneNumber: &profile.PhoneNumber,
|
|
Website: &profile.Website,
|
|
Twitter: &profile.Twitter,
|
|
Facebook: &profile.Facebook,
|
|
LinkedIn: &profile.LinkedIn,
|
|
Github: &profile.Github,
|
|
}, nil
|
|
}
|
|
|
|
// Collection is the resolver for the collection field.
|
|
func (r *queryResolver) Collection(ctx context.Context, id string) (*model.Collection, error) {
|
|
collID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid collection ID", domain.ErrValidation)
|
|
}
|
|
|
|
collectionRecord, err := r.App.Collection.Queries.Collection(ctx, uint(collID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if collectionRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
workRecords, err := r.App.Work.Queries.ListByCollectionID(ctx, uint(collID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var works []*model.Work
|
|
for _, w := range workRecords {
|
|
content := r.resolveWorkContent(ctx, w.ID, w.Language)
|
|
works = append(works, &model.Work{
|
|
ID: fmt.Sprintf("%d", w.ID),
|
|
Name: w.Title,
|
|
Language: w.Language,
|
|
Content: content,
|
|
})
|
|
}
|
|
|
|
return &model.Collection{
|
|
ID: fmt.Sprintf("%d", collectionRecord.ID),
|
|
Name: collectionRecord.Name,
|
|
Description: &collectionRecord.Description,
|
|
Works: works,
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", collectionRecord.UserID),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Collections is the resolver for the collections field.
|
|
func (r *queryResolver) Collections(ctx context.Context, userID *string, limit *int32, offset *int32) ([]*model.Collection, error) {
|
|
var collectionRecords []domain.Collection
|
|
var err error
|
|
|
|
if userID != nil {
|
|
uID, idErr := strconv.ParseUint(*userID, 10, 32)
|
|
if idErr != nil {
|
|
return nil, fmt.Errorf("%w: invalid user ID", domain.ErrValidation)
|
|
}
|
|
collectionRecords, err = r.App.Collection.Queries.CollectionsByUserID(ctx, uint(uID))
|
|
} else {
|
|
collectionRecords, err = r.App.Collection.Queries.PublicCollections(ctx)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
start := 0
|
|
if offset != nil {
|
|
start = int(*offset)
|
|
}
|
|
end := len(collectionRecords)
|
|
if limit != nil {
|
|
end = start + int(*limit)
|
|
if end > len(collectionRecords) {
|
|
end = len(collectionRecords)
|
|
}
|
|
}
|
|
if start > len(collectionRecords) {
|
|
start = len(collectionRecords)
|
|
}
|
|
paginatedRecords := collectionRecords[start:end]
|
|
|
|
var result []*model.Collection
|
|
for _, c := range paginatedRecords {
|
|
result = append(result, &model.Collection{
|
|
ID: fmt.Sprintf("%d", c.ID),
|
|
Name: c.Name,
|
|
Description: &c.Description,
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", c.UserID),
|
|
},
|
|
})
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// 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.Tag.Queries.Tag(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) {
|
|
tags, err := r.App.Tag.Queries.Tags(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Tag
|
|
for _, t := range tags {
|
|
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, fmt.Errorf("invalid category ID: %v", err)
|
|
}
|
|
|
|
category, err := r.App.Category.Queries.Category(ctx, uint(categoryID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if category == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
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) {
|
|
categories, err := r.App.Category.Queries.Categories(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Category
|
|
for _, c := range categories {
|
|
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) {
|
|
cID, err := strconv.ParseUint(id, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid comment ID", domain.ErrValidation)
|
|
}
|
|
|
|
commentRecord, err := r.App.Comment.Queries.Comment(ctx, uint(cID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if commentRecord == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &model.Comment{
|
|
ID: fmt.Sprintf("%d", commentRecord.ID),
|
|
Text: commentRecord.Text,
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", commentRecord.UserID),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// 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) {
|
|
var commentRecords []domain.Comment
|
|
var err error
|
|
|
|
if workID != nil {
|
|
wID, idErr := strconv.ParseUint(*workID, 10, 32)
|
|
if idErr != nil {
|
|
return nil, fmt.Errorf("%w: invalid work ID", domain.ErrValidation)
|
|
}
|
|
commentRecords, err = r.App.Comment.Queries.CommentsByWorkID(ctx, uint(wID))
|
|
} else if translationID != nil {
|
|
tID, idErr := strconv.ParseUint(*translationID, 10, 32)
|
|
if idErr != nil {
|
|
return nil, fmt.Errorf("%w: invalid translation ID", domain.ErrValidation)
|
|
}
|
|
commentRecords, err = r.App.Comment.Queries.CommentsByTranslationID(ctx, uint(tID))
|
|
} else if userID != nil {
|
|
uID, idErr := strconv.ParseUint(*userID, 10, 32)
|
|
if idErr != nil {
|
|
return nil, fmt.Errorf("%w: invalid user ID", domain.ErrValidation)
|
|
}
|
|
commentRecords, err = r.App.Comment.Queries.CommentsByUserID(ctx, uint(uID))
|
|
} else {
|
|
commentRecords, err = r.App.Comment.Queries.Comments(ctx)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
start := 0
|
|
if offset != nil {
|
|
start = int(*offset)
|
|
}
|
|
end := len(commentRecords)
|
|
if limit != nil {
|
|
end = start + int(*limit)
|
|
if end > len(commentRecords) {
|
|
end = len(commentRecords)
|
|
}
|
|
}
|
|
if start > len(commentRecords) {
|
|
start = len(commentRecords)
|
|
}
|
|
paginatedRecords := commentRecords[start:end]
|
|
|
|
var result []*model.Comment
|
|
for _, c := range paginatedRecords {
|
|
result = append(result, &model.Comment{
|
|
ID: fmt.Sprintf("%d", c.ID),
|
|
Text: c.Text,
|
|
User: &model.User{
|
|
ID: fmt.Sprintf("%d", c.UserID),
|
|
},
|
|
})
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// 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) {
|
|
page := 1
|
|
pageSize := 20
|
|
if limit != nil {
|
|
pageSize = int(*limit)
|
|
}
|
|
if offset != nil {
|
|
page = int(*offset)/pageSize + 1
|
|
}
|
|
|
|
var searchFilters domain.SearchFilters
|
|
if filters != nil {
|
|
searchFilters.Languages = filters.Languages
|
|
searchFilters.Categories = filters.Categories
|
|
searchFilters.Tags = filters.Tags
|
|
searchFilters.Authors = filters.Authors
|
|
|
|
if filters.DateFrom != nil {
|
|
t, err := time.Parse(time.RFC3339, *filters.DateFrom)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid DateFrom format: %w", err)
|
|
}
|
|
searchFilters.DateFrom = &t
|
|
}
|
|
if filters.DateTo != nil {
|
|
t, err := time.Parse(time.RFC3339, *filters.DateTo)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid DateTo format: %w", err)
|
|
}
|
|
searchFilters.DateTo = &t
|
|
}
|
|
}
|
|
|
|
params := domainsearch.SearchParams{
|
|
Query: query,
|
|
Filters: domainsearch.SearchFilters{
|
|
Languages: searchFilters.Languages,
|
|
Tags: searchFilters.Tags,
|
|
Categories: searchFilters.Categories,
|
|
Authors: searchFilters.Authors,
|
|
DateFrom: searchFilters.DateFrom,
|
|
DateTo: searchFilters.DateTo,
|
|
},
|
|
Limit: pageSize,
|
|
Offset: (page - 1) * pageSize,
|
|
}
|
|
results, err := r.App.Search.Search(ctx, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var works []*model.Work
|
|
var translations []*model.Translation
|
|
var authors []*model.Author
|
|
|
|
for _, item := range results.Results {
|
|
switch item.Type {
|
|
case "Work":
|
|
if work, ok := item.Entity.(domain.Work); ok {
|
|
works = append(works, &model.Work{
|
|
ID: fmt.Sprintf("%d", work.ID),
|
|
Name: work.Title,
|
|
Language: work.Language,
|
|
})
|
|
}
|
|
case "Translation":
|
|
if translation, ok := item.Entity.(domain.Translation); ok {
|
|
translations = append(translations, &model.Translation{
|
|
ID: fmt.Sprintf("%d", translation.ID),
|
|
Name: translation.Title,
|
|
Language: translation.Language,
|
|
Content: &translation.Content,
|
|
WorkID: fmt.Sprintf("%d", translation.TranslatableID),
|
|
})
|
|
}
|
|
case "Author":
|
|
if author, ok := item.Entity.(domain.Author); ok {
|
|
authors = append(authors, &model.Author{
|
|
ID: fmt.Sprintf("%d", author.ID),
|
|
Name: author.Name,
|
|
Language: author.Language,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return &model.SearchResults{
|
|
Works: works,
|
|
Translations: translations,
|
|
Authors: authors,
|
|
Total: int32(results.TotalResults),
|
|
}, 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)
|
|
}
|
|
|
|
workRecords, err := r.App.Analytics.GetTrendingWorks(ctx, tp, l)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*model.Work
|
|
for _, w := range workRecords {
|
|
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 }
|