This commit is contained in:
@@ -83,7 +83,7 @@ func main() {
|
||||
|
||||
hideList := make(map[string]bool)
|
||||
|
||||
if parts[1] != "story" {
|
||||
if parts[1] != "story" && parts[1] != "chapter" {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
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 chapterRepository struct {
|
||||
insertWithIDs bool
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func (r *chapterRepository) Find(ctx context.Context, id string) (*models.Chapter, error) {
|
||||
chapter, err := psqlcore.New(r.db).SelectChapter(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.chapter(chapter), nil
|
||||
}
|
||||
|
||||
func (r *chapterRepository) List(ctx context.Context, filter models.ChapterFilter) ([]*models.Chapter, error) {
|
||||
params := psqlcore.SelectChaptersParams{
|
||||
StoryID: "",
|
||||
LimitSize: 10000,
|
||||
}
|
||||
if filter.StoryID != nil {
|
||||
params.StoryID = *filter.StoryID
|
||||
}
|
||||
if filter.Limit > 0 {
|
||||
params.LimitSize = int32(filter.Limit)
|
||||
}
|
||||
|
||||
chapters, err := psqlcore.New(r.db).SelectChapters(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.chapters(chapters), nil
|
||||
}
|
||||
|
||||
func (r *chapterRepository) Insert(ctx context.Context, chapter models.Chapter) (*models.Chapter, error) {
|
||||
if !r.insertWithIDs || len(chapter.ID) < 8 {
|
||||
chapter.ID = generate.ChapterID()
|
||||
}
|
||||
|
||||
err := psqlcore.New(r.db).InsertChapter(ctx, psqlcore.InsertChapterParams{
|
||||
ID: chapter.ID,
|
||||
StoryID: chapter.StoryID,
|
||||
Title: chapter.Title,
|
||||
Author: chapter.Author,
|
||||
Source: chapter.Source,
|
||||
CreatedDate: chapter.CreatedDate.UTC(),
|
||||
FictionalDate: chapter.FictionalDate.UTC(),
|
||||
EditedDate: chapter.EditedDate.UTC(),
|
||||
CommentMode: string(chapter.CommentMode),
|
||||
CommentsLocked: chapter.CommentsLocked,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &chapter, nil
|
||||
}
|
||||
|
||||
func (r *chapterRepository) Update(ctx context.Context, chapter models.Chapter, update models.ChapterUpdate) (*models.Chapter, error) {
|
||||
chapter.ApplyUpdate(update)
|
||||
|
||||
err := psqlcore.New(r.db).UpdateChapter(ctx, psqlcore.UpdateChapterParams{
|
||||
Title: chapter.Title,
|
||||
Source: chapter.Source,
|
||||
FictionalDate: chapter.FictionalDate.UTC(),
|
||||
CommentMode: string(chapter.CommentMode),
|
||||
CommentsLocked: chapter.CommentsLocked,
|
||||
ID: chapter.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &chapter, nil
|
||||
}
|
||||
|
||||
func (r *chapterRepository) Move(ctx context.Context, chapter models.Chapter, from, to models.Story) (*models.Chapter, error) {
|
||||
err := psqlcore.New(r.db).UpdateChapterStoryID(ctx, psqlcore.UpdateChapterStoryIDParams{
|
||||
StoryID: to.ID,
|
||||
ID: chapter.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chapter.StoryID = to.ID
|
||||
|
||||
return &chapter, nil
|
||||
}
|
||||
|
||||
func (r *chapterRepository) Delete(ctx context.Context, chapter models.Chapter) error {
|
||||
return psqlcore.New(r.db).DeleteChapter(ctx, chapter.ID)
|
||||
}
|
||||
|
||||
func (r *chapterRepository) chapter(chapter psqlcore.StoryChapter) *models.Chapter {
|
||||
return &models.Chapter{
|
||||
ID: chapter.ID,
|
||||
StoryID: chapter.StoryID,
|
||||
Title: chapter.Title,
|
||||
Author: chapter.Author,
|
||||
Source: chapter.Source,
|
||||
CreatedDate: chapter.CreatedDate,
|
||||
FictionalDate: chapter.FictionalDate,
|
||||
EditedDate: chapter.EditedDate,
|
||||
CommentMode: models.ChapterCommentMode(chapter.CommentMode),
|
||||
CommentsLocked: chapter.CommentsLocked,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *chapterRepository) chapters(chapters []psqlcore.StoryChapter) []*models.Chapter {
|
||||
results := make([]*models.Chapter, 0, len(chapters))
|
||||
for _, chapter := range chapters {
|
||||
results = append(results, r.chapter(chapter))
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func (d *DB) Stories() repositories.StoryRepository {
|
||||
}
|
||||
|
||||
func (d *DB) Chapters() repositories.ChapterRepository {
|
||||
panic("implement me")
|
||||
return &chapterRepository{insertWithIDs: d.insertWithIDs, db: d.db}
|
||||
}
|
||||
|
||||
func (d *DB) Comments() repositories.CommentRepository {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE story_chapter (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
story_id TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
author TEXT NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
created_date TIMESTAMP NOT NULL,
|
||||
fictional_date TIMESTAMP NOT NULL,
|
||||
edited_date TIMESTAMP NOT NULL,
|
||||
comment_mode TEXT NOT NULL,
|
||||
comments_locked BOOLEAN NOT NULL
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE story_chapter;
|
||||
-- +goose StatementEnd
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE INDEX story_chapter_index_story_id ON story_chapter (story_id);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP INDEX IF EXISTS story_chapter_index_story_id;
|
||||
-- +goose StatementEnd
|
||||
@@ -80,6 +80,7 @@ WHERE ($1::bool = false OR keys && ($2::text[]))
|
||||
AND ($3::bool = false OR date >= $4::timestamp)
|
||||
AND ($5::bool = false OR date <= $6::timestamp)
|
||||
AND ($7::bool = false OR author = $8::text)
|
||||
ORDER BY date DESC
|
||||
LIMIT $9::int
|
||||
`
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ WHERE ($1::bool = false OR name = ANY($2::text[]))
|
||||
AND ($3::bool = false OR logged = $4)
|
||||
AND ($5::bool = false OR event_name = $6)
|
||||
AND ($7::bool = false OR location_name = $8)
|
||||
ORDER BY name
|
||||
LIMIT $9::int
|
||||
`
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// source: chapters.sql
|
||||
|
||||
package psqlcore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const deleteChapter = `-- name: DeleteChapter :exec
|
||||
DELETE FROM story_chapter WHERE id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteChapter(ctx context.Context, id string) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteChapter, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteChaptersByStoryID = `-- name: DeleteChaptersByStoryID :exec
|
||||
DELETE FROM story_chapter WHERE story_id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteChaptersByStoryID(ctx context.Context, storyID string) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteChaptersByStoryID, storyID)
|
||||
return err
|
||||
}
|
||||
|
||||
const insertChapter = `-- name: InsertChapter :exec
|
||||
INSERT INTO story_chapter (id, story_id, title, author, source, created_date, fictional_date, edited_date, comment_mode, comments_locked)
|
||||
VALUES (
|
||||
$1::TEXT, $2::TEXT, $3::TEXT, $4::TEXT, $5::TEXT,
|
||||
$6::TIMESTAMP, $7::TIMESTAMP, $8::TIMESTAMP,
|
||||
$9::TEXT, $10::BOOLEAN
|
||||
)
|
||||
`
|
||||
|
||||
type InsertChapterParams struct {
|
||||
ID string `json:"id"`
|
||||
StoryID string `json:"story_id"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Source string `json:"source"`
|
||||
CreatedDate time.Time `json:"created_date"`
|
||||
FictionalDate time.Time `json:"fictional_date"`
|
||||
EditedDate time.Time `json:"edited_date"`
|
||||
CommentMode string `json:"comment_mode"`
|
||||
CommentsLocked bool `json:"comments_locked"`
|
||||
}
|
||||
|
||||
func (q *Queries) InsertChapter(ctx context.Context, arg InsertChapterParams) error {
|
||||
_, err := q.db.ExecContext(ctx, insertChapter,
|
||||
arg.ID,
|
||||
arg.StoryID,
|
||||
arg.Title,
|
||||
arg.Author,
|
||||
arg.Source,
|
||||
arg.CreatedDate,
|
||||
arg.FictionalDate,
|
||||
arg.EditedDate,
|
||||
arg.CommentMode,
|
||||
arg.CommentsLocked,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const selectChapter = `-- name: SelectChapter :one
|
||||
SELECT id, story_id, title, author, source, created_date, fictional_date, edited_date, comment_mode, comments_locked FROM story_chapter WHERE id=$1::TEXT LIMIT 1
|
||||
`
|
||||
|
||||
func (q *Queries) SelectChapter(ctx context.Context, dollar_1 string) (StoryChapter, error) {
|
||||
row := q.db.QueryRowContext(ctx, selectChapter, dollar_1)
|
||||
var i StoryChapter
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.StoryID,
|
||||
&i.Title,
|
||||
&i.Author,
|
||||
&i.Source,
|
||||
&i.CreatedDate,
|
||||
&i.FictionalDate,
|
||||
&i.EditedDate,
|
||||
&i.CommentMode,
|
||||
&i.CommentsLocked,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const selectChapters = `-- name: SelectChapters :many
|
||||
SELECT id, story_id, title, author, source, created_date, fictional_date, edited_date, comment_mode, comments_locked FROM story_chapter WHERE (sqlx.arg(story_id)::TEXT == '' OR story_id=$1::TEXT) ORDER BY created_date LIMIT $2::INT
|
||||
`
|
||||
|
||||
type SelectChaptersParams struct {
|
||||
StoryID string `json:"story_id"`
|
||||
LimitSize int32 `json:"limit_size"`
|
||||
}
|
||||
|
||||
func (q *Queries) SelectChapters(ctx context.Context, arg SelectChaptersParams) ([]StoryChapter, error) {
|
||||
rows, err := q.db.QueryContext(ctx, selectChapters, arg.StoryID, arg.LimitSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []StoryChapter{}
|
||||
for rows.Next() {
|
||||
var i StoryChapter
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.StoryID,
|
||||
&i.Title,
|
||||
&i.Author,
|
||||
&i.Source,
|
||||
&i.CreatedDate,
|
||||
&i.FictionalDate,
|
||||
&i.EditedDate,
|
||||
&i.CommentMode,
|
||||
&i.CommentsLocked,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateChapter = `-- name: UpdateChapter :exec
|
||||
UPDATE story_chapter
|
||||
SET title=$1,
|
||||
source=$2,
|
||||
fictional_date=$3,
|
||||
comment_mode=$4,
|
||||
comments_locked=$5
|
||||
WHERE id=$6
|
||||
`
|
||||
|
||||
type UpdateChapterParams struct {
|
||||
Title string `json:"title"`
|
||||
Source string `json:"source"`
|
||||
FictionalDate time.Time `json:"fictional_date"`
|
||||
CommentMode string `json:"comment_mode"`
|
||||
CommentsLocked bool `json:"comments_locked"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateChapter(ctx context.Context, arg UpdateChapterParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateChapter,
|
||||
arg.Title,
|
||||
arg.Source,
|
||||
arg.FictionalDate,
|
||||
arg.CommentMode,
|
||||
arg.CommentsLocked,
|
||||
arg.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateChapterStoryID = `-- name: UpdateChapterStoryID :exec
|
||||
UPDATE story_chapter
|
||||
SET story_id=$1::TEXT
|
||||
WHERE id=$2
|
||||
`
|
||||
|
||||
type UpdateChapterStoryIDParams struct {
|
||||
StoryID string `json:"story_id"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateChapterStoryID(ctx context.Context, arg UpdateChapterStoryIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateChapterStoryID, arg.StoryID, arg.ID)
|
||||
return err
|
||||
}
|
||||
@@ -60,3 +60,16 @@ type Story struct {
|
||||
FictionalDate time.Time `json:"fictional_date"`
|
||||
UpdatedDate time.Time `json:"updated_date"`
|
||||
}
|
||||
|
||||
type StoryChapter struct {
|
||||
ID string `json:"id"`
|
||||
StoryID string `json:"story_id"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Source string `json:"source"`
|
||||
CreatedDate time.Time `json:"created_date"`
|
||||
FictionalDate time.Time `json:"fictional_date"`
|
||||
EditedDate time.Time `json:"edited_date"`
|
||||
CommentMode string `json:"comment_mode"`
|
||||
CommentsLocked bool `json:"comments_locked"`
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ WHERE ($1::bool = false OR id = ANY($2::text[]))
|
||||
AND ($9::bool = false OR category = $10::text)
|
||||
AND ($11::bool = false OR open = $12::bool)
|
||||
AND ($13::bool = false OR unlisted = $14::bool)
|
||||
ORDER BY updated_date
|
||||
LIMIT $15::int
|
||||
`
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
-- name: SelectChapter :one
|
||||
SELECT * FROM story_chapter WHERE id=$1::TEXT LIMIT 1;
|
||||
|
||||
-- name: SelectChapters :many
|
||||
SELECT * FROM story_chapter WHERE (sqlx.arg(story_id)::TEXT == '' OR story_id=sqlc.arg(story_id)::TEXT) ORDER BY created_date LIMIT sqlc.arg(limit_size)::INT;
|
||||
|
||||
-- name: InsertChapter :exec
|
||||
INSERT INTO story_chapter (id, story_id, title, author, source, created_date, fictional_date, edited_date, comment_mode, comments_locked)
|
||||
VALUES (
|
||||
sqlc.arg(id)::TEXT, sqlc.arg(story_id)::TEXT, sqlc.arg(title)::TEXT, sqlc.arg(author)::TEXT, sqlc.arg(source)::TEXT,
|
||||
sqlc.arg(created_date)::TIMESTAMP, sqlc.arg(fictional_date)::TIMESTAMP, sqlc.arg(edited_date)::TIMESTAMP,
|
||||
sqlc.arg(comment_mode)::TEXT, sqlc.arg(comments_locked)::BOOLEAN
|
||||
);
|
||||
|
||||
-- name: UpdateChapterStoryID :exec
|
||||
UPDATE story_chapter
|
||||
SET story_id=sqlc.arg(story_id)::TEXT
|
||||
WHERE id=sqlc.arg(id);
|
||||
|
||||
-- name: UpdateChapter :exec
|
||||
UPDATE story_chapter
|
||||
SET title=sqlc.arg(title),
|
||||
source=sqlc.arg(source),
|
||||
fictional_date=sqlc.arg(fictional_date),
|
||||
comment_mode=sqlc.arg(comment_mode),
|
||||
comments_locked=sqlc.arg(comments_locked)
|
||||
WHERE id=sqlc.arg(id);
|
||||
|
||||
-- name: DeleteChapter :exec
|
||||
DELETE FROM story_chapter WHERE id=$1;
|
||||
|
||||
-- name: DeleteChaptersByStoryID :exec
|
||||
DELETE FROM story_chapter WHERE story_id=$1;
|
||||
@@ -178,9 +178,14 @@ func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag
|
||||
}
|
||||
|
||||
func (r *storyRepository) Delete(ctx context.Context, story models.Story) error {
|
||||
q := psqlcore.New(r.db)
|
||||
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{
|
||||
err = q.ClearTagsByTarget(ctx, psqlcore.ClearTagsByTargetParams{
|
||||
TargetKind: "Story",
|
||||
TargetID: story.ID,
|
||||
})
|
||||
@@ -188,7 +193,17 @@ func (r *storyRepository) Delete(ctx context.Context, story models.Story) error
|
||||
return err
|
||||
}
|
||||
|
||||
return q.DeleteStory(ctx, story.ID)
|
||||
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 {
|
||||
|
||||
@@ -16,6 +16,24 @@ type Chapter struct {
|
||||
CommentsLocked bool `bson:"commentsLocked"`
|
||||
}
|
||||
|
||||
func (chapter *Chapter) ApplyUpdate(update ChapterUpdate) {
|
||||
if update.Title != nil {
|
||||
chapter.Title = *update.Title
|
||||
}
|
||||
if update.Source != nil {
|
||||
chapter.Source = *update.Source
|
||||
}
|
||||
if update.FictionalDate != nil {
|
||||
chapter.FictionalDate = update.FictionalDate.UTC()
|
||||
}
|
||||
if update.CommentMode != nil {
|
||||
chapter.CommentMode = *update.CommentMode
|
||||
}
|
||||
if update.CommentsLocked != nil {
|
||||
chapter.CommentsLocked = *update.CommentsLocked
|
||||
}
|
||||
}
|
||||
|
||||
// CanComment returns true if the chapter can be commented to.
|
||||
func (chapter *Chapter) CanComment() bool {
|
||||
return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled()
|
||||
|
||||
Reference in New Issue
Block a user