You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
6.2 KiB
245 lines
6.2 KiB
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore"
|
|
"git.aiterp.net/rpdata/api/internal/generate"
|
|
"git.aiterp.net/rpdata/api/models"
|
|
)
|
|
|
|
type storyRepository struct {
|
|
insertWithIDs bool
|
|
db *sql.DB
|
|
}
|
|
|
|
func (r *storyRepository) Find(ctx context.Context, id string) (*models.Story, error) {
|
|
q := psqlcore.New(r.db)
|
|
story, err := q.SelectStory(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tags, err := q.SelectTagsByTarget(ctx, psqlcore.SelectTagsByTargetParams{
|
|
TargetKind: "Story",
|
|
TargetID: story.ID,
|
|
})
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return nil, err
|
|
}
|
|
|
|
return r.story(story, tags), nil
|
|
}
|
|
|
|
func (r *storyRepository) List(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) {
|
|
q := psqlcore.New(r.db)
|
|
params := psqlcore.SelectStoriesParams{LimitSize: 100}
|
|
|
|
if len(filter.Tags) > 0 {
|
|
targets, err := q.SelectTargetsByTags(ctx, psqlcore.SelectTargetsByTagsParams{
|
|
TagNames: models.EncodeTagArray(filter.Tags),
|
|
TargetKind: "Story",
|
|
})
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return nil, err
|
|
}
|
|
|
|
if len(params.Ids) == 0 {
|
|
return []*models.Story{}, nil
|
|
}
|
|
|
|
params.FilterID = true
|
|
params.Ids = targets
|
|
}
|
|
if filter.Author != nil {
|
|
params.FilterAuthor = true
|
|
params.Author = *filter.Author
|
|
}
|
|
if filter.Category != nil {
|
|
params.FilterCategory = true
|
|
params.Category = string(*filter.Category)
|
|
}
|
|
if !filter.EarliestFictionalDate.IsZero() {
|
|
params.FilterEarlistFictionalDate = true
|
|
params.EarliestFictionalDate = filter.EarliestFictionalDate.UTC()
|
|
}
|
|
if !filter.LatestFictionalDate.IsZero() {
|
|
params.FilterLastestFictionalDate = true
|
|
params.LatestFictionalDate = filter.LatestFictionalDate.UTC()
|
|
}
|
|
if filter.Open != nil {
|
|
params.FilterOpen = true
|
|
params.Open = *filter.Open
|
|
}
|
|
if filter.Unlisted != nil {
|
|
params.FilterUnlisted = true
|
|
params.Unlisted = *filter.Unlisted
|
|
}
|
|
if filter.Limit <= 0 {
|
|
params.LimitSize = 1000
|
|
}
|
|
|
|
stories, err := q.SelectStories(ctx, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
targetIDs := make([]string, len(stories))
|
|
for i, story := range stories {
|
|
targetIDs[i] = story.ID
|
|
}
|
|
tags, err := q.SelectTagsByTargets(ctx, psqlcore.SelectTagsByTargetsParams{
|
|
TargetKind: "Story",
|
|
TargetIds: targetIDs,
|
|
})
|
|
|
|
return r.stories(stories, tags), nil
|
|
}
|
|
|
|
func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
q := psqlcore.New(tx)
|
|
|
|
if !r.insertWithIDs || len(story.ID) < 8 {
|
|
story.ID = generate.StoryID()
|
|
}
|
|
|
|
if story.UpdatedDate.Before(story.CreatedDate) {
|
|
story.UpdatedDate = story.CreatedDate
|
|
}
|
|
|
|
err = q.InsertStory(ctx, psqlcore.InsertStoryParams{
|
|
ID: story.ID,
|
|
Author: story.Author,
|
|
Name: story.Name,
|
|
Category: string(story.Category),
|
|
Open: story.Open,
|
|
Listed: story.Listed,
|
|
SortByFictionalDate: story.SortByFictionalDate,
|
|
CreatedDate: story.CreatedDate.UTC(),
|
|
FictionalDate: story.FictionalDate.UTC(),
|
|
UpdatedDate: story.UpdatedDate.UTC(),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// This is inefficient, but tags are very rarely added before the story is submitted.
|
|
err = q.SetTags(ctx, psqlcore.SetTagsParams{
|
|
Tags: models.EncodeTagArray(story.Tags),
|
|
TargetKind: "Story",
|
|
TargetID: story.ID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &story, tx.Commit()
|
|
}
|
|
|
|
func (r *storyRepository) Update(ctx context.Context, story models.Story, update models.StoryUpdate) (*models.Story, error) {
|
|
story.ApplyUpdate(update)
|
|
|
|
err := psqlcore.New(r.db).UpdateStory(ctx, psqlcore.UpdateStoryParams{
|
|
Name: story.Name,
|
|
Category: string(story.Category),
|
|
Author: story.Author,
|
|
Open: story.Open,
|
|
Listed: story.Listed,
|
|
FictionalDate: story.FictionalDate.UTC(),
|
|
UpdatedDate: story.UpdatedDate.UTC(),
|
|
SortByFictionalDate: story.SortByFictionalDate,
|
|
ID: story.ID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &story, nil
|
|
}
|
|
|
|
func (r *storyRepository) AddTag(ctx context.Context, story models.Story, tag models.Tag) error {
|
|
return psqlcore.New(r.db).SetTag(ctx, psqlcore.SetTagParams{
|
|
TargetKind: "Story",
|
|
TargetID: story.ID,
|
|
Tag: tag.String(),
|
|
})
|
|
}
|
|
|
|
func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag models.Tag) error {
|
|
return psqlcore.New(r.db).ClearTag(ctx, psqlcore.ClearTagParams{
|
|
TargetKind: "Story",
|
|
TargetID: story.ID,
|
|
Tag: tag.String(),
|
|
})
|
|
}
|
|
|
|
func (r *storyRepository) Delete(ctx context.Context, story models.Story) error {
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = tx.Rollback() }()
|
|
q := psqlcore.New(tx)
|
|
|
|
err = q.ClearTagsByTarget(ctx, psqlcore.ClearTagsByTargetParams{
|
|
TargetKind: "Story",
|
|
TargetID: story.ID,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = q.DeleteChaptersByStoryID(ctx, story.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = q.DeleteStory(ctx, story.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (r *storyRepository) story(story psqlcore.Story, tags []psqlcore.CommonTag) *models.Story {
|
|
storyTags := make([]models.Tag, 0, 8)
|
|
for _, tag := range tags {
|
|
if tag.TargetKind == "Story" && tag.TargetID == story.ID {
|
|
newTag := models.Tag{}
|
|
err := newTag.Decode(tag.Tag)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
storyTags = append(storyTags, newTag)
|
|
}
|
|
}
|
|
|
|
return &models.Story{
|
|
ID: story.ID,
|
|
Author: story.Author,
|
|
Name: story.Name,
|
|
Category: models.StoryCategory(story.Category),
|
|
Open: story.Open,
|
|
Listed: story.Listed,
|
|
Tags: storyTags,
|
|
CreatedDate: story.CreatedDate,
|
|
FictionalDate: story.FictionalDate,
|
|
UpdatedDate: story.UpdatedDate,
|
|
SortByFictionalDate: story.SortByFictionalDate,
|
|
}
|
|
}
|
|
|
|
func (r *storyRepository) stories(stories []psqlcore.Story, tags []psqlcore.CommonTag) []*models.Story {
|
|
results := make([]*models.Story, 0, len(stories))
|
|
for _, story := range stories {
|
|
results = append(results, r.story(story, tags))
|
|
}
|
|
|
|
return results
|
|
}
|