initial commit
This commit is contained in:
commit
1253955070
75 changed files with 13972 additions and 0 deletions
116
api/internal/store/store.go
Normal file
116
api/internal/store/store.go
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Package store defines persistence for the domain entities. The interface
|
||||
// exists so that the query implementation can be swapped (a different SQL
|
||||
// dialect, or an in-memory fake in tests) without touching the service layer.
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go-places-api/internal/domain"
|
||||
"go-places-api/internal/geo"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotFound is returned when a lookup matches no row.
|
||||
ErrNotFound = errors.New("not found")
|
||||
// ErrConflict is returned when a unique constraint would be violated.
|
||||
ErrConflict = errors.New("conflict")
|
||||
)
|
||||
|
||||
// ImageFilter narrows a picture search.
|
||||
type ImageFilter struct {
|
||||
// PlaceID restricts results to one place when non-empty.
|
||||
PlaceID string
|
||||
// CreatorID restricts results to one author when non-empty.
|
||||
CreatorID string
|
||||
// Within restricts results to pictures inside this box and radius. A nil
|
||||
// Coordinate disables the geographic restriction.
|
||||
Within *NearbyQuery
|
||||
// Limit caps the number of rows. Zero means unlimited.
|
||||
Limit int
|
||||
// Offset skips rows for pagination.
|
||||
Offset int
|
||||
}
|
||||
|
||||
// NearbyQuery selects entities within a radius of a point.
|
||||
type NearbyQuery struct {
|
||||
Center domain.Coordinate
|
||||
RadiusMeters float64
|
||||
}
|
||||
|
||||
// Store is the persistence contract required by the service layer.
|
||||
type Store interface {
|
||||
// CreatePlace inserts a place and returns it as persisted.
|
||||
CreatePlace(ctx context.Context, p domain.Place) (domain.Place, error)
|
||||
// GetPlace loads a place, its pictures, comments and author.
|
||||
GetPlace(ctx context.Context, id string) (domain.Place, error)
|
||||
// UpdatePlace replaces the mutable fields of a place.
|
||||
UpdatePlace(ctx context.Context, p domain.Place) (domain.Place, error)
|
||||
// ListPlaces returns places newest first.
|
||||
ListPlaces(ctx context.Context, limit, offset int) ([]domain.Place, error)
|
||||
// DeletePlace removes a place and everything attached to it.
|
||||
DeletePlace(ctx context.Context, id string) error
|
||||
|
||||
// CreateImage inserts a picture record.
|
||||
CreateImage(ctx context.Context, img domain.Image) (domain.Image, error)
|
||||
// GetImage loads one picture.
|
||||
GetImage(ctx context.Context, id string) (domain.Image, error)
|
||||
// GetImageByStorageKey loads the picture a blob key belongs to. The media
|
||||
// route needs it to learn the declared content type and size of a stored
|
||||
// object without trusting the file's own bytes.
|
||||
GetImageByStorageKey(ctx context.Context, key string) (domain.Image, error)
|
||||
// UpdateImage replaces the mutable fields of a picture.
|
||||
UpdateImage(ctx context.Context, img domain.Image) (domain.Image, error)
|
||||
// ListImages returns pictures matching the filter, newest first.
|
||||
ListImages(ctx context.Context, f ImageFilter) ([]domain.Image, error)
|
||||
// ImagesWithin returns pictures whose capture point lies inside the radius.
|
||||
ImagesWithin(ctx context.Context, q NearbyQuery) ([]domain.Image, error)
|
||||
// ReplaceBadges swaps the badge set for a picture.
|
||||
ReplaceBadges(ctx context.Context, imageID string, badges []domain.Badge) error
|
||||
// CreateImageLink records that two pictures were taken at the same place.
|
||||
// Re-recording the same pair updates the measured distance.
|
||||
CreateImageLink(ctx context.Context, imageID, relatedImageID string, distanceMeters float64, reason string) error
|
||||
// ListImageLinks returns the recorded links for a picture in both
|
||||
// directions, so a link is visible from either end.
|
||||
ListImageLinks(ctx context.Context, imageID string) ([]domain.Link, error)
|
||||
// SetAnalysisState records the outcome of the analysis pipeline.
|
||||
SetAnalysisState(ctx context.Context, imageID string, status domain.AnalysisStatus, failure string) error
|
||||
// DeleteImage removes a picture and its badges.
|
||||
DeleteImage(ctx context.Context, id string) error
|
||||
|
||||
// AddComment inserts a comment and returns it as persisted.
|
||||
AddComment(ctx context.Context, c domain.Comment) (domain.Comment, error)
|
||||
// ListComments returns the comments on a place, oldest first.
|
||||
ListComments(ctx context.Context, placeID string) ([]domain.Comment, error)
|
||||
|
||||
// UpsertUser stores the identity asserted by the auth layer.
|
||||
UpsertUser(ctx context.Context, u domain.User) error
|
||||
// GetUser loads a user by identifier.
|
||||
GetUser(ctx context.Context, id string) (domain.User, error)
|
||||
|
||||
// Close releases resources held by the store.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// BoundingBox converts a nearby query into the coarse window used to pre-filter
|
||||
// rows in SQL. Exposed here so implementations and tests agree on the filter.
|
||||
func (q NearbyQuery) BoundingBox() geo.BoundingBox {
|
||||
return geo.Around(q.Center, q.RadiusMeters)
|
||||
}
|
||||
|
||||
// ErrInvalidInput is returned when a write would violate a domain rule, such as
|
||||
// linking a picture to itself.
|
||||
var ErrInvalidInput = errors.New("invalid input")
|
||||
|
||||
// ImageGetter is the optional extra surface the service uses for storage-key
|
||||
// lookups. It is separate from Store so an implementation that cannot map a key
|
||||
// back to a record simply does not satisfy it.
|
||||
type ImageGetter interface {
|
||||
GetImageByStorageKey(ctx context.Context, key string) (domain.Image, error)
|
||||
}
|
||||
|
||||
// Pinger is the optional extra surface used by the readiness probe.
|
||||
type Pinger interface {
|
||||
Ping(ctx context.Context) error
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue