initial commit
This commit is contained in:
commit
1253955070
75 changed files with 13972 additions and 0 deletions
160
shared-interfaces/schema/models.graphql
Normal file
160
shared-interfaces/schema/models.graphql
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# --- Shared Data Models (Go source of truth) ---
|
||||
#
|
||||
# These structs are documentation of the domain layer; the executable GraphQL
|
||||
# schema lives in `api/internal/transport/schema.go` and is what clients get
|
||||
# from an introspection query. The two are kept in step: every field below has a
|
||||
# matching field in the executable schema, and vice versa.
|
||||
#
|
||||
# Note the `json` tag on each field. The Go handlers serialise the domain types
|
||||
# directly, so these tags are also the REST field names, and the REST client in
|
||||
# `web/src/types.ts` mirrors them.
|
||||
|
||||
// Coordinate is a WGS-84 geographic point.
|
||||
type Coordinate struct {
|
||||
Latitude float64 `json:"latitude" gql:"latitude"`
|
||||
Longitude float64 `json:"longitude" gql:"longitude"`
|
||||
}
|
||||
|
||||
// User represents a logged-in user. Identity is owned by the external go-loose
|
||||
// SSO service; this service only stores the identifier it asserts.
|
||||
type User struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
Username string `json:"username" gql:"username"`
|
||||
Email string `json:"email" gql:"email"`
|
||||
}
|
||||
|
||||
// Badge is a recognisable attribute detected in a picture, for example an object
|
||||
// ("dog"), a scene ("beach") or a property ("low-light"). Label is the stable
|
||||
// machine-readable name; Display is the human-facing rendering. Confidence is a
|
||||
// 0..1 score, and Source records which analyzer produced the badge.
|
||||
type Badge struct {
|
||||
Label string `json:"label" gql:"label"`
|
||||
Display string `json:"display" gql:"display"`
|
||||
Confidence float64 `json:"confidence" gql:"confidence"`
|
||||
Source string `json:"source" gql:"source"`
|
||||
}
|
||||
|
||||
// Image is a picture attached to a Place.
|
||||
type Image struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
PlaceID string `json:"placeId" gql:"placeId"`
|
||||
|
||||
// StorageKey is the location of the bytes inside the blob store. It is never
|
||||
// exposed: URL is the public rendering of this key.
|
||||
StorageKey string `json:"-" gql:"-"`
|
||||
URL string `json:"url" gql:"url"`
|
||||
Caption string `json:"caption" gql:"caption"`
|
||||
ContentType string `json:"contentType" gql:"contentType"`
|
||||
ByteSize int64 `json:"byteSize" gql:"byteSize"`
|
||||
Width int `json:"width" gql:"width"`
|
||||
Height int `json:"height" gql:"height"`
|
||||
|
||||
// Coordinate is where the picture was taken: the EXIF GPS tag when present,
|
||||
// and the parent Place otherwise.
|
||||
Coordinate *Coordinate `json:"coordinate" gql:"coordinate"`
|
||||
// TakenAt, CameraMake and CameraModel come from EXIF.
|
||||
TakenAt *time.Time `json:"takenAt" gql:"takenAt"`
|
||||
CameraMake string `json:"cameraMake" gql:"cameraMake"`
|
||||
CameraModel string `json:"cameraModel" gql:"cameraModel"`
|
||||
|
||||
Badges []Badge `json:"badges" gql:"badges"`
|
||||
AnalysisStatus string `json:"analysisStatus" gql:"analysisStatus"`
|
||||
AnalysisError string `json:"analysisError" gql:"analysisError"`
|
||||
|
||||
// LinkedImages are other pictures taken at the same location. They are
|
||||
// resolved by the linking pass on read, never stored.
|
||||
LinkedImages []Image `json:"linkedImages" gql:"linkedImages"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt" gql:"createdAt"`
|
||||
}
|
||||
|
||||
// Place is a location the user visited and shared. It anchors geographic linking.
|
||||
type Place struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
Title string `json:"title" gql:"title"`
|
||||
Description string `json:"description" gql:"description"`
|
||||
|
||||
// Coordinate is required for linking to work. LinkRadiusMeters is how close
|
||||
// two pictures must be to count as the same place.
|
||||
Coordinate *Coordinate `json:"coordinate" gql:"coordinate"`
|
||||
LinkRadiusMeters float64 `json:"linkRadiusMeters" gql:"linkRadiusMeters"`
|
||||
|
||||
Creator User `json:"creator" gql:"creator"`
|
||||
Images []Image `json:"images" gql:"images"`
|
||||
Comments []Comment `json:"comments" gql:"comments"`
|
||||
// RelatedPlaces are other Places whose pictures overlap this one.
|
||||
RelatedPlaces []RelatedPlace `json:"relatedPlaces" gql:"relatedPlaces"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt" gql:"createdAt"`
|
||||
}
|
||||
|
||||
// RelatedPlace records the link between two Places discovered by the linking
|
||||
// pass, along with the evidence for it.
|
||||
type RelatedPlace struct {
|
||||
Place Place `json:"place" gql:"place"`
|
||||
DistanceMeters float64 `json:"distanceMeters" gql:"distanceMeters"`
|
||||
SharedImageIDs []string `json:"sharedImageIds" gql:"sharedImageIds"`
|
||||
}
|
||||
|
||||
// Comment is a user reaction on a Place.
|
||||
type Comment struct {
|
||||
ID string `json:"id" gql:"id"`
|
||||
PlaceID string `json:"placeId" gql:"placeId"`
|
||||
Content string `json:"content" gql:"content"`
|
||||
Creator User `json:"creator" gql:"creator"`
|
||||
CreatedAt time.Time `json:"createdAt" gql:"createdAt"`
|
||||
}
|
||||
|
||||
// Link is a single edge produced by the linking pass, connecting one picture to
|
||||
// another taken at the same place. Links are a diagnostic view and are not
|
||||
// exposed over the API.
|
||||
type Link struct {
|
||||
ImageID string `json:"imageId" gql:"-"`
|
||||
RelatedImageID string `json:"relatedImageId" gql:"-"`
|
||||
DistanceMeters float64 `json:"distanceMeters" gql:"-"`
|
||||
// SamePlace is true when both pictures belong to the same Place record.
|
||||
SamePlace bool `json:"samePlace" gql:"-"`
|
||||
Reason string `json:"reason" gql:"-"`
|
||||
}
|
||||
|
||||
// PlaceInput is the payload for creating a Place, and the argument to the
|
||||
// createPlace mutation. Latitude and Longitude are pointers so an omitted
|
||||
// coordinate can be told apart from the origin, which is a legitimate point.
|
||||
type PlaceInput struct {
|
||||
Title string `json:"title" gql:"title"`
|
||||
Description string `json:"description" gql:"description"`
|
||||
Latitude *float64 `json:"latitude" gql:"latitude"`
|
||||
Longitude *float64 `json:"longitude" gql:"longitude"`
|
||||
LinkRadiusMeters *float64 `json:"linkRadiusMeters" gql:"linkRadiusMeters"`
|
||||
// Caption is the description of the first picture in the upload, if any.
|
||||
Caption string `json:"caption" gql:"caption"`
|
||||
}
|
||||
|
||||
// --- Queries ---
|
||||
type Query struct {
|
||||
GetPlace(id string) *Place `gql:"place"`
|
||||
GetAllPlaces() []*Place `gql:"places"`
|
||||
GetImages() []*Image `gql:"images"`
|
||||
GetRelatedPlaces() []*RelatedPlace `gql:"relatedPlaces"`
|
||||
}
|
||||
|
||||
// --- Mutations ---
|
||||
type Mutation struct {
|
||||
CreatePlace(input PlaceInput) *Place `gql:"createPlace"`
|
||||
AddComment(placeID, content) *Comment `gql:"addComment"`
|
||||
UpdateImageCaption(imageID, caption) *Image `gql:"updateImageCaption"`
|
||||
DeletePlace(id string) bool `gql:"deletePlace"`
|
||||
DeleteImage(id string) bool `gql:"deleteImage"`
|
||||
}
|
||||
|
||||
// --- REST surface ---
|
||||
//
|
||||
// The React client uses these instead of GraphQL; both serialise the same
|
||||
// domain structs, so the shapes match field for field. Enabled with
|
||||
// ENABLE_REST=true.
|
||||
//
|
||||
// GET /api/places -> []Place
|
||||
// POST /api/places -> Place
|
||||
// GET /api/places/{placeId} -> Place (with relatedPlaces)
|
||||
// GET /api/places/{placeId}/images -> []Image
|
||||
// POST /places/{placeId}/images -> {"image": Image} multipart
|
||||
Loading…
Add table
Add a link
Reference in a new issue