Skip to content

Commit b4ee0fc

Browse files
feat: start refactoring the report command (#238)
1 parent 8455003 commit b4ee0fc

File tree

8 files changed

+171
-107
lines changed

8 files changed

+171
-107
lines changed

.deepsource.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,4 @@ enabled = true
1313

1414
[[analyzers]]
1515
name = "test-coverage"
16-
enabled = true
17-
18-
[[transformers]]
19-
name = "gofumpt"
20-
enabled = true
16+
enabled = true

command/report/dsn.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package report
2+
3+
import (
4+
"errors"
5+
"regexp"
6+
)
7+
8+
var ErrInvalidDSN = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
9+
10+
type DSN struct {
11+
Protocol string
12+
Host string
13+
Token string
14+
}
15+
16+
func NewDSN(raw string) (*DSN, error) {
17+
dsnPattern := regexp.MustCompile(`^(https?)://([^:@]+)@([^:/]+(?:\:\d+)?)`)
18+
matches := dsnPattern.FindStringSubmatch(raw)
19+
if len(matches) != 4 {
20+
return nil, ErrInvalidDSN
21+
}
22+
return &DSN{
23+
Protocol: matches[1],
24+
Token: matches[2],
25+
Host: matches[3],
26+
}, nil
27+
}

command/report/dsn_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package report
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestNewDSN(t *testing.T) {
9+
type args struct {
10+
raw string
11+
}
12+
tests := []struct {
13+
name string
14+
args args
15+
want *DSN
16+
wantErr error
17+
}{
18+
{
19+
name: "valid DSN",
20+
args: args{
21+
raw: "https://e1099ed7240c4045b5ab3fedebc7b5d7@app.deepsource.com",
22+
},
23+
want: &DSN{
24+
Token: "e1099ed7240c4045b5ab3fedebc7b5d7",
25+
Host: "app.deepsource.com",
26+
Protocol: "https",
27+
},
28+
wantErr: nil,
29+
},
30+
{
31+
name: "valid DSN with port",
32+
args: args{
33+
raw: "http://f59a44307@localhost:8081",
34+
},
35+
want: &DSN{
36+
Token: "f59a44307",
37+
Host: "localhost:8081",
38+
Protocol: "http",
39+
},
40+
},
41+
{
42+
name: "invalid DSN no http",
43+
args: args{
44+
raw: "no http",
45+
},
46+
want: nil,
47+
wantErr: ErrInvalidDSN,
48+
},
49+
{
50+
name: "invalid DSN",
51+
args: args{
52+
raw: "https://e1099ed7240c4045b5ab3fedebc7b5d7",
53+
},
54+
want: nil,
55+
wantErr: ErrInvalidDSN,
56+
},
57+
}
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
got, err := NewDSN(tt.args.raw)
61+
if err != tt.wantErr {
62+
t.Errorf("NewDSN() error = %v, wantErr %v", err, tt.wantErr)
63+
return
64+
}
65+
if !reflect.DeepEqual(got, tt.want) {
66+
t.Errorf("NewDSN() = %v, want %v", got, tt.want)
67+
}
68+
})
69+
}
70+
}

command/report/report.go

Lines changed: 52 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type ReportOptions struct {
2424
Value string
2525
ValueFile string
2626
SkipCertificateVerification bool
27+
DSN string
2728
}
2829

2930
// NewCmdVersion returns the current version of cli being used
@@ -72,42 +73,16 @@ func NewCmdReport() *cobra.Command {
7273
return cmd
7374
}
7475

75-
func (opts *ReportOptions) Run() int {
76-
// Verify the env variables
77-
dsn := os.Getenv("DEEPSOURCE_DSN")
78-
if dsn == "" {
79-
fmt.Fprintln(os.Stderr, "DeepSource | Error | Environment variable DEEPSOURCE_DSN not set (or) is empty. You can find it under the repository settings page")
80-
return 1
81-
}
82-
sentry.ConfigureScope(func(scope *sentry.Scope) {
83-
scope.SetUser(sentry.User{ID: dsn})
84-
})
85-
86-
/////////////////////
87-
// Command: report //
88-
/////////////////////
89-
90-
reportCommandAnalyzerShortcode := strings.TrimSpace(opts.Analyzer)
91-
reportCommandAnalyzerType := strings.TrimSpace(opts.AnalyzerType)
92-
reportCommandKey := strings.TrimSpace(opts.Key)
93-
reportCommandValue := opts.Value
94-
reportCommandValueFile := strings.TrimSpace(opts.ValueFile)
95-
96-
// Get current path
97-
currentDir, err := os.Getwd()
98-
if err != nil {
99-
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to identify current directory")
100-
sentry.CaptureException(err)
101-
return 1
102-
}
103-
sentry.ConfigureScope(func(scope *sentry.Scope) {
104-
scope.SetExtra("currentDir", currentDir)
105-
})
106-
107-
//////////////////
108-
// Validate Key //
109-
//////////////////
76+
func (opts *ReportOptions) sanitize() {
77+
opts.Analyzer = strings.TrimSpace(opts.Analyzer)
78+
opts.AnalyzerType = strings.TrimSpace(opts.AnalyzerType)
79+
opts.Key = strings.TrimSpace(opts.Key)
80+
opts.Value = strings.TrimSpace(opts.Value)
81+
opts.ValueFile = strings.TrimSpace(opts.ValueFile)
82+
opts.DSN = strings.TrimSpace(os.Getenv("DEEPSOURCE_DSN"))
83+
}
11084

85+
func (opts *ReportOptions) validateKey() error {
11186
supportedKeys := map[string]bool{
11287
"python": true,
11388
"go": true,
@@ -123,66 +98,56 @@ func (opts *ReportOptions) Run() int {
12398
"kotlin": true,
12499
}
125100

126-
allowedKeys := func(m map[string]bool) []string {
127-
keys := make([]string, 0, len(supportedKeys))
128-
for k := range m {
129-
keys = append(keys, k)
130-
}
131-
return keys
101+
if opts.Analyzer == "test-coverage" && !supportedKeys[opts.Key] {
102+
return fmt.Errorf("DeepSource | Error | Invalid Key: %s (Supported Keys: %v)", opts.Key, supportedKeys)
132103
}
133104

134-
if reportCommandAnalyzerShortcode == "test-coverage" && !supportedKeys[reportCommandKey] {
135-
err = fmt.Errorf("DeepSource | Error | Invalid Key: %s (Supported Keys: %v)", reportCommandKey, allowedKeys(supportedKeys))
136-
fmt.Fprintln(os.Stderr, err)
137-
sentry.CaptureException(err)
105+
return nil
106+
}
107+
108+
func (opts *ReportOptions) Run() int {
109+
opts.sanitize()
110+
if opts.DSN == "" {
111+
fmt.Fprintln(os.Stderr, "DeepSource | Error | Environment variable DEEPSOURCE_DSN not set (or) is empty. You can find it under the repository settings page")
138112
return 1
139113
}
114+
sentry.ConfigureScope(func(scope *sentry.Scope) {
115+
scope.SetUser(sentry.User{ID: opts.DSN})
116+
})
140117

141-
//////////////////
142-
// Validate DSN //
143-
//////////////////
144-
145-
// Protocol
146-
dsnSplitProtocolBody := strings.Split(dsn, "://")
118+
/////////////////////
119+
// Command: report //
120+
/////////////////////
147121

148-
// Validate DSN parsing
149-
if len(dsnSplitProtocolBody) != 2 {
150-
err = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
151-
fmt.Fprintln(os.Stderr, err)
122+
// Get current path
123+
currentDir, err := os.Getwd()
124+
if err != nil {
125+
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to identify current directory")
152126
sentry.CaptureException(err)
153127
return 1
154128
}
129+
sentry.ConfigureScope(func(scope *sentry.Scope) {
130+
scope.SetExtra("currentDir", currentDir)
131+
})
155132

156-
// Check for valid protocol
157-
if !strings.HasPrefix(dsnSplitProtocolBody[0], "http") {
158-
err = errors.New("DeepSource | Error | DSN specified should start with http(s). Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
133+
// validate key
134+
if err := opts.validateKey(); err != nil {
159135
fmt.Fprintln(os.Stderr, err)
160136
sentry.CaptureException(err)
161137
return 1
162138
}
163-
dsnProtocol := dsnSplitProtocolBody[0]
164-
165-
// Parse body of the DSN
166-
dsnSplitTokenHost := strings.Split(dsnSplitProtocolBody[1], "@")
167139

168-
// Validate DSN parsing
169-
if len(dsnSplitTokenHost) != 2 {
170-
err = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
140+
dsn, err := NewDSN(opts.DSN)
141+
if err != nil {
171142
fmt.Fprintln(os.Stderr, err)
172143
sentry.CaptureException(err)
173144
return 1
174145
}
175146

176-
// Set values parsed from DSN
177-
dsnHost := dsnSplitTokenHost[1]
178-
179147
///////////////////////
180148
// Generate metadata //
181149
///////////////////////
182150

183-
// Access token
184-
dsnAccessToken := dsnSplitTokenHost[0]
185-
186151
// Head Commit OID
187152
headCommitOID, warning, err := gitGetHead(currentDir)
188153
if err != nil {
@@ -196,7 +161,7 @@ func (opts *ReportOptions) Run() int {
196161
})
197162

198163
// Flag validation
199-
if reportCommandValue == "" && reportCommandValueFile == "" {
164+
if opts.Value == "" && opts.ValueFile == "" {
200165
fmt.Fprintln(os.Stderr, "DeepSource | Error | '--value' (or) '--value-file' not passed")
201166
return 1
202167
}
@@ -206,26 +171,26 @@ func (opts *ReportOptions) Run() int {
206171
var artifactKey string
207172
var artifactValue string
208173

209-
analyzerShortcode = reportCommandAnalyzerShortcode
210-
analyzerType = reportCommandAnalyzerType
211-
artifactKey = reportCommandKey
174+
analyzerShortcode = opts.Analyzer
175+
analyzerType = opts.AnalyzerType
176+
artifactKey = opts.Key
212177

213-
if reportCommandValue != "" {
214-
artifactValue = reportCommandValue
178+
if opts.Value != "" {
179+
artifactValue = opts.Value
215180
}
216181

217-
if reportCommandValueFile != "" {
182+
if opts.ValueFile != "" {
218183
// Check file size
219-
_, err := os.Stat(reportCommandValueFile)
184+
_, err := os.Stat(opts.ValueFile)
220185
if err != nil {
221-
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", reportCommandValueFile)
186+
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", opts.ValueFile)
222187
sentry.CaptureException(err)
223188
return 1
224189
}
225190

226-
valueBytes, err := os.ReadFile(reportCommandValueFile)
191+
valueBytes, err := os.ReadFile(opts.ValueFile)
227192
if err != nil {
228-
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", reportCommandValueFile)
193+
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", opts.ValueFile)
229194
sentry.CaptureException(err)
230195
return 1
231196
}
@@ -244,7 +209,7 @@ func (opts *ReportOptions) Run() int {
244209
}
245210

246211
r, err := makeQuery(
247-
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
212+
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
248213
qBytes,
249214
"application/json",
250215
opts.SkipCertificateVerification,
@@ -284,7 +249,7 @@ func (opts *ReportOptions) Run() int {
284249
compressLevel := 20
285250
compressedBytes, err = zstd.CompressLevel(compressedBytes, []byte(artifactValue), compressLevel)
286251
if err != nil {
287-
fmt.Fprintln(os.Stderr, "DeepSource | Error | Failed to compress value file:", reportCommandValueFile)
252+
fmt.Fprintln(os.Stderr, "DeepSource | Error | Failed to compress value file:", opts.ValueFile)
288253
sentry.CaptureException(err)
289254
return 1
290255
}
@@ -302,7 +267,7 @@ func (opts *ReportOptions) Run() int {
302267
////////////////////
303268

304269
queryInput := ReportQueryInput{
305-
AccessToken: dsnAccessToken,
270+
AccessToken: dsn.Token,
306271
CommitOID: headCommitOID,
307272
ReporterName: "cli",
308273
ReporterVersion: CliVersion,
@@ -331,7 +296,7 @@ func (opts *ReportOptions) Run() int {
331296
}
332297

333298
queryResponseBody, err := makeQuery(
334-
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
299+
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
335300
queryBodyBytes,
336301
"application/json",
337302
opts.SkipCertificateVerification,
@@ -347,7 +312,7 @@ func (opts *ReportOptions) Run() int {
347312
return 1
348313
}
349314
queryResponseBody, err = makeQuery(
350-
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
315+
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
351316
queryBodyBytes,
352317
"application/json",
353318
opts.SkipCertificateVerification,

0 commit comments

Comments
 (0)