Skip to content

Commit 2943925

Browse files
committed
feat: add character contact labels
1 parent ad64e56 commit 2943925

File tree

8 files changed

+314
-0
lines changed

8 files changed

+314
-0
lines changed

internal/app/contact.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package app
33
import (
44
"fmt"
55

6+
"github.com/ErikKalkoken/go-set"
7+
68
"github.com/ErikKalkoken/evebuddy/internal/optional"
79
)
810

@@ -53,5 +55,6 @@ type CharacterContact struct {
5355
Contact *EveEntity
5456
IsBlocked optional.Optional[bool]
5557
IsWatched optional.Optional[bool]
58+
Labels set.Set[string]
5659
Standing float64
5760
}

internal/app/storage/charactercontact.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,62 @@ func (st *Storage) UpdateOrCreateCharacterContact(ctx context.Context, arg Updat
104104
}
105105
return nil
106106
}
107+
108+
type CreateCharacterContactLabelParams struct {
109+
CharacterID int64
110+
LabelID int64
111+
Name string
112+
}
113+
114+
func (st *Storage) CreateCharacterContactLabel(ctx context.Context, arg CreateCharacterContactLabelParams) error {
115+
err := st.qRW.CreateCharacterContactLabel(ctx, queries.CreateCharacterContactLabelParams{
116+
CharacterID: arg.CharacterID,
117+
LabelID: arg.LabelID,
118+
Name: arg.Name,
119+
})
120+
if err != nil {
121+
return fmt.Errorf("CreateCharacterContactLabel: %v: %w", arg, err)
122+
}
123+
return nil
124+
}
125+
126+
func (st *Storage) GetCharacterContactLabel(ctx context.Context, characterID, labelID int64) (string, error) {
127+
r, err := st.qRO.GetCharacterContactLabel(ctx, queries.GetCharacterContactLabelParams{
128+
CharacterID: characterID,
129+
LabelID: labelID,
130+
})
131+
if err != nil {
132+
return "", fmt.Errorf("GetCharacterContactLabel: %d %d: %w", characterID, labelID, err)
133+
}
134+
return r.Name, nil
135+
}
136+
137+
func (st *Storage) ListCharacterContactLabels(ctx context.Context, characterID int64) (set.Set[string], error) {
138+
rows, err := st.qRO.ListCharacterContactLabels(ctx, characterID)
139+
if err != nil {
140+
return set.Set[string]{}, fmt.Errorf("ListCharacterContactLabels for character %d: %w", characterID, err)
141+
}
142+
x := set.Collect(slices.Values(rows))
143+
return x, nil
144+
}
145+
146+
func (st *Storage) DeleteCharacterContactLabels(ctx context.Context, characterID int64, names set.Set[string]) error {
147+
wrapErr := func(err error) error {
148+
return fmt.Errorf("DeleteCharacterContactLabels for character %d and contact IDs: %v: %w", characterID, names, err)
149+
}
150+
if characterID == 0 {
151+
return wrapErr(app.ErrInvalid)
152+
}
153+
if names.Size() == 0 {
154+
return nil
155+
}
156+
err := st.qRW.DeleteCharacterContactLabels(ctx, queries.DeleteCharacterContactLabelsParams{
157+
CharacterID: characterID,
158+
Names: slices.Collect(names.All()),
159+
})
160+
if err != nil {
161+
return wrapErr(err)
162+
}
163+
slog.Info("Character labels deleted", "characterID", characterID, "labels", names)
164+
return nil
165+
}

internal/app/storage/charactercontact_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,51 @@ func TestCharacterContact(t *testing.T) {
146146
xassert.Equal(t, want, got)
147147
})
148148
}
149+
150+
func TestCharacterContactLabel(t *testing.T) {
151+
db, st, factory := testutil.NewDBInMemory()
152+
defer db.Close()
153+
ctx := context.Background()
154+
t.Run("can create from scratch", func(t *testing.T) {
155+
// given
156+
testutil.MustTruncateTables(db)
157+
c := factory.CreateCharacter()
158+
// when
159+
err := st.CreateCharacterContactLabel(ctx, storage.CreateCharacterContactLabelParams{
160+
CharacterID: c.ID,
161+
LabelID: 42,
162+
Name: "Alpha",
163+
})
164+
// then
165+
require.NoError(t, err)
166+
got, err := st.GetCharacterContactLabel(ctx, c.ID, 42)
167+
require.NoError(t, err)
168+
xassert.Equal(t, "Alpha", got)
169+
})
170+
t.Run("can list labels", func(t *testing.T) {
171+
// given
172+
testutil.MustTruncateTables(db)
173+
c := factory.CreateCharacter()
174+
l1 := factory.CreateCharacterContactLabel()
175+
l2 := factory.CreateCharacterContactLabel()
176+
// when
177+
got, err := st.ListCharacterContactLabels(ctx, c.ID)
178+
// then
179+
require.NoError(t, err)
180+
xassert.Equal(t, set.Of(l1, l2), got)
181+
})
182+
t.Run("can delete labels", func(t *testing.T) {
183+
// given
184+
testutil.MustTruncateTables(db)
185+
c := factory.CreateCharacter()
186+
l1 := factory.CreateCharacterContactLabel()
187+
l2 := factory.CreateCharacterContactLabel()
188+
// when
189+
err := st.DeleteCharacterContactLabels(ctx, c.ID, set.Of(l1))
190+
// then
191+
require.NoError(t, err)
192+
got, err := st.ListCharacterContactLabels(ctx, c.ID)
193+
require.NoError(t, err)
194+
xassert.Equal(t, set.Of(l2), got)
195+
})
196+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
CREATE TABLE character_contact_labels (
2+
id INTEGER PRIMARY KEY AUTOINCREMENT,
3+
character_id INTEGER NOT NULL,
4+
label_id INTEGER NOT NULL,
5+
name TEXT NOT NULL,
6+
FOREIGN KEY (character_id) REFERENCES characters (id) ON DELETE CASCADE,
7+
UNIQUE (character_id, label_id)
8+
);
9+
10+
CREATE INDEX character_contact_labels_idx1 ON character_contact_labels (character_id);
11+
12+
CREATE INDEX character_contact_labels_idx2 ON character_contact_labels (label_id);
13+
14+
CREATE TABLE character_contacts_labels (
15+
id INTEGER PRIMARY KEY AUTOINCREMENT,
16+
contact_id INTEGER NOT NULL,
17+
label_id INTEGER NOT NULL,
18+
FOREIGN KEY (contact_id) REFERENCES character_contacts (id) ON DELETE CASCADE,
19+
FOREIGN KEY (label_id) REFERENCES character_contact_labels (id) ON DELETE CASCADE,
20+
UNIQUE (contact_id, label_id)
21+
);
22+
23+
CREATE INDEX character_contacts_labels_idx1 ON character_contacts_labels (contact_id);
24+
25+
CREATE INDEX character_contacts_labels_idx2 ON character_contacts_labels (label_id);

internal/app/storage/queries/character_contacts.sql

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,32 @@ SET
4949
is_blocked = ?3,
5050
is_watched = ?4,
5151
standing = ?5;
52+
53+
-- name: CreateCharacterContactLabel :exec
54+
INSERT INTO
55+
character_contact_labels (character_id, label_id, name)
56+
VALUES
57+
(?, ?, ?);
58+
59+
-- name: DeleteCharacterContactLabels :exec
60+
DELETE FROM character_contact_labels
61+
WHERE
62+
character_id = ?
63+
AND name IN (sqlc.slice('names'));
64+
65+
-- name: GetCharacterContactLabel :one
66+
SELECT
67+
*
68+
FROM
69+
character_contact_labels
70+
WHERE
71+
character_id = ?
72+
AND label_id = ?;
73+
74+
-- name: ListCharacterContactLabels :many
75+
SELECT
76+
name
77+
FROM
78+
character_contact_labels
79+
WHERE
80+
character_id = ?;

internal/app/storage/queries/character_contacts.sql.go

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/app/storage/queries/models.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/app/testutil/factory.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,38 @@ func (f Factory) CreateCharacterContact(args ...storage.UpdateOrCreateCharacterC
245245
return o
246246
}
247247

248+
func (f Factory) CreateCharacterContactLabel(args ...storage.CreateCharacterContactLabelParams) string {
249+
ctx := context.Background()
250+
var arg storage.CreateCharacterContactLabelParams
251+
if len(args) > 0 {
252+
arg = args[0]
253+
}
254+
if arg.CharacterID == 0 {
255+
x := f.CreateCharacter()
256+
arg.CharacterID = x.ID
257+
}
258+
if arg.LabelID == 0 {
259+
arg.LabelID = f.calcNewIDWithParam(
260+
"character_contact_labels",
261+
"label_id",
262+
"character_id",
263+
arg.CharacterID,
264+
)
265+
}
266+
if arg.Name == "" {
267+
arg.Name = fake.Color()
268+
}
269+
err := f.st.CreateCharacterContactLabel(ctx, arg)
270+
if err != nil {
271+
panic(fmt.Sprintf("%s|%+v", err, arg))
272+
}
273+
v, err := f.st.GetCharacterContactLabel(ctx, arg.CharacterID, arg.LabelID)
274+
if err != nil {
275+
panic(err)
276+
}
277+
return v
278+
}
279+
248280
func (f Factory) CreateCharacterContract(args ...storage.CreateCharacterContractParams) *app.CharacterContract {
249281
ctx := context.Background()
250282
var arg storage.CreateCharacterContractParams

0 commit comments

Comments
 (0)