Compare commits
3 Commits
c7176fadf6
...
b774d2b3c9
| Author | SHA1 | Date | |
|---|---|---|---|
| b774d2b3c9 | |||
| d124229308 | |||
| bf7a11076f |
@ -1,18 +1,20 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type ErrorCode string
|
type ErrorCode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// File errors
|
// File errors
|
||||||
ErrFileNotFound ErrorCode = "FILE_NOT_FOUND"
|
ErrCodeFileNotFound ErrorCode = "FILE_NOT_FOUND"
|
||||||
ErrMIMENotSupported ErrorCode = "MIME_NOT_SUPPORTED"
|
ErrCodeMIMENotSupported ErrorCode = "MIME_NOT_SUPPORTED"
|
||||||
|
|
||||||
// Tag errors
|
// Tag errors
|
||||||
ErrTagNotFound ErrorCode = "TAG_NOT_FOUND"
|
ErrCodeTagNotFound ErrorCode = "TAG_NOT_FOUND"
|
||||||
|
|
||||||
// General errors
|
// General errors
|
||||||
ErrValidation ErrorCode = "VALIDATION_ERROR"
|
ErrCodeBadRequest ErrorCode = "BAD_REQUEST"
|
||||||
ErrInternal ErrorCode = "INTERNAL_SERVER_ERROR"
|
ErrCodeInternal ErrorCode = "INTERNAL_SERVER_ERROR"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DomainError struct {
|
type DomainError struct {
|
||||||
@ -22,25 +24,42 @@ type DomainError struct {
|
|||||||
Details []any `json:"-"`
|
Details []any `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DomainError) Error() string {
|
func (e *DomainError) Wrap(err error) *DomainError {
|
||||||
if e.Err != nil {
|
e.Err = err
|
||||||
return e.Message + ": " + e.Err.Error()
|
return e
|
||||||
}
|
|
||||||
return e.Message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainError(err error, code ErrorCode, details ...any) *DomainError {
|
func NewErrorFileNotFound(file_id string) *DomainError {
|
||||||
return &DomainError{
|
return &DomainError{
|
||||||
Err: err,
|
Code: ErrCodeFileNotFound,
|
||||||
Code: code,
|
Message: fmt.Sprintf("File not found: %q", file_id),
|
||||||
Details: details,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUnexpectedError(err error) *DomainError {
|
func NewErrorMIMENotSupported(mime string) *DomainError {
|
||||||
return &DomainError{
|
return &DomainError{
|
||||||
Err: err,
|
Code: ErrCodeMIMENotSupported,
|
||||||
Code: ErrInternal,
|
Message: fmt.Sprintf("MIME not supported: %q", mime),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorTagNotFound(tag_id string) *DomainError {
|
||||||
|
return &DomainError{
|
||||||
|
Code: ErrCodeTagNotFound,
|
||||||
|
Message: fmt.Sprintf("Tag not found: %q", tag_id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorBadRequest(message string) *DomainError {
|
||||||
|
return &DomainError{
|
||||||
|
Code: ErrCodeBadRequest,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorUnexpected() *DomainError {
|
||||||
|
return &DomainError{
|
||||||
|
Code: ErrCodeInternal,
|
||||||
Message: "An unexpected error occured",
|
Message: "An unexpected error occured",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ type FileRepository interface {
|
|||||||
GetSlice(ctx context.Context, user_id int, filter, sort string, limit, offset int) (files Slice[FileItem], domainErr *DomainError)
|
GetSlice(ctx context.Context, user_id int, filter, sort string, limit, offset int) (files Slice[FileItem], domainErr *DomainError)
|
||||||
Get(ctx context.Context, user_id int, file_id string) (file FileFull, domainErr *DomainError)
|
Get(ctx context.Context, user_id int, file_id string) (file FileFull, domainErr *DomainError)
|
||||||
Add(ctx context.Context, user_id int, name, mime string, datetime time.Time, notes string, metadata json.RawMessage) (file FileCore, domainErr *DomainError)
|
Add(ctx context.Context, user_id int, name, mime string, datetime time.Time, notes string, metadata json.RawMessage) (file FileCore, domainErr *DomainError)
|
||||||
Update(ctx context.Context, user_id int, file_id string, updates map[string]interface{}) (domainErr *DomainError)
|
Update(ctx context.Context, file_id string, updates map[string]interface{}) (domainErr *DomainError)
|
||||||
Delete(ctx context.Context, user_id int, file_id string) (domainErr *DomainError)
|
Delete(ctx context.Context, file_id string) (domainErr *DomainError)
|
||||||
GetTags(ctx context.Context, user_id int, file_id string) (tags []TagItem, domainErr *DomainError)
|
GetTags(ctx context.Context, user_id int, file_id string) (tags []TagItem, domainErr *DomainError)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import (
|
|||||||
func New(dbURL string) (*pgxpool.Pool, error) {
|
func New(dbURL string) (*pgxpool.Pool, error) {
|
||||||
poolConfig, err := pgxpool.ParseConfig(dbURL)
|
poolConfig, err := pgxpool.ParseConfig(dbURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while parsing connection string: %w", err)
|
return nil, fmt.Errorf("failed to parse connection string: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
poolConfig.MaxConns = 100
|
poolConfig.MaxConns = 100
|
||||||
@ -23,9 +23,13 @@ func New(dbURL string) (*pgxpool.Pool, error) {
|
|||||||
poolConfig.MaxConnLifetime = time.Hour
|
poolConfig.MaxConnLifetime = time.Hour
|
||||||
poolConfig.HealthCheckPeriod = 30 * time.Second
|
poolConfig.HealthCheckPeriod = 30 * time.Second
|
||||||
|
|
||||||
db, err := pgxpool.NewWithConfig(context.Background(), poolConfig)
|
ctx := context.Background()
|
||||||
|
db, err := pgxpool.NewWithConfig(ctx, poolConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while initializing DB connections pool: %w", err)
|
return nil, fmt.Errorf("failed to initialize DB connections pool: %w", err)
|
||||||
|
}
|
||||||
|
if err = db.Ping(ctx); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||||
}
|
}
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
@ -34,7 +38,7 @@ func New(dbURL string) (*pgxpool.Pool, error) {
|
|||||||
func transaction(ctx context.Context, db *pgxpool.Pool, handler func(context.Context, pgx.Tx) *domain.DomainError) (domainErr *domain.DomainError) {
|
func transaction(ctx context.Context, db *pgxpool.Pool, handler func(context.Context, pgx.Tx) *domain.DomainError) (domainErr *domain.DomainError) {
|
||||||
tx, err := db.Begin(ctx)
|
tx, err := db.Begin(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = handler(ctx, tx)
|
domainErr = handler(ctx, tx)
|
||||||
@ -44,7 +48,7 @@ func transaction(ctx context.Context, db *pgxpool.Pool, handler func(context.Con
|
|||||||
}
|
}
|
||||||
err = tx.Commit(ctx)
|
err = tx.Commit(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ func NewFileRepository(db *pgxpool.Pool) *FileRepository {
|
|||||||
return &FileRepository{db: db}
|
return &FileRepository{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user can view file
|
// Get user permissions on file
|
||||||
func (s *FileRepository) GetAccess(ctx context.Context, user_id int, file_id string) (canView, canEdit bool, domainErr *domain.DomainError) {
|
func (s *FileRepository) GetAccess(ctx context.Context, user_id int, file_id string) (canView, canEdit bool, domainErr *domain.DomainError) {
|
||||||
row := s.db.QueryRow(ctx, `
|
row := s.db.QueryRow(ctx, `
|
||||||
SELECT
|
SELECT
|
||||||
@ -36,15 +36,18 @@ func (s *FileRepository) GetAccess(ctx context.Context, user_id int, file_id str
|
|||||||
err := row.Scan(&canView, &canEdit)
|
err := row.Scan(&canView, &canEdit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrFileNotFound, file_id)
|
domainErr = domain.NewErrorFileNotFound(file_id).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
if errors.As(err, &pgErr) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
switch pgErr.Code {
|
||||||
|
case "22P02":
|
||||||
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid file id: %q", file_id)).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
}
|
||||||
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -53,12 +56,12 @@ func (s *FileRepository) GetAccess(ctx context.Context, user_id int, file_id str
|
|||||||
func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort string, limit, offset int) (files domain.Slice[domain.FileItem], domainErr *domain.DomainError) {
|
func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort string, limit, offset int) (files domain.Slice[domain.FileItem], domainErr *domain.DomainError) {
|
||||||
filterCond, err := filterToSQL(filter)
|
filterCond, err := filterToSQL(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "filter", err.Error())
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid filter string: %q", filter)).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sortExpr, err := sortToSQL(sort)
|
sortExpr, err := sortToSQL(sort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "sort param", err.Error())
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid sorting parameter: %q", sort)).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// prepare query
|
// prepare query
|
||||||
@ -74,7 +77,7 @@ func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort
|
|||||||
FROM data.files f
|
FROM data.files f
|
||||||
JOIN system.mime m ON m.id=f.mime_id
|
JOIN system.mime m ON m.id=f.mime_id
|
||||||
JOIN system.users u ON u.id=f.creator_id
|
JOIN system.users u ON u.id=f.creator_id
|
||||||
WHERE NOT f.is_deleted AND (f.creator_id=$1 OR (SELECT view FROM acl.files WHERE file_id=f.id AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1)) AND
|
WHERE f.is_deleted IS FALSE AND (f.creator_id=$1 OR (SELECT view FROM acl.files WHERE file_id=f.id AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1)) AND
|
||||||
`
|
`
|
||||||
query += filterCond
|
query += filterCond
|
||||||
queryCount := query
|
queryCount := query
|
||||||
@ -92,15 +95,12 @@ func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort
|
|||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) {
|
if errors.As(err, &pgErr) {
|
||||||
switch pgErr.Code {
|
switch pgErr.Code {
|
||||||
case "22P02", "22007":
|
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
|
||||||
return
|
|
||||||
case "42P10":
|
case "42P10":
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "sort field", sort[1:])
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid sorting field: %q", sort[1:])).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
@ -109,7 +109,7 @@ func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort
|
|||||||
var file domain.FileItem
|
var file domain.FileItem
|
||||||
err = rows.Scan(&file.ID, &file.Name, &file.MIME.Name, &file.MIME.Extension, &file.CreatedAt, &file.Creator.Name, &file.Creator.IsAdmin)
|
err = rows.Scan(&file.ID, &file.Name, &file.MIME.Name, &file.MIME.Extension, &file.CreatedAt, &file.Creator.Name, &file.Creator.IsAdmin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
files.Data = append(files.Data, file)
|
files.Data = append(files.Data, file)
|
||||||
@ -117,7 +117,7 @@ func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort
|
|||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
files.Pagination.Limit = limit
|
files.Pagination.Limit = limit
|
||||||
@ -126,7 +126,7 @@ func (s *FileRepository) GetSlice(ctx context.Context, user_id int, filter, sort
|
|||||||
row := tx.QueryRow(ctx, fmt.Sprintf("SELECT COUNT(*) FROM (%s) tmp", queryCount), user_id)
|
row := tx.QueryRow(ctx, fmt.Sprintf("SELECT COUNT(*) FROM (%s) tmp", queryCount), user_id)
|
||||||
err = row.Scan(&files.Pagination.Total)
|
err = row.Scan(&files.Pagination.Total)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -150,20 +150,23 @@ func (s *FileRepository) Get(ctx context.Context, user_id int, file_id string) (
|
|||||||
FROM data.files f
|
FROM data.files f
|
||||||
JOIN system.mime m ON m.id=f.mime_id
|
JOIN system.mime m ON m.id=f.mime_id
|
||||||
JOIN system.users u ON u.id=f.creator_id
|
JOIN system.users u ON u.id=f.creator_id
|
||||||
WHERE NOT f.is_deleted AND f.id=$2 AND (f.creator_id=$1 OR (SELECT view FROM acl.files WHERE file_id=$2 AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1))
|
WHERE f.is_deleted IS FALSE
|
||||||
`, user_id, file_id)
|
`, user_id, file_id)
|
||||||
err := row.Scan(&file.ID, &file.Name, &file.MIME.Name, &file.MIME.Extension, &file.CreatedAt, &file.Creator.Name, &file.Creator.IsAdmin, &file.Notes, &file.Metadata, &file.Viewed)
|
err := row.Scan(&file.ID, &file.Name, &file.MIME.Name, &file.MIME.Extension, &file.CreatedAt, &file.Creator.Name, &file.Creator.IsAdmin, &file.Notes, &file.Metadata, &file.Viewed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrFileNotFound, file_id)
|
domainErr = domain.NewErrorFileNotFound(file_id).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
if errors.As(err, &pgErr) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
switch pgErr.Code {
|
||||||
|
case "22P02":
|
||||||
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid file id: %q", file_id)).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
}
|
||||||
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -177,10 +180,10 @@ func (s *FileRepository) Add(ctx context.Context, user_id int, name, mime string
|
|||||||
err := row.Scan(&mime_id, &extension)
|
err := row.Scan(&mime_id, &extension)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrMIMENotSupported, mime)
|
domainErr = domain.NewErrorMIMENotSupported(mime).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
row = s.db.QueryRow(ctx, `
|
row = s.db.QueryRow(ctx, `
|
||||||
@ -191,11 +194,17 @@ func (s *FileRepository) Add(ctx context.Context, user_id int, name, mime string
|
|||||||
err = row.Scan(&file.ID)
|
err = row.Scan(&file.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
if errors.As(err, &pgErr) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
switch pgErr.Code {
|
||||||
|
case "22007":
|
||||||
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid datetime: %q", datetime)).Wrap(err)
|
||||||
|
return
|
||||||
|
case "23502":
|
||||||
|
domainErr = domain.NewErrorBadRequest("Unable to set NULL to some fields").Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
}
|
||||||
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file.Name = &name
|
file.Name = &name
|
||||||
@ -205,71 +214,81 @@ func (s *FileRepository) Add(ctx context.Context, user_id int, name, mime string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update file
|
// Update file
|
||||||
func (s *FileRepository) Update(ctx context.Context, user_id int, file_id string, updates map[string]interface{}) (domainErr *domain.DomainError) {
|
func (s *FileRepository) Update(ctx context.Context, file_id string, updates map[string]interface{}) (domainErr *domain.DomainError) {
|
||||||
if len(updates) == 0 {
|
if len(updates) == 0 {
|
||||||
domainErr = domain.NewDomainError(errors.ErrUnsupported, domain.ErrValidation, "request body", "no fields provided for update")
|
// domainErr = domain.NewErrorBadRequest(nil, "No fields provided for update")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writableFields := map[string]bool{
|
|
||||||
"name": true,
|
|
||||||
"datetime": true,
|
|
||||||
"notes": true,
|
|
||||||
"metadata": true,
|
|
||||||
}
|
|
||||||
query := "UPDATE data.files SET"
|
query := "UPDATE data.files SET"
|
||||||
newValues := []interface{}{user_id}
|
newValues := []interface{}{file_id}
|
||||||
count := 2
|
count := 2
|
||||||
for field, value := range updates {
|
for field, value := range updates {
|
||||||
if !writableFields[field] {
|
switch field {
|
||||||
domainErr = domain.NewDomainError(errors.ErrUnsupported, domain.ErrValidation, "field", field)
|
case "name", "notes":
|
||||||
|
query += fmt.Sprintf(" %s=NULLIF($%d, '')", field, count)
|
||||||
|
case "datetime":
|
||||||
|
query += fmt.Sprintf(" %s=NULLIF($%d, '')::timestamptz", field, count)
|
||||||
|
case "metadata":
|
||||||
|
query += fmt.Sprintf(" %s=NULLIF($%d, '')::jsonb", field, count)
|
||||||
|
default:
|
||||||
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Unknown field: %q", field))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
query += fmt.Sprintf(" %s=NULLIF($%d, '')", field, count)
|
|
||||||
newValues = append(newValues, value)
|
newValues = append(newValues, value)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
query += fmt.Sprintf(
|
query += fmt.Sprintf(" WHERE id=$1 AND is_deleted IS FALSE")
|
||||||
" WHERE id=$%d AND (creator_id=$1 OR (SELECT edit FROM acl.files WHERE file_id=$%d AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1))",
|
|
||||||
count, count)
|
|
||||||
newValues = append(newValues, file_id)
|
|
||||||
commandTag, err := s.db.Exec(ctx, query, newValues...)
|
commandTag, err := s.db.Exec(ctx, query, newValues...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
if errors.As(err, &pgErr) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
switch pgErr.Code {
|
||||||
|
case "22P02":
|
||||||
|
domainErr = domain.NewErrorBadRequest("Invalid format of some values").Wrap(err)
|
||||||
|
return
|
||||||
|
case "22007":
|
||||||
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid datetime: %q", updates["datetime"])).Wrap(err)
|
||||||
|
return
|
||||||
|
case "23502":
|
||||||
|
domainErr = domain.NewErrorBadRequest("Some fields cannot be empty").Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
}
|
||||||
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if commandTag.RowsAffected() == 0 {
|
if commandTag.RowsAffected() == 0 {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrFileNotFound, file_id)
|
domainErr = domain.NewErrorFileNotFound(file_id).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete file
|
// Delete file
|
||||||
func (s *FileRepository) Delete(ctx context.Context, user_id int, file_id string) (domainErr *domain.DomainError) {
|
func (s *FileRepository) Delete(ctx context.Context, file_id string) (domainErr *domain.DomainError) {
|
||||||
commandTag, err := s.db.Exec(ctx,
|
commandTag, err := s.db.Exec(ctx,
|
||||||
"DELETE FROM data.files WHERE id=$2 AND (creator_id=$1 OR (SELECT edit FROM acl.files WHERE file_id=$2 AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1))",
|
"UPDATE data.files SET is_deleted=true WHERE id=$1 AND is_deleted IS FALSE",
|
||||||
user_id, file_id)
|
file_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
if errors.As(err, &pgErr) {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
switch pgErr.Code {
|
||||||
|
case "22P02":
|
||||||
|
domainErr = domain.NewErrorBadRequest(fmt.Sprintf("Invalid file id: %q", file_id)).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
}
|
||||||
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if commandTag.RowsAffected() == 0 {
|
if commandTag.RowsAffected() == 0 {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrFileNotFound, file_id)
|
domainErr = domain.NewErrorFileNotFound(file_id).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get list of tags of file
|
||||||
func (s *FileRepository) GetTags(ctx context.Context, user_id int, file_id string) (tags []domain.TagItem, domainErr *domain.DomainError) {
|
func (s *FileRepository) GetTags(ctx context.Context, user_id int, file_id string) (tags []domain.TagItem, domainErr *domain.DomainError) {
|
||||||
rows, err := s.db.Query(ctx, `
|
rows, err := s.db.Query(ctx, `
|
||||||
SELECT
|
SELECT
|
||||||
@ -288,10 +307,10 @@ func (s *FileRepository) GetTags(ctx context.Context, user_id int, file_id strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var pgErr *pgconn.PgError
|
var pgErr *pgconn.PgError
|
||||||
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
if errors.As(err, &pgErr) && (pgErr.Code == "22P02" || pgErr.Code == "22007") {
|
||||||
domainErr = domain.NewDomainError(err, domain.ErrValidation, "format", pgErr.Message)
|
domainErr = domain.NewErrorBadRequest(pgErr.Message).Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
@ -299,14 +318,14 @@ func (s *FileRepository) GetTags(ctx context.Context, user_id int, file_id strin
|
|||||||
var tag domain.TagItem
|
var tag domain.TagItem
|
||||||
err = rows.Scan(&tag.ID, &tag.Name, &tag.Color, &tag.Category.ID, &tag.Category.Name, &tag.Category.Color)
|
err = rows.Scan(&tag.ID, &tag.Name, &tag.Color, &tag.Category.ID, &tag.Category.Name, &tag.Category.Color)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
domainErr = domain.NewUnexpectedError(err)
|
domainErr = domain.NewErrorUnexpected().Wrap(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"tanabata/internal/domain"
|
"tanabata/internal/domain"
|
||||||
@ -17,28 +16,28 @@ type ErrorMapper struct{}
|
|||||||
|
|
||||||
func (m *ErrorMapper) MapError(err domain.DomainError) (int, ErrorResponse) {
|
func (m *ErrorMapper) MapError(err domain.DomainError) (int, ErrorResponse) {
|
||||||
switch err.Code {
|
switch err.Code {
|
||||||
case domain.ErrFileNotFound:
|
case domain.ErrCodeFileNotFound:
|
||||||
return http.StatusNotFound, ErrorResponse{
|
return http.StatusNotFound, ErrorResponse{
|
||||||
Error: "Not Found",
|
Error: "Not Found",
|
||||||
Code: string(err.Code),
|
Code: string(err.Code),
|
||||||
Message: fmt.Sprintf("File %q not found", err.Details...),
|
Message: err.Message,
|
||||||
}
|
}
|
||||||
case domain.ErrMIMENotSupported:
|
case domain.ErrCodeMIMENotSupported:
|
||||||
return http.StatusNotFound, ErrorResponse{
|
return http.StatusNotFound, ErrorResponse{
|
||||||
Error: "MIME not supported",
|
Error: "MIME not supported",
|
||||||
Code: string(err.Code),
|
Code: string(err.Code),
|
||||||
Message: fmt.Sprintf("MIME not supported: %q", err.Details...),
|
Message: err.Message,
|
||||||
}
|
}
|
||||||
case domain.ErrValidation:
|
case domain.ErrCodeBadRequest:
|
||||||
return http.StatusNotFound, ErrorResponse{
|
return http.StatusNotFound, ErrorResponse{
|
||||||
Error: "Bad Request",
|
Error: "Bad Request",
|
||||||
Code: string(err.Code),
|
Code: string(err.Code),
|
||||||
Message: fmt.Sprintf("Invalid %s: %s", err.Details...),
|
Message: err.Message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return http.StatusInternalServerError, ErrorResponse{
|
return http.StatusInternalServerError, ErrorResponse{
|
||||||
Error: "Internal Server Error",
|
Error: "Internal Server Error",
|
||||||
Code: string(err.Code),
|
Code: string(err.Code),
|
||||||
Message: "An unexpected error occured",
|
Message: err.Message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user