Shift sibling sort_order when creating or updating LMS hierarchy rows.
Sequential reorder uses a temporary negative id slot plus range shifts so UNIQUE constraints on programs, courses, modules, and lessons stay valid; replaces module pairwise swap-only behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d28bddace1
commit
1136a166f5
|
|
@ -105,18 +105,56 @@ func (s *Store) ListCoursesByProgramID(ctx context.Context, programID int64, lim
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateCourse(ctx context.Context, id int64, input domain.UpdateCourseInput) (domain.Course, error) {
|
func (s *Store) UpdateCourse(ctx context.Context, id int64, input domain.UpdateCourseInput) (domain.Course, error) {
|
||||||
|
sortParam := optionalInt4Update(input.SortOrder)
|
||||||
var nameText pgtype.Text
|
var nameText pgtype.Text
|
||||||
if input.Name != nil {
|
if input.Name != nil {
|
||||||
nameText = pgtype.Text{String: *input.Name, Valid: true}
|
nameText = pgtype.Text{String: *input.Name, Valid: true}
|
||||||
} else {
|
} else {
|
||||||
nameText = pgtype.Text{Valid: false}
|
nameText = pgtype.Text{Valid: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.SortOrder != nil {
|
||||||
|
cur, err := s.GetCourseByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Course{}, err
|
||||||
|
}
|
||||||
|
oldPos := int32(cur.SortOrder)
|
||||||
|
newPos := int32(*input.SortOrder)
|
||||||
|
if oldPos != newPos {
|
||||||
|
q, tx, err := s.BeginTx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Course{}, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
if err := repositionCourseSortOrder(ctx, tx, cur.ProgramID, id, oldPos, newPos); err != nil {
|
||||||
|
return domain.Course{}, err
|
||||||
|
}
|
||||||
|
c, err := q.UpdateCourse(ctx, dbgen.UpdateCourseParams{
|
||||||
|
ID: id,
|
||||||
|
Name: nameText,
|
||||||
|
Description: optionalTextUpdate(input.Description),
|
||||||
|
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
||||||
|
SortOrder: pgtype.Int4{Valid: false},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.Course{}, err
|
||||||
|
}
|
||||||
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return domain.Course{}, err
|
||||||
|
}
|
||||||
|
out := courseToDomain(c)
|
||||||
|
out.HasPractice = cur.HasPractice
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
sortParam = pgtype.Int4{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
c, err := s.queries.UpdateCourse(ctx, dbgen.UpdateCourseParams{
|
c, err := s.queries.UpdateCourse(ctx, dbgen.UpdateCourseParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: nameText,
|
Name: nameText,
|
||||||
Description: optionalTextUpdate(input.Description),
|
Description: optionalTextUpdate(input.Description),
|
||||||
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
||||||
SortOrder: optionalInt4Update(input.SortOrder),
|
SortOrder: sortParam,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
|
|
||||||
|
|
@ -102,19 +102,58 @@ func (s *Store) ListLessonsByModuleID(ctx context.Context, moduleID int64, limit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateLesson(ctx context.Context, id int64, input domain.UpdateLessonInput) (domain.Lesson, error) {
|
func (s *Store) UpdateLesson(ctx context.Context, id int64, input domain.UpdateLessonInput) (domain.Lesson, error) {
|
||||||
|
sortParam := optionalInt4Update(input.SortOrder)
|
||||||
var titleText pgtype.Text
|
var titleText pgtype.Text
|
||||||
if input.Title != nil {
|
if input.Title != nil {
|
||||||
titleText = pgtype.Text{String: *input.Title, Valid: true}
|
titleText = pgtype.Text{String: *input.Title, Valid: true}
|
||||||
} else {
|
} else {
|
||||||
titleText = pgtype.Text{Valid: false}
|
titleText = pgtype.Text{Valid: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.SortOrder != nil {
|
||||||
|
cur, err := s.GetLessonByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
oldPos := int32(cur.SortOrder)
|
||||||
|
newPos := int32(*input.SortOrder)
|
||||||
|
if oldPos != newPos {
|
||||||
|
q, tx, err := s.BeginTx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
if err := repositionLessonSortOrder(ctx, tx, cur.ModuleID, id, oldPos, newPos); err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
l, err := q.UpdateLesson(ctx, dbgen.UpdateLessonParams{
|
||||||
|
ID: id,
|
||||||
|
Title: titleText,
|
||||||
|
VideoUrl: optionalTextUpdate(input.VideoURL),
|
||||||
|
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
||||||
|
Description: optionalTextUpdate(input.Description),
|
||||||
|
SortOrder: pgtype.Int4{Valid: false},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return domain.Lesson{}, err
|
||||||
|
}
|
||||||
|
out := lessonToDomain(l)
|
||||||
|
out.HasPractice = cur.HasPractice
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
sortParam = pgtype.Int4{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
l, err := s.queries.UpdateLesson(ctx, dbgen.UpdateLessonParams{
|
l, err := s.queries.UpdateLesson(ctx, dbgen.UpdateLessonParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Title: titleText,
|
Title: titleText,
|
||||||
VideoUrl: optionalTextUpdate(input.VideoURL),
|
VideoUrl: optionalTextUpdate(input.VideoURL),
|
||||||
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
||||||
Description: optionalTextUpdate(input.Description),
|
Description: optionalTextUpdate(input.Description),
|
||||||
SortOrder: optionalInt4Update(input.SortOrder),
|
SortOrder: sortParam,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
|
|
||||||
|
|
@ -107,69 +107,27 @@ func (s *Store) ListModulesByProgramAndCourse(ctx context.Context, programID, co
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateModule(ctx context.Context, id int64, input domain.UpdateModuleInput) (domain.Module, error) {
|
func (s *Store) UpdateModule(ctx context.Context, id int64, input domain.UpdateModuleInput) (domain.Module, error) {
|
||||||
|
cur, err := s.GetModuleByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Module{}, err
|
||||||
|
}
|
||||||
|
|
||||||
q, tx, err := s.BeginTx(ctx)
|
q, tx, err := s.BeginTx(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.Module{}, err
|
return domain.Module{}, err
|
||||||
}
|
}
|
||||||
defer tx.Rollback(ctx)
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
|
||||||
var current dbgen.Module
|
|
||||||
err = tx.QueryRow(ctx, `
|
|
||||||
SELECT id, program_id, course_id, name, description, icon, sort_order, created_at, updated_at
|
|
||||||
FROM modules
|
|
||||||
WHERE id = $1
|
|
||||||
FOR UPDATE
|
|
||||||
`, id).Scan(
|
|
||||||
¤t.ID,
|
|
||||||
¤t.ProgramID,
|
|
||||||
¤t.CourseID,
|
|
||||||
¤t.Name,
|
|
||||||
¤t.Description,
|
|
||||||
¤t.Icon,
|
|
||||||
¤t.SortOrder,
|
|
||||||
¤t.CreatedAt,
|
|
||||||
¤t.UpdatedAt,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
|
||||||
return domain.Module{}, pgx.ErrNoRows
|
|
||||||
}
|
|
||||||
return domain.Module{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
sortParam := optionalInt4Update(input.SortOrder)
|
||||||
if input.SortOrder != nil {
|
if input.SortOrder != nil {
|
||||||
targetSort := int32(*input.SortOrder)
|
oldPos := int32(cur.SortOrder)
|
||||||
if targetSort != current.SortOrder {
|
newPos := int32(*input.SortOrder)
|
||||||
var conflictID int64
|
if oldPos != newPos {
|
||||||
err = tx.QueryRow(ctx, `
|
if err := repositionModuleSortOrder(ctx, tx, cur.CourseID, id, oldPos, newPos); err != nil {
|
||||||
SELECT id
|
|
||||||
FROM modules
|
|
||||||
WHERE course_id = $1
|
|
||||||
AND sort_order = $2
|
|
||||||
AND id <> $3
|
|
||||||
FOR UPDATE
|
|
||||||
`, current.CourseID, targetSort, id).Scan(&conflictID)
|
|
||||||
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
|
|
||||||
return domain.Module{}, err
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
var tempSort int32
|
|
||||||
if err := tx.QueryRow(ctx, `
|
|
||||||
SELECT COALESCE(MIN(sort_order), 0) - 1
|
|
||||||
FROM modules
|
|
||||||
WHERE course_id = $1
|
|
||||||
`, current.CourseID).Scan(&tempSort); err != nil {
|
|
||||||
return domain.Module{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := tx.Exec(ctx, `UPDATE modules SET sort_order = $1 WHERE id = $2`, tempSort, id); err != nil {
|
|
||||||
return domain.Module{}, err
|
|
||||||
}
|
|
||||||
if _, err := tx.Exec(ctx, `UPDATE modules SET sort_order = $1 WHERE id = $2`, current.SortOrder, conflictID); err != nil {
|
|
||||||
return domain.Module{}, err
|
return domain.Module{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
sortParam = pgtype.Int4{Valid: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nameText pgtype.Text
|
var nameText pgtype.Text
|
||||||
|
|
@ -183,7 +141,7 @@ WHERE course_id = $1
|
||||||
Name: nameText,
|
Name: nameText,
|
||||||
Description: optionalTextUpdate(input.Description),
|
Description: optionalTextUpdate(input.Description),
|
||||||
Icon: optionalTextUpdate(input.Icon),
|
Icon: optionalTextUpdate(input.Icon),
|
||||||
SortOrder: optionalInt4Update(input.SortOrder),
|
SortOrder: sortParam,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
|
@ -195,7 +153,9 @@ WHERE course_id = $1
|
||||||
if err := tx.Commit(ctx); err != nil {
|
if err := tx.Commit(ctx); err != nil {
|
||||||
return domain.Module{}, err
|
return domain.Module{}, err
|
||||||
}
|
}
|
||||||
return moduleToDomain(m), nil
|
out := moduleToDomain(m)
|
||||||
|
out.HasPractice = cur.HasPractice
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) DeleteModule(ctx context.Context, id int64) error {
|
func (s *Store) DeleteModule(ctx context.Context, id int64) error {
|
||||||
|
|
|
||||||
106
internal/repository/lms_sort_order_shift.go
Normal file
106
internal/repository/lms_sort_order_shift.go
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sequential siblings (programs global, courses per program, modules per course, lessons per module) use unique sort_order.
|
||||||
|
// These helpers move id from oldPos to newPos without collisions by temporarily assigning sort_order = -id then shifting intermediates.
|
||||||
|
|
||||||
|
func repositionProgramSortOrder(ctx context.Context, tx pgx.Tx, id int64, oldPos, newPos int32) error {
|
||||||
|
if oldPos == newPos {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(ctx, `UPDATE programs SET sort_order = -id WHERE id = $1`, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if newPos < oldPos {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE programs SET sort_order = sort_order + 1
|
||||||
|
WHERE sort_order >= $1 AND sort_order < $2 AND id <> $3`, newPos, oldPos, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE programs SET sort_order = sort_order - 1
|
||||||
|
WHERE sort_order > $1 AND sort_order <= $2 AND id <> $3`, oldPos, newPos, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := tx.Exec(ctx, `UPDATE programs SET sort_order = $1 WHERE id = $2`, newPos, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func repositionCourseSortOrder(ctx context.Context, tx pgx.Tx, programID, id int64, oldPos, newPos int32) error {
|
||||||
|
if oldPos == newPos {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(ctx, `UPDATE courses SET sort_order = -id WHERE id = $1`, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if newPos < oldPos {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE courses SET sort_order = sort_order + 1
|
||||||
|
WHERE program_id = $4 AND sort_order >= $1 AND sort_order < $2 AND id <> $3`, newPos, oldPos, id, programID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE courses SET sort_order = sort_order - 1
|
||||||
|
WHERE program_id = $4 AND sort_order > $1 AND sort_order <= $2 AND id <> $3`, oldPos, newPos, id, programID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := tx.Exec(ctx, `UPDATE courses SET sort_order = $1 WHERE id = $2`, newPos, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func repositionModuleSortOrder(ctx context.Context, tx pgx.Tx, courseID, id int64, oldPos, newPos int32) error {
|
||||||
|
if oldPos == newPos {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(ctx, `UPDATE modules SET sort_order = -id WHERE id = $1`, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if newPos < oldPos {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE modules SET sort_order = sort_order + 1
|
||||||
|
WHERE course_id = $4 AND sort_order >= $1 AND sort_order < $2 AND id <> $3`, newPos, oldPos, id, courseID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE modules SET sort_order = sort_order - 1
|
||||||
|
WHERE course_id = $4 AND sort_order > $1 AND sort_order <= $2 AND id <> $3`, oldPos, newPos, id, courseID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := tx.Exec(ctx, `UPDATE modules SET sort_order = $1 WHERE id = $2`, newPos, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func repositionLessonSortOrder(ctx context.Context, tx pgx.Tx, moduleID, id int64, oldPos, newPos int32) error {
|
||||||
|
if oldPos == newPos {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(ctx, `UPDATE lessons SET sort_order = -id WHERE id = $1`, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if newPos < oldPos {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE lessons SET sort_order = sort_order + 1
|
||||||
|
WHERE module_id = $4 AND sort_order >= $1 AND sort_order < $2 AND id <> $3`, newPos, oldPos, id, moduleID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := tx.Exec(ctx, `
|
||||||
|
UPDATE lessons SET sort_order = sort_order - 1
|
||||||
|
WHERE module_id = $4 AND sort_order > $1 AND sort_order <= $2 AND id <> $3`, oldPos, newPos, id, moduleID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := tx.Exec(ctx, `UPDATE lessons SET sort_order = $1 WHERE id = $2`, newPos, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -28,11 +28,36 @@ func programToDomain(p dbgen.Program) domain.Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) CreateProgram(ctx context.Context, input domain.CreateProgramInput) (domain.Program, error) {
|
func (s *Store) CreateProgram(ctx context.Context, input domain.CreateProgramInput) (domain.Program, error) {
|
||||||
|
if input.SortOrder != nil {
|
||||||
|
q, tx, err := s.BeginTx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
target := int32(*input.SortOrder)
|
||||||
|
if _, err := tx.Exec(ctx, `UPDATE programs SET sort_order = sort_order + 1 WHERE sort_order >= $1`, target); err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
p, err := q.CreateProgram(ctx, dbgen.CreateProgramParams{
|
||||||
|
Name: input.Name,
|
||||||
|
Description: toPgText(input.Description),
|
||||||
|
Thumbnail: toPgText(input.Thumbnail),
|
||||||
|
SortOrder: pgtype.Int4{Int32: target, Valid: true},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
return programToDomain(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
p, err := s.queries.CreateProgram(ctx, dbgen.CreateProgramParams{
|
p, err := s.queries.CreateProgram(ctx, dbgen.CreateProgramParams{
|
||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
Description: toPgText(input.Description),
|
Description: toPgText(input.Description),
|
||||||
Thumbnail: toPgText(input.Thumbnail),
|
Thumbnail: toPgText(input.Thumbnail),
|
||||||
SortOrder: optionalInt4Update(input.SortOrder),
|
SortOrder: pgtype.Int4{Valid: false},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.Program{}, err
|
return domain.Program{}, err
|
||||||
|
|
@ -100,6 +125,47 @@ func optionalInt4Update(v *int) pgtype.Int4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdateProgram(ctx context.Context, id int64, input domain.UpdateProgramInput) (domain.Program, error) {
|
func (s *Store) UpdateProgram(ctx context.Context, id int64, input domain.UpdateProgramInput) (domain.Program, error) {
|
||||||
|
sortParam := optionalInt4Update(input.SortOrder)
|
||||||
|
if input.SortOrder != nil {
|
||||||
|
cur, err := s.GetProgramByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
oldPos := int32(cur.SortOrder)
|
||||||
|
newPos := int32(*input.SortOrder)
|
||||||
|
if oldPos != newPos {
|
||||||
|
q, tx, err := s.BeginTx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tx.Rollback(ctx) }()
|
||||||
|
if err := repositionProgramSortOrder(ctx, tx, id, oldPos, newPos); err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
var nameText pgtype.Text
|
||||||
|
if input.Name != nil {
|
||||||
|
nameText = pgtype.Text{String: *input.Name, Valid: true}
|
||||||
|
} else {
|
||||||
|
nameText = pgtype.Text{Valid: false}
|
||||||
|
}
|
||||||
|
p, err := q.UpdateProgram(ctx, dbgen.UpdateProgramParams{
|
||||||
|
ID: id,
|
||||||
|
Name: nameText,
|
||||||
|
Description: optionalTextUpdate(input.Description),
|
||||||
|
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
||||||
|
SortOrder: pgtype.Int4{Valid: false},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return domain.Program{}, err
|
||||||
|
}
|
||||||
|
return programToDomain(p), nil
|
||||||
|
}
|
||||||
|
sortParam = pgtype.Int4{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
var nameText pgtype.Text
|
var nameText pgtype.Text
|
||||||
if input.Name != nil {
|
if input.Name != nil {
|
||||||
nameText = pgtype.Text{String: *input.Name, Valid: true}
|
nameText = pgtype.Text{String: *input.Name, Valid: true}
|
||||||
|
|
@ -111,7 +177,7 @@ func (s *Store) UpdateProgram(ctx context.Context, id int64, input domain.Update
|
||||||
Name: nameText,
|
Name: nameText,
|
||||||
Description: optionalTextUpdate(input.Description),
|
Description: optionalTextUpdate(input.Description),
|
||||||
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
Thumbnail: optionalTextUpdate(input.Thumbnail),
|
||||||
SortOrder: optionalInt4Update(input.SortOrder),
|
SortOrder: sortParam,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user