Skip to content

Commit

Permalink
Merge pull request #17 from abjrcode/favorites-and-dashboard
Browse files Browse the repository at this point in the history
Add navigation sidebar and mechanism for marking provider instance as favorite
  • Loading branch information
abjrcode authored Nov 24, 2023
2 parents 2b229ef + 5fc107f commit 912f63d
Show file tree
Hide file tree
Showing 23 changed files with 724 additions and 174 deletions.
55 changes: 22 additions & 33 deletions dashboard-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ package main

import (
"context"
"database/sql"

"github.com/abjrcode/swervo/favorites"
"github.com/abjrcode/swervo/internal/logging"
"github.com/abjrcode/swervo/providers"
"github.com/rs/zerolog"
)

type DashboardController struct {
ctx context.Context
logger *zerolog.Logger
errorHandler logging.ErrorHandler
db *sql.DB
ctx context.Context
logger *zerolog.Logger
errorHandler logging.ErrorHandler
favoritesRepo favorites.FavoritesRepo
}

type Provider struct {
Expand All @@ -26,21 +27,12 @@ type FavoriteInstance struct {
InstanceId string `json:"instanceId"`
}

var (
SupportedProviders = map[string]Provider{
"aws-iam-idc": {
Code: "aws-iam-idc",
Name: "AWS IAM IDC",
},
}
)

var supportedProviders []Provider

func NewDashboardController(db *sql.DB) *DashboardController {
func NewDashboardController(favoritesRepo favorites.FavoritesRepo) *DashboardController {

return &DashboardController{
db: db,
favoritesRepo: favoritesRepo,
}
}

Expand All @@ -50,35 +42,32 @@ func (c *DashboardController) Init(ctx context.Context, errorHandler logging.Err
c.logger = &enrichedLogger
c.errorHandler = errorHandler

supportedProviders = make([]Provider, 0, len(SupportedProviders))
for _, provider := range SupportedProviders {
supportedProviders = append(supportedProviders, provider)
supportedProviders = make([]Provider, 0, len(providers.SupportedProviders))
for _, provider := range providers.SupportedProviders {
supportedProviders = append(supportedProviders, Provider{
Code: provider.Code,
Name: provider.Name,
})
}
}

func (c *DashboardController) ListFavorites() ([]FavoriteInstance, error) {
rows, err := c.db.QueryContext(c.ctx, `SELECT * FROM favorite_instances`)
favorites, err := c.favoritesRepo.ListAll(c.ctx)

if err != nil {
if err == sql.ErrNoRows {
return []FavoriteInstance{}, nil
}

c.errorHandler.Catch(c.logger, err)
}

favorites := make([]FavoriteInstance, 0, 10)
favoriteInstances := make([]FavoriteInstance, 0, len(favorites))

for rows.Next() {
var favorite FavoriteInstance
err := rows.Scan(&favorite.ProviderCode, &favorite.InstanceId)
if err != nil {
c.errorHandler.Catch(c.logger, err)
}
favorites = append(favorites, favorite)
for _, favorite := range favorites {
favoriteInstances = append(favoriteInstances, FavoriteInstance{
ProviderCode: favorite.ProviderCode,
InstanceId: favorite.InstanceId,
})
}

return favorites, nil
return favoriteInstances, nil
}

func (c *DashboardController) ListProviders() []Provider {
Expand Down
8 changes: 6 additions & 2 deletions dashboard-controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"testing"

"github.com/abjrcode/swervo/favorites"
"github.com/abjrcode/swervo/internal/migrations"
"github.com/abjrcode/swervo/internal/testhelpers"
"github.com/rs/zerolog"
Expand All @@ -12,10 +13,13 @@ import (

func initDashboardController(t *testing.T) *DashboardController {
db, err := migrations.NewInMemoryMigratedDatabase(t, "dashboard-controller-tests.db")

require.NoError(t, err)

logger := zerolog.Nop()
favoritesRepo := favorites.NewFavorites(db, &logger)

controller := &DashboardController{
db: db,
favoritesRepo: favoritesRepo,
}
ctx := zerolog.Nop().WithContext(context.Background())
errHandler := testhelpers.NewMockErrorHandler(t)
Expand Down
109 changes: 109 additions & 0 deletions favorites/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package favorites

import (
"context"
"database/sql"
"errors"

"github.com/rs/zerolog"
)

type Favorite struct {
ProviderCode string
InstanceId string
}

type FavoritesRepo interface {
ListAll(ctx context.Context) ([]*Favorite, error)
IsFavorite(ctx context.Context, favorite *Favorite) (bool, error)
Add(ctx context.Context, favorite *Favorite) error
Remove(ctx context.Context, favorite *Favorite) error
}

type favoritesImpl struct {
logger *zerolog.Logger
db *sql.DB
}

func NewFavorites(db *sql.DB, logger *zerolog.Logger) FavoritesRepo {
enrichedLogger := logger.With().Str("component", "favorites_repo").Logger()

return &favoritesImpl{
db: db,
logger: &enrichedLogger,
}
}

func (f *favoritesImpl) ListAll(ctx context.Context) ([]*Favorite, error) {
rows, err := f.db.QueryContext(ctx, `SELECT * FROM favorite_instances`)

if err != nil {
if err == sql.ErrNoRows {
return []*Favorite{}, nil
}

return nil, err
}

favorites := make([]*Favorite, 0, 10)

for rows.Next() {
var favorite Favorite
err := rows.Scan(&favorite.ProviderCode, &favorite.InstanceId)

if err != nil {
return nil, err
}

favorites = append(favorites, &favorite)
}

return favorites, nil
}

func (f *favoritesImpl) IsFavorite(ctx context.Context, favorite *Favorite) (bool, error) {
row := f.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM favorite_instances WHERE provider_code = ? AND instance_id = ? `, favorite.ProviderCode, favorite.InstanceId)

var count int
err := row.Scan(&count)

if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}

return false, err
}

return count > 0, nil
}

func (f *favoritesImpl) Add(ctx context.Context, favorite *Favorite) error {
_, err := f.db.ExecContext(ctx, `INSERT INTO favorite_instances (provider_code, instance_id) VALUES (?, ?) `, favorite.ProviderCode, favorite.InstanceId)

if err != nil {
return err
}

return nil
}

func (f *favoritesImpl) Remove(ctx context.Context, favorite *Favorite) error {
res, err := f.db.ExecContext(ctx, `DELETE FROM favorite_instances WHERE provider_code = ? AND instance_id = ? `, favorite.ProviderCode, favorite.InstanceId)

if err != nil {
return err
}

rowsAffected, err := res.RowsAffected()

if err != nil {
return err
}

if rowsAffected == 0 {
return sql.ErrNoRows
}

return nil
}
95 changes: 95 additions & 0 deletions favorites/repository_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package favorites

import (
"context"
"testing"

"github.com/abjrcode/swervo/internal/migrations"
"github.com/abjrcode/swervo/providers"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
)

func TestAddFavorite(t *testing.T) {
db, err := migrations.NewInMemoryMigratedDatabase(t, "favorites-repo-tests.db")
require.NoError(t, err)

logger := zerolog.Nop()
repo := NewFavorites(db, &logger)

favorite := &Favorite{
ProviderCode: providers.AwsIamIdc,
InstanceId: "some-nice-id",
}

ctx := context.Background()
err = repo.Add(ctx, favorite)
require.NoError(t, err)

favorites, err := repo.ListAll(ctx)
require.NoError(t, err)

require.Len(t, favorites, 1)
require.Equal(t, favorite, favorites[0])
}

func TestRemoveFavorite(t *testing.T) {
db, err := migrations.NewInMemoryMigratedDatabase(t, "favorites-repo-tests.db")
require.NoError(t, err)

logger := zerolog.Nop()

repo := NewFavorites(db, &logger)
ctx := context.Background()

favorite := &Favorite{
ProviderCode: providers.AwsIamIdc,
InstanceId: "some-nice-id",
}

err = repo.Add(ctx, favorite)
require.NoError(t, err)

favorites, err := repo.ListAll(ctx)
require.NoError(t, err)

require.Len(t, favorites, 1)
require.Equal(t, favorite, favorites[0])

err = repo.Remove(ctx, favorite)
require.NoError(t, err)

favorites, err = repo.ListAll(ctx)
require.NoError(t, err)

require.Len(t, favorites, 0)
}

func TestIsFavorite(t *testing.T) {
db, err := migrations.NewInMemoryMigratedDatabase(t, "favorites-repo-tests.db")
require.NoError(t, err)

logger := zerolog.Nop()

repo := NewFavorites(db, &logger)
ctx := context.Background()

favorite := &Favorite{
ProviderCode: providers.AwsIamIdc,
InstanceId: "some-nice-id",
}

err = repo.Add(ctx, favorite)
require.NoError(t, err)

isFavorite, err := repo.IsFavorite(ctx, favorite)
require.NoError(t, err)
require.True(t, isFavorite)

isFavorite, err = repo.IsFavorite(ctx, &Favorite{
ProviderCode: providers.AwsIamIdc,
InstanceId: "some-nice-id-2",
})
require.NoError(t, err)
require.False(t, isFavorite)
}
4 changes: 2 additions & 2 deletions frontend/src/components/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export function Toast({
}`

const toastAnimationClass = `${
isEntering.current ? "animate-[slideInLeft_0.5s_ease-in_both]" : ""
} ${isExiting ? "animate-[slideOutLeft_0.3s_ease-out_both]" : ""}`
isEntering.current ? "animate-[slideInRight_0.5s_ease-in_both]" : ""
} ${isExiting ? "animate-[slideOutRight_0.3s_ease-out_both]" : ""}`

const exitAnimationTimer = useRef<number | null>(null)
const toastRemovalTimer = useRef<number | null>(null)
Expand Down
Loading

0 comments on commit 912f63d

Please sign in to comment.