Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
18 changes: 15 additions & 3 deletions cmd/importURL.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ func NewImportURLCommand(globalClientOpts *connectors.ClientOptions) *cobra.Comm
mainArtifact := true
secret := ""

f, mainArtifact, secret = parseImportURLArg(f)
var err error
f, mainArtifact, secret, err = parseImportURLArg(f)
if err != nil {
return err
}

// Try downloading the artifcat
msg, err := mc.DownloadArtifact(f, mainArtifact, secret)
Expand All @@ -64,7 +68,15 @@ func NewImportURLCommand(globalClientOpts *connectors.ClientOptions) *cobra.Comm
return importURLCmd
}

func parseImportURLArg(f string) (string, bool, string) {
func parseImportURLArg(f string) (string, bool, string, error) {
if !strings.HasPrefix(f, "https://") && !strings.HasPrefix(f, "http://") {
Comment thread
dipak0000812 marked this conversation as resolved.
Resolved
return "", false, "", errors.Wrapf(
errors.KindUsage,
"invalid artifact URL '%s': must start with http:// or https://",
f,
)
}
Comment thread
dipak0000812 marked this conversation as resolved.
Resolved

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this way it gives to many responsibilities to this function.
Cobra offer pre-hook Args which is the right place for this kind of validation
this is also valid for the if len(arg) == 0 check

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I totally agree here. We can use that functionality to shift our validation much closer to the actual user input.

mainArtifact := true
secret := ""

Expand All @@ -84,5 +96,5 @@ func parseImportURLArg(f string) (string, bool, string) {
}
}
}
return f, mainArtifact, secret
return f, mainArtifact, secret, nil
}
193 changes: 116 additions & 77 deletions cmd/importURL_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,148 +19,187 @@ package cmd
import (
"testing"

"github.com/microcks/microcks-cli/pkg/errors"
"github.com/stretchr/testify/assert"
)

func TestParseImportURLArg(t *testing.T) {
tests := []struct {
name string
input string
expectedURL string
name string
input string
expectedURL string
expectedMainArtifact bool
expectedSecret string
expectedSecret string
wantErr bool
expectedErrMsg string
}{
{
name: "standard URL without suffixes",
input: "https://example.com/openapi.yaml",
expectedURL: "https://example.com/openapi.yaml",
name: "standard URL without suffixes",
input: "https://example.com/openapi.yaml",
expectedURL: "https://example.com/openapi.yaml",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "standard URL with mainArtifact true",
input: "https://example.com/spec1.yaml:true",
expectedURL: "https://example.com/spec1.yaml",
name: "standard URL with mainArtifact true",
input: "https://example.com/spec1.yaml:true",
expectedURL: "https://example.com/spec1.yaml",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "standard URL with mainArtifact false",
input: "https://example.com/spec1.yaml:false",
expectedURL: "https://example.com/spec1.yaml",
name: "standard URL with mainArtifact false",
input: "https://example.com/spec1.yaml:false",
expectedURL: "https://example.com/spec1.yaml",
expectedMainArtifact: false,
expectedSecret: "",
expectedSecret: "",
},
{
name: "standard URL with mainArtifact and secret",
input: "https://example.com/spec1.yaml:true:my-secret",
expectedURL: "https://example.com/spec1.yaml",
name: "standard URL with mainArtifact and secret",
input: "https://example.com/spec1.yaml:true:my-secret",
expectedURL: "https://example.com/spec1.yaml",
expectedMainArtifact: true,
expectedSecret: "my-secret",
expectedSecret: "my-secret",
},
{
name: "standard URL with mainArtifact false and secret",
input: "https://example.com/spec1.yaml:false:my-secret-token",
expectedURL: "https://example.com/spec1.yaml",
name: "standard URL with mainArtifact false and secret",
input: "https://example.com/spec1.yaml:false:my-secret-token",
expectedURL: "https://example.com/spec1.yaml",
expectedMainArtifact: false,
expectedSecret: "my-secret-token",
expectedSecret: "my-secret-token",
},
{
name: "URL with port and no suffixes",
input: "http://localhost:8585/spec.yaml",
expectedURL: "http://localhost:8585/spec.yaml",
name: "URL with port and no suffixes",
input: "http://localhost:8585/spec.yaml",
expectedURL: "http://localhost:8585/spec.yaml",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "URL with port and mainArtifact true",
input: "http://localhost:8585/spec.yaml:true",
expectedURL: "http://localhost:8585/spec.yaml",
name: "URL with port and mainArtifact true",
input: "http://localhost:8585/spec.yaml:true",
expectedURL: "http://localhost:8585/spec.yaml",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "URL with port and mainArtifact false",
input: "http://localhost:8585/spec.yaml:false",
expectedURL: "http://localhost:8585/spec.yaml",
name: "URL with port and mainArtifact false",
input: "http://localhost:8585/spec.yaml:false",
expectedURL: "http://localhost:8585/spec.yaml",
expectedMainArtifact: false,
expectedSecret: "",
expectedSecret: "",
},
{
name: "URL with port, mainArtifact and secret",
input: "http://localhost:8585/spec.yaml:true:my-secret-token",
expectedURL: "http://localhost:8585/spec.yaml",
name: "URL with port, mainArtifact and secret",
input: "http://localhost:8585/spec.yaml:true:my-secret-token",
expectedURL: "http://localhost:8585/spec.yaml",
expectedMainArtifact: true,
expectedSecret: "my-secret-token",
expectedSecret: "my-secret-token",
},
{
name: "URL with port, mainArtifact false and secret",
input: "http://localhost:8585/spec.yaml:false:my-secret-token",
expectedURL: "http://localhost:8585/spec.yaml",
name: "URL with port, mainArtifact false and secret",
input: "http://localhost:8585/spec.yaml:false:my-secret-token",
expectedURL: "http://localhost:8585/spec.yaml",
expectedMainArtifact: false,
expectedSecret: "my-secret-token",
expectedSecret: "my-secret-token",
},
{
name: "malformed bool suffix preserves URL",
input: "http://localhost:8585/spec.yaml:tru",
expectedURL: "http://localhost:8585/spec.yaml:tru",
name: "malformed bool suffix preserves URL",
input: "http://localhost:8585/spec.yaml:tru",
expectedURL: "http://localhost:8585/spec.yaml:tru",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "malformed bool with secret preserves URL",
input: "http://localhost:8585/spec.yaml:tru:mysecret",
expectedURL: "http://localhost:8585/spec.yaml:tru:mysecret",
name: "malformed bool with secret preserves URL",
input: "http://localhost:8585/spec.yaml:tru:mysecret",
expectedURL: "http://localhost:8585/spec.yaml:tru:mysecret",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "URL with path and no port",
input: "https://raw.githubusercontent.com/org/repo/main/spec.yaml",
expectedURL: "https://raw.githubusercontent.com/org/repo/main/spec.yaml",
name: "URL with path and no port",
input: "https://raw.githubusercontent.com/org/repo/main/spec.yaml",
expectedURL: "https://raw.githubusercontent.com/org/repo/main/spec.yaml",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "short URL without path",
input: "http://localhost:true",
expectedURL: "http://localhost",
name: "short URL without path",
input: "http://localhost:true",
expectedURL: "http://localhost",
expectedMainArtifact: true,
expectedSecret: "",
expectedSecret: "",
},
{
name: "boolean-like secret is not consumed as primary flag",
input: "http://example.com/api:true:false",
expectedURL: "http://example.com/api",
name: "boolean-like secret is not consumed as primary flag",
input: "http://example.com/api:true:false",
expectedURL: "http://example.com/api",
expectedMainArtifact: true,
expectedSecret: "false",
expectedSecret: "false",
},
{
name: "secret containing colons",
input: "http://example.com/api:true:my:secret:token",
expectedURL: "http://example.com/api",
name: "secret containing colons",
input: "http://example.com/api:true:my:secret:token",
expectedURL: "http://example.com/api",
expectedMainArtifact: true,
expectedSecret: "my:secret:token",
expectedSecret: "my:secret:token",
},
{
name: "URL with port and secret containing colons",
input: "http://localhost:8080/api:false:auth:basic:user:pass",
expectedURL: "http://localhost:8080/api",
name: "URL with port and secret containing colons",
input: "http://localhost:8080/api:false:auth:basic:user:pass",
expectedURL: "http://localhost:8080/api",
expectedMainArtifact: false,
expectedSecret: "auth:basic:user:pass",
expectedSecret: "auth:basic:user:pass",
},
{
name: "boolean host with port does not get misparsed as primary flag",
input: "http://true:8080/api:false:secret",
expectedURL: "http://true:8080/api",
name: "boolean host with port does not get misparsed as primary flag",
input: "http://true:8080/api:false:secret",
expectedURL: "http://true:8080/api",
expectedMainArtifact: false,
expectedSecret: "secret",
expectedSecret: "secret",
},
{
name: "relative path without scheme",
input: "spec.yaml",
wantErr: true,
expectedErrMsg: "invalid artifact URL 'spec.yaml': must start with http:// or https://",
},
{
name: "relative path with suffixes without scheme",
input: "spec.yaml:true:mysecret",
wantErr: true,
expectedErrMsg: "invalid artifact URL 'spec.yaml:true:mysecret': must start with http:// or https://",
},
{
name: "unsupported ftp scheme",
input: "ftp://example.com/spec.yaml",
wantErr: true,
expectedErrMsg: "invalid artifact URL 'ftp://example.com/spec.yaml': must start with http:// or https://",
},
{
name: "file scheme",
input: "file:///tmp/spec.yaml",
wantErr: true,
expectedErrMsg: "invalid artifact URL 'file:///tmp/spec.yaml': must start with http:// or https://",
},
{
name: "empty string",
input: "",
wantErr: true,
expectedErrMsg: "invalid artifact URL '': must start with http:// or https://",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
url, mainArtifact, secret := parseImportURLArg(tt.input)
url, mainArtifact, secret, err := parseImportURLArg(tt.input)
if tt.wantErr {
assert.EqualError(t, err, tt.expectedErrMsg)
assert.Equal(t, errors.KindUsage, errors.KindOf(err))
return
}
assert.NoError(t, err)
assert.Equal(t, tt.expectedURL, url)
assert.Equal(t, tt.expectedMainArtifact, mainArtifact)
assert.Equal(t, tt.expectedSecret, secret)
Expand Down
Loading