Skip to content

Commit ee9bd7b

Browse files
Merge branch 'main' into miryamFoifer/containersRTScanCommand
2 parents 7429f87 + 62e0888 commit ee9bd7b

File tree

11 files changed

+204
-1
lines changed

11 files changed

+204
-1
lines changed

cmd/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func main() {
9595
byorWrapper := wrappers.NewByorHTTPWrapper(byorPath)
9696
containerResolverWrapper := wrappers.NewContainerResolverWrapper()
9797
realTimeWrapper := wrappers.NewRealtimeScannerHTTPWrapper(realtimeScannerPath, jwtWrapper, featureFlagsWrapper)
98+
telemetryWrapper := wrappers.NewHTTPTelemetryAIWrapper(realtimeScannerPath)
9899

99100
astCli := commands.NewAstCLI(
100101
applicationsWrapper,
@@ -133,6 +134,7 @@ func main() {
133134
byorWrapper,
134135
containerResolverWrapper,
135136
realTimeWrapper,
137+
telemetryWrapper,
136138
)
137139
exitListener()
138140
err = astCli.Execute()

internal/commands/root.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func NewAstCLI(
6060
byorWrapper wrappers.ByorWrapper,
6161
containerResolverWrapper wrappers.ContainerResolverWrapper,
6262
realTimeWrapper wrappers.RealtimeScannerWrapper,
63+
telemetryWrapper wrappers.TelemetryWrapper,
6364
) *cobra.Command {
6465
// Create the root
6566
rootCmd := &cobra.Command{
@@ -224,7 +225,7 @@ func NewAstCLI(
224225

225226
chatCmd := NewChatCommand(chatWrapper, tenantWrapper)
226227
hooksCmd := NewHooksCommand(jwtWrapper)
227-
228+
telemetryCmd := NewTelemetryCommand(telemetryWrapper)
228229
rootCmd.AddCommand(
229230
scanCmd,
230231
projectCmd,
@@ -236,6 +237,7 @@ func NewAstCLI(
236237
configCmd,
237238
chatCmd,
238239
hooksCmd,
240+
telemetryCmd,
239241
)
240242

241243
rootCmd.SilenceUsage = true

internal/commands/root_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func createASTTestCommand() *cobra.Command {
7272
containerResolverMockWrapper := &mock.ContainerResolverMockWrapper{}
7373
customStatesMockWrapper := &mock.CustomStatesMockWrapper{}
7474
realTimeWrapper := &mock.RealtimeScannerMockWrapper{}
75+
telemetryWrapper := &mock.TelemetryMockWrapper{}
7576
return NewAstCLI(
7677
applicationWrapper,
7778
scansMockWrapper,
@@ -109,6 +110,7 @@ func createASTTestCommand() *cobra.Command {
109110
byorWrapper,
110111
containerResolverMockWrapper,
111112
realTimeWrapper,
113+
telemetryWrapper,
112114
)
113115
}
114116

internal/commands/telemetry.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package commands
2+
3+
import (
4+
"github.com/MakeNowJust/heredoc"
5+
"github.com/checkmarx/ast-cli/internal/params"
6+
"github.com/checkmarx/ast-cli/internal/wrappers"
7+
"github.com/pkg/errors"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func NewTelemetryCommand(telemetryWrapper wrappers.TelemetryWrapper) *cobra.Command {
12+
telemetryCmd := &cobra.Command{
13+
Use: "telemetry",
14+
Short: "Telemetry user events",
15+
Long: "The 'telemetry' command allows collecting and sending user interaction events for analysis purposes.",
16+
}
17+
telemetryAICmd := telemetryAISubCommand(telemetryWrapper)
18+
19+
telemetryCmd.AddCommand(telemetryAICmd)
20+
return telemetryCmd
21+
}
22+
23+
func telemetryAISubCommand(telemetryAIWrapper wrappers.TelemetryWrapper) *cobra.Command {
24+
telemetryAICmd := &cobra.Command{
25+
Use: "ai",
26+
Short: "telemetry for user events related to AI functionality.",
27+
Long: "Collects telemetry data for user interactions related to AI features.",
28+
Example: heredoc.Doc(
29+
`
30+
$ cx telemetry ai --ai-provider <AI Provider> --problem-severity <Problem Severity> --type<Event Type> --sub-type<Event Name> --agent <Agent> --engine <Engine>
31+
`,
32+
),
33+
34+
RunE: runTelemetryAI(telemetryAIWrapper),
35+
}
36+
37+
telemetryAICmd.PersistentFlags().String(params.AiProviderFlag, "", "AI Provider")
38+
telemetryAICmd.PersistentFlags().String(params.ProblemSeverityFlag, "", "Problem Severity")
39+
telemetryAICmd.PersistentFlags().String(params.TypeFlag, "", "Type")
40+
telemetryAICmd.PersistentFlags().String(params.SubTypeFlag, "", "Sub Type")
41+
telemetryAICmd.PersistentFlags().String(params.EngineFlag, "", "Engine")
42+
telemetryAICmd.PersistentFlags().String(params.AgentFlag, "", "Agent")
43+
44+
return telemetryAICmd
45+
}
46+
47+
func runTelemetryAI(telemetryWrapper wrappers.TelemetryWrapper) func(*cobra.Command, []string) error {
48+
return func(cmd *cobra.Command, _ []string) error {
49+
aiProvider, _ := cmd.Flags().GetString("ai-provider")
50+
problemSeverity, _ := cmd.Flags().GetString("problem-severity")
51+
eventType, _ := cmd.Flags().GetString("type")
52+
subType, _ := cmd.Flags().GetString("sub-type")
53+
agent, _ := cmd.Flags().GetString("agent")
54+
engine, _ := cmd.Flags().GetString("engine")
55+
56+
err := telemetryWrapper.SendAIDataToLog(&wrappers.DataForAITelemetry{
57+
AIProvider: aiProvider,
58+
ProblemSeverity: problemSeverity,
59+
Type: eventType,
60+
SubType: subType,
61+
Agent: agent,
62+
Engine: engine,
63+
})
64+
65+
if err != nil {
66+
return errors.Wrapf(err, "%s", "Failed logging the data")
67+
}
68+
69+
return nil
70+
}
71+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package commands
2+
3+
import (
4+
"testing"
5+
6+
"github.com/checkmarx/ast-cli/internal/wrappers"
7+
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
8+
)
9+
10+
func TestRunTelemetryAI_SendToLogSuccess(t *testing.T) {
11+
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
12+
execCmdNilAssertion(
13+
t,
14+
"telemetry", "ai", "--ai-provider", "Cursor", "--problem-severity", "Critical", "--type", "click", "--sub-type", "ast-results.viewPackageDetails", "--agent", "Cursor", "--engine", "secrets")
15+
}
16+
17+
func TestTelemetryHelp(t *testing.T) {
18+
execCmdNilAssertion(t, "help", "telemetry", "ai")
19+
}

internal/params/flags.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ package params
44
const (
55
AllStatesFlag = "all"
66
AgentFlag = "agent"
7+
AiProviderFlag = "ai-provider"
8+
ProblemSeverityFlag = "problem-severity"
9+
TypeFlag = "type"
10+
SubTypeFlag = "sub-type"
11+
EngineFlag = "engine"
712
OriginFlag = "origin"
813
AgentFlagUsage = "Scan origin name"
914
ApplicationName = "application-name"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package mock
2+
3+
import "github.com/checkmarx/ast-cli/internal/wrappers"
4+
5+
type TelemetryMockWrapper struct {
6+
}
7+
8+
func (t TelemetryMockWrapper) SendAIDataToLog(data *wrappers.DataForAITelemetry) error {
9+
return nil
10+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package wrappers
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
9+
commonParams "github.com/checkmarx/ast-cli/internal/params"
10+
"github.com/pkg/errors"
11+
"github.com/spf13/viper"
12+
)
13+
14+
type TelemetryHTTPWrapper struct {
15+
path string
16+
}
17+
18+
func NewHTTPTelemetryAIWrapper(path string) *TelemetryHTTPWrapper {
19+
return &TelemetryHTTPWrapper{
20+
path: path,
21+
}
22+
}
23+
24+
func (r *TelemetryHTTPWrapper) SendAIDataToLog(data *DataForAITelemetry) error {
25+
clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey)
26+
jsonBytes, err := json.Marshal(data)
27+
if err != nil {
28+
return err
29+
}
30+
31+
fn := func() (*http.Response, error) {
32+
return SendHTTPRequest(http.MethodPost, fmt.Sprint(r.path, "/log"), bytes.NewBuffer(jsonBytes), true, clientTimeout)
33+
}
34+
resp, err := retryHTTPRequest(fn, retryAttempts, retryDelay)
35+
if err != nil {
36+
return err
37+
}
38+
defer func() {
39+
if err == nil {
40+
_ = resp.Body.Close()
41+
}
42+
}()
43+
switch resp.StatusCode {
44+
case http.StatusBadRequest, http.StatusInternalServerError:
45+
return errors.Errorf("Failed to log the data, status code: %s", resp.Status)
46+
case http.StatusNotFound:
47+
return errors.Errorf("Telemetry AI endpoint not found")
48+
}
49+
return nil
50+
}

internal/wrappers/telemetry.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package wrappers
2+
3+
type DataForAITelemetry struct {
4+
AIProvider string `json:"aiProvider"`
5+
ProblemSeverity string `json:"problemSeverity"`
6+
Type string `json:"type"`
7+
SubType string `json:"subType"`
8+
Agent string `json:"agent"`
9+
Engine string `json:"engine"`
10+
}
11+
12+
type TelemetryWrapper interface {
13+
SendAIDataToLog(data *DataForAITelemetry) error
14+
}

test/integration/telemetry_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//go:build integration
2+
3+
package integration
4+
5+
import (
6+
"github.com/checkmarx/ast-cli/internal/params"
7+
"github.com/stretchr/testify/assert"
8+
"testing"
9+
)
10+
11+
func TestTelemetryAI(t *testing.T) {
12+
t.Skip()
13+
bindKeysToEnvAndDefault(t)
14+
args := []string{
15+
"telemetry", "ai",
16+
flag(params.AiProviderFlag), "Cursor",
17+
flag(params.ProblemSeverityFlag), "Critical",
18+
flag(params.TypeFlag), "click",
19+
flag(params.SubTypeFlag), "ast-results.viewPackageDetails",
20+
flag(params.AgentFlag), "Cursor",
21+
flag(params.EngineFlag), "secrets",
22+
}
23+
24+
err, _ := executeCommand(t, args...)
25+
assert.Nil(t, err)
26+
}

0 commit comments

Comments
 (0)