Skip to content

Commit

Permalink
feat: start refactoring the report command (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
vishnu-deepsource committed Feb 5, 2024
1 parent 8455003 commit b4ee0fc
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 107 deletions.
6 changes: 1 addition & 5 deletions .deepsource.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,4 @@ enabled = true

[[analyzers]]
name = "test-coverage"
enabled = true

[[transformers]]
name = "gofumpt"
enabled = true
enabled = true
27 changes: 27 additions & 0 deletions command/report/dsn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package report

import (
"errors"
"regexp"
)

var ErrInvalidDSN = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")

type DSN struct {
Protocol string
Host string
Token string
}

func NewDSN(raw string) (*DSN, error) {
dsnPattern := regexp.MustCompile(`^(https?)://([^:@]+)@([^:/]+(?:\:\d+)?)`)
matches := dsnPattern.FindStringSubmatch(raw)
if len(matches) != 4 {
return nil, ErrInvalidDSN
}
return &DSN{
Protocol: matches[1],
Token: matches[2],
Host: matches[3],
}, nil
}
70 changes: 70 additions & 0 deletions command/report/dsn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package report

import (
"reflect"
"testing"
)

func TestNewDSN(t *testing.T) {
type args struct {
raw string
}
tests := []struct {
name string
args args
want *DSN
wantErr error
}{
{
name: "valid DSN",
args: args{
raw: "https://e1099ed7240c4045b5ab3fedebc7b5d7@app.deepsource.com",
},
want: &DSN{
Token: "e1099ed7240c4045b5ab3fedebc7b5d7",
Host: "app.deepsource.com",
Protocol: "https",
},
wantErr: nil,
},
{
name: "valid DSN with port",
args: args{
raw: "http://f59a44307@localhost:8081",
},
want: &DSN{
Token: "f59a44307",
Host: "localhost:8081",
Protocol: "http",
},
},
{
name: "invalid DSN no http",
args: args{
raw: "no http",
},
want: nil,
wantErr: ErrInvalidDSN,
},
{
name: "invalid DSN",
args: args{
raw: "https://e1099ed7240c4045b5ab3fedebc7b5d7",
},
want: nil,
wantErr: ErrInvalidDSN,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewDSN(tt.args.raw)
if err != tt.wantErr {
t.Errorf("NewDSN() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewDSN() = %v, want %v", got, tt.want)
}
})
}
}
139 changes: 52 additions & 87 deletions command/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type ReportOptions struct {
Value string
ValueFile string
SkipCertificateVerification bool
DSN string
}

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

func (opts *ReportOptions) Run() int {
// Verify the env variables
dsn := os.Getenv("DEEPSOURCE_DSN")
if dsn == "" {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Environment variable DEEPSOURCE_DSN not set (or) is empty. You can find it under the repository settings page")
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(sentry.User{ID: dsn})
})

/////////////////////
// Command: report //
/////////////////////

reportCommandAnalyzerShortcode := strings.TrimSpace(opts.Analyzer)
reportCommandAnalyzerType := strings.TrimSpace(opts.AnalyzerType)
reportCommandKey := strings.TrimSpace(opts.Key)
reportCommandValue := opts.Value
reportCommandValueFile := strings.TrimSpace(opts.ValueFile)

// Get current path
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to identify current directory")
sentry.CaptureException(err)
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetExtra("currentDir", currentDir)
})

//////////////////
// Validate Key //
//////////////////
func (opts *ReportOptions) sanitize() {
opts.Analyzer = strings.TrimSpace(opts.Analyzer)
opts.AnalyzerType = strings.TrimSpace(opts.AnalyzerType)
opts.Key = strings.TrimSpace(opts.Key)
opts.Value = strings.TrimSpace(opts.Value)
opts.ValueFile = strings.TrimSpace(opts.ValueFile)
opts.DSN = strings.TrimSpace(os.Getenv("DEEPSOURCE_DSN"))
}

func (opts *ReportOptions) validateKey() error {
supportedKeys := map[string]bool{
"python": true,
"go": true,
Expand All @@ -123,66 +98,56 @@ func (opts *ReportOptions) Run() int {
"kotlin": true,
}

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

if reportCommandAnalyzerShortcode == "test-coverage" && !supportedKeys[reportCommandKey] {
err = fmt.Errorf("DeepSource | Error | Invalid Key: %s (Supported Keys: %v)", reportCommandKey, allowedKeys(supportedKeys))
fmt.Fprintln(os.Stderr, err)
sentry.CaptureException(err)
return nil
}

func (opts *ReportOptions) Run() int {
opts.sanitize()
if opts.DSN == "" {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Environment variable DEEPSOURCE_DSN not set (or) is empty. You can find it under the repository settings page")
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(sentry.User{ID: opts.DSN})
})

//////////////////
// Validate DSN //
//////////////////

// Protocol
dsnSplitProtocolBody := strings.Split(dsn, "://")
/////////////////////
// Command: report //
/////////////////////

// Validate DSN parsing
if len(dsnSplitProtocolBody) != 2 {
err = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
fmt.Fprintln(os.Stderr, err)
// Get current path
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to identify current directory")
sentry.CaptureException(err)
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetExtra("currentDir", currentDir)
})

// Check for valid protocol
if !strings.HasPrefix(dsnSplitProtocolBody[0], "http") {
err = errors.New("DeepSource | Error | DSN specified should start with http(s). Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
// validate key
if err := opts.validateKey(); err != nil {
fmt.Fprintln(os.Stderr, err)
sentry.CaptureException(err)
return 1
}
dsnProtocol := dsnSplitProtocolBody[0]

// Parse body of the DSN
dsnSplitTokenHost := strings.Split(dsnSplitProtocolBody[1], "@")

// Validate DSN parsing
if len(dsnSplitTokenHost) != 2 {
err = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
dsn, err := NewDSN(opts.DSN)
if err != nil {
fmt.Fprintln(os.Stderr, err)
sentry.CaptureException(err)
return 1
}

// Set values parsed from DSN
dsnHost := dsnSplitTokenHost[1]

///////////////////////
// Generate metadata //
///////////////////////

// Access token
dsnAccessToken := dsnSplitTokenHost[0]

// Head Commit OID
headCommitOID, warning, err := gitGetHead(currentDir)
if err != nil {
Expand All @@ -196,7 +161,7 @@ func (opts *ReportOptions) Run() int {
})

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

analyzerShortcode = reportCommandAnalyzerShortcode
analyzerType = reportCommandAnalyzerType
artifactKey = reportCommandKey
analyzerShortcode = opts.Analyzer
analyzerType = opts.AnalyzerType
artifactKey = opts.Key

if reportCommandValue != "" {
artifactValue = reportCommandValue
if opts.Value != "" {
artifactValue = opts.Value
}

if reportCommandValueFile != "" {
if opts.ValueFile != "" {
// Check file size
_, err := os.Stat(reportCommandValueFile)
_, err := os.Stat(opts.ValueFile)
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", reportCommandValueFile)
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", opts.ValueFile)
sentry.CaptureException(err)
return 1
}

valueBytes, err := os.ReadFile(reportCommandValueFile)
valueBytes, err := os.ReadFile(opts.ValueFile)
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", reportCommandValueFile)
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", opts.ValueFile)
sentry.CaptureException(err)
return 1
}
Expand All @@ -244,7 +209,7 @@ func (opts *ReportOptions) Run() int {
}

r, err := makeQuery(
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
qBytes,
"application/json",
opts.SkipCertificateVerification,
Expand Down Expand Up @@ -284,7 +249,7 @@ func (opts *ReportOptions) Run() int {
compressLevel := 20
compressedBytes, err = zstd.CompressLevel(compressedBytes, []byte(artifactValue), compressLevel)
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Failed to compress value file:", reportCommandValueFile)
fmt.Fprintln(os.Stderr, "DeepSource | Error | Failed to compress value file:", opts.ValueFile)
sentry.CaptureException(err)
return 1
}
Expand All @@ -302,7 +267,7 @@ func (opts *ReportOptions) Run() int {
////////////////////

queryInput := ReportQueryInput{
AccessToken: dsnAccessToken,
AccessToken: dsn.Token,
CommitOID: headCommitOID,
ReporterName: "cli",
ReporterVersion: CliVersion,
Expand Down Expand Up @@ -331,7 +296,7 @@ func (opts *ReportOptions) Run() int {
}

queryResponseBody, err := makeQuery(
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
queryBodyBytes,
"application/json",
opts.SkipCertificateVerification,
Expand All @@ -347,7 +312,7 @@ func (opts *ReportOptions) Run() int {
return 1
}
queryResponseBody, err = makeQuery(
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
queryBodyBytes,
"application/json",
opts.SkipCertificateVerification,
Expand Down

0 comments on commit b4ee0fc

Please sign in to comment.