Skip to content
Open
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
6 changes: 3 additions & 3 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ tasks:
generate:schema:
desc: Generate PowerSync and SQLite schema (uses local control plane)
cmds:
- go generate ./internal/powersync
- go generate ./internal/sqlite
- sed '/^-- Auto-generated/d; /^-- Run .task generate/d' ../control-plane/internal/powersync/schema.sql > internal/chat/tools/query_schema.sql
- doppler run -- go generate ./internal/powersync
- doppler run -- go generate ./internal/sqlite
- sed '/^-- Auto-generated/d; /^-- Run .task generate/d' ../control-plane/internal/infra/powersync/schema.sql > internal/app/chattools/query_schema.sql

# ===========================================================================
# Build
Expand Down
357 changes: 169 additions & 188 deletions internal/app/chattools/query_schema.sql

Large diffs are not rendered by default.

18 changes: 16 additions & 2 deletions internal/app/onboarding/preflight/preflight_effects.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,35 @@ import (

tea "charm.land/bubbletea/v2"

"github.com/usetero/cli/internal/auth"
"github.com/usetero/cli/internal/core/bootstrap"
"github.com/usetero/cli/internal/domain"
)

func (m *Model) checkAuth() tea.Cmd {
return func() tea.Msg {
hasValidAuth := false
var user *auth.User
if m.auth.IsAuthenticated() {
if _, err := m.auth.GetAccessToken(m.ctx); err == nil {
if token, err := m.auth.GetAccessToken(m.ctx); err == nil {
hasValidAuth = true
user = userFromAccessToken(token)
} else {
_ = m.auth.ClearTokens()
}
}
return preflightAuthCheckCompletedMsg{hasValidAuth: hasValidAuth}
return preflightAuthCheckCompletedMsg{hasValidAuth: hasValidAuth, user: user}
}
}

func userFromAccessToken(token string) *auth.User {
claims, err := auth.ParseToken(token)
if err != nil || claims.Sub == "" {
return nil
}
return &auth.User{
ID: claims.Sub,
Email: claims.Email,
}
}

Expand Down
50 changes: 50 additions & 0 deletions internal/app/onboarding/preflight/preflight_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package preflight

import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"testing"

Expand Down Expand Up @@ -70,3 +72,51 @@ func TestPreflightOutcomeForError(t *testing.T) {
t.Fatalf("expected inconclusive outcome for generic error, got %v", outcome)
}
}

func TestUserFromAccessToken(t *testing.T) {
t.Parallel()

token := testJWT(map[string]any{
"sub": "user-123",
"email": "user@example.com",
"exp": int64(4102444800), // 2100-01-01
})
user := userFromAccessToken(token)
if user == nil {
t.Fatal("expected user from token")
}
if user.ID != "user-123" {
t.Fatalf("user.ID = %q, want %q", user.ID, "user-123")
}
if user.Email != "user@example.com" {
t.Fatalf("user.Email = %q, want %q", user.Email, "user@example.com")
}
}

func TestUserFromAccessTokenMissingSubReturnsNil(t *testing.T) {
t.Parallel()

token := testJWT(map[string]any{
"email": "user@example.com",
"exp": int64(4102444800),
})
if got := userFromAccessToken(token); got != nil {
t.Fatalf("expected nil user for missing sub, got %#v", got)
}
}

func TestUserFromAccessTokenInvalidTokenReturnsNil(t *testing.T) {
t.Parallel()

if got := userFromAccessToken("not-a-jwt"); got != nil {
t.Fatalf("expected nil user for invalid token, got %#v", got)
}
}

func testJWT(claims map[string]any) string {
headerJSON, _ := json.Marshal(map[string]string{"alg": "none", "typ": "JWT"})
payloadJSON, _ := json.Marshal(claims)
header := base64.RawURLEncoding.EncodeToString(headerJSON)
payload := base64.RawURLEncoding.EncodeToString(payloadJSON)
return header + "." + payload + ".sig"
}
2 changes: 2 additions & 0 deletions internal/app/onboarding/preflight/preflight_types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package preflight

import (
"github.com/usetero/cli/internal/auth"
"github.com/usetero/cli/internal/core/bootstrap"
"github.com/usetero/cli/internal/domain"
)
Expand All @@ -11,6 +12,7 @@ type preflightResolutionCompletedMsg struct {

type preflightAuthCheckCompletedMsg struct {
hasValidAuth bool
user *auth.User
}

type preflightOrganizationsLoadedMsg struct {
Expand Down
1 change: 1 addition & 0 deletions internal/app/onboarding/preflight/preflight_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd {

func (m *Model) handleAuthChecked(msg preflightAuthCheckCompletedMsg) tea.Cmd {
m.state.HasValidAuth = msg.hasValidAuth
m.state.User = msg.user
if !m.state.HasValidAuth {
return m.emitResult()
}
Expand Down
38 changes: 0 additions & 38 deletions internal/app/statusbar/waste/waste.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,6 @@ func (m *Model) buildStateKey(s domain.AccountSummary, cats []domain.PolicyCateg
EstimatedCostPerHourVol: s.EstimatedCostPerHourVolume,
EstimatedVolumePerHour: s.EstimatedVolumePerHour,
EstimatedBytesPerHour: s.EstimatedBytesPerHour,
ObservedCostBefore: s.ObservedCostBefore,
ObservedCostAfter: s.ObservedCostAfter,
ObservedVolumeBefore: s.ObservedVolumeBefore,
ObservedVolumeAfter: s.ObservedVolumeAfter,
TotalCostPerHour: s.TotalCostPerHour,
TotalVolumePerHour: s.TotalVolumePerHour,
TotalBytesPerHour: s.TotalBytesPerHour,
Expand Down Expand Up @@ -206,10 +202,6 @@ type wasteSummaryKey struct {
EstimatedCostPerHourVol *float64 `json:"estimated_cost_per_hour_volume"`
EstimatedVolumePerHour *float64 `json:"estimated_volume_per_hour"`
EstimatedBytesPerHour *float64 `json:"estimated_bytes_per_hour"`
ObservedCostBefore *float64 `json:"observed_cost_before"`
ObservedCostAfter *float64 `json:"observed_cost_after"`
ObservedVolumeBefore *float64 `json:"observed_volume_before"`
ObservedVolumeAfter *float64 `json:"observed_volume_after"`
TotalCostPerHour *float64 `json:"total_cost_per_hour"`
TotalVolumePerHour *float64 `json:"total_volume_per_hour"`
TotalBytesPerHour *float64 `json:"total_bytes_per_hour"`
Expand Down Expand Up @@ -275,12 +267,6 @@ func (m *Model) CompactView() string {

var segments []string

// Observed savings from approved policies (always shown — these are measured).
if saving := formatObservedSaving(s); saving != "" {
savingStyle := lipgloss.NewStyle().Foreground(colors.Success).Background(colors.Bg)
segments = append(segments, savingStyle.Render("saving "+saving))
}

if s.AnalysisReady() {
// Waste percentage with pending count.
if wp := wastePercent(s); wp > 0 {
Expand Down Expand Up @@ -350,12 +336,6 @@ func (m *Model) renderWasteHeadline() string {

var parts []string

// Observed savings from approved policies (always shown — these are measured).
if saving := formatObservedSaving(s); saving != "" {
savingStyle := lipgloss.NewStyle().Foreground(colors.Success).Background(colors.Bg)
parts = append(parts, savingStyle.Render("saving "+saving))
}

// Pending count + waste %. Count first for consistency with other tabs.
if s.PendingPolicyCount > 0 {
dot := lipgloss.NewStyle().Foreground(colors.Warning).Background(colors.Bg).Render("●")
Expand Down Expand Up @@ -507,21 +487,3 @@ func (m *Model) cursorPrinciple() string {
}
return ""
}

// formatObservedSaving returns the observed savings from approved policies.
func formatObservedSaving(s domain.AccountSummary) string {
if s.ObservedCostBefore != nil && s.ObservedCostAfter != nil {
diff := *s.ObservedCostBefore - *s.ObservedCostAfter
if diff > 0 {
return format.YearlyCost(diff)
}
return ""
}
if s.ObservedVolumeBefore != nil && s.ObservedVolumeAfter != nil {
diff := *s.ObservedVolumeBefore - *s.ObservedVolumeAfter
if diff > 0 {
return format.Volume(diff) + " evt/hr"
}
}
return ""
}
6 changes: 3 additions & 3 deletions internal/boundary/graphql/datadog_account_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ func (s *DatadogAccountService) GetStatus(ctx context.Context, datadogAccountID
InactiveServices: statusNode.InactiveServices,
EventCount: statusNode.LogEventCount,
AnalyzedCount: statusNode.LogEventAnalyzedCount,
PendingPolicyCount: statusNode.PolicyPendingCount,
ApprovedPolicyCount: statusNode.PolicyApprovedCount,
DismissedPolicyCount: statusNode.PolicyDismissedCount,
PendingPolicyCount: statusNode.PolicyPendingLowCount + statusNode.PolicyPendingMediumCount + statusNode.PolicyPendingHighCount + statusNode.PolicyPendingCriticalCount,
ApprovedPolicyCount: statusNode.ApprovedRecommendationCount,
DismissedPolicyCount: statusNode.DismissedRecommendationCount,
}

s.scope.Debug("fetched datadog account status",
Expand Down
31 changes: 21 additions & 10 deletions internal/boundary/graphql/datadog_account_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,16 +292,21 @@ func TestDatadogAccountService_GetStatus(t *testing.T) {
Node: &gen.GetDatadogAccountStatusDatadogAccountsDatadogAccountConnectionEdgesDatadogAccountEdgeNodeDatadogAccount{
Id: "dd-123",
Status: &gen.GetDatadogAccountStatusDatadogAccountsDatadogAccountConnectionEdgesDatadogAccountEdgeNodeDatadogAccountStatusDatadogAccountStatusCache{
Health: gen.DatadogAccountStatusCacheHealthOk,
ReadyForUse: true,
LogServiceCount: 10,
LogActiveServices: 8,
OkServices: 7,
DisabledServices: 1,
InactiveServices: 1,
LogEventCount: 200,
LogEventAnalyzedCount: 180,
PolicyPendingCount: 12,
Health: gen.DatadogAccountStatusCacheHealthOk,
ReadyForUse: true,
LogServiceCount: 10,
LogActiveServices: 8,
OkServices: 7,
DisabledServices: 1,
InactiveServices: 1,
LogEventCount: 200,
LogEventAnalyzedCount: 180,
PolicyPendingLowCount: 3,
PolicyPendingMediumCount: 4,
PolicyPendingHighCount: 5,
PolicyPendingCriticalCount: 0,
ApprovedRecommendationCount: 9,
DismissedRecommendationCount: 2,
},
},
},
Expand Down Expand Up @@ -338,6 +343,12 @@ func TestDatadogAccountService_GetStatus(t *testing.T) {
if status.PendingPolicyCount != 12 {
t.Errorf("PendingPolicyCount = %d, want 12", status.PendingPolicyCount)
}
if status.ApprovedPolicyCount != 9 {
t.Errorf("ApprovedPolicyCount = %d, want 9", status.ApprovedPolicyCount)
}
if status.DismissedPolicyCount != 2 {
t.Errorf("DismissedPolicyCount = %d, want 2", status.DismissedPolicyCount)
}
})

t.Run("returns nil when no datadog account found", func(t *testing.T) {
Expand Down
Loading
Loading