Skip to content

Commit

Permalink
[feat] Add validation for build request types
Browse files Browse the repository at this point in the history
  • Loading branch information
kinbiko committed Jul 31, 2022
1 parent 9fc0b22 commit 98d3f07
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 40 deletions.
43 changes: 3 additions & 40 deletions builds/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ type JSONBuildRequest struct {
// link errors to the source code (for supported source control tools)
type JSONSourceControl struct {
// If the provider can be inferred from the repository then it is not
// required.
Provider Provider `json:"provider,omitempty"`
// required. Must be one of: "github", "github-enterprise", "bitbucket",
// "bitbucket-server", "gitlab", or "gitlab-onpremise".
Provider string `json:"provider,omitempty"`

// Repository represents the URL of the repository containing the source
// code being deployed.
Expand All @@ -69,41 +70,3 @@ type JSONSourceControl struct {
// built (short or long hash).
Revision string `json:"revision"`
}

// Provider is the name of the source control provider that contains the
// source code for the build.
type Provider string

// ToProvider converts a string into a Provider, if valid, otherwise returns "".
func ToProvider(s string) Provider {
return map[string]Provider{
"github": ProviderGitHub,
"github-enterprise": ProviderGitHubEnterprise,
"bitbucket": ProviderBitbucket,
"bitbucket-server": ProviderBitbucketServer,
"gitlab": ProviderGitLab,
"gitlab-onpremise": ProviderGitLabOnPremise,
}[s]
}

const (
// ProviderGitHub indicates that your source code is stored on GitHub's
// SaaS platform.
ProviderGitHub = "github"
// ProviderGitHubEnterprise indicates that your source code is stored on
// GitHub's enterprise platform.
ProviderGitHubEnterprise = "github-enterprise"
// ProviderBitbucket indicates that your source code is stored on
// Bitbucket's SaaS platform. Git and Mercurial are supported for
// Bitbucket.
ProviderBitbucket = "bitbucket"
// ProviderBitbucketServer indicates that your source code is stored on
// Bitbucket's Server platform. Formerly known as Stash.
ProviderBitbucketServer = "bitbucket-server"
// ProviderGitLab indicates that your source code is stored on
// GitLab's SaaS platform.
ProviderGitLab = "gitlab"
// ProviderGitLabOnPremise indicates that your source code is stored on
// GitLab's GitLab CE or GitLab Enterprise platform.
ProviderGitLabOnPremise = "gitlab-onpremise"
)
37 changes: 37 additions & 0 deletions builds/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package builds

import (
"fmt"
"regexp"
"strings"
)

func (r *JSONBuildRequest) Validate() error {
if re := regexp.MustCompile("^[0-9a-f]{32}$"); !re.MatchString(r.APIKey) {
return fmt.Errorf(`APIKey must be 32 hex characters, but got "%s"`, r.APIKey)
}

if strings.TrimSpace(r.AppVersion) == "" {
return fmt.Errorf(`AppVersion must be present`)
}

if r.SourceControl != nil {
if r.SourceControl.Repository == "" {
return fmt.Errorf(`SourceControl.Repository must be present when SourceControl is set`)
}
if r.SourceControl.Revision == "" {
return fmt.Errorf(`SourceControl.Revision must be present when SourceControl is set`)
}
if r.SourceControl.Provider != "" {
validProviders := []string{"github", "github-enterprise", "bitbucket", "bitbucket-server", "gitlab", "gitlab-onpremise"}
found := false
for _, p := range validProviders {
found = found || p == r.SourceControl.Provider
}
if !found {
return fmt.Errorf(`SourceControl.Provider must be unset (for automatic inference) or one of %v`, validProviders)
}
}
}
return nil
}
95 changes: 95 additions & 0 deletions builds/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package builds_test

import (
"strings"
"testing"

"github.com/kinbiko/bugsnag/builds"
)

func TestValidate(t *testing.T) {
makeValidReq := func() *builds.JSONBuildRequest {
return &builds.JSONBuildRequest{
APIKey: "1234abcd1234abcd1234abcd1234abcd",
AppVersion: "1.5.2",
ReleaseStage: "staging",
BuilderName: "River Tam",
Metadata: map[string]string{
"Tickets": "JIRA-1234, JIRA-4321",
},
SourceControl: &builds.JSONSourceControl{
Provider: "github",
Repository: "https://github.com/kinbiko/bugsnag",
Revision: "9fc0b224985fc09d1ced97e51a0e8f166f1d190a",
},
AppVersionCode: 33,
AppBundleVersion: "42.3",
AutoAssignRelease: true,
}
}

mustContain := func(t *testing.T, err error, strs ...string) {
t.Helper()
if err == nil {
t.Fatalf("expected error but got nil")
}
errMsg := err.Error()
for _, str := range strs {
if !strings.Contains(errMsg, str) {
t.Errorf("expected error message to contain '%s' but was:\n%s", str, errMsg)
}
}
}

t.Run("API key", func(t *testing.T) {
r := makeValidReq()
r.APIKey = ""
mustContain(t, r.Validate(), "APIKey", "32 hex")

r = makeValidReq()
r.APIKey = "1234567890"
mustContain(t, r.Validate(), "APIKey", "32 hex")

r = makeValidReq()
r.APIKey = "123456789012345678901234567890ZZ"
mustContain(t, r.Validate(), "APIKey", "32 hex")
})

t.Run("App version", func(t *testing.T) {
r := makeValidReq()
r.AppVersion = ""
mustContain(t, r.Validate(), "AppVersion", "present")
})

t.Run("Source control", func(t *testing.T) {
r := makeValidReq()
r.SourceControl.Repository = ""
mustContain(t, r.Validate(), "SourceControl.Repository", "present when")

r = makeValidReq()
r.SourceControl.Revision = ""
mustContain(t, r.Validate(), "SourceControl.Revision", "present when")

t.Run("Provider", func(t *testing.T) {
r := makeValidReq()
r.SourceControl.Provider = "sourceforge"
mustContain(t, r.Validate(), "SourceControl.Provider", "unset", "automatic", "one of", "server")
})
})

t.Run("perfectly valid", func(t *testing.T) {
t.Run("all values populated", func(t *testing.T) {
r := makeValidReq()
if err := r.Validate(); err != nil {
t.Errorf("unexpected error: %v", err)
}

})
t.Run("only bare-minimum populated", func(t *testing.T) {
r := &builds.JSONBuildRequest{APIKey: "1234abcd1234abcd1234abcd1234abcd", AppVersion: "1.5.2"}
if err := r.Validate(); err != nil {
t.Errorf("unexpected error: %v", err)
}
})
})
}

0 comments on commit 98d3f07

Please sign in to comment.