user instruments
This commit is contained in:
parent
dd03cae0f3
commit
abb2411af7
9 changed files with 494 additions and 48 deletions
|
|
@ -8,15 +8,20 @@ import (
|
|||
)
|
||||
|
||||
const instrumentListQuery = `
|
||||
select i.id, c_base.symbol, c_quote.symbol
|
||||
select i.id, c_base.symbol, c_quote.symbol, i.is_global
|
||||
from instrument i
|
||||
join currency c_base on c_base.id = i.base_currency_id
|
||||
join currency c_quote on c_quote.id = i.quoted_currency_id
|
||||
order by i.id desc
|
||||
offset $1 limit $2`
|
||||
where i.is_global = true
|
||||
or exists (
|
||||
select 1 from user_instrument ui
|
||||
where ui.instrument_id = i.id and ui.user_id = $1
|
||||
)
|
||||
order by i.is_global desc, i.id asc
|
||||
offset $2 limit $3`
|
||||
|
||||
func (p *Postgresql) InstrumentList(ctx context.Context, offset, limit int) ([]entities.Instrument, error) {
|
||||
rows, err := p.db.Query(ctx, instrumentListQuery, offset, limit)
|
||||
func (p *Postgresql) InstrumentList(ctx context.Context, userID entities.UserID, offset, limit int) ([]entities.Instrument, error) {
|
||||
rows, err := p.db.Query(ctx, instrumentListQuery, userID, offset, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to exec instrumentListQuery: %w", err)
|
||||
}
|
||||
|
|
@ -25,7 +30,7 @@ func (p *Postgresql) InstrumentList(ctx context.Context, offset, limit int) ([]e
|
|||
var instruments []entities.Instrument
|
||||
for rows.Next() {
|
||||
var inst entities.Instrument
|
||||
if err := rows.Scan(&inst.ID, &inst.BaseCurrency, &inst.QuoteCurrency); err != nil {
|
||||
if err := rows.Scan(&inst.ID, &inst.BaseCurrency, &inst.QuoteCurrency, &inst.IsGlobal); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan instrument row: %w", err)
|
||||
}
|
||||
instruments = append(instruments, inst)
|
||||
|
|
@ -34,21 +39,62 @@ func (p *Postgresql) InstrumentList(ctx context.Context, offset, limit int) ([]e
|
|||
return instruments, nil
|
||||
}
|
||||
|
||||
// createInstrumentQuery upserts both currency symbols then the instrument itself.
|
||||
// It always returns an ID — the newly inserted row or the existing one on conflict.
|
||||
const createInstrumentQuery = `
|
||||
insert into instrument(base_currency_id, quoted_currency_id)
|
||||
values (
|
||||
(select id from currency where symbol = $1),
|
||||
(select id from currency where symbol = $2)
|
||||
with upsert_base as (
|
||||
insert into currency(symbol) values($1)
|
||||
on conflict(symbol) do update set symbol = excluded.symbol
|
||||
returning id
|
||||
),
|
||||
upsert_quote as (
|
||||
insert into currency(symbol) values($2)
|
||||
on conflict(symbol) do update set symbol = excluded.symbol
|
||||
returning id
|
||||
),
|
||||
ins as (
|
||||
insert into instrument(base_currency_id, quoted_currency_id, is_global)
|
||||
select upsert_base.id, upsert_quote.id, false
|
||||
from upsert_base, upsert_quote
|
||||
on conflict (base_currency_id, quoted_currency_id) do nothing
|
||||
returning id
|
||||
)
|
||||
returning id`
|
||||
select coalesce(
|
||||
(select id from ins),
|
||||
(select i.id from instrument i
|
||||
where i.base_currency_id = (select id from upsert_base)
|
||||
and i.quoted_currency_id = (select id from upsert_quote))
|
||||
)`
|
||||
|
||||
func (p *Postgresql) CreateInstrument(ctx context.Context, instrument *entities.Instrument) (entities.InstrumentID, error) {
|
||||
var id entities.InstrumentID
|
||||
|
||||
err := p.db.QueryRow(ctx, createInstrumentQuery, instrument.BaseCurrency, instrument.QuoteCurrency).Scan(&id)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to exec createInstrumentQuery: %w", err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
const addUserInstrumentQuery = `
|
||||
insert into user_instrument(user_id, instrument_id)
|
||||
values ($1, $2)
|
||||
on conflict (user_id, instrument_id) do nothing`
|
||||
|
||||
func (p *Postgresql) AddUserInstrument(ctx context.Context, userID entities.UserID, instrumentID entities.InstrumentID) error {
|
||||
_, err := p.db.Exec(ctx, addUserInstrumentQuery, userID, instrumentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to exec addUserInstrumentQuery: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const removeUserInstrumentQuery = `
|
||||
delete from user_instrument where user_id = $1 and instrument_id = $2`
|
||||
|
||||
func (p *Postgresql) RemoveUserInstrument(ctx context.Context, userID entities.UserID, instrumentID entities.InstrumentID) error {
|
||||
_, err := p.db.Exec(ctx, removeUserInstrumentQuery, userID, instrumentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to exec removeUserInstrumentQuery: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
drop table if exists user_instrument;
|
||||
alter table instrument drop column if exists is_global;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
alter table instrument add column is_global bool not null default false;
|
||||
update instrument set is_global = true;
|
||||
|
||||
create table user_instrument (
|
||||
user_id uuid references users(id) not null,
|
||||
instrument_id uuid references instrument(id) not null,
|
||||
primary key (user_id, instrument_id)
|
||||
);
|
||||
|
|
@ -13,8 +13,16 @@ type Storage interface {
|
|||
UserByID(ctx context.Context, id entities.UserID) (*entities.User, error)
|
||||
UserByTelegramID(ctx context.Context, tgID entities.TelegramID) (*entities.User, error)
|
||||
|
||||
InstrumentList(ctx context.Context, offset, limit int) ([]entities.Instrument, error)
|
||||
// InstrumentList returns instruments visible to userID: global ones plus any
|
||||
// the user has explicitly added.
|
||||
InstrumentList(ctx context.Context, userID entities.UserID, offset, limit int) ([]entities.Instrument, error)
|
||||
// CreateInstrument upserts the instrument (and its currencies) and returns the ID,
|
||||
// whether the row was just created or already existed.
|
||||
CreateInstrument(ctx context.Context, instrument *entities.Instrument) (entities.InstrumentID, error)
|
||||
// AddUserInstrument links an instrument to a user (idempotent).
|
||||
AddUserInstrument(ctx context.Context, userID entities.UserID, instrumentID entities.InstrumentID) error
|
||||
// RemoveUserInstrument removes a user's link to a non-global instrument.
|
||||
RemoveUserInstrument(ctx context.Context, userID entities.UserID, instrumentID entities.InstrumentID) error
|
||||
|
||||
SaveAlert(ctx context.Context, alert *entities.Alert) (entities.AlertID, error)
|
||||
AllActiveAlerts(ctx context.Context) ([]entities.Alert, error)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue