init
This commit is contained in:
commit
b15b95781c
108 changed files with 14802 additions and 0 deletions
285
internal/db/releases.go
Normal file
285
internal/db/releases.go
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CreateReleaseParams struct {
|
||||
ProjectID int64
|
||||
Version string
|
||||
Build string
|
||||
Filename string
|
||||
StoragePath string
|
||||
ChecksumSHA256 string
|
||||
SizeBytes int64
|
||||
ContentType string
|
||||
ReleaseNotes string
|
||||
UploadedByUserID *int64
|
||||
IsActive bool
|
||||
}
|
||||
|
||||
func (r *ReleaseRepository) Create(ctx context.Context, params CreateReleaseParams) (*Release, error) {
|
||||
isActive := 0
|
||||
if params.IsActive {
|
||||
isActive = 1
|
||||
}
|
||||
|
||||
result, err := r.q.ExecContext(
|
||||
ctx,
|
||||
`INSERT INTO releases (
|
||||
project_id,
|
||||
version,
|
||||
build,
|
||||
filename,
|
||||
storage_path,
|
||||
checksum_sha256,
|
||||
size_bytes,
|
||||
content_type,
|
||||
release_notes,
|
||||
uploaded_by_user_id,
|
||||
is_active
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
params.ProjectID,
|
||||
strings.TrimSpace(params.Version),
|
||||
strings.TrimSpace(params.Build),
|
||||
strings.TrimSpace(params.Filename),
|
||||
strings.TrimSpace(params.StoragePath),
|
||||
strings.TrimSpace(params.ChecksumSHA256),
|
||||
params.SizeBytes,
|
||||
strings.TrimSpace(params.ContentType),
|
||||
strings.TrimSpace(params.ReleaseNotes),
|
||||
params.UploadedByUserID,
|
||||
isActive,
|
||||
)
|
||||
if err != nil {
|
||||
if isUniqueConstraintError(err) {
|
||||
return nil, errors.Join(ErrConflict, fmt.Errorf("insert release: %w", err))
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("insert release: %w", err)
|
||||
}
|
||||
|
||||
releaseID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load inserted release id: %w", err)
|
||||
}
|
||||
|
||||
return r.GetByID(ctx, releaseID)
|
||||
}
|
||||
|
||||
func (r *ReleaseRepository) GetByID(ctx context.Context, id int64) (*Release, error) {
|
||||
release, err := scanRelease(r.q.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT
|
||||
id,
|
||||
project_id,
|
||||
version,
|
||||
build,
|
||||
filename,
|
||||
storage_path,
|
||||
checksum_sha256,
|
||||
size_bytes,
|
||||
content_type,
|
||||
release_notes,
|
||||
created_at,
|
||||
updated_at,
|
||||
uploaded_by_user_id,
|
||||
is_active
|
||||
FROM releases
|
||||
WHERE id = ?`,
|
||||
id,
|
||||
))
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("scan release by id: %w", err)
|
||||
}
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
func (r *ReleaseRepository) GetLatestByProjectID(ctx context.Context, projectID int64) (*Release, error) {
|
||||
release, err := scanRelease(r.q.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT
|
||||
id,
|
||||
project_id,
|
||||
version,
|
||||
build,
|
||||
filename,
|
||||
storage_path,
|
||||
checksum_sha256,
|
||||
size_bytes,
|
||||
content_type,
|
||||
release_notes,
|
||||
created_at,
|
||||
updated_at,
|
||||
uploaded_by_user_id,
|
||||
is_active
|
||||
FROM releases
|
||||
WHERE project_id = ?
|
||||
AND is_active = 1
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT 1`,
|
||||
projectID,
|
||||
))
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("scan latest release by project id: %w", err)
|
||||
}
|
||||
|
||||
return release, nil
|
||||
}
|
||||
|
||||
func (r *ReleaseRepository) ListByProjectID(ctx context.Context, projectID int64) ([]ReleaseListItem, error) {
|
||||
rows, err := r.q.QueryContext(
|
||||
ctx,
|
||||
`SELECT
|
||||
r.id,
|
||||
r.project_id,
|
||||
r.version,
|
||||
r.build,
|
||||
r.filename,
|
||||
r.storage_path,
|
||||
r.checksum_sha256,
|
||||
r.size_bytes,
|
||||
r.content_type,
|
||||
r.release_notes,
|
||||
r.created_at,
|
||||
r.updated_at,
|
||||
r.uploaded_by_user_id,
|
||||
r.is_active,
|
||||
COALESCE(u.email, '')
|
||||
FROM releases AS r
|
||||
LEFT JOIN users AS u ON u.id = r.uploaded_by_user_id
|
||||
WHERE r.project_id = ?
|
||||
ORDER BY r.created_at DESC, r.id DESC`,
|
||||
projectID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query project releases: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
releases := make([]ReleaseListItem, 0)
|
||||
for rows.Next() {
|
||||
item, err := scanReleaseListItem(rows)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scan project release: %w", err)
|
||||
}
|
||||
|
||||
releases = append(releases, *item)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("iterate project releases: %w", err)
|
||||
}
|
||||
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
func scanRelease(scanner rowScanner) (*Release, error) {
|
||||
var (
|
||||
release Release
|
||||
createdAtRaw string
|
||||
updatedAtRaw string
|
||||
uploadedByUserID sql.NullInt64
|
||||
isActive int
|
||||
)
|
||||
|
||||
if err := scanner.Scan(
|
||||
&release.ID,
|
||||
&release.ProjectID,
|
||||
&release.Version,
|
||||
&release.Build,
|
||||
&release.Filename,
|
||||
&release.StoragePath,
|
||||
&release.ChecksumSHA256,
|
||||
&release.SizeBytes,
|
||||
&release.ContentType,
|
||||
&release.ReleaseNotes,
|
||||
&createdAtRaw,
|
||||
&updatedAtRaw,
|
||||
&uploadedByUserID,
|
||||
&isActive,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createdAt, err := parseTimestamp(createdAtRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse release created_at: %w", err)
|
||||
}
|
||||
|
||||
updatedAt, err := parseTimestamp(updatedAtRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse release updated_at: %w", err)
|
||||
}
|
||||
|
||||
release.CreatedAt = createdAt
|
||||
release.UpdatedAt = updatedAt
|
||||
release.IsActive = isActive == 1
|
||||
if uploadedByUserID.Valid {
|
||||
release.UploadedByUserID = &uploadedByUserID.Int64
|
||||
}
|
||||
|
||||
return &release, nil
|
||||
}
|
||||
|
||||
func scanReleaseListItem(scanner rowScanner) (*ReleaseListItem, error) {
|
||||
var (
|
||||
item ReleaseListItem
|
||||
createdAtRaw string
|
||||
updatedAtRaw string
|
||||
uploadedByUserID sql.NullInt64
|
||||
isActive int
|
||||
)
|
||||
|
||||
if err := scanner.Scan(
|
||||
&item.Release.ID,
|
||||
&item.Release.ProjectID,
|
||||
&item.Release.Version,
|
||||
&item.Release.Build,
|
||||
&item.Release.Filename,
|
||||
&item.Release.StoragePath,
|
||||
&item.Release.ChecksumSHA256,
|
||||
&item.Release.SizeBytes,
|
||||
&item.Release.ContentType,
|
||||
&item.Release.ReleaseNotes,
|
||||
&createdAtRaw,
|
||||
&updatedAtRaw,
|
||||
&uploadedByUserID,
|
||||
&isActive,
|
||||
&item.UploadedByEmail,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createdAt, err := parseTimestamp(createdAtRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse release list created_at: %w", err)
|
||||
}
|
||||
|
||||
updatedAt, err := parseTimestamp(updatedAtRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse release list updated_at: %w", err)
|
||||
}
|
||||
|
||||
item.Release.CreatedAt = createdAt
|
||||
item.Release.UpdatedAt = updatedAt
|
||||
item.Release.IsActive = isActive == 1
|
||||
if uploadedByUserID.Valid {
|
||||
item.Release.UploadedByUserID = &uploadedByUserID.Int64
|
||||
}
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue