Honor optional sort_order on lesson create under a module.
Accept sort_order on CreateLessonInput; SQL falls back to max+1. When set, shift sibling lessons and insert at that position (same pattern as module create). Regenerate sqlc and update Swagger. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
7e61e34292
commit
fffdff1031
|
|
@ -1,17 +1,18 @@
|
||||||
-- name: CreateLesson :one
|
-- name: CreateLesson :one
|
||||||
INSERT INTO lessons (module_id, title, video_url, thumbnail, description, sort_order)
|
INSERT INTO lessons (module_id, title, video_url, thumbnail, description, sort_order)
|
||||||
SELECT
|
SELECT
|
||||||
$1,
|
sqlc.arg('module_id'),
|
||||||
$2,
|
sqlc.arg('title'),
|
||||||
$3,
|
sqlc.arg('video_url'),
|
||||||
$4,
|
sqlc.arg('thumbnail'),
|
||||||
$5,
|
sqlc.arg('description'),
|
||||||
coalesce((
|
COALESCE(sqlc.narg('sort_order')::int,
|
||||||
SELECT
|
COALESCE((
|
||||||
max(l.sort_order)
|
SELECT
|
||||||
FROM lessons l
|
max(l.sort_order)
|
||||||
WHERE
|
FROM lessons l
|
||||||
l.module_id = $1), 0) + 1
|
WHERE
|
||||||
|
l.module_id = sqlc.arg('module_id')), 0) + 1)
|
||||||
RETURNING
|
RETURNING
|
||||||
*;
|
*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10484,6 +10484,9 @@ const docTemplate = `{
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"sort_order": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10476,6 +10476,9 @@
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"sort_order": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,8 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
|
sort_order:
|
||||||
|
type: integer
|
||||||
thumbnail:
|
thumbnail:
|
||||||
type: string
|
type: string
|
||||||
title:
|
title:
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ SELECT
|
||||||
$3,
|
$3,
|
||||||
$4,
|
$4,
|
||||||
$5,
|
$5,
|
||||||
coalesce((
|
COALESCE($6::int,
|
||||||
SELECT
|
COALESCE((
|
||||||
max(l.sort_order)
|
SELECT
|
||||||
FROM lessons l
|
max(l.sort_order)
|
||||||
WHERE
|
FROM lessons l
|
||||||
l.module_id = $1), 0) + 1
|
WHERE
|
||||||
|
l.module_id = $1), 0) + 1)
|
||||||
RETURNING
|
RETURNING
|
||||||
id, module_id, title, video_url, thumbnail, description, created_at, updated_at, sort_order
|
id, module_id, title, video_url, thumbnail, description, created_at, updated_at, sort_order
|
||||||
`
|
`
|
||||||
|
|
@ -35,6 +36,7 @@ type CreateLessonParams struct {
|
||||||
VideoUrl pgtype.Text `json:"video_url"`
|
VideoUrl pgtype.Text `json:"video_url"`
|
||||||
Thumbnail pgtype.Text `json:"thumbnail"`
|
Thumbnail pgtype.Text `json:"thumbnail"`
|
||||||
Description pgtype.Text `json:"description"`
|
Description pgtype.Text `json:"description"`
|
||||||
|
SortOrder pgtype.Int4 `json:"sort_order"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateLesson(ctx context.Context, arg CreateLessonParams) (Lesson, error) {
|
func (q *Queries) CreateLesson(ctx context.Context, arg CreateLessonParams) (Lesson, error) {
|
||||||
|
|
@ -44,6 +46,7 @@ func (q *Queries) CreateLesson(ctx context.Context, arg CreateLessonParams) (Les
|
||||||
arg.VideoUrl,
|
arg.VideoUrl,
|
||||||
arg.Thumbnail,
|
arg.Thumbnail,
|
||||||
arg.Description,
|
arg.Description,
|
||||||
|
arg.SortOrder,
|
||||||
)
|
)
|
||||||
var i Lesson
|
var i Lesson
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ type CreateLessonInput struct {
|
||||||
VideoURL *string `json:"video_url,omitempty"`
|
VideoURL *string `json:"video_url,omitempty"`
|
||||||
Thumbnail *string `json:"thumbnail,omitempty"`
|
Thumbnail *string `json:"thumbnail,omitempty"`
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
// SortOrder within the module when set; omit to append after current max within module_id.
|
||||||
|
SortOrder *int `json:"sort_order,omitempty" validate:"omitempty,min=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateLessonInput struct {
|
type UpdateLessonInput struct {
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,43 @@ func lessonToDomain(l dbgen.Lesson) domain.Lesson {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) CreateLesson(ctx context.Context, moduleID int64, input domain.CreateLessonInput) (domain.Lesson, error) {
|
func (s *Store) CreateLesson(ctx context.Context, moduleID int64, input domain.CreateLessonInput) (domain.Lesson, error) {
|
||||||
|
if input.SortOrder != nil {
|
||||||
|
q, tx, err := s.BeginTx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
target := int32(*input.SortOrder)
|
||||||
|
if _, err := tx.Exec(ctx,
|
||||||
|
`UPDATE lessons SET sort_order = sort_order + 1 WHERE module_id = $1 AND sort_order >= $2`,
|
||||||
|
moduleID, target,
|
||||||
|
); err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
l, err := q.CreateLesson(ctx, dbgen.CreateLessonParams{
|
||||||
|
ModuleID: moduleID,
|
||||||
|
Title: input.Title,
|
||||||
|
VideoUrl: toPgText(input.VideoURL),
|
||||||
|
Thumbnail: toPgText(input.Thumbnail),
|
||||||
|
Description: toPgText(input.Description),
|
||||||
|
SortOrder: pgtype.Int4{Int32: target, Valid: true},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
return lessonToDomain(l), nil
|
||||||
|
}
|
||||||
|
|
||||||
l, err := s.queries.CreateLesson(ctx, dbgen.CreateLessonParams{
|
l, err := s.queries.CreateLesson(ctx, dbgen.CreateLessonParams{
|
||||||
ModuleID: moduleID,
|
ModuleID: moduleID,
|
||||||
Title: input.Title,
|
Title: input.Title,
|
||||||
VideoUrl: toPgText(input.VideoURL),
|
VideoUrl: toPgText(input.VideoURL),
|
||||||
Thumbnail: toPgText(input.Thumbnail),
|
Thumbnail: toPgText(input.Thumbnail),
|
||||||
Description: toPgText(input.Description),
|
Description: toPgText(input.Description),
|
||||||
|
SortOrder: pgtype.Int4{Valid: false},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.Lesson{}, err
|
return domain.Lesson{}, err
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user