Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion db/query/query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,15 @@ SELECT
t.symbol,
t.target_price,
t.type,
t.quote_currency
t.quote_currency,
t.id
FROM portfolio_targets AS t
JOIN portfolios AS p ON t.portfolio_id = p.id
WHERE t.portfolio_id = ?
AND t.deleted_at IS NULL
ORDER BY t.symbol ASC;

-- name: DeleteTarget :execresult
DELETE FROM portfolio_targets
WHERE id = ?
AND portfolio_id = ?;
2 changes: 2 additions & 0 deletions internal/app/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Runtime struct {
GetMorningBrief *usecase.GetMorningBriefUsecase
SetTarget *usecase.SetTargetUseCase
ListTargets *usecase.ListTargetsUseCase
RemoveTarget *usecase.RemoveTargetUsecase
}

func BuildRuntime(dbPath string) (*Runtime, error) {
Expand Down Expand Up @@ -116,5 +117,6 @@ func BuildRuntime(dbPath string) (*Runtime, error) {
GetMorningBrief: usecase.NewGetMorningBriefUsecase(reportingBuilder),
SetTarget: usecase.NewSetTargetUseCase(portfolioRepo, targetRespository),
ListTargets: usecase.NewListTargetsUseCase(portfolioRepo, targetRespository),
RemoveTarget: usecase.NewRemoveTargetUsecase(portfolioRepo, targetRespository),
}, nil
}
39 changes: 38 additions & 1 deletion internal/cli/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
Expand All @@ -17,7 +18,7 @@ func newTargetCmd(runtimeBuilder RuntimeBuilder) *cobra.Command {

cmd.AddCommand(newTargetSetCmd(runtimeBuilder))
cmd.AddCommand(newTargetListCmd(runtimeBuilder))
// cmd.AddCommand(newTargetDeleteCmd(runtimeBuilder))
cmd.AddCommand(newTargetDeleteCmd(runtimeBuilder))

return cmd
}
Expand Down Expand Up @@ -104,3 +105,39 @@ func newTargetListCmd(runtimeBuilder RuntimeBuilder) *cobra.Command {

return cmd
}

func newTargetDeleteCmd(runtimeBuilder RuntimeBuilder) *cobra.Command {
var portfolioName string

cmd := &cobra.Command{
Use: "remove TARGET_ID",
Short: "Remove a portfolio price target",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
targetID, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
return fmt.Errorf("invalid target ID %q", args[0])
}

rt, err := runtimeBuilder()
if err != nil {
return err
}

err = rt.RemoveTarget.Execute(cmd.Context(), domain.DeleteTargetUseCaseInput{
PortfolioName: portfolioName,
TargetID: targetID,
})
if err != nil {
return err
}

_, err = fmt.Fprintf(cmd.OutOrStdout(), "Removed target %d\n", targetID)
return err
},
}

cmd.Flags().StringVar(&portfolioName, "portfolio", "main", "Portfolio name")

return cmd
}
20 changes: 19 additions & 1 deletion internal/db/query.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/domain/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ var (
ErrPriceCacheNotFound = errors.New("price cache not found")
ErrFXCacheNotFound = errors.New("fx cache not found")
ErrPortfolioSnapshotNotFound = errors.New("portfolio snapshot not found")
ErrTargetNotFound = errors.New("target not found")
)
6 changes: 6 additions & 0 deletions internal/domain/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ type SetTargetUseCaseOutput struct {
Type TargetType
TargetPrice float64
QuoteCurrency string
TargetID int64
}

type ListTargetsUseCaseInput struct {
Expand All @@ -238,3 +239,8 @@ type ListTargetsUseCaseOutput struct {
PortfolioName string
Targets []Target
}

type DeleteTargetUseCaseInput struct {
TargetID int64
PortfolioName string
}
15 changes: 12 additions & 3 deletions internal/render/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ import (
"github.com/squeakycheese75/tick/internal/domain"
)

const targetListFormat = "%-4s %-8s %-14s %12s %-3s\n"
const targetRowFormat = "%-4d %-8s %-14s %12.2f %-3s\n"

func RenderSetTarget(w io.Writer, out domain.SetTargetUseCaseOutput) error {
_, err := fmt.Fprintf(
w,
"Set %s target for %s in portfolio %s: %.2f %s\n",
"Set %s target %d for %s in portfolio %s: %.2f %s\n",
out.Type,
out.TargetID,
out.Symbol,
out.PortfolioName,
out.TargetPrice,
out.QuoteCurrency,
)
return err
}

func RenderListTargets(w io.Writer, out domain.ListTargetsUseCaseOutput) error {
if len(out.Targets) == 0 {
_, err := fmt.Fprintf(w, "No targets set for portfolio %q\n", out.PortfolioName)
Expand All @@ -31,10 +34,16 @@ func RenderListTargets(w io.Writer, out domain.ListTargetsUseCaseOutput) error {
return err
}

_, err = fmt.Fprintf(w, targetListFormat, "ID", "SYMBOL", "TYPE", "PRICE", "CCY")
if err != nil {
return err
}

for _, t := range out.Targets {
_, err := fmt.Fprintf(
w,
"%-6s %-11s %12.2f %s\n",
targetRowFormat,
t.ID,
t.Symbol,
t.Type,
t.TargetPrice,
Expand Down
21 changes: 21 additions & 0 deletions internal/repository/target_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,29 @@ func (r *TargetRepository) ListByPortfolio(ctx context.Context, portfolioID int6
Type: domain.TargetType(row.Type),
QuoteCurrency: row.QuoteCurrency,
TargetPrice: row.TargetPrice,
ID: row.ID,
})
}

return targets, nil
}

func (r *TargetRepository) Delete(ctx context.Context, targetID, portfolioID int64) error {
result, err := r.q.DeleteTarget(ctx, db.DeleteTargetParams{
ID: targetID,
PortfolioID: portfolioID,
})
if err != nil {
return err
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return domain.ErrTargetNotFound
}

return nil
}
37 changes: 37 additions & 0 deletions internal/usecase/target_remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package usecase

import (
"context"
"errors"
"fmt"

"github.com/squeakycheese75/tick/internal/domain"
)

type RemoveTargetUsecase struct {
targets TargetRepository
portfolios PortfolioRepository
}

func NewRemoveTargetUsecase(portfolioRepo PortfolioRepository, targetRepo TargetRepository) *RemoveTargetUsecase {
return &RemoveTargetUsecase{
portfolios: portfolioRepo,
targets: targetRepo,
}
}

func (uc *RemoveTargetUsecase) Execute(ctx context.Context, in domain.DeleteTargetUseCaseInput) error {
portfolio, err := uc.portfolios.GetByName(ctx, in.PortfolioName)
if err != nil {
if errors.Is(err, domain.ErrPortfolioNotFound) {
return fmt.Errorf("portfolio %q not found", in.PortfolioName)
}
return fmt.Errorf("get portfolio %q: %w", in.PortfolioName, err)
}

if in.TargetID <= 0 {
return domain.ErrTargetNotFound
}

return uc.targets.Delete(ctx, in.TargetID, portfolio.ID)
}
2 changes: 1 addition & 1 deletion internal/usecase/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type (
TargetRepository interface {
Save(ctx context.Context, target domain.Target) error
ListByPortfolio(ctx context.Context, portfolioID int64) ([]domain.Target, error)
// Delete(ctx context.Context, portfolioID int64, symbol string, targetType domain.TargetType) error
Delete(ctx context.Context, targetID, portfolioID int64) error
}
)

Expand Down