diff --git a/errors.go b/errors.go index 226d256e4..be39eea15 100644 --- a/errors.go +++ b/errors.go @@ -22,6 +22,12 @@ var ( ErrUnsupportedPrivateKey = errors.New("private Key can only be present with Azure DevOps Server service provider") + ErrUnsupportedBothTagsRegexAndFileTriggersEnabled = errors.New(`"TagsRegex" cannot be populated when "FileTriggersEnabled" is true`) + + ErrUnsupportedBothTagsRegexAndTriggerPatterns = errors.New(`"TagsRegex" and "TriggerPrefixes" cannot be populated at the same time`) + + ErrUnsupportedBothTagsRegexAndTriggerPrefixes = errors.New(`"TagsRegex" and "TriggerPatterns" cannot be populated at the same time`) + ErrUnsupportedRunTriggerType = errors.New(`"RunTriggerType" must be "inbound" when requesting "include" query params`) ErrUnsupportedBothTriggerPatternsAndPrefixes = errors.New(`"TriggerPatterns" and "TriggerPrefixes" cannot be populated at the same time`) diff --git a/workspace.go b/workspace.go index df627b811..51a45aee8 100644 --- a/workspace.go +++ b/workspace.go @@ -182,6 +182,7 @@ type VCSRepo struct { OAuthTokenID string `jsonapi:"attr,oauth-token-id"` RepositoryHTTPURL string `jsonapi:"attr,repository-http-url"` ServiceProvider string `jsonapi:"attr,service-provider"` + TagsRegex string `jsonapi:"attr,tags-regex"` WebhookURL string `jsonapi:"attr,webhook-url"` } @@ -354,6 +355,7 @@ type VCSRepoOptions struct { Identifier *string `json:"identifier,omitempty"` IngressSubmodules *bool `json:"ingress-submodules,omitempty"` OAuthTokenID *string `json:"oauth-token-id,omitempty"` + TagsRegex *string `json:"tags-regex,omitempty"` } // WorkspaceUpdateOptions represents the options for updating a workspace. @@ -1067,6 +1069,18 @@ func (o WorkspaceCreateOptions) valid() error { o.TriggerPatterns != nil && len(o.TriggerPatterns) > 0 { return ErrUnsupportedBothTriggerPatternsAndPrefixes } + if o.VCSRepo!= nil && o.VCSRepo.TagsRegex != nil && + o.TriggerPatterns != nil && len(o.TriggerPatterns) > 0 { + return ErrUnsupportedBothTagsRegexAndTriggerPatterns + } + if o.VCSRepo!= nil && o.VCSRepo.TagsRegex != nil && + o.TriggerPrefixes != nil && len(o.TriggerPrefixes) > 0 { + return ErrUnsupportedBothTagsRegexAndTriggerPrefixes + } + if o.VCSRepo != nil && o.VCSRepo.TagsRegex != nil && + o.FileTriggersEnabled != nil && *o.FileTriggersEnabled { + return ErrUnsupportedBothTagsRegexAndFileTriggersEnabled + } return nil } @@ -1086,6 +1100,19 @@ func (o WorkspaceUpdateOptions) valid() error { return ErrUnsupportedBothTriggerPatternsAndPrefixes } + if o.VCSRepo != nil && o.VCSRepo.TagsRegex != nil && + o.TriggerPatterns != nil && len(o.TriggerPatterns) > 0 { + return ErrUnsupportedBothTagsRegexAndTriggerPatterns + } + if o.VCSRepo != nil && o.VCSRepo.TagsRegex != nil && + o.TriggerPrefixes != nil && len(o.TriggerPrefixes) > 0 { + return ErrUnsupportedBothTagsRegexAndTriggerPrefixes + } + if o.VCSRepo != nil && o.VCSRepo.TagsRegex != nil && + o.FileTriggersEnabled != nil && *o.FileTriggersEnabled { + return ErrUnsupportedBothTagsRegexAndFileTriggersEnabled + } + return nil } diff --git a/workspace_integration_test.go b/workspace_integration_test.go index 04f0ee553..f5189d140 100644 --- a/workspace_integration_test.go +++ b/workspace_integration_test.go @@ -322,6 +322,74 @@ func TestWorkspacesCreate(t *testing.T) { assert.EqualError(t, err, ErrUnsupportedBothTriggerPatternsAndPrefixes.Error()) }) + t.Run("when options include tags-regex(behind a feature flag)", func(t *testing.T) { + // Remove the below organization creation and use the one from the outer scope once the feature flag is removed + orgTest, orgTestCleanup := createOrganizationWithOptions(t, client, OrganizationCreateOptions{ + Name: String("tst-" + randomString(t)[0:20] + "-ff-on"), + Email: String(fmt.Sprintf("%s@tfe.local", randomString(t))), + }) + defer orgTestCleanup() + + options := WorkspaceCreateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(false), + VCSRepo: &VCSRepoOptions{TagsRegex: String("barfoo")}, + } + w, err := client.Workspaces.Create(ctx, orgTest.Name, options) + + require.NoError(t, err) + assert.Equal(t, options.VCSRepo.TagsRegex, w.VCSRepo.TagsRegex) + + // Get a refreshed view from the API. + refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) + require.NoError(t, err) + + for _, item := range []*Workspace{ + w, + refreshed, + } { + assert.Equal(t, options.VCSRepo.TagsRegex, item.VCSRepo.TagsRegex) + } + }) + + t.Run("when options include both non-empty tags-regex and trigger-patterns error is returned", func(t *testing.T) { + options := WorkspaceCreateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(false), + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + TriggerPatterns: []string{"/module-1/**/*", "/**/networking/*"}, + } + w, err := client.Workspaces.Create(ctx, orgTest.Name, options) + + assert.Nil(t, w) + assert.EqualError(t, err, ErrUnsupportedBothTagsRegexAndTriggerPatterns.Error()) + }) + + t.Run("when options include both non-empty tags-regex and trigger-prefixes error is returned", func(t *testing.T) { + options := WorkspaceCreateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(false), + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + TriggerPrefixes: []string{"/module-1", "/module-2"}, + } + w, err := client.Workspaces.Create(ctx, orgTest.Name, options) + + assert.Nil(t, w) + assert.EqualError(t, err, ErrUnsupportedBothTagsRegexAndTriggerPrefixes.Error()) + }) + + t.Run("when options include both non-empty tags-regex and file-triggers-enabled as true an error is returned", func(t *testing.T) { + options := WorkspaceCreateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(true), + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + } + w, err := client.Workspaces.Create(ctx, orgTest.Name, options) + + assert.Nil(t, w) + assert.EqualError(t, err, ErrUnsupportedBothTagsRegexAndFileTriggersEnabled.Error()) + }) + t.Run("when options include trigger-patterns populated and empty trigger-paths workspace is created", func(t *testing.T) { // Remove the below organization creation and use the one from the outer scope once the feature flag is removed orgTest, orgTestCleanup := createOrganizationWithOptions(t, client, OrganizationCreateOptions{ @@ -727,6 +795,79 @@ func TestWorkspacesUpdate(t *testing.T) { assert.Equal(t, options.TriggerPatterns, item.TriggerPatterns) } }) + + t.Run("when options include VCSRepo tags-regex (behind a feature flag)", func(t *testing.T) { + // Remove the below organization and workspace creation and use the one from the outer scope once the feature flag is removed + orgTest, orgTestCleanup := createOrganizationWithOptions(t, client, OrganizationCreateOptions{ + Name: String("tst-" + randomString(t)[0:20] + "-ff-on"), + Email: String(fmt.Sprintf("%s@tfe.local", randomString(t))), + }) + defer orgTestCleanup() + + wTest, _ := createWorkspaceWithOptions(t, client, orgTest, WorkspaceCreateOptions{ + Name: String(randomString(t)), + FileTriggersEnabled: Bool(false), + VCSRepo: &VCSRepoOptions{TagsRegex: String("barfoo")}, + }) + assert.Equal(t, wTest.VCSRepo.TriggerPrefixes, String("barfoo")) // Sanity test + + options := WorkspaceUpdateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(false), + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + } + w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) + require.NoError(t, err) + + // Get a refreshed view from the API. + refreshed, err := client.Workspaces.Read(ctx, orgTest.Name, *options.Name) + require.NoError(t, err) + + for _, item := range []*Workspace{ + w, + refreshed, + } { + assert.Equal(t, options.VCSRepo.TagsRegex, item.VCSRepo.TagsRegex) + } + }) + + t.Run("when options include tags-regex and file-triggers-enabled is true an error is returned", func(t *testing.T) { + options := WorkspaceUpdateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(true), + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + } + w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) + + assert.Nil(t, w) + assert.EqualError(t, err, ErrUnsupportedBothTagsRegexAndFileTriggersEnabled.Error()) + }) + + t.Run("when options include both tags-regex and trigger-prefixes an error is returned", func(t *testing.T) { + options := WorkspaceUpdateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(false), + TriggerPrefixes: []string{"/module-1", "/module-2"}, + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + } + w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) + + assert.Nil(t, w) + assert.EqualError(t, err, ErrUnsupportedBothTagsRegexAndTriggerPrefixes.Error()) + }) + + t.Run("when options include both tags-regex and trigger-patterns error is returned", func(t *testing.T) { + options := WorkspaceUpdateOptions{ + Name: String("foobar"), + FileTriggersEnabled: Bool(false), + TriggerPatterns: []string{"/module-1/**/*", "/**/networking/*"}, + VCSRepo: &VCSRepoOptions{TagsRegex: String("foobar")}, + } + w, err := client.Workspaces.Update(ctx, orgTest.Name, wTest.Name, options) + + assert.Nil(t, w) + assert.EqualError(t, err, ErrUnsupportedBothTagsRegexAndTriggerPrefixes.Error()) + }) } func TestWorkspacesUpdateByID(t *testing.T) {