274 lines
6.4 KiB
Go
274 lines
6.4 KiB
Go
package accounts
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"ruben/inventory2/internal/consts"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// TODO: simpify, if possible (single query ideal)
|
|
func (db *Store) SetOrderOfPlatformOnAccountPage(ctx context.Context, acctID int64, platform Platform, orderIndex int) error {
|
|
tx, err := db.db.Begin(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start transaction: %w", err)
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
|
|
// look up specified indexes
|
|
|
|
rows, err := tx.Query(
|
|
ctx,
|
|
`
|
|
SELECT
|
|
platform,
|
|
order_index
|
|
FROM
|
|
accounts_page_platform_order_indexes
|
|
WHERE
|
|
account_id = @account_id
|
|
ORDER BY
|
|
order_index ASC
|
|
`,
|
|
pgx.NamedArgs{
|
|
"account_id": acctID,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to perform query to look up existing indexes: %w", err)
|
|
}
|
|
|
|
indexes, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct {
|
|
Platform Platform
|
|
Order_index int
|
|
}])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to scan rows for query to look up existing indexes: %w", err)
|
|
}
|
|
|
|
// compute the implied indexes,
|
|
// and construct the full sequence of platforms
|
|
|
|
indexPerPlatform := make(map[Platform]int, len(allPlatforms))
|
|
platformPerIndex := make(map[int]Platform, len(allPlatforms))
|
|
for _, v := range indexes {
|
|
indexPerPlatform[v.Platform] = v.Order_index
|
|
platformPerIndex[v.Order_index] = v.Platform
|
|
}
|
|
|
|
prevIndex := -1
|
|
for _, p := range allPlatforms {
|
|
if _, indexSet := indexPerPlatform[p]; !indexSet {
|
|
index := prevIndex
|
|
for indexUsed := true; indexUsed; _, indexUsed = platformPerIndex[index] {
|
|
index += 1
|
|
}
|
|
prevIndex = index
|
|
|
|
indexPerPlatform[p] = index
|
|
platformPerIndex[index] = p
|
|
}
|
|
}
|
|
|
|
prevOrderIndex := indexPerPlatform[platform]
|
|
if indexPerPlatform[platform] == orderIndex {
|
|
return nil
|
|
}
|
|
|
|
if increased := orderIndex > prevOrderIndex; increased {
|
|
// decrement in indexes between the previous index and new index
|
|
for index := prevOrderIndex + 1; index <= orderIndex; index += 1 {
|
|
p := platformPerIndex[index]
|
|
indexPerPlatform[p] = index - 1
|
|
platformPerIndex[index-1] = p
|
|
}
|
|
indexPerPlatform[platform] = orderIndex
|
|
platformPerIndex[orderIndex] = platform
|
|
} else {
|
|
// increment in indexes between the previous index and new index
|
|
for index := prevOrderIndex - 1; index >= orderIndex; index -= 1 {
|
|
p := platformPerIndex[index]
|
|
indexPerPlatform[p] = index + 1
|
|
platformPerIndex[index+1] = p
|
|
}
|
|
indexPerPlatform[platform] = orderIndex
|
|
platformPerIndex[orderIndex] = platform
|
|
}
|
|
|
|
// delete all indexes for the acct in the db,
|
|
// then insert all updated indexes
|
|
|
|
valuesLines := make([]string, len(allPlatforms))
|
|
args := pgx.NamedArgs{
|
|
"account_id": acctID,
|
|
}
|
|
for i, p := range allPlatforms {
|
|
valuesLines[i] = fmt.Sprintf("(@account_id, @platform_%d, @order_index_%d::smallint)", i, i)
|
|
args[fmt.Sprintf("platform_%d", i)] = p
|
|
args[fmt.Sprintf("order_index_%d", i)] = indexPerPlatform[p]
|
|
}
|
|
|
|
_, err = tx.Exec(
|
|
ctx,
|
|
fmt.Sprintf(`
|
|
WITH deleted_indexes AS (
|
|
DELETE FROM
|
|
accounts_page_platform_order_indexes
|
|
WHERE
|
|
account_id = @account_id
|
|
RETURNING
|
|
account_id
|
|
), new_indexes(account_id, platform, order_index) AS (
|
|
SELECT DISTINCT
|
|
x.account_id, x.platform, x.order_index
|
|
FROM
|
|
(VALUES %s) AS x(account_id, platform, order_index)
|
|
LEFT JOIN
|
|
deleted_indexes
|
|
ON
|
|
x.account_id = deleted_indexes.account_id
|
|
)
|
|
INSERT INTO
|
|
accounts_page_platform_order_indexes (
|
|
account_id,
|
|
platform,
|
|
order_index
|
|
)
|
|
SELECT
|
|
account_id,
|
|
platform::Platform,
|
|
order_index
|
|
FROM
|
|
new_indexes
|
|
`, strings.Join(valuesLines, ", ")),
|
|
args,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to perform query to delete old indexes and insert new indexes: %w", err)
|
|
}
|
|
|
|
if tx.Commit(ctx); err != nil {
|
|
return fmt.Errorf("failed to commit txn: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *Store) GetOrderOfPlatformsOnAccountPage(ctx context.Context, acctID int64) ([]Platform, error) {
|
|
// get specified order indexes per platform
|
|
rows, err := db.db.Query(
|
|
ctx,
|
|
`
|
|
SELECT
|
|
platform,
|
|
order_index
|
|
FROM
|
|
accounts_page_platform_order_indexes
|
|
WHERE
|
|
account_id = @account_id
|
|
`,
|
|
pgx.NamedArgs{
|
|
"account_id": acctID,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to perform query: %w", err)
|
|
}
|
|
|
|
type Row struct {
|
|
Platform Platform
|
|
Order_index int
|
|
}
|
|
|
|
platformsWithIndexes, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct {
|
|
Platform Platform
|
|
Order_index int
|
|
}])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan rows: %w", err)
|
|
}
|
|
|
|
// hash the specified platforms by index
|
|
|
|
platformsByIndex := make(map[int]Platform, len(platformsWithIndexes))
|
|
for _, v := range platformsWithIndexes {
|
|
platformsByIndex[v.Order_index] = v.Platform
|
|
}
|
|
|
|
// form a sorted list of platforms without indexes specified, sorted alphabetically
|
|
|
|
allPlatformsByName := make(map[Platform]struct{}, len(allPlatforms))
|
|
for _, p := range allPlatforms {
|
|
allPlatformsByName[p] = struct{}{}
|
|
}
|
|
for _, v := range platformsWithIndexes {
|
|
delete(allPlatformsByName, v.Platform)
|
|
}
|
|
platformsWithoutAnIndexSpecified := make([]Platform, 0, len(allPlatformsByName))
|
|
for p := range allPlatformsByName {
|
|
platformsWithoutAnIndexSpecified = append(platformsWithoutAnIndexSpecified, p)
|
|
}
|
|
slices.SortFunc(platformsWithoutAnIndexSpecified, func(a, b Platform) int {
|
|
al := strings.ToLower(string(a))
|
|
bl := strings.ToLower(string(b))
|
|
if al < bl {
|
|
return -1
|
|
}
|
|
if bl < al {
|
|
return 1
|
|
}
|
|
return 0
|
|
})
|
|
|
|
// construct the list of the ordered platforms
|
|
|
|
res := make([]Platform, len(allPlatforms))
|
|
nextIndex := 0
|
|
for i := range res {
|
|
if p, ok := platformsByIndex[i]; ok {
|
|
res[i] = p
|
|
} else {
|
|
res[i] = platformsWithoutAnIndexSpecified[nextIndex]
|
|
nextIndex += 1
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (db *Store) GetOrderOfPlatformOnAccountPage(ctx context.Context, acctID int64, platform Platform) (int, error) {
|
|
rows, err := db.db.Query(
|
|
ctx,
|
|
`
|
|
SELECT
|
|
order_index
|
|
FROM
|
|
accounts_page_platform_order_indexes
|
|
WHERE
|
|
account_id = @account_id
|
|
AND platform = @platform
|
|
`,
|
|
pgx.NamedArgs{
|
|
"account_id": acctID,
|
|
"platform": platform,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to perform query: %w", err)
|
|
}
|
|
|
|
i, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[int])
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return 0, consts.ErrNotFound
|
|
}
|
|
return 0, fmt.Errorf("failed to scan rows: %w", err)
|
|
}
|
|
|
|
return i, nil
|
|
}
|