Rename LMS persona image field to profile_picture.

Add migration 000064 renaming avatar_url column; expose profile_picture in API, sqlc, and Swagger.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yared Yemane 2026-05-20 06:17:15 -07:00
parent 9631711090
commit 6ab077b53d
10 changed files with 85 additions and 80 deletions

View File

@ -0,0 +1,2 @@
ALTER TABLE lms_personas
RENAME COLUMN profile_picture TO avatar_url;

View File

@ -0,0 +1,3 @@
-- Persona profile image URL stored as profile_picture (replaces avatar_url naming).
ALTER TABLE lms_personas
RENAME COLUMN avatar_url TO profile_picture;

View File

@ -1,5 +1,5 @@
-- name: CreateLmsPersona :one -- name: CreateLmsPersona :one
INSERT INTO lms_personas (name, description, avatar_url, is_active) INSERT INTO lms_personas (name, description, profile_picture, is_active)
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4)
RETURNING *; RETURNING *;
@ -13,7 +13,7 @@ UPDATE lms_personas
SET SET
name = COALESCE(sqlc.narg('name')::varchar, name), name = COALESCE(sqlc.narg('name')::varchar, name),
description = COALESCE(sqlc.narg('description')::text, description), description = COALESCE(sqlc.narg('description')::text, description),
avatar_url = COALESCE(sqlc.narg('avatar_url')::text, avatar_url), profile_picture = COALESCE(sqlc.narg('profile_picture')::text, profile_picture),
is_active = COALESCE(sqlc.narg('is_active')::boolean, is_active), is_active = COALESCE(sqlc.narg('is_active')::boolean, is_active),
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
WHERE id = sqlc.arg('id') WHERE id = sqlc.arg('id')
@ -29,7 +29,7 @@ SELECT
p.id, p.id,
p.name, p.name,
p.description, p.description,
p.avatar_url, p.profile_picture,
p.is_active, p.is_active,
p.created_at, p.created_at,
p.updated_at p.updated_at

View File

@ -10641,9 +10641,6 @@ const docTemplate = `{
"name" "name"
], ],
"properties": { "properties": {
"avatar_url": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@ -10652,6 +10649,9 @@ const docTemplate = `{
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"profile_picture": {
"type": "string"
} }
} }
}, },
@ -11581,9 +11581,6 @@ const docTemplate = `{
"domain.UpdateLmsPersonaInput": { "domain.UpdateLmsPersonaInput": {
"type": "object", "type": "object",
"properties": { "properties": {
"avatar_url": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@ -11592,6 +11589,9 @@ const docTemplate = `{
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"profile_picture": {
"type": "string"
} }
} }
}, },

View File

@ -10633,9 +10633,6 @@
"name" "name"
], ],
"properties": { "properties": {
"avatar_url": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@ -10644,6 +10641,9 @@
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"profile_picture": {
"type": "string"
} }
} }
}, },
@ -11573,9 +11573,6 @@
"domain.UpdateLmsPersonaInput": { "domain.UpdateLmsPersonaInput": {
"type": "object", "type": "object",
"properties": { "properties": {
"avatar_url": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
@ -11584,6 +11581,9 @@
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"profile_picture": {
"type": "string"
} }
} }
}, },

View File

@ -449,14 +449,14 @@ definitions:
type: object type: object
domain.CreateLmsPersonaInput: domain.CreateLmsPersonaInput:
properties: properties:
avatar_url:
type: string
description: description:
type: string type: string
is_active: is_active:
type: boolean type: boolean
name: name:
type: string type: string
profile_picture:
type: string
required: required:
- name - name
type: object type: object
@ -1096,14 +1096,14 @@ definitions:
type: object type: object
domain.UpdateLmsPersonaInput: domain.UpdateLmsPersonaInput:
properties: properties:
avatar_url:
type: string
description: description:
type: string type: string
is_active: is_active:
type: boolean type: boolean
name: name:
type: string type: string
profile_picture:
type: string
type: object type: object
domain.UpdateModuleInput: domain.UpdateModuleInput:
properties: properties:

View File

@ -12,23 +12,23 @@ import (
) )
const CreateLmsPersona = `-- name: CreateLmsPersona :one const CreateLmsPersona = `-- name: CreateLmsPersona :one
INSERT INTO lms_personas (name, description, avatar_url, is_active) INSERT INTO lms_personas (name, description, profile_picture, is_active)
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3, $4)
RETURNING id, name, description, avatar_url, is_active, created_at, updated_at RETURNING id, name, description, profile_picture, is_active, created_at, updated_at
` `
type CreateLmsPersonaParams struct { type CreateLmsPersonaParams struct {
Name string `json:"name"` Name string `json:"name"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
AvatarUrl pgtype.Text `json:"avatar_url"` ProfilePicture pgtype.Text `json:"profile_picture"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
} }
func (q *Queries) CreateLmsPersona(ctx context.Context, arg CreateLmsPersonaParams) (LmsPersona, error) { func (q *Queries) CreateLmsPersona(ctx context.Context, arg CreateLmsPersonaParams) (LmsPersona, error) {
row := q.db.QueryRow(ctx, CreateLmsPersona, row := q.db.QueryRow(ctx, CreateLmsPersona,
arg.Name, arg.Name,
arg.Description, arg.Description,
arg.AvatarUrl, arg.ProfilePicture,
arg.IsActive, arg.IsActive,
) )
var i LmsPersona var i LmsPersona
@ -36,7 +36,7 @@ func (q *Queries) CreateLmsPersona(ctx context.Context, arg CreateLmsPersonaPara
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Description, &i.Description,
&i.AvatarUrl, &i.ProfilePicture,
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
@ -55,7 +55,7 @@ func (q *Queries) DeleteLmsPersona(ctx context.Context, id int64) error {
} }
const GetLmsPersonaByID = `-- name: GetLmsPersonaByID :one const GetLmsPersonaByID = `-- name: GetLmsPersonaByID :one
SELECT id, name, description, avatar_url, is_active, created_at, updated_at SELECT id, name, description, profile_picture, is_active, created_at, updated_at
FROM lms_personas FROM lms_personas
WHERE id = $1 WHERE id = $1
` `
@ -67,7 +67,7 @@ func (q *Queries) GetLmsPersonaByID(ctx context.Context, id int64) (LmsPersona,
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Description, &i.Description,
&i.AvatarUrl, &i.ProfilePicture,
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
@ -81,7 +81,7 @@ SELECT
p.id, p.id,
p.name, p.name,
p.description, p.description,
p.avatar_url, p.profile_picture,
p.is_active, p.is_active,
p.created_at, p.created_at,
p.updated_at p.updated_at
@ -101,14 +101,14 @@ type ListLmsPersonasParams struct {
} }
type ListLmsPersonasRow struct { type ListLmsPersonasRow struct {
TotalCount int64 `json:"total_count"` TotalCount int64 `json:"total_count"`
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
AvatarUrl pgtype.Text `json:"avatar_url"` ProfilePicture pgtype.Text `json:"profile_picture"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"`
} }
func (q *Queries) ListLmsPersonas(ctx context.Context, arg ListLmsPersonasParams) ([]ListLmsPersonasRow, error) { func (q *Queries) ListLmsPersonas(ctx context.Context, arg ListLmsPersonasParams) ([]ListLmsPersonasRow, error) {
@ -125,7 +125,7 @@ func (q *Queries) ListLmsPersonas(ctx context.Context, arg ListLmsPersonasParams
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Description, &i.Description,
&i.AvatarUrl, &i.ProfilePicture,
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
@ -145,26 +145,26 @@ UPDATE lms_personas
SET SET
name = COALESCE($1::varchar, name), name = COALESCE($1::varchar, name),
description = COALESCE($2::text, description), description = COALESCE($2::text, description),
avatar_url = COALESCE($3::text, avatar_url), profile_picture = COALESCE($3::text, profile_picture),
is_active = COALESCE($4::boolean, is_active), is_active = COALESCE($4::boolean, is_active),
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
WHERE id = $5 WHERE id = $5
RETURNING id, name, description, avatar_url, is_active, created_at, updated_at RETURNING id, name, description, profile_picture, is_active, created_at, updated_at
` `
type UpdateLmsPersonaParams struct { type UpdateLmsPersonaParams struct {
Name pgtype.Text `json:"name"` Name pgtype.Text `json:"name"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
AvatarUrl pgtype.Text `json:"avatar_url"` ProfilePicture pgtype.Text `json:"profile_picture"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
ID int64 `json:"id"` ID int64 `json:"id"`
} }
func (q *Queries) UpdateLmsPersona(ctx context.Context, arg UpdateLmsPersonaParams) (LmsPersona, error) { func (q *Queries) UpdateLmsPersona(ctx context.Context, arg UpdateLmsPersonaParams) (LmsPersona, error) {
row := q.db.QueryRow(ctx, UpdateLmsPersona, row := q.db.QueryRow(ctx, UpdateLmsPersona,
arg.Name, arg.Name,
arg.Description, arg.Description,
arg.AvatarUrl, arg.ProfilePicture,
arg.IsActive, arg.IsActive,
arg.ID, arg.ID,
) )
@ -173,7 +173,7 @@ func (q *Queries) UpdateLmsPersona(ctx context.Context, arg UpdateLmsPersonaPara
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Description, &i.Description,
&i.AvatarUrl, &i.ProfilePicture,
&i.IsActive, &i.IsActive,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,

View File

@ -139,13 +139,13 @@ type LevelToSubCourse struct {
} }
type LmsPersona struct { type LmsPersona struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
AvatarUrl pgtype.Text `json:"avatar_url"` ProfilePicture pgtype.Text `json:"profile_picture"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"` CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"`
} }
type LmsPractice struct { type LmsPractice struct {

View File

@ -10,25 +10,25 @@ var ErrPersonaNotFound = errors.New("persona not found")
// LmsPersona is a coach / character profile stored in lms_personas and referenced by practice shells. // LmsPersona is a coach / character profile stored in lms_personas and referenced by practice shells.
type LmsPersona struct { type LmsPersona struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"` ProfilePicture *string `json:"profile_picture,omitempty"` // image URL (e.g. MinIO or HTTPS)
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"`
} }
type CreateLmsPersonaInput struct { type CreateLmsPersonaInput struct {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"` ProfilePicture *string `json:"profile_picture,omitempty"`
IsActive *bool `json:"is_active,omitempty"` IsActive *bool `json:"is_active,omitempty"`
} }
type UpdateLmsPersonaInput struct { type UpdateLmsPersonaInput struct {
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"` ProfilePicture *string `json:"profile_picture,omitempty"`
IsActive *bool `json:"is_active,omitempty"` IsActive *bool `json:"is_active,omitempty"`
} }

View File

@ -18,7 +18,7 @@ func lmsPersonaToDomain(p dbgen.LmsPersona) domain.LmsPersona {
IsActive: p.IsActive, IsActive: p.IsActive,
} }
out.Description = fromPgText(p.Description) out.Description = fromPgText(p.Description)
out.AvatarURL = fromPgText(p.AvatarUrl) out.ProfilePicture = fromPgText(p.ProfilePicture)
out.CreatedAt = p.CreatedAt.Time out.CreatedAt = p.CreatedAt.Time
if p.UpdatedAt.Valid { if p.UpdatedAt.Valid {
t := p.UpdatedAt.Time t := p.UpdatedAt.Time
@ -42,7 +42,7 @@ func (s *Store) CreateLmsPersona(ctx context.Context, in domain.CreateLmsPersona
p, err := s.queries.CreateLmsPersona(ctx, dbgen.CreateLmsPersonaParams{ p, err := s.queries.CreateLmsPersona(ctx, dbgen.CreateLmsPersonaParams{
Name: in.Name, Name: in.Name,
Description: toPgText(in.Description), Description: toPgText(in.Description),
AvatarUrl: toPgText(in.AvatarURL), ProfilePicture: toPgText(in.ProfilePicture),
IsActive: active, IsActive: active,
}) })
if err != nil { if err != nil {
@ -67,7 +67,7 @@ func (s *Store) UpdateLmsPersona(ctx context.Context, id int64, in domain.Update
ID: id, ID: id,
Name: optionalTextUpdate(in.Name), Name: optionalTextUpdate(in.Name),
Description: optionalTextUpdate(in.Description), Description: optionalTextUpdate(in.Description),
AvatarUrl: optionalTextUpdate(in.AvatarURL), ProfilePicture: optionalTextUpdate(in.ProfilePicture),
IsActive: optionalBoolUpdatePB(in.IsActive), IsActive: optionalBoolUpdatePB(in.IsActive),
}) })
if err != nil { if err != nil {
@ -102,13 +102,13 @@ func (s *Store) ListLmsPersonas(ctx context.Context, activeOnly bool, limit, off
total = r.TotalCount total = r.TotalCount
} }
out = append(out, lmsPersonaToDomain(dbgen.LmsPersona{ out = append(out, lmsPersonaToDomain(dbgen.LmsPersona{
ID: r.ID, ID: r.ID,
Name: r.Name, Name: r.Name,
Description: r.Description, Description: r.Description,
AvatarUrl: r.AvatarUrl, ProfilePicture: r.ProfilePicture,
IsActive: r.IsActive, IsActive: r.IsActive,
CreatedAt: r.CreatedAt, CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt, UpdatedAt: r.UpdatedAt,
})) }))
} }
return out, total, nil return out, total, nil