Compare commits
No commits in common. "main" and "gitea-24" have entirely different histories.
|
|
@ -23,8 +23,6 @@ func (a *Admin) CreateGroup(name string, reference string) (*Group, error)
|
||||||
|
|
||||||
func (*Admin) GetUser(id string) (*User, error)
|
func (*Admin) GetUser(id string) (*User, error)
|
||||||
|
|
||||||
func (a *Admin) ListEvents() ([]Event, error)
|
|
||||||
|
|
||||||
func (a *Admin) ListGroups() ([]Group, error)
|
func (a *Admin) ListGroups() ([]Group, error)
|
||||||
|
|
||||||
func (*Admin) ListUsers() ([]User, error)
|
func (*Admin) ListUsers() ([]User, error)
|
||||||
|
|
@ -39,15 +37,6 @@ func (u *Admin) UserSetLive(userReference string, setting bool) error
|
||||||
|
|
||||||
type ErrorInvalidCredentials error
|
type ErrorInvalidCredentials error
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
Id string
|
|
||||||
ActorId string
|
|
||||||
ActionType string
|
|
||||||
TargetType string
|
|
||||||
TargetId string
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type Group struct {
|
type Group struct {
|
||||||
Id string
|
Id string
|
||||||
Name string
|
Name string
|
||||||
|
|
@ -83,7 +72,7 @@ func (s *Session) GetOthersWishes(userReference string) ([]Wish, error)
|
||||||
|
|
||||||
func (s *Session) GetWishes() ([]Wish, error)
|
func (s *Session) GetWishes() ([]Wish, error)
|
||||||
|
|
||||||
func (s *Session) MakeWish(name string) (string, error)
|
func (s *Session) MakeWish(name string) error
|
||||||
|
|
||||||
func (s *Session) RecindWishesForUser(ids ...string) error
|
func (s *Session) RecindWishesForUser(ids ...string) error
|
||||||
|
|
||||||
|
|
|
||||||
150
core/event.go
150
core/event.go
|
|
@ -1,150 +0,0 @@
|
||||||
package lishwist
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"lishwist/core/internal/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
eventActionCreate = "CREATE"
|
|
||||||
eventActionHide = "HIDE"
|
|
||||||
eventActionUnhide = "UNHIDE"
|
|
||||||
eventActionClaim = "CLAIM"
|
|
||||||
eventActionUnclaim = "UNCLAIM"
|
|
||||||
eventActionComplete = "COMPLETE"
|
|
||||||
// eventActionDelete = "DELETE" NOTE: We can't have this, because there'll be no target to reference
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
eventTargetGroup = "GROUP"
|
|
||||||
eventTargetUser = "USER"
|
|
||||||
eventTargetWish = "WISH"
|
|
||||||
eventTargetGroupMember = "GROUP_MEMBER"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
Id string
|
|
||||||
ActorId string
|
|
||||||
ActionType string
|
|
||||||
TargetType string
|
|
||||||
TargetId string
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// type EventCreateGroupMember struct {
|
|
||||||
// Event
|
|
||||||
// Actor User
|
|
||||||
// User
|
|
||||||
// Group
|
|
||||||
// }
|
|
||||||
|
|
||||||
func queryManyEvents(query string, args ...any) ([]Event, error) {
|
|
||||||
rows, err := db.Connection.Query(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
events := []Event{}
|
|
||||||
for rows.Next() {
|
|
||||||
var g Event
|
|
||||||
var createdAt string
|
|
||||||
err = rows.Scan(&g.Id, &g.ActorId, &g.ActionType, &g.TargetType, &g.TargetId, &createdAt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
g.CreatedAt, err = time.Parse(time.RFC3339Nano, createdAt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse created_at: %w", err)
|
|
||||||
}
|
|
||||||
events = append(events, g)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return events, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryOneEvent(query string, args ...any) (*Event, error) {
|
|
||||||
events, err := queryManyEvents(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(events) < 1 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return &events[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Admin) ListEvents() ([]Event, error) {
|
|
||||||
query := "SELECT id, actor_id, action_type, target_type, target_id, created_at FROM event;"
|
|
||||||
return queryManyEvents(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEvent(actorId, actionType, targetType string, targetIds ...string) {
|
|
||||||
// TODO: If this were to accept sql.Tx it could be used in atomic transactions
|
|
||||||
numTargets := len(targetIds)
|
|
||||||
if numTargets < 1 {
|
|
||||||
log.Println("Warning: recordEvent called with no target IDs. Skipping.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stmt := "INSERT INTO event (actor_id, action_type, target_type, target_id) VALUES (?, ?, ?, ?)"
|
|
||||||
extraValuePlaceholders := strings.Repeat(", (?, ?, ?, ?)", numTargets-1)
|
|
||||||
args := make([]any, numTargets*4)
|
|
||||||
for i, id := range targetIds {
|
|
||||||
args[i*4] = actorId
|
|
||||||
args[i*4+1] = actionType
|
|
||||||
args[i*4+2] = targetType
|
|
||||||
args[i*4+3] = id
|
|
||||||
}
|
|
||||||
_, err := db.Connection.Exec(stmt+extraValuePlaceholders, args...)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if numTargets == 1 {
|
|
||||||
log.Printf("Failed to record %s %s event: failed to execute query: %s\n", actionType, targetType, err)
|
|
||||||
} else {
|
|
||||||
log.Printf("Failed to record %d %s %s events: failed to execute query: %s\n", numTargets, actionType, targetType, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEventCreateGroup(actorId, groupId string) {
|
|
||||||
recordEvent(actorId, eventActionCreate, eventTargetGroup, groupId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEventCreateUser(actorId, userId string) {
|
|
||||||
recordEvent(actorId, eventActionCreate, eventTargetUser, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEventCreateWish(actorId, wishId string) {
|
|
||||||
recordEvent(actorId, eventActionCreate, eventTargetWish, wishId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEventCreateGroupMember(actorId, groupMemberId string) {
|
|
||||||
recordEvent(actorId, eventActionCreate, eventTargetGroupMember, groupMemberId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: I can't use these yet because the associated actions use reference
|
|
||||||
// func recordEventHideUser(actorId, userId string) {
|
|
||||||
// recordEvent(actorId, eventActionHide, eventTargetUser, userId)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func recordEventUnhideUser(actorId, userId string) {
|
|
||||||
// recordEvent(actorId, eventActionUnhide, eventTargetUser, userId)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func recordEventClaimWishes(actorId string, wishIds ...string) {
|
|
||||||
recordEvent(actorId, eventActionClaim, eventTargetWish, wishIds...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEventUnclaimWishes(actorId string, wishIds ...string) {
|
|
||||||
recordEvent(actorId, eventActionUnclaim, eventTargetWish, wishIds...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func recordEventCompleteWishes(actorId string, wishIds ...string) {
|
|
||||||
recordEvent(actorId, eventActionComplete, eventTargetWish, wishIds...)
|
|
||||||
}
|
|
||||||
|
|
@ -103,21 +103,15 @@ func (a *Admin) CreateGroup(name string, reference string) (*Group, error) {
|
||||||
Name: name,
|
Name: name,
|
||||||
Reference: reference,
|
Reference: reference,
|
||||||
}
|
}
|
||||||
recordEventCreateGroup(a.session.user.Id, group.Id)
|
|
||||||
return &group, nil
|
return &group, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Admin) AddUserToGroup(userId, groupId string) error {
|
func (a *Admin) AddUserToGroup(userId, groupId string) error {
|
||||||
stmt := "INSERT INTO group_member (user_id, group_id) VALUES (?, ?)"
|
stmt := "INSERT INTO group_member (user_id, group_id) VALUES (?, ?)"
|
||||||
result, err := db.Connection.Exec(stmt, userId, groupId)
|
_, err := db.Connection.Exec(stmt, userId, groupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("query execution failed: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
id, err := result.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get last insert id: %w", err)
|
|
||||||
}
|
|
||||||
recordEventCreateGroupMember(a.session.user.Id, strconv.FormatInt(id, 10))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,6 @@ func TestCreateGroup(t *testing.T) {
|
||||||
|
|
||||||
fixtures.AssertEq(t, "Number of users", "My Friends", group.Name)
|
fixtures.AssertEq(t, "Number of users", "My Friends", group.Name)
|
||||||
fixtures.AssertEq(t, "Number of users", "my-friends", group.Reference)
|
fixtures.AssertEq(t, "Number of users", "my-friends", group.Reference)
|
||||||
|
|
||||||
// FIXME: disabled for now because datetimes break this
|
|
||||||
// events, err := s.Admin().ListEvents()
|
|
||||||
// assert.FatalErr(t, "listing events", err)
|
|
||||||
|
|
||||||
// assert.JsonSnapshot(t, "TestCreateGroup.snap.txt", events)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCantSeeSelfInGroup(t *testing.T) {
|
func TestCantSeeSelfInGroup(t *testing.T) {
|
||||||
|
|
@ -45,10 +39,4 @@ func TestCantSeeSelfInGroup(t *testing.T) {
|
||||||
fixtures.AssertEq(t, "Group contains 2 users", 2, len(group.Members))
|
fixtures.AssertEq(t, "Group contains 2 users", 2, len(group.Members))
|
||||||
fixtures.AssertEq(t, "Group user 1 is thomas", "thomas", group.Members[0].Name)
|
fixtures.AssertEq(t, "Group user 1 is thomas", "thomas", group.Members[0].Name)
|
||||||
fixtures.AssertEq(t, "Group user 2 is caleb", "caleb", group.Members[1].Name)
|
fixtures.AssertEq(t, "Group user 2 is caleb", "caleb", group.Members[1].Name)
|
||||||
|
|
||||||
// FIXME: disabled for now because datetimes break this
|
|
||||||
// events, err := s.Admin().ListEvents()
|
|
||||||
// assert.FatalErr(t, "listing events", err)
|
|
||||||
|
|
||||||
// assert.JsonSnapshot(t, "TestCantSeeSelfInGroup.snap.txt", events)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"slices"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Eq[C comparable](t *testing.T, context string, expected, actual C) {
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("%s: %#v != %#v", context, expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func True(t *testing.T, context string, condition bool) {
|
|
||||||
if !condition {
|
|
||||||
t.Errorf("false: %s", context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FatalTrue(t *testing.T, context string, condition bool) {
|
|
||||||
if !condition {
|
|
||||||
t.Fatalf("%s", context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FatalErr(t *testing.T, context string, err error) {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%s: %s", context, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FatalErrIs(t *testing.T, context string, err, target error) {
|
|
||||||
if !errors.Is(err, target) {
|
|
||||||
t.Fatalf("%s: encountered unexpected error: %s", context, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FatalErrAs(t *testing.T, context string, err error, target any) {
|
|
||||||
if !errors.As(err, target) {
|
|
||||||
t.Fatalf("%s: encountered unexpected error: %s", context, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SlicesEq[S ~[]E, E comparable](t *testing.T, context string, expected, actual S) {
|
|
||||||
if !slices.Equal(expected, actual) {
|
|
||||||
t.Errorf("%s: %#v != %#v", context, expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
package assert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func pathExists(path string) bool {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
return !errors.Is(err, os.ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeSnapshot(t *testing.T, path, actual string) {
|
|
||||||
f, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create snapshot file: %s", err)
|
|
||||||
}
|
|
||||||
_, err = f.Write([]byte(actual))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to write to snapshot file: %s", err)
|
|
||||||
}
|
|
||||||
err = f.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to close snapshot file: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TextSnapshot(t *testing.T, path, actual string) {
|
|
||||||
if !pathExists(path) {
|
|
||||||
writeSnapshot(t, path, actual)
|
|
||||||
t.Errorf("Snapshot file created: %s", path)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to read snapshot file: %s", err)
|
|
||||||
}
|
|
||||||
expected := string(content)
|
|
||||||
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Value doesn't match snapshot %s:\n%s", path, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.Getenv("UPDATE_SNAPSHOTS") == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSnapshot(t, path, actual)
|
|
||||||
fmt.Printf("Snapshot file %s updated\n", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func JsonSnapshot(t *testing.T, path string, actual any) {
|
|
||||||
data, err := json.MarshalIndent(actual, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Snapshot failed to serialize actual to JSON: %s", err)
|
|
||||||
}
|
|
||||||
TextSnapshot(t, path, string(data))
|
|
||||||
}
|
|
||||||
|
|
@ -30,10 +30,8 @@ CREATE TABLE IF NOT EXISTS "group" (
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
PRIMARY KEY("id" AUTOINCREMENT)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "group_member" (
|
CREATE TABLE IF NOT EXISTS "group_member" (
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"group_id" INTEGER NOT NULL,
|
"group_id" INTEGER NOT NULL,
|
||||||
"user_id" INTEGER NOT NULL,
|
"user_id" INTEGER NOT NULL,
|
||||||
PRIMARY KEY("id" AUTOINCREMENT),
|
|
||||||
UNIQUE("user_id","group_id"),
|
UNIQUE("user_id","group_id"),
|
||||||
FOREIGN KEY("group_id") REFERENCES "group"("id"),
|
FOREIGN KEY("group_id") REFERENCES "group"("id"),
|
||||||
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
||||||
|
|
@ -46,15 +44,6 @@ CREATE TABLE IF NOT EXISTS "session" (
|
||||||
PRIMARY KEY("id" AUTOINCREMENT),
|
PRIMARY KEY("id" AUTOINCREMENT),
|
||||||
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "event" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"actor_id" INTEGER NOT NULL,
|
|
||||||
"action_type" TEXT NOT NULL,
|
|
||||||
"target_type" TEXT NOT NULL,
|
|
||||||
"target_id" INTEGER NOT NULL,
|
|
||||||
"created_at" TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ')),
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
);
|
|
||||||
|
|
||||||
DROP VIEW IF EXISTS "v_user";
|
DROP VIEW IF EXISTS "v_user";
|
||||||
CREATE VIEW "v_user"
|
CREATE VIEW "v_user"
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
BEGIN TRANSACTION;
|
|
||||||
|
|
||||||
ALTER TABLE group_member RENAME TO old_group_member;
|
|
||||||
|
|
||||||
CREATE TABLE "group_member" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"group_id" INTEGER NOT NULL,
|
|
||||||
"user_id" INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT),
|
|
||||||
UNIQUE("user_id","group_id"),
|
|
||||||
FOREIGN KEY("group_id") REFERENCES "group"("id"),
|
|
||||||
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO group_member (group_id, user_id) SELECT group_id, user_id FROM old_group_member;
|
|
||||||
|
|
||||||
DROP TABLE "old_group_member";
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
|
|
@ -2,28 +2,24 @@ package fixtures
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
// Deprecated: use internal/assert
|
|
||||||
func AssertEq[C comparable](t *testing.T, context string, expected, actual C) {
|
func AssertEq[C comparable](t *testing.T, context string, expected, actual C) {
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("%s: %#v != %#v", context, expected, actual)
|
t.Errorf("%s: %#v != %#v", context, expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: use internal/assert
|
|
||||||
func Assert(t *testing.T, context string, condition bool) {
|
func Assert(t *testing.T, context string, condition bool) {
|
||||||
if !condition {
|
if !condition {
|
||||||
t.Errorf("%s", context)
|
t.Errorf("%s", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: use internal/assert
|
|
||||||
func FatalAssert(t *testing.T, context string, condition bool) {
|
func FatalAssert(t *testing.T, context string, condition bool) {
|
||||||
if !condition {
|
if !condition {
|
||||||
t.Fatalf("%s", context)
|
t.Fatalf("%s", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: use internal/assert
|
|
||||||
func FailIfErr(t *testing.T, err error, context string) {
|
func FailIfErr(t *testing.T, err error, context string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s: %s\n", context, err)
|
t.Fatalf("%s: %s\n", context, err)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package fixtures
|
package fixtures
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -14,36 +15,21 @@ func TestInit(t *testing.T) error {
|
||||||
return lishwist.Init(uri)
|
return lishwist.Init(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: This function also inits the test, which prevents it from being used more than once per test
|
|
||||||
func Login(t *testing.T, username, password string) *lishwist.Session {
|
func Login(t *testing.T, username, password string) *lishwist.Session {
|
||||||
uri := memdb.TestDB(t)
|
uri := memdb.TestDB(t)
|
||||||
err := lishwist.Init(uri)
|
err := lishwist.Init(uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to init db: %s\n", err)
|
log.Fatalf("Failed to init db: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = lishwist.Register(username, password)
|
_, err = lishwist.Register(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to register on login fixture: %s\n", err)
|
log.Fatalf("Failed to register on login fixture: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := lishwist.Login(username, password, time.Hour*24)
|
session, err := lishwist.Login(username, password, time.Hour*24)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to login on fixture: %s\n", err)
|
log.Fatalf("Failed to login on fixture: %s\n", err)
|
||||||
}
|
|
||||||
|
|
||||||
return session
|
|
||||||
}
|
|
||||||
|
|
||||||
func Login2(t *testing.T, username, password string) *lishwist.Session {
|
|
||||||
_, err := lishwist.Register(username, password)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to register on login fixture: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := lishwist.Login(username, password, time.Hour*24)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to login on fixture: %s\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return session
|
return session
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,6 @@ func createUser(name string, passHash []byte, isAdmin bool) (*User, error) {
|
||||||
Id: fmt.Sprintf("%d", id),
|
Id: fmt.Sprintf("%d", id),
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
recordEventCreateUser(user.Id, user.Id)
|
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
48
core/wish.go
48
core/wish.go
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"lishwist/core/internal/db"
|
"lishwist/core/internal/db"
|
||||||
|
|
@ -53,20 +52,13 @@ func (s *Session) GetWishes() ([]Wish, error) {
|
||||||
return wishs, nil
|
return wishs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// May return the id of the wish
|
func (s *Session) MakeWish(name string) error {
|
||||||
func (s *Session) MakeWish(name string) (string, error) {
|
|
||||||
stmt := "INSERT INTO wish (name, recipient_id, creator_id) VALUES (?, ?, ?)"
|
stmt := "INSERT INTO wish (name, recipient_id, creator_id) VALUES (?, ?, ?)"
|
||||||
result, err := db.Connection.Exec(stmt, strings.TrimSpace(name), s.User().Id, s.User().Id)
|
_, err := db.Connection.Exec(stmt, strings.TrimSpace(name), s.User().Id, s.User().Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Query execution failed: %w", err)
|
return fmt.Errorf("Query execution failed: %w", err)
|
||||||
}
|
}
|
||||||
id, err := result.LastInsertId()
|
return nil
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to get last insert id: %w", err)
|
|
||||||
}
|
|
||||||
wishId := strconv.FormatInt(id, 10)
|
|
||||||
recordEventCreateWish(s.user.Id, wishId)
|
|
||||||
return wishId, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) deleteWishes(tx *sql.Tx, ids []string) error {
|
func (s *Session) deleteWishes(tx *sql.Tx, ids []string) error {
|
||||||
|
|
@ -202,10 +194,7 @@ func (s *Session) executeClaims(tx *sql.Tx, claims, unclaims []string) error {
|
||||||
|
|
||||||
// Undertake or abandon wishes made by other users
|
// Undertake or abandon wishes made by other users
|
||||||
func (s *Session) ClaimWishes(claims, unclaims []string) error {
|
func (s *Session) ClaimWishes(claims, unclaims []string) error {
|
||||||
lenClaims := len(claims)
|
if len(claims) < 1 && len(unclaims) < 1 {
|
||||||
lenUnclaims := len(unclaims)
|
|
||||||
// TODO: Would be nice if this used a request builder
|
|
||||||
if lenClaims < 1 && lenUnclaims < 1 {
|
|
||||||
return fmt.Errorf("Attempt to claim/unclaim zero wishes")
|
return fmt.Errorf("Attempt to claim/unclaim zero wishes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,17 +213,7 @@ func (s *Session) ClaimWishes(claims, unclaims []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TODO: This could be atomic. See recordEvent function
|
|
||||||
if lenClaims > 0 {
|
|
||||||
recordEventClaimWishes(s.user.Id, claims...)
|
|
||||||
}
|
|
||||||
if lenUnclaims > 0 {
|
|
||||||
recordEventUnclaimWishes(s.user.Id, unclaims...)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeCompletions(tx *sql.Tx, claims []string) error {
|
func executeCompletions(tx *sql.Tx, claims []string) error {
|
||||||
|
|
@ -255,8 +234,8 @@ func executeCompletions(tx *sql.Tx, claims []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: User ought not be able to interact with wishes outside their group network
|
||||||
func (s *Session) CompleteWishes(claims []string) error {
|
func (s *Session) CompleteWishes(claims []string) error {
|
||||||
// TODO: User ought not be able to interact with wishes outside their group network
|
|
||||||
if len(claims) < 1 {
|
if len(claims) < 1 {
|
||||||
return fmt.Errorf("Attempt to complete zero wishes")
|
return fmt.Errorf("Attempt to complete zero wishes")
|
||||||
}
|
}
|
||||||
|
|
@ -276,13 +255,7 @@ func (s *Session) CompleteWishes(claims []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
recordEventCompleteWishes(s.user.Id, claims...)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Session) SuggestWishForUser(otherUserReference string, wishName string) error {
|
func (u *Session) SuggestWishForUser(otherUserReference string, wishName string) error {
|
||||||
|
|
@ -318,8 +291,5 @@ func (s *Session) RecindWishesForUser(ids ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
if err != nil {
|
return err
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,17 @@ package lishwist_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"lishwist/core/internal/assert"
|
|
||||||
"lishwist/core/internal/fixtures"
|
"lishwist/core/internal/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeWish(t *testing.T) {
|
func TestMakeWish(t *testing.T) {
|
||||||
s := fixtures.Login(t, "thomas", "123")
|
s := fixtures.Login(t, "thomas", "123")
|
||||||
|
|
||||||
if _, err := s.MakeWish("apple"); err != nil {
|
if err := s.MakeWish("apple"); err != nil {
|
||||||
t.Fatalf("Failed to make wish 1: %s\n", err)
|
t.Fatalf("Failed to make wish 1: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.MakeWish(" A car "); err != nil {
|
if err := s.MakeWish(" A car "); err != nil {
|
||||||
t.Fatalf("Failed to make wish 2: %s\n", err)
|
t.Fatalf("Failed to make wish 2: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,35 +26,3 @@ func TestMakeWish(t *testing.T) {
|
||||||
fixtures.AssertEq(t, "Wish 1 name", wishes[0].Name, "apple")
|
fixtures.AssertEq(t, "Wish 1 name", wishes[0].Name, "apple")
|
||||||
fixtures.AssertEq(t, "Wish 2 name", wishes[1].Name, "A car")
|
fixtures.AssertEq(t, "Wish 2 name", wishes[1].Name, "A car")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClaimUnclaimWishes(t *testing.T) {
|
|
||||||
err := fixtures.TestInit(t)
|
|
||||||
assert.FatalErr(t, "initializing db", err)
|
|
||||||
|
|
||||||
thomas := fixtures.Login2(t, "thomas", "123")
|
|
||||||
assert.FatalErr(t, "registering thomas", err)
|
|
||||||
|
|
||||||
caleb := fixtures.Login2(t, "caleb", "123")
|
|
||||||
assert.FatalErr(t, "registering caleb", err)
|
|
||||||
|
|
||||||
food, err := caleb.MakeWish("food")
|
|
||||||
assert.FatalErr(t, "making wish 1", err)
|
|
||||||
|
|
||||||
box, err := caleb.MakeWish("box")
|
|
||||||
assert.FatalErr(t, "making wish 2", err)
|
|
||||||
|
|
||||||
drink, err := caleb.MakeWish("drink")
|
|
||||||
assert.FatalErr(t, "making wish 3", err)
|
|
||||||
|
|
||||||
err = thomas.ClaimWishes([]string{food, box, drink}, []string{})
|
|
||||||
assert.FatalErr(t, "claiming wishes", err)
|
|
||||||
|
|
||||||
err = thomas.ClaimWishes([]string{}, []string{food, box, drink})
|
|
||||||
assert.FatalErr(t, "unclaiming wishes", err)
|
|
||||||
|
|
||||||
// FIXME: disabled for now because datetimes break this
|
|
||||||
// events, err := thomas.Admin().ListEvents()
|
|
||||||
// assert.FatalErr(t, "listing events", err)
|
|
||||||
|
|
||||||
// assert.JsonSnapshot(t, "TestClaimUnclaimWishes.snap.txt", events)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
top_level=$(git rev-parse --show-toplevel)
|
top_level=$(git rev-parse --show-toplevel)
|
||||||
git_version=$($top_level/scripts/git-version)
|
git_version=$($top_level/scripts/git-version)
|
||||||
|
|
||||||
go generate ../core/internal/db
|
|
||||||
go run -ldflags=-X=lishwist/http/env.GitVersion=$git_version main.go
|
go run -ldflags=-X=lishwist/http/env.GitVersion=$git_version main.go
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package routing
|
|
||||||
|
|
||||||
import (
|
|
||||||
lishwist "lishwist/core"
|
|
||||||
"lishwist/http/response"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/Teajey/rsvp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Events struct {
|
|
||||||
Events []lishwist.Event `xml:"Event"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func EventList(app *lishwist.Session, session *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
|
||||||
admin := app.Admin()
|
|
||||||
if admin == nil {
|
|
||||||
log.Println("Attempt to access EventList by non-admin. Responding 404 Not Found.")
|
|
||||||
return response.NotFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
events, err := admin.ListEvents()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Admin failed to ListEvents: %s\n", err)
|
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to get events: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Data("", Events{Events: events})
|
|
||||||
}
|
|
||||||
|
|
@ -17,7 +17,7 @@ func WishlistAdd(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Res
|
||||||
}
|
}
|
||||||
|
|
||||||
newGiftName := r.Form.Get("gift_name")
|
newGiftName := r.Form.Get("gift_name")
|
||||||
_, err = app.MakeWish(newGiftName)
|
err = app.MakeWish(newGiftName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s\n", err)
|
log.Printf("%s\n", err)
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to add gift.")
|
return response.Error(http.StatusInternalServerError, "Failed to add gift.")
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ func Create(useSecureCookies bool) *router.VisibilityRouter {
|
||||||
r.Public.HandleFunc("POST /", routing.LoginPost)
|
r.Public.HandleFunc("POST /", routing.LoginPost)
|
||||||
r.Public.HandleFunc("POST /register", routing.RegisterPost)
|
r.Public.HandleFunc("POST /register", routing.RegisterPost)
|
||||||
|
|
||||||
r.Private.HandleFunc("GET /events", routing.ExpectAppSession(routing.EventList))
|
|
||||||
r.Private.HandleFunc("GET /account", routing.ExpectAppSession(routing.Account))
|
r.Private.HandleFunc("GET /account", routing.ExpectAppSession(routing.Account))
|
||||||
r.Private.HandleFunc("GET /health", routing.ExpectAppSession(routing.Health))
|
r.Private.HandleFunc("GET /health", routing.ExpectAppSession(routing.Health))
|
||||||
r.Private.HandleFunc("GET /", routing.NotFound)
|
r.Private.HandleFunc("GET /", routing.NotFound)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue