ozon-task/internal/storage/memory.go
erius 8fce488888 Implemented pagination for posts, comments and replies
TODO: use dataloaders to reduce amount of sql queries (figure out how to batch query nested paginated data) and add some basic unit or integrated testing
2024-06-27 04:28:18 +03:00

157 lines
4.1 KiB
Go

package storage
import (
"context"
"fmt"
"git.obamna.ru/erius/ozon-task/graph/model"
)
type InMemory struct {
postId uint
commentId uint
posts []*model.Post
comments []*model.Comment
}
func InitInMemory() *InMemory {
return &InMemory{
postId: 0,
commentId: 0,
posts: make([]*model.Post, 0),
comments: make([]*model.Comment, 0),
}
}
func (s *InMemory) AddPost(input *model.PostInput) (*model.AddResult, error) {
post := model.PostFromInput(input)
s.insertPost(post)
return &model.AddResult{ItemID: &post.ID}, nil
}
func (s *InMemory) AddReplyToComment(input *model.CommentInput) (*model.AddResult, error) {
if !s.commentExists(*input.ParentCommentID) {
return nil, &IDNotFoundError{objName: "comment", id: *input.ParentCommentID}
}
comment, parent := model.CommentFromInput(input), s.comments[*input.ParentCommentID]
s.insertComment(comment)
parent.Replies = append(parent.Replies, comment)
return &model.AddResult{ItemID: &comment.ID}, nil
}
func (s *InMemory) AddCommentToPost(input *model.CommentInput) (*model.AddResult, error) {
if !s.postExists(*input.ParentPostID) {
return nil, &IDNotFoundError{objName: "post", id: *input.ParentPostID}
}
parent := s.posts[*input.ParentPostID]
if !parent.AllowComments {
return nil, fmt.Errorf("author disabled comments for this post")
}
comment := model.CommentFromInput(input)
comment.RootComment = true
s.insertComment(comment)
parent.Comments = append(parent.Comments, comment)
return &model.AddResult{ItemID: &comment.ID}, nil
}
func (s *InMemory) GetPost(id uint, ctx context.Context) (*model.Post, error) {
if !s.postExists(id) {
return nil, &IDNotFoundError{objName: "post", id: id}
}
return s.posts[id], nil
}
func (s *InMemory) GetComment(id uint, ctx context.Context) (*model.Comment, error) {
if !s.commentExists(id) {
return nil, &IDNotFoundError{objName: "comment", id: id}
}
return s.comments[id], nil
}
func (s *InMemory) GetPosts(first uint, cursor *uint, ctx context.Context) (*model.PostsConnection, error) {
start := uint(0)
if cursor != nil {
start = *cursor + 1
}
if !s.postExists(start) {
return &model.EmptyPostsConnections, nil
}
nextPage, until := true, start+first
if !s.postExists(until) {
nextPage = false
until = uint(len(s.posts))
}
info, edges := &model.PageInfo{
StartCursor: start,
EndCursor: until - 1,
HasNextPage: nextPage,
}, make([]*model.PostsEdge, until-start)
for i, p := range s.posts[start:until] {
edges[i] = &model.PostsEdge{
Cursor: p.ID,
Node: p,
}
}
return &model.PostsConnection{
Edges: edges,
PageInfo: info,
}, nil
}
func (s *InMemory) GetComments(post *model.Post, first uint, cursor *uint, ctx context.Context) (*model.CommentsConnection, error) {
return getCommentsFrom(post.Comments, first, cursor), nil
}
func (s *InMemory) GetReplies(comment *model.Comment, first uint, cursor *uint, ctx context.Context) (*model.CommentsConnection, error) {
return getCommentsFrom(comment.Replies, first, cursor), nil
}
func getCommentsFrom(source []*model.Comment, first uint, cursor *uint) *model.CommentsConnection {
start := uint(0)
if cursor != nil {
start = *cursor + 1
}
if start >= uint(len(source)) {
return &model.EmptyCommentsConnection
}
nextPage, until := true, start+first
if until >= uint(len(source)) {
nextPage = false
until = uint(len(source))
}
info, edges := &model.PageInfo{
StartCursor: start,
EndCursor: until - 1,
HasNextPage: nextPage,
}, make([]*model.CommentsEdge, until-start)
for i, c := range source[start:until] {
edges[i] = &model.CommentsEdge{
Cursor: c.ID,
Node: c,
}
}
return &model.CommentsConnection{
Edges: edges,
PageInfo: info,
}
}
func (s *InMemory) postExists(id uint) bool {
return id < uint(len(s.posts))
}
func (s *InMemory) commentExists(id uint) bool {
return id < uint(len(s.comments))
}
func (s *InMemory) insertComment(comment *model.Comment) {
comment.ID = s.commentId
s.comments = append(s.comments, comment)
s.commentId++
}
func (s *InMemory) insertPost(post *model.Post) {
post.ID = s.postId
s.posts = append(s.posts, post)
s.postId++
}