Haystack V2: Removing entities completely

This commit is contained in:
2025-07-29 14:52:33 +01:00
parent 3d05ff708e
commit a0bf27dd16
47 changed files with 16 additions and 3052 deletions

View File

@@ -1,117 +0,0 @@
package models
import (
"context"
"database/sql"
"screenmark/screenmark/.gen/haystack/haystack/model"
. "screenmark/screenmark/.gen/haystack/haystack/table"
. "github.com/go-jet/jet/v2/postgres"
"github.com/google/uuid"
)
type ContactModel struct {
dbPool *sql.DB
}
func (m ContactModel) List(ctx context.Context, userId uuid.UUID) ([]model.Contacts, error) {
listContactsStmt := SELECT(Contacts.AllColumns).
FROM(
Contacts.
INNER_JOIN(UserContacts, UserContacts.ContactID.EQ(Contacts.ID)),
).
WHERE(UserContacts.UserID.EQ(UUID(userId)))
locations := []model.Contacts{}
err := listContactsStmt.QueryContext(ctx, m.dbPool, &locations)
return locations, err
}
func (m ContactModel) Get(ctx context.Context, contactId uuid.UUID) (model.Contacts, error) {
getContactStmt := Contacts.
SELECT(Contacts.AllColumns).
WHERE(Contacts.ID.EQ(UUID(contactId)))
contact := model.Contacts{}
err := getContactStmt.QueryContext(ctx, m.dbPool, &contact)
return contact, err
}
func (m ContactModel) Update(ctx context.Context, contact model.Contacts) (model.Contacts, error) {
existingContact, err := m.Get(ctx, contact.ID)
if err != nil {
return model.Contacts{}, err
}
existingContact.Name = contact.Name
if contact.Description != nil {
existingContact.Description = contact.Description
}
if contact.PhoneNumber != nil {
existingContact.PhoneNumber = contact.PhoneNumber
}
if contact.Email != nil {
existingContact.Email = contact.Email
}
updateContactStmt := Contacts.
UPDATE(Contacts.MutableColumns).
MODEL(existingContact).
WHERE(Contacts.ID.EQ(UUID(contact.ID))).
RETURNING(Contacts.AllColumns)
updatedContact := model.Contacts{}
err = updateContactStmt.QueryContext(ctx, m.dbPool, &updatedContact)
return updatedContact, err
}
func (m ContactModel) Save(ctx context.Context, userId uuid.UUID, contact model.Contacts) (model.Contacts, error) {
// TODO: make this a transaction
if contact.ID != uuid.Nil {
return m.Update(ctx, contact)
}
insertContactStmt := Contacts.
INSERT(Contacts.Name, Contacts.Description, Contacts.PhoneNumber, Contacts.Email).
VALUES(contact.Name, contact.Description, contact.PhoneNumber, contact.Email).
RETURNING(Contacts.AllColumns)
insertedContact := model.Contacts{}
err := insertContactStmt.QueryContext(ctx, m.dbPool, &insertedContact)
if err != nil {
return insertedContact, err
}
insertUserContactStmt := UserContacts.
INSERT(UserContacts.UserID, UserContacts.ContactID).
VALUES(userId, insertedContact.ID)
_, err = insertUserContactStmt.ExecContext(ctx, m.dbPool)
return insertedContact, err
}
func (m ContactModel) SaveToImage(ctx context.Context, imageId uuid.UUID, contactId uuid.UUID) (model.ImageContacts, error) {
insertImageContactStmt := ImageContacts.
INSERT(ImageContacts.ImageID, ImageContacts.ContactID).
VALUES(imageId, contactId).
RETURNING(ImageContacts.AllColumns)
imageContact := model.ImageContacts{}
err := insertImageContactStmt.QueryContext(ctx, m.dbPool, &imageContact)
return imageContact, err
}
func NewContactModel(db *sql.DB) ContactModel {
return ContactModel{dbPool: db}
}

View File

@@ -1,94 +0,0 @@
package models
import (
"context"
"database/sql"
. "github.com/go-jet/jet/v2/postgres"
"screenmark/screenmark/.gen/haystack/haystack/model"
. "screenmark/screenmark/.gen/haystack/haystack/table"
"github.com/google/uuid"
)
type EventModel struct {
dbPool *sql.DB
}
func (m EventModel) List(ctx context.Context, userId uuid.UUID) ([]model.Events, error) {
listEventsStmt := SELECT(Events.AllColumns).
FROM(
Events.
INNER_JOIN(UserEvents, UserEvents.EventID.EQ(Events.ID)),
).
WHERE(UserEvents.UserID.EQ(UUID(userId)))
events := []model.Events{}
err := listEventsStmt.QueryContext(ctx, m.dbPool, &events)
return events, err
}
func (m EventModel) Save(ctx context.Context, userId uuid.UUID, event model.Events) (model.Events, error) {
// TODO tx here
insertEventStmt := Events.
INSERT(Events.MutableColumns).
MODEL(event).
RETURNING(Events.AllColumns)
insertedEvent := model.Events{}
err := insertEventStmt.QueryContext(ctx, m.dbPool, &insertedEvent)
if err != nil {
return insertedEvent, err
}
insertUserEventStmt := UserEvents.
INSERT(UserEvents.UserID, UserEvents.EventID).
VALUES(userId, insertedEvent.ID)
_, err = insertUserEventStmt.ExecContext(ctx, m.dbPool)
return insertedEvent, err
}
func (m EventModel) SaveToImage(ctx context.Context, imageId uuid.UUID, eventId uuid.UUID) (model.ImageEvents, error) {
insertImageEventStmt := ImageEvents.
INSERT(ImageEvents.ImageID, ImageEvents.EventID).
VALUES(imageId, eventId).
RETURNING(ImageEvents.AllColumns)
imageEvent := model.ImageEvents{}
err := insertImageEventStmt.QueryContext(ctx, m.dbPool, &imageEvent)
return imageEvent, err
}
func (m EventModel) UpdateLocation(ctx context.Context, eventId uuid.UUID, locationId uuid.UUID) (model.Events, error) {
updateEventLocationStmt := Events.
UPDATE(Events.LocationID).
SET(locationId).
WHERE(Events.ID.EQ(UUID(eventId))).
RETURNING(Events.AllColumns)
updatedEvent := model.Events{}
err := updateEventLocationStmt.QueryContext(ctx, m.dbPool, &updatedEvent)
return updatedEvent, err
}
func (m EventModel) UpdateOrganizer(ctx context.Context, eventId uuid.UUID, organizerId uuid.UUID) (model.Events, error) {
updateEventContactStmt := Events.
UPDATE(Events.OrganizerID).
SET(organizerId).
WHERE(Events.ID.EQ(UUID(eventId))).
RETURNING(Events.AllColumns)
updatedEvent := model.Events{}
err := updateEventContactStmt.QueryContext(ctx, m.dbPool, &updatedEvent)
return updatedEvent, err
}
func NewEventModel(db *sql.DB) EventModel {
return EventModel{dbPool: db}
}

View File

@@ -1,130 +0,0 @@
package models
import (
"context"
"database/sql"
"screenmark/screenmark/.gen/haystack/haystack/model"
. "screenmark/screenmark/.gen/haystack/haystack/table"
. "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/qrm"
"github.com/google/uuid"
)
type LocationModel struct {
dbPool *sql.DB
}
func (m LocationModel) List(ctx context.Context, userId uuid.UUID) ([]model.Locations, error) {
listLocationsStmt := SELECT(Locations.AllColumns).
FROM(
Locations.
INNER_JOIN(UserLocations, UserLocations.LocationID.EQ(Locations.ID)),
).
WHERE(UserLocations.UserID.EQ(UUID(userId)))
locations := []model.Locations{}
err := listLocationsStmt.QueryContext(ctx, m.dbPool, &locations)
return locations, err
}
func (m LocationModel) Get(ctx context.Context, locationId uuid.UUID) (model.Locations, error) {
getLocationStmt := Locations.
SELECT(Locations.AllColumns).
WHERE(Locations.ID.EQ(UUID(locationId)))
location := model.Locations{}
err := getLocationStmt.QueryContext(ctx, m.dbPool, &location)
return location, err
}
func (m LocationModel) Update(ctx context.Context, location model.Locations) (model.Locations, error) {
existingLocation, err := m.Get(ctx, location.ID)
if err != nil {
return model.Locations{}, err
}
existingLocation.Name = location.Name
if location.Description != nil {
existingLocation.Description = location.Description
}
if location.Address != nil {
existingLocation.Address = location.Address
}
updateLocationStmt := Locations.
UPDATE(Locations.MutableColumns).
MODEL(existingLocation).
WHERE(Locations.ID.EQ(UUID(location.ID))).
RETURNING(Locations.AllColumns)
updatedLocation := model.Locations{}
err = updateLocationStmt.QueryContext(ctx, m.dbPool, &updatedLocation)
return updatedLocation, err
}
func (m LocationModel) Save(ctx context.Context, userId uuid.UUID, location model.Locations) (model.Locations, error) {
if location.ID != uuid.Nil {
return m.Update(ctx, location)
}
insertLocationStmt := Locations.
INSERT(Locations.Name, Locations.Address, Locations.Description).
VALUES(location.Name, location.Address, location.Description).
RETURNING(Locations.AllColumns)
insertedLocation := model.Locations{}
err := insertLocationStmt.QueryContext(ctx, m.dbPool, &insertedLocation)
if err != nil {
return model.Locations{}, err
}
insertUserLocationStmt := UserLocations.
INSERT(UserLocations.UserID, UserLocations.LocationID).
VALUES(userId, insertedLocation.ID)
_, err = insertUserLocationStmt.ExecContext(ctx, m.dbPool)
return insertedLocation, err
}
func (m LocationModel) SaveToImage(ctx context.Context, imageId uuid.UUID, locationId uuid.UUID) (model.ImageLocations, error) {
imageLocation := model.ImageLocations{}
checkExistingStmt := ImageLocations.
SELECT(ImageLocations.AllColumns).
WHERE(
ImageLocations.ImageID.EQ(UUID(imageId)).
AND(ImageLocations.LocationID.EQ(UUID(locationId))),
)
err := checkExistingStmt.QueryContext(ctx, m.dbPool, &imageLocation)
if err != nil && err != qrm.ErrNoRows {
// A real error
return model.ImageLocations{}, err
}
if err == nil {
// Already exists.
return imageLocation, nil
}
insertImageLocationStmt := ImageLocations.
INSERT(ImageLocations.ImageID, ImageLocations.LocationID).
VALUES(imageId, locationId).
RETURNING(ImageLocations.AllColumns)
err = insertImageLocationStmt.QueryContext(ctx, m.dbPool, &imageLocation)
return imageLocation, err
}
func NewLocationModel(db *sql.DB) LocationModel {
return LocationModel{dbPool: db}
}

View File

@@ -19,189 +19,6 @@ type ImageWithProperties struct {
ID uuid.UUID
Image model.Image
Locations []model.Locations
Events []model.Events
Contacts []model.Contacts
}
type PropertiesWithImage struct {
Locations []struct {
model.Locations
Images uuid.UUIDs
}
Contacts []struct {
model.Contacts
Images uuid.UUIDs
}
Events []struct {
model.Events
Images uuid.UUIDs
}
}
func transpose(imageProperties []ImageWithProperties) PropertiesWithImage {
// EntityID -> []ImageIDs
dependencies := make(map[uuid.UUID]uuid.UUIDs)
addDependency := func(entityId uuid.UUID, imageId uuid.UUID) {
deps, exists := dependencies[entityId]
if !exists {
dep := uuid.UUIDs{imageId}
dependencies[entityId] = dep
return
}
dependencies[entityId] = append(deps, imageId)
}
contactMap := make(map[uuid.UUID]model.Contacts)
locationMap := make(map[uuid.UUID]model.Locations)
eventMap := make(map[uuid.UUID]model.Events)
for _, image := range imageProperties {
for _, contact := range image.Contacts {
contactMap[contact.ID] = contact
addDependency(contact.ID, image.Image.ID)
}
for _, location := range image.Locations {
locationMap[location.ID] = location
addDependency(location.ID, image.Image.ID)
}
for _, event := range image.Events {
eventMap[event.ID] = event
addDependency(event.ID, image.Image.ID)
}
}
properties := PropertiesWithImage{
Contacts: make([]struct {
model.Contacts
Images uuid.UUIDs
}, 0),
Locations: make([]struct {
model.Locations
Images uuid.UUIDs
}, 0),
Events: make([]struct {
model.Events
Images uuid.UUIDs
}, 0),
}
for contactId, contact := range contactMap {
properties.Contacts = append(properties.Contacts, struct {
model.Contacts
Images uuid.UUIDs
}{
Contacts: contact,
Images: dependencies[contactId],
})
}
for locationId, location := range locationMap {
properties.Locations = append(properties.Locations, struct {
model.Locations
Images uuid.UUIDs
}{
Locations: location,
Images: dependencies[locationId],
})
}
for eventId, event := range eventMap {
properties.Events = append(properties.Events, struct {
model.Events
Images uuid.UUIDs
}{
Events: event,
Images: dependencies[eventId],
})
}
return properties
}
type TypedProperties struct {
Type string `json:"type"`
Data any `json:"data"`
}
func propertiesToTypeArray(properties PropertiesWithImage) []TypedProperties {
typedProperties := make([]TypedProperties, 0)
for _, location := range properties.Locations {
typedProperties = append(typedProperties, TypedProperties{
Type: "location",
Data: location,
})
}
for _, contact := range properties.Contacts {
typedProperties = append(typedProperties, TypedProperties{
Type: "contact",
Data: contact,
})
}
for _, event := range properties.Events {
typedProperties = append(typedProperties, TypedProperties{
Type: "event",
Data: event,
})
}
return typedProperties
}
func GetTypedImageProperties(imageProperties []ImageWithProperties) []TypedProperties {
return propertiesToTypeArray(transpose(imageProperties))
}
func getListImagesStmt() SelectStatement {
return SELECT(
UserImages.ID.AS("ImageWithProperties.ID"),
Image.ID,
Image.ImageName,
ImageLocations.AllColumns,
Locations.AllColumns,
ImageEvents.AllColumns,
Events.AllColumns,
ImageContacts.AllColumns,
Contacts.AllColumns,
).
FROM(
UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID)).
LEFT_JOIN(ImageLocations, ImageLocations.ImageID.EQ(UserImages.ImageID)).
LEFT_JOIN(Locations, Locations.ID.EQ(ImageLocations.LocationID)).
LEFT_JOIN(ImageEvents, ImageEvents.ImageID.EQ(UserImages.ImageID)).
LEFT_JOIN(Events, Events.ID.EQ(ImageEvents.EventID)).
LEFT_JOIN(ImageContacts, ImageContacts.ImageID.EQ(UserImages.ImageID)).
LEFT_JOIN(Contacts, Contacts.ID.EQ(ImageContacts.ContactID)))
}
func (m UserModel) ListImageWithProperties(ctx context.Context, userId uuid.UUID, imageId uuid.UUID) (ImageWithProperties, error) {
listImagePropertiesStmt := getListImagesStmt().
WHERE(UserImages.ImageID.EQ(UUID(imageId)))
image := ImageWithProperties{}
err := listImagePropertiesStmt.QueryContext(ctx, m.dbPool, &image)
return image, err
}
func (m UserModel) ListWithProperties(ctx context.Context, userId uuid.UUID) ([]ImageWithProperties, error) {
listWithPropertiesStmt := getListImagesStmt().
WHERE(UserImages.UserID.EQ(UUID(userId)))
images := []ImageWithProperties{}
err := listWithPropertiesStmt.QueryContext(ctx, m.dbPool, &images)
return images, err
}
func (m UserModel) GetUserIdFromEmail(ctx context.Context, email string) (uuid.UUID, error) {