From 58d57941293c52f67d69329fd1c65b03d669a6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 12 Sep 2024 13:24:30 +0200 Subject: [PATCH 01/65] initial secret_def file for sdk --- pkg/sdk/poc/main.go | 1 + pkg/sdk/secrets_def.go | 71 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 pkg/sdk/secrets_def.go diff --git a/pkg/sdk/poc/main.go b/pkg/sdk/poc/main.go index 1d98ea99de..72c675a73f 100644 --- a/pkg/sdk/poc/main.go +++ b/pkg/sdk/poc/main.go @@ -45,6 +45,7 @@ var definitionMapping = map[string]*generator.Interface{ "data_metric_function_references_def.go": sdk.DataMetricFunctionReferenceDef, "external_volumes_def.go": sdk.ExternalVolumesDef, "authentication_policies_def.go": sdk.AuthenticationPoliciesDef, + "secrets_def.go": sdk.SecretsDef, } func main() { diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go new file mode 100644 index 0000000000..99e9b8a751 --- /dev/null +++ b/pkg/sdk/secrets_def.go @@ -0,0 +1,71 @@ +package sdk + +import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" + +//go:generate go run ./poc/main.go + +var secretsSecurityIntegrationScopeDef = g.NewQueryStruct("SecurityIntegrationScope").Text("Scope", g.KeywordOptions().SingleQuotes().Required()) + +var SecretsDef = g.NewInterface( + "Secrets", + "Secret", + g.KindOfT[SchemaObjectIdentifier](), +).CustomOperation( + "CreateWithOAuthClientCredentialsFlow", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateOAuthWithClientCredentialsFlow"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION").Required()). + ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses()). + OptionalComment(). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), + secretsSecurityIntegrationScopeDef, +).CustomOperation( + "CreateWithOAuthAuthorizationCode", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithOAuthAuthorizationCode"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + TextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). + TextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). + Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). + OptionalComment(). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), + secretsSecurityIntegrationScopeDef, +).CustomOperation( + "CreateWithBasicAuthentication", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithBasicAuthentication"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = PASSWORD")). + TextAssignment("USERNAME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). + TextAssignment("PASSWORD", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). + OptionalComment(). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), +).CustomOperation( + "CreateWithGenericString", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithGenericString"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = GENERIC_STRING")). + TextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes().Required()). + OptionalComment(). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), +) From f1488ca47330d5fac6fb0a81b2c303e1231219ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 12 Sep 2024 15:05:13 +0200 Subject: [PATCH 02/65] naming changes to secret create options --- pkg/sdk/secrets_def.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 99e9b8a751..7c50aa41c7 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -13,7 +13,7 @@ var SecretsDef = g.NewInterface( ).CustomOperation( "CreateWithOAuthClientCredentialsFlow", "https://docs.snowflake.com/en/sql-reference/sql/create-secret", - g.NewQueryStruct("CreateOAuthWithClientCredentialsFlow"). + g.NewQueryStruct("CreateWithOAuthClientCredentialsFlow"). Create(). OrReplace(). SQL("SECRET"). @@ -23,12 +23,13 @@ var SecretsDef = g.NewInterface( Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION").Required()). ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses()). OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), secretsSecurityIntegrationScopeDef, ).CustomOperation( - "CreateWithOAuthAuthorizationCode", + "CreateWithOAuthAuthorizationCodeFlow", "https://docs.snowflake.com/en/sql-reference/sql/create-secret", - g.NewQueryStruct("CreateWithOAuthAuthorizationCode"). + g.NewQueryStruct("CreateWithOAuthAuthorizationCodeFlow"). Create(). OrReplace(). SQL("SECRET"). @@ -39,6 +40,7 @@ var SecretsDef = g.NewInterface( TextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), secretsSecurityIntegrationScopeDef, ).CustomOperation( @@ -54,6 +56,7 @@ var SecretsDef = g.NewInterface( TextAssignment("USERNAME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). TextAssignment("PASSWORD", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), ).CustomOperation( "CreateWithGenericString", @@ -67,5 +70,6 @@ var SecretsDef = g.NewInterface( PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = GENERIC_STRING")). TextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes().Required()). OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), ) From 752f92446de873d97b4d55fe8125e9403ef1e478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 13 Sep 2024 09:54:15 +0200 Subject: [PATCH 03/65] create secrets and alter with two basic tests --- pkg/sdk/secrets_def.go | 39 ++++- pkg/sdk/secrets_dto_builders_gen.go | 196 +++++++++++++++++++++ pkg/sdk/secrets_dto_gen.go | 72 ++++++++ pkg/sdk/secrets_gen.go | 91 ++++++++++ pkg/sdk/secrets_gen_integration_test.go | 27 +++ pkg/sdk/secrets_gen_test.go | 215 ++++++++++++++++++++++++ pkg/sdk/secrets_impl_gen.go | 120 +++++++++++++ pkg/sdk/secrets_validations_gen.go | 76 +++++++++ 8 files changed, 835 insertions(+), 1 deletion(-) create mode 100644 pkg/sdk/secrets_dto_builders_gen.go create mode 100644 pkg/sdk/secrets_dto_gen.go create mode 100644 pkg/sdk/secrets_gen.go create mode 100644 pkg/sdk/secrets_gen_integration_test.go create mode 100644 pkg/sdk/secrets_gen_test.go create mode 100644 pkg/sdk/secrets_impl_gen.go create mode 100644 pkg/sdk/secrets_validations_gen.go diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 7c50aa41c7..e0bdea0f34 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -4,7 +4,25 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen //go:generate go run ./poc/main.go -var secretsSecurityIntegrationScopeDef = g.NewQueryStruct("SecurityIntegrationScope").Text("Scope", g.KeywordOptions().SingleQuotes().Required()) +var secretsSecurityIntegrationScopeDef = g.NewQueryStruct("SecurityIntegrationScope"). + Text("Scope", g.KeywordOptions().SingleQuotes().Required()) + +var secretsOAuthScopes = g.NewQueryStruct("OAuthScopes"). + List("OAuthScopes", "SecurityIntegrationScope", g.ListOptions().MustParentheses()) + +var secretSet = g.NewQueryStruct("SecretSet"). + OptionalComment(). + OptionalQueryStructField("OAuthScopes", secretsOAuthScopes, g.ParameterOptions().MustParentheses().SQL("OAUTH_SCOPES")). + OptionalTextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("USERNAME", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("PASSWORD", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes()) + +// unset doest work, need to use "set comment = null" +// OptionalSQL("SET COMMENT = NULL") +var secretUnset = g.NewQueryStruct("SecretUnset"). + OptionalSQL("UNSET COMMENT") var SecretsDef = g.NewInterface( "Secrets", @@ -22,6 +40,7 @@ var SecretsDef = g.NewInterface( PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION").Required()). ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses()). + //QueryStructField("OAuthScopes", secretsOAuthScopes, g.ParameterOptions().MustParentheses()). OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), @@ -72,4 +91,22 @@ var SecretsDef = g.NewInterface( OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), +).AlterOperation( + "https://docs.snowflake.com/en/sql-reference/sql/alter-secret", + g.NewQueryStruct("AlterSecret"). + Alter(). + SQL("SECRET"). + IfExists(). + Name(). + OptionalQueryStructField( + "Set", + secretSet, + g.KeywordOptions().SQL("SET"), + ). + OptionalQueryStructField( + "Unset", + secretUnset, + g.KeywordOptions().SQL("UNSET"), + ). + WithValidation(g.ExactlyOneValueSet, "Set", "Unset"), ) diff --git a/pkg/sdk/secrets_dto_builders_gen.go b/pkg/sdk/secrets_dto_builders_gen.go new file mode 100644 index 0000000000..dc1f083aa3 --- /dev/null +++ b/pkg/sdk/secrets_dto_builders_gen.go @@ -0,0 +1,196 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewCreateWithOAuthClientCredentialsFlowSecretRequest( + name SchemaObjectIdentifier, + SecurityIntegration AccountObjectIdentifier, +) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s := CreateWithOAuthClientCredentialsFlowSecretRequest{} + s.name = name + s.SecurityIntegration = SecurityIntegration + return &s +} + +func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithOrReplace(OrReplace bool) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s.OrReplace = &OrReplace + return s +} + +func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithIfNotExists(IfNotExists bool) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s.IfNotExists = &IfNotExists + return s +} + +func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithOauthScopes(OauthScopes []SecurityIntegrationScope) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s.OauthScopes = OauthScopes + return s +} + +func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithComment(Comment string) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s.Comment = &Comment + return s +} + +func NewCreateWithOAuthAuthorizationCodeFlowSecretRequest( + name SchemaObjectIdentifier, + OauthRefreshToken string, + OauthRefreshTokenExpiryTime string, + SecurityIntegration AccountObjectIdentifier, +) *CreateWithOAuthAuthorizationCodeFlowSecretRequest { + s := CreateWithOAuthAuthorizationCodeFlowSecretRequest{} + s.name = name + s.OauthRefreshToken = OauthRefreshToken + s.OauthRefreshTokenExpiryTime = OauthRefreshTokenExpiryTime + s.SecurityIntegration = SecurityIntegration + return &s +} + +func (s *CreateWithOAuthAuthorizationCodeFlowSecretRequest) WithOrReplace(OrReplace bool) *CreateWithOAuthAuthorizationCodeFlowSecretRequest { + s.OrReplace = &OrReplace + return s +} + +func (s *CreateWithOAuthAuthorizationCodeFlowSecretRequest) WithIfNotExists(IfNotExists bool) *CreateWithOAuthAuthorizationCodeFlowSecretRequest { + s.IfNotExists = &IfNotExists + return s +} + +func (s *CreateWithOAuthAuthorizationCodeFlowSecretRequest) WithComment(Comment string) *CreateWithOAuthAuthorizationCodeFlowSecretRequest { + s.Comment = &Comment + return s +} + +func NewCreateWithBasicAuthenticationSecretRequest( + name SchemaObjectIdentifier, + Username string, + Password string, +) *CreateWithBasicAuthenticationSecretRequest { + s := CreateWithBasicAuthenticationSecretRequest{} + s.name = name + s.Username = Username + s.Password = Password + return &s +} + +func (s *CreateWithBasicAuthenticationSecretRequest) WithOrReplace(OrReplace bool) *CreateWithBasicAuthenticationSecretRequest { + s.OrReplace = &OrReplace + return s +} + +func (s *CreateWithBasicAuthenticationSecretRequest) WithIfNotExists(IfNotExists bool) *CreateWithBasicAuthenticationSecretRequest { + s.IfNotExists = &IfNotExists + return s +} + +func (s *CreateWithBasicAuthenticationSecretRequest) WithComment(Comment string) *CreateWithBasicAuthenticationSecretRequest { + s.Comment = &Comment + return s +} + +func NewCreateWithGenericStringSecretRequest( + name SchemaObjectIdentifier, + SecretString string, +) *CreateWithGenericStringSecretRequest { + s := CreateWithGenericStringSecretRequest{} + s.name = name + s.SecretString = SecretString + return &s +} + +func (s *CreateWithGenericStringSecretRequest) WithOrReplace(OrReplace bool) *CreateWithGenericStringSecretRequest { + s.OrReplace = &OrReplace + return s +} + +func (s *CreateWithGenericStringSecretRequest) WithIfNotExists(IfNotExists bool) *CreateWithGenericStringSecretRequest { + s.IfNotExists = &IfNotExists + return s +} + +func (s *CreateWithGenericStringSecretRequest) WithComment(Comment string) *CreateWithGenericStringSecretRequest { + s.Comment = &Comment + return s +} + +func NewAlterSecretRequest( + name SchemaObjectIdentifier, +) *AlterSecretRequest { + s := AlterSecretRequest{} + s.name = name + return &s +} + +func (s *AlterSecretRequest) WithIfExists(IfExists bool) *AlterSecretRequest { + s.IfExists = &IfExists + return s +} + +func (s *AlterSecretRequest) WithSet(Set SecretSetRequest) *AlterSecretRequest { + s.Set = &Set + return s +} + +func (s *AlterSecretRequest) WithUnset(Unset SecretUnsetRequest) *AlterSecretRequest { + s.Unset = &Unset + return s +} + +func NewSecretSetRequest() *SecretSetRequest { + return &SecretSetRequest{} +} + +func (s *SecretSetRequest) WithComment(Comment string) *SecretSetRequest { + s.Comment = &Comment + return s +} + +func (s *SecretSetRequest) WithOAuthScopes(OAuthScopes OAuthScopesRequest) *SecretSetRequest { + s.OAuthScopes = &OAuthScopes + return s +} + +func (s *SecretSetRequest) WithOauthRefreshToken(OauthRefreshToken string) *SecretSetRequest { + s.OauthRefreshToken = &OauthRefreshToken + return s +} + +func (s *SecretSetRequest) WithOauthRefreshTokenExpiryTime(OauthRefreshTokenExpiryTime string) *SecretSetRequest { + s.OauthRefreshTokenExpiryTime = &OauthRefreshTokenExpiryTime + return s +} + +func (s *SecretSetRequest) WithUsername(Username string) *SecretSetRequest { + s.Username = &Username + return s +} + +func (s *SecretSetRequest) WithPassword(Password string) *SecretSetRequest { + s.Password = &Password + return s +} + +func (s *SecretSetRequest) WithSecretString(SecretString string) *SecretSetRequest { + s.SecretString = &SecretString + return s +} + +func NewOAuthScopesRequest() *OAuthScopesRequest { + return &OAuthScopesRequest{} +} + +func (s *OAuthScopesRequest) WithOAuthScopes(OAuthScopes []SecurityIntegrationScope) *OAuthScopesRequest { + s.OAuthScopes = OAuthScopes + return s +} + +func NewSecretUnsetRequest() *SecretUnsetRequest { + return &SecretUnsetRequest{} +} + +func (s *SecretUnsetRequest) WithUnsetComment(UnsetComment bool) *SecretUnsetRequest { + s.UnsetComment = &UnsetComment + return s +} diff --git a/pkg/sdk/secrets_dto_gen.go b/pkg/sdk/secrets_dto_gen.go new file mode 100644 index 0000000000..5f37d8caf0 --- /dev/null +++ b/pkg/sdk/secrets_dto_gen.go @@ -0,0 +1,72 @@ +package sdk + +//go:generate go run ./dto-builder-generator/main.go + +var ( + _ optionsProvider[CreateWithOAuthClientCredentialsFlowSecretOptions] = new(CreateWithOAuthClientCredentialsFlowSecretRequest) + _ optionsProvider[CreateWithOAuthAuthorizationCodeFlowSecretOptions] = new(CreateWithOAuthAuthorizationCodeFlowSecretRequest) + _ optionsProvider[CreateWithBasicAuthenticationSecretOptions] = new(CreateWithBasicAuthenticationSecretRequest) + _ optionsProvider[CreateWithGenericStringSecretOptions] = new(CreateWithGenericStringSecretRequest) + _ optionsProvider[AlterSecretOptions] = new(AlterSecretRequest) +) + +type CreateWithOAuthClientCredentialsFlowSecretRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + SecurityIntegration AccountObjectIdentifier // required + OauthScopes []SecurityIntegrationScope + Comment *string +} + +type CreateWithOAuthAuthorizationCodeFlowSecretRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + OauthRefreshToken string // required + OauthRefreshTokenExpiryTime string // required + SecurityIntegration AccountObjectIdentifier // required + Comment *string +} + +type CreateWithBasicAuthenticationSecretRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + Username string // required + Password string // required + Comment *string +} + +type CreateWithGenericStringSecretRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + SecretString string // required + Comment *string +} + +type AlterSecretRequest struct { + IfExists *bool + name SchemaObjectIdentifier // required + Set *SecretSetRequest + Unset *SecretUnsetRequest +} + +type SecretSetRequest struct { + Comment *string + OAuthScopes *OAuthScopesRequest + OauthRefreshToken *string + OauthRefreshTokenExpiryTime *string + Username *string + Password *string + SecretString *string +} + +type OAuthScopesRequest struct { + OAuthScopes []SecurityIntegrationScope +} + +type SecretUnsetRequest struct { + UnsetComment *bool +} diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go new file mode 100644 index 0000000000..9f5ac8638b --- /dev/null +++ b/pkg/sdk/secrets_gen.go @@ -0,0 +1,91 @@ +package sdk + +import "context" + +type Secrets interface { + CreateWithOAuthClientCredentialsFlow(ctx context.Context, request *CreateWithOAuthClientCredentialsFlowSecretRequest) error + CreateWithOAuthAuthorizationCodeFlow(ctx context.Context, request *CreateWithOAuthAuthorizationCodeFlowSecretRequest) error + CreateWithBasicAuthentication(ctx context.Context, request *CreateWithBasicAuthenticationSecretRequest) error + CreateWithGenericString(ctx context.Context, request *CreateWithGenericStringSecretRequest) error + Alter(ctx context.Context, request *AlterSecretRequest) error +} + +// CreateWithOAuthClientCredentialsFlowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. +type CreateWithOAuthClientCredentialsFlowSecretOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + secret bool `ddl:"static" sql:"SECRET"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Type string `ddl:"static" sql:"TYPE = OAUTH2"` + SecurityIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` + OauthScopes []SecurityIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} +type SecurityIntegrationScope struct { + Scope string `ddl:"keyword,single_quotes"` +} + +// CreateWithOAuthAuthorizationCodeFlowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. +type CreateWithOAuthAuthorizationCodeFlowSecretOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + secret bool `ddl:"static" sql:"SECRET"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Type string `ddl:"static" sql:"TYPE = OAUTH2"` + OauthRefreshToken string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` + OauthRefreshTokenExpiryTime string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN_EXPIRY_TIME"` + SecurityIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +// CreateWithBasicAuthenticationSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. +type CreateWithBasicAuthenticationSecretOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + secret bool `ddl:"static" sql:"SECRET"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Type string `ddl:"static" sql:"TYPE = PASSWORD"` + Username string `ddl:"parameter,single_quotes" sql:"USERNAME"` + Password string `ddl:"parameter,single_quotes" sql:"PASSWORD"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +// CreateWithGenericStringSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. +type CreateWithGenericStringSecretOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + secret bool `ddl:"static" sql:"SECRET"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Type string `ddl:"static" sql:"TYPE = GENERIC_STRING"` + SecretString string `ddl:"parameter,single_quotes" sql:"SECRET_STRING"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +// AlterSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-secret. +type AlterSecretOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + secret bool `ddl:"static" sql:"SECRET"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Set *SecretSet `ddl:"keyword" sql:"SET"` + Unset *SecretUnset `ddl:"keyword" sql:"UNSET"` +} +type SecretSet struct { + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + OAuthScopes *OAuthScopes `ddl:"parameter,must_parentheses" sql:"OAUTH_SCOPES"` + OauthRefreshToken *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` + OauthRefreshTokenExpiryTime *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN_EXPIRY_TIME"` + Username *string `ddl:"parameter,single_quotes" sql:"USERNAME"` + Password *string `ddl:"parameter,single_quotes" sql:"PASSWORD"` + SecretString *string `ddl:"parameter,single_quotes" sql:"SECRET_STRING"` +} +type OAuthScopes struct { + OAuthScopes []SecurityIntegrationScope `ddl:"list,must_parentheses"` +} +type SecretUnset struct { + UnsetComment *bool `ddl:"keyword" sql:"UNSET COMMENT"` +} diff --git a/pkg/sdk/secrets_gen_integration_test.go b/pkg/sdk/secrets_gen_integration_test.go new file mode 100644 index 0000000000..ee9af4239a --- /dev/null +++ b/pkg/sdk/secrets_gen_integration_test.go @@ -0,0 +1,27 @@ +package sdk + +import "testing" + +func TestInt_Secrets(t *testing.T) { + // TODO: prepare common resources + + t.Run("CreateWithOAuthClientCredentialsFlow", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithBasicAuthentication", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithGenericString", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Alter", func(t *testing.T) { + // TODO: fill me + }) +} diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go new file mode 100644 index 0000000000..426e5eee09 --- /dev/null +++ b/pkg/sdk/secrets_gen_test.go @@ -0,0 +1,215 @@ +package sdk + +import "testing" + +func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { + + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithOAuthClientCredentialsFlowSecretOptions + defaultOpts := func() *CreateWithOAuthClientCredentialsFlowSecretOptions { + return &CreateWithOAuthClientCredentialsFlowSecretOptions{ + + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateWithOAuthClientCredentialsFlowSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthClientCredentialsFlowSecretOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("basic", func(t *testing.T) { + securityIntegration := NewAccountObjectIdentifier("security_integration") + oauthScopes := []SecurityIntegrationScope{{Scope: "sample_scope"}} + + opts := defaultOpts() + opts.SecurityIntegration = securityIntegration + opts.OauthScopes = oauthScopes + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName(), securityIntegration.FullyQualifiedName()) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} + +func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { + + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithOAuthAuthorizationCodeFlowSecretOptions + defaultOpts := func() *CreateWithOAuthAuthorizationCodeFlowSecretOptions { + return &CreateWithOAuthAuthorizationCodeFlowSecretOptions{ + + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateWithOAuthAuthorizationCodeFlowSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthAuthorizationCodeFlowSecretOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} + +func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { + + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithBasicAuthenticationSecretOptions + defaultOpts := func() *CreateWithBasicAuthenticationSecretOptions { + return &CreateWithBasicAuthenticationSecretOptions{ + + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateWithBasicAuthenticationSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithBasicAuthenticationSecretOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} + +func TestSecrets_CreateWithGenericString(t *testing.T) { + + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithGenericStringSecretOptions + defaultOpts := func() *CreateWithGenericStringSecretOptions { + return &CreateWithGenericStringSecretOptions{ + + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateWithGenericStringSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithGenericStringSecretOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} + +func TestSecrets_Alter(t *testing.T) { + + id := randomSchemaObjectIdentifier() + // Minimal valid AlterSecretOptions + defaultOpts := func() *AlterSecretOptions { + return &AlterSecretOptions{ + + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *AlterSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: exactly one field from [opts.Set opts.Unset] should be present", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) + }) + + t.Run("alter: set options", func(t *testing.T) { + oauthScopes := &OAuthScopes{[]SecurityIntegrationScope{{Scope: "different_scope_name"}}} + + opts := defaultOpts() + opts.Set = &SecretSet{ + OAuthScopes: oauthScopes, + OauthRefreshToken: String("refresh_token"), + OauthRefreshTokenExpiryTime: String("2024-10-10"), + } + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET %s SET OAUTH_SCOPES = ('different_scope_name')", id.FullyQualifiedName()) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go new file mode 100644 index 0000000000..bdce5ad21a --- /dev/null +++ b/pkg/sdk/secrets_impl_gen.go @@ -0,0 +1,120 @@ +package sdk + +import ( + "context" +) + +var _ Secrets = (*secrets)(nil) + +type secrets struct { + client *Client +} + +func (v *secrets) CreateWithOAuthClientCredentialsFlow(ctx context.Context, request *CreateWithOAuthClientCredentialsFlowSecretRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *secrets) CreateWithOAuthAuthorizationCodeFlow(ctx context.Context, request *CreateWithOAuthAuthorizationCodeFlowSecretRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *secrets) CreateWithBasicAuthentication(ctx context.Context, request *CreateWithBasicAuthenticationSecretRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *secrets) CreateWithGenericString(ctx context.Context, request *CreateWithGenericStringSecretRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *secrets) Alter(ctx context.Context, request *AlterSecretRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (r *CreateWithOAuthClientCredentialsFlowSecretRequest) toOpts() *CreateWithOAuthClientCredentialsFlowSecretOptions { + opts := &CreateWithOAuthClientCredentialsFlowSecretOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + SecurityIntegration: r.SecurityIntegration, + OauthScopes: r.OauthScopes, + Comment: r.Comment, + } + return opts +} + +func (r *CreateWithOAuthAuthorizationCodeFlowSecretRequest) toOpts() *CreateWithOAuthAuthorizationCodeFlowSecretOptions { + opts := &CreateWithOAuthAuthorizationCodeFlowSecretOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + OauthRefreshToken: r.OauthRefreshToken, + OauthRefreshTokenExpiryTime: r.OauthRefreshTokenExpiryTime, + SecurityIntegration: r.SecurityIntegration, + Comment: r.Comment, + } + return opts +} + +func (r *CreateWithBasicAuthenticationSecretRequest) toOpts() *CreateWithBasicAuthenticationSecretOptions { + opts := &CreateWithBasicAuthenticationSecretOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + Username: r.Username, + Password: r.Password, + Comment: r.Comment, + } + return opts +} + +func (r *CreateWithGenericStringSecretRequest) toOpts() *CreateWithGenericStringSecretOptions { + opts := &CreateWithGenericStringSecretOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + SecretString: r.SecretString, + Comment: r.Comment, + } + return opts +} + +func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { + opts := &AlterSecretOptions{ + IfExists: r.IfExists, + name: r.name, + } + + if r.Set != nil { + opts.Set = &SecretSet{ + Comment: r.Set.Comment, + + OauthRefreshToken: r.Set.OauthRefreshToken, + OauthRefreshTokenExpiryTime: r.Set.OauthRefreshTokenExpiryTime, + Username: r.Set.Username, + Password: r.Set.Password, + SecretString: r.Set.SecretString, + } + + if r.Set.OAuthScopes != nil { + opts.Set.OAuthScopes = &OAuthScopes{ + OAuthScopes: r.Set.OAuthScopes.OAuthScopes, + } + + } + + } + + if r.Unset != nil { + opts.Unset = &SecretUnset{ + UnsetComment: r.Unset.UnsetComment, + } + + } + + return opts +} diff --git a/pkg/sdk/secrets_validations_gen.go b/pkg/sdk/secrets_validations_gen.go new file mode 100644 index 0000000000..513145bf1c --- /dev/null +++ b/pkg/sdk/secrets_validations_gen.go @@ -0,0 +1,76 @@ +package sdk + +var ( + _ validatable = new(CreateWithOAuthClientCredentialsFlowSecretOptions) + _ validatable = new(CreateWithOAuthAuthorizationCodeFlowSecretOptions) + _ validatable = new(CreateWithBasicAuthenticationSecretOptions) + _ validatable = new(CreateWithGenericStringSecretOptions) + _ validatable = new(AlterSecretOptions) +) + +func (opts *CreateWithOAuthClientCredentialsFlowSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateWithOAuthClientCredentialsFlowSecretOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *CreateWithOAuthAuthorizationCodeFlowSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateWithOAuthAuthorizationCodeFlowSecretOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *CreateWithBasicAuthenticationSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateWithBasicAuthenticationSecretOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *CreateWithGenericStringSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateWithGenericStringSecretOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *AlterSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !exactlyOneValueSet(opts.Set, opts.Unset) { + errs = append(errs, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) + } + return JoinErrors(errs...) +} From d7ae12f4325a2e3f02e77a971d8d8b013760b65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 13 Sep 2024 14:54:15 +0200 Subject: [PATCH 04/65] unit tests for alter --- pkg/sdk/secrets_def.go | 59 +++++++++++++++----- pkg/sdk/secrets_dto_builders_gen.go | 67 +++++++++++++++-------- pkg/sdk/secrets_dto_gen.go | 34 ++++++++---- pkg/sdk/secrets_gen.go | 31 +++++++---- pkg/sdk/secrets_gen_test.go | 85 ++++++++++++++++++++--------- pkg/sdk/secrets_impl_gen.go | 41 +++++++++++--- pkg/sdk/secrets_validations_gen.go | 5 ++ 7 files changed, 229 insertions(+), 93 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index e0bdea0f34..204db83165 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -7,22 +7,49 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen var secretsSecurityIntegrationScopeDef = g.NewQueryStruct("SecurityIntegrationScope"). Text("Scope", g.KeywordOptions().SingleQuotes().Required()) -var secretsOAuthScopes = g.NewQueryStruct("OAuthScopes"). +/* +var secretsIntegrationScopes = g.NewQueryStruct("OAuthScopes"). List("OAuthScopes", "SecurityIntegrationScope", g.ListOptions().MustParentheses()) +*/ var secretSet = g.NewQueryStruct("SecretSet"). OptionalComment(). - OptionalQueryStructField("OAuthScopes", secretsOAuthScopes, g.ParameterOptions().MustParentheses().SQL("OAUTH_SCOPES")). - OptionalTextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("USERNAME", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("PASSWORD", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes()) + OptionalQueryStructField( + "SetForOAuthClientCredentialsFlow", + g.NewQueryStruct("SetForOAuthClientCredentialsFlow"). + //OptionalQueryStructField("OAuthScopes", secretsIntegrationScopes, g.ParameterOptions().MustParentheses().SQL("OAUTH_SCOPES")), + ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses().Required()), + g.KeywordOptions(), + ). + OptionalQueryStructField( + "SetForOAuthAuthorizationFlow", + g.NewQueryStruct("SetForOAuthAuthorizationFlow"). + //optional or just TextAssignment()?? + OptionalTextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().SingleQuotes()), + g.KeywordOptions(), + ). + OptionalQueryStructField( + "SetForBasicAuthentication", + g.NewQueryStruct("SetForBasicAuthentication"). + OptionalTextAssignment("USERNAME", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("PASSWORD", g.ParameterOptions().SingleQuotes()), + g.KeywordOptions(), + ). + OptionalQueryStructField( + "SetForGenericString", + g.NewQueryStruct("SetForGenericString"). + OptionalTextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes()), + g.KeywordOptions(), + ). + WithValidation(g.ExactlyOneValueSet, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") -// unset doest work, need to use "set comment = null" -// OptionalSQL("SET COMMENT = NULL") +// TODO: unset doest work, need to use "SET COMMENT = NULL" var secretUnset = g.NewQueryStruct("SecretUnset"). - OptionalSQL("UNSET COMMENT") + //OptionalSQL("UNSET COMMENT") + PredefinedQueryStructField("Comment", "*bool", g.KeywordOptions().SQL("SET COMMENT = NULL")) + +//OptionalSQL("SET COMMENT = NULL") var SecretsDef = g.NewInterface( "Secrets", @@ -39,8 +66,14 @@ var SecretsDef = g.NewInterface( Name(). PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION").Required()). - ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses()). - //QueryStructField("OAuthScopes", secretsOAuthScopes, g.ParameterOptions().MustParentheses()). + ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses().Required()). + /* + ListQueryStructField( + "OAuthScopes", + secretsSecurityIntegrationScopeDef, + g.KeywordOptions().Parentheses().SQL("OAUTH_SCOPES"), + ). + */ OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), @@ -106,7 +139,7 @@ var SecretsDef = g.NewInterface( OptionalQueryStructField( "Unset", secretUnset, - g.KeywordOptions().SQL("UNSET"), + g.KeywordOptions(), ). WithValidation(g.ExactlyOneValueSet, "Set", "Unset"), ) diff --git a/pkg/sdk/secrets_dto_builders_gen.go b/pkg/sdk/secrets_dto_builders_gen.go index dc1f083aa3..a8bacc259b 100644 --- a/pkg/sdk/secrets_dto_builders_gen.go +++ b/pkg/sdk/secrets_dto_builders_gen.go @@ -7,10 +7,12 @@ import () func NewCreateWithOAuthClientCredentialsFlowSecretRequest( name SchemaObjectIdentifier, SecurityIntegration AccountObjectIdentifier, + OauthScopes []SecurityIntegrationScope, ) *CreateWithOAuthClientCredentialsFlowSecretRequest { s := CreateWithOAuthClientCredentialsFlowSecretRequest{} s.name = name s.SecurityIntegration = SecurityIntegration + s.OauthScopes = OauthScopes return &s } @@ -24,11 +26,6 @@ func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithIfNotExists(IfNo return s } -func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithOauthScopes(OauthScopes []SecurityIntegrationScope) *CreateWithOAuthClientCredentialsFlowSecretRequest { - s.OauthScopes = OauthScopes - return s -} - func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithComment(Comment string) *CreateWithOAuthClientCredentialsFlowSecretRequest { s.Comment = &Comment return s @@ -147,42 +144,68 @@ func (s *SecretSetRequest) WithComment(Comment string) *SecretSetRequest { return s } -func (s *SecretSetRequest) WithOAuthScopes(OAuthScopes OAuthScopesRequest) *SecretSetRequest { - s.OAuthScopes = &OAuthScopes +func (s *SecretSetRequest) WithSetForOAuthClientCredentialsFlow(SetForOAuthClientCredentialsFlow SetForOAuthClientCredentialsFlowRequest) *SecretSetRequest { + s.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow + return s +} + +func (s *SecretSetRequest) WithSetForOAuthAuthorizationFlow(SetForOAuthAuthorizationFlow SetForOAuthAuthorizationFlowRequest) *SecretSetRequest { + s.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow + return s +} + +func (s *SecretSetRequest) WithSetForBasicAuthentication(SetForBasicAuthentication SetForBasicAuthenticationRequest) *SecretSetRequest { + s.SetForBasicAuthentication = &SetForBasicAuthentication + return s +} + +func (s *SecretSetRequest) WithSetForGenericString(SetForGenericString SetForGenericStringRequest) *SecretSetRequest { + s.SetForGenericString = &SetForGenericString return s } -func (s *SecretSetRequest) WithOauthRefreshToken(OauthRefreshToken string) *SecretSetRequest { +func NewSetForOAuthClientCredentialsFlowRequest( + OauthScopes []SecurityIntegrationScope, +) *SetForOAuthClientCredentialsFlowRequest { + s := SetForOAuthClientCredentialsFlowRequest{} + s.OauthScopes = OauthScopes + return &s +} + +func NewSetForOAuthAuthorizationFlowRequest() *SetForOAuthAuthorizationFlowRequest { + return &SetForOAuthAuthorizationFlowRequest{} +} + +func (s *SetForOAuthAuthorizationFlowRequest) WithOauthRefreshToken(OauthRefreshToken string) *SetForOAuthAuthorizationFlowRequest { s.OauthRefreshToken = &OauthRefreshToken return s } -func (s *SecretSetRequest) WithOauthRefreshTokenExpiryTime(OauthRefreshTokenExpiryTime string) *SecretSetRequest { +func (s *SetForOAuthAuthorizationFlowRequest) WithOauthRefreshTokenExpiryTime(OauthRefreshTokenExpiryTime string) *SetForOAuthAuthorizationFlowRequest { s.OauthRefreshTokenExpiryTime = &OauthRefreshTokenExpiryTime return s } -func (s *SecretSetRequest) WithUsername(Username string) *SecretSetRequest { - s.Username = &Username - return s +func NewSetForBasicAuthenticationRequest() *SetForBasicAuthenticationRequest { + return &SetForBasicAuthenticationRequest{} } -func (s *SecretSetRequest) WithPassword(Password string) *SecretSetRequest { - s.Password = &Password +func (s *SetForBasicAuthenticationRequest) WithUsername(Username string) *SetForBasicAuthenticationRequest { + s.Username = &Username return s } -func (s *SecretSetRequest) WithSecretString(SecretString string) *SecretSetRequest { - s.SecretString = &SecretString +func (s *SetForBasicAuthenticationRequest) WithPassword(Password string) *SetForBasicAuthenticationRequest { + s.Password = &Password return s } -func NewOAuthScopesRequest() *OAuthScopesRequest { - return &OAuthScopesRequest{} +func NewSetForGenericStringRequest() *SetForGenericStringRequest { + return &SetForGenericStringRequest{} } -func (s *OAuthScopesRequest) WithOAuthScopes(OAuthScopes []SecurityIntegrationScope) *OAuthScopesRequest { - s.OAuthScopes = OAuthScopes +func (s *SetForGenericStringRequest) WithSecretString(SecretString string) *SetForGenericStringRequest { + s.SecretString = &SecretString return s } @@ -190,7 +213,7 @@ func NewSecretUnsetRequest() *SecretUnsetRequest { return &SecretUnsetRequest{} } -func (s *SecretUnsetRequest) WithUnsetComment(UnsetComment bool) *SecretUnsetRequest { - s.UnsetComment = &UnsetComment +func (s *SecretUnsetRequest) WithComment(Comment bool) *SecretUnsetRequest { + s.Comment = &Comment return s } diff --git a/pkg/sdk/secrets_dto_gen.go b/pkg/sdk/secrets_dto_gen.go index 5f37d8caf0..5f23fd9a25 100644 --- a/pkg/sdk/secrets_dto_gen.go +++ b/pkg/sdk/secrets_dto_gen.go @@ -13,9 +13,9 @@ var ( type CreateWithOAuthClientCredentialsFlowSecretRequest struct { OrReplace *bool IfNotExists *bool - name SchemaObjectIdentifier // required - SecurityIntegration AccountObjectIdentifier // required - OauthScopes []SecurityIntegrationScope + name SchemaObjectIdentifier // required + SecurityIntegration AccountObjectIdentifier // required + OauthScopes []SecurityIntegrationScope // required Comment *string } @@ -54,19 +54,31 @@ type AlterSecretRequest struct { } type SecretSetRequest struct { - Comment *string - OAuthScopes *OAuthScopesRequest + Comment *string + SetForOAuthClientCredentialsFlow *SetForOAuthClientCredentialsFlowRequest + SetForOAuthAuthorizationFlow *SetForOAuthAuthorizationFlowRequest + SetForBasicAuthentication *SetForBasicAuthenticationRequest + SetForGenericString *SetForGenericStringRequest +} + +type SetForOAuthClientCredentialsFlowRequest struct { + OauthScopes []SecurityIntegrationScope // required +} + +type SetForOAuthAuthorizationFlowRequest struct { OauthRefreshToken *string OauthRefreshTokenExpiryTime *string - Username *string - Password *string - SecretString *string } -type OAuthScopesRequest struct { - OAuthScopes []SecurityIntegrationScope +type SetForBasicAuthenticationRequest struct { + Username *string + Password *string +} + +type SetForGenericStringRequest struct { + SecretString *string } type SecretUnsetRequest struct { - UnsetComment *bool + Comment *bool } diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index 9f5ac8638b..dd1f181069 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -72,20 +72,29 @@ type AlterSecretOptions struct { IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` Set *SecretSet `ddl:"keyword" sql:"SET"` - Unset *SecretUnset `ddl:"keyword" sql:"UNSET"` + Unset *SecretUnset `ddl:"keyword"` } type SecretSet struct { - Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - OAuthScopes *OAuthScopes `ddl:"parameter,must_parentheses" sql:"OAUTH_SCOPES"` - OauthRefreshToken *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` - OauthRefreshTokenExpiryTime *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN_EXPIRY_TIME"` - Username *string `ddl:"parameter,single_quotes" sql:"USERNAME"` - Password *string `ddl:"parameter,single_quotes" sql:"PASSWORD"` - SecretString *string `ddl:"parameter,single_quotes" sql:"SECRET_STRING"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + SetForOAuthClientCredentialsFlow *SetForOAuthClientCredentialsFlow `ddl:"keyword"` + SetForOAuthAuthorizationFlow *SetForOAuthAuthorizationFlow `ddl:"keyword"` + SetForBasicAuthentication *SetForBasicAuthentication `ddl:"keyword"` + SetForGenericString *SetForGenericString `ddl:"keyword"` } -type OAuthScopes struct { - OAuthScopes []SecurityIntegrationScope `ddl:"list,must_parentheses"` +type SetForOAuthClientCredentialsFlow struct { + OauthScopes []SecurityIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` +} +type SetForOAuthAuthorizationFlow struct { + OauthRefreshToken *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` + OauthRefreshTokenExpiryTime *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN_EXPIRY_TIME"` +} +type SetForBasicAuthentication struct { + Username *string `ddl:"parameter,single_quotes" sql:"USERNAME"` + Password *string `ddl:"parameter,single_quotes" sql:"PASSWORD"` +} +type SetForGenericString struct { + SecretString *string `ddl:"parameter,single_quotes" sql:"SECRET_STRING"` } type SecretUnset struct { - UnsetComment *bool `ddl:"keyword" sql:"UNSET COMMENT"` + Comment *bool `ddl:"keyword" sql:"SET COMMENT = NULL"` } diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 426e5eee09..bac04d5e29 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -30,14 +30,9 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { }) t.Run("basic", func(t *testing.T) { - securityIntegration := NewAccountObjectIdentifier("security_integration") - oauthScopes := []SecurityIntegrationScope{{Scope: "sample_scope"}} - opts := defaultOpts() - opts.SecurityIntegration = securityIntegration - opts.OauthScopes = oauthScopes // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName(), securityIntegration.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) t.Run("all options", func(t *testing.T) { @@ -168,48 +163,84 @@ func TestSecrets_CreateWithGenericString(t *testing.T) { } func TestSecrets_Alter(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid AlterSecretOptions + defaultOpts := func() *AlterSecretOptions { return &AlterSecretOptions{ - name: id, } } + setOpts := func() *AlterSecretOptions { + return &AlterSecretOptions{ + name: id, + Set: &SecretSet{}, + IfExists: Bool(true), + } + } + + unsetOpts := func() *AlterSecretOptions { + return &AlterSecretOptions{ + name: id, + Unset: &SecretUnset{}, + IfExists: Bool(true), + } + } + t.Run("validation: nil options", func(t *testing.T) { var opts *AlterSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) + t.Run("validation: exactly one field from [opts.Set opts.Unset] should be present", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) }) - t.Run("alter: set options", func(t *testing.T) { - oauthScopes := &OAuthScopes{[]SecurityIntegrationScope{{Scope: "different_scope_name"}}} + t.Run("validation: exactly one field from [opts.Set.SetForOAuthClientCredentialsFlow opts.Set.SetForOAuthAuthorizationFlow opts.Set.SetForBasicAuthentication opts.Set.SetForGenericString] should be present", func(t *testing.T) { + opts := setOpts() + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{} + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + }) - opts := defaultOpts() - opts.Set = &SecretSet{ - OAuthScopes: oauthScopes, - OauthRefreshToken: String("refresh_token"), - OauthRefreshTokenExpiryTime: String("2024-10-10"), + t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{[]SecurityIntegrationScope{{"sample_scope"}}} + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName()) + }) + + t.Run("alter: set options for Oauth Authorization Flow", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ + String("test_token"), + String("2024-11-11"), } - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET %s SET OAUTH_SCOPES = ('different_scope_name')", id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_REFRESH_TOKEN = 'test_token' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = '2024-11-11'", id.FullyQualifiedName()) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + t.Run("alter: set options for Basic Authentication", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ + Username: String("foo"), + Password: String("bar"), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' USERNAME = 'foo' PASSWORD = 'bar'", id.FullyQualifiedName()) }) - t.Run("all options", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + t.Run("alter: set options for Generic string", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForGenericString = &SetForGenericString{String("test")} + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' SECRET_STRING = 'test'", id.FullyQualifiedName()) + }) + + t.Run("alter: unset options", func(t *testing.T) { + opts := unsetOpts() + opts.Unset.Comment = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = NULL", id.FullyQualifiedName()) }) } diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index bdce5ad21a..a5995fe3ce 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -90,19 +90,41 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Set != nil { + opts.Set = &SecretSet{ Comment: r.Set.Comment, + } + + if r.Set.SetForOAuthClientCredentialsFlow != nil { + + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{ + OauthScopes: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes, + } - OauthRefreshToken: r.Set.OauthRefreshToken, - OauthRefreshTokenExpiryTime: r.Set.OauthRefreshTokenExpiryTime, - Username: r.Set.Username, - Password: r.Set.Password, - SecretString: r.Set.SecretString, } - if r.Set.OAuthScopes != nil { - opts.Set.OAuthScopes = &OAuthScopes{ - OAuthScopes: r.Set.OAuthScopes.OAuthScopes, + if r.Set.SetForOAuthAuthorizationFlow != nil { + + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ + OauthRefreshToken: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshToken, + OauthRefreshTokenExpiryTime: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshTokenExpiryTime, + } + + } + + if r.Set.SetForBasicAuthentication != nil { + + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ + Username: r.Set.SetForBasicAuthentication.Username, + Password: r.Set.SetForBasicAuthentication.Password, + } + + } + + if r.Set.SetForGenericString != nil { + + opts.Set.SetForGenericString = &SetForGenericString{ + SecretString: r.Set.SetForGenericString.SecretString, } } @@ -110,8 +132,9 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Unset != nil { + opts.Unset = &SecretUnset{ - UnsetComment: r.Unset.UnsetComment, + Comment: r.Unset.Comment, } } diff --git a/pkg/sdk/secrets_validations_gen.go b/pkg/sdk/secrets_validations_gen.go index 513145bf1c..e703dbec57 100644 --- a/pkg/sdk/secrets_validations_gen.go +++ b/pkg/sdk/secrets_validations_gen.go @@ -72,5 +72,10 @@ func (opts *AlterSecretOptions) validate() error { if !exactlyOneValueSet(opts.Set, opts.Unset) { errs = append(errs, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) } + if valueSet(opts.Set) { + if !exactlyOneValueSet(opts.Set.SetForOAuthClientCredentialsFlow, opts.Set.SetForOAuthAuthorizationFlow, opts.Set.SetForBasicAuthentication, opts.Set.SetForGenericString) { + errs = append(errs, errExactlyOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + } + } return JoinErrors(errs...) } From 57ca19a26af904a0a40962512f6425f875450af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 13 Sep 2024 15:30:59 +0200 Subject: [PATCH 05/65] add tests for all four create types --- pkg/sdk/secrets_gen_test.go | 99 ++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index bac04d5e29..89f7de7966 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -3,12 +3,10 @@ package sdk import "testing" func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithOAuthClientCredentialsFlowSecretOptions + defaultOpts := func() *CreateWithOAuthClientCredentialsFlowSecretOptions { return &CreateWithOAuthClientCredentialsFlowSecretOptions{ - name: id, } } @@ -17,38 +15,36 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { var opts *CreateWithOAuthClientCredentialsFlowSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthClientCredentialsFlowSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.SecurityIntegration = integration + opts.OauthScopes = []SecurityIntegrationScope{{"test"}} + opts.Comment = String("foo") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('test') COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) } func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithOAuthAuthorizationCodeFlowSecretOptions + defaultOpts := func() *CreateWithOAuthAuthorizationCodeFlowSecretOptions { return &CreateWithOAuthAuthorizationCodeFlowSecretOptions{ - name: id, } } @@ -57,38 +53,37 @@ func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { var opts *CreateWithOAuthAuthorizationCodeFlowSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthAuthorizationCodeFlowSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.OauthRefreshToken = "foo" + opts.OauthRefreshTokenExpiryTime = "bar" + opts.SecurityIntegration = integration + opts.Comment = String("test") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 OAUTH_REFRESH_TOKEN = 'foo' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = 'bar' API_AUTHENTICATION = %s COMMENT = 'test'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) } func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithBasicAuthenticationSecretOptions + defaultOpts := func() *CreateWithBasicAuthenticationSecretOptions { return &CreateWithBasicAuthenticationSecretOptions{ - name: id, } } @@ -97,38 +92,34 @@ func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { var opts *CreateWithBasicAuthenticationSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithBasicAuthenticationSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.Username = "foo" + opts.Password = "bar" + opts.Comment = String("test") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = PASSWORD USERNAME = 'foo' PASSWORD = 'bar' COMMENT = 'test'", id.FullyQualifiedName()) }) } func TestSecrets_CreateWithGenericString(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithGenericStringSecretOptions + defaultOpts := func() *CreateWithGenericStringSecretOptions { return &CreateWithGenericStringSecretOptions{ - name: id, } } @@ -137,28 +128,24 @@ func TestSecrets_CreateWithGenericString(t *testing.T) { var opts *CreateWithGenericStringSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithGenericStringSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.SecretString = "test" + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = GENERIC_STRING SECRET_STRING = 'test'", id.FullyQualifiedName()) }) } From 8d55a63c9aaa23a0d784c7009c78caabd5e53f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 16 Sep 2024 11:24:34 +0200 Subject: [PATCH 06/65] rename previous secret to secretReference in common_types.go due to naming clash --- pkg/sdk/common_types.go | 2 +- pkg/sdk/functions_def.go | 4 ++-- pkg/sdk/functions_dto_builders_gen.go | 4 ++-- pkg/sdk/functions_dto_gen.go | 4 ++-- pkg/sdk/functions_gen.go | 4 ++-- pkg/sdk/functions_gen_test.go | 4 ++-- pkg/sdk/procedures_def.go | 4 ++-- pkg/sdk/procedures_dto_builders_gen.go | 4 ++-- pkg/sdk/procedures_dto_gen.go | 4 ++-- pkg/sdk/procedures_gen.go | 4 ++-- pkg/sdk/procedures_gen_test.go | 4 ++-- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/sdk/common_types.go b/pkg/sdk/common_types.go index f99fe2fdd5..1627ab9d2a 100644 --- a/pkg/sdk/common_types.go +++ b/pkg/sdk/common_types.go @@ -257,7 +257,7 @@ func ReturnNullValuesPointer(v ReturnNullValues) *ReturnNullValues { return &v } -type Secret struct { +type SecretReference struct { VariableName string `ddl:"keyword,single_quotes"` Name string `ddl:"parameter,no_quotes"` } diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index 4411f6a389..017ccd8335 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -79,7 +79,7 @@ var FunctionsDef = g.NewInterface( ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). - ListAssignment("SECRETS", "Secret", g.ParameterOptions().Parentheses()). + ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). @@ -152,7 +152,7 @@ var FunctionsDef = g.NewInterface( ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). - ListAssignment("SECRETS", "Secret", g.ParameterOptions().Parentheses()). + ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "RuntimeVersion"). diff --git a/pkg/sdk/functions_dto_builders_gen.go b/pkg/sdk/functions_dto_builders_gen.go index 0a72ce0fdc..0aef014932 100644 --- a/pkg/sdk/functions_dto_builders_gen.go +++ b/pkg/sdk/functions_dto_builders_gen.go @@ -86,7 +86,7 @@ func (s *CreateForJavaFunctionRequest) WithExternalAccessIntegrations(ExternalAc return s } -func (s *CreateForJavaFunctionRequest) WithSecrets(Secrets []Secret) *CreateForJavaFunctionRequest { +func (s *CreateForJavaFunctionRequest) WithSecrets(Secrets []SecretReference) *CreateForJavaFunctionRequest { s.Secrets = Secrets return s } @@ -311,7 +311,7 @@ func (s *CreateForPythonFunctionRequest) WithExternalAccessIntegrations(External return s } -func (s *CreateForPythonFunctionRequest) WithSecrets(Secrets []Secret) *CreateForPythonFunctionRequest { +func (s *CreateForPythonFunctionRequest) WithSecrets(Secrets []SecretReference) *CreateForPythonFunctionRequest { s.Secrets = Secrets return s } diff --git a/pkg/sdk/functions_dto_gen.go b/pkg/sdk/functions_dto_gen.go index 6376eea28b..3fa0ff387b 100644 --- a/pkg/sdk/functions_dto_gen.go +++ b/pkg/sdk/functions_dto_gen.go @@ -32,7 +32,7 @@ type CreateForJavaFunctionRequest struct { Packages []FunctionPackageRequest Handler string // required ExternalAccessIntegrations []AccountObjectIdentifier - Secrets []Secret + Secrets []SecretReference TargetPath *string FunctionDefinition *string } @@ -102,7 +102,7 @@ type CreateForPythonFunctionRequest struct { Packages []FunctionPackageRequest Handler string // required ExternalAccessIntegrations []AccountObjectIdentifier - Secrets []Secret + Secrets []SecretReference FunctionDefinition *string } diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 7c3d55d76b..85f2b8378d 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -40,7 +40,7 @@ type CreateForJavaFunctionOptions struct { Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` - Secrets []Secret `ddl:"parameter,parentheses" sql:"SECRETS"` + Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` FunctionDefinition *string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` } @@ -118,7 +118,7 @@ type CreateForPythonFunctionOptions struct { Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` - Secrets []Secret `ddl:"parameter,parentheses" sql:"SECRETS"` + Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` FunctionDefinition *string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` } diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index 0bb4778832..b0c1c5b0b5 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -103,7 +103,7 @@ func TestFunctions_CreateForJava(t *testing.T) { opts.ExternalAccessIntegrations = []AccountObjectIdentifier{ NewAccountObjectIdentifier("ext_integration"), } - opts.Secrets = []Secret{ + opts.Secrets = []SecretReference{ { VariableName: "variable1", Name: "name1", @@ -272,7 +272,7 @@ func TestFunctions_CreateForPython(t *testing.T) { opts.ExternalAccessIntegrations = []AccountObjectIdentifier{ NewAccountObjectIdentifier("ext_integration"), } - opts.Secrets = []Secret{ + opts.Secrets = []SecretReference{ { VariableName: "variable1", Name: "name1", diff --git a/pkg/sdk/procedures_def.go b/pkg/sdk/procedures_def.go index 1788c99a44..3b5eb69882 100644 --- a/pkg/sdk/procedures_def.go +++ b/pkg/sdk/procedures_def.go @@ -101,7 +101,7 @@ var ProceduresDef = g.NewInterface( ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). - ListAssignment("SECRETS", "Secret", g.ParameterOptions().Parentheses()). + ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("NullInputBehavior", "*NullInputBehavior", g.KeywordOptions()). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). @@ -169,7 +169,7 @@ var ProceduresDef = g.NewInterface( ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). - ListAssignment("SECRETS", "Secret", g.ParameterOptions().Parentheses()). + ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). PredefinedQueryStructField("NullInputBehavior", "*NullInputBehavior", g.KeywordOptions()). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("ExecuteAs", "*ExecuteAs", g.KeywordOptions()). diff --git a/pkg/sdk/procedures_dto_builders_gen.go b/pkg/sdk/procedures_dto_builders_gen.go index 75671ecf4e..c88c8bd9ac 100644 --- a/pkg/sdk/procedures_dto_builders_gen.go +++ b/pkg/sdk/procedures_dto_builders_gen.go @@ -50,7 +50,7 @@ func (s *CreateForJavaProcedureRequest) WithExternalAccessIntegrations(ExternalA return s } -func (s *CreateForJavaProcedureRequest) WithSecrets(Secrets []Secret) *CreateForJavaProcedureRequest { +func (s *CreateForJavaProcedureRequest) WithSecrets(Secrets []SecretReference) *CreateForJavaProcedureRequest { s.Secrets = Secrets return s } @@ -260,7 +260,7 @@ func (s *CreateForPythonProcedureRequest) WithExternalAccessIntegrations(Externa return s } -func (s *CreateForPythonProcedureRequest) WithSecrets(Secrets []Secret) *CreateForPythonProcedureRequest { +func (s *CreateForPythonProcedureRequest) WithSecrets(Secrets []SecretReference) *CreateForPythonProcedureRequest { s.Secrets = Secrets return s } diff --git a/pkg/sdk/procedures_dto_gen.go b/pkg/sdk/procedures_dto_gen.go index 398169a682..8ad24b86e6 100644 --- a/pkg/sdk/procedures_dto_gen.go +++ b/pkg/sdk/procedures_dto_gen.go @@ -32,7 +32,7 @@ type CreateForJavaProcedureRequest struct { Imports []ProcedureImportRequest Handler string // required ExternalAccessIntegrations []AccountObjectIdentifier - Secrets []Secret + Secrets []SecretReference TargetPath *string NullInputBehavior *NullInputBehavior Comment *string @@ -100,7 +100,7 @@ type CreateForPythonProcedureRequest struct { Imports []ProcedureImportRequest Handler string // required ExternalAccessIntegrations []AccountObjectIdentifier - Secrets []Secret + Secrets []SecretReference NullInputBehavior *NullInputBehavior Comment *string ExecuteAs *ExecuteAs diff --git a/pkg/sdk/procedures_gen.go b/pkg/sdk/procedures_gen.go index b12fd6beeb..e265558f70 100644 --- a/pkg/sdk/procedures_gen.go +++ b/pkg/sdk/procedures_gen.go @@ -40,7 +40,7 @@ type CreateForJavaProcedureOptions struct { Imports []ProcedureImport `ddl:"parameter,parentheses" sql:"IMPORTS"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` - Secrets []Secret `ddl:"parameter,parentheses" sql:"SECRETS"` + Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` NullInputBehavior *NullInputBehavior `ddl:"keyword"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` @@ -116,7 +116,7 @@ type CreateForPythonProcedureOptions struct { Imports []ProcedureImport `ddl:"parameter,parentheses" sql:"IMPORTS"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` - Secrets []Secret `ddl:"parameter,parentheses" sql:"SECRETS"` + Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` NullInputBehavior *NullInputBehavior `ddl:"keyword"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` ExecuteAs *ExecuteAs `ddl:"keyword"` diff --git a/pkg/sdk/procedures_gen_test.go b/pkg/sdk/procedures_gen_test.go index 6345324d0b..7717308d51 100644 --- a/pkg/sdk/procedures_gen_test.go +++ b/pkg/sdk/procedures_gen_test.go @@ -93,7 +93,7 @@ func TestProcedures_CreateForJava(t *testing.T) { opts.ExternalAccessIntegrations = []AccountObjectIdentifier{ NewAccountObjectIdentifier("ext_integration"), } - opts.Secrets = []Secret{ + opts.Secrets = []SecretReference{ { VariableName: "variable1", Name: "name1", @@ -235,7 +235,7 @@ func TestProcedures_CreateForPython(t *testing.T) { opts.ExternalAccessIntegrations = []AccountObjectIdentifier{ NewAccountObjectIdentifier("ext_integration"), } - opts.Secrets = []Secret{ + opts.Secrets = []SecretReference{ { VariableName: "variable1", Name: "name1", From 9894799b9b81b016a4675c1fe68d103607d34af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 16 Sep 2024 11:25:02 +0200 Subject: [PATCH 07/65] secrets gen with drop, show and desc --- pkg/sdk/secrets_def.go | 93 +++++++++++++++++++++---- pkg/sdk/secrets_dto_builders_gen.go | 35 ++++++++++ pkg/sdk/secrets_dto_gen.go | 17 +++++ pkg/sdk/secrets_gen.go | 82 +++++++++++++++++++++- pkg/sdk/secrets_gen_integration_test.go | 16 +++++ pkg/sdk/secrets_gen_test.go | 89 +++++++++++++++++++++++ pkg/sdk/secrets_impl_gen.go | 70 +++++++++++++++++++ pkg/sdk/secrets_validations_gen.go | 33 +++++++++ 8 files changed, 420 insertions(+), 15 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 204db83165..9d3ae947c9 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -7,10 +7,56 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen var secretsSecurityIntegrationScopeDef = g.NewQueryStruct("SecurityIntegrationScope"). Text("Scope", g.KeywordOptions().SingleQuotes().Required()) -/* -var secretsIntegrationScopes = g.NewQueryStruct("OAuthScopes"). - List("OAuthScopes", "SecurityIntegrationScope", g.ListOptions().MustParentheses()) -*/ +var secretDbRow = g.DbStruct("secretDBRow"). + Field("created_on", "string"). + Field("name", "string"). + Field("schema_name", "string"). + Field("database_name", "string"). + Field("owner", "string"). + Field("comment", "sql.NullString"). + Field("secret_type", "string"). + Field("oauth_scopes", "sql.NullString"). // its a list tho + Field("owner_role_type", "string") + +// cannot name 'secret' due to clash in common_types.go +var secret = g.PlainStruct("Secret"). + Field("CreatedOn", "string"). + Field("Name", "string"). + Field("SchemaName", "string"). + Field("DatabaseName", "string"). + Field("Owner", "string"). + Field("Comment", "string"). + Field("SecretType", "string"). + Field("OauthScopes", "sql.NullString"). + Field("OwnerRoleType", "string") + +var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). + Field("created_on", "string"). + Field("name", "string"). + Field("schema_name", "string"). + Field("database_name", "string"). + Field("owner", "string"). + Field("comment", "sql.NullString"). + Field("secret_type", "string"). + Field("username", "sql.NullString"). + Field("oauth_access_token_expiry_time", "sql.NullString"). + Field("oauth_refresh_token_expiry_time", "sql.NullString"). + Field("oauth_scopes", "sql.NullString"). // its a list tho + Field("integration_name", "sql.NullString") + +var secretDetails = g.PlainStruct("SecretDetails"). + Field("CreatedOn", "string"). + Field("Name", "string"). + Field("SchemaName", "string"). + Field("DatabaseName", "string"). + Field("Owner", "string"). + Field("Comment", "sql.NullString"). + Field("SecretType", "string"). + Field("Username", "sql.NullString"). + Field("OauthAccessTokenExpiryTime", "sql.NullString"). + Field("OauthRefreshTokenExpiryTime", "sql.NullString"). + Field("OauthScopes", "sql.NullString"). // its a list tho + Field("IntegrationName", "sql.NullString") var secretSet = g.NewQueryStruct("SecretSet"). OptionalComment(). @@ -49,8 +95,6 @@ var secretUnset = g.NewQueryStruct("SecretUnset"). //OptionalSQL("UNSET COMMENT") PredefinedQueryStructField("Comment", "*bool", g.KeywordOptions().SQL("SET COMMENT = NULL")) -//OptionalSQL("SET COMMENT = NULL") - var SecretsDef = g.NewInterface( "Secrets", "Secret", @@ -67,13 +111,6 @@ var SecretsDef = g.NewInterface( PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION").Required()). ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses().Required()). - /* - ListQueryStructField( - "OAuthScopes", - secretsSecurityIntegrationScopeDef, - g.KeywordOptions().Parentheses().SQL("OAUTH_SCOPES"), - ). - */ OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), @@ -142,4 +179,32 @@ var SecretsDef = g.NewInterface( g.KeywordOptions(), ). WithValidation(g.ExactlyOneValueSet, "Set", "Unset"), -) +).DropOperation( + "https://docs.snowflake.com/en/sql-reference/sql/drop-secret", + g.NewQueryStruct("DropSecret"). + Drop(). + SQL("SECRET"). + IfExists(). + Name(). + WithValidation(g.ValidIdentifier, "name"), +).ShowOperation( + "https://docs.snowflake.com/en/sql-reference/sql/show-secret", + secretDbRow, + secret, + g.NewQueryStruct("ShowSecret"). + Show(). + SQL("SECRETS"). + OptionalLike(). + OptionalIn(), +).ShowByIdOperation(). + DescribeOperation( + g.DescriptionMappingKindSingleValue, + "https://docs.snowflake.com/en/sql-reference/sql/desc-secret", + secretDetailsDbRow, + secretDetails, + g.NewQueryStruct("DescribeSecret"). + Describe(). + SQL("SECRET"). + Name(). + WithValidation(g.ValidIdentifier, "name"), + ) diff --git a/pkg/sdk/secrets_dto_builders_gen.go b/pkg/sdk/secrets_dto_builders_gen.go index a8bacc259b..faf3a01bf9 100644 --- a/pkg/sdk/secrets_dto_builders_gen.go +++ b/pkg/sdk/secrets_dto_builders_gen.go @@ -217,3 +217,38 @@ func (s *SecretUnsetRequest) WithComment(Comment bool) *SecretUnsetRequest { s.Comment = &Comment return s } + +func NewDropSecretRequest( + name SchemaObjectIdentifier, +) *DropSecretRequest { + s := DropSecretRequest{} + s.name = name + return &s +} + +func (s *DropSecretRequest) WithIfExists(IfExists bool) *DropSecretRequest { + s.IfExists = &IfExists + return s +} + +func NewShowSecretRequest() *ShowSecretRequest { + return &ShowSecretRequest{} +} + +func (s *ShowSecretRequest) WithLike(Like Like) *ShowSecretRequest { + s.Like = &Like + return s +} + +func (s *ShowSecretRequest) WithIn(In In) *ShowSecretRequest { + s.In = &In + return s +} + +func NewDescribeSecretRequest( + name SchemaObjectIdentifier, +) *DescribeSecretRequest { + s := DescribeSecretRequest{} + s.name = name + return &s +} diff --git a/pkg/sdk/secrets_dto_gen.go b/pkg/sdk/secrets_dto_gen.go index 5f23fd9a25..36d3f1ef22 100644 --- a/pkg/sdk/secrets_dto_gen.go +++ b/pkg/sdk/secrets_dto_gen.go @@ -8,6 +8,9 @@ var ( _ optionsProvider[CreateWithBasicAuthenticationSecretOptions] = new(CreateWithBasicAuthenticationSecretRequest) _ optionsProvider[CreateWithGenericStringSecretOptions] = new(CreateWithGenericStringSecretRequest) _ optionsProvider[AlterSecretOptions] = new(AlterSecretRequest) + _ optionsProvider[DropSecretOptions] = new(DropSecretRequest) + _ optionsProvider[ShowSecretOptions] = new(ShowSecretRequest) + _ optionsProvider[DescribeSecretOptions] = new(DescribeSecretRequest) ) type CreateWithOAuthClientCredentialsFlowSecretRequest struct { @@ -82,3 +85,17 @@ type SetForGenericStringRequest struct { type SecretUnsetRequest struct { Comment *bool } + +type DropSecretRequest struct { + IfExists *bool + name SchemaObjectIdentifier // required +} + +type ShowSecretRequest struct { + Like *Like + In *In +} + +type DescribeSecretRequest struct { + name SchemaObjectIdentifier // required +} diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index dd1f181069..6ff0313c1e 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -1,6 +1,9 @@ package sdk -import "context" +import ( + "context" + "database/sql" +) type Secrets interface { CreateWithOAuthClientCredentialsFlow(ctx context.Context, request *CreateWithOAuthClientCredentialsFlowSecretRequest) error @@ -8,6 +11,10 @@ type Secrets interface { CreateWithBasicAuthentication(ctx context.Context, request *CreateWithBasicAuthenticationSecretRequest) error CreateWithGenericString(ctx context.Context, request *CreateWithGenericStringSecretRequest) error Alter(ctx context.Context, request *AlterSecretRequest) error + Drop(ctx context.Context, request *DropSecretRequest) error + Show(ctx context.Context, request *ShowSecretRequest) ([]Secret, error) + ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*Secret, error) + Describe(ctx context.Context, id SchemaObjectIdentifier) (*SecretDetails, error) } // CreateWithOAuthClientCredentialsFlowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. @@ -98,3 +105,76 @@ type SetForGenericString struct { type SecretUnset struct { Comment *bool `ddl:"keyword" sql:"SET COMMENT = NULL"` } + +// DropSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-secret. +type DropSecretOptions struct { + drop bool `ddl:"static" sql:"DROP"` + secret bool `ddl:"static" sql:"SECRET"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` +} + +// ShowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-secret. +type ShowSecretOptions struct { + show bool `ddl:"static" sql:"SHOW"` + secrets bool `ddl:"static" sql:"SECRETS"` + Like *Like `ddl:"keyword" sql:"LIKE"` + In *In `ddl:"keyword" sql:"IN"` +} +type secretDBRow struct { + CreatedOn string `db:"created_on"` + Name string `db:"name"` + SchemaName string `db:"schema_name"` + DatabaseName string `db:"database_name"` + Owner string `db:"owner"` + Comment sql.NullString `db:"comment"` + SecretType string `db:"secret_type"` + OauthScopes sql.NullString `db:"oauth_scopes"` + OwnerRoleType string `db:"owner_role_type"` +} +type Secret struct { + CreatedOn string + Name string + SchemaName string + DatabaseName string + Owner string + Comment string + SecretType string + OauthScopes sql.NullString + OwnerRoleType string +} + +// DescribeSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-secret. +type DescribeSecretOptions struct { + describe bool `ddl:"static" sql:"DESCRIBE"` + secret bool `ddl:"static" sql:"SECRET"` + name SchemaObjectIdentifier `ddl:"identifier"` +} +type secretDetailsDBRow struct { + CreatedOn string `db:"created_on"` + Name string `db:"name"` + SchemaName string `db:"schema_name"` + DatabaseName string `db:"database_name"` + Owner string `db:"owner"` + Comment sql.NullString `db:"comment"` + SecretType string `db:"secret_type"` + Username sql.NullString `db:"username"` + OauthAccessTokenExpiryTime sql.NullString `db:"oauth_access_token_expiry_time"` + OauthRefreshTokenExpiryTime sql.NullString `db:"oauth_refresh_token_expiry_time"` + OauthScopes sql.NullString `db:"oauth_scopes"` + IntegrationName sql.NullString `db:"integration_name"` +} +type SecretDetails struct { + CreatedOn string + Name string + SchemaName string + DatabaseName string + Owner string + Comment sql.NullString + SecretType string + Username sql.NullString + OauthAccessTokenExpiryTime sql.NullString + OauthRefreshTokenExpiryTime sql.NullString + OauthScopes sql.NullString + IntegrationName sql.NullString +} diff --git a/pkg/sdk/secrets_gen_integration_test.go b/pkg/sdk/secrets_gen_integration_test.go index ee9af4239a..4474555b36 100644 --- a/pkg/sdk/secrets_gen_integration_test.go +++ b/pkg/sdk/secrets_gen_integration_test.go @@ -24,4 +24,20 @@ func TestInt_Secrets(t *testing.T) { t.Run("Alter", func(t *testing.T) { // TODO: fill me }) + + t.Run("Drop", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Show", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("ShowByID", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Describe", func(t *testing.T) { + // TODO: fill me + }) } diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 89f7de7966..a07505fdb8 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -231,3 +231,92 @@ func TestSecrets_Alter(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = NULL", id.FullyQualifiedName()) }) } + +func TestSecrets_Drop(t *testing.T) { + id := randomSchemaObjectIdentifier() + defaultOpts := func() *DropSecretOptions { + return &DropSecretOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *DropSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} + +func TestSecrets_Show(t *testing.T) { + // Minimal valid ShowSecretOptions + defaultOpts := func() *ShowSecretOptions { + return &ShowSecretOptions{} + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *ShowSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} + +func TestSecrets_Describe(t *testing.T) { + + id := randomSchemaObjectIdentifier() + // Minimal valid DescribeSecretOptions + defaultOpts := func() *DescribeSecretOptions { + return &DescribeSecretOptions{ + + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *DescribeSecretOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) +} diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index a5995fe3ce..dc31263fc8 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -2,6 +2,8 @@ package sdk import ( "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" ) var _ Secrets = (*secrets)(nil) @@ -35,6 +37,41 @@ func (v *secrets) Alter(ctx context.Context, request *AlterSecretRequest) error return validateAndExec(v.client, ctx, opts) } +func (v *secrets) Drop(ctx context.Context, request *DropSecretRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *secrets) Show(ctx context.Context, request *ShowSecretRequest) ([]Secret, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[secretDBRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + resultList := convertRows[secretDBRow, Secret](dbRows) + return resultList, nil +} + +func (v *secrets) ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*Secret, error) { + // TODO: adjust request if e.g. LIKE is supported for the resource + secrets, err := v.Show(ctx, NewShowSecretRequest()) + if err != nil { + return nil, err + } + return collections.FindFirst(secrets, func(r Secret) bool { return r.Name == id.Name() }) +} + +func (v *secrets) Describe(ctx context.Context, id SchemaObjectIdentifier) (*SecretDetails, error) { + opts := &DescribeSecretOptions{ + name: id, + } + result, err := validateAndQueryOne[secretDetailsDBRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + return result.convert(), nil +} + func (r *CreateWithOAuthClientCredentialsFlowSecretRequest) toOpts() *CreateWithOAuthClientCredentialsFlowSecretOptions { opts := &CreateWithOAuthClientCredentialsFlowSecretOptions{ OrReplace: r.OrReplace, @@ -141,3 +178,36 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { return opts } + +func (r *DropSecretRequest) toOpts() *DropSecretOptions { + opts := &DropSecretOptions{ + IfExists: r.IfExists, + name: r.name, + } + return opts +} + +func (r *ShowSecretRequest) toOpts() *ShowSecretOptions { + opts := &ShowSecretOptions{ + Like: r.Like, + In: r.In, + } + return opts +} + +func (r secretDBRow) convert() *Secret { + // TODO: Mapping + return &Secret{} +} + +func (r *DescribeSecretRequest) toOpts() *DescribeSecretOptions { + opts := &DescribeSecretOptions{ + name: r.name, + } + return opts +} + +func (r secretDetailsDBRow) convert() *SecretDetails { + // TODO: Mapping + return &SecretDetails{} +} diff --git a/pkg/sdk/secrets_validations_gen.go b/pkg/sdk/secrets_validations_gen.go index e703dbec57..c414a5cd6f 100644 --- a/pkg/sdk/secrets_validations_gen.go +++ b/pkg/sdk/secrets_validations_gen.go @@ -6,6 +6,9 @@ var ( _ validatable = new(CreateWithBasicAuthenticationSecretOptions) _ validatable = new(CreateWithGenericStringSecretOptions) _ validatable = new(AlterSecretOptions) + _ validatable = new(DropSecretOptions) + _ validatable = new(ShowSecretOptions) + _ validatable = new(DescribeSecretOptions) ) func (opts *CreateWithOAuthClientCredentialsFlowSecretOptions) validate() error { @@ -79,3 +82,33 @@ func (opts *AlterSecretOptions) validate() error { } return JoinErrors(errs...) } + +func (opts *DropSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *ShowSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + return JoinErrors(errs...) +} + +func (opts *DescribeSecretOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} From e4a0827ecd4e76cf340f95a7b77ac80222fc5a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 16 Sep 2024 11:39:47 +0200 Subject: [PATCH 08/65] all unit tests --- pkg/sdk/secrets_gen_test.go | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index a07505fdb8..692737fe14 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -234,6 +234,7 @@ func TestSecrets_Alter(t *testing.T) { func TestSecrets_Drop(t *testing.T) { id := randomSchemaObjectIdentifier() + defaultOpts := func() *DropSecretOptions { return &DropSecretOptions{ name: id, @@ -244,7 +245,7 @@ func TestSecrets_Drop(t *testing.T) { var opts *DropSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) @@ -252,19 +253,17 @@ func TestSecrets_Drop(t *testing.T) { t.Run("basic", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + assertOptsValidAndSQLEquals(t, opts, "DROP SECRET %s", id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfExists = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "DROP SECRET IF EXISTS %s", id.FullyQualifiedName()) }) } func TestSecrets_Show(t *testing.T) { - // Minimal valid ShowSecretOptions defaultOpts := func() *ShowSecretOptions { return &ShowSecretOptions{} } @@ -276,24 +275,31 @@ func TestSecrets_Show(t *testing.T) { t.Run("basic", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS") }) - t.Run("all options", func(t *testing.T) { + t.Run("show with like", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.Like = &Like{ + Pattern: String("pattern"), + } + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS LIKE 'pattern'") + }) + + t.Run("show with in", func(t *testing.T) { + opts := defaultOpts() + opts.In = &In{ + Account: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS IN ACCOUNT") }) } func TestSecrets_Describe(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid DescribeSecretOptions + defaultOpts := func() *DescribeSecretOptions { return &DescribeSecretOptions{ - name: id, } } @@ -302,21 +308,14 @@ func TestSecrets_Describe(t *testing.T) { var opts *DescribeSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + assertOptsValidAndSQLEquals(t, opts, "DESCRIBE SECRET %s", id.FullyQualifiedName()) }) } From 3bb8c859e55a5c2ccf180234b9c56f35b5f947f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 16 Sep 2024 16:10:25 +0200 Subject: [PATCH 09/65] init for integration tests --- pkg/acceptance/helpers/secret_client.go | 19 +++ pkg/acceptance/helpers/test_client.go | 2 + pkg/sdk/client.go | 1 + pkg/sdk/secrets_gen_integration_test.go | 43 ------ .../testint/secrets_gen_integration_test.go | 135 ++++++++++++++++++ 5 files changed, 157 insertions(+), 43 deletions(-) create mode 100644 pkg/acceptance/helpers/secret_client.go delete mode 100644 pkg/sdk/secrets_gen_integration_test.go create mode 100644 pkg/sdk/testint/secrets_gen_integration_test.go diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go new file mode 100644 index 0000000000..e800d6f59f --- /dev/null +++ b/pkg/acceptance/helpers/secret_client.go @@ -0,0 +1,19 @@ +package helpers + +import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + +type SecretClient struct { + context *TestClientContext + ids *IdsGenerator +} + +func NewSecretClient(context *TestClientContext, idsGenerator *IdsGenerator) *SecretClient { + return &SecretClient{ + context: context, + ids: idsGenerator, + } +} + +func (c *SecretClient) client() sdk.Secrets { + return c.context.client.Secrets +} diff --git a/pkg/acceptance/helpers/test_client.go b/pkg/acceptance/helpers/test_client.go index 975c74a061..63b59097ed 100644 --- a/pkg/acceptance/helpers/test_client.go +++ b/pkg/acceptance/helpers/test_client.go @@ -47,6 +47,7 @@ type TestClient struct { Role *RoleClient RowAccessPolicy *RowAccessPolicyClient Schema *SchemaClient + Secret *SecretClient SecurityIntegration *SecurityIntegrationClient SessionPolicy *SessionPolicyClient Share *ShareClient @@ -112,6 +113,7 @@ func NewTestClient(c *sdk.Client, database string, schema string, warehouse stri Role: NewRoleClient(context, idsGenerator), RowAccessPolicy: NewRowAccessPolicyClient(context, idsGenerator), Schema: NewSchemaClient(context, idsGenerator), + Secret: NewSecretClient(context, idsGenerator), SecurityIntegration: NewSecurityIntegrationClient(context, idsGenerator), SessionPolicy: NewSessionPolicyClient(context, idsGenerator), Share: NewShareClient(context, idsGenerator), diff --git a/pkg/sdk/client.go b/pkg/sdk/client.go index 6f2e57200e..5d1ddaa887 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -75,6 +75,7 @@ type Client struct { Roles Roles RowAccessPolicies RowAccessPolicies Schemas Schemas + Secrets Secrets SecurityIntegrations SecurityIntegrations Sequences Sequences SessionPolicies SessionPolicies diff --git a/pkg/sdk/secrets_gen_integration_test.go b/pkg/sdk/secrets_gen_integration_test.go deleted file mode 100644 index 4474555b36..0000000000 --- a/pkg/sdk/secrets_gen_integration_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package sdk - -import "testing" - -func TestInt_Secrets(t *testing.T) { - // TODO: prepare common resources - - t.Run("CreateWithOAuthClientCredentialsFlow", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithBasicAuthentication", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithGenericString", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Alter", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Drop", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Show", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("ShowByID", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Describe", func(t *testing.T) { - // TODO: fill me - }) -} diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go new file mode 100644 index 0000000000..898ee92414 --- /dev/null +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -0,0 +1,135 @@ +package testint + +import ( + "database/sql" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestInt_Secrets(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + cleanupSecret := func(id sdk.SchemaObjectIdentifier) { + t.Cleanup(func() { + err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) + assert.NoError(t, err) + }) + } + + assertSecret := func(t *testing.T, s *sdk.Secret, id sdk.SchemaObjectIdentifier, secretType, comment string) { + t.Helper() + assert.NotEmpty(t, s.CreatedOn) + assert.NotEmpty(t, s.DatabaseName) + assert.NotEmpty(t, s.SchemaName) + assert.NotEmpty(t, s.OwnerRoleType) + assert.NotEmpty(t, s.Owner) + assert.Equal(t, id.Name(), s.Name) + assert.Equal(t, secretType, s.SecretType) + assert.Equal(t, comment, s.Comment) + } + + type secretDetails struct { + Name string + Comment sql.NullString + SecretType string + Username sql.NullString + OauthAccessTokenExpiryTime sql.NullString + OauthRefreshTokenExpiryTime sql.NullString + OauthScopes sql.NullString + IntegrationName sql.NullString + } + + assertBasicAuth := func(details *sdk.SecretDetails, d secretDetails) { + assert.Equal(t, d.Name, details.Name) + assert.Equal(t, d.Comment, details.Comment) + assert.Equal(t, d.SecretType, details.SecretType) + assert.Equal(t, d.Username, details.Username) + assert.Equal(t, d.OauthAccessTokenExpiryTime, details.OauthAccessTokenExpiryTime) + assert.Equal(t, d.OauthRefreshTokenExpiryTime, details.OauthRefreshTokenExpiryTime) + assert.Equal(t, d.OauthScopes, details.OauthScopes) + assert.Equal(t, d.IntegrationName, details.IntegrationName) + } + _ = assertBasicAuth + + /* + createOAuthClientCredentialsFlowSecret := func(id sdk.SchemaObjectIdentifier, si sdk.CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest) { + //TODO + } + + createBasicAuthenticationSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) *sdk.Secret { + t.Helper() + + err := client.Secrets.CreateWithBasicAuthentication(ctx, sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password)) + require.NoError(t, err) + cleanupSecret(id) + + secret, err := client.Secrets.ShowByID(ctx, id) + return secret + } + */ + + t.Run("Create secret with OAuth Client Credentials Flow", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Create With Basic Authentication", func(t *testing.T) { + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + comment := random.Comment() + + request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, "foo", "foo").WithComment(comment).WithIfNotExists(true) + // todo: secrets = nil + err := client.Secrets.CreateWithBasicAuthentication(ctx, request) + require.NoError(t, err) + cleanupSecret(id) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + assertSecret(t, secret, id, "PASSWORD", comment) + /* + details, err := client.Secrets.Describe(ctx, id) + + assertBasicAuth(details, secretDetails{ + Name: id.FullyQualifiedName(), + Comment: sql.NullString{String: comment}, + SecretType: "PASSWORD", + Username: sql.NullString{String: "foo"}, + OauthAccessTokenExpiryTime: sql.NullString{}, + OauthRefreshTokenExpiryTime: sql.NullString{}, + OauthScopes: sql.NullString{}, + IntegrationName: sql.NullString{}, + }) + */ + }) + + t.Run("CreateWithGenericString", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Alter", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Drop", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Show", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("ShowByID", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Describe", func(t *testing.T) { + // TODO: fill me + }) +} From 4130618bec6f9fed1d72319164d0fb03d945e535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 17 Sep 2024 11:02:32 +0200 Subject: [PATCH 10/65] added secret to client.go and created secret_client --- pkg/acceptance/helpers/secret_client.go | 38 ++++++++++++++++++++++++- pkg/sdk/client.go | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index e800d6f59f..aa98ceafb1 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -1,6 +1,13 @@ package helpers -import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +import ( + "context" + "errors" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) type SecretClient struct { context *TestClientContext @@ -17,3 +24,32 @@ func NewSecretClient(context *TestClientContext, idsGenerator *IdsGenerator) *Se func (c *SecretClient) client() sdk.Secrets { return c.context.client.Secrets } + +func (c *SecretClient) CreateSecreteWithBasicFlow(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) (*sdk.Secret, func()) { + t.Helper() + ctx := context.Background() + request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password) + + err := c.client().CreateWithBasicAuthentication(ctx, request) + require.NoError(t, err) + + secret, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return secret, c.CleanupSecretFunc(t, id) +} + +func (c *SecretClient) CleanupSecretFunc(t *testing.T, id sdk.SchemaObjectIdentifier) func() { + t.Helper() + ctx := context.Background() + + return func() { + _, err := c.client().ShowByID(ctx, id) + if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { + return + } + + err = c.client().Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) + assert.NoError(t, err) + } +} diff --git a/pkg/sdk/client.go b/pkg/sdk/client.go index 5d1ddaa887..b5a76425c9 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -236,6 +236,7 @@ func (c *Client) initialize() { c.Roles = &roles{client: c} c.RowAccessPolicies = &rowAccessPolicies{client: c} c.Schemas = &schemas{client: c} + c.Secrets = &secrets{client: c} c.SecurityIntegrations = &securityIntegrations{client: c} c.Sequences = &sequences{client: c} c.SessionPolicies = &sessionPolicies{client: c} From fc85c660e43d8546f161ea8f86aa75c4295cb53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 17 Sep 2024 11:03:36 +0200 Subject: [PATCH 11/65] added like and in to showByID for secret --- pkg/sdk/secrets_impl_gen.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index dc31263fc8..b1c7418ae2 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -53,8 +53,8 @@ func (v *secrets) Show(ctx context.Context, request *ShowSecretRequest) ([]Secre } func (v *secrets) ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*Secret, error) { - // TODO: adjust request if e.g. LIKE is supported for the resource - secrets, err := v.Show(ctx, NewShowSecretRequest()) + request := NewShowSecretRequest().WithIn(In{Schema: id.SchemaId()}).WithLike(Like{String(id.Name())}) + secrets, err := v.Show(ctx, request) if err != nil { return nil, err } From aa645112a119676b5ab9ccc23c8f4f614717210f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 17 Sep 2024 11:03:55 +0200 Subject: [PATCH 12/65] init for integration tests --- .../testint/secrets_gen_integration_test.go | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 898ee92414..bb271f7b9e 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -13,23 +13,23 @@ func TestInt_Secrets(t *testing.T) { client := testClient(t) ctx := testContext(t) - cleanupSecret := func(id sdk.SchemaObjectIdentifier) { - t.Cleanup(func() { + cleanupSecret := func(id sdk.SchemaObjectIdentifier) func() { + return func() { err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) - assert.NoError(t, err) - }) + require.NoError(t, err) + } } - assertSecret := func(t *testing.T, s *sdk.Secret, id sdk.SchemaObjectIdentifier, secretType, comment string) { + assertSecret := func(t *testing.T, s *sdk.Secret, expectedId sdk.SchemaObjectIdentifier, expectedSecretType, expectedComment string) { t.Helper() + assert.Equal(t, expectedId.Name(), s.Name) + assert.Equal(t, expectedSecretType, s.SecretType) + assert.Equal(t, expectedComment, s.Comment) assert.NotEmpty(t, s.CreatedOn) assert.NotEmpty(t, s.DatabaseName) assert.NotEmpty(t, s.SchemaName) assert.NotEmpty(t, s.OwnerRoleType) assert.NotEmpty(t, s.Owner) - assert.Equal(t, id.Name(), s.Name) - assert.Equal(t, secretType, s.SecretType) - assert.Equal(t, comment, s.Comment) } type secretDetails struct { @@ -56,18 +56,21 @@ func TestInt_Secrets(t *testing.T) { _ = assertBasicAuth /* - createOAuthClientCredentialsFlowSecret := func(id sdk.SchemaObjectIdentifier, si sdk.CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest) { - //TODO - } + createOAuthClientCredentialsFlowSecret := func(id sdk.SchemaObjectIdentifier, si sdk.CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest) { + //TODO + } - createBasicAuthenticationSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) *sdk.Secret { + createBasicAuthenticationSecret := func(t *testing.T, username, password string) *sdk.Secret { t.Helper() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() err := client.Secrets.CreateWithBasicAuthentication(ctx, sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password)) require.NoError(t, err) - cleanupSecret(id) + t.Cleanup(cleanupSecret(id)) secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + return secret } */ @@ -83,32 +86,33 @@ func TestInt_Secrets(t *testing.T) { t.Run("Create With Basic Authentication", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifier() comment := random.Comment() - request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, "foo", "foo").WithComment(comment).WithIfNotExists(true) - // todo: secrets = nil + err := client.Secrets.CreateWithBasicAuthentication(ctx, request) require.NoError(t, err) - cleanupSecret(id) + t.Cleanup(cleanupSecret(id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) + assertSecret(t, secret, id, "PASSWORD", comment) - /* - details, err := client.Secrets.Describe(ctx, id) - - assertBasicAuth(details, secretDetails{ - Name: id.FullyQualifiedName(), - Comment: sql.NullString{String: comment}, - SecretType: "PASSWORD", - Username: sql.NullString{String: "foo"}, - OauthAccessTokenExpiryTime: sql.NullString{}, - OauthRefreshTokenExpiryTime: sql.NullString{}, - OauthScopes: sql.NullString{}, - IntegrationName: sql.NullString{}, - }) - */ }) + /* + details, err := client.Secrets.Describe(ctx, id) + + assertBasicAuth(details, secretDetails{ + Name: id.FullyQualifiedName(), + Comment: sql.NullString{String: comment}, + SecretType: "PASSWORD", + Username: sql.NullString{String: "foo"}, + OauthAccessTokenExpiryTime: sql.NullString{}, + OauthRefreshTokenExpiryTime: sql.NullString{}, + OauthScopes: sql.NullString{}, + IntegrationName: sql.NullString{}, + }) + */ + t.Run("CreateWithGenericString", func(t *testing.T) { // TODO: fill me }) From ab8bee1867cc707dfb8626c517bb3491173ca35d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 17 Sep 2024 11:04:05 +0200 Subject: [PATCH 13/65] secret_def changes --- pkg/sdk/secrets_def.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 9d3ae947c9..8a0909d988 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -15,10 +15,9 @@ var secretDbRow = g.DbStruct("secretDBRow"). Field("owner", "string"). Field("comment", "sql.NullString"). Field("secret_type", "string"). - Field("oauth_scopes", "sql.NullString"). // its a list tho + Field("oauth_scopes", "[]string"). // its a list tho Field("owner_role_type", "string") -// cannot name 'secret' due to clash in common_types.go var secret = g.PlainStruct("Secret"). Field("CreatedOn", "string"). Field("Name", "string"). @@ -27,7 +26,7 @@ var secret = g.PlainStruct("Secret"). Field("Owner", "string"). Field("Comment", "string"). Field("SecretType", "string"). - Field("OauthScopes", "sql.NullString"). + Field("OauthScopes", "[]string"). Field("OwnerRoleType", "string") var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). @@ -63,14 +62,12 @@ var secretSet = g.NewQueryStruct("SecretSet"). OptionalQueryStructField( "SetForOAuthClientCredentialsFlow", g.NewQueryStruct("SetForOAuthClientCredentialsFlow"). - //OptionalQueryStructField("OAuthScopes", secretsIntegrationScopes, g.ParameterOptions().MustParentheses().SQL("OAUTH_SCOPES")), ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses().Required()), g.KeywordOptions(), ). OptionalQueryStructField( "SetForOAuthAuthorizationFlow", g.NewQueryStruct("SetForOAuthAuthorizationFlow"). - //optional or just TextAssignment()?? OptionalTextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().SingleQuotes()). OptionalTextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().SingleQuotes()), g.KeywordOptions(), From 3541c70e05de487f452745c42b87a1c11e6a8f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 17 Sep 2024 16:12:43 +0200 Subject: [PATCH 14/65] tests for two methods of creating secret --- pkg/sdk/secrets_def.go | 18 +- pkg/sdk/secrets_impl_gen.go | 34 +++- .../testint/secrets_gen_integration_test.go | 158 +++++++++++++----- 3 files changed, 162 insertions(+), 48 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 8a0909d988..3bed2d18e1 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -15,7 +15,7 @@ var secretDbRow = g.DbStruct("secretDBRow"). Field("owner", "string"). Field("comment", "sql.NullString"). Field("secret_type", "string"). - Field("oauth_scopes", "[]string"). // its a list tho + Field("oauth_scopes", "sql.NullString"). // its a list tho Field("owner_role_type", "string") var secret = g.PlainStruct("Secret"). @@ -26,7 +26,7 @@ var secret = g.PlainStruct("Secret"). Field("Owner", "string"). Field("Comment", "string"). Field("SecretType", "string"). - Field("OauthScopes", "[]string"). + Field("OauthScopes", "sql.NullString"). Field("OwnerRoleType", "string") var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). @@ -87,7 +87,7 @@ var secretSet = g.NewQueryStruct("SecretSet"). ). WithValidation(g.ExactlyOneValueSet, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") -// TODO: unset doest work, need to use "SET COMMENT = NULL" +// TODO: UNSET doest work, need to use "SET COMMENT = NULL" var secretUnset = g.NewQueryStruct("SecretUnset"). //OptionalSQL("UNSET COMMENT") PredefinedQueryStructField("Comment", "*bool", g.KeywordOptions().SQL("SET COMMENT = NULL")) @@ -199,6 +199,18 @@ var SecretsDef = g.NewInterface( "https://docs.snowflake.com/en/sql-reference/sql/desc-secret", secretDetailsDbRow, secretDetails, + + /* + g.DescriptionMappingKindSlice, + "https://docs.snowflake.com/en/sql-reference/sql/desc-secret", + g.DbStruct("secretDetailsRow"). + Field("property", "string"). + Field("value", "sql.NullString"), + g.PlainStruct("SecretDetails"). + Field("Property", "string"). + Field("Value", "string"), + */ + g.NewQueryStruct("DescribeSecret"). Describe(). SQL("SECRET"). diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index b1c7418ae2..1386955543 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -196,8 +196,22 @@ func (r *ShowSecretRequest) toOpts() *ShowSecretOptions { } func (r secretDBRow) convert() *Secret { - // TODO: Mapping - return &Secret{} + s := &Secret{ + CreatedOn: r.CreatedOn, + Name: r.Name, + SchemaName: r.SchemaName, + DatabaseName: r.DatabaseName, + Owner: r.Owner, + SecretType: r.SecretType, + OwnerRoleType: r.OwnerRoleType, + } + if r.Comment.Valid { + s.Comment = r.Comment.String + } + if r.OauthScopes.Valid { + s.OauthScopes = r.OauthScopes + } + return s } func (r *DescribeSecretRequest) toOpts() *DescribeSecretOptions { @@ -208,6 +222,18 @@ func (r *DescribeSecretRequest) toOpts() *DescribeSecretOptions { } func (r secretDetailsDBRow) convert() *SecretDetails { - // TODO: Mapping - return &SecretDetails{} + return &SecretDetails{ + CreatedOn: r.CreatedOn, + Name: r.Name, + SchemaName: r.SchemaName, + DatabaseName: r.DatabaseName, + Owner: r.Owner, + Comment: r.Comment, + SecretType: r.SecretType, + Username: r.Username, + OauthAccessTokenExpiryTime: r.OauthAccessTokenExpiryTime, + OauthRefreshTokenExpiryTime: r.OauthRefreshTokenExpiryTime, + OauthScopes: r.OauthScopes, + IntegrationName: r.IntegrationName, + } } diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index bb271f7b9e..696cdf2a2c 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -12,6 +12,20 @@ import ( func TestInt_Secrets(t *testing.T) { client := testClient(t) ctx := testContext(t) + integrationId := testClientHelper().Ids.RandomAccountObjectIdentifier() + + cleanupIntegration := func(t *testing.T, integrationId sdk.AccountObjectIdentifier) func() { + return func() { + err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(integrationId).WithIfExists(true)) + require.NoError(t, err) + } + } + + integrationRequeset := sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{"foo"}, {"bar"}}) + err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow(ctx, integrationRequeset) + require.NoError(t, err) + t.Cleanup(cleanupIntegration(t, integrationId)) cleanupSecret := func(id sdk.SchemaObjectIdentifier) func() { return func() { @@ -32,6 +46,40 @@ func TestInt_Secrets(t *testing.T) { assert.NotEmpty(t, s.Owner) } + createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integration sdk.AccountObjectIdentifier, scopes []sdk.SecurityIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + t.Helper() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integration, scopes) + if with != nil { + with(request) + } + err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupSecret(id)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + return secret, id + } + + createSecretWithBasicAuthorization := func(t *testing.T, username, password string, with func(*sdk.CreateWithBasicAuthenticationSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + t.Helper() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password) + if with != nil { + with(request) + } + err := client.Secrets.CreateWithBasicAuthentication(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupSecret(id)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + return secret, id + } + type secretDetails struct { Name string Comment sql.NullString @@ -43,75 +91,103 @@ func TestInt_Secrets(t *testing.T) { IntegrationName sql.NullString } - assertBasicAuth := func(details *sdk.SecretDetails, d secretDetails) { - assert.Equal(t, d.Name, details.Name) - assert.Equal(t, d.Comment, details.Comment) - assert.Equal(t, d.SecretType, details.SecretType) - assert.Equal(t, d.Username, details.Username) - assert.Equal(t, d.OauthAccessTokenExpiryTime, details.OauthAccessTokenExpiryTime) - assert.Equal(t, d.OauthRefreshTokenExpiryTime, details.OauthRefreshTokenExpiryTime) - assert.Equal(t, d.OauthScopes, details.OauthScopes) - assert.Equal(t, d.IntegrationName, details.IntegrationName) + assertSecretDetails := func(actual *sdk.SecretDetails, expected secretDetails) { + assert.Equal(t, expected.Name, actual.Name) + assert.Equal(t, expected.Comment, actual.Comment) + assert.Equal(t, expected.SecretType, actual.SecretType) + assert.Equal(t, expected.Username, actual.Username) + assert.Equal(t, expected.OauthAccessTokenExpiryTime, actual.OauthAccessTokenExpiryTime) + assert.Equal(t, expected.OauthRefreshTokenExpiryTime, actual.OauthRefreshTokenExpiryTime) + assert.Equal(t, expected.OauthScopes, actual.OauthScopes) + assert.Equal(t, expected.IntegrationName, actual.IntegrationName) } - _ = assertBasicAuth - /* - createOAuthClientCredentialsFlowSecret := func(id sdk.SchemaObjectIdentifier, si sdk.CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest) { - //TODO - } + t.Run("Create secret with OAuth Client Credentials Flow", func(t *testing.T) { + scopes := []sdk.SecurityIntegrationScope{{"foo"}, {"bar"}} + secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, scopes, func(req *sdk.CreateWithOAuthClientCredentialsFlowSecretRequest) { + req.WithComment("a"). + WithIfNotExists(true) + }) + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + Comment: sql.NullString{String: "a", Valid: true}, + SecretType: "OAUTH2", + OauthScopes: sql.NullString{String: "[foo, bar]", Valid: true}, + IntegrationName: sql.NullString{String: integrationId.Name(), Valid: true}, + }) - createBasicAuthenticationSecret := func(t *testing.T, username, password string) *sdk.Secret { - t.Helper() - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + assertSecret(t, secret, id, "OAUTH2", "a") + }) - err := client.Secrets.CreateWithBasicAuthentication(ctx, sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password)) - require.NoError(t, err) - t.Cleanup(cleanupSecret(id)) + // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret should inherit security_integration scopes, but it does not + t.Run("CreateSecretWithOAuthClientCredentialsFlow: No Scopes Specified", func(t *testing.T) { + secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{}, nil) - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) - return secret - } - */ + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + OauthScopes: sql.NullString{}, + IntegrationName: sql.NullString{String: integrationId.Name(), Valid: true}, + }) - t.Run("Create secret with OAuth Client Credentials Flow", func(t *testing.T) { - // TODO: fill me + assertSecret(t, secret, id, "OAUTH2", "") }) t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { // TODO: fill me }) - t.Run("Create With Basic Authentication", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + t.Run("CreateWithBasicAuthentication", func(t *testing.T) { comment := random.Comment() - request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, "foo", "foo").WithComment(comment).WithIfNotExists(true) - - err := client.Secrets.CreateWithBasicAuthentication(ctx, request) + secret, id := createSecretWithBasicAuthorization(t, "foo", "foo", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { + req.WithComment(comment). + WithIfNotExists(true) + }) + details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) - t.Cleanup(cleanupSecret(id)) - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + Comment: sql.NullString{String: comment, Valid: true}, + SecretType: "PASSWORD", + Username: sql.NullString{String: "foo", Valid: true}, + OauthAccessTokenExpiryTime: sql.NullString{}, + OauthRefreshTokenExpiryTime: sql.NullString{}, + OauthScopes: sql.NullString{}, + IntegrationName: sql.NullString{}, + }) assertSecret(t, secret, id, "PASSWORD", comment) }) - /* + t.Run("CreateWithBasicAuthentication: Empty Username and Password", func(t *testing.T) { + comment := random.Comment() + secret, id := createSecretWithBasicAuthorization(t, "", "", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { + req.WithComment(comment). + WithIfNotExists(true) + }) details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) - assertBasicAuth(details, secretDetails{ - Name: id.FullyQualifiedName(), - Comment: sql.NullString{String: comment}, + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + Comment: sql.NullString{String: comment, Valid: true}, SecretType: "PASSWORD", - Username: sql.NullString{String: "foo"}, + Username: sql.NullString{String: "", Valid: true}, OauthAccessTokenExpiryTime: sql.NullString{}, OauthRefreshTokenExpiryTime: sql.NullString{}, OauthScopes: sql.NullString{}, IntegrationName: sql.NullString{}, }) - */ + + assertSecret(t, secret, id, "PASSWORD", comment) + }) t.Run("CreateWithGenericString", func(t *testing.T) { // TODO: fill me From 180a7ce03118e63988f826da1a835a2ff264566a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 18 Sep 2024 10:45:42 +0200 Subject: [PATCH 15/65] changed refresh token expiry time from string to time.Time --- pkg/sdk/secrets_def.go | 8 +- .../testint/secrets_gen_integration_test.go | 124 ++++++++++++------ 2 files changed, 89 insertions(+), 43 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 3bed2d18e1..9ecfb0bf46 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -38,8 +38,8 @@ var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). Field("comment", "sql.NullString"). Field("secret_type", "string"). Field("username", "sql.NullString"). - Field("oauth_access_token_expiry_time", "sql.NullString"). - Field("oauth_refresh_token_expiry_time", "sql.NullString"). + Time("oauth_access_token_expiry_time"). + Time("oauth_refresh_token_expiry_time"). Field("oauth_scopes", "sql.NullString"). // its a list tho Field("integration_name", "sql.NullString") @@ -52,8 +52,8 @@ var secretDetails = g.PlainStruct("SecretDetails"). Field("Comment", "sql.NullString"). Field("SecretType", "string"). Field("Username", "sql.NullString"). - Field("OauthAccessTokenExpiryTime", "sql.NullString"). - Field("OauthRefreshTokenExpiryTime", "sql.NullString"). + Time("OauthAccessTokenExpiryTime"). + Time("OauthRefreshTokenExpiryTime"). Field("OauthScopes", "sql.NullString"). // its a list tho Field("IntegrationName", "sql.NullString") diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 696cdf2a2c..d86270636e 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -1,12 +1,12 @@ package testint import ( - "database/sql" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" + "time" ) func TestInt_Secrets(t *testing.T) { @@ -21,9 +21,9 @@ func TestInt_Secrets(t *testing.T) { } } - integrationRequeset := sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + integrationRequest := sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). WithOauthAllowedScopes([]sdk.AllowedScope{{"foo"}, {"bar"}}) - err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow(ctx, integrationRequeset) + err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow(ctx, integrationRequest) require.NoError(t, err) t.Cleanup(cleanupIntegration(t, integrationId)) @@ -46,10 +46,10 @@ func TestInt_Secrets(t *testing.T) { assert.NotEmpty(t, s.Owner) } - createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integration sdk.AccountObjectIdentifier, scopes []sdk.SecurityIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, scopes []sdk.SecurityIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integration, scopes) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, scopes) if with != nil { with(request) } @@ -63,6 +63,23 @@ func TestInt_Secrets(t *testing.T) { return secret, id } + createSecretWithOAuthAuthorizationCodeFlow := func(t *testing.T, refreshToken, refreshTokenExpiryTime string, integrationId sdk.AccountObjectIdentifier, with func(*sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + t.Helper() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, integrationId) + if with != nil { + with(request) + } + err := client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupSecret(id)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + return secret, id + } + createSecretWithBasicAuthorization := func(t *testing.T, username, password string, with func(*sdk.CreateWithBasicAuthenticationSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -82,24 +99,24 @@ func TestInt_Secrets(t *testing.T) { type secretDetails struct { Name string - Comment sql.NullString + Comment string SecretType string - Username sql.NullString - OauthAccessTokenExpiryTime sql.NullString - OauthRefreshTokenExpiryTime sql.NullString - OauthScopes sql.NullString - IntegrationName sql.NullString + Username string + OauthAccessTokenExpiryTime time.Time + OauthRefreshTokenExpiryTime time.Time + OauthScopes string + IntegrationName string } assertSecretDetails := func(actual *sdk.SecretDetails, expected secretDetails) { assert.Equal(t, expected.Name, actual.Name) - assert.Equal(t, expected.Comment, actual.Comment) + assert.Equal(t, expected.Comment, actual.Comment.String) assert.Equal(t, expected.SecretType, actual.SecretType) - assert.Equal(t, expected.Username, actual.Username) - assert.Equal(t, expected.OauthAccessTokenExpiryTime, actual.OauthAccessTokenExpiryTime) - assert.Equal(t, expected.OauthRefreshTokenExpiryTime, actual.OauthRefreshTokenExpiryTime) - assert.Equal(t, expected.OauthScopes, actual.OauthScopes) - assert.Equal(t, expected.IntegrationName, actual.IntegrationName) + assert.Equal(t, expected.Username, actual.Username.String) + assert.Equal(t, expected.OauthAccessTokenExpiryTime.String(), actual.OauthAccessTokenExpiryTime.String) + assert.Equal(t, expected.OauthRefreshTokenExpiryTime.String(), actual.OauthRefreshTokenExpiryTime.String) + assert.Equal(t, expected.OauthScopes, actual.OauthScopes.String) + assert.Equal(t, expected.IntegrationName, actual.IntegrationName.String) } t.Run("Create secret with OAuth Client Credentials Flow", func(t *testing.T) { @@ -113,10 +130,10 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - Comment: sql.NullString{String: "a", Valid: true}, + Comment: "a", SecretType: "OAUTH2", - OauthScopes: sql.NullString{String: "[foo, bar]", Valid: true}, - IntegrationName: sql.NullString{String: integrationId.Name(), Valid: true}, + OauthScopes: "[foo, bar]", + IntegrationName: integrationId.Name(), }) assertSecret(t, secret, id, "OAUTH2", "a") @@ -132,17 +149,55 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), SecretType: "OAUTH2", - OauthScopes: sql.NullString{}, - IntegrationName: sql.NullString{String: integrationId.Name(), Valid: true}, + IntegrationName: integrationId.Name(), }) assertSecret(t, secret, id, "OAUTH2", "") }) t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { - // TODO: fill me + secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, "foo", "2024-09-20", integrationId, func(req *sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest) { + req.WithComment("a"). + WithIfNotExists(true) + }) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + Comment: "a", + OauthRefreshTokenExpiryTime: time.Time{}, //"2024-09-20", + IntegrationName: integrationId.Name(), + }) + + assertSecret(t, secret, id, "OAUTH2", "") }) + /* + //require.EqualError(t, err, "Invalid data/time format string") + t.Run("CreateWithOAuthAuthorizationCodeFlow: Invalid date/time format string", func(t *testing.T) { + secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, func(req *sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest) { + req.WithComment("a"). + WithIfNotExists(true) + }) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + OauthAccessTokenExpiryTime: "foo", + OauthRefreshTokenExpiryTime: "foo", + IntegrationName: integrationId.Name(), + }) + + assertSecret(t, secret, id, "OAUTH2", "") + }) + */ + t.Run("CreateWithBasicAuthentication", func(t *testing.T) { comment := random.Comment() secret, id := createSecretWithBasicAuthorization(t, "foo", "foo", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { @@ -153,14 +208,10 @@ func TestInt_Secrets(t *testing.T) { require.NoError(t, err) assertSecretDetails(details, secretDetails{ - Name: id.Name(), - Comment: sql.NullString{String: comment, Valid: true}, - SecretType: "PASSWORD", - Username: sql.NullString{String: "foo", Valid: true}, - OauthAccessTokenExpiryTime: sql.NullString{}, - OauthRefreshTokenExpiryTime: sql.NullString{}, - OauthScopes: sql.NullString{}, - IntegrationName: sql.NullString{}, + Name: id.Name(), + Comment: comment, + SecretType: "PASSWORD", + Username: "foo", }) assertSecret(t, secret, id, "PASSWORD", comment) @@ -176,14 +227,9 @@ func TestInt_Secrets(t *testing.T) { require.NoError(t, err) assertSecretDetails(details, secretDetails{ - Name: id.Name(), - Comment: sql.NullString{String: comment, Valid: true}, - SecretType: "PASSWORD", - Username: sql.NullString{String: "", Valid: true}, - OauthAccessTokenExpiryTime: sql.NullString{}, - OauthRefreshTokenExpiryTime: sql.NullString{}, - OauthScopes: sql.NullString{}, - IntegrationName: sql.NullString{}, + Name: id.Name(), + Comment: comment, + SecretType: "PASSWORD", }) assertSecret(t, secret, id, "PASSWORD", comment) From 7a9ad924cba2e5d64357a4d33302d53f9bbc2a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 19 Sep 2024 10:26:08 +0200 Subject: [PATCH 16/65] added remaining integration tests --- pkg/sdk/secrets_def.go | 25 +- pkg/sdk/secrets_gen.go | 9 +- .../testint/secrets_gen_integration_test.go | 565 +++++++++++++++--- 3 files changed, 505 insertions(+), 94 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 9ecfb0bf46..ddd1fa95fd 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -15,7 +15,7 @@ var secretDbRow = g.DbStruct("secretDBRow"). Field("owner", "string"). Field("comment", "sql.NullString"). Field("secret_type", "string"). - Field("oauth_scopes", "sql.NullString"). // its a list tho + Field("oauth_scopes", "sql.NullString"). Field("owner_role_type", "string") var secret = g.PlainStruct("Secret"). @@ -40,7 +40,7 @@ var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). Field("username", "sql.NullString"). Time("oauth_access_token_expiry_time"). Time("oauth_refresh_token_expiry_time"). - Field("oauth_scopes", "sql.NullString"). // its a list tho + Field("oauth_scopes", "sql.NullString"). Field("integration_name", "sql.NullString") var secretDetails = g.PlainStruct("SecretDetails"). @@ -52,9 +52,9 @@ var secretDetails = g.PlainStruct("SecretDetails"). Field("Comment", "sql.NullString"). Field("SecretType", "string"). Field("Username", "sql.NullString"). - Time("OauthAccessTokenExpiryTime"). - Time("OauthRefreshTokenExpiryTime"). - Field("OauthScopes", "sql.NullString"). // its a list tho + Field("OauthAccessTokenExpiryTime", "*time.Time"). + Field("OauthRefreshTokenExpiryTime", "*time.Time"). + Field("OauthScopes", "sql.NullString"). Field("IntegrationName", "sql.NullString") var secretSet = g.NewQueryStruct("SecretSet"). @@ -87,9 +87,8 @@ var secretSet = g.NewQueryStruct("SecretSet"). ). WithValidation(g.ExactlyOneValueSet, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") -// TODO: UNSET doest work, need to use "SET COMMENT = NULL" +// UNSET doest work, need to use "SET COMMENT = NULL" var secretUnset = g.NewQueryStruct("SecretUnset"). - //OptionalSQL("UNSET COMMENT") PredefinedQueryStructField("Comment", "*bool", g.KeywordOptions().SQL("SET COMMENT = NULL")) var SecretsDef = g.NewInterface( @@ -199,18 +198,6 @@ var SecretsDef = g.NewInterface( "https://docs.snowflake.com/en/sql-reference/sql/desc-secret", secretDetailsDbRow, secretDetails, - - /* - g.DescriptionMappingKindSlice, - "https://docs.snowflake.com/en/sql-reference/sql/desc-secret", - g.DbStruct("secretDetailsRow"). - Field("property", "string"). - Field("value", "sql.NullString"), - g.PlainStruct("SecretDetails"). - Field("Property", "string"). - Field("Value", "string"), - */ - g.NewQueryStruct("DescribeSecret"). Describe(). SQL("SECRET"). diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index 6ff0313c1e..b90ac53ea3 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -3,6 +3,7 @@ package sdk import ( "context" "database/sql" + "time" ) type Secrets interface { @@ -159,8 +160,8 @@ type secretDetailsDBRow struct { Comment sql.NullString `db:"comment"` SecretType string `db:"secret_type"` Username sql.NullString `db:"username"` - OauthAccessTokenExpiryTime sql.NullString `db:"oauth_access_token_expiry_time"` - OauthRefreshTokenExpiryTime sql.NullString `db:"oauth_refresh_token_expiry_time"` + OauthAccessTokenExpiryTime *time.Time `db:"oauth_access_token_expiry_time"` + OauthRefreshTokenExpiryTime *time.Time `db:"oauth_refresh_token_expiry_time"` OauthScopes sql.NullString `db:"oauth_scopes"` IntegrationName sql.NullString `db:"integration_name"` } @@ -173,8 +174,8 @@ type SecretDetails struct { Comment sql.NullString SecretType string Username sql.NullString - OauthAccessTokenExpiryTime sql.NullString - OauthRefreshTokenExpiryTime sql.NullString + OauthAccessTokenExpiryTime *time.Time + OauthRefreshTokenExpiryTime *time.Time OauthScopes sql.NullString IntegrationName sql.NullString } diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index d86270636e..ce1070e654 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -1,6 +1,7 @@ package testint import ( + "database/sql" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" @@ -9,11 +10,27 @@ import ( "time" ) +const Day = 24 * time.Hour + func TestInt_Secrets(t *testing.T) { client := testClient(t) ctx := testContext(t) + integrationId := testClientHelper().Ids.RandomAccountObjectIdentifier() + refreshTokenExpiryTime := time.Now().Add(Day).Format(time.DateOnly) + + stringDateToSnowflakeTimeFormat := func(inputLayout, date string) *time.Time { + parsedTime, err := time.Parse(inputLayout, date) + require.NoError(t, err) + + loc, err := time.LoadLocation("America/Los_Angeles") + require.NoError(t, err) + + adjustedTime := parsedTime.In(loc) + return &adjustedTime + } + cleanupIntegration := func(t *testing.T, integrationId sdk.AccountObjectIdentifier) func() { return func() { err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(integrationId).WithIfExists(true)) @@ -21,31 +38,22 @@ func TestInt_Secrets(t *testing.T) { } } - integrationRequest := sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). - WithOauthAllowedScopes([]sdk.AllowedScope{{"foo"}, {"bar"}}) - err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow(ctx, integrationRequest) + err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow( + ctx, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{"foo"}, {"bar"}}), + ) require.NoError(t, err) t.Cleanup(cleanupIntegration(t, integrationId)) - cleanupSecret := func(id sdk.SchemaObjectIdentifier) func() { + cleanupSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { + t.Helper() return func() { err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) require.NoError(t, err) } } - assertSecret := func(t *testing.T, s *sdk.Secret, expectedId sdk.SchemaObjectIdentifier, expectedSecretType, expectedComment string) { - t.Helper() - assert.Equal(t, expectedId.Name(), s.Name) - assert.Equal(t, expectedSecretType, s.SecretType) - assert.Equal(t, expectedComment, s.Comment) - assert.NotEmpty(t, s.CreatedOn) - assert.NotEmpty(t, s.DatabaseName) - assert.NotEmpty(t, s.SchemaName) - assert.NotEmpty(t, s.OwnerRoleType) - assert.NotEmpty(t, s.Owner) - } - createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, scopes []sdk.SecurityIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -55,7 +63,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(id)) + t.Cleanup(cleanupSecret(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -63,7 +71,7 @@ func TestInt_Secrets(t *testing.T) { return secret, id } - createSecretWithOAuthAuthorizationCodeFlow := func(t *testing.T, refreshToken, refreshTokenExpiryTime string, integrationId sdk.AccountObjectIdentifier, with func(*sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + createSecretWithOAuthAuthorizationCodeFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, refreshToken, refreshTokenExpiryTime string, with func(*sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, integrationId) @@ -72,7 +80,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(id)) + t.Cleanup(cleanupSecret(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -80,7 +88,7 @@ func TestInt_Secrets(t *testing.T) { return secret, id } - createSecretWithBasicAuthorization := func(t *testing.T, username, password string, with func(*sdk.CreateWithBasicAuthenticationSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + createSecretWithBasicAuthentication := func(t *testing.T, username, password string, with func(*sdk.CreateWithBasicAuthenticationSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password) @@ -89,7 +97,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithBasicAuthentication(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(id)) + t.Cleanup(cleanupSecret(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -97,13 +105,42 @@ func TestInt_Secrets(t *testing.T) { return secret, id } + createSecretWithGenericString := func(t *testing.T, secretString string, with func(options *sdk.CreateWithGenericStringSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + t.Helper() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) + if with != nil { + with(request) + } + err := client.Secrets.CreateWithGenericString(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupSecret(t, id)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + return secret, id + } + + assertSecret := func(t *testing.T, s *sdk.Secret, expectedId sdk.SchemaObjectIdentifier, expectedSecretType, expectedComment string) { + t.Helper() + assert.Equal(t, expectedId.Name(), s.Name) + assert.Equal(t, expectedSecretType, s.SecretType) + assert.Equal(t, expectedComment, s.Comment) + assert.NotEmpty(t, s.CreatedOn) + assert.NotEmpty(t, s.DatabaseName) + assert.NotEmpty(t, s.SchemaName) + assert.NotEmpty(t, s.OwnerRoleType) + assert.NotEmpty(t, s.Owner) + } + type secretDetails struct { Name string Comment string SecretType string Username string - OauthAccessTokenExpiryTime time.Time - OauthRefreshTokenExpiryTime time.Time + OauthAccessTokenExpiryTime *time.Time + OauthRefreshTokenExpiryTime *time.Time OauthScopes string IntegrationName string } @@ -113,14 +150,15 @@ func TestInt_Secrets(t *testing.T) { assert.Equal(t, expected.Comment, actual.Comment.String) assert.Equal(t, expected.SecretType, actual.SecretType) assert.Equal(t, expected.Username, actual.Username.String) - assert.Equal(t, expected.OauthAccessTokenExpiryTime.String(), actual.OauthAccessTokenExpiryTime.String) - assert.Equal(t, expected.OauthRefreshTokenExpiryTime.String(), actual.OauthRefreshTokenExpiryTime.String) + assert.Equal(t, expected.OauthAccessTokenExpiryTime, actual.OauthAccessTokenExpiryTime) + assert.Equal(t, expected.OauthRefreshTokenExpiryTime, actual.OauthRefreshTokenExpiryTime) assert.Equal(t, expected.OauthScopes, actual.OauthScopes.String) assert.Equal(t, expected.IntegrationName, actual.IntegrationName.String) } - t.Run("Create secret with OAuth Client Credentials Flow", func(t *testing.T) { + t.Run("Create: secretWithOAuthClientCredentialsFlow", func(t *testing.T) { scopes := []sdk.SecurityIntegrationScope{{"foo"}, {"bar"}} + secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, scopes, func(req *sdk.CreateWithOAuthClientCredentialsFlowSecretRequest) { req.WithComment("a"). WithIfNotExists(true) @@ -139,24 +177,39 @@ func TestInt_Secrets(t *testing.T) { assertSecret(t, secret, id, "OAUTH2", "a") }) - // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret should inherit security_integration scopes, but it does not - t.Run("CreateSecretWithOAuthClientCredentialsFlow: No Scopes Specified", func(t *testing.T) { + // It is possible to create secret without specifying both refresh token properties and scopes + // Scopes are not being inherited from the security_integration what is tested further + t.Run("Create: secretWithOAuth - minimal, without token and scopes", func(t *testing.T) { secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{}, nil) - details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) assertSecretDetails(details, secretDetails{ - Name: id.Name(), - SecretType: "OAUTH2", - IntegrationName: integrationId.Name(), + Name: id.Name(), + SecretType: "OAUTH2", + OauthScopes: "", + OauthAccessTokenExpiryTime: nil, + OauthRefreshTokenExpiryTime: nil, + IntegrationName: integrationId.Name(), }) assertSecret(t, secret, id, "OAUTH2", "") }) - t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { - secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, "foo", "2024-09-20", integrationId, func(req *sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest) { + // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret should inherit security_integration scopes, but it does not do so + t.Run("Create: SecretWithOAuthClientCredentialsFlow - No Scopes Specified", func(t *testing.T) { + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{}, nil) + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + securityIntegrationProperties, _ := client.SecurityIntegrations.Describe(ctx, integrationId) + assert.Contains(t, securityIntegrationProperties, sdk.SecurityIntegrationProperty{Name: "OAUTH_ALLOWED_SCOPES", Type: "List", Value: "[foo, bar]", Default: "[]"}) + + assert.NotEqual(t, details.OauthScopes, securityIntegrationProperties) + }) + + t.Run("Create: SecretWithOAuthAuthorizationCodeFlow", func(t *testing.T) { + secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, func(req *sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest) { req.WithComment("a"). WithIfNotExists(true) }) @@ -168,39 +221,17 @@ func TestInt_Secrets(t *testing.T) { Name: id.Name(), SecretType: "OAUTH2", Comment: "a", - OauthRefreshTokenExpiryTime: time.Time{}, //"2024-09-20", + OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, refreshTokenExpiryTime), IntegrationName: integrationId.Name(), }) - assertSecret(t, secret, id, "OAUTH2", "") + assertSecret(t, secret, id, "OAUTH2", "a") }) - /* - //require.EqualError(t, err, "Invalid data/time format string") - t.Run("CreateWithOAuthAuthorizationCodeFlow: Invalid date/time format string", func(t *testing.T) { - secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, func(req *sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest) { - req.WithComment("a"). - WithIfNotExists(true) - }) - - details, err := client.Secrets.Describe(ctx, id) - require.NoError(t, err) - - assertSecretDetails(details, secretDetails{ - Name: id.Name(), - SecretType: "OAUTH2", - OauthAccessTokenExpiryTime: "foo", - OauthRefreshTokenExpiryTime: "foo", - IntegrationName: integrationId.Name(), - }) - - assertSecret(t, secret, id, "OAUTH2", "") - }) - */ - - t.Run("CreateWithBasicAuthentication", func(t *testing.T) { + t.Run("Create: WithBasicAuthentication", func(t *testing.T) { comment := random.Comment() - secret, id := createSecretWithBasicAuthorization(t, "foo", "foo", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { + + secret, id := createSecretWithBasicAuthentication(t, "foo", "foo", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { req.WithComment(comment). WithIfNotExists(true) }) @@ -217,9 +248,9 @@ func TestInt_Secrets(t *testing.T) { assertSecret(t, secret, id, "PASSWORD", comment) }) - t.Run("CreateWithBasicAuthentication: Empty Username and Password", func(t *testing.T) { + t.Run("Create: WithBasicAuthentication - Empty Username and Password", func(t *testing.T) { comment := random.Comment() - secret, id := createSecretWithBasicAuthorization(t, "", "", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { + secret, id := createSecretWithBasicAuthentication(t, "", "", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { req.WithComment(comment). WithIfNotExists(true) }) @@ -230,32 +261,424 @@ func TestInt_Secrets(t *testing.T) { Name: id.Name(), Comment: comment, SecretType: "PASSWORD", + Username: "", }) assertSecret(t, secret, id, "PASSWORD", comment) }) - t.Run("CreateWithGenericString", func(t *testing.T) { - // TODO: fill me + t.Run("Create: WithGenericString", func(t *testing.T) { + comment := random.Comment() + secret, id := createSecretWithGenericString(t, "foo", func(req *sdk.CreateWithGenericStringSecretRequest) { + req.WithComment(comment). + WithIfNotExists(true) + }) + + assertSecret(t, secret, id, "GENERIC_STRING", comment) + }) + + t.Run("Create: WithGenericString - empty secret_string", func(t *testing.T) { + secret, id := createSecretWithGenericString(t, "", nil) + require.NoError(t, err) + + assertSecret(t, secret, id, "GENERIC_STRING", "") + }) + + t.Run("Alter: SecretWithOAuthClientCredentials", func(t *testing.T) { + comment := random.Comment() + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + setRequest := sdk.NewAlterSecretRequest(id). + WithSet( + *sdk.NewSecretSetRequest(). + WithComment(comment). + WithSetForOAuthClientCredentialsFlow( + *sdk.NewSetForOAuthClientCredentialsFlowRequest( + []sdk.SecurityIntegrationScope{{"foo"}, {"bar"}}, + ), + ), + ) + err := client.Secrets.Alter(ctx, setRequest) + require.NoError(t, err) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + Comment: comment, + OauthScopes: "[foo, bar]", + IntegrationName: integrationId.Name(), + }) + + unsetRequest := sdk.NewAlterSecretRequest(id). + WithUnset( + *sdk.NewSecretUnsetRequest(). + WithComment(true), + ) + err = client.Secrets.Alter(ctx, unsetRequest) + require.NoError(t, err) + + details, err = client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assert.Equal(t, details.Comment, sql.NullString{"", false}) + }) + + t.Run("Alter: SecretWithOAuthAuthorizationCode", func(t *testing.T) { + comment := random.Comment() + alteredRefreshTokenExpiryTime := time.Now().Add(4 * Day).Format(time.DateOnly) + + _, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + setRequest := sdk.NewAlterSecretRequest(id). + WithSet( + *sdk.NewSecretSetRequest(). + WithComment(comment). + WithSetForOAuthAuthorizationFlow( + *sdk.NewSetForOAuthAuthorizationFlowRequest(). + WithOauthRefreshToken("bar"). + WithOauthRefreshTokenExpiryTime(alteredRefreshTokenExpiryTime), + ), + ) + err := client.Secrets.Alter(ctx, setRequest) + require.NoError(t, err) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + Comment: comment, + OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, alteredRefreshTokenExpiryTime), + IntegrationName: integrationId.Name(), + }) + + unsetRequest := sdk.NewAlterSecretRequest(id). + WithUnset( + *sdk.NewSecretUnsetRequest(). + WithComment(true), + ) + err = client.Secrets.Alter(ctx, unsetRequest) + require.NoError(t, err) + + details, err = client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assert.Equal(t, details.Comment, sql.NullString{"", false}) + }) + + t.Run("Alter: SecretWithBasicAuthorization", func(t *testing.T) { + comment := random.Comment() + + _, id := createSecretWithBasicAuthentication(t, "foo", "foo", nil) + setRequest := sdk.NewAlterSecretRequest(id). + WithSet( + *sdk.NewSecretSetRequest(). + WithComment(comment). + WithSetForBasicAuthentication( + *sdk.NewSetForBasicAuthenticationRequest(). + WithUsername("bar"). + WithPassword("bar"), + ), + ) + err := client.Secrets.Alter(ctx, setRequest) + require.NoError(t, err) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + // Cannot check password since show and describe on secret do not have access to password property + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "PASSWORD", + Comment: comment, + Username: "bar", + }) + + unsetRequest := sdk.NewAlterSecretRequest(id). + WithUnset( + *sdk.NewSecretUnsetRequest(). + WithComment(true), + ) + err = client.Secrets.Alter(ctx, unsetRequest) + require.NoError(t, err) + + details, err = client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assert.Equal(t, details.Comment, sql.NullString{"", false}) }) - t.Run("Alter", func(t *testing.T) { - // TODO: fill me + t.Run("Alter: SecretWithGenericString", func(t *testing.T) { + comment := random.Comment() + _, id := createSecretWithGenericString(t, "foo", nil) + setRequest := sdk.NewAlterSecretRequest(id). + WithSet( + *sdk.NewSecretSetRequest(). + WithComment(comment). + WithSetForGenericString( + *sdk.NewSetForGenericStringRequest(). + WithSecretString("bar"), + ), + ) + err := client.Secrets.Alter(ctx, setRequest) + require.NoError(t, err) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + /* + // Cannot check secret_string since show and describe on secret do not have access to secret_string property + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "PASSWORD", + Comment: comment, + }) + */ + + unsetRequest := sdk.NewAlterSecretRequest(id). + WithUnset( + *sdk.NewSecretUnsetRequest(). + WithComment(true), + ) + err = client.Secrets.Alter(ctx, unsetRequest) + require.NoError(t, err) + + details, err = client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assert.Equal(t, details.Comment, sql.NullString{"", false}) }) t.Run("Drop", func(t *testing.T) { - // TODO: fill me + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NotNil(t, secret) + require.NoError(t, err) + + err = client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id)) + require.NoError(t, err) + + secret, err = client.Secrets.ShowByID(ctx, id) + require.Nil(t, secret) + require.Error(t, err) + }) + + t.Run("Drop: non-existing", func(t *testing.T) { + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id)) + assert.ErrorIs(t, err, sdk.ErrObjectNotExistOrAuthorized) }) t.Run("Show", func(t *testing.T) { - // TODO: fill me + secretOAuthClientCredentials, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) + secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest()) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) + require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) + require.Contains(t, returnedSecrets, *secretBasicAuthentication) + require.Contains(t, returnedSecrets, *secretGenericString) + }) + + t.Run("Show: SecretWithOAuthClientCredentialsFlow with Like", func(t *testing.T) { + secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"bar"}}, nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ + Pattern: sdk.Pointer(id1.Name()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithOAuthAuthorization with Like", func(t *testing.T) { + secret1, id1 := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_1", refreshTokenExpiryTime, nil) + secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ + Pattern: sdk.Pointer(id1.Name()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithBasicAuthentication with Like", func(t *testing.T) { + secret1, id1 := createSecretWithBasicAuthentication(t, "foo_1", "bar_1", nil) + secret2, _ := createSecretWithBasicAuthentication(t, "foo_2", "bar_2", nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ + Pattern: sdk.Pointer(id1.Name()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithGenericString with Like", func(t *testing.T) { + secret1, id1 := createSecretWithGenericString(t, "foo_1", nil) + secret2, _ := createSecretWithGenericString(t, "foo_2", nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ + Pattern: sdk.Pointer(id1.Name()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithOAuthClientCredentialsFlow with In", func(t *testing.T) { + secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + }) + + t.Run("Show: SecretWithOAuthAuthorizationCodeFlow with In", func(t *testing.T) { + secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + }) + + t.Run("Show: with In", func(t *testing.T) { + secretOAuthClientCredentials, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) + secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) + require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) + require.Contains(t, returnedSecrets, *secretBasicAuthentication) + require.Contains(t, returnedSecrets, *secretGenericString) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) + require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) + require.Contains(t, returnedSecrets, *secretBasicAuthentication) + require.Contains(t, returnedSecrets, *secretGenericString) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) + require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) + require.Contains(t, returnedSecrets, *secretBasicAuthentication) + require.Contains(t, returnedSecrets, *secretGenericString) + }) + + t.Run("Show: SecretWithGenericString with In", func(t *testing.T) { + secret, id := createSecretWithGenericString(t, "foo", nil) + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) }) t.Run("ShowByID", func(t *testing.T) { - // TODO: fill me + _, id := createSecretWithGenericString(t, "foo", nil) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + assertSecret(t, secret, id, "GENERIC_STRING", "") }) t.Run("Describe", func(t *testing.T) { - // TODO: fill me + _, id := createSecretWithGenericString(t, "foo", func(req *sdk.CreateWithGenericStringSecretRequest) { + req.WithComment("Lorem ipsum") + }) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + Comment: "Lorem ipsum", + SecretType: "GENERIC_STRING", + }) + }) +} + +func TestInt_SecretsShowWithIn(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + cleanupSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { + t.Helper() + return func() { + err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) + require.NoError(t, err) + } + } + + createSecretWithGenericString := func(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) *sdk.Secret { + t.Helper() + request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) + err := client.Secrets.CreateWithGenericString(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupSecret(t, id)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + return secret + } + + t.Run("Show with In - same id in different schemas", func(t *testing.T) { + schema, schemaCleanup := testClientHelper().Schema.CreateSchema(t) + t.Cleanup(schemaCleanup) + + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + id2 := testClientHelper().Ids.NewSchemaObjectIdentifierInSchema(id1.Name(), schema.ID()) + + secret1 := createSecretWithGenericString(t, id1, "foo") + secret2 := createSecretWithGenericString(t, id2, "bar") + + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id1.SchemaId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id1.DatabaseId()})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.Contains(t, returnedSecrets, *secret2) }) + } From 0e9cf948be2be755b8992eeb1923f28649c36bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 19 Sep 2024 11:20:08 +0200 Subject: [PATCH 17/65] linter --- pkg/acceptance/helpers/secret_client.go | 3 ++- pkg/sdk/secrets_impl_gen.go | 10 ---------- pkg/sdk/testint/secrets_gen_integration_test.go | 6 +++--- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index aa98ceafb1..ab980051c3 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -3,10 +3,11 @@ package helpers import ( "context" "errors" + "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) type SecretClient struct { diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index 1386955543..7d548240ad 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -133,47 +133,37 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Set.SetForOAuthClientCredentialsFlow != nil { - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{ OauthScopes: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes, } - } if r.Set.SetForOAuthAuthorizationFlow != nil { - opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ OauthRefreshToken: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshToken, OauthRefreshTokenExpiryTime: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshTokenExpiryTime, } - } if r.Set.SetForBasicAuthentication != nil { - opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ Username: r.Set.SetForBasicAuthentication.Username, Password: r.Set.SetForBasicAuthentication.Password, } - } if r.Set.SetForGenericString != nil { - opts.Set.SetForGenericString = &SetForGenericString{ SecretString: r.Set.SetForGenericString.SecretString, } - } } if r.Unset != nil { - opts.Unset = &SecretUnset{ Comment: r.Unset.Comment, } - } return opts diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index ce1070e654..4ee38140d0 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -2,12 +2,13 @@ package testint import ( "database/sql" + "testing" + "time" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" - "time" ) const Day = 24 * time.Hour @@ -680,5 +681,4 @@ func TestInt_SecretsShowWithIn(t *testing.T) { require.Contains(t, returnedSecrets, *secret1) require.Contains(t, returnedSecrets, *secret2) }) - } From deadc651019c0f6016ed602914993184c4f1019a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 19 Sep 2024 11:48:05 +0200 Subject: [PATCH 18/65] linting --- pkg/sdk/secrets_impl_gen.go | 2 - .../testint/secrets_gen_integration_test.go | 46 +++++++++---------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index 7d548240ad..3321b27380 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -127,7 +127,6 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Set != nil { - opts.Set = &SecretSet{ Comment: r.Set.Comment, } @@ -157,7 +156,6 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { SecretString: r.Set.SetForGenericString.SecretString, } } - } if r.Unset != nil { diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 4ee38140d0..7b7a9d9618 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -33,6 +33,7 @@ func TestInt_Secrets(t *testing.T) { } cleanupIntegration := func(t *testing.T, integrationId sdk.AccountObjectIdentifier) func() { + t.Helper() return func() { err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(integrationId).WithIfExists(true)) require.NoError(t, err) @@ -42,7 +43,7 @@ func TestInt_Secrets(t *testing.T) { err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow( ctx, sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). - WithOauthAllowedScopes([]sdk.AllowedScope{{"foo"}, {"bar"}}), + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), ) require.NoError(t, err) t.Cleanup(cleanupIntegration(t, integrationId)) @@ -158,7 +159,7 @@ func TestInt_Secrets(t *testing.T) { } t.Run("Create: secretWithOAuthClientCredentialsFlow", func(t *testing.T) { - scopes := []sdk.SecurityIntegrationScope{{"foo"}, {"bar"}} + scopes := []sdk.SecurityIntegrationScope{{Scope: "foo"}, {Scope: "bar"}} secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, scopes, func(req *sdk.CreateWithOAuthClientCredentialsFlowSecretRequest) { req.WithComment("a"). @@ -287,14 +288,14 @@ func TestInt_Secrets(t *testing.T) { t.Run("Alter: SecretWithOAuthClientCredentials", func(t *testing.T) { comment := random.Comment() - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). WithComment(comment). WithSetForOAuthClientCredentialsFlow( *sdk.NewSetForOAuthClientCredentialsFlowRequest( - []sdk.SecurityIntegrationScope{{"foo"}, {"bar"}}, + []sdk.SecurityIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}, ), ), ) @@ -323,7 +324,7 @@ func TestInt_Secrets(t *testing.T) { details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{"", false}) + assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) }) t.Run("Alter: SecretWithOAuthAuthorizationCode", func(t *testing.T) { @@ -366,7 +367,7 @@ func TestInt_Secrets(t *testing.T) { details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{"", false}) + assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) }) t.Run("Alter: SecretWithBasicAuthorization", func(t *testing.T) { @@ -408,7 +409,7 @@ func TestInt_Secrets(t *testing.T) { details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{"", false}) + assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) }) t.Run("Alter: SecretWithGenericString", func(t *testing.T) { @@ -426,16 +427,13 @@ func TestInt_Secrets(t *testing.T) { err := client.Secrets.Alter(ctx, setRequest) require.NoError(t, err) - details, err := client.Secrets.Describe(ctx, id) - require.NoError(t, err) - + // Cannot check secret_string since show and describe on secret do not have access to secret_string property /* - // Cannot check secret_string since show and describe on secret do not have access to secret_string property - assertSecretDetails(details, secretDetails{ - Name: id.Name(), - SecretType: "PASSWORD", - Comment: comment, - }) + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "PASSWORD", + Comment: comment, + }) */ unsetRequest := sdk.NewAlterSecretRequest(id). @@ -446,14 +444,14 @@ func TestInt_Secrets(t *testing.T) { err = client.Secrets.Alter(ctx, unsetRequest) require.NoError(t, err) - details, err = client.Secrets.Describe(ctx, id) + details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{"", false}) + assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) }) t.Run("Drop", func(t *testing.T) { - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) secret, err := client.Secrets.ShowByID(ctx, id) require.NotNil(t, secret) @@ -475,7 +473,7 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show", func(t *testing.T) { - secretOAuthClientCredentials, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + secretOAuthClientCredentials, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) @@ -489,8 +487,8 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with Like", func(t *testing.T) { - secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) - secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"bar"}}, nil) + secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) + secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "bar"}}, nil) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ Pattern: sdk.Pointer(id1.Name()), @@ -537,7 +535,7 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with In", func(t *testing.T) { - secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) require.NoError(t, err) @@ -569,7 +567,7 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: with In", func(t *testing.T) { - secretOAuthClientCredentials, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{"foo"}}, nil) + secretOAuthClientCredentials, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) From 235fbe51ccfe942b624ba05ab38d8164bf7a38bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 19 Sep 2024 11:50:26 +0200 Subject: [PATCH 19/65] removing comment --- pkg/sdk/testint/secrets_gen_integration_test.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 7b7a9d9618..538590a28e 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -390,7 +390,7 @@ func TestInt_Secrets(t *testing.T) { details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) - // Cannot check password since show and describe on secret do not have access to password property + // Cannot check password property since show and describe on secret do not have access to it assertSecretDetails(details, secretDetails{ Name: id.Name(), SecretType: "PASSWORD", @@ -427,15 +427,6 @@ func TestInt_Secrets(t *testing.T) { err := client.Secrets.Alter(ctx, setRequest) require.NoError(t, err) - // Cannot check secret_string since show and describe on secret do not have access to secret_string property - /* - assertSecretDetails(details, secretDetails{ - Name: id.Name(), - SecretType: "PASSWORD", - Comment: comment, - }) - */ - unsetRequest := sdk.NewAlterSecretRequest(id). WithUnset( *sdk.NewSecretUnsetRequest(). From 73eeb634e4c8b3921606be6dcf7b22f4a5160b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 20 Sep 2024 14:27:21 +0200 Subject: [PATCH 20/65] mid review commit before generating assertions --- pkg/sdk/secrets_def.go | 20 +++--- pkg/sdk/secrets_gen.go | 12 ++-- pkg/sdk/secrets_gen_test.go | 6 ++ .../testint/secrets_gen_integration_test.go | 65 ++++++++++--------- 4 files changed, 60 insertions(+), 43 deletions(-) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index ddd1fa95fd..2b173f2cd3 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -4,7 +4,7 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen //go:generate go run ./poc/main.go -var secretsSecurityIntegrationScopeDef = g.NewQueryStruct("SecurityIntegrationScope"). +var secretsApiIntegrationScopeDef = g.NewQueryStruct("ApiIntegrationScope"). Text("Scope", g.KeywordOptions().SingleQuotes().Required()) var secretDbRow = g.DbStruct("secretDBRow"). @@ -62,7 +62,7 @@ var secretSet = g.NewQueryStruct("SecretSet"). OptionalQueryStructField( "SetForOAuthClientCredentialsFlow", g.NewQueryStruct("SetForOAuthClientCredentialsFlow"). - ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses().Required()), + ListAssignment("OAUTH_SCOPES", "ApiIntegrationScope", g.ParameterOptions().Parentheses().Required()), g.KeywordOptions(), ). OptionalQueryStructField( @@ -85,7 +85,7 @@ var secretSet = g.NewQueryStruct("SecretSet"). OptionalTextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes()), g.KeywordOptions(), ). - WithValidation(g.ExactlyOneValueSet, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") + WithValidation(g.ConflictingFields, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") // UNSET doest work, need to use "SET COMMENT = NULL" var secretUnset = g.NewQueryStruct("SecretUnset"). @@ -105,12 +105,12 @@ var SecretsDef = g.NewInterface( IfNotExists(). Name(). PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). - Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION").Required()). - ListAssignment("OAUTH_SCOPES", "SecurityIntegrationScope", g.ParameterOptions().Parentheses().Required()). + Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). + ListAssignment("OAUTH_SCOPES", "ApiIntegrationScope", g.ParameterOptions().Parentheses().Required()). OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), - secretsSecurityIntegrationScopeDef, + secretsApiIntegrationScopeDef, ).CustomOperation( "CreateWithOAuthAuthorizationCodeFlow", "https://docs.snowflake.com/en/sql-reference/sql/create-secret", @@ -123,11 +123,11 @@ var SecretsDef = g.NewInterface( PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). TextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). TextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). - Identifier("SecurityIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). + Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), - secretsSecurityIntegrationScopeDef, + secretsApiIntegrationScopeDef, ).CustomOperation( "CreateWithBasicAuthentication", "https://docs.snowflake.com/en/sql-reference/sql/create-secret", @@ -184,14 +184,14 @@ var SecretsDef = g.NewInterface( Name(). WithValidation(g.ValidIdentifier, "name"), ).ShowOperation( - "https://docs.snowflake.com/en/sql-reference/sql/show-secret", + "https://docs.snowflake.com/en/sql-reference/sql/show-secrets", secretDbRow, secret, g.NewQueryStruct("ShowSecret"). Show(). SQL("SECRETS"). OptionalLike(). - OptionalIn(), + OptionalExtendedIn(), ).ShowByIdOperation(). DescribeOperation( g.DescriptionMappingKindSingleValue, diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index b90ac53ea3..d1bf5d9132 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -145,6 +145,10 @@ type Secret struct { OwnerRoleType string } +func (s *Secret) ID() SchemaObjectIdentifier { + return NewSchemaObjectIdentifier(s.DatabaseName, s.SchemaName, s.Name) +} + // DescribeSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-secret. type DescribeSecretOptions struct { describe bool `ddl:"static" sql:"DESCRIBE"` @@ -171,11 +175,11 @@ type SecretDetails struct { SchemaName string DatabaseName string Owner string - Comment sql.NullString + Comment *string SecretType string - Username sql.NullString + Username *string OauthAccessTokenExpiryTime *time.Time OauthRefreshTokenExpiryTime *time.Time - OauthScopes sql.NullString - IntegrationName sql.NullString + OauthScopes *string + IntegrationName *string } diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 692737fe14..27f0c50036 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -218,6 +218,12 @@ func TestSecrets_Alter(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' USERNAME = 'foo' PASSWORD = 'bar'", id.FullyQualifiedName()) }) + t.Run("alter: set comment only", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test'", id.FullyQualifiedName()) + }) + t.Run("alter: set options for Generic string", func(t *testing.T) { opts := setOpts() opts.Set.Comment = String("test") diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 538590a28e..bd50d292e3 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -11,26 +11,13 @@ import ( "github.com/stretchr/testify/require" ) -const Day = 24 * time.Hour - func TestInt_Secrets(t *testing.T) { client := testClient(t) ctx := testContext(t) integrationId := testClientHelper().Ids.RandomAccountObjectIdentifier() - refreshTokenExpiryTime := time.Now().Add(Day).Format(time.DateOnly) - - stringDateToSnowflakeTimeFormat := func(inputLayout, date string) *time.Time { - parsedTime, err := time.Parse(inputLayout, date) - require.NoError(t, err) - - loc, err := time.LoadLocation("America/Los_Angeles") - require.NoError(t, err) - - adjustedTime := parsedTime.In(loc) - return &adjustedTime - } + refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) cleanupIntegration := func(t *testing.T, integrationId sdk.AccountObjectIdentifier) func() { t.Helper() @@ -48,14 +35,34 @@ func TestInt_Secrets(t *testing.T) { require.NoError(t, err) t.Cleanup(cleanupIntegration(t, integrationId)) - cleanupSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { - t.Helper() - return func() { - err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) - require.NoError(t, err) - } + /* + apiIntegration, dropApiIntegration := testClientHelper().ApiIntegration.CreateApiIntegration(t) + sdk.NewAlterApiIntegrationRequest(apiIntegration.ID()). + WithSet(sdk.NewApiIntegrationSetRequest()) + t.Cleanup(dropApiIntegration) + */ + + stringDateToSnowflakeTimeFormat := func(inputLayout, date string) *time.Time { + parsedTime, err := time.Parse(inputLayout, date) + require.NoError(t, err) + + loc, err := time.LoadLocation("America/Los_Angeles") + require.NoError(t, err) + + adjustedTime := parsedTime.In(loc) + return &adjustedTime } + /* + cleanupSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { + t.Helper() + return func() { + err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) + require.NoError(t, err) + } + } + */ + createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, scopes []sdk.SecurityIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -65,7 +72,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(t, id)) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -82,7 +89,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(t, id)) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -99,7 +106,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithBasicAuthentication(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(t, id)) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -116,7 +123,7 @@ func TestInt_Secrets(t *testing.T) { } err := client.Secrets.CreateWithGenericString(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupSecret(t, id)) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) @@ -149,13 +156,13 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails := func(actual *sdk.SecretDetails, expected secretDetails) { assert.Equal(t, expected.Name, actual.Name) - assert.Equal(t, expected.Comment, actual.Comment.String) + assert.Equal(t, expected.Comment, actual.Comment) assert.Equal(t, expected.SecretType, actual.SecretType) - assert.Equal(t, expected.Username, actual.Username.String) + assert.Equal(t, expected.Username, actual.Username) assert.Equal(t, expected.OauthAccessTokenExpiryTime, actual.OauthAccessTokenExpiryTime) assert.Equal(t, expected.OauthRefreshTokenExpiryTime, actual.OauthRefreshTokenExpiryTime) - assert.Equal(t, expected.OauthScopes, actual.OauthScopes.String) - assert.Equal(t, expected.IntegrationName, actual.IntegrationName.String) + assert.Equal(t, expected.OauthScopes, actual.OauthScopes) + assert.Equal(t, expected.IntegrationName, actual.IntegrationName) } t.Run("Create: secretWithOAuthClientCredentialsFlow", func(t *testing.T) { @@ -329,7 +336,7 @@ func TestInt_Secrets(t *testing.T) { t.Run("Alter: SecretWithOAuthAuthorizationCode", func(t *testing.T) { comment := random.Comment() - alteredRefreshTokenExpiryTime := time.Now().Add(4 * Day).Format(time.DateOnly) + alteredRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) _, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) setRequest := sdk.NewAlterSecretRequest(id). From bd0b64fe1f27254e33bec37ff926a16e08587023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 23 Sep 2024 13:49:50 +0200 Subject: [PATCH 21/65] rebase conflict resolved --- .../assert/objectassert/gen/sdk_object_def.go | 5 + .../objectassert/secret_snowflake_gen.go | 136 ++++++ pkg/acceptance/helpers/secret_client.go | 21 +- pkg/sdk/secrets_def.go | 26 +- pkg/sdk/secrets_dto_builders_gen.go | 14 +- pkg/sdk/secrets_dto_gen.go | 18 +- pkg/sdk/secrets_gen.go | 50 ++- pkg/sdk/secrets_gen_test.go | 24 +- pkg/sdk/secrets_impl_gen.go | 62 ++- pkg/sdk/secrets_validations_gen.go | 4 +- .../testint/secrets_gen_integration_test.go | 400 ++++++++++-------- 11 files changed, 499 insertions(+), 261 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go b/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go index 9fe810dfbe..e3480374f2 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go @@ -62,6 +62,11 @@ var allStructs = []SdkObjectDef{ ObjectType: sdk.ObjectTypeTask, ObjectStruct: sdk.Task{}, }, + { + IdType: "sdk.SchemaObjectIdentifier", + ObjectType: sdk.ObjectTypeSecret, + ObjectStruct: sdk.Secret{}, + }, } func GetSdkObjectDetails() []genhelpers.SdkObjectDetails { diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go new file mode 100644 index 0000000000..193e09b18e --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -0,0 +1,136 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package objectassert + +import ( + "fmt" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +type SecretAssert struct { + *assert.SnowflakeObjectAssert[sdk.Secret, sdk.SchemaObjectIdentifier] +} + +func Secret(t *testing.T, id sdk.SchemaObjectIdentifier) *SecretAssert { + t.Helper() + return &SecretAssert{ + assert.NewSnowflakeObjectAssertWithProvider(sdk.ObjectTypeSecret, id, acc.TestClient().Secret.Show), + } +} + +func SecretFromObject(t *testing.T, secret *sdk.Secret) *SecretAssert { + t.Helper() + return &SecretAssert{ + assert.NewSnowflakeObjectAssertWithObject(sdk.ObjectTypeSecret, secret.ID(), secret), + } +} + +func (s *SecretAssert) HasCreatedOn(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.CreatedOn != expected { + return fmt.Errorf("expected created on: %v; got: %v", expected, o.CreatedOn) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasName(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.Name != expected { + return fmt.Errorf("expected name: %v; got: %v", expected, o.Name) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasSchemaName(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.SchemaName != expected { + return fmt.Errorf("expected schema name: %v; got: %v", expected, o.SchemaName) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasDatabaseName(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.DatabaseName != expected { + return fmt.Errorf("expected database name: %v; got: %v", expected, o.DatabaseName) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasOwner(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.Owner != expected { + return fmt.Errorf("expected owner: %v; got: %v", expected, o.Owner) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasComment(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.Comment == nil { + return fmt.Errorf("expected comment to have value; got: nil") + } + if *o.Comment != expected { + return fmt.Errorf("expected comment: %v; got: %v", expected, *o.Comment) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasSecretType(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.SecretType != expected { + return fmt.Errorf("expected secret type: %v; got: %v", expected, o.SecretType) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasOauthScopes(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.OauthScopes == nil { + return fmt.Errorf("expected oauth scopes to have value; got: nil") + } + if *o.OauthScopes != expected { + return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, *o.OauthScopes) + } + return nil + }) + return s +} + +func (s *SecretAssert) HasOwnerRoleType(expected string) *SecretAssert { + s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { + t.Helper() + if o.OwnerRoleType != expected { + return fmt.Errorf("expected owner role type: %v; got: %v", expected, o.OwnerRoleType) + } + return nil + }) + return s +} diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index ab980051c3..314f6248b5 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -2,7 +2,6 @@ package helpers import ( "context" - "errors" "testing" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -26,7 +25,7 @@ func (c *SecretClient) client() sdk.Secrets { return c.context.client.Secrets } -func (c *SecretClient) CreateSecreteWithBasicFlow(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) (*sdk.Secret, func()) { +func (c *SecretClient) CreateWithBasicFlow(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) (*sdk.Secret, func()) { t.Helper() ctx := context.Background() request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password) @@ -37,20 +36,22 @@ func (c *SecretClient) CreateSecreteWithBasicFlow(t *testing.T, id sdk.SchemaObj secret, err := c.client().ShowByID(ctx, id) require.NoError(t, err) - return secret, c.CleanupSecretFunc(t, id) + return secret, c.DropFunc(t, id) } -func (c *SecretClient) CleanupSecretFunc(t *testing.T, id sdk.SchemaObjectIdentifier) func() { +func (c *SecretClient) DropFunc(t *testing.T, id sdk.SchemaObjectIdentifier) func() { t.Helper() ctx := context.Background() return func() { - _, err := c.client().ShowByID(ctx, id) - if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { - return - } - - err = c.client().Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) + err := c.client().Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) assert.NoError(t, err) } } + +func (c *SecretClient) Show(t *testing.T, id sdk.SchemaObjectIdentifier) (*sdk.Secret, error) { + t.Helper() + ctx := context.Background() + + return c.client().ShowByID(ctx, id) +} diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 2b173f2cd3..23f51352d8 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -8,7 +8,7 @@ var secretsApiIntegrationScopeDef = g.NewQueryStruct("ApiIntegrationScope"). Text("Scope", g.KeywordOptions().SingleQuotes().Required()) var secretDbRow = g.DbStruct("secretDBRow"). - Field("created_on", "string"). + Field("created_on", "time.Time"). Field("name", "string"). Field("schema_name", "string"). Field("database_name", "string"). @@ -19,18 +19,18 @@ var secretDbRow = g.DbStruct("secretDBRow"). Field("owner_role_type", "string") var secret = g.PlainStruct("Secret"). - Field("CreatedOn", "string"). + Field("CreatedOn", "time.Time"). Field("Name", "string"). Field("SchemaName", "string"). Field("DatabaseName", "string"). Field("Owner", "string"). - Field("Comment", "string"). + Field("Comment", "*string"). Field("SecretType", "string"). - Field("OauthScopes", "sql.NullString"). + Field("OauthScopes", "[]string"). Field("OwnerRoleType", "string") var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). - Field("created_on", "string"). + Field("created_on", "time.Time"). Field("name", "string"). Field("schema_name", "string"). Field("database_name", "string"). @@ -38,24 +38,24 @@ var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). Field("comment", "sql.NullString"). Field("secret_type", "string"). Field("username", "sql.NullString"). - Time("oauth_access_token_expiry_time"). - Time("oauth_refresh_token_expiry_time"). + Field("oauth_access_token_expiry_time", "*time.Time"). + Field("oauth_refresh_token_expiry_time", "*time.Time"). Field("oauth_scopes", "sql.NullString"). Field("integration_name", "sql.NullString") var secretDetails = g.PlainStruct("SecretDetails"). - Field("CreatedOn", "string"). + Field("CreatedOn", "time.Time"). Field("Name", "string"). Field("SchemaName", "string"). Field("DatabaseName", "string"). Field("Owner", "string"). - Field("Comment", "sql.NullString"). + Field("Comment", "*string"). Field("SecretType", "string"). - Field("Username", "sql.NullString"). + Field("Username", "*string"). Field("OauthAccessTokenExpiryTime", "*time.Time"). Field("OauthRefreshTokenExpiryTime", "*time.Time"). - Field("OauthScopes", "sql.NullString"). - Field("IntegrationName", "sql.NullString") + Field("OauthScopes", "*string"). + Field("IntegrationName", "*string") var secretSet = g.NewQueryStruct("SecretSet"). OptionalComment(). @@ -137,7 +137,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = PASSWORD")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = PASSWORD")). TextAssignment("USERNAME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). TextAssignment("PASSWORD", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). OptionalComment(). diff --git a/pkg/sdk/secrets_dto_builders_gen.go b/pkg/sdk/secrets_dto_builders_gen.go index faf3a01bf9..9faeabb35a 100644 --- a/pkg/sdk/secrets_dto_builders_gen.go +++ b/pkg/sdk/secrets_dto_builders_gen.go @@ -6,12 +6,12 @@ import () func NewCreateWithOAuthClientCredentialsFlowSecretRequest( name SchemaObjectIdentifier, - SecurityIntegration AccountObjectIdentifier, - OauthScopes []SecurityIntegrationScope, + ApiIntegration AccountObjectIdentifier, + OauthScopes []ApiIntegrationScope, ) *CreateWithOAuthClientCredentialsFlowSecretRequest { s := CreateWithOAuthClientCredentialsFlowSecretRequest{} s.name = name - s.SecurityIntegration = SecurityIntegration + s.ApiIntegration = ApiIntegration s.OauthScopes = OauthScopes return &s } @@ -35,13 +35,13 @@ func NewCreateWithOAuthAuthorizationCodeFlowSecretRequest( name SchemaObjectIdentifier, OauthRefreshToken string, OauthRefreshTokenExpiryTime string, - SecurityIntegration AccountObjectIdentifier, + ApiIntegration AccountObjectIdentifier, ) *CreateWithOAuthAuthorizationCodeFlowSecretRequest { s := CreateWithOAuthAuthorizationCodeFlowSecretRequest{} s.name = name s.OauthRefreshToken = OauthRefreshToken s.OauthRefreshTokenExpiryTime = OauthRefreshTokenExpiryTime - s.SecurityIntegration = SecurityIntegration + s.ApiIntegration = ApiIntegration return &s } @@ -165,7 +165,7 @@ func (s *SecretSetRequest) WithSetForGenericString(SetForGenericString SetForGen } func NewSetForOAuthClientCredentialsFlowRequest( - OauthScopes []SecurityIntegrationScope, + OauthScopes []ApiIntegrationScope, ) *SetForOAuthClientCredentialsFlowRequest { s := SetForOAuthClientCredentialsFlowRequest{} s.OauthScopes = OauthScopes @@ -240,7 +240,7 @@ func (s *ShowSecretRequest) WithLike(Like Like) *ShowSecretRequest { return s } -func (s *ShowSecretRequest) WithIn(In In) *ShowSecretRequest { +func (s *ShowSecretRequest) WithIn(In ExtendedIn) *ShowSecretRequest { s.In = &In return s } diff --git a/pkg/sdk/secrets_dto_gen.go b/pkg/sdk/secrets_dto_gen.go index 36d3f1ef22..3eec952bb4 100644 --- a/pkg/sdk/secrets_dto_gen.go +++ b/pkg/sdk/secrets_dto_gen.go @@ -14,12 +14,12 @@ var ( ) type CreateWithOAuthClientCredentialsFlowSecretRequest struct { - OrReplace *bool - IfNotExists *bool - name SchemaObjectIdentifier // required - SecurityIntegration AccountObjectIdentifier // required - OauthScopes []SecurityIntegrationScope // required - Comment *string + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + ApiIntegration AccountObjectIdentifier // required + OauthScopes []ApiIntegrationScope // required + Comment *string } type CreateWithOAuthAuthorizationCodeFlowSecretRequest struct { @@ -28,7 +28,7 @@ type CreateWithOAuthAuthorizationCodeFlowSecretRequest struct { name SchemaObjectIdentifier // required OauthRefreshToken string // required OauthRefreshTokenExpiryTime string // required - SecurityIntegration AccountObjectIdentifier // required + ApiIntegration AccountObjectIdentifier // required Comment *string } @@ -65,7 +65,7 @@ type SecretSetRequest struct { } type SetForOAuthClientCredentialsFlowRequest struct { - OauthScopes []SecurityIntegrationScope // required + OauthScopes []ApiIntegrationScope // required } type SetForOAuthAuthorizationFlowRequest struct { @@ -93,7 +93,7 @@ type DropSecretRequest struct { type ShowSecretRequest struct { Like *Like - In *In + In *ExtendedIn } type DescribeSecretRequest struct { diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index d1bf5d9132..7e35b008bc 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -20,17 +20,17 @@ type Secrets interface { // CreateWithOAuthClientCredentialsFlowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. type CreateWithOAuthClientCredentialsFlowSecretOptions struct { - create bool `ddl:"static" sql:"CREATE"` - OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` - secret bool `ddl:"static" sql:"SECRET"` - IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` - name SchemaObjectIdentifier `ddl:"identifier"` - Type string `ddl:"static" sql:"TYPE = OAUTH2"` - SecurityIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` - OauthScopes []SecurityIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` - Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` -} -type SecurityIntegrationScope struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + secret bool `ddl:"static" sql:"SECRET"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + secretType string `ddl:"static" sql:"TYPE = OAUTH2"` + ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` + OauthScopes []ApiIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} +type ApiIntegrationScope struct { Scope string `ddl:"keyword,single_quotes"` } @@ -44,7 +44,7 @@ type CreateWithOAuthAuthorizationCodeFlowSecretOptions struct { Type string `ddl:"static" sql:"TYPE = OAUTH2"` OauthRefreshToken string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` OauthRefreshTokenExpiryTime string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN_EXPIRY_TIME"` - SecurityIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` + ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` } @@ -90,7 +90,7 @@ type SecretSet struct { SetForGenericString *SetForGenericString `ddl:"keyword"` } type SetForOAuthClientCredentialsFlow struct { - OauthScopes []SecurityIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` + OauthScopes []ApiIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` } type SetForOAuthAuthorizationFlow struct { OauthRefreshToken *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` @@ -115,15 +115,15 @@ type DropSecretOptions struct { name SchemaObjectIdentifier `ddl:"identifier"` } -// ShowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-secret. +// ShowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-secrets. type ShowSecretOptions struct { - show bool `ddl:"static" sql:"SHOW"` - secrets bool `ddl:"static" sql:"SECRETS"` - Like *Like `ddl:"keyword" sql:"LIKE"` - In *In `ddl:"keyword" sql:"IN"` + show bool `ddl:"static" sql:"SHOW"` + secrets bool `ddl:"static" sql:"SECRETS"` + Like *Like `ddl:"keyword" sql:"LIKE"` + In *ExtendedIn `ddl:"keyword" sql:"IN"` } type secretDBRow struct { - CreatedOn string `db:"created_on"` + CreatedOn time.Time `db:"created_on"` Name string `db:"name"` SchemaName string `db:"schema_name"` DatabaseName string `db:"database_name"` @@ -134,14 +134,14 @@ type secretDBRow struct { OwnerRoleType string `db:"owner_role_type"` } type Secret struct { - CreatedOn string + CreatedOn time.Time Name string SchemaName string DatabaseName string Owner string - Comment string + Comment *string SecretType string - OauthScopes sql.NullString + OauthScopes []string OwnerRoleType string } @@ -149,6 +149,10 @@ func (s *Secret) ID() SchemaObjectIdentifier { return NewSchemaObjectIdentifier(s.DatabaseName, s.SchemaName, s.Name) } +func (s *Secret) ObjectType() ObjectType { + return ObjectTypeSecret +} + // DescribeSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-secret. type DescribeSecretOptions struct { describe bool `ddl:"static" sql:"DESCRIBE"` @@ -180,6 +184,6 @@ type SecretDetails struct { Username *string OauthAccessTokenExpiryTime *time.Time OauthRefreshTokenExpiryTime *time.Time - OauthScopes *string + OauthScopes []string IntegrationName *string } diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 27f0c50036..01b5ced01a 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -33,8 +33,8 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { opts := defaultOpts() opts.IfNotExists = Bool(true) - opts.SecurityIntegration = integration - opts.OauthScopes = []SecurityIntegrationScope{{"test"}} + opts.ApiIntegration = integration + opts.OauthScopes = []ApiIntegrationScope{{"test"}} opts.Comment = String("foo") assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('test') COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) @@ -73,7 +73,7 @@ func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { opts.IfNotExists = Bool(true) opts.OauthRefreshToken = "foo" opts.OauthRefreshTokenExpiryTime = "bar" - opts.SecurityIntegration = integration + opts.ApiIntegration = integration opts.Comment = String("test") assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 OAUTH_REFRESH_TOKEN = 'foo' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = 'bar' API_AUTHENTICATION = %s COMMENT = 'test'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) @@ -184,17 +184,19 @@ func TestSecrets_Alter(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) }) - t.Run("validation: exactly one field from [opts.Set.SetForOAuthClientCredentialsFlow opts.Set.SetForOAuthAuthorizationFlow opts.Set.SetForBasicAuthentication opts.Set.SetForGenericString] should be present", func(t *testing.T) { + t.Run("validation: conflicting fields for [opts.Set.SetForOAuthClientCredentialsFlow opts.Set.SetForOAuthAuthorizationFlow opts.Set.SetForBasicAuthentication opts.Set.SetForGenericString]", func(t *testing.T) { opts := setOpts() - opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{} - opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{} - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{{Scope: "foo"}}} + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{OauthRefreshToken: String("foo"), OauthRefreshTokenExpiryTime: String("bar")} + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{Username: String("foo"), Password: String("bar")} + opts.Set.SetForGenericString = &SetForGenericString{SecretString: String("secret")} + assertOptsInvalidJoinedErrors(t, opts, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) }) t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { opts := setOpts() opts.Set.Comment = String("test") - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{[]SecurityIntegrationScope{{"sample_scope"}}} + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{[]ApiIntegrationScope{{"sample_scope"}}} assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName()) }) @@ -294,8 +296,10 @@ func TestSecrets_Show(t *testing.T) { t.Run("show with in", func(t *testing.T) { opts := defaultOpts() - opts.In = &In{ - Account: Bool(true), + opts.In = &ExtendedIn{ + In: In{ + Account: Bool(true), + }, } assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS IN ACCOUNT") }) diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index 3321b27380..0786ecb8a7 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -2,6 +2,7 @@ package sdk import ( "context" + "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" ) @@ -53,7 +54,7 @@ func (v *secrets) Show(ctx context.Context, request *ShowSecretRequest) ([]Secre } func (v *secrets) ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*Secret, error) { - request := NewShowSecretRequest().WithIn(In{Schema: id.SchemaId()}).WithLike(Like{String(id.Name())}) + request := NewShowSecretRequest().WithIn(ExtendedIn{In: In{Schema: id.SchemaId()}}).WithLike(Like{String(id.Name())}) secrets, err := v.Show(ctx, request) if err != nil { return nil, err @@ -74,12 +75,12 @@ func (v *secrets) Describe(ctx context.Context, id SchemaObjectIdentifier) (*Sec func (r *CreateWithOAuthClientCredentialsFlowSecretRequest) toOpts() *CreateWithOAuthClientCredentialsFlowSecretOptions { opts := &CreateWithOAuthClientCredentialsFlowSecretOptions{ - OrReplace: r.OrReplace, - IfNotExists: r.IfNotExists, - name: r.name, - SecurityIntegration: r.SecurityIntegration, - OauthScopes: r.OauthScopes, - Comment: r.Comment, + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + ApiIntegration: r.ApiIntegration, + OauthScopes: r.OauthScopes, + Comment: r.Comment, } return opts } @@ -91,7 +92,7 @@ func (r *CreateWithOAuthAuthorizationCodeFlowSecretRequest) toOpts() *CreateWith name: r.name, OauthRefreshToken: r.OauthRefreshToken, OauthRefreshTokenExpiryTime: r.OauthRefreshTokenExpiryTime, - SecurityIntegration: r.SecurityIntegration, + ApiIntegration: r.ApiIntegration, Comment: r.Comment, } return opts @@ -127,41 +128,53 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Set != nil { + opts.Set = &SecretSet{ Comment: r.Set.Comment, } if r.Set.SetForOAuthClientCredentialsFlow != nil { + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{ OauthScopes: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes, } + } if r.Set.SetForOAuthAuthorizationFlow != nil { + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ OauthRefreshToken: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshToken, OauthRefreshTokenExpiryTime: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshTokenExpiryTime, } + } if r.Set.SetForBasicAuthentication != nil { + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ Username: r.Set.SetForBasicAuthentication.Username, Password: r.Set.SetForBasicAuthentication.Password, } + } if r.Set.SetForGenericString != nil { + opts.Set.SetForGenericString = &SetForGenericString{ SecretString: r.Set.SetForGenericString.SecretString, } + } + } if r.Unset != nil { + opts.Unset = &SecretUnset{ Comment: r.Unset.Comment, } + } return opts @@ -183,6 +196,16 @@ func (r *ShowSecretRequest) toOpts() *ShowSecretOptions { return opts } +func getOauthScopes(scopesString string) []string { + formatedScopes := make([]string, 0) + scopesString = strings.TrimPrefix(scopesString, "[") + scopesString = strings.TrimSuffix(scopesString, "]") + for _, scope := range strings.Split(scopesString, ",") { + formatedScopes = append(formatedScopes, strings.TrimSpace(scope)) + } + return formatedScopes +} + func (r secretDBRow) convert() *Secret { s := &Secret{ CreatedOn: r.CreatedOn, @@ -194,10 +217,10 @@ func (r secretDBRow) convert() *Secret { OwnerRoleType: r.OwnerRoleType, } if r.Comment.Valid { - s.Comment = r.Comment.String + s.Comment = String(r.Comment.String) } if r.OauthScopes.Valid { - s.OauthScopes = r.OauthScopes + s.OauthScopes = getOauthScopes(r.OauthScopes.String) } return s } @@ -210,18 +233,27 @@ func (r *DescribeSecretRequest) toOpts() *DescribeSecretOptions { } func (r secretDetailsDBRow) convert() *SecretDetails { - return &SecretDetails{ + s := &SecretDetails{ CreatedOn: r.CreatedOn, Name: r.Name, SchemaName: r.SchemaName, DatabaseName: r.DatabaseName, Owner: r.Owner, - Comment: r.Comment, SecretType: r.SecretType, - Username: r.Username, OauthAccessTokenExpiryTime: r.OauthAccessTokenExpiryTime, OauthRefreshTokenExpiryTime: r.OauthRefreshTokenExpiryTime, - OauthScopes: r.OauthScopes, - IntegrationName: r.IntegrationName, } + if r.Username.Valid { + s.Username = String(r.Username.String) + } + if r.Comment.Valid { + s.Comment = String(r.Comment.String) + } + if r.OauthScopes.Valid { + s.OauthScopes = getOauthScopes(r.OauthScopes.String) + } + if r.IntegrationName.Valid { + s.IntegrationName = String(r.IntegrationName.String) + } + return s } diff --git a/pkg/sdk/secrets_validations_gen.go b/pkg/sdk/secrets_validations_gen.go index c414a5cd6f..b5f7e14800 100644 --- a/pkg/sdk/secrets_validations_gen.go +++ b/pkg/sdk/secrets_validations_gen.go @@ -76,8 +76,8 @@ func (opts *AlterSecretOptions) validate() error { errs = append(errs, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) } if valueSet(opts.Set) { - if !exactlyOneValueSet(opts.Set.SetForOAuthClientCredentialsFlow, opts.Set.SetForOAuthAuthorizationFlow, opts.Set.SetForBasicAuthentication, opts.Set.SetForGenericString) { - errs = append(errs, errExactlyOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + if everyValueSet(opts.Set.SetForOAuthClientCredentialsFlow, opts.Set.SetForOAuthAuthorizationFlow, opts.Set.SetForBasicAuthentication, opts.Set.SetForGenericString) { + errs = append(errs, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) } } return JoinErrors(errs...) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index bd50d292e3..a65a7cc61b 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -2,6 +2,8 @@ package testint import ( "database/sql" + assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" "testing" "time" @@ -35,13 +37,6 @@ func TestInt_Secrets(t *testing.T) { require.NoError(t, err) t.Cleanup(cleanupIntegration(t, integrationId)) - /* - apiIntegration, dropApiIntegration := testClientHelper().ApiIntegration.CreateApiIntegration(t) - sdk.NewAlterApiIntegrationRequest(apiIntegration.ID()). - WithSet(sdk.NewApiIntegrationSetRequest()) - t.Cleanup(dropApiIntegration) - */ - stringDateToSnowflakeTimeFormat := func(inputLayout, date string) *time.Time { parsedTime, err := time.Parse(inputLayout, date) require.NoError(t, err) @@ -53,17 +48,7 @@ func TestInt_Secrets(t *testing.T) { return &adjustedTime } - /* - cleanupSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { - t.Helper() - return func() { - err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) - require.NoError(t, err) - } - } - */ - - createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, scopes []sdk.SecurityIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { + createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, scopes []sdk.ApiIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { t.Helper() id := testClientHelper().Ids.RandomSchemaObjectIdentifier() request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, scopes) @@ -131,97 +116,146 @@ func TestInt_Secrets(t *testing.T) { return secret, id } - assertSecret := func(t *testing.T, s *sdk.Secret, expectedId sdk.SchemaObjectIdentifier, expectedSecretType, expectedComment string) { + createSecretWithId := func(t *testing.T, id sdk.SchemaObjectIdentifier) *sdk.Secret { t.Helper() - assert.Equal(t, expectedId.Name(), s.Name) - assert.Equal(t, expectedSecretType, s.SecretType) - assert.Equal(t, expectedComment, s.Comment) - assert.NotEmpty(t, s.CreatedOn) - assert.NotEmpty(t, s.DatabaseName) - assert.NotEmpty(t, s.SchemaName) - assert.NotEmpty(t, s.OwnerRoleType) - assert.NotEmpty(t, s.Owner) + request := sdk.NewCreateWithGenericStringSecretRequest(id, "foo") + err := client.Secrets.CreateWithGenericString(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + return secret } type secretDetails struct { Name string - Comment string + Comment *string SecretType string - Username string + Username *string OauthAccessTokenExpiryTime *time.Time OauthRefreshTokenExpiryTime *time.Time - OauthScopes string - IntegrationName string + OauthScopes []string + IntegrationName *string } assertSecretDetails := func(actual *sdk.SecretDetails, expected secretDetails) { assert.Equal(t, expected.Name, actual.Name) - assert.Equal(t, expected.Comment, actual.Comment) + assert.EqualValues(t, expected.Comment, actual.Comment) assert.Equal(t, expected.SecretType, actual.SecretType) - assert.Equal(t, expected.Username, actual.Username) + assert.EqualValues(t, expected.Username, actual.Username) assert.Equal(t, expected.OauthAccessTokenExpiryTime, actual.OauthAccessTokenExpiryTime) assert.Equal(t, expected.OauthRefreshTokenExpiryTime, actual.OauthRefreshTokenExpiryTime) - assert.Equal(t, expected.OauthScopes, actual.OauthScopes) - assert.Equal(t, expected.IntegrationName, actual.IntegrationName) + assert.EqualValues(t, expected.OauthScopes, actual.OauthScopes) + assert.EqualValues(t, expected.IntegrationName, actual.IntegrationName) } t.Run("Create: secretWithOAuthClientCredentialsFlow", func(t *testing.T) { - scopes := []sdk.SecurityIntegrationScope{{Scope: "foo"}, {Scope: "bar"}} + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}). + WithComment("a"). + WithIfNotExists(true) + + err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasComment("a"). + HasSecretType("OAUTH2"). + HasOauthScopes("[foo, bar]"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) - secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, scopes, func(req *sdk.CreateWithOAuthClientCredentialsFlowSecretRequest) { - req.WithComment("a"). - WithIfNotExists(true) - }) details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) assertSecretDetails(details, secretDetails{ Name: id.Name(), - Comment: "a", + Comment: sdk.String("a"), SecretType: "OAUTH2", - OauthScopes: "[foo, bar]", - IntegrationName: integrationId.Name(), + OauthScopes: []string{"foo", "bar"}, + IntegrationName: sdk.String(integrationId.Name()), }) - - assertSecret(t, secret, id, "OAUTH2", "a") }) // It is possible to create secret without specifying both refresh token properties and scopes // Scopes are not being inherited from the security_integration what is tested further t.Run("Create: secretWithOAuth - minimal, without token and scopes", func(t *testing.T) { - secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{}, nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, nil) + + err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) + details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) assertSecretDetails(details, secretDetails{ - Name: id.Name(), - SecretType: "OAUTH2", - OauthScopes: "", - OauthAccessTokenExpiryTime: nil, - OauthRefreshTokenExpiryTime: nil, - IntegrationName: integrationId.Name(), + Name: id.Name(), + SecretType: "OAUTH2", + IntegrationName: sdk.String(integrationId.Name()), }) - - assertSecret(t, secret, id, "OAUTH2", "") }) // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret should inherit security_integration scopes, but it does not do so t.Run("Create: SecretWithOAuthClientCredentialsFlow - No Scopes Specified", func(t *testing.T) { - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{}, nil) - details, err := client.Secrets.Describe(ctx, id) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, []sdk.ApiIntegrationScope{}) + + err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasOauthScopes("[]"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) securityIntegrationProperties, _ := client.SecurityIntegrations.Describe(ctx, integrationId) assert.Contains(t, securityIntegrationProperties, sdk.SecurityIntegrationProperty{Name: "OAUTH_ALLOWED_SCOPES", Type: "List", Value: "[foo, bar]", Default: "[]"}) - assert.NotEqual(t, details.OauthScopes, securityIntegrationProperties) + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + assert.NotEqual(t, details.OauthScopes, "[foo, bar]") }) - t.Run("Create: SecretWithOAuthAuthorizationCodeFlow", func(t *testing.T) { - secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, func(req *sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest) { - req.WithComment("a"). - WithIfNotExists(true) - }) + t.Run("Create: SecretWithOAuthAuthorizationCodeFlow - refreshTokenExpiry date format", func(t *testing.T) { + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, "foo", refreshTokenExpiryTime, integrationId). + WithComment("a"). + WithIfNotExists(true) + + err := client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + _, err = client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasComment("a"). + HasSecretType("OAUTH2"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) @@ -229,80 +263,138 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), SecretType: "OAUTH2", - Comment: "a", + Comment: sdk.String("a"), OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, refreshTokenExpiryTime), - IntegrationName: integrationId.Name(), + IntegrationName: sdk.String(integrationId.Name()), }) + }) - assertSecret(t, secret, id, "OAUTH2", "a") + t.Run("Create: SecretWithOAuthAuthorizationCodeFlow - refreshTokenExpiry datetime format", func(t *testing.T) { + refreshTokenWithTime := refreshTokenExpiryTime + " 12:00:00" + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, "foo", refreshTokenWithTime, integrationId) + + err := client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateTime, refreshTokenWithTime), + IntegrationName: sdk.String(integrationId.Name()), + }) }) t.Run("Create: WithBasicAuthentication", func(t *testing.T) { comment := random.Comment() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, "foo", "foo"). + WithComment(comment). + WithIfNotExists(true) + + err := client.Secrets.CreateWithBasicAuthentication(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + _, err = client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasComment(comment). + HasSecretType("PASSWORD"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) - secret, id := createSecretWithBasicAuthentication(t, "foo", "foo", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { - req.WithComment(comment). - WithIfNotExists(true) - }) details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) assertSecretDetails(details, secretDetails{ Name: id.Name(), - Comment: comment, + Comment: sdk.String(comment), SecretType: "PASSWORD", - Username: "foo", + Username: sdk.String("foo"), }) - - assertSecret(t, secret, id, "PASSWORD", comment) }) t.Run("Create: WithBasicAuthentication - Empty Username and Password", func(t *testing.T) { - comment := random.Comment() - secret, id := createSecretWithBasicAuthentication(t, "", "", func(req *sdk.CreateWithBasicAuthenticationSecretRequest) { - req.WithComment(comment). - WithIfNotExists(true) - }) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, "", ""). + WithIfNotExists(true) + + err := client.Secrets.CreateWithBasicAuthentication(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) assertSecretDetails(details, secretDetails{ Name: id.Name(), - Comment: comment, SecretType: "PASSWORD", - Username: "", + Username: sdk.String(""), }) - - assertSecret(t, secret, id, "PASSWORD", comment) }) t.Run("Create: WithGenericString", func(t *testing.T) { comment := random.Comment() - secret, id := createSecretWithGenericString(t, "foo", func(req *sdk.CreateWithGenericStringSecretRequest) { - req.WithComment(comment). - WithIfNotExists(true) - }) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithGenericStringSecretRequest(id, "secret"). + WithComment(comment). + WithIfNotExists(true) - assertSecret(t, secret, id, "GENERIC_STRING", comment) + err := client.Secrets.CreateWithGenericString(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) + + _, err = client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasComment(comment). + HasSecretType("GENERIC_STRING"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) }) t.Run("Create: WithGenericString - empty secret_string", func(t *testing.T) { - secret, id := createSecretWithGenericString(t, "", nil) + + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithGenericStringSecretRequest(id, "") + + err := client.Secrets.CreateWithGenericString(ctx, request) require.NoError(t, err) + t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - assertSecret(t, secret, id, "GENERIC_STRING", "") + assertions.AssertThat(t, + objectassert.Secret(t, id). + HasName(id.Name()). + HasSecretType("GENERIC_STRING"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) }) t.Run("Alter: SecretWithOAuthClientCredentials", func(t *testing.T) { comment := random.Comment() - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). WithComment(comment). WithSetForOAuthClientCredentialsFlow( *sdk.NewSetForOAuthClientCredentialsFlowRequest( - []sdk.SecurityIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}, + []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}, ), ), ) @@ -315,9 +407,9 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), SecretType: "OAUTH2", - Comment: comment, - OauthScopes: "[foo, bar]", - IntegrationName: integrationId.Name(), + Comment: sdk.String(comment), + OauthScopes: sdk.String("[foo, bar]"), + IntegrationName: sdk.String(integrationId.Name()), }) unsetRequest := sdk.NewAlterSecretRequest(id). @@ -358,9 +450,9 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), SecretType: "OAUTH2", - Comment: comment, + Comment: sdk.String(comment), OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, alteredRefreshTokenExpiryTime), - IntegrationName: integrationId.Name(), + IntegrationName: sdk.String(integrationId.Name()), }) unsetRequest := sdk.NewAlterSecretRequest(id). @@ -401,8 +493,8 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), SecretType: "PASSWORD", - Comment: comment, - Username: "bar", + Comment: sdk.String(comment), + Username: sdk.String("bar"), }) unsetRequest := sdk.NewAlterSecretRequest(id). @@ -449,7 +541,7 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Drop", func(t *testing.T) { - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) + _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) secret, err := client.Secrets.ShowByID(ctx, id) require.NotNil(t, secret) @@ -471,7 +563,7 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show", func(t *testing.T) { - secretOAuthClientCredentials, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) + secretOAuthClientCredentials, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) @@ -485,11 +577,11 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with Like", func(t *testing.T) { - secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) - secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "bar"}}, nil) + secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}, nil) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ - Pattern: sdk.Pointer(id1.Name()), + Pattern: sdk.String(id1.Name()), })) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret1) @@ -501,7 +593,7 @@ func TestInt_Secrets(t *testing.T) { secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ - Pattern: sdk.Pointer(id1.Name()), + Pattern: sdk.String(id1.Name()), })) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret1) @@ -513,7 +605,7 @@ func TestInt_Secrets(t *testing.T) { secret2, _ := createSecretWithBasicAuthentication(t, "foo_2", "bar_2", nil) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ - Pattern: sdk.Pointer(id1.Name()), + Pattern: sdk.String(id1.Name()), })) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret1) @@ -525,7 +617,7 @@ func TestInt_Secrets(t *testing.T) { secret2, _ := createSecretWithGenericString(t, "foo_2", nil) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ - Pattern: sdk.Pointer(id1.Name()), + Pattern: sdk.String(id1.Name()), })) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret1) @@ -533,17 +625,17 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with In", func(t *testing.T) { - secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) + secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) - returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Database: id.DatabaseId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Schema: id.SchemaId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) }) @@ -551,40 +643,40 @@ func TestInt_Secrets(t *testing.T) { t.Run("Show: SecretWithOAuthAuthorizationCodeFlow with In", func(t *testing.T) { secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) - returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Database: id.DatabaseId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Schema: id.SchemaId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) }) t.Run("Show: with In", func(t *testing.T) { - secretOAuthClientCredentials, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.SecurityIntegrationScope{{Scope: "foo"}}, nil) + secretOAuthClientCredentials, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) - returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) require.Contains(t, returnedSecrets, *secretBasicAuthentication) require.Contains(t, returnedSecrets, *secretGenericString) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Database: id.DatabaseId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) require.Contains(t, returnedSecrets, *secretBasicAuthentication) require.Contains(t, returnedSecrets, *secretGenericString) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Schema: id.SchemaId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secretOAuthClientCredentials) require.Contains(t, returnedSecrets, *secretOAuthAuthorizationCode) @@ -595,25 +687,36 @@ func TestInt_Secrets(t *testing.T) { t.Run("Show: SecretWithGenericString with In", func(t *testing.T) { secret, id := createSecretWithGenericString(t, "foo", nil) - returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Account: sdk.Pointer(true)})) + returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id.DatabaseId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Database: id.DatabaseId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id.SchemaId()})) + returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Schema: id.SchemaId()}})) require.NoError(t, err) require.Contains(t, returnedSecrets, *secret) }) - t.Run("ShowByID", func(t *testing.T) { - _, id := createSecretWithGenericString(t, "foo", nil) + t.Run("ShowByID - same name different schemas", func(t *testing.T) { + schema, schemaCleanup := testClientHelper().Schema.CreateSchema(t) + t.Cleanup(schemaCleanup) - secret, err := client.Secrets.ShowByID(ctx, id) + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + id2 := testClientHelper().Ids.NewSchemaObjectIdentifierInSchema(id1.Name(), schema.ID()) + + createSecretWithId(t, id1) + createSecretWithId(t, id2) + + secretShowResult1, err := client.Secrets.ShowByID(ctx, id1) + require.NoError(t, err) + require.Equal(t, id1, secretShowResult1.ID()) + + secretShowResult2, err := client.Secrets.ShowByID(ctx, id2) require.NoError(t, err) - assertSecret(t, secret, id, "GENERIC_STRING", "") + require.Equal(t, id2, secretShowResult2.ID()) }) t.Run("Describe", func(t *testing.T) { @@ -626,55 +729,8 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - Comment: "Lorem ipsum", + Comment: sdk.String("Lorem ipsum"), SecretType: "GENERIC_STRING", }) }) } - -func TestInt_SecretsShowWithIn(t *testing.T) { - client := testClient(t) - ctx := testContext(t) - - cleanupSecret := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { - t.Helper() - return func() { - err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)) - require.NoError(t, err) - } - } - - createSecretWithGenericString := func(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) *sdk.Secret { - t.Helper() - request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) - err := client.Secrets.CreateWithGenericString(ctx, request) - require.NoError(t, err) - t.Cleanup(cleanupSecret(t, id)) - - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) - - return secret - } - - t.Run("Show with In - same id in different schemas", func(t *testing.T) { - schema, schemaCleanup := testClientHelper().Schema.CreateSchema(t) - t.Cleanup(schemaCleanup) - - id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() - id2 := testClientHelper().Ids.NewSchemaObjectIdentifierInSchema(id1.Name(), schema.ID()) - - secret1 := createSecretWithGenericString(t, id1, "foo") - secret2 := createSecretWithGenericString(t, id2, "bar") - - returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Schema: id1.SchemaId()})) - require.NoError(t, err) - require.Contains(t, returnedSecrets, *secret1) - require.NotContains(t, returnedSecrets, *secret2) - - returnedSecrets, err = client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.In{Database: id1.DatabaseId()})) - require.NoError(t, err) - require.Contains(t, returnedSecrets, *secret1) - require.Contains(t, returnedSecrets, *secret2) - }) -} From 2050b85f100e7f4324418ead58e3703c68ee3afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 23 Sep 2024 14:46:07 +0200 Subject: [PATCH 22/65] tests pass after changing to []string for oauthScopes --- .../objectassert/secret_snowflake_gen.go | 13 +- pkg/sdk/secrets_def.go | 2 +- pkg/sdk/secrets_gen.go | 8 +- pkg/sdk/secrets_gen_integration_test.go | 43 ++++ pkg/sdk/secrets_gen_test.go | 232 ++++++++---------- .../testint/secrets_gen_integration_test.go | 15 +- 6 files changed, 159 insertions(+), 154 deletions(-) create mode 100644 pkg/sdk/secrets_gen_integration_test.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go index 193e09b18e..44ebf3c3ef 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -4,7 +4,9 @@ package objectassert import ( "fmt" + "slices" "testing" + "time" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" @@ -30,7 +32,7 @@ func SecretFromObject(t *testing.T, secret *sdk.Secret) *SecretAssert { } } -func (s *SecretAssert) HasCreatedOn(expected string) *SecretAssert { +func (s *SecretAssert) HasCreatedOn(expected time.Time) *SecretAssert { s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { t.Helper() if o.CreatedOn != expected { @@ -110,14 +112,11 @@ func (s *SecretAssert) HasSecretType(expected string) *SecretAssert { return s } -func (s *SecretAssert) HasOauthScopes(expected string) *SecretAssert { +func (s *SecretAssert) HasOauthScopes(expected []string) *SecretAssert { s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { t.Helper() - if o.OauthScopes == nil { - return fmt.Errorf("expected oauth scopes to have value; got: nil") - } - if *o.OauthScopes != expected { - return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, *o.OauthScopes) + if !slices.Equal(o.OauthScopes, expected) { + return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, o.OauthScopes) } return nil }) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 23f51352d8..80e8227a7c 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -54,7 +54,7 @@ var secretDetails = g.PlainStruct("SecretDetails"). Field("Username", "*string"). Field("OauthAccessTokenExpiryTime", "*time.Time"). Field("OauthRefreshTokenExpiryTime", "*time.Time"). - Field("OauthScopes", "*string"). + Field("OauthScopes", "[]string"). Field("IntegrationName", "*string") var secretSet = g.NewQueryStruct("SecretSet"). diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index 7e35b008bc..39bc7b87a2 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -25,7 +25,7 @@ type CreateWithOAuthClientCredentialsFlowSecretOptions struct { secret bool `ddl:"static" sql:"SECRET"` IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` - secretType string `ddl:"static" sql:"TYPE = OAUTH2"` + Type string `ddl:"static" sql:"TYPE = OAUTH2"` ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` OauthScopes []ApiIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` @@ -55,7 +55,7 @@ type CreateWithBasicAuthenticationSecretOptions struct { secret bool `ddl:"static" sql:"SECRET"` IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` - Type string `ddl:"static" sql:"TYPE = PASSWORD"` + secretType string `ddl:"static" sql:"TYPE = PASSWORD"` Username string `ddl:"parameter,single_quotes" sql:"USERNAME"` Password string `ddl:"parameter,single_quotes" sql:"PASSWORD"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` @@ -160,7 +160,7 @@ type DescribeSecretOptions struct { name SchemaObjectIdentifier `ddl:"identifier"` } type secretDetailsDBRow struct { - CreatedOn string `db:"created_on"` + CreatedOn time.Time `db:"created_on"` Name string `db:"name"` SchemaName string `db:"schema_name"` DatabaseName string `db:"database_name"` @@ -174,7 +174,7 @@ type secretDetailsDBRow struct { IntegrationName sql.NullString `db:"integration_name"` } type SecretDetails struct { - CreatedOn string + CreatedOn time.Time Name string SchemaName string DatabaseName string diff --git a/pkg/sdk/secrets_gen_integration_test.go b/pkg/sdk/secrets_gen_integration_test.go new file mode 100644 index 0000000000..4474555b36 --- /dev/null +++ b/pkg/sdk/secrets_gen_integration_test.go @@ -0,0 +1,43 @@ +package sdk + +import "testing" + +func TestInt_Secrets(t *testing.T) { + // TODO: prepare common resources + + t.Run("CreateWithOAuthClientCredentialsFlow", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithBasicAuthentication", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("CreateWithGenericString", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Alter", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Drop", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Show", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("ShowByID", func(t *testing.T) { + // TODO: fill me + }) + + t.Run("Describe", func(t *testing.T) { + // TODO: fill me + }) +} diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 01b5ced01a..5d5b8ebf30 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -3,10 +3,12 @@ package sdk import "testing" func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithOAuthClientCredentialsFlowSecretOptions defaultOpts := func() *CreateWithOAuthClientCredentialsFlowSecretOptions { return &CreateWithOAuthClientCredentialsFlowSecretOptions{ + name: id, } } @@ -15,36 +17,38 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { var opts *CreateWithOAuthClientCredentialsFlowSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: invalid identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() - opts.name = emptySchemaObjectIdentifier + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.OrReplace = Bool(true) + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthClientCredentialsFlowSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("all options", func(t *testing.T) { - integration := randomAccountObjectIdentifier() + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + t.Run("all options", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.ApiIntegration = integration - opts.OauthScopes = []ApiIntegrationScope{{"test"}} - opts.Comment = String("foo") - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('test') COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithOAuthAuthorizationCodeFlowSecretOptions defaultOpts := func() *CreateWithOAuthAuthorizationCodeFlowSecretOptions { return &CreateWithOAuthAuthorizationCodeFlowSecretOptions{ + name: id, } } @@ -53,37 +57,38 @@ func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { var opts *CreateWithOAuthAuthorizationCodeFlowSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: invalid identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() - opts.name = emptySchemaObjectIdentifier + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.OrReplace = Bool(true) + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthAuthorizationCodeFlowSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("all options", func(t *testing.T) { - integration := randomAccountObjectIdentifier() + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + t.Run("all options", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.OauthRefreshToken = "foo" - opts.OauthRefreshTokenExpiryTime = "bar" - opts.ApiIntegration = integration - opts.Comment = String("test") - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 OAUTH_REFRESH_TOKEN = 'foo' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = 'bar' API_AUTHENTICATION = %s COMMENT = 'test'", id.FullyQualifiedName(), integration.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithBasicAuthenticationSecretOptions defaultOpts := func() *CreateWithBasicAuthenticationSecretOptions { return &CreateWithBasicAuthenticationSecretOptions{ + name: id, } } @@ -92,34 +97,38 @@ func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { var opts *CreateWithBasicAuthenticationSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: invalid identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() - opts.name = emptySchemaObjectIdentifier + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.OrReplace = Bool(true) + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithBasicAuthenticationSecretOptions", "OrReplace", "IfNotExists")) }) + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + t.Run("all options", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.Username = "foo" - opts.Password = "bar" - opts.Comment = String("test") - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = PASSWORD USERNAME = 'foo' PASSWORD = 'bar' COMMENT = 'test'", id.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_CreateWithGenericString(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid CreateWithGenericStringSecretOptions defaultOpts := func() *CreateWithGenericStringSecretOptions { return &CreateWithGenericStringSecretOptions{ + name: id, } } @@ -128,49 +137,39 @@ func TestSecrets_CreateWithGenericString(t *testing.T) { var opts *CreateWithGenericStringSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: invalid identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() - opts.name = emptySchemaObjectIdentifier + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.OrReplace = Bool(true) + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithGenericStringSecretOptions", "OrReplace", "IfNotExists")) }) + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + t.Run("all options", func(t *testing.T) { opts := defaultOpts() - opts.IfNotExists = Bool(true) - opts.SecretString = "test" - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = GENERIC_STRING SECRET_STRING = 'test'", id.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_Alter(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid AlterSecretOptions defaultOpts := func() *AlterSecretOptions { return &AlterSecretOptions{ - name: id, - } - } - setOpts := func() *AlterSecretOptions { - return &AlterSecretOptions{ - name: id, - Set: &SecretSet{}, - IfExists: Bool(true), - } - } - - unsetOpts := func() *AlterSecretOptions { - return &AlterSecretOptions{ - name: id, - Unset: &SecretUnset{}, - IfExists: Bool(true), + name: id, } } @@ -178,73 +177,38 @@ func TestSecrets_Alter(t *testing.T) { var opts *AlterSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: exactly one field from [opts.Set opts.Unset] should be present", func(t *testing.T) { opts := defaultOpts() + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) }) t.Run("validation: conflicting fields for [opts.Set.SetForOAuthClientCredentialsFlow opts.Set.SetForOAuthAuthorizationFlow opts.Set.SetForBasicAuthentication opts.Set.SetForGenericString]", func(t *testing.T) { - opts := setOpts() - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{{Scope: "foo"}}} - opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{OauthRefreshToken: String("foo"), OauthRefreshTokenExpiryTime: String("bar")} - opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{Username: String("foo"), Password: String("bar")} - opts.Set.SetForGenericString = &SetForGenericString{SecretString: String("secret")} + opts := defaultOpts() + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) }) - t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { - opts := setOpts() - opts.Set.Comment = String("test") - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{[]ApiIntegrationScope{{"sample_scope"}}} - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName()) - }) - - t.Run("alter: set options for Oauth Authorization Flow", func(t *testing.T) { - opts := setOpts() - opts.Set.Comment = String("test") - opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ - String("test_token"), - String("2024-11-11"), - } - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_REFRESH_TOKEN = 'test_token' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = '2024-11-11'", id.FullyQualifiedName()) - }) - - t.Run("alter: set options for Basic Authentication", func(t *testing.T) { - opts := setOpts() - opts.Set.Comment = String("test") - opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ - Username: String("foo"), - Password: String("bar"), - } - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' USERNAME = 'foo' PASSWORD = 'bar'", id.FullyQualifiedName()) - }) - - t.Run("alter: set comment only", func(t *testing.T) { - opts := setOpts() - opts.Set.Comment = String("test") - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test'", id.FullyQualifiedName()) - }) - - t.Run("alter: set options for Generic string", func(t *testing.T) { - opts := setOpts() - opts.Set.Comment = String("test") - opts.Set.SetForGenericString = &SetForGenericString{String("test")} - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' SECRET_STRING = 'test'", id.FullyQualifiedName()) + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) - t.Run("alter: unset options", func(t *testing.T) { - opts := unsetOpts() - opts.Unset.Comment = Bool(true) - assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = NULL", id.FullyQualifiedName()) + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_Drop(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid DropSecretOptions defaultOpts := func() *DropSecretOptions { return &DropSecretOptions{ + name: id, } } @@ -253,25 +217,27 @@ func TestSecrets_Drop(t *testing.T) { var opts *DropSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: invalid identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() - opts.name = emptySchemaObjectIdentifier + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("basic", func(t *testing.T) { opts := defaultOpts() - assertOptsValidAndSQLEquals(t, opts, "DROP SECRET %s", id.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) t.Run("all options", func(t *testing.T) { opts := defaultOpts() - opts.IfExists = Bool(true) - assertOptsValidAndSQLEquals(t, opts, "DROP SECRET IF EXISTS %s", id.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_Show(t *testing.T) { + // Minimal valid ShowSecretOptions defaultOpts := func() *ShowSecretOptions { return &ShowSecretOptions{} } @@ -283,33 +249,24 @@ func TestSecrets_Show(t *testing.T) { t.Run("basic", func(t *testing.T) { opts := defaultOpts() - assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS") - }) - - t.Run("show with like", func(t *testing.T) { - opts := defaultOpts() - opts.Like = &Like{ - Pattern: String("pattern"), - } - assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS LIKE 'pattern'") + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) - t.Run("show with in", func(t *testing.T) { + t.Run("all options", func(t *testing.T) { opts := defaultOpts() - opts.In = &ExtendedIn{ - In: In{ - Account: Bool(true), - }, - } - assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS IN ACCOUNT") + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } func TestSecrets_Describe(t *testing.T) { - id := randomSchemaObjectIdentifier() + id := randomSchemaObjectIdentifier() + // Minimal valid DescribeSecretOptions defaultOpts := func() *DescribeSecretOptions { return &DescribeSecretOptions{ + name: id, } } @@ -318,14 +275,21 @@ func TestSecrets_Describe(t *testing.T) { var opts *DescribeSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: invalid identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() - opts.name = emptySchemaObjectIdentifier + // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + }) + t.Run("all options", func(t *testing.T) { opts := defaultOpts() - assertOptsValidAndSQLEquals(t, opts, "DESCRIBE SECRET %s", id.FullyQualifiedName()) + // TODO: fill me + assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") }) } diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index a65a7cc61b..16ad532e8f 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -1,7 +1,6 @@ package testint import ( - "database/sql" assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" "testing" @@ -166,7 +165,7 @@ func TestInt_Secrets(t *testing.T) { HasName(id.Name()). HasComment("a"). HasSecretType("OAUTH2"). - HasOauthScopes("[foo, bar]"). + HasOauthScopes([]string{"foo", "bar"}). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), ) @@ -222,7 +221,7 @@ func TestInt_Secrets(t *testing.T) { assertions.AssertThat(t, objectassert.Secret(t, id). HasName(id.Name()). - HasOauthScopes("[]"). + HasOauthScopes([]string{}). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), ) @@ -408,7 +407,7 @@ func TestInt_Secrets(t *testing.T) { Name: id.Name(), SecretType: "OAUTH2", Comment: sdk.String(comment), - OauthScopes: sdk.String("[foo, bar]"), + OauthScopes: []string{"foo", "bar"}, IntegrationName: sdk.String(integrationId.Name()), }) @@ -423,7 +422,7 @@ func TestInt_Secrets(t *testing.T) { details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) + assert.Empty(t, details.Comment) }) t.Run("Alter: SecretWithOAuthAuthorizationCode", func(t *testing.T) { @@ -466,7 +465,7 @@ func TestInt_Secrets(t *testing.T) { details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) + assert.Empty(t, details.Comment) }) t.Run("Alter: SecretWithBasicAuthorization", func(t *testing.T) { @@ -508,7 +507,7 @@ func TestInt_Secrets(t *testing.T) { details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) + assert.Empty(t, details.Comment) }) t.Run("Alter: SecretWithGenericString", func(t *testing.T) { @@ -537,7 +536,7 @@ func TestInt_Secrets(t *testing.T) { details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.Equal(t, details.Comment, sql.NullString{String: "", Valid: false}) + assert.Empty(t, details.Comment) }) t.Run("Drop", func(t *testing.T) { From 59c7b147eaec46903ed37cd422d3d610ffae73be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 23 Sep 2024 14:58:39 +0200 Subject: [PATCH 23/65] readded unit tests --- pkg/sdk/secrets_gen_test.go | 232 +++++++++++++++++++++--------------- 1 file changed, 134 insertions(+), 98 deletions(-) diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 5d5b8ebf30..01b5ced01a 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -3,12 +3,10 @@ package sdk import "testing" func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithOAuthClientCredentialsFlowSecretOptions + defaultOpts := func() *CreateWithOAuthClientCredentialsFlowSecretOptions { return &CreateWithOAuthClientCredentialsFlowSecretOptions{ - name: id, } } @@ -17,38 +15,36 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { var opts *CreateWithOAuthClientCredentialsFlowSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthClientCredentialsFlowSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.ApiIntegration = integration + opts.OauthScopes = []ApiIntegrationScope{{"test"}} + opts.Comment = String("foo") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('test') COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) } func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithOAuthAuthorizationCodeFlowSecretOptions + defaultOpts := func() *CreateWithOAuthAuthorizationCodeFlowSecretOptions { return &CreateWithOAuthAuthorizationCodeFlowSecretOptions{ - name: id, } } @@ -57,38 +53,37 @@ func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { var opts *CreateWithOAuthAuthorizationCodeFlowSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthAuthorizationCodeFlowSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.OauthRefreshToken = "foo" + opts.OauthRefreshTokenExpiryTime = "bar" + opts.ApiIntegration = integration + opts.Comment = String("test") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 OAUTH_REFRESH_TOKEN = 'foo' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = 'bar' API_AUTHENTICATION = %s COMMENT = 'test'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) } func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithBasicAuthenticationSecretOptions + defaultOpts := func() *CreateWithBasicAuthenticationSecretOptions { return &CreateWithBasicAuthenticationSecretOptions{ - name: id, } } @@ -97,38 +92,34 @@ func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { var opts *CreateWithBasicAuthenticationSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithBasicAuthenticationSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.Username = "foo" + opts.Password = "bar" + opts.Comment = String("test") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = PASSWORD USERNAME = 'foo' PASSWORD = 'bar' COMMENT = 'test'", id.FullyQualifiedName()) }) } func TestSecrets_CreateWithGenericString(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid CreateWithGenericStringSecretOptions + defaultOpts := func() *CreateWithGenericStringSecretOptions { return &CreateWithGenericStringSecretOptions{ - name: id, } } @@ -137,78 +128,123 @@ func TestSecrets_CreateWithGenericString(t *testing.T) { var opts *CreateWithGenericStringSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.IfNotExists = Bool(true) + opts.OrReplace = Bool(true) assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithGenericStringSecretOptions", "OrReplace", "IfNotExists")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfNotExists = Bool(true) + opts.SecretString = "test" + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = GENERIC_STRING SECRET_STRING = 'test'", id.FullyQualifiedName()) }) } func TestSecrets_Alter(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid AlterSecretOptions + defaultOpts := func() *AlterSecretOptions { return &AlterSecretOptions{ - name: id, } } + setOpts := func() *AlterSecretOptions { + return &AlterSecretOptions{ + name: id, + Set: &SecretSet{}, + IfExists: Bool(true), + } + } + + unsetOpts := func() *AlterSecretOptions { + return &AlterSecretOptions{ + name: id, + Unset: &SecretUnset{}, + IfExists: Bool(true), + } + } + t.Run("validation: nil options", func(t *testing.T) { var opts *AlterSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) + t.Run("validation: exactly one field from [opts.Set opts.Unset] should be present", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) }) t.Run("validation: conflicting fields for [opts.Set.SetForOAuthClientCredentialsFlow opts.Set.SetForOAuthAuthorizationFlow opts.Set.SetForBasicAuthentication opts.Set.SetForGenericString]", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me + opts := setOpts() + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{{Scope: "foo"}}} + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{OauthRefreshToken: String("foo"), OauthRefreshTokenExpiryTime: String("bar")} + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{Username: String("foo"), Password: String("bar")} + opts.Set.SetForGenericString = &SetForGenericString{SecretString: String("secret")} assertOptsInvalidJoinedErrors(t, opts, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{[]ApiIntegrationScope{{"sample_scope"}}} + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName()) }) - t.Run("all options", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + t.Run("alter: set options for Oauth Authorization Flow", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ + String("test_token"), + String("2024-11-11"), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_REFRESH_TOKEN = 'test_token' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = '2024-11-11'", id.FullyQualifiedName()) + }) + + t.Run("alter: set options for Basic Authentication", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ + Username: String("foo"), + Password: String("bar"), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' USERNAME = 'foo' PASSWORD = 'bar'", id.FullyQualifiedName()) + }) + + t.Run("alter: set comment only", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test'", id.FullyQualifiedName()) + }) + + t.Run("alter: set options for Generic string", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForGenericString = &SetForGenericString{String("test")} + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' SECRET_STRING = 'test'", id.FullyQualifiedName()) + }) + + t.Run("alter: unset options", func(t *testing.T) { + opts := unsetOpts() + opts.Unset.Comment = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = NULL", id.FullyQualifiedName()) }) } func TestSecrets_Drop(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid DropSecretOptions + defaultOpts := func() *DropSecretOptions { return &DropSecretOptions{ - name: id, } } @@ -217,27 +253,25 @@ func TestSecrets_Drop(t *testing.T) { var opts *DropSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) t.Run("basic", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + assertOptsValidAndSQLEquals(t, opts, "DROP SECRET %s", id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.IfExists = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "DROP SECRET IF EXISTS %s", id.FullyQualifiedName()) }) } func TestSecrets_Show(t *testing.T) { - // Minimal valid ShowSecretOptions defaultOpts := func() *ShowSecretOptions { return &ShowSecretOptions{} } @@ -249,24 +283,33 @@ func TestSecrets_Show(t *testing.T) { t.Run("basic", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS") }) - t.Run("all options", func(t *testing.T) { + t.Run("show with like", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{ + Pattern: String("pattern"), + } + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS LIKE 'pattern'") + }) + + t.Run("show with in", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + opts.In = &ExtendedIn{ + In: In{ + Account: Bool(true), + }, + } + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS IN ACCOUNT") }) } func TestSecrets_Describe(t *testing.T) { - id := randomSchemaObjectIdentifier() - // Minimal valid DescribeSecretOptions + defaultOpts := func() *DescribeSecretOptions { return &DescribeSecretOptions{ - name: id, } } @@ -275,21 +318,14 @@ func TestSecrets_Describe(t *testing.T) { var opts *DescribeSecretOptions = nil assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + t.Run("validation: invalid identifier", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me + opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) - t.Run("basic", func(t *testing.T) { - opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() - // TODO: fill me - assertOptsValidAndSQLEquals(t, opts, "TODO: fill me") + assertOptsValidAndSQLEquals(t, opts, "DESCRIBE SECRET %s", id.FullyQualifiedName()) }) } From df7a8ff42eab42c7f56edf575a8fc100e9ce7d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 24 Sep 2024 09:46:53 +0200 Subject: [PATCH 24/65] added CreateApiAuthenticationWithRequest to security integration client --- .../helpers/security_integration_client.go | 13 +++++++++++++ pkg/sdk/security_integrations_dto_gen.go | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/pkg/acceptance/helpers/security_integration_client.go b/pkg/acceptance/helpers/security_integration_client.go index 23b84c500f..5931b8fa9d 100644 --- a/pkg/acceptance/helpers/security_integration_client.go +++ b/pkg/acceptance/helpers/security_integration_client.go @@ -56,6 +56,19 @@ func (c *SecurityIntegrationClient) CreateScim(t *testing.T) (*sdk.SecurityInteg return c.CreateScimWithRequest(t, sdk.NewCreateScimSecurityIntegrationRequest(c.ids.RandomAccountObjectIdentifier(), sdk.ScimSecurityIntegrationScimClientGeneric, sdk.ScimSecurityIntegrationRunAsRoleGenericScimProvisioner)) } +func (c *SecurityIntegrationClient) CreateApiAuthenticationClientCredentialsWithRequest(t *testing.T, request *sdk.CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest) (*sdk.SecurityIntegration, func()) { + t.Helper() + ctx := context.Background() + + err := c.client().CreateApiAuthenticationWithClientCredentialsFlow(ctx, request) + require.NoError(t, err) + + si, err := c.client().ShowByID(ctx, request.GetName()) + require.NoError(t, err) + + return si, c.DropSecurityIntegrationFunc(t, request.GetName()) +} + func (c *SecurityIntegrationClient) UpdateSaml2(t *testing.T, request *sdk.AlterSaml2SecurityIntegrationRequest) { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/security_integrations_dto_gen.go b/pkg/sdk/security_integrations_dto_gen.go index 7726e8d8b8..43b2d73aae 100644 --- a/pkg/sdk/security_integrations_dto_gen.go +++ b/pkg/sdk/security_integrations_dto_gen.go @@ -40,6 +40,10 @@ type CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest Comment *string } +func (r *CreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest) GetName() AccountObjectIdentifier { + return r.name +} + type CreateApiAuthenticationWithAuthorizationCodeGrantFlowSecurityIntegrationRequest struct { OrReplace *bool IfNotExists *bool From eed310a33f6504e4c6efdfb888b33adf06baa05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 24 Sep 2024 09:47:23 +0200 Subject: [PATCH 25/65] changes to secret after review, all tests pass --- pkg/acceptance/helpers/secret_client.go | 44 +++- pkg/sdk/secrets_def.go | 6 +- pkg/sdk/secrets_gen.go | 6 +- pkg/sdk/secrets_gen_test.go | 27 +- .../testint/secrets_gen_integration_test.go | 236 ++++++++---------- 5 files changed, 181 insertions(+), 138 deletions(-) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index 314f6248b5..f2657b39bb 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -25,7 +25,35 @@ func (c *SecretClient) client() sdk.Secrets { return c.context.client.Secrets } -func (c *SecretClient) CreateWithBasicFlow(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) (*sdk.Secret, func()) { +func (c *SecretClient) CreateWithOAuthClientCredentialsFlow(t *testing.T, id sdk.SchemaObjectIdentifier, apiIntegration sdk.AccountObjectIdentifier, oauthScopes []sdk.ApiIntegrationScope) (*sdk.Secret, func()) { + t.Helper() + ctx := context.Background() + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) + + err := c.client().CreateWithOAuthClientCredentialsFlow(ctx, request) + require.NoError(t, err) + + secret, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return secret, c.DropFunc(t, id) +} + +func (c *SecretClient) CreateWithOAuthAuthorizationCodeFlow(t *testing.T, id sdk.SchemaObjectIdentifier, apiIntegration sdk.AccountObjectIdentifier, refreshToken, refreshTokenExpiryTime string) (*sdk.Secret, func()) { + t.Helper() + ctx := context.Background() + request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, apiIntegration) + + err := c.client().CreateWithOAuthAuthorizationCodeFlow(ctx, request) + require.NoError(t, err) + + secret, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return secret, c.DropFunc(t, id) +} + +func (c *SecretClient) CreateWithBasicAuthenticationFlow(t *testing.T, id sdk.SchemaObjectIdentifier, username, password string) (*sdk.Secret, func()) { t.Helper() ctx := context.Background() request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password) @@ -39,6 +67,20 @@ func (c *SecretClient) CreateWithBasicFlow(t *testing.T, id sdk.SchemaObjectIden return secret, c.DropFunc(t, id) } +func (c *SecretClient) CreateWithGenericString(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) (*sdk.Secret, func()) { + t.Helper() + ctx := context.Background() + request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) + + err := c.client().CreateWithGenericString(ctx, request) + require.NoError(t, err) + + secret, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return secret, c.DropFunc(t, id) +} + func (c *SecretClient) DropFunc(t *testing.T, id sdk.SchemaObjectIdentifier) func() { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 80e8227a7c..d4edb0494f 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -104,7 +104,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). ListAssignment("OAUTH_SCOPES", "ApiIntegrationScope", g.ParameterOptions().Parentheses().Required()). OptionalComment(). @@ -120,7 +120,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). TextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). TextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). @@ -152,7 +152,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("Type", "string", g.StaticOptions().SQL("TYPE = GENERIC_STRING")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = GENERIC_STRING")). TextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes().Required()). OptionalComment(). WithValidation(g.ValidIdentifier, "name"). diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index 39bc7b87a2..8a5a819e5f 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -25,7 +25,7 @@ type CreateWithOAuthClientCredentialsFlowSecretOptions struct { secret bool `ddl:"static" sql:"SECRET"` IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` - Type string `ddl:"static" sql:"TYPE = OAUTH2"` + secretType string `ddl:"static" sql:"TYPE = OAUTH2"` ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` OauthScopes []ApiIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` @@ -41,7 +41,7 @@ type CreateWithOAuthAuthorizationCodeFlowSecretOptions struct { secret bool `ddl:"static" sql:"SECRET"` IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` - Type string `ddl:"static" sql:"TYPE = OAUTH2"` + secretType string `ddl:"static" sql:"TYPE = OAUTH2"` OauthRefreshToken string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` OauthRefreshTokenExpiryTime string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN_EXPIRY_TIME"` ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` @@ -68,7 +68,7 @@ type CreateWithGenericStringSecretOptions struct { secret bool `ddl:"static" sql:"SECRET"` IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` - Type string `ddl:"static" sql:"TYPE = GENERIC_STRING"` + secretType string `ddl:"static" sql:"TYPE = GENERIC_STRING"` SecretString string `ddl:"parameter,single_quotes" sql:"SECRET_STRING"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` } diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 01b5ced01a..35a4a86988 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -38,6 +38,17 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { opts.Comment = String("foo") assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('test') COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) + + t.Run("empty oauth scopes list", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + + opts := defaultOpts() + opts.IfNotExists = Bool(true) + opts.ApiIntegration = integration + opts.OauthScopes = []ApiIntegrationScope{} + opts.Comment = String("foo") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) + }) } func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { @@ -143,9 +154,10 @@ func TestSecrets_CreateWithGenericString(t *testing.T) { t.Run("all options", func(t *testing.T) { opts := defaultOpts() + opts.Comment = String("test") opts.IfNotExists = Bool(true) opts.SecretString = "test" - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = GENERIC_STRING SECRET_STRING = 'test'", id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = GENERIC_STRING SECRET_STRING = 'test' COMMENT = 'test'", id.FullyQualifiedName()) }) } @@ -196,16 +208,23 @@ func TestSecrets_Alter(t *testing.T) { t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { opts := setOpts() opts.Set.Comment = String("test") - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{[]ApiIntegrationScope{{"sample_scope"}}} + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{{"sample_scope"}}} assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName()) }) + t.Run("alter: set options for Oauth Client Credentials Flow - empty oauth scopes list", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{}} + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test'", id.FullyQualifiedName()) + }) + t.Run("alter: set options for Oauth Authorization Flow", func(t *testing.T) { opts := setOpts() opts.Set.Comment = String("test") opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ - String("test_token"), - String("2024-11-11"), + OauthRefreshToken: String("test_token"), + OauthRefreshTokenExpiryTime: String("2024-11-11"), } assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_REFRESH_TOKEN = 'test_token' OAUTH_REFRESH_TOKEN_EXPIRY_TIME = '2024-11-11'", id.FullyQualifiedName()) }) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 16ad532e8f..e09ef680e3 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -17,24 +17,13 @@ func TestInt_Secrets(t *testing.T) { ctx := testContext(t) integrationId := testClientHelper().Ids.RandomAccountObjectIdentifier() - refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) - cleanupIntegration := func(t *testing.T, integrationId sdk.AccountObjectIdentifier) func() { - t.Helper() - return func() { - err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(integrationId).WithIfExists(true)) - require.NoError(t, err) - } - } - - err := client.SecurityIntegrations.CreateApiAuthenticationWithClientCredentialsFlow( - ctx, + _, apiIntegrationCleanup := testClientHelper().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), ) - require.NoError(t, err) - t.Cleanup(cleanupIntegration(t, integrationId)) + t.Cleanup(apiIntegrationCleanup) stringDateToSnowflakeTimeFormat := func(inputLayout, date string) *time.Time { parsedTime, err := time.Parse(inputLayout, date) @@ -47,87 +36,6 @@ func TestInt_Secrets(t *testing.T) { return &adjustedTime } - createSecretWithOAuthClientCredentialsFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, scopes []sdk.ApiIntegrationScope, with func(*sdk.CreateWithOAuthClientCredentialsFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { - t.Helper() - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, scopes) - if with != nil { - with(request) - } - err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) - - return secret, id - } - - createSecretWithOAuthAuthorizationCodeFlow := func(t *testing.T, integrationId sdk.AccountObjectIdentifier, refreshToken, refreshTokenExpiryTime string, with func(*sdk.CreateWithOAuthAuthorizationCodeFlowSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { - t.Helper() - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, integrationId) - if with != nil { - with(request) - } - err := client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) - - return secret, id - } - - createSecretWithBasicAuthentication := func(t *testing.T, username, password string, with func(*sdk.CreateWithBasicAuthenticationSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { - t.Helper() - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, username, password) - if with != nil { - with(request) - } - err := client.Secrets.CreateWithBasicAuthentication(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) - - return secret, id - } - - createSecretWithGenericString := func(t *testing.T, secretString string, with func(options *sdk.CreateWithGenericStringSecretRequest)) (*sdk.Secret, sdk.SchemaObjectIdentifier) { - t.Helper() - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) - if with != nil { - with(request) - } - err := client.Secrets.CreateWithGenericString(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) - - return secret, id - } - - createSecretWithId := func(t *testing.T, id sdk.SchemaObjectIdentifier) *sdk.Secret { - t.Helper() - request := sdk.NewCreateWithGenericStringSecretRequest(id, "foo") - err := client.Secrets.CreateWithGenericString(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - - secret, err := client.Secrets.ShowByID(ctx, id) - require.NoError(t, err) - - return secret - } - type secretDetails struct { Name string Comment *string @@ -209,8 +117,8 @@ func TestInt_Secrets(t *testing.T) { }) }) - // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret should inherit security_integration scopes, but it does not do so - t.Run("Create: SecretWithOAuthClientCredentialsFlow - No Scopes Specified", func(t *testing.T) { + // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret with empty oauth_scopes list should inherit scopes from security_integration, but it does not + t.Run("Create: SecretWithOAuthClientCredentialsFlow - Empty Scopes List", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifier() request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, []sdk.ApiIntegrationScope{}) @@ -231,7 +139,10 @@ func TestInt_Secrets(t *testing.T) { details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) - assert.NotEqual(t, details.OauthScopes, "[foo, bar]") + + assert.NotContains(t, details.OauthScopes, "foo") + assert.NotContains(t, details.OauthScopes, "bar") + assert.Empty(t, details.OauthScopes) }) t.Run("Create: SecretWithOAuthAuthorizationCodeFlow - refreshTokenExpiry date format", func(t *testing.T) { @@ -386,7 +297,10 @@ func TestInt_Secrets(t *testing.T) { t.Run("Alter: SecretWithOAuthClientCredentials", func(t *testing.T) { comment := random.Comment() - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + _, secretDropFunc := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretDropFunc) + setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). @@ -429,7 +343,11 @@ func TestInt_Secrets(t *testing.T) { comment := random.Comment() alteredRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) - _, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + _, secretCleanup := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id, integrationId, "foo", refreshTokenExpiryTime) + t.Cleanup(secretCleanup) + + //_, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). @@ -470,8 +388,11 @@ func TestInt_Secrets(t *testing.T) { t.Run("Alter: SecretWithBasicAuthorization", func(t *testing.T) { comment := random.Comment() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + _, secretCleanup := testClientHelper().Secret.CreateWithBasicAuthenticationFlow(t, id, "foo", "foo") + t.Cleanup(secretCleanup) - _, id := createSecretWithBasicAuthentication(t, "foo", "foo", nil) setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). @@ -512,7 +433,10 @@ func TestInt_Secrets(t *testing.T) { t.Run("Alter: SecretWithGenericString", func(t *testing.T) { comment := random.Comment() - _, id := createSecretWithGenericString(t, "foo", nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + _, secretCleanup := testClientHelper().Secret.CreateWithGenericString(t, id, "foo") + t.Cleanup(secretCleanup) setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). @@ -540,7 +464,10 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Drop", func(t *testing.T) { - _, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + //_, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + _, secretCleanup := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretCleanup) secret, err := client.Secrets.ShowByID(ctx, id) require.NotNil(t, secret) @@ -562,10 +489,17 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show", func(t *testing.T) { - secretOAuthClientCredentials, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) - secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) - secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) - secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) + secretOAuthClientCredentials, secretCleanupClientCredentialsFlow := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretCleanupClientCredentialsFlow) + + secretOAuthAuthorizationCode, secretCleanupAuthorizationCodeFlow := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), integrationId, "foo", refreshTokenExpiryTime) + t.Cleanup(secretCleanupAuthorizationCodeFlow) + + secretBasicAuthentication, secretCleanupBasicAuthentication := testClientHelper().Secret.CreateWithBasicAuthenticationFlow(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), "foo", "bar") + t.Cleanup(secretCleanupBasicAuthentication) + + secretGenericString, secretCleanupGenericString := testClientHelper().Secret.CreateWithGenericString(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), "foo") + t.Cleanup(secretCleanupGenericString) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest()) require.NoError(t, err) @@ -576,8 +510,16 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with Like", func(t *testing.T) { - secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) - secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}, nil) + //secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + //secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}, nil) + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + secret1, secretCleanup1 := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id1, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretCleanup1) + + secret2, secretCleanup2 := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id2, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}) + t.Cleanup(secretCleanup2) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ Pattern: sdk.String(id1.Name()), @@ -588,8 +530,17 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthAuthorization with Like", func(t *testing.T) { - secret1, id1 := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_1", refreshTokenExpiryTime, nil) - secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) + //secret2, id1 := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_1", refreshTokenExpiryTime, nil) + //secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) + + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + secret1, secretCleanup1 := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id1, integrationId, "foo", refreshTokenExpiryTime) + t.Cleanup(secretCleanup1) + + secret2, secretCleanup2 := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id2, integrationId, "bar", refreshTokenExpiryTime) + t.Cleanup(secretCleanup2) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ Pattern: sdk.String(id1.Name()), @@ -600,8 +551,14 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithBasicAuthentication with Like", func(t *testing.T) { - secret1, id1 := createSecretWithBasicAuthentication(t, "foo_1", "bar_1", nil) - secret2, _ := createSecretWithBasicAuthentication(t, "foo_2", "bar_2", nil) + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + secret1, secretCleanup1 := testClientHelper().Secret.CreateWithBasicAuthenticationFlow(t, id1, "foo", "foo") + t.Cleanup(secretCleanup1) + + secret2, secretCleanup2 := testClientHelper().Secret.CreateWithBasicAuthenticationFlow(t, id2, "bar", "bar") + t.Cleanup(secretCleanup2) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ Pattern: sdk.String(id1.Name()), @@ -612,8 +569,14 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithGenericString with Like", func(t *testing.T) { - secret1, id1 := createSecretWithGenericString(t, "foo_1", nil) - secret2, _ := createSecretWithGenericString(t, "foo_2", nil) + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + secret1, secretCleanup1 := testClientHelper().Secret.CreateWithGenericString(t, id1, "foo") + t.Cleanup(secretCleanup1) + + secret2, secretCleanup2 := testClientHelper().Secret.CreateWithGenericString(t, id2, "bar") + t.Cleanup(secretCleanup2) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithLike(sdk.Like{ Pattern: sdk.String(id1.Name()), @@ -624,7 +587,9 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with In", func(t *testing.T) { - secret, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + secret, secretCleanup := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretCleanup) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) @@ -640,7 +605,11 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthAuthorizationCodeFlow with In", func(t *testing.T) { - secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + //secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + secret, secretCleanup := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id, integrationId, "foo", refreshTokenExpiryTime) + t.Cleanup(secretCleanup) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) @@ -656,10 +625,19 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: with In", func(t *testing.T) { - secretOAuthClientCredentials, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) - secretOAuthAuthorizationCode, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) - secretBasicAuthentication, _ := createSecretWithBasicAuthentication(t, "foo", "bar", nil) - secretGenericString, _ := createSecretWithGenericString(t, "foo", nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + secretOAuthClientCredentials, secretCleanupClientCredentialsFlow := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretCleanupClientCredentialsFlow) + + secretOAuthAuthorizationCode, secretCleanupAuthorizationFlow := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), integrationId, "foo", refreshTokenExpiryTime) + t.Cleanup(secretCleanupAuthorizationFlow) + + secretBasicAuthentication, secretCleanupBasicAuthentication := testClientHelper().Secret.CreateWithBasicAuthenticationFlow(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), "foo", "foo") + t.Cleanup(secretCleanupBasicAuthentication) + + secretGenericString, secretCleanupWithGenericString := testClientHelper().Secret.CreateWithGenericString(t, testClientHelper().Ids.RandomSchemaObjectIdentifier(), "foo") + t.Cleanup(secretCleanupWithGenericString) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) @@ -684,7 +662,9 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithGenericString with In", func(t *testing.T) { - secret, id := createSecretWithGenericString(t, "foo", nil) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + secret, secretCleanup := testClientHelper().Secret.CreateWithGenericString(t, id, "foo") + t.Cleanup(secretCleanup) returnedSecrets, err := client.Secrets.Show(ctx, sdk.NewShowSecretRequest().WithIn(sdk.ExtendedIn{In: sdk.In{Account: sdk.Pointer(true)}})) require.NoError(t, err) @@ -706,8 +686,11 @@ func TestInt_Secrets(t *testing.T) { id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() id2 := testClientHelper().Ids.NewSchemaObjectIdentifierInSchema(id1.Name(), schema.ID()) - createSecretWithId(t, id1) - createSecretWithId(t, id2) + _, cleanup1 := testClientHelper().Secret.CreateWithGenericString(t, id1, "foo") + t.Cleanup(cleanup1) + + _, cleanup2 := testClientHelper().Secret.CreateWithGenericString(t, id2, "bar") + t.Cleanup(cleanup2) secretShowResult1, err := client.Secrets.ShowByID(ctx, id1) require.NoError(t, err) @@ -719,16 +702,15 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Describe", func(t *testing.T) { - _, id := createSecretWithGenericString(t, "foo", func(req *sdk.CreateWithGenericStringSecretRequest) { - req.WithComment("Lorem ipsum") - }) + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + _, secretCleanup := testClientHelper().Secret.CreateWithGenericString(t, id, "foo") + t.Cleanup(secretCleanup) details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) assertSecretDetails(details, secretDetails{ Name: id.Name(), - Comment: sdk.String("Lorem ipsum"), SecretType: "GENERIC_STRING", }) }) From af2e0306905dc35892959154416d90cc23d0a568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 24 Sep 2024 09:56:24 +0200 Subject: [PATCH 26/65] added assert for details --- pkg/sdk/testint/secrets_gen_integration_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index e09ef680e3..ba04602865 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -454,12 +454,19 @@ func TestInt_Secrets(t *testing.T) { *sdk.NewSecretUnsetRequest(). WithComment(true), ) + err = client.Secrets.Alter(ctx, unsetRequest) require.NoError(t, err) details, err := client.Secrets.Describe(ctx, id) require.NoError(t, err) + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "GENERIC_STRING", + Comment: nil, + }) + assert.Empty(t, details.Comment) }) From 4ccc4e7c6e391151251af148c4f0d03d559d2344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 24 Sep 2024 09:58:54 +0200 Subject: [PATCH 27/65] linter hints applied --- pkg/sdk/secrets_impl_gen.go | 12 ------------ .../testint/secrets_gen_integration_test.go | 18 ++++++++---------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index 0786ecb8a7..4464553f9a 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -128,53 +128,41 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Set != nil { - opts.Set = &SecretSet{ Comment: r.Set.Comment, } if r.Set.SetForOAuthClientCredentialsFlow != nil { - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{ OauthScopes: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes, } - } if r.Set.SetForOAuthAuthorizationFlow != nil { - opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ OauthRefreshToken: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshToken, OauthRefreshTokenExpiryTime: r.Set.SetForOAuthAuthorizationFlow.OauthRefreshTokenExpiryTime, } - } if r.Set.SetForBasicAuthentication != nil { - opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{ Username: r.Set.SetForBasicAuthentication.Username, Password: r.Set.SetForBasicAuthentication.Password, } - } if r.Set.SetForGenericString != nil { - opts.Set.SetForGenericString = &SetForGenericString{ SecretString: r.Set.SetForGenericString.SecretString, } - } - } if r.Unset != nil { - opts.Unset = &SecretUnset{ Comment: r.Unset.Comment, } - } return opts diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index ba04602865..76df4308ef 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -1,11 +1,12 @@ package testint import ( - assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" "testing" "time" + assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" @@ -278,7 +279,6 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Create: WithGenericString - empty secret_string", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() request := sdk.NewCreateWithGenericStringSecretRequest(id, "") @@ -347,7 +347,6 @@ func TestInt_Secrets(t *testing.T) { _, secretCleanup := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id, integrationId, "foo", refreshTokenExpiryTime) t.Cleanup(secretCleanup) - //_, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) setRequest := sdk.NewAlterSecretRequest(id). WithSet( *sdk.NewSecretSetRequest(). @@ -471,7 +470,6 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Drop", func(t *testing.T) { - //_, id := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) id := testClientHelper().Ids.RandomSchemaObjectIdentifier() _, secretCleanup := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) t.Cleanup(secretCleanup) @@ -517,8 +515,8 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with Like", func(t *testing.T) { - //secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) - //secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}, nil) + // secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) + // secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}, nil) id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -537,8 +535,8 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthAuthorization with Like", func(t *testing.T) { - //secret2, id1 := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_1", refreshTokenExpiryTime, nil) - //secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) + // secret2, id1 := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_1", refreshTokenExpiryTime, nil) + // secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -612,7 +610,7 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthAuthorizationCodeFlow with In", func(t *testing.T) { - //secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) + // secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) id := testClientHelper().Ids.RandomSchemaObjectIdentifier() secret, secretCleanup := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id, integrationId, "foo", refreshTokenExpiryTime) From 34983afc46d8be0a08ca27c1a058de7d458ac888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 24 Sep 2024 14:40:37 +0200 Subject: [PATCH 28/65] updated generator README with validation issue for ConflictingFields validationType --- pkg/sdk/poc/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/sdk/poc/README.md b/pkg/sdk/poc/README.md index 44af1e130b..1bac4c2a02 100644 --- a/pkg/sdk/poc/README.md +++ b/pkg/sdk/poc/README.md @@ -132,6 +132,18 @@ type SomeReq struct { } ``` +- ConflictingFields validation results in: +```go +if everyValueSet(...) { + errs = append(errs, errOneOf(...)) +} + +// for now need to be changed manually to example bellow but should result in: +if moreThanOneValueSet(...) { + errs = append(errs, errMoreThanOneOf(...)) +} +``` + ##### Known limitations - automatic array conversion is not recursive, so we're only supporting one level mapping - []Request1{ foo Request2, bar int } won't be converted, but []Request1{ foo string, bar int } will From bdf2585574da6c6a5f77546015d0d107907aae1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 24 Sep 2024 14:40:59 +0200 Subject: [PATCH 29/65] updated after re-review --- pkg/sdk/secrets_validations_gen.go | 4 ++-- pkg/sdk/testint/secrets_gen_integration_test.go | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/sdk/secrets_validations_gen.go b/pkg/sdk/secrets_validations_gen.go index b5f7e14800..f747d54ad7 100644 --- a/pkg/sdk/secrets_validations_gen.go +++ b/pkg/sdk/secrets_validations_gen.go @@ -76,8 +76,8 @@ func (opts *AlterSecretOptions) validate() error { errs = append(errs, errExactlyOneOf("AlterSecretOptions", "Set", "Unset")) } if valueSet(opts.Set) { - if everyValueSet(opts.Set.SetForOAuthClientCredentialsFlow, opts.Set.SetForOAuthAuthorizationFlow, opts.Set.SetForBasicAuthentication, opts.Set.SetForGenericString) { - errs = append(errs, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + if moreThanOneValueSet(opts.Set.SetForOAuthClientCredentialsFlow, opts.Set.SetForOAuthAuthorizationFlow, opts.Set.SetForBasicAuthentication, opts.Set.SetForGenericString) { + errs = append(errs, errMoreThanOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) } } return JoinErrors(errs...) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 76df4308ef..5503f362ee 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -18,6 +18,8 @@ func TestInt_Secrets(t *testing.T) { ctx := testContext(t) integrationId := testClientHelper().Ids.RandomAccountObjectIdentifier() + + // "YYYY-MM-DD" or "YYYY-MM-DD HH-MI-SS" format has to be used, otherwise Snowflake returns error: "Invalid date/time format" refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) _, apiIntegrationCleanup := testClientHelper().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, @@ -212,11 +214,11 @@ func TestInt_Secrets(t *testing.T) { require.NoError(t, err) t.Cleanup(testClientHelper().Secret.DropFunc(t, id)) - _, err = client.Secrets.ShowByID(ctx, id) + secret, err := client.Secrets.ShowByID(ctx, id) require.NoError(t, err) assertions.AssertThat(t, - objectassert.Secret(t, id). + objectassert.SecretFromObject(t, secret). HasName(id.Name()). HasComment(comment). HasSecretType("PASSWORD"). @@ -535,9 +537,6 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthAuthorization with Like", func(t *testing.T) { - // secret2, id1 := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_1", refreshTokenExpiryTime, nil) - // secret2, _ := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo_2", refreshTokenExpiryTime, nil) - id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() From 7791f33dae8de262967283156065ac9cee49972e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 25 Sep 2024 10:27:19 +0200 Subject: [PATCH 30/65] test changed to use errMoreThanOneOf --- pkg/sdk/secrets_gen_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 35a4a86988..4b591a7b7c 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -202,7 +202,7 @@ func TestSecrets_Alter(t *testing.T) { opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{OauthRefreshToken: String("foo"), OauthRefreshTokenExpiryTime: String("bar")} opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{Username: String("foo"), Password: String("bar")} opts.Set.SetForGenericString = &SetForGenericString{SecretString: String("secret")} - assertOptsInvalidJoinedErrors(t, opts, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) }) t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { From 1f1fb2b8dcf4742211e4c93ed63982ec37d03503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 26 Sep 2024 11:50:31 +0200 Subject: [PATCH 31/65] init commit for secret resource --- ...et_with_client_credentials_resource_ext.go | 14 ++ ...et_with_client_credentials_resource_gen.go | 107 +++++++++ ...ecret_with_client_credentials_model_gen.go | 143 ++++++++++++ pkg/acceptance/check_destroy.go | 3 + pkg/provider/provider.go | 1 + pkg/provider/resources/resources.go | 1 + .../secret_with_client_credentials_flow.go | 211 ++++++++++++++++++ ...client_credentials_flow_acceptance_test.go | 191 ++++++++++++++++ pkg/sdk/secrets_gen_integration_test.go | 43 ---- 9 files changed, 671 insertions(+), 43 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go create mode 100644 pkg/resources/secret_with_client_credentials_flow.go create mode 100644 pkg/resources/secret_with_client_credentials_flow_acceptance_test.go delete mode 100644 pkg/sdk/secrets_gen_integration_test.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go new file mode 100644 index 0000000000..5b3c26dd5d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go @@ -0,0 +1,14 @@ +package resourceassert + +import ( + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopes(expected []sdk.ApiIntegrationScope) *SecretWithClientCredentialsResourceAssert { + for i, v := range expected { + s.AddAssertion(assert.ValueSet(fmt.Sprintf("oauth_scopes.%d", i), v.Scope)) + } + return s +} diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go new file mode 100644 index 0000000000..12a36fa46d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go @@ -0,0 +1,107 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +type SecretWithClientCredentialsResourceAssert struct { + *assert.ResourceAssert +} + +func SecretWithClientCredentialsResource(t *testing.T, name string) *SecretWithClientCredentialsResourceAssert { + t.Helper() + + return &SecretWithClientCredentialsResourceAssert{ + ResourceAssert: assert.NewResourceAssert(name, "resource"), + } +} + +func ImportedSecretWithClientCredentialsResource(t *testing.T, id string) *SecretWithClientCredentialsResourceAssert { + t.Helper() + + return &SecretWithClientCredentialsResourceAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"), + } +} + +/////////////////////////////////// +// Attribute value string checks // +/////////////////////////////////// + +func (s *SecretWithClientCredentialsResourceAssert) HasApiAuthenticationString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("api_authentication", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasCommentString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("comment", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasDatabaseString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("database", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasFullyQualifiedNameString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("fully_qualified_name", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNameString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("name", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopesString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_scopes", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasSchemaString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("schema", expected)) + return s +} + +//////////////////////////// +// Attribute empty checks // +//////////////////////////// + +func (s *SecretWithClientCredentialsResourceAssert) HasNoApiAuthentication() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("api_authentication")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoComment() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("comment")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoDatabase() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("database")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoFullyQualifiedName() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("fully_qualified_name")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoName() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("name")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoOauthScopes() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("oauth_scopes")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoSchema() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("schema")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go new file mode 100644 index 0000000000..8601977c78 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go @@ -0,0 +1,143 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SecretWithClientCredentialsModel struct { + ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` + Comment tfconfig.Variable `json:"comment,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + OauthScopes tfconfig.Variable `json:"oauth_scopes,omitempty"` + Schema tfconfig.Variable `json:"schema,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func SecretWithClientCredentials( + resourceName string, + apiAuthentication string, + database string, + schema string, + name string, + oauthScopes []string, +) *SecretWithClientCredentialsModel { + s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithClientCredentials)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthScopes(oauthScopes) + s.WithSchema(schema) + return s +} + +func SecretWithClientCredentialsWithDefaultMeta( + apiAuthentication string, + database string, + name string, + oauthScopes []string, + schema string, +) *SecretWithClientCredentialsModel { + s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithClientCredentials)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthScopes(oauthScopes) + s.WithSchema(schema) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SecretWithClientCredentialsModel) WithApiAuthentication(apiAuthentication string) *SecretWithClientCredentialsModel { + s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) + return s +} + +func (s *SecretWithClientCredentialsModel) WithComment(comment string) *SecretWithClientCredentialsModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SecretWithClientCredentialsModel) WithDatabase(database string) *SecretWithClientCredentialsModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SecretWithClientCredentialsModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithClientCredentialsModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SecretWithClientCredentialsModel) WithName(name string) *SecretWithClientCredentialsModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +// oauth_scopes attribute type is not yet supported, so WithOauthScopes can't be generated + +func (s *SecretWithClientCredentialsModel) WithSchema(schema string) *SecretWithClientCredentialsModel { + s.Schema = tfconfig.StringVariable(schema) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SecretWithClientCredentialsModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.ApiAuthentication = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithCommentValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Comment = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Database = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.FullyQualifiedName = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithNameValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Name = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithOauthScopesValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.OauthScopes = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithSchemaValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Schema = value + return s +} +func (s*SecretWithClientCredentialsModel) WithOauthScopes(oauthScopes []string) *SecretWithClientCredentialsModel { + oauthScopesStringVariables := make([]tfconfig.Variable, len(oauthScopes)) + for i, v := range oauthScopes { + oauthScopesStringVariables[i] = tfconfig.StringVariable(v) + } + + s.OauthScopes = tfconfig.SetVariable(oauthScopesStringVariables...) + return s +} diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 0001f75795..0eacca77b0 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -173,6 +173,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ScimSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, + resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.Databases.ShowByID) + }, resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Databases.ShowByID) }, diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index edb760e412..f3a647471a 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -470,6 +470,7 @@ func getResources() map[string]*schema.Resource { "snowflake_saml2_integration": resources.SAML2Integration(), "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), + "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 219617d4e6..2eda84ffed 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -39,6 +39,7 @@ const ( Saml2SecurityIntegration resource = "snowflake_saml2_integration" Schema resource = "snowflake_schema" ScimSecurityIntegration resource = "snowflake_scim_integration" + SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go new file mode 100644 index 0000000000..b31e229eb4 --- /dev/null +++ b/pkg/resources/secret_with_client_credentials_flow.go @@ -0,0 +1,211 @@ +package resources + +import ( + "context" + "errors" + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretClientCredentialsSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "database": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "schema": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + }, + "oauth_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the secret.", + }, + FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, +} + +func SecretWithClientCredentials() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextSecret, + ReadContext: ReadContextSecret, + UpdateContext: UpdateContextSecret, + DeleteContext: DeleteContextSecret, + + Schema: secretClientCredentialsSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func CreateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + databaseName := d.Get("database").(string) + schemaName := d.Get("schema").(string) + name := d.Get("name").(string) + apiIntegrationString := d.Get("api_authentication").(string) + apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) + if err != nil { + return diag.FromErr(err) + } + + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + + stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) + oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) + for i, scope := range stringScopes { + oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} + } + + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) + + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) + } + + err = client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeResourceIdentifier(id)) + + return ReadContextSecret(ctx, d, meta) +} + +func ReadContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if secret == nil || err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, + } + } + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + if err = d.Set("name", secretDescription.Name); err != nil { + return diag.FromErr(err) + } + if err = d.Set("database", secretDescription.DatabaseName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("schema", secretDescription.SchemaName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { + return diag.FromErr(err) + } + if err = d.Set("comment", secretDescription.Comment); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChange("oauth_scopes") { + stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) + oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) + for i, scope := range stringScopes { + oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} + } + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest(oauthScopes) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthClientCredentialsFlow(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("comment") { + comment := d.Get("comment").(string) + request := sdk.NewAlterSecretRequest(id) + if len(comment) == 0 { + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + request.WithUnset(*unsetRequest) + } else { + setRequest := sdk.NewSecretSetRequest().WithComment(comment) + request.WithSet(*setRequest) + } + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextSecret(ctx, d, meta) +} + +func DeleteContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go new file mode 100644 index 0000000000..854d928b9e --- /dev/null +++ b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go @@ -0,0 +1,191 @@ +package resources_test + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := "aaa" + newComment := random.Comment() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), + ) + t.Cleanup(apiIntegrationCleanup) + + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}).WithComment(comment) + secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + Steps: []resource.TestStep{ + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(comment), + ), + resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), + ), + }, + // set oauth_scopes and comment in config + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{"foo"}). + WithComment(newComment)), + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), + ), + }, + // unset comment + { + Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(newComment), nil), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). + HasCommentString(""), + ), + }, + // destroy + { + Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), + Destroy: true, + }, + // create without comment + { + Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo", "bar"})), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(""), + ), + resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), + ), + }, + // import + { + ResourceName: secretModelWithoutComment.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "api_authentication", integrationId.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_scopes.#", "2"), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), + ), + }, + }, + }) +} + +func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), + ) + t.Cleanup(apiIntegrationCleanup) + + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, nil) + secretModelWithScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo"}) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + Steps: []resource.TestStep{ + // Create secret without providing oauth_scopes value + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(""), + ), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "0"), + ), + }, + // Set oauth_scopes + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{"foo"}), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()), + assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), + ), + }, + }, + }) +} diff --git a/pkg/sdk/secrets_gen_integration_test.go b/pkg/sdk/secrets_gen_integration_test.go deleted file mode 100644 index 4474555b36..0000000000 --- a/pkg/sdk/secrets_gen_integration_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package sdk - -import "testing" - -func TestInt_Secrets(t *testing.T) { - // TODO: prepare common resources - - t.Run("CreateWithOAuthClientCredentialsFlow", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithBasicAuthentication", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithGenericString", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Alter", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Drop", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Show", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("ShowByID", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Describe", func(t *testing.T) { - // TODO: fill me - }) -} From 366313ff7a191339ea28c14062fd1932d09e1e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 26 Sep 2024 14:00:28 +0200 Subject: [PATCH 32/65] adding secret common to avoid duplication --- .../objectassert/secret_snowflake_gen.go | 3 +- ...et_with_authorization_code_resource_gen.go | 117 +++++++++ .../secret_show_output_gen.go | 87 +++++++ ...ecret_with_authorization_code_model_gen.go | 152 ++++++++++++ pkg/provider/provider.go | 1 + pkg/resources/secret_common.go | 105 ++++++++ ...cret_with_authorization_code_grant_flow.go | 228 ++++++++++++++++++ ...ization_code_grant_flow_acceptance_test.go | 22 ++ .../secret_with_client_credentials_flow.go | 28 ++- ...client_credentials_flow_acceptance_test.go | 2 +- pkg/schemas/gen/sdk_show_result_structs.go | 1 + pkg/schemas/secret_gen.go | 68 ++++++ 12 files changed, 801 insertions(+), 13 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go create mode 100644 pkg/resources/secret_common.go create mode 100644 pkg/resources/secret_with_authorization_code_grant_flow.go create mode 100644 pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go create mode 100644 pkg/schemas/secret_gen.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go index 44ebf3c3ef..2a61443f09 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -4,7 +4,6 @@ package objectassert import ( "fmt" - "slices" "testing" "time" @@ -115,7 +114,7 @@ func (s *SecretAssert) HasSecretType(expected string) *SecretAssert { func (s *SecretAssert) HasOauthScopes(expected []string) *SecretAssert { s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { t.Helper() - if !slices.Equal(o.OauthScopes, expected) { + if o.OauthScopes != expected { return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, o.OauthScopes) } return nil diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go new file mode 100644 index 0000000000..610faae14e --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go @@ -0,0 +1,117 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +type SecretWithAuthorizationCodeResourceAssert struct { + *assert.ResourceAssert +} + +func SecretWithAuthorizationCodeResource(t *testing.T, name string) *SecretWithAuthorizationCodeResourceAssert { + t.Helper() + + return &SecretWithAuthorizationCodeResourceAssert{ + ResourceAssert: assert.NewResourceAssert(name, "resource"), + } +} + +func ImportedSecretWithAuthorizationCodeResource(t *testing.T, id string) *SecretWithAuthorizationCodeResourceAssert { + t.Helper() + + return &SecretWithAuthorizationCodeResourceAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"), + } +} + +/////////////////////////////////// +// Attribute value string checks // +/////////////////////////////////// + +func (s *SecretWithAuthorizationCodeResourceAssert) HasApiAuthenticationString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("api_authentication", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasCommentString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("comment", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasDatabaseString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("database", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasFullyQualifiedNameString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("fully_qualified_name", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNameString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("name", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_refresh_token", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenExpiryTimeString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_refresh_token_expiry_time", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasSchemaString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("schema", expected)) + return s +} + +//////////////////////////// +// Attribute empty checks // +//////////////////////////// + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoApiAuthentication() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("api_authentication")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoComment() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("comment")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoDatabase() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("database")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoFullyQualifiedName() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("fully_qualified_name")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoName() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("name")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoOauthRefreshToken() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("oauth_refresh_token")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoOauthRefreshTokenExpiryTime() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("oauth_refresh_token_expiry_time")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoSchema() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("schema")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go new file mode 100644 index 0000000000..23a3f42818 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go @@ -0,0 +1,87 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceshowoutputassert + +import ( + "testing" + "time" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +// to ensure sdk package is used +var _ = sdk.Object{} + +type SecretShowOutputAssert struct { + *assert.ResourceAssert +} + +func SecretShowOutput(t *testing.T, name string) *SecretShowOutputAssert { + t.Helper() + + s := SecretShowOutputAssert{ + ResourceAssert: assert.NewResourceAssert(name, "show_output"), + } + s.AddAssertion(assert.ValueSet("show_output.#", "1")) + return &s +} + +func ImportedSecretShowOutput(t *testing.T, id string) *SecretShowOutputAssert { + t.Helper() + + s := SecretShowOutputAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "show_output"), + } + s.AddAssertion(assert.ValueSet("show_output.#", "1")) + return &s +} + +//////////////////////////// +// Attribute value checks // +//////////////////////////// + +func (s *SecretShowOutputAssert) HasCreatedOn(expected time.Time) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("created_on", expected.String())) + return s +} + +func (s *SecretShowOutputAssert) HasName(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("name", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasSchemaName(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("schema_name", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasDatabaseName(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("database_name", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasOwner(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("owner", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasComment(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("comment", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasSecretType(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("secret_type", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasOauthScopes(expected []string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("oauth_scopes", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasOwnerRoleType(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("owner_role_type", expected)) + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go new file mode 100644 index 0000000000..83047aa647 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go @@ -0,0 +1,152 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SecretWithAuthorizationCodeModel struct { + ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` + Comment tfconfig.Variable `json:"comment,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + OauthRefreshToken tfconfig.Variable `json:"oauth_refresh_token,omitempty"` + OauthRefreshTokenExpiryTime tfconfig.Variable `json:"oauth_refresh_token_expiry_time,omitempty"` + Schema tfconfig.Variable `json:"schema,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func SecretWithAuthorizationCode( + resourceName string, + apiAuthentication string, + database string, + name string, + oauthRefreshToken string, + oauthRefreshTokenExpiryTime string, + schema string, +) *SecretWithAuthorizationCodeModel { + s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCode)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthRefreshToken(oauthRefreshToken) + s.WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime) + s.WithSchema(schema) + return s +} + +func SecretWithAuthorizationCodeWithDefaultMeta( + apiAuthentication string, + database string, + name string, + oauthRefreshToken string, + oauthRefreshTokenExpiryTime string, + schema string, +) *SecretWithAuthorizationCodeModel { + s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithAuthorizationCode)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthRefreshToken(oauthRefreshToken) + s.WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime) + s.WithSchema(schema) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SecretWithAuthorizationCodeModel) WithApiAuthentication(apiAuthentication string) *SecretWithAuthorizationCodeModel { + s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithComment(comment string) *SecretWithAuthorizationCodeModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithDatabase(database string) *SecretWithAuthorizationCodeModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithAuthorizationCodeModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithName(name string) *SecretWithAuthorizationCodeModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshToken(oauthRefreshToken string) *SecretWithAuthorizationCodeModel { + s.OauthRefreshToken = tfconfig.StringVariable(oauthRefreshToken) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime string) *SecretWithAuthorizationCodeModel { + s.OauthRefreshTokenExpiryTime = tfconfig.StringVariable(oauthRefreshTokenExpiryTime) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithSchema(schema string) *SecretWithAuthorizationCodeModel { + s.Schema = tfconfig.StringVariable(schema) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SecretWithAuthorizationCodeModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.ApiAuthentication = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithCommentValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Comment = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Database = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.FullyQualifiedName = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Name = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.OauthRefreshToken = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTimeValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.OauthRefreshTokenExpiryTime = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithSchemaValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Schema = value + return s +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index f3a647471a..920e69286f 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -471,6 +471,7 @@ func getResources() map[string]*schema.Resource { "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), + "snowflake_secret_with_authorization_code": resources.SecretWithAuthorizationCode(), "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go new file mode 100644 index 0000000000..dc1ee3eaab --- /dev/null +++ b/pkg/resources/secret_common.go @@ -0,0 +1,105 @@ +package resources + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretCommonSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "database": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "schema": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the secret.", + }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecretSchema, + }, + }, + FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, +} + +type commonSecretCreate struct { + name string + database string + schema string + comment *string +} + +func handleSecretCreate(d *schema.ResourceData) commonSecretCreate { + create := commonSecretCreate{ + name: d.Get("name").(string), + database: d.Get("database").(string), + schema: d.Get("schema").(string), + } + if v, ok := d.GetOk("comment"); ok { + create.comment = sdk.Pointer(v.(string)) + } + + return create +} + +func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret) error { + if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { + return err + } + if err := d.Set("name", id.Name()); err != nil { + return err + } + if err := d.Set("database", secret.DatabaseName); err != nil { + return err + } + if err := d.Set("schema", secret.SchemaName); err != nil { + return err + } + if err := d.Set("comment", secret.Comment); err != nil { + return err + } + return nil +} + +type commonSecretSet struct { + comment *string +} + +type commonSecretUnset struct { + comment *bool +} + +func handleSecretUpdate(d *schema.ResourceData) (commonSecretSet, commonSecretUnset) { + set, unset := commonSecretSet{}, commonSecretUnset{} + + if d.HasChange("comment") { + if v, ok := d.GetOk("comment"); ok { + set.comment = sdk.Pointer(v.(string)) + } else { + unset.comment = sdk.Pointer(true) + } + } + + return set, unset +} diff --git a/pkg/resources/secret_with_authorization_code_grant_flow.go b/pkg/resources/secret_with_authorization_code_grant_flow.go new file mode 100644 index 0000000000..6d78aa987b --- /dev/null +++ b/pkg/resources/secret_with_authorization_code_grant_flow.go @@ -0,0 +1,228 @@ +package resources + +import ( + "context" + "errors" + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretAuthorizationCodeSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "database": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "schema": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "oauth_refresh_token": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", + }, + "oauth_refresh_token_expiry_time": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", + }, + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the secret.", + }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecretSchema, + }, + }, + FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, +} + +func SecretWithAuthorizationCode() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextSecretWithAuthorizationCode, + ReadContext: ReadContextSecretWithAuthorizationCode, + UpdateContext: UpdateContextSecretWithAuthorizationCode, + DeleteContext: DeleteContextSecretWithAuthorizationCode, + + Schema: secretAuthorizationCodeSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + databaseName := d.Get("database").(string) + schemaName := d.Get("schema").(string) + name := d.Get("name").(string) + + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + + apiIntegrationString := d.Get("api_authentication").(string) + apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) + if err != nil { + return diag.FromErr(err) + } + + refreshToken := d.Get("oauth_refresh_token").(string) + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + + request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, apiIntegration) + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) + } + + err = client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeResourceIdentifier(id)) + + return ReadContextSecretWithAuthorizationCode(ctx, d, meta) +} + +func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if secret == nil || err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, + } + } + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + if err = d.Set("name", secretDescription.Name); err != nil { + return diag.FromErr(err) + } + if err = d.Set("database", secretDescription.DatabaseName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("schema", secretDescription.SchemaName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime); err != nil { + return diag.FromErr(err) + } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("comment", secretDescription.Comment); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChange("oauth_refresh_token") { + refreshToken := d.Get("oauth_refresh_token").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshToken(refreshToken) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("oauth_refresh_token_expiry_time") { + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("comment") { + comment := d.Get("comment").(string) + request := sdk.NewAlterSecretRequest(id) + if len(comment) == 0 { + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + request.WithUnset(*unsetRequest) + } else { + setRequest := sdk.NewSecretSetRequest().WithComment(comment) + request.WithSet(*setRequest) + } + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextSecretWithAuthorizationCode(ctx, d, meta) +} + +func DeleteContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go new file mode 100644 index 0000000000..cc343ed42d --- /dev/null +++ b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go @@ -0,0 +1,22 @@ +package resources + +import ( + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "testing" +) + +func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + newComment := random.Comment() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + +} diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go index b31e229eb4..f1b345c678 100644 --- a/pkg/resources/secret_with_client_credentials_flow.go +++ b/pkg/resources/secret_with_client_credentials_flow.go @@ -50,15 +50,23 @@ var secretClientCredentialsSchema = map[string]*schema.Schema{ Optional: true, Description: "Specifies a comment for the secret.", }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecretSchema, + }, + }, FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, } func SecretWithClientCredentials() *schema.Resource { return &schema.Resource{ - CreateContext: CreateContextSecret, - ReadContext: ReadContextSecret, - UpdateContext: UpdateContextSecret, - DeleteContext: DeleteContextSecret, + CreateContext: CreateContextSecretWithClientCredentials, + ReadContext: ReadContextSecretWithClientCredentials, + UpdateContext: UpdateContextSecretWithClientCredentials, + DeleteContext: DeleteContextSecretWithClientCredentials, Schema: secretClientCredentialsSchema, Importer: &schema.ResourceImporter{ @@ -67,7 +75,7 @@ func SecretWithClientCredentials() *schema.Resource { } } -func CreateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client databaseName := d.Get("database").(string) schemaName := d.Get("schema").(string) @@ -99,10 +107,10 @@ func CreateContextSecret(ctx context.Context, d *schema.ResourceData, meta inter d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadContextSecret(ctx, d, meta) + return ReadContextSecretWithClientCredentials(ctx, d, meta) } -func ReadContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -154,7 +162,7 @@ func ReadContextSecret(ctx context.Context, d *schema.ResourceData, meta interfa return nil } -func UpdateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -192,10 +200,10 @@ func UpdateContextSecret(ctx context.Context, d *schema.ResourceData, meta inter } } - return ReadContextSecret(ctx, d, meta) + return ReadContextSecretWithClientCredentials(ctx, d, meta) } -func DeleteContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { diff --git a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go index 854d928b9e..4be88ceaa3 100644 --- a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go @@ -151,7 +151,7 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { }, CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), Steps: []resource.TestStep{ - // Create secret without providing oauth_scopes value + // create secret without providing oauth_scopes value { Config: config.FromModel(t, secretModel), Check: resource.ComposeTestCheckFunc( diff --git a/pkg/schemas/gen/sdk_show_result_structs.go b/pkg/schemas/gen/sdk_show_result_structs.go index ea2e977160..337367a112 100644 --- a/pkg/schemas/gen/sdk_show_result_structs.go +++ b/pkg/schemas/gen/sdk_show_result_structs.go @@ -37,6 +37,7 @@ var SdkShowResultStructs = []any{ sdk.Role{}, sdk.RowAccessPolicy{}, sdk.Schema{}, + sdk.Secret{}, sdk.SecurityIntegration{}, sdk.Sequence{}, sdk.SessionPolicy{}, diff --git a/pkg/schemas/secret_gen.go b/pkg/schemas/secret_gen.go new file mode 100644 index 0000000000..5a341ca7d7 --- /dev/null +++ b/pkg/schemas/secret_gen.go @@ -0,0 +1,68 @@ +// Code generated by sdk-to-schema generator; DO NOT EDIT. + +package schemas + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ShowSecretSchema represents output of SHOW query for the single Secret. +var ShowSecretSchema = map[string]*schema.Schema{ + "created_on": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "schema_name": { + Type: schema.TypeString, + Computed: true, + }, + "database_name": { + Type: schema.TypeString, + Computed: true, + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "comment": { + Type: schema.TypeString, + Computed: true, + }, + "secret_type": { + Type: schema.TypeString, + Computed: true, + }, + "oauth_scopes": { + Type: schema.TypeInvalid, + Computed: true, + }, + "owner_role_type": { + Type: schema.TypeString, + Computed: true, + }, +} + +var _ = ShowSecretSchema + +func SecretToSchema(secret *sdk.Secret) map[string]any { + secretSchema := make(map[string]any) + secretSchema["created_on"] = secret.CreatedOn.String() + secretSchema["name"] = secret.Name + secretSchema["schema_name"] = secret.SchemaName + secretSchema["database_name"] = secret.DatabaseName + secretSchema["owner"] = secret.Owner + if secret.Comment != nil { + secretSchema["comment"] = secret.Comment + } + secretSchema["secret_type"] = secret.SecretType + secretSchema["oauth_scopes"] = secret.OauthScopes + secretSchema["owner_role_type"] = secret.OwnerRoleType + return secretSchema +} + +var _ = SecretToSchema From a383ac402138f2ecb44289865a2dceee9cdaa3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 26 Sep 2024 15:25:28 +0200 Subject: [PATCH 33/65] acc tests pass with usage of secret_common --- .../objectassert/secret_snowflake_gen.go | 3 +- .../secret_show_output_gen.go | 4 +- pkg/acceptance/check_destroy.go | 2 +- pkg/provider/resources/resources.go | 1 + pkg/resources/secret_common.go | 28 +++-- ...ization_code_grant_flow_acceptance_test.go | 28 ++--- .../secret_with_client_credentials_flow.go | 102 +++++------------- pkg/schemas/secret_gen.go | 3 +- 8 files changed, 61 insertions(+), 110 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go index 2a61443f09..44ebf3c3ef 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -4,6 +4,7 @@ package objectassert import ( "fmt" + "slices" "testing" "time" @@ -114,7 +115,7 @@ func (s *SecretAssert) HasSecretType(expected string) *SecretAssert { func (s *SecretAssert) HasOauthScopes(expected []string) *SecretAssert { s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { t.Helper() - if o.OauthScopes != expected { + if !slices.Equal(o.OauthScopes, expected) { return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, o.OauthScopes) } return nil diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go index 23a3f42818..f1182d6d9e 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go @@ -77,7 +77,9 @@ func (s *SecretShowOutputAssert) HasSecretType(expected string) *SecretShowOutpu } func (s *SecretShowOutputAssert) HasOauthScopes(expected []string) *SecretShowOutputAssert { - s.AddAssertion(assert.ResourceShowOutputValueSet("oauth_scopes", expected)) + for _, v := range expected { + s.AddAssertion(assert.ValueSet("oauth_scopes.*", v)) + } return s } diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 0eacca77b0..5857663a9c 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -174,7 +174,7 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { - return runShowById(ctx, id, client.Databases.ShowByID) + return runShowById(ctx, id, client.Secrets.ShowByID) }, resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Databases.ShowByID) diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 2eda84ffed..7c72d000c5 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -40,6 +40,7 @@ const ( Schema resource = "snowflake_schema" ScimSecurityIntegration resource = "snowflake_scim_integration" SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" + SecretWithAuthorizationCode resource = "snowflake_secret_with_authorization_code" SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index dc1ee3eaab..a2c54f2364 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -79,27 +79,23 @@ func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, sec if err := d.Set("comment", secret.Comment); err != nil { return err } + if err := d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}); err != nil { + return err + } return nil } -type commonSecretSet struct { - comment *string -} - -type commonSecretUnset struct { - comment *bool -} - -func handleSecretUpdate(d *schema.ResourceData) (commonSecretSet, commonSecretUnset) { - set, unset := commonSecretSet{}, commonSecretUnset{} - +func handleSecretUpdate(id sdk.SchemaObjectIdentifier, d *schema.ResourceData) *sdk.AlterSecretRequest { if d.HasChange("comment") { - if v, ok := d.GetOk("comment"); ok { - set.comment = sdk.Pointer(v.(string)) + comment := d.Get("comment").(string) + request := sdk.NewAlterSecretRequest(id) + if len(comment) == 0 { + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + return request.WithUnset(*unsetRequest) } else { - unset.comment = sdk.Pointer(true) + setRequest := sdk.NewSecretSetRequest().WithComment(comment) + return request.WithSet(*setRequest) } } - - return set, unset + return nil } diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go index cc343ed42d..b04a9c415c 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go @@ -1,22 +1,22 @@ -package resources +package resources_test import ( - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "testing" ) -func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - comment := random.Comment() - newComment := random.Comment() +func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { + /* + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + newComment := random.Comment() - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), - ) - t.Cleanup(apiIntegrationCleanup) + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + + */ } diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go index f1b345c678..8e987fa694 100644 --- a/pkg/resources/secret_with_client_credentials_flow.go +++ b/pkg/resources/secret_with_client_credentials_flow.go @@ -6,60 +6,28 @@ import ( "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -var secretClientCredentialsSchema = map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "database": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "schema": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "api_authentication": { - Type: schema.TypeString, - ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", - }, - "oauth_scopes": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Required: true, - Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a comment for the secret.", - }, - ShowOutputAttributeName: { - Type: schema.TypeList, - Computed: true, - Description: "Outputs the result of `SHOW SECRET` for the given secret.", - Elem: &schema.Resource{ - Schema: schemas.ShowSecretSchema, +var secretClientCredentialsSchema = func() map[string]*schema.Schema { + secretClientCredentials := map[string]*schema.Schema{ + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", }, - }, - FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, -} + "oauth_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", + }, + } + return helpers.MergeMaps(secretCommonSchema, secretClientCredentials) +}() func SecretWithClientCredentials() *schema.Resource { return &schema.Resource{ @@ -77,16 +45,15 @@ func SecretWithClientCredentials() *schema.Resource { func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName := d.Get("database").(string) - schemaName := d.Get("schema").(string) - name := d.Get("name").(string) + commonCreate := handleSecretCreate(d) + apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) if err != nil { return diag.FromErr(err) } - id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) @@ -96,8 +63,8 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) - if v, ok := d.GetOk("comment"); ok { - request.WithComment(v.(string)) + if commonCreate.comment != nil { + request.WithComment(*commonCreate.comment) } err = client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) @@ -137,17 +104,12 @@ func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.Resou } } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - if err = d.Set("name", secretDescription.Name); err != nil { + if err := handleSecretRead(d, id, secret); err != nil { return diag.FromErr(err) } - if err = d.Set("database", secretDescription.DatabaseName); err != nil { - return diag.FromErr(err) - } - if err = d.Set("schema", secretDescription.SchemaName); err != nil { + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { return diag.FromErr(err) } if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { @@ -156,9 +118,6 @@ func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.Resou if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { return diag.FromErr(err) } - if err = d.Set("comment", secretDescription.Comment); err != nil { - return diag.FromErr(err) - } return nil } @@ -185,16 +144,7 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res } } - if d.HasChange("comment") { - comment := d.Get("comment").(string) - request := sdk.NewAlterSecretRequest(id) - if len(comment) == 0 { - unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) - request.WithUnset(*unsetRequest) - } else { - setRequest := sdk.NewSecretSetRequest().WithComment(comment) - request.WithSet(*setRequest) - } + if request := handleSecretUpdate(id, d); request != nil { if err := client.Secrets.Alter(ctx, request); err != nil { return diag.FromErr(err) } diff --git a/pkg/schemas/secret_gen.go b/pkg/schemas/secret_gen.go index 5a341ca7d7..e76a1777d1 100644 --- a/pkg/schemas/secret_gen.go +++ b/pkg/schemas/secret_gen.go @@ -38,7 +38,8 @@ var ShowSecretSchema = map[string]*schema.Schema{ Computed: true, }, "oauth_scopes": { - Type: schema.TypeInvalid, + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, }, "owner_role_type": { From 40179ecbde55ce52f8520560ff55af1c23232c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 27 Sep 2024 10:54:23 +0200 Subject: [PATCH 34/65] Secret with authorization code acceptance test, diff suppress --- ...cret_with_authorization_code_grant_flow.go | 123 ++++++------------ ...ization_code_grant_flow_acceptance_test.go | 95 ++++++++++++-- 2 files changed, 121 insertions(+), 97 deletions(-) diff --git a/pkg/resources/secret_with_authorization_code_grant_flow.go b/pkg/resources/secret_with_authorization_code_grant_flow.go index 6d78aa987b..922dc870fa 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow.go +++ b/pkg/resources/secret_with_authorization_code_grant_flow.go @@ -6,64 +6,35 @@ import ( "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "time" ) -var secretAuthorizationCodeSchema = map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "database": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "schema": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "oauth_refresh_token": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", - }, - "oauth_refresh_token_expiry_time": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", - }, - "api_authentication": { - Type: schema.TypeString, - ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a comment for the secret.", - }, - ShowOutputAttributeName: { - Type: schema.TypeList, - Computed: true, - Description: "Outputs the result of `SHOW SECRET` for the given secret.", - Elem: &schema.Resource{ - Schema: schemas.ShowSecretSchema, +var secretAuthorizationCodeSchema = func() map[string]*schema.Schema { + secretAuthorizationCode := map[string]*schema.Schema{ + "oauth_refresh_token": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", }, - }, - FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, -} + "oauth_refresh_token_expiry_time": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", + // has to be ignored since snowflake changes the date format to different one than is required in the input + //DiffSuppressFunc: IgnoreAfterCreation, + }, + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + }, + } + return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) +}() func SecretWithAuthorizationCode() *schema.Resource { return &schema.Resource{ @@ -81,11 +52,9 @@ func SecretWithAuthorizationCode() *schema.Resource { func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName := d.Get("database").(string) - schemaName := d.Get("schema").(string) - name := d.Get("name").(string) + commonCreate := handleSecretCreate(d) - id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) @@ -138,28 +107,19 @@ func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Resou } } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { + if err := handleSecretRead(d, id, secret); err != nil { return diag.FromErr(err) } - if err = d.Set("name", secretDescription.Name); err != nil { - return diag.FromErr(err) - } - if err = d.Set("database", secretDescription.DatabaseName); err != nil { - return diag.FromErr(err) - } - if err = d.Set("schema", secretDescription.SchemaName); err != nil { + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { return diag.FromErr(err) } - if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime); err != nil { + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { return diag.FromErr(err) } if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } - if err = d.Set("comment", secretDescription.Comment); err != nil { - return diag.FromErr(err) - } return nil } @@ -170,6 +130,12 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res return diag.FromErr(err) } + if request := handleSecretUpdate(id, d); request != nil { + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("oauth_refresh_token") { refreshToken := d.Get("oauth_refresh_token").(string) @@ -183,10 +149,10 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res } if d.HasChange("oauth_refresh_token_expiry_time") { - refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(time.Time) request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime.Format(time.DateOnly)) request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) if err := client.Secrets.Alter(ctx, request); err != nil { @@ -194,21 +160,6 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res } } - if d.HasChange("comment") { - comment := d.Get("comment").(string) - request := sdk.NewAlterSecretRequest(id) - if len(comment) == 0 { - unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) - request.WithUnset(*unsetRequest) - } else { - setRequest := sdk.NewSecretSetRequest().WithComment(comment) - request.WithSet(*setRequest) - } - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - return ReadContextSecretWithAuthorizationCode(ctx, d, meta) } diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go index b04a9c415c..2978889b4d 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go @@ -1,22 +1,95 @@ package resources_test import ( + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" "testing" + "time" ) func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { - /* - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - comment := random.Comment() - newComment := random.Comment() + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + newComment := random.Comment() + refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) + newRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), - ) - t.Cleanup(apiIntegrationCleanup) + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + secretModel := model.SecretWithAuthorizationCode("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryTime).WithComment(comment) - */ + //expectedTime := helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, newRefreshTokenExpiryTime).String() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + Steps: []resource.TestStep{ + // create + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString("test_token"). + HasOauthRefreshTokenExpiryTimeString(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()). + HasCommentString(comment), + ), + ), + }, + // set all + { + Config: config.FromModel(t, secretModel. + WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryTime). + WithOauthRefreshToken("new_test_token"). + WithComment(newComment), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String("test_token"), sdk.String("new_test_token")), + //diff suppressed + //planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()), sdk.String(expectedTime)), + }, + }, + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString("new_test_token"). + //diff suppressed + //HasOauthRefreshTokenExpiryTimeString(expectedTime). + HasCommentString(newComment), + ), + ), + }, + }, + }) } From 04d3feb6039c432cefcf1dedecaf06a959399885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 27 Sep 2024 11:02:23 +0200 Subject: [PATCH 35/65] helper function for snowflake datetime for secret --- pkg/acceptance/helpers/secret_client.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index f2657b39bb..a6bc932e24 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -3,6 +3,7 @@ package helpers import ( "context" "testing" + "time" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" @@ -21,6 +22,18 @@ func NewSecretClient(context *TestClientContext, idsGenerator *IdsGenerator) *Se } } +func StringDateToSnowflakeTimeFormat(t *testing.T, inputLayout, date string) *time.Time { + t.Helper() + parsedTime, err := time.Parse(inputLayout, date) + require.NoError(t, err) + + loc, err := time.LoadLocation("America/Los_Angeles") + require.NoError(t, err) + + adjustedTime := parsedTime.In(loc) + return &adjustedTime +} + func (c *SecretClient) client() sdk.Secrets { return c.context.client.Secrets } From fa408fd3c2e43b7e1877a490fafd1f8f11978143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 27 Sep 2024 11:02:57 +0200 Subject: [PATCH 36/65] check destroy and few cleanups --- .../secret_with_client_credentials_resource_ext.go | 5 ++--- .../config/model/secret_with_authorization_code_model_gen.go | 2 +- pkg/acceptance/check_destroy.go | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go index 5b3c26dd5d..543d330d5e 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go @@ -1,14 +1,13 @@ package resourceassert import ( - "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" ) func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopes(expected []sdk.ApiIntegrationScope) *SecretWithClientCredentialsResourceAssert { - for i, v := range expected { - s.AddAssertion(assert.ValueSet(fmt.Sprintf("oauth_scopes.%d", i), v.Scope)) + for _, v := range expected { + s.AddAssertion(assert.ValueSet("oauth_scopes.*", v.Scope)) } return s } diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go index 83047aa647..65516f427d 100644 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go @@ -30,10 +30,10 @@ func SecretWithAuthorizationCode( resourceName string, apiAuthentication string, database string, + schema string, name string, oauthRefreshToken string, oauthRefreshTokenExpiryTime string, - schema string, ) *SecretWithAuthorizationCodeModel { s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCode)} s.WithApiAuthentication(apiAuthentication) diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 5857663a9c..ea0bcd17c4 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -173,6 +173,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ScimSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, + resources.SecretWithAuthorizationCode: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.Secrets.ShowByID) + }, resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, From d3e54db783bd43d49b0684e3b6b1c83b4672b628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 30 Sep 2024 14:32:31 +0200 Subject: [PATCH 37/65] Wrapped OAuth Scopes in separate queryStructField to allow empty list creation and alter set --- pkg/acceptance/helpers/secret_client.go | 3 +- pkg/sdk/secrets_def.go | 13 +++--- pkg/sdk/secrets_dto_builders_gen.go | 28 ++++++++---- pkg/sdk/secrets_dto_gen.go | 8 +++- pkg/sdk/secrets_gen.go | 7 ++- pkg/sdk/secrets_gen_integration_test.go | 43 ------------------- pkg/sdk/secrets_gen_test.go | 18 +++++--- pkg/sdk/secrets_impl_gen.go | 22 ++++++++-- .../testint/secrets_gen_integration_test.go | 37 +++++++--------- 9 files changed, 86 insertions(+), 93 deletions(-) delete mode 100644 pkg/sdk/secrets_gen_integration_test.go diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index f2657b39bb..74073b5389 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -28,7 +28,8 @@ func (c *SecretClient) client() sdk.Secrets { func (c *SecretClient) CreateWithOAuthClientCredentialsFlow(t *testing.T, id sdk.SchemaObjectIdentifier, apiIntegration sdk.AccountObjectIdentifier, oauthScopes []sdk.ApiIntegrationScope) (*sdk.Secret, func()) { t.Helper() ctx := context.Background() - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration). + WithOauthScopes(sdk.OauthScopesListRequest{oauthScopes}) err := c.client().CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index d4edb0494f..d816f19dcd 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -4,9 +4,6 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen //go:generate go run ./poc/main.go -var secretsApiIntegrationScopeDef = g.NewQueryStruct("ApiIntegrationScope"). - Text("Scope", g.KeywordOptions().SingleQuotes().Required()) - var secretDbRow = g.DbStruct("secretDBRow"). Field("created_on", "time.Time"). Field("name", "string"). @@ -57,12 +54,17 @@ var secretDetails = g.PlainStruct("SecretDetails"). Field("OauthScopes", "[]string"). Field("IntegrationName", "*string") +var secretsApiIntegrationScopeDef = g.NewQueryStruct("ApiIntegrationScope"). + Text("Scope", g.KeywordOptions().SingleQuotes().Required()) + +var oauthScopesListDef = g.NewQueryStruct("OauthScopesList").List("OauthScopesList", "ApiIntegrationScope", g.ListOptions().Required().MustParentheses()) + var secretSet = g.NewQueryStruct("SecretSet"). OptionalComment(). OptionalQueryStructField( "SetForOAuthClientCredentialsFlow", g.NewQueryStruct("SetForOAuthClientCredentialsFlow"). - ListAssignment("OAUTH_SCOPES", "ApiIntegrationScope", g.ParameterOptions().Parentheses().Required()), + OptionalQueryStructField("OauthScopes", oauthScopesListDef, g.ParameterOptions().SQL("OAUTH_SCOPES").Parentheses()), g.KeywordOptions(), ). OptionalQueryStructField( @@ -106,7 +108,7 @@ var SecretsDef = g.NewInterface( Name(). PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). - ListAssignment("OAUTH_SCOPES", "ApiIntegrationScope", g.ParameterOptions().Parentheses().Required()). + OptionalQueryStructField("OauthScopes", oauthScopesListDef, g.ParameterOptions().SQL("OAUTH_SCOPES").Parentheses()). OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), @@ -127,7 +129,6 @@ var SecretsDef = g.NewInterface( OptionalComment(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), - secretsApiIntegrationScopeDef, ).CustomOperation( "CreateWithBasicAuthentication", "https://docs.snowflake.com/en/sql-reference/sql/create-secret", diff --git a/pkg/sdk/secrets_dto_builders_gen.go b/pkg/sdk/secrets_dto_builders_gen.go index 9faeabb35a..25eff46b72 100644 --- a/pkg/sdk/secrets_dto_builders_gen.go +++ b/pkg/sdk/secrets_dto_builders_gen.go @@ -7,12 +7,10 @@ import () func NewCreateWithOAuthClientCredentialsFlowSecretRequest( name SchemaObjectIdentifier, ApiIntegration AccountObjectIdentifier, - OauthScopes []ApiIntegrationScope, ) *CreateWithOAuthClientCredentialsFlowSecretRequest { s := CreateWithOAuthClientCredentialsFlowSecretRequest{} s.name = name s.ApiIntegration = ApiIntegration - s.OauthScopes = OauthScopes return &s } @@ -26,11 +24,24 @@ func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithIfNotExists(IfNo return s } +func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithOauthScopes(OauthScopes OauthScopesListRequest) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s.OauthScopes = &OauthScopes + return s +} + func (s *CreateWithOAuthClientCredentialsFlowSecretRequest) WithComment(Comment string) *CreateWithOAuthClientCredentialsFlowSecretRequest { s.Comment = &Comment return s } +func NewOauthScopesListRequest( + OauthScopesList []ApiIntegrationScope, +) *OauthScopesListRequest { + s := OauthScopesListRequest{} + s.OauthScopesList = OauthScopesList + return &s +} + func NewCreateWithOAuthAuthorizationCodeFlowSecretRequest( name SchemaObjectIdentifier, OauthRefreshToken string, @@ -164,12 +175,13 @@ func (s *SecretSetRequest) WithSetForGenericString(SetForGenericString SetForGen return s } -func NewSetForOAuthClientCredentialsFlowRequest( - OauthScopes []ApiIntegrationScope, -) *SetForOAuthClientCredentialsFlowRequest { - s := SetForOAuthClientCredentialsFlowRequest{} - s.OauthScopes = OauthScopes - return &s +func NewSetForOAuthClientCredentialsFlowRequest() *SetForOAuthClientCredentialsFlowRequest { + return &SetForOAuthClientCredentialsFlowRequest{} +} + +func (s *SetForOAuthClientCredentialsFlowRequest) WithOauthScopes(OauthScopes OauthScopesListRequest) *SetForOAuthClientCredentialsFlowRequest { + s.OauthScopes = &OauthScopes + return s } func NewSetForOAuthAuthorizationFlowRequest() *SetForOAuthAuthorizationFlowRequest { diff --git a/pkg/sdk/secrets_dto_gen.go b/pkg/sdk/secrets_dto_gen.go index 3eec952bb4..656ca9ac69 100644 --- a/pkg/sdk/secrets_dto_gen.go +++ b/pkg/sdk/secrets_dto_gen.go @@ -18,10 +18,14 @@ type CreateWithOAuthClientCredentialsFlowSecretRequest struct { IfNotExists *bool name SchemaObjectIdentifier // required ApiIntegration AccountObjectIdentifier // required - OauthScopes []ApiIntegrationScope // required + OauthScopes *OauthScopesListRequest Comment *string } +type OauthScopesListRequest struct { + OauthScopesList []ApiIntegrationScope // required +} + type CreateWithOAuthAuthorizationCodeFlowSecretRequest struct { OrReplace *bool IfNotExists *bool @@ -65,7 +69,7 @@ type SecretSetRequest struct { } type SetForOAuthClientCredentialsFlowRequest struct { - OauthScopes []ApiIntegrationScope // required + OauthScopes *OauthScopesListRequest } type SetForOAuthAuthorizationFlowRequest struct { diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go index 8a5a819e5f..db2d30df35 100644 --- a/pkg/sdk/secrets_gen.go +++ b/pkg/sdk/secrets_gen.go @@ -27,12 +27,15 @@ type CreateWithOAuthClientCredentialsFlowSecretOptions struct { name SchemaObjectIdentifier `ddl:"identifier"` secretType string `ddl:"static" sql:"TYPE = OAUTH2"` ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` - OauthScopes []ApiIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` + OauthScopes *OauthScopesList `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` } type ApiIntegrationScope struct { Scope string `ddl:"keyword,single_quotes"` } +type OauthScopesList struct { + OauthScopesList []ApiIntegrationScope `ddl:"list,must_parentheses"` +} // CreateWithOAuthAuthorizationCodeFlowSecretOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-secret. type CreateWithOAuthAuthorizationCodeFlowSecretOptions struct { @@ -90,7 +93,7 @@ type SecretSet struct { SetForGenericString *SetForGenericString `ddl:"keyword"` } type SetForOAuthClientCredentialsFlow struct { - OauthScopes []ApiIntegrationScope `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` + OauthScopes *OauthScopesList `ddl:"parameter,parentheses" sql:"OAUTH_SCOPES"` } type SetForOAuthAuthorizationFlow struct { OauthRefreshToken *string `ddl:"parameter,single_quotes" sql:"OAUTH_REFRESH_TOKEN"` diff --git a/pkg/sdk/secrets_gen_integration_test.go b/pkg/sdk/secrets_gen_integration_test.go deleted file mode 100644 index 4474555b36..0000000000 --- a/pkg/sdk/secrets_gen_integration_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package sdk - -import "testing" - -func TestInt_Secrets(t *testing.T) { - // TODO: prepare common resources - - t.Run("CreateWithOAuthClientCredentialsFlow", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithOAuthAuthorizationCodeFlow", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithBasicAuthentication", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("CreateWithGenericString", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Alter", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Drop", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Show", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("ShowByID", func(t *testing.T) { - // TODO: fill me - }) - - t.Run("Describe", func(t *testing.T) { - // TODO: fill me - }) -} diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go index 4b591a7b7c..486560c642 100644 --- a/pkg/sdk/secrets_gen_test.go +++ b/pkg/sdk/secrets_gen_test.go @@ -34,7 +34,7 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { opts := defaultOpts() opts.IfNotExists = Bool(true) opts.ApiIntegration = integration - opts.OauthScopes = []ApiIntegrationScope{{"test"}} + opts.OauthScopes = &OauthScopesList{[]ApiIntegrationScope{{"test"}}} opts.Comment = String("foo") assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = ('test') COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) @@ -45,9 +45,9 @@ func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { opts := defaultOpts() opts.IfNotExists = Bool(true) opts.ApiIntegration = integration - opts.OauthScopes = []ApiIntegrationScope{} + opts.OauthScopes = &OauthScopesList{[]ApiIntegrationScope{}} opts.Comment = String("foo") - assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = () COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) }) } @@ -198,7 +198,7 @@ func TestSecrets_Alter(t *testing.T) { t.Run("validation: conflicting fields for [opts.Set.SetForOAuthClientCredentialsFlow opts.Set.SetForOAuthAuthorizationFlow opts.Set.SetForBasicAuthentication opts.Set.SetForGenericString]", func(t *testing.T) { opts := setOpts() - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{{Scope: "foo"}}} + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: &OauthScopesList{[]ApiIntegrationScope{{Scope: "foo"}}}} opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{OauthRefreshToken: String("foo"), OauthRefreshTokenExpiryTime: String("bar")} opts.Set.SetForBasicAuthentication = &SetForBasicAuthentication{Username: String("foo"), Password: String("bar")} opts.Set.SetForGenericString = &SetForGenericString{SecretString: String("secret")} @@ -208,14 +208,20 @@ func TestSecrets_Alter(t *testing.T) { t.Run("alter: set options for Oauth Client Credentials Flow", func(t *testing.T) { opts := setOpts() opts.Set.Comment = String("test") - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{{"sample_scope"}}} + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: &OauthScopesList{[]ApiIntegrationScope{{"sample_scope"}}}} assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ('sample_scope')", id.FullyQualifiedName()) }) t.Run("alter: set options for Oauth Client Credentials Flow - empty oauth scopes list", func(t *testing.T) { opts := setOpts() opts.Set.Comment = String("test") - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: []ApiIntegrationScope{}} + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{OauthScopes: &OauthScopesList{}} + assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test' OAUTH_SCOPES = ()", id.FullyQualifiedName()) + }) + + t.Run("alter: set options for Oauth Client Credentials Flow - without oauth scopes provided", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") assertOptsValidAndSQLEquals(t, opts, "ALTER SECRET IF EXISTS %s SET COMMENT = 'test'", id.FullyQualifiedName()) }) diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index 4464553f9a..89e7796c0a 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -79,9 +79,18 @@ func (r *CreateWithOAuthClientCredentialsFlowSecretRequest) toOpts() *CreateWith IfNotExists: r.IfNotExists, name: r.name, ApiIntegration: r.ApiIntegration, - OauthScopes: r.OauthScopes, - Comment: r.Comment, + + Comment: r.Comment, } + + if r.OauthScopes != nil { + + opts.OauthScopes = &OauthScopesList{ + OauthScopesList: r.OauthScopes.OauthScopesList, + } + + } + return opts } @@ -133,8 +142,13 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { } if r.Set.SetForOAuthClientCredentialsFlow != nil { - opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{ - OauthScopes: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes, + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{} + + if r.Set.SetForOAuthClientCredentialsFlow.OauthScopes != nil { + opts.Set.SetForOAuthClientCredentialsFlow.OauthScopes = &OauthScopesList{ + OauthScopesList: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes.OauthScopesList, + } + } } diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 5503f362ee..5e845368db 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -63,7 +63,8 @@ func TestInt_Secrets(t *testing.T) { t.Run("Create: secretWithOAuthClientCredentialsFlow", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}). + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId). + WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}}). WithComment("a"). WithIfNotExists(true) @@ -93,11 +94,11 @@ func TestInt_Secrets(t *testing.T) { }) }) - // It is possible to create secret without specifying both refresh token properties and scopes - // Scopes are not being inherited from the security_integration what is tested further + // It is possible to create secret without specifying both refresh token properties and OAuth scopes + // Regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret with empty oauth_scopes list should inherit scopes from security_integration, but it does not t.Run("Create: secretWithOAuth - minimal, without token and scopes", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, nil) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId) err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) @@ -123,7 +124,7 @@ func TestInt_Secrets(t *testing.T) { // regarding the https://docs.snowflake.com/en/sql-reference/sql/create-secret secret with empty oauth_scopes list should inherit scopes from security_integration, but it does not t.Run("Create: SecretWithOAuthClientCredentialsFlow - Empty Scopes List", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifier() - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId, []sdk.ApiIntegrationScope{}) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId).WithOauthScopes(sdk.OauthScopesListRequest{}) err := client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) @@ -145,7 +146,7 @@ func TestInt_Secrets(t *testing.T) { assert.NotContains(t, details.OauthScopes, "foo") assert.NotContains(t, details.OauthScopes, "bar") - assert.Empty(t, details.OauthScopes) + assert.Equal(t, []string{""}, details.OauthScopes) }) t.Run("Create: SecretWithOAuthAuthorizationCodeFlow - refreshTokenExpiry date format", func(t *testing.T) { @@ -308,9 +309,8 @@ func TestInt_Secrets(t *testing.T) { *sdk.NewSecretSetRequest(). WithComment(comment). WithSetForOAuthClientCredentialsFlow( - *sdk.NewSetForOAuthClientCredentialsFlowRequest( - []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}, - ), + *sdk.NewSetForOAuthClientCredentialsFlowRequest(). + WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "bar"}}}), ), ) err := client.Secrets.Alter(ctx, setRequest) @@ -450,24 +450,23 @@ func TestInt_Secrets(t *testing.T) { err := client.Secrets.Alter(ctx, setRequest) require.NoError(t, err) + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + // Cannot assert secret string because snowflake does not output its value neither with SHOW nor DESCRIBE + assert.Equal(t, comment, *details.Comment) + unsetRequest := sdk.NewAlterSecretRequest(id). WithUnset( *sdk.NewSecretUnsetRequest(). WithComment(true), ) - err = client.Secrets.Alter(ctx, unsetRequest) require.NoError(t, err) - details, err := client.Secrets.Describe(ctx, id) + details, err = client.Secrets.Describe(ctx, id) require.NoError(t, err) - assertSecretDetails(details, secretDetails{ - Name: id.Name(), - SecretType: "GENERIC_STRING", - Comment: nil, - }) - assert.Empty(t, details.Comment) }) @@ -517,8 +516,6 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthClientCredentialsFlow with Like", func(t *testing.T) { - // secret1, id1 := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}, nil) - // secret2, _ := createSecretWithOAuthClientCredentialsFlow(t, integrationId, []sdk.ApiIntegrationScope{{Scope: "bar"}}, nil) id1 := testClientHelper().Ids.RandomSchemaObjectIdentifier() id2 := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -609,8 +606,6 @@ func TestInt_Secrets(t *testing.T) { }) t.Run("Show: SecretWithOAuthAuthorizationCodeFlow with In", func(t *testing.T) { - // secret, id := createSecretWithOAuthAuthorizationCodeFlow(t, integrationId, "foo", refreshTokenExpiryTime, nil) - id := testClientHelper().Ids.RandomSchemaObjectIdentifier() secret, secretCleanup := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id, integrationId, "foo", refreshTokenExpiryTime) t.Cleanup(secretCleanup) From 0e181360fceab0b55bdadc37a4822102200908d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 30 Sep 2024 14:35:20 +0200 Subject: [PATCH 38/65] Linter adjustments --- pkg/acceptance/helpers/secret_client.go | 2 +- pkg/sdk/secrets_impl_gen.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index 74073b5389..e3eb87c8f1 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -29,7 +29,7 @@ func (c *SecretClient) CreateWithOAuthClientCredentialsFlow(t *testing.T, id sdk t.Helper() ctx := context.Background() request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration). - WithOauthScopes(sdk.OauthScopesListRequest{oauthScopes}) + WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) err := c.client().CreateWithOAuthClientCredentialsFlow(ctx, request) require.NoError(t, err) diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index 89e7796c0a..f6804181f0 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -84,11 +84,9 @@ func (r *CreateWithOAuthClientCredentialsFlowSecretRequest) toOpts() *CreateWith } if r.OauthScopes != nil { - opts.OauthScopes = &OauthScopesList{ OauthScopesList: r.OauthScopes.OauthScopesList, } - } return opts @@ -148,7 +146,6 @@ func (r *AlterSecretRequest) toOpts() *AlterSecretOptions { opts.Set.SetForOAuthClientCredentialsFlow.OauthScopes = &OauthScopesList{ OauthScopesList: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes.OauthScopesList, } - } } From f60985c925d4a55991730fa756432970810c7c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 26 Sep 2024 11:50:31 +0200 Subject: [PATCH 39/65] init commit for secret resource --- ...et_with_client_credentials_resource_ext.go | 14 ++ ...et_with_client_credentials_resource_gen.go | 107 +++++++++ ...ecret_with_client_credentials_model_gen.go | 143 ++++++++++++ pkg/acceptance/check_destroy.go | 3 + pkg/provider/provider.go | 1 + pkg/provider/resources/resources.go | 1 + .../secret_with_client_credentials_flow.go | 211 ++++++++++++++++++ ...client_credentials_flow_acceptance_test.go | 191 ++++++++++++++++ 8 files changed, 671 insertions(+) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go create mode 100644 pkg/resources/secret_with_client_credentials_flow.go create mode 100644 pkg/resources/secret_with_client_credentials_flow_acceptance_test.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go new file mode 100644 index 0000000000..5b3c26dd5d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go @@ -0,0 +1,14 @@ +package resourceassert + +import ( + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopes(expected []sdk.ApiIntegrationScope) *SecretWithClientCredentialsResourceAssert { + for i, v := range expected { + s.AddAssertion(assert.ValueSet(fmt.Sprintf("oauth_scopes.%d", i), v.Scope)) + } + return s +} diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go new file mode 100644 index 0000000000..12a36fa46d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_gen.go @@ -0,0 +1,107 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +type SecretWithClientCredentialsResourceAssert struct { + *assert.ResourceAssert +} + +func SecretWithClientCredentialsResource(t *testing.T, name string) *SecretWithClientCredentialsResourceAssert { + t.Helper() + + return &SecretWithClientCredentialsResourceAssert{ + ResourceAssert: assert.NewResourceAssert(name, "resource"), + } +} + +func ImportedSecretWithClientCredentialsResource(t *testing.T, id string) *SecretWithClientCredentialsResourceAssert { + t.Helper() + + return &SecretWithClientCredentialsResourceAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"), + } +} + +/////////////////////////////////// +// Attribute value string checks // +/////////////////////////////////// + +func (s *SecretWithClientCredentialsResourceAssert) HasApiAuthenticationString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("api_authentication", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasCommentString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("comment", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasDatabaseString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("database", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasFullyQualifiedNameString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("fully_qualified_name", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNameString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("name", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopesString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_scopes", expected)) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasSchemaString(expected string) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("schema", expected)) + return s +} + +//////////////////////////// +// Attribute empty checks // +//////////////////////////// + +func (s *SecretWithClientCredentialsResourceAssert) HasNoApiAuthentication() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("api_authentication")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoComment() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("comment")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoDatabase() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("database")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoFullyQualifiedName() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("fully_qualified_name")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoName() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("name")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoOauthScopes() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("oauth_scopes")) + return s +} + +func (s *SecretWithClientCredentialsResourceAssert) HasNoSchema() *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueNotSet("schema")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go new file mode 100644 index 0000000000..8601977c78 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go @@ -0,0 +1,143 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SecretWithClientCredentialsModel struct { + ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` + Comment tfconfig.Variable `json:"comment,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + OauthScopes tfconfig.Variable `json:"oauth_scopes,omitempty"` + Schema tfconfig.Variable `json:"schema,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func SecretWithClientCredentials( + resourceName string, + apiAuthentication string, + database string, + schema string, + name string, + oauthScopes []string, +) *SecretWithClientCredentialsModel { + s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithClientCredentials)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthScopes(oauthScopes) + s.WithSchema(schema) + return s +} + +func SecretWithClientCredentialsWithDefaultMeta( + apiAuthentication string, + database string, + name string, + oauthScopes []string, + schema string, +) *SecretWithClientCredentialsModel { + s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithClientCredentials)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthScopes(oauthScopes) + s.WithSchema(schema) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SecretWithClientCredentialsModel) WithApiAuthentication(apiAuthentication string) *SecretWithClientCredentialsModel { + s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) + return s +} + +func (s *SecretWithClientCredentialsModel) WithComment(comment string) *SecretWithClientCredentialsModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SecretWithClientCredentialsModel) WithDatabase(database string) *SecretWithClientCredentialsModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SecretWithClientCredentialsModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithClientCredentialsModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SecretWithClientCredentialsModel) WithName(name string) *SecretWithClientCredentialsModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +// oauth_scopes attribute type is not yet supported, so WithOauthScopes can't be generated + +func (s *SecretWithClientCredentialsModel) WithSchema(schema string) *SecretWithClientCredentialsModel { + s.Schema = tfconfig.StringVariable(schema) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SecretWithClientCredentialsModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.ApiAuthentication = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithCommentValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Comment = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Database = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.FullyQualifiedName = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithNameValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Name = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithOauthScopesValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.OauthScopes = value + return s +} + +func (s *SecretWithClientCredentialsModel) WithSchemaValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { + s.Schema = value + return s +} +func (s*SecretWithClientCredentialsModel) WithOauthScopes(oauthScopes []string) *SecretWithClientCredentialsModel { + oauthScopesStringVariables := make([]tfconfig.Variable, len(oauthScopes)) + for i, v := range oauthScopes { + oauthScopesStringVariables[i] = tfconfig.StringVariable(v) + } + + s.OauthScopes = tfconfig.SetVariable(oauthScopesStringVariables...) + return s +} diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 0001f75795..0eacca77b0 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -173,6 +173,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ScimSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, + resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.Databases.ShowByID) + }, resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Databases.ShowByID) }, diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index edb760e412..f3a647471a 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -470,6 +470,7 @@ func getResources() map[string]*schema.Resource { "snowflake_saml2_integration": resources.SAML2Integration(), "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), + "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 219617d4e6..2eda84ffed 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -39,6 +39,7 @@ const ( Saml2SecurityIntegration resource = "snowflake_saml2_integration" Schema resource = "snowflake_schema" ScimSecurityIntegration resource = "snowflake_scim_integration" + SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go new file mode 100644 index 0000000000..b31e229eb4 --- /dev/null +++ b/pkg/resources/secret_with_client_credentials_flow.go @@ -0,0 +1,211 @@ +package resources + +import ( + "context" + "errors" + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretClientCredentialsSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "database": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "schema": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + }, + "oauth_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the secret.", + }, + FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, +} + +func SecretWithClientCredentials() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextSecret, + ReadContext: ReadContextSecret, + UpdateContext: UpdateContextSecret, + DeleteContext: DeleteContextSecret, + + Schema: secretClientCredentialsSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func CreateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + databaseName := d.Get("database").(string) + schemaName := d.Get("schema").(string) + name := d.Get("name").(string) + apiIntegrationString := d.Get("api_authentication").(string) + apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) + if err != nil { + return diag.FromErr(err) + } + + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + + stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) + oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) + for i, scope := range stringScopes { + oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} + } + + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) + + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) + } + + err = client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeResourceIdentifier(id)) + + return ReadContextSecret(ctx, d, meta) +} + +func ReadContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if secret == nil || err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, + } + } + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + if err = d.Set("name", secretDescription.Name); err != nil { + return diag.FromErr(err) + } + if err = d.Set("database", secretDescription.DatabaseName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("schema", secretDescription.SchemaName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { + return diag.FromErr(err) + } + if err = d.Set("comment", secretDescription.Comment); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChange("oauth_scopes") { + stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) + oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) + for i, scope := range stringScopes { + oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} + } + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest(oauthScopes) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthClientCredentialsFlow(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("comment") { + comment := d.Get("comment").(string) + request := sdk.NewAlterSecretRequest(id) + if len(comment) == 0 { + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + request.WithUnset(*unsetRequest) + } else { + setRequest := sdk.NewSecretSetRequest().WithComment(comment) + request.WithSet(*setRequest) + } + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextSecret(ctx, d, meta) +} + +func DeleteContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go new file mode 100644 index 0000000000..854d928b9e --- /dev/null +++ b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go @@ -0,0 +1,191 @@ +package resources_test + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := "aaa" + newComment := random.Comment() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), + ) + t.Cleanup(apiIntegrationCleanup) + + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}).WithComment(comment) + secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + Steps: []resource.TestStep{ + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(comment), + ), + resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), + ), + }, + // set oauth_scopes and comment in config + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{"foo"}). + WithComment(newComment)), + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), + ), + }, + // unset comment + { + Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(newComment), nil), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). + HasCommentString(""), + ), + }, + // destroy + { + Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), + Destroy: true, + }, + // create without comment + { + Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo", "bar"})), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(""), + ), + resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), + ), + }, + // import + { + ResourceName: secretModelWithoutComment.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "api_authentication", integrationId.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_scopes.#", "2"), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), + ), + }, + }, + }) +} + +func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), + ) + t.Cleanup(apiIntegrationCleanup) + + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, nil) + secretModelWithScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo"}) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + Steps: []resource.TestStep{ + // Create secret without providing oauth_scopes value + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(""), + ), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "0"), + ), + }, + // Set oauth_scopes + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{"foo"}), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()), + assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), + ), + }, + }, + }) +} From 8348809c7254bbbfc20cd2dcd04c04376aba0c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 26 Sep 2024 14:00:28 +0200 Subject: [PATCH 40/65] adding secret common to avoid duplication --- .../objectassert/secret_snowflake_gen.go | 3 +- ...et_with_authorization_code_resource_gen.go | 117 +++++++++ .../secret_show_output_gen.go | 87 +++++++ ...ecret_with_authorization_code_model_gen.go | 152 ++++++++++++ pkg/provider/provider.go | 1 + pkg/resources/secret_common.go | 105 ++++++++ ...cret_with_authorization_code_grant_flow.go | 228 ++++++++++++++++++ ...ization_code_grant_flow_acceptance_test.go | 22 ++ .../secret_with_client_credentials_flow.go | 28 ++- ...client_credentials_flow_acceptance_test.go | 2 +- pkg/schemas/gen/sdk_show_result_structs.go | 1 + pkg/schemas/secret_gen.go | 68 ++++++ 12 files changed, 801 insertions(+), 13 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go create mode 100644 pkg/resources/secret_common.go create mode 100644 pkg/resources/secret_with_authorization_code_grant_flow.go create mode 100644 pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go create mode 100644 pkg/schemas/secret_gen.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go index 44ebf3c3ef..2a61443f09 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -4,7 +4,6 @@ package objectassert import ( "fmt" - "slices" "testing" "time" @@ -115,7 +114,7 @@ func (s *SecretAssert) HasSecretType(expected string) *SecretAssert { func (s *SecretAssert) HasOauthScopes(expected []string) *SecretAssert { s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { t.Helper() - if !slices.Equal(o.OauthScopes, expected) { + if o.OauthScopes != expected { return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, o.OauthScopes) } return nil diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go new file mode 100644 index 0000000000..610faae14e --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go @@ -0,0 +1,117 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +type SecretWithAuthorizationCodeResourceAssert struct { + *assert.ResourceAssert +} + +func SecretWithAuthorizationCodeResource(t *testing.T, name string) *SecretWithAuthorizationCodeResourceAssert { + t.Helper() + + return &SecretWithAuthorizationCodeResourceAssert{ + ResourceAssert: assert.NewResourceAssert(name, "resource"), + } +} + +func ImportedSecretWithAuthorizationCodeResource(t *testing.T, id string) *SecretWithAuthorizationCodeResourceAssert { + t.Helper() + + return &SecretWithAuthorizationCodeResourceAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"), + } +} + +/////////////////////////////////// +// Attribute value string checks // +/////////////////////////////////// + +func (s *SecretWithAuthorizationCodeResourceAssert) HasApiAuthenticationString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("api_authentication", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasCommentString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("comment", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasDatabaseString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("database", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasFullyQualifiedNameString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("fully_qualified_name", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNameString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("name", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_refresh_token", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenExpiryTimeString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_refresh_token_expiry_time", expected)) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasSchemaString(expected string) *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueSet("schema", expected)) + return s +} + +//////////////////////////// +// Attribute empty checks // +//////////////////////////// + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoApiAuthentication() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("api_authentication")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoComment() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("comment")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoDatabase() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("database")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoFullyQualifiedName() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("fully_qualified_name")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoName() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("name")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoOauthRefreshToken() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("oauth_refresh_token")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoOauthRefreshTokenExpiryTime() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("oauth_refresh_token_expiry_time")) + return s +} + +func (s *SecretWithAuthorizationCodeResourceAssert) HasNoSchema() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValueNotSet("schema")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go new file mode 100644 index 0000000000..23a3f42818 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go @@ -0,0 +1,87 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceshowoutputassert + +import ( + "testing" + "time" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +// to ensure sdk package is used +var _ = sdk.Object{} + +type SecretShowOutputAssert struct { + *assert.ResourceAssert +} + +func SecretShowOutput(t *testing.T, name string) *SecretShowOutputAssert { + t.Helper() + + s := SecretShowOutputAssert{ + ResourceAssert: assert.NewResourceAssert(name, "show_output"), + } + s.AddAssertion(assert.ValueSet("show_output.#", "1")) + return &s +} + +func ImportedSecretShowOutput(t *testing.T, id string) *SecretShowOutputAssert { + t.Helper() + + s := SecretShowOutputAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "show_output"), + } + s.AddAssertion(assert.ValueSet("show_output.#", "1")) + return &s +} + +//////////////////////////// +// Attribute value checks // +//////////////////////////// + +func (s *SecretShowOutputAssert) HasCreatedOn(expected time.Time) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("created_on", expected.String())) + return s +} + +func (s *SecretShowOutputAssert) HasName(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("name", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasSchemaName(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("schema_name", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasDatabaseName(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("database_name", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasOwner(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("owner", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasComment(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("comment", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasSecretType(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("secret_type", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasOauthScopes(expected []string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("oauth_scopes", expected)) + return s +} + +func (s *SecretShowOutputAssert) HasOwnerRoleType(expected string) *SecretShowOutputAssert { + s.AddAssertion(assert.ResourceShowOutputValueSet("owner_role_type", expected)) + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go new file mode 100644 index 0000000000..83047aa647 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go @@ -0,0 +1,152 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SecretWithAuthorizationCodeModel struct { + ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` + Comment tfconfig.Variable `json:"comment,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + OauthRefreshToken tfconfig.Variable `json:"oauth_refresh_token,omitempty"` + OauthRefreshTokenExpiryTime tfconfig.Variable `json:"oauth_refresh_token_expiry_time,omitempty"` + Schema tfconfig.Variable `json:"schema,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func SecretWithAuthorizationCode( + resourceName string, + apiAuthentication string, + database string, + name string, + oauthRefreshToken string, + oauthRefreshTokenExpiryTime string, + schema string, +) *SecretWithAuthorizationCodeModel { + s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCode)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthRefreshToken(oauthRefreshToken) + s.WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime) + s.WithSchema(schema) + return s +} + +func SecretWithAuthorizationCodeWithDefaultMeta( + apiAuthentication string, + database string, + name string, + oauthRefreshToken string, + oauthRefreshTokenExpiryTime string, + schema string, +) *SecretWithAuthorizationCodeModel { + s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithAuthorizationCode)} + s.WithApiAuthentication(apiAuthentication) + s.WithDatabase(database) + s.WithName(name) + s.WithOauthRefreshToken(oauthRefreshToken) + s.WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime) + s.WithSchema(schema) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SecretWithAuthorizationCodeModel) WithApiAuthentication(apiAuthentication string) *SecretWithAuthorizationCodeModel { + s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithComment(comment string) *SecretWithAuthorizationCodeModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithDatabase(database string) *SecretWithAuthorizationCodeModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithAuthorizationCodeModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithName(name string) *SecretWithAuthorizationCodeModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshToken(oauthRefreshToken string) *SecretWithAuthorizationCodeModel { + s.OauthRefreshToken = tfconfig.StringVariable(oauthRefreshToken) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime string) *SecretWithAuthorizationCodeModel { + s.OauthRefreshTokenExpiryTime = tfconfig.StringVariable(oauthRefreshTokenExpiryTime) + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithSchema(schema string) *SecretWithAuthorizationCodeModel { + s.Schema = tfconfig.StringVariable(schema) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SecretWithAuthorizationCodeModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.ApiAuthentication = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithCommentValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Comment = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Database = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.FullyQualifiedName = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Name = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.OauthRefreshToken = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTimeValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.OauthRefreshTokenExpiryTime = value + return s +} + +func (s *SecretWithAuthorizationCodeModel) WithSchemaValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { + s.Schema = value + return s +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index f3a647471a..920e69286f 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -471,6 +471,7 @@ func getResources() map[string]*schema.Resource { "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), + "snowflake_secret_with_authorization_code": resources.SecretWithAuthorizationCode(), "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go new file mode 100644 index 0000000000..dc1ee3eaab --- /dev/null +++ b/pkg/resources/secret_common.go @@ -0,0 +1,105 @@ +package resources + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretCommonSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "database": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "schema": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the secret.", + }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecretSchema, + }, + }, + FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, +} + +type commonSecretCreate struct { + name string + database string + schema string + comment *string +} + +func handleSecretCreate(d *schema.ResourceData) commonSecretCreate { + create := commonSecretCreate{ + name: d.Get("name").(string), + database: d.Get("database").(string), + schema: d.Get("schema").(string), + } + if v, ok := d.GetOk("comment"); ok { + create.comment = sdk.Pointer(v.(string)) + } + + return create +} + +func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret) error { + if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { + return err + } + if err := d.Set("name", id.Name()); err != nil { + return err + } + if err := d.Set("database", secret.DatabaseName); err != nil { + return err + } + if err := d.Set("schema", secret.SchemaName); err != nil { + return err + } + if err := d.Set("comment", secret.Comment); err != nil { + return err + } + return nil +} + +type commonSecretSet struct { + comment *string +} + +type commonSecretUnset struct { + comment *bool +} + +func handleSecretUpdate(d *schema.ResourceData) (commonSecretSet, commonSecretUnset) { + set, unset := commonSecretSet{}, commonSecretUnset{} + + if d.HasChange("comment") { + if v, ok := d.GetOk("comment"); ok { + set.comment = sdk.Pointer(v.(string)) + } else { + unset.comment = sdk.Pointer(true) + } + } + + return set, unset +} diff --git a/pkg/resources/secret_with_authorization_code_grant_flow.go b/pkg/resources/secret_with_authorization_code_grant_flow.go new file mode 100644 index 0000000000..6d78aa987b --- /dev/null +++ b/pkg/resources/secret_with_authorization_code_grant_flow.go @@ -0,0 +1,228 @@ +package resources + +import ( + "context" + "errors" + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretAuthorizationCodeSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "database": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "schema": { + Type: schema.TypeString, + Required: true, + Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), + ForceNew: true, + DiffSuppressFunc: suppressIdentifierQuoting, + }, + "oauth_refresh_token": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", + }, + "oauth_refresh_token_expiry_time": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", + }, + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the secret.", + }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecretSchema, + }, + }, + FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, +} + +func SecretWithAuthorizationCode() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextSecretWithAuthorizationCode, + ReadContext: ReadContextSecretWithAuthorizationCode, + UpdateContext: UpdateContextSecretWithAuthorizationCode, + DeleteContext: DeleteContextSecretWithAuthorizationCode, + + Schema: secretAuthorizationCodeSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + databaseName := d.Get("database").(string) + schemaName := d.Get("schema").(string) + name := d.Get("name").(string) + + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + + apiIntegrationString := d.Get("api_authentication").(string) + apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) + if err != nil { + return diag.FromErr(err) + } + + refreshToken := d.Get("oauth_refresh_token").(string) + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + + request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, apiIntegration) + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) + } + + err = client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeResourceIdentifier(id)) + + return ReadContextSecretWithAuthorizationCode(ctx, d, meta) +} + +func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if secret == nil || err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, + } + } + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + if err = d.Set("name", secretDescription.Name); err != nil { + return diag.FromErr(err) + } + if err = d.Set("database", secretDescription.DatabaseName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("schema", secretDescription.SchemaName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime); err != nil { + return diag.FromErr(err) + } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } + if err = d.Set("comment", secretDescription.Comment); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if d.HasChange("oauth_refresh_token") { + refreshToken := d.Get("oauth_refresh_token").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshToken(refreshToken) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("oauth_refresh_token_expiry_time") { + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("comment") { + comment := d.Get("comment").(string) + request := sdk.NewAlterSecretRequest(id) + if len(comment) == 0 { + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + request.WithUnset(*unsetRequest) + } else { + setRequest := sdk.NewSecretSetRequest().WithComment(comment) + request.WithSet(*setRequest) + } + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextSecretWithAuthorizationCode(ctx, d, meta) +} + +func DeleteContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go new file mode 100644 index 0000000000..cc343ed42d --- /dev/null +++ b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go @@ -0,0 +1,22 @@ +package resources + +import ( + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "testing" +) + +func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + newComment := random.Comment() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + +} diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go index b31e229eb4..f1b345c678 100644 --- a/pkg/resources/secret_with_client_credentials_flow.go +++ b/pkg/resources/secret_with_client_credentials_flow.go @@ -50,15 +50,23 @@ var secretClientCredentialsSchema = map[string]*schema.Schema{ Optional: true, Description: "Specifies a comment for the secret.", }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecretSchema, + }, + }, FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, } func SecretWithClientCredentials() *schema.Resource { return &schema.Resource{ - CreateContext: CreateContextSecret, - ReadContext: ReadContextSecret, - UpdateContext: UpdateContextSecret, - DeleteContext: DeleteContextSecret, + CreateContext: CreateContextSecretWithClientCredentials, + ReadContext: ReadContextSecretWithClientCredentials, + UpdateContext: UpdateContextSecretWithClientCredentials, + DeleteContext: DeleteContextSecretWithClientCredentials, Schema: secretClientCredentialsSchema, Importer: &schema.ResourceImporter{ @@ -67,7 +75,7 @@ func SecretWithClientCredentials() *schema.Resource { } } -func CreateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client databaseName := d.Get("database").(string) schemaName := d.Get("schema").(string) @@ -99,10 +107,10 @@ func CreateContextSecret(ctx context.Context, d *schema.ResourceData, meta inter d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadContextSecret(ctx, d, meta) + return ReadContextSecretWithClientCredentials(ctx, d, meta) } -func ReadContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -154,7 +162,7 @@ func ReadContextSecret(ctx context.Context, d *schema.ResourceData, meta interfa return nil } -func UpdateContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -192,10 +200,10 @@ func UpdateContextSecret(ctx context.Context, d *schema.ResourceData, meta inter } } - return ReadContextSecret(ctx, d, meta) + return ReadContextSecretWithClientCredentials(ctx, d, meta) } -func DeleteContextSecret(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { diff --git a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go index 854d928b9e..4be88ceaa3 100644 --- a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go @@ -151,7 +151,7 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { }, CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), Steps: []resource.TestStep{ - // Create secret without providing oauth_scopes value + // create secret without providing oauth_scopes value { Config: config.FromModel(t, secretModel), Check: resource.ComposeTestCheckFunc( diff --git a/pkg/schemas/gen/sdk_show_result_structs.go b/pkg/schemas/gen/sdk_show_result_structs.go index ea2e977160..337367a112 100644 --- a/pkg/schemas/gen/sdk_show_result_structs.go +++ b/pkg/schemas/gen/sdk_show_result_structs.go @@ -37,6 +37,7 @@ var SdkShowResultStructs = []any{ sdk.Role{}, sdk.RowAccessPolicy{}, sdk.Schema{}, + sdk.Secret{}, sdk.SecurityIntegration{}, sdk.Sequence{}, sdk.SessionPolicy{}, diff --git a/pkg/schemas/secret_gen.go b/pkg/schemas/secret_gen.go new file mode 100644 index 0000000000..5a341ca7d7 --- /dev/null +++ b/pkg/schemas/secret_gen.go @@ -0,0 +1,68 @@ +// Code generated by sdk-to-schema generator; DO NOT EDIT. + +package schemas + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ShowSecretSchema represents output of SHOW query for the single Secret. +var ShowSecretSchema = map[string]*schema.Schema{ + "created_on": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "schema_name": { + Type: schema.TypeString, + Computed: true, + }, + "database_name": { + Type: schema.TypeString, + Computed: true, + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "comment": { + Type: schema.TypeString, + Computed: true, + }, + "secret_type": { + Type: schema.TypeString, + Computed: true, + }, + "oauth_scopes": { + Type: schema.TypeInvalid, + Computed: true, + }, + "owner_role_type": { + Type: schema.TypeString, + Computed: true, + }, +} + +var _ = ShowSecretSchema + +func SecretToSchema(secret *sdk.Secret) map[string]any { + secretSchema := make(map[string]any) + secretSchema["created_on"] = secret.CreatedOn.String() + secretSchema["name"] = secret.Name + secretSchema["schema_name"] = secret.SchemaName + secretSchema["database_name"] = secret.DatabaseName + secretSchema["owner"] = secret.Owner + if secret.Comment != nil { + secretSchema["comment"] = secret.Comment + } + secretSchema["secret_type"] = secret.SecretType + secretSchema["oauth_scopes"] = secret.OauthScopes + secretSchema["owner_role_type"] = secret.OwnerRoleType + return secretSchema +} + +var _ = SecretToSchema From 4753096aceb7bce9ddfd7fc70016805edf2acfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 26 Sep 2024 15:25:28 +0200 Subject: [PATCH 41/65] acc tests pass with usage of secret_common --- .../objectassert/secret_snowflake_gen.go | 3 +- .../secret_show_output_gen.go | 4 +- pkg/acceptance/check_destroy.go | 2 +- pkg/provider/resources/resources.go | 1 + pkg/resources/secret_common.go | 28 +++-- ...ization_code_grant_flow_acceptance_test.go | 28 ++--- .../secret_with_client_credentials_flow.go | 102 +++++------------- pkg/schemas/secret_gen.go | 3 +- 8 files changed, 61 insertions(+), 110 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go index 2a61443f09..44ebf3c3ef 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -4,6 +4,7 @@ package objectassert import ( "fmt" + "slices" "testing" "time" @@ -114,7 +115,7 @@ func (s *SecretAssert) HasSecretType(expected string) *SecretAssert { func (s *SecretAssert) HasOauthScopes(expected []string) *SecretAssert { s.AddAssertion(func(t *testing.T, o *sdk.Secret) error { t.Helper() - if o.OauthScopes != expected { + if !slices.Equal(o.OauthScopes, expected) { return fmt.Errorf("expected oauth scopes: %v; got: %v", expected, o.OauthScopes) } return nil diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go index 23a3f42818..f1182d6d9e 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go @@ -77,7 +77,9 @@ func (s *SecretShowOutputAssert) HasSecretType(expected string) *SecretShowOutpu } func (s *SecretShowOutputAssert) HasOauthScopes(expected []string) *SecretShowOutputAssert { - s.AddAssertion(assert.ResourceShowOutputValueSet("oauth_scopes", expected)) + for _, v := range expected { + s.AddAssertion(assert.ValueSet("oauth_scopes.*", v)) + } return s } diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 0eacca77b0..5857663a9c 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -174,7 +174,7 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { - return runShowById(ctx, id, client.Databases.ShowByID) + return runShowById(ctx, id, client.Secrets.ShowByID) }, resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Databases.ShowByID) diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 2eda84ffed..7c72d000c5 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -40,6 +40,7 @@ const ( Schema resource = "snowflake_schema" ScimSecurityIntegration resource = "snowflake_scim_integration" SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" + SecretWithAuthorizationCode resource = "snowflake_secret_with_authorization_code" SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index dc1ee3eaab..a2c54f2364 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -79,27 +79,23 @@ func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, sec if err := d.Set("comment", secret.Comment); err != nil { return err } + if err := d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}); err != nil { + return err + } return nil } -type commonSecretSet struct { - comment *string -} - -type commonSecretUnset struct { - comment *bool -} - -func handleSecretUpdate(d *schema.ResourceData) (commonSecretSet, commonSecretUnset) { - set, unset := commonSecretSet{}, commonSecretUnset{} - +func handleSecretUpdate(id sdk.SchemaObjectIdentifier, d *schema.ResourceData) *sdk.AlterSecretRequest { if d.HasChange("comment") { - if v, ok := d.GetOk("comment"); ok { - set.comment = sdk.Pointer(v.(string)) + comment := d.Get("comment").(string) + request := sdk.NewAlterSecretRequest(id) + if len(comment) == 0 { + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + return request.WithUnset(*unsetRequest) } else { - unset.comment = sdk.Pointer(true) + setRequest := sdk.NewSecretSetRequest().WithComment(comment) + return request.WithSet(*setRequest) } } - - return set, unset + return nil } diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go index cc343ed42d..b04a9c415c 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go @@ -1,22 +1,22 @@ -package resources +package resources_test import ( - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "testing" ) -func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - comment := random.Comment() - newComment := random.Comment() +func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { + /* + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + newComment := random.Comment() - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), - ) - t.Cleanup(apiIntegrationCleanup) + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + + */ } diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go index f1b345c678..8e987fa694 100644 --- a/pkg/resources/secret_with_client_credentials_flow.go +++ b/pkg/resources/secret_with_client_credentials_flow.go @@ -6,60 +6,28 @@ import ( "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -var secretClientCredentialsSchema = map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "database": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "schema": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "api_authentication": { - Type: schema.TypeString, - ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", - }, - "oauth_scopes": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Required: true, - Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a comment for the secret.", - }, - ShowOutputAttributeName: { - Type: schema.TypeList, - Computed: true, - Description: "Outputs the result of `SHOW SECRET` for the given secret.", - Elem: &schema.Resource{ - Schema: schemas.ShowSecretSchema, +var secretClientCredentialsSchema = func() map[string]*schema.Schema { + secretClientCredentials := map[string]*schema.Schema{ + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", }, - }, - FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, -} + "oauth_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", + }, + } + return helpers.MergeMaps(secretCommonSchema, secretClientCredentials) +}() func SecretWithClientCredentials() *schema.Resource { return &schema.Resource{ @@ -77,16 +45,15 @@ func SecretWithClientCredentials() *schema.Resource { func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName := d.Get("database").(string) - schemaName := d.Get("schema").(string) - name := d.Get("name").(string) + commonCreate := handleSecretCreate(d) + apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) if err != nil { return diag.FromErr(err) } - id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) @@ -96,8 +63,8 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) - if v, ok := d.GetOk("comment"); ok { - request.WithComment(v.(string)) + if commonCreate.comment != nil { + request.WithComment(*commonCreate.comment) } err = client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) @@ -137,17 +104,12 @@ func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.Resou } } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - if err = d.Set("name", secretDescription.Name); err != nil { + if err := handleSecretRead(d, id, secret); err != nil { return diag.FromErr(err) } - if err = d.Set("database", secretDescription.DatabaseName); err != nil { - return diag.FromErr(err) - } - if err = d.Set("schema", secretDescription.SchemaName); err != nil { + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { return diag.FromErr(err) } if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { @@ -156,9 +118,6 @@ func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.Resou if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { return diag.FromErr(err) } - if err = d.Set("comment", secretDescription.Comment); err != nil { - return diag.FromErr(err) - } return nil } @@ -185,16 +144,7 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res } } - if d.HasChange("comment") { - comment := d.Get("comment").(string) - request := sdk.NewAlterSecretRequest(id) - if len(comment) == 0 { - unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) - request.WithUnset(*unsetRequest) - } else { - setRequest := sdk.NewSecretSetRequest().WithComment(comment) - request.WithSet(*setRequest) - } + if request := handleSecretUpdate(id, d); request != nil { if err := client.Secrets.Alter(ctx, request); err != nil { return diag.FromErr(err) } diff --git a/pkg/schemas/secret_gen.go b/pkg/schemas/secret_gen.go index 5a341ca7d7..e76a1777d1 100644 --- a/pkg/schemas/secret_gen.go +++ b/pkg/schemas/secret_gen.go @@ -38,7 +38,8 @@ var ShowSecretSchema = map[string]*schema.Schema{ Computed: true, }, "oauth_scopes": { - Type: schema.TypeInvalid, + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, }, "owner_role_type": { From 46e2181e946531c060b50b5ee1240138adf18ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 27 Sep 2024 10:54:23 +0200 Subject: [PATCH 42/65] Secret with authorization code acceptance test, diff suppress --- ...cret_with_authorization_code_grant_flow.go | 123 ++++++------------ ...ization_code_grant_flow_acceptance_test.go | 95 ++++++++++++-- 2 files changed, 121 insertions(+), 97 deletions(-) diff --git a/pkg/resources/secret_with_authorization_code_grant_flow.go b/pkg/resources/secret_with_authorization_code_grant_flow.go index 6d78aa987b..922dc870fa 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow.go +++ b/pkg/resources/secret_with_authorization_code_grant_flow.go @@ -6,64 +6,35 @@ import ( "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "time" ) -var secretAuthorizationCodeSchema = map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "database": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The database in which to create the secret"), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "schema": { - Type: schema.TypeString, - Required: true, - Description: blocklistedCharactersFieldDescription("The schema in which to create the secret."), - ForceNew: true, - DiffSuppressFunc: suppressIdentifierQuoting, - }, - "oauth_refresh_token": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", - }, - "oauth_refresh_token_expiry_time": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", - }, - "api_authentication": { - Type: schema.TypeString, - ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a comment for the secret.", - }, - ShowOutputAttributeName: { - Type: schema.TypeList, - Computed: true, - Description: "Outputs the result of `SHOW SECRET` for the given secret.", - Elem: &schema.Resource{ - Schema: schemas.ShowSecretSchema, +var secretAuthorizationCodeSchema = func() map[string]*schema.Schema { + secretAuthorizationCode := map[string]*schema.Schema{ + "oauth_refresh_token": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", }, - }, - FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, -} + "oauth_refresh_token_expiry_time": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", + // has to be ignored since snowflake changes the date format to different one than is required in the input + //DiffSuppressFunc: IgnoreAfterCreation, + }, + "api_authentication": { + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + Required: true, + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + }, + } + return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) +}() func SecretWithAuthorizationCode() *schema.Resource { return &schema.Resource{ @@ -81,11 +52,9 @@ func SecretWithAuthorizationCode() *schema.Resource { func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName := d.Get("database").(string) - schemaName := d.Get("schema").(string) - name := d.Get("name").(string) + commonCreate := handleSecretCreate(d) - id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) + id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) @@ -138,28 +107,19 @@ func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Resou } } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { + if err := handleSecretRead(d, id, secret); err != nil { return diag.FromErr(err) } - if err = d.Set("name", secretDescription.Name); err != nil { - return diag.FromErr(err) - } - if err = d.Set("database", secretDescription.DatabaseName); err != nil { - return diag.FromErr(err) - } - if err = d.Set("schema", secretDescription.SchemaName); err != nil { + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { return diag.FromErr(err) } - if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime); err != nil { + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { return diag.FromErr(err) } if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } - if err = d.Set("comment", secretDescription.Comment); err != nil { - return diag.FromErr(err) - } return nil } @@ -170,6 +130,12 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res return diag.FromErr(err) } + if request := handleSecretUpdate(id, d); request != nil { + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("oauth_refresh_token") { refreshToken := d.Get("oauth_refresh_token").(string) @@ -183,10 +149,10 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res } if d.HasChange("oauth_refresh_token_expiry_time") { - refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(time.Time) request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime.Format(time.DateOnly)) request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) if err := client.Secrets.Alter(ctx, request); err != nil { @@ -194,21 +160,6 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res } } - if d.HasChange("comment") { - comment := d.Get("comment").(string) - request := sdk.NewAlterSecretRequest(id) - if len(comment) == 0 { - unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) - request.WithUnset(*unsetRequest) - } else { - setRequest := sdk.NewSecretSetRequest().WithComment(comment) - request.WithSet(*setRequest) - } - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - return ReadContextSecretWithAuthorizationCode(ctx, d, meta) } diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go index b04a9c415c..2978889b4d 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go @@ -1,22 +1,95 @@ package resources_test import ( + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" "testing" + "time" ) func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { - /* - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - comment := random.Comment() - newComment := random.Comment() + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + newComment := random.Comment() + refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) + newRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), - ) - t.Cleanup(apiIntegrationCleanup) + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + secretModel := model.SecretWithAuthorizationCode("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryTime).WithComment(comment) - */ + //expectedTime := helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, newRefreshTokenExpiryTime).String() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + Steps: []resource.TestStep{ + // create + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString("test_token"). + HasOauthRefreshTokenExpiryTimeString(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()). + HasCommentString(comment), + ), + ), + }, + // set all + { + Config: config.FromModel(t, secretModel. + WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryTime). + WithOauthRefreshToken("new_test_token"). + WithComment(newComment), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String("test_token"), sdk.String("new_test_token")), + //diff suppressed + //planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()), sdk.String(expectedTime)), + }, + }, + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString("new_test_token"). + //diff suppressed + //HasOauthRefreshTokenExpiryTimeString(expectedTime). + HasCommentString(newComment), + ), + ), + }, + }, + }) } From c3f3ea7698888de22f179a3189424a53e64db62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 27 Sep 2024 11:02:23 +0200 Subject: [PATCH 43/65] helper function for snowflake datetime for secret --- pkg/acceptance/helpers/secret_client.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index e3eb87c8f1..4c161ea9b6 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -3,6 +3,7 @@ package helpers import ( "context" "testing" + "time" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" @@ -21,6 +22,18 @@ func NewSecretClient(context *TestClientContext, idsGenerator *IdsGenerator) *Se } } +func StringDateToSnowflakeTimeFormat(t *testing.T, inputLayout, date string) *time.Time { + t.Helper() + parsedTime, err := time.Parse(inputLayout, date) + require.NoError(t, err) + + loc, err := time.LoadLocation("America/Los_Angeles") + require.NoError(t, err) + + adjustedTime := parsedTime.In(loc) + return &adjustedTime +} + func (c *SecretClient) client() sdk.Secrets { return c.context.client.Secrets } From 91269d64aae0321de90dca668930c4fb2892f2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 27 Sep 2024 11:02:57 +0200 Subject: [PATCH 44/65] check destroy and few cleanups --- .../secret_with_client_credentials_resource_ext.go | 5 ++--- .../config/model/secret_with_authorization_code_model_gen.go | 2 +- pkg/acceptance/check_destroy.go | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go index 5b3c26dd5d..543d330d5e 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go @@ -1,14 +1,13 @@ package resourceassert import ( - "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" ) func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopes(expected []sdk.ApiIntegrationScope) *SecretWithClientCredentialsResourceAssert { - for i, v := range expected { - s.AddAssertion(assert.ValueSet(fmt.Sprintf("oauth_scopes.%d", i), v.Scope)) + for _, v := range expected { + s.AddAssertion(assert.ValueSet("oauth_scopes.*", v.Scope)) } return s } diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go index 83047aa647..65516f427d 100644 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go @@ -30,10 +30,10 @@ func SecretWithAuthorizationCode( resourceName string, apiAuthentication string, database string, + schema string, name string, oauthRefreshToken string, oauthRefreshTokenExpiryTime string, - schema string, ) *SecretWithAuthorizationCodeModel { s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCode)} s.WithApiAuthentication(apiAuthentication) diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 5857663a9c..ea0bcd17c4 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -173,6 +173,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ScimSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, + resources.SecretWithAuthorizationCode: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.Secrets.ShowByID) + }, resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, From b91b97c42d3c6c6e761d0e099a0b4b7f50218dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 30 Sep 2024 11:59:44 +0200 Subject: [PATCH 45/65] adding secret with basic authentication flow --- ...et_with_authorization_code_resource_ext.go | 10 ++ ..._with_basic_authentication_resource_gen.go | 107 ++++++++++++ ...ret_with_basic_authentication_model_gen.go | 137 +++++++++++++++ ...ith_oauth_authorization_code_model_gen.go} | 0 ...ith_oauth_client_credentials_model_gen.go} | 0 pkg/acceptance/check_destroy.go | 3 + pkg/acceptance/helpers/secret_client.go | 16 +- pkg/provider/provider.go | 1 + pkg/provider/resources/resources.go | 1 + pkg/resources/secret_common.go | 13 +- .../secret_with_basic_authentication_flow.go | 160 ++++++++++++++++++ ...sic_authentication_flow_acceptance_test.go | 104 ++++++++++++ ...th_oauth_authorization_code_grant_flow.go} | 15 +- ...zation_code_grant_flow_acceptance_test.go} | 41 +++-- ...ret_with_oauth_client_credentials_flow.go} | 8 +- ...lient_credentials_flow_acceptance_test.go} | 0 pkg/schemas/secret.go | 84 +++++++++ 17 files changed, 656 insertions(+), 44 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_basic_authentication_resource_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_basic_authentication_model_gen.go rename pkg/acceptance/bettertestspoc/config/model/{secret_with_authorization_code_model_gen.go => secret_with_oauth_authorization_code_model_gen.go} (100%) rename pkg/acceptance/bettertestspoc/config/model/{secret_with_client_credentials_model_gen.go => secret_with_oauth_client_credentials_model_gen.go} (100%) create mode 100644 pkg/resources/secret_with_basic_authentication_flow.go create mode 100644 pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go rename pkg/resources/{secret_with_authorization_code_grant_flow.go => secret_with_oauth_authorization_code_grant_flow.go} (94%) rename pkg/resources/{secret_with_authorization_code_grant_flow_acceptance_test.go => secret_with_oauth_authorization_code_grant_flow_acceptance_test.go} (65%) rename pkg/resources/{secret_with_client_credentials_flow.go => secret_with_oauth_client_credentials_flow.go} (98%) rename pkg/resources/{secret_with_client_credentials_flow_acceptance_test.go => secret_with_oauth_client_credentials_flow_acceptance_test.go} (100%) create mode 100644 pkg/schemas/secret.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go new file mode 100644 index 0000000000..73989173af --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go @@ -0,0 +1,10 @@ +package resourceassert + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenNoEmpty() *SecretWithAuthorizationCodeResourceAssert { + s.AddAssertion(assert.ValuePresent("oauth_refresh_token_expiry_time")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_basic_authentication_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_basic_authentication_resource_gen.go new file mode 100644 index 0000000000..186aed6035 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_basic_authentication_resource_gen.go @@ -0,0 +1,107 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +type SecretWithBasicAuthenticationResourceAssert struct { + *assert.ResourceAssert +} + +func SecretWithBasicAuthenticationResource(t *testing.T, name string) *SecretWithBasicAuthenticationResourceAssert { + t.Helper() + + return &SecretWithBasicAuthenticationResourceAssert{ + ResourceAssert: assert.NewResourceAssert(name, "resource"), + } +} + +func ImportedSecretWithBasicAuthenticationResource(t *testing.T, id string) *SecretWithBasicAuthenticationResourceAssert { + t.Helper() + + return &SecretWithBasicAuthenticationResourceAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"), + } +} + +/////////////////////////////////// +// Attribute value string checks // +/////////////////////////////////// + +func (s *SecretWithBasicAuthenticationResourceAssert) HasCommentString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("comment", expected)) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasDatabaseString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("database", expected)) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasFullyQualifiedNameString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("fully_qualified_name", expected)) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNameString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("name", expected)) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasPasswordString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("password", expected)) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasSchemaString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("schema", expected)) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasUsernameString(expected string) *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueSet("username", expected)) + return s +} + +//////////////////////////// +// Attribute empty checks // +//////////////////////////// + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoComment() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("comment")) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoDatabase() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("database")) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoFullyQualifiedName() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("fully_qualified_name")) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoName() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("name")) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoPassword() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("password")) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoSchema() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("schema")) + return s +} + +func (s *SecretWithBasicAuthenticationResourceAssert) HasNoUsername() *SecretWithBasicAuthenticationResourceAssert { + s.AddAssertion(assert.ValueNotSet("username")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_basic_authentication_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_basic_authentication_model_gen.go new file mode 100644 index 0000000000..975b3c7ff7 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_basic_authentication_model_gen.go @@ -0,0 +1,137 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SecretWithBasicAuthenticationModel struct { + Comment tfconfig.Variable `json:"comment,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + Password tfconfig.Variable `json:"password,omitempty"` + Schema tfconfig.Variable `json:"schema,omitempty"` + Username tfconfig.Variable `json:"username,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func SecretWithBasicAuthentication( + resourceName string, + database string, + name string, + password string, + schema string, + username string, +) *SecretWithBasicAuthenticationModel { + s := &SecretWithBasicAuthenticationModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithBasicAuthentication)} + s.WithDatabase(database) + s.WithName(name) + s.WithPassword(password) + s.WithSchema(schema) + s.WithUsername(username) + return s +} + +func SecretWithBasicAuthenticationWithDefaultMeta( + database string, + name string, + password string, + schema string, + username string, +) *SecretWithBasicAuthenticationModel { + s := &SecretWithBasicAuthenticationModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithBasicAuthentication)} + s.WithDatabase(database) + s.WithName(name) + s.WithPassword(password) + s.WithSchema(schema) + s.WithUsername(username) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SecretWithBasicAuthenticationModel) WithComment(comment string) *SecretWithBasicAuthenticationModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithDatabase(database string) *SecretWithBasicAuthenticationModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithBasicAuthenticationModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithName(name string) *SecretWithBasicAuthenticationModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithPassword(password string) *SecretWithBasicAuthenticationModel { + s.Password = tfconfig.StringVariable(password) + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithSchema(schema string) *SecretWithBasicAuthenticationModel { + s.Schema = tfconfig.StringVariable(schema) + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithUsername(username string) *SecretWithBasicAuthenticationModel { + s.Username = tfconfig.StringVariable(username) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SecretWithBasicAuthenticationModel) WithCommentValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.Comment = value + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.Database = value + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.FullyQualifiedName = value + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithNameValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.Name = value + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithPasswordValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.Password = value + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithSchemaValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.Schema = value + return s +} + +func (s *SecretWithBasicAuthenticationModel) WithUsernameValue(value tfconfig.Variable) *SecretWithBasicAuthenticationModel { + s.Username = value + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_model_gen.go similarity index 100% rename from pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go rename to pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_model_gen.go diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go similarity index 100% rename from pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go rename to pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index ea0bcd17c4..e60bd81942 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -179,6 +179,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, + resources.SecretWithBasicAuthentication: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.Secrets.ShowByID) + }, resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Databases.ShowByID) }, diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index 4c161ea9b6..5e52d30836 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -2,12 +2,10 @@ package helpers import ( "context" - "testing" - "time" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "testing" ) type SecretClient struct { @@ -22,18 +20,6 @@ func NewSecretClient(context *TestClientContext, idsGenerator *IdsGenerator) *Se } } -func StringDateToSnowflakeTimeFormat(t *testing.T, inputLayout, date string) *time.Time { - t.Helper() - parsedTime, err := time.Parse(inputLayout, date) - require.NoError(t, err) - - loc, err := time.LoadLocation("America/Los_Angeles") - require.NoError(t, err) - - adjustedTime := parsedTime.In(loc) - return &adjustedTime -} - func (c *SecretClient) client() sdk.Secrets { return c.context.client.Secrets } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 920e69286f..891d13d1e2 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -472,6 +472,7 @@ func getResources() map[string]*schema.Resource { "snowflake_scim_integration": resources.SCIMIntegration(), "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), "snowflake_secret_with_authorization_code": resources.SecretWithAuthorizationCode(), + "snowflake_secret_with_basic_authentication": resources.SecretWithBasicAuthentication(), "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 7c72d000c5..3a6f7fd1a5 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -41,6 +41,7 @@ const ( ScimSecurityIntegration resource = "snowflake_scim_integration" SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" SecretWithAuthorizationCode resource = "snowflake_secret_with_authorization_code" + SecretWithBasicAuthentication resource = "snowflake_secret_with_basic_authentication" SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index a2c54f2364..169cf2f625 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -40,6 +40,14 @@ var secretCommonSchema = map[string]*schema.Schema{ Schema: schemas.ShowSecretSchema, }, }, + DescribeOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `DESCRIBE SECRET` for the given secret.", + Elem: &schema.Resource{ + Schema: schemas.DescribeSecretSchema, + }, + }, FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, } @@ -63,7 +71,7 @@ func handleSecretCreate(d *schema.ResourceData) commonSecretCreate { return create } -func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret) error { +func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret, secretDescription *sdk.SecretDetails) error { if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { return err } @@ -82,6 +90,9 @@ func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, sec if err := d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}); err != nil { return err } + if err := d.Set(DescribeOutputAttributeName, []map[string]any{schemas.SecretDescriptionToSchema(*secretDescription)}); err != nil { + return err + } return nil } diff --git a/pkg/resources/secret_with_basic_authentication_flow.go b/pkg/resources/secret_with_basic_authentication_flow.go new file mode 100644 index 0000000000..a56511ace1 --- /dev/null +++ b/pkg/resources/secret_with_basic_authentication_flow.go @@ -0,0 +1,160 @@ +package resources + +import ( + "context" + "errors" + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretBasicAuthenticationSchema = func() map[string]*schema.Schema { + secretAuthorizationCode := map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the username value to store in the secret when setting the TYPE value to PASSWORD.", + }, + "password": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the password value to store in the secret when setting the TYPE value to PASSWORD.", + }, + } + return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) +}() + +func SecretWithBasicAuthentication() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextSecretWithBasicAuthentication, + ReadContext: ReadContextSecretWithBasicAuthentication, + UpdateContext: UpdateContextSecretWithBasicAuthentication, + DeleteContext: DeleteContextSecretWithBasicAuthentication, + + Schema: secretBasicAuthenticationSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func CreateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + commonCreate := handleSecretCreate(d) + + id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) + + usernameString := d.Get("username").(string) + passwordString := d.Get("password").(string) + + request := sdk.NewCreateWithBasicAuthenticationSecretRequest(id, usernameString, passwordString) + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) + } + + err := client.Secrets.CreateWithBasicAuthentication(ctx, request) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeResourceIdentifier(id)) + + return ReadContextSecretWithBasicAuthentication(ctx, d, meta) +} + +func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if secret == nil || err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, + } + } + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } + if err = d.Set("username", secretDescription.Username); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if request := handleSecretUpdate(id, d); request != nil { + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("username") { + username := d.Get("username").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForBasicAuthenticationRequest().WithUsername(username) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForBasicAuthentication(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("password") { + password := d.Get("password").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForBasicAuthenticationRequest().WithPassword(password) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForBasicAuthentication(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextSecretWithBasicAuthentication(ctx, d, meta) +} + +func DeleteContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go new file mode 100644 index 0000000000..cd82f85f3e --- /dev/null +++ b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go @@ -0,0 +1,104 @@ +package resources_test + +import ( + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "testing" +) + +func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + + secretModel := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "foo", id.SchemaName(), "foo") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithBasicAuthentication), + Steps: []resource.TestStep{ + // create + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithBasicAuthenticationResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasUsernameString("foo"). + HasPasswordString("foo"). + HasNoComment(), + //HasCommentString(""), + ), + ), + }, + // set username, password and comment + { + Config: config.FromModel(t, secretModel. + WithUsername("bar"). + WithPassword("bar"). + WithComment(comment), + ), + Check: assert.AssertThat(t, + resourceassert.SecretWithBasicAuthenticationResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasUsernameString("bar"). + HasPasswordString("bar"). + HasCommentString(comment), + ), + }, + // unset comment + { + Config: config.FromModel(t, secretModel.WithComment("")), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). + HasCommentString(""), + ), + }, + // destroy + { + Config: config.FromModel(t, secretModel), + Destroy: true, + }, + /* + // import + { + ResourceName: secretModelWithoutComment.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "api_authentication", integrationId.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_scopes.#", "2"), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), + ), + }, + */ + }, + }) +} diff --git a/pkg/resources/secret_with_authorization_code_grant_flow.go b/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go similarity index 94% rename from pkg/resources/secret_with_authorization_code_grant_flow.go rename to pkg/resources/secret_with_oauth_authorization_code_grant_flow.go index 922dc870fa..32e60449f0 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go @@ -23,8 +23,6 @@ var secretAuthorizationCodeSchema = func() map[string]*schema.Schema { Type: schema.TypeString, Required: true, Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", - // has to be ignored since snowflake changes the date format to different one than is required in the input - //DiffSuppressFunc: IgnoreAfterCreation, }, "api_authentication": { Type: schema.TypeString, @@ -106,15 +104,14 @@ func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Resou }, } } - - if err := handleSecretRead(d, id, secret); err != nil { - return diag.FromErr(err) - } secretDescription, err := client.Secrets.Describe(ctx, id) if err != nil { return diag.FromErr(err) } - if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.In(time.UTC).Format(time.DateOnly)); err != nil { return diag.FromErr(err) } if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { @@ -149,10 +146,10 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res } if d.HasChange("oauth_refresh_token_expiry_time") { - refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(time.Time) + refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime.Format(time.DateOnly)) + setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) if err := client.Secrets.Alter(ctx, request); err != nil { diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go similarity index 65% rename from pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go rename to pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go index 2978889b4d..af03ec3b84 100644 --- a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go @@ -6,9 +6,10 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" tfjson "github.com/hashicorp/terraform-json" @@ -26,6 +27,8 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { newComment := random.Comment() refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) newRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + refreshToken := "test_token" + newRefreshToken := "new_test_token" integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, @@ -33,9 +36,7 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithAuthorizationCode("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryTime).WithComment(comment) - - //expectedTime := helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, newRefreshTokenExpiryTime).String() + secretModel := model.SecretWithAuthorizationCode("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryTime).WithComment(comment) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -43,7 +44,7 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithAuthorizationCode), Steps: []resource.TestStep{ // create { @@ -55,8 +56,8 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). - HasOauthRefreshTokenString("test_token"). - HasOauthRefreshTokenExpiryTimeString(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()). + HasOauthRefreshTokenString(refreshToken). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryTime). HasCommentString(comment), ), ), @@ -65,15 +66,14 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { { Config: config.FromModel(t, secretModel. WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryTime). - WithOauthRefreshToken("new_test_token"). + WithOauthRefreshToken(newRefreshToken). WithComment(newComment), ), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String("test_token"), sdk.String("new_test_token")), - //diff suppressed - //planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()), sdk.String(expectedTime)), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String(refreshToken), sdk.String(newRefreshToken)), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryTime), sdk.String(newRefreshTokenExpiryTime)), }, }, Check: resource.ComposeTestCheckFunc( @@ -83,13 +83,26 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). - HasOauthRefreshTokenString("new_test_token"). - //diff suppressed - //HasOauthRefreshTokenExpiryTimeString(expectedTime). + HasOauthRefreshTokenString(newRefreshToken). + HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryTime). HasCommentString(newComment), ), ), }, + // import + { + ResourceName: secretModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"oauth_refresh_token"}, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_refresh_token_expiry_time", newRefreshTokenExpiryTime), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", newComment), + ), + }, }, }) } diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_oauth_client_credentials_flow.go similarity index 98% rename from pkg/resources/secret_with_client_credentials_flow.go rename to pkg/resources/secret_with_oauth_client_credentials_flow.go index 8e987fa694..4c955a4520 100644 --- a/pkg/resources/secret_with_client_credentials_flow.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow.go @@ -103,15 +103,13 @@ func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.Resou }, } } - - if err := handleSecretRead(d, id, secret); err != nil { - return diag.FromErr(err) - } - secretDescription, err := client.Secrets.Describe(ctx, id) if err != nil { return diag.FromErr(err) } + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go similarity index 100% rename from pkg/resources/secret_with_client_credentials_flow_acceptance_test.go rename to pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go diff --git a/pkg/schemas/secret.go b/pkg/schemas/secret.go new file mode 100644 index 0000000000..9b5158d95c --- /dev/null +++ b/pkg/schemas/secret.go @@ -0,0 +1,84 @@ +package schemas + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// DescribeSecretSchema represents output of DESCRIBE query for the single secret. +var DescribeSecretSchema = map[string]*schema.Schema{ + "created_on": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "database_name": { + Type: schema.TypeString, + Computed: true, + }, + "schema_name": { + Type: schema.TypeString, + Computed: true, + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "comment": { + Type: schema.TypeString, + Computed: true, + }, + "secret_type": { + Type: schema.TypeString, + Computed: true, + }, + "username": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "oauth_access_token_expiry_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "oauth_refresh_token_expiry_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "oauth_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + "integration_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, +} + +func SecretDescriptionToSchema(details sdk.SecretDetails) map[string]any { + s := map[string]any{ + "name": details.Name, + "database_name": details.DatabaseName, + "schema_name": details.SchemaName, + "owner": details.Owner, + "comment": details.Comment, + "secret_type": details.SecretType, + "username": details.Username, + "oauth_scopes": details.OauthScopes, + "integration_name": details.IntegrationName, + } + if details.OauthAccessTokenExpiryTime != nil { + s["oauth_access_token_expiry_time"] = details.OauthAccessTokenExpiryTime.String() + } + if details.OauthRefreshTokenExpiryTime != nil { + s["oauth_refresh_token_expiry_time"] = details.OauthRefreshTokenExpiryTime.String() + } + return s +} From 8de6e8431a9946d092319f1a718ecf0f58af4197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 30 Sep 2024 15:46:00 +0200 Subject: [PATCH 46/65] checking secret with optional oauth_scopes --- ...with_oauth_client_credentials_model_gen.go | 2 - ...cret_with_oauth_client_credentials_flow.go | 6 +-- ...client_credentials_flow_acceptance_test.go | 46 ++++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go index 8601977c78..edff83f8a0 100644 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go @@ -31,13 +31,11 @@ func SecretWithClientCredentials( database string, schema string, name string, - oauthScopes []string, ) *SecretWithClientCredentialsModel { s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithClientCredentials)} s.WithApiAuthentication(apiAuthentication) s.WithDatabase(database) s.WithName(name) - s.WithOauthScopes(oauthScopes) s.WithSchema(schema) return s } diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow.go b/pkg/resources/secret_with_oauth_client_credentials_flow.go index 4c955a4520..1416f366b5 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow.go @@ -22,7 +22,7 @@ var secretClientCredentialsSchema = func() map[string]*schema.Schema { "oauth_scopes": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, - Required: true, + Optional: true, Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", }, } @@ -61,7 +61,7 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} } - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration).WithOauthScopes(sdk.OauthScopesListRequest{oauthScopes}) if commonCreate.comment != nil { request.WithComment(*commonCreate.comment) @@ -134,7 +134,7 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res } request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest(oauthScopes) + setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest().WithOauthScopes(sdk.OauthScopesListRequest{oauthScopes}) request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthClientCredentialsFlow(*setRequest)) if err := client.Secrets.Alter(ctx, request); err != nil { diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go index 4be88ceaa3..070d290d7c 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go @@ -33,8 +33,8 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}).WithComment(comment) - secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}) + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name).WithOauthScopes([]string{"foo", "bar"}).WithComment(comment) + secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name).WithOauthScopes([]string{"foo", "bar"}) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -140,8 +140,8 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, nil) - secretModelWithScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo"}) + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name) + //secretModelWithScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name).WithOauthScopes([]string{"foo"}) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -156,7 +156,7 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { Config: config.FromModel(t, secretModel), Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). + resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -167,25 +167,27 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { ), }, // Set oauth_scopes - { - Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{"foo"}), - ), - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), + /* + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{"foo"}), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), + }, }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()), + assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo")), + ), }, - Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()), - assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), - ), - }, + */ }, }) } From c64a1ed28494d8053357ab0c2ade3df057ff5600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 1 Oct 2024 15:23:38 +0200 Subject: [PATCH 47/65] secret basic authentication flow acceptance tests and fix for parsing oauth_scopes to use helper function --- ...et_with_client_credentials_resource_ext.go | 13 ----- ...with_oauth_client_credentials_model_gen.go | 4 +- pkg/acceptance/helpers/secret_client.go | 7 +++ ...sic_authentication_flow_acceptance_test.go | 54 ++++++++++++------- ...cret_with_oauth_client_credentials_flow.go | 4 +- ...client_credentials_flow_acceptance_test.go | 50 ++++++++--------- pkg/sdk/secrets_impl_gen.go | 16 +----- .../testint/secrets_gen_integration_test.go | 2 +- 8 files changed, 76 insertions(+), 74 deletions(-) delete mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go deleted file mode 100644 index 543d330d5e..0000000000 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go +++ /dev/null @@ -1,13 +0,0 @@ -package resourceassert - -import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" -) - -func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopes(expected []sdk.ApiIntegrationScope) *SecretWithClientCredentialsResourceAssert { - for _, v := range expected { - s.AddAssertion(assert.ValueSet("oauth_scopes.*", v.Scope)) - } - return s -} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go index edff83f8a0..54d25ff47a 100644 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go @@ -30,12 +30,14 @@ func SecretWithClientCredentials( apiAuthentication string, database string, schema string, + oauthScopes []string, name string, ) *SecretWithClientCredentialsModel { s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithClientCredentials)} s.WithApiAuthentication(apiAuthentication) s.WithDatabase(database) s.WithName(name) + s.WithOauthScopes(oauthScopes) s.WithSchema(schema) return s } @@ -130,7 +132,7 @@ func (s *SecretWithClientCredentialsModel) WithSchemaValue(value tfconfig.Variab s.Schema = value return s } -func (s*SecretWithClientCredentialsModel) WithOauthScopes(oauthScopes []string) *SecretWithClientCredentialsModel { +func (s *SecretWithClientCredentialsModel) WithOauthScopes(oauthScopes []string) *SecretWithClientCredentialsModel { oauthScopesStringVariables := make([]tfconfig.Variable, len(oauthScopes)) for i, v := range oauthScopes { oauthScopesStringVariables[i] = tfconfig.StringVariable(v) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index 5e52d30836..51652c3c10 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -81,6 +81,13 @@ func (c *SecretClient) CreateWithGenericString(t *testing.T, id sdk.SchemaObject return secret, c.DropFunc(t, id) } +func (c *SecretClient) Alter(t *testing.T, req *sdk.AlterSecretRequest) { + t.Helper() + ctx := context.Background() + err := c.client().Alter(ctx, req) + require.NoError(t, err) +} + func (c *SecretClient) DropFunc(t *testing.T, id sdk.SchemaObjectIdentifier) func() { t.Helper() ctx := context.Background() diff --git a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go index cd82f85f3e..2424914c9e 100644 --- a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go @@ -7,7 +7,9 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" tfjson "github.com/hashicorp/terraform-json" @@ -23,6 +25,8 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { comment := random.Comment() secretModel := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "foo", id.SchemaName(), "foo") + secretModelWithoutComment := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "bar", id.SchemaName(), "bar") + secretModelEmpty := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "", id.SchemaName(), "") resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -43,8 +47,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasSchemaString(id.SchemaName()). HasUsernameString("foo"). HasPasswordString("foo"). - HasNoComment(), - //HasCommentString(""), + HasCommentString(""), ), ), }, @@ -65,16 +68,30 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasCommentString(comment), ), }, + // import + { + ResourceName: secretModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "username", "bar"), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", comment), + ), + }, // unset comment { - Config: config.FromModel(t, secretModel.WithComment("")), + Config: config.FromModel(t, secretModelWithoutComment), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), }, }, Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). HasCommentString(""), ), }, @@ -83,22 +100,21 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModel), Destroy: true, }, - /* - // import - { - ResourceName: secretModelWithoutComment.ResourceReference(), - ImportState: true, - ImportStateVerify: true, - ImportStateCheck: importchecks.ComposeImportStateCheck( - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "api_authentication", integrationId.Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_scopes.#", "2"), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), + // create with empty username and password + { + Config: config.FromModel(t, secretModelEmpty), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithBasicAuthenticationResource(t, secretModelEmpty.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasUsernameString(""). + HasPasswordString(""). + HasCommentString(""), ), - }, - */ + ), + }, }, }) } diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow.go b/pkg/resources/secret_with_oauth_client_credentials_flow.go index 1416f366b5..98327344ee 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow.go @@ -22,7 +22,7 @@ var secretClientCredentialsSchema = func() map[string]*schema.Schema { "oauth_scopes": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, + Required: true, Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", }, } @@ -61,7 +61,7 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} } - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration).WithOauthScopes(sdk.OauthScopesListRequest{oauthScopes}) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration).WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) if commonCreate.comment != nil { request.WithComment(*commonCreate.comment) diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go index 070d290d7c..fc80d87489 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go @@ -33,8 +33,8 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name).WithOauthScopes([]string{"foo", "bar"}).WithComment(comment) - secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name).WithOauthScopes([]string{"foo", "bar"}) + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), []string{"foo", "bar"}, name).WithComment(comment) + secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), []string{"foo", "bar"}, name) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -125,11 +125,16 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), ), }, + // destroy + { + Config: config.FromModel(t, secretModel), + Destroy: true, + }, }, }) } -func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { +func TestAcc_SecretWithClientCredentials_EmptyScopesList(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() name := id.Name() @@ -140,8 +145,7 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name) - //secretModelWithScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name).WithOauthScopes([]string{"foo"}) + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), []string{}, name) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -167,27 +171,25 @@ func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { ), }, // Set oauth_scopes - /* - { - Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{"foo"}), - ), - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), - }, + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{"foo"}), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), }, - Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()), - assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo")), - ), }, - */ + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()), + assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo")), + ), + }, }, }) } diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index f6804181f0..b184550976 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -2,8 +2,6 @@ package sdk import ( "context" - "strings" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" ) @@ -195,16 +193,6 @@ func (r *ShowSecretRequest) toOpts() *ShowSecretOptions { return opts } -func getOauthScopes(scopesString string) []string { - formatedScopes := make([]string, 0) - scopesString = strings.TrimPrefix(scopesString, "[") - scopesString = strings.TrimSuffix(scopesString, "]") - for _, scope := range strings.Split(scopesString, ",") { - formatedScopes = append(formatedScopes, strings.TrimSpace(scope)) - } - return formatedScopes -} - func (r secretDBRow) convert() *Secret { s := &Secret{ CreatedOn: r.CreatedOn, @@ -219,7 +207,7 @@ func (r secretDBRow) convert() *Secret { s.Comment = String(r.Comment.String) } if r.OauthScopes.Valid { - s.OauthScopes = getOauthScopes(r.OauthScopes.String) + s.OauthScopes = ParseCommaSeparatedStringArray(r.OauthScopes.String, false) } return s } @@ -249,7 +237,7 @@ func (r secretDetailsDBRow) convert() *SecretDetails { s.Comment = String(r.Comment.String) } if r.OauthScopes.Valid { - s.OauthScopes = getOauthScopes(r.OauthScopes.String) + s.OauthScopes = ParseCommaSeparatedStringArray(r.OauthScopes.String, false) } if r.IntegrationName.Valid { s.IntegrationName = String(r.IntegrationName.String) diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index 5e845368db..a7b032af42 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -146,7 +146,7 @@ func TestInt_Secrets(t *testing.T) { assert.NotContains(t, details.OauthScopes, "foo") assert.NotContains(t, details.OauthScopes, "bar") - assert.Equal(t, []string{""}, details.OauthScopes) + assert.Equal(t, []string{}, details.OauthScopes) }) t.Run("Create: SecretWithOAuthAuthorizationCodeFlow - refreshTokenExpiry date format", func(t *testing.T) { From fde6908c6bf541b103e641fdebe72f02edae7aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 1 Oct 2024 15:29:49 +0200 Subject: [PATCH 48/65] removed unwanted duplicats after merge --- ...ization_code_grant_flow_acceptance_test.go | 95 --------- ...client_credentials_flow_acceptance_test.go | 191 ------------------ ...client_credentials_flow_acceptance_test.go | 6 +- 3 files changed, 3 insertions(+), 289 deletions(-) delete mode 100644 pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go delete mode 100644 pkg/resources/secret_with_client_credentials_flow_acceptance_test.go diff --git a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go deleted file mode 100644 index 2978889b4d..0000000000 --- a/pkg/resources/secret_with_authorization_code_grant_flow_acceptance_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package resources_test - -import ( - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - tfjson "github.com/hashicorp/terraform-json" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/plancheck" - "github.com/hashicorp/terraform-plugin-testing/tfversion" - "testing" - "time" -) - -func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - comment := random.Comment() - newComment := random.Comment() - refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) - newRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) - - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), - ) - t.Cleanup(apiIntegrationCleanup) - - secretModel := model.SecretWithAuthorizationCode("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryTime).WithComment(comment) - - //expectedTime := helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, newRefreshTokenExpiryTime).String() - - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), - Steps: []resource.TestStep{ - // create - { - Config: config.FromModel(t, secretModel), - Check: resource.ComposeTestCheckFunc( - assert.AssertThat(t, - resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasOauthRefreshTokenString("test_token"). - HasOauthRefreshTokenExpiryTimeString(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()). - HasCommentString(comment), - ), - ), - }, - // set all - { - Config: config.FromModel(t, secretModel. - WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryTime). - WithOauthRefreshToken("new_test_token"). - WithComment(newComment), - ), - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String("test_token"), sdk.String("new_test_token")), - //diff suppressed - //planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(helpers.StringDateToSnowflakeTimeFormat(t, time.DateOnly, refreshTokenExpiryTime).String()), sdk.String(expectedTime)), - }, - }, - Check: resource.ComposeTestCheckFunc( - assert.AssertThat(t, - resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasOauthRefreshTokenString("new_test_token"). - //diff suppressed - //HasOauthRefreshTokenExpiryTimeString(expectedTime). - HasCommentString(newComment), - ), - ), - }, - }, - }) -} diff --git a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go deleted file mode 100644 index 4be88ceaa3..0000000000 --- a/pkg/resources/secret_with_client_credentials_flow_acceptance_test.go +++ /dev/null @@ -1,191 +0,0 @@ -package resources_test - -import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - tfjson "github.com/hashicorp/terraform-json" - "github.com/hashicorp/terraform-plugin-testing/plancheck" - "testing" - - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/tfversion" -) - -func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - comment := "aaa" - newComment := random.Comment() - - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). - WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), - ) - t.Cleanup(apiIntegrationCleanup) - - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}).WithComment(comment) - secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}) - - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), - Steps: []resource.TestStep{ - { - Config: config.FromModel(t, secretModel), - Check: resource.ComposeTestCheckFunc( - assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasCommentString(comment), - ), - resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), - ), - }, - // set oauth_scopes and comment in config - { - Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{"foo"}). - WithComment(newComment)), - Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), - ), - }, - // unset comment - { - Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(newComment), nil), - }, - }, - Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). - HasCommentString(""), - ), - }, - // destroy - { - Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), - Destroy: true, - }, - // create without comment - { - Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo", "bar"})), - Check: resource.ComposeTestCheckFunc( - assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasCommentString(""), - ), - resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), - ), - }, - // import - { - ResourceName: secretModelWithoutComment.ResourceReference(), - ImportState: true, - ImportStateVerify: true, - ImportStateCheck: importchecks.ComposeImportStateCheck( - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "api_authentication", integrationId.Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_scopes.#", "2"), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), - ), - }, - }, - }) -} - -func TestAcc_SecretWithClientCredentials_NoScopesProvided(t *testing.T) { - id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() - name := id.Name() - - integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). - WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), - ) - t.Cleanup(apiIntegrationCleanup) - - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, nil) - secretModelWithScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo"}) - - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - CheckDestroy: acc.CheckDestroy(t, resources.SecretWithClientCredentials), - Steps: []resource.TestStep{ - // create secret without providing oauth_scopes value - { - Config: config.FromModel(t, secretModel), - Check: resource.ComposeTestCheckFunc( - assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasCommentString(""), - ), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "0"), - ), - }, - // Set oauth_scopes - { - Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{"foo"}), - ), - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), - }, - }, - Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithScopes.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()), - assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), - ), - }, - }, - }) -} diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go index fc80d87489..2be356fbbf 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go @@ -33,8 +33,8 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), []string{"foo", "bar"}, name).WithComment(comment) - secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), []string{"foo", "bar"}, name) + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}).WithComment(comment) + secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -145,7 +145,7 @@ func TestAcc_SecretWithClientCredentials_EmptyScopesList(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), []string{}, name) + secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{}) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, From a147b999109148bb458d4512e8dadc84598585ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 2 Oct 2024 15:09:00 +0200 Subject: [PATCH 49/65] All secret flows with acceptance tests and change to handle different dateTime input formats for refresh_token_expiry_time --- ..._authorization_code_grant_resource_ext.go} | 2 +- ..._authorization_code_grant_resource_gen.go} | 0 ...ecret_with_client_credentials_model_gen.go | 143 -------------- ...uth_authorization_code_grant_model_gen.go} | 46 ++--- ...with_oauth_authorization_code_model_gen.go | 152 --------------- ...with_oauth_client_credentials_model_gen.go | 2 +- pkg/acceptance/check_destroy.go | 7 +- pkg/acceptance/helpers/secret_client.go | 2 +- pkg/provider/provider.go | 3 +- pkg/provider/resources/resources.go | 3 +- pkg/resources/secret_common.go | 2 +- ...cret_with_authorization_code_grant_flow.go | 179 ------------------ .../secret_with_basic_authentication_flow.go | 2 + ...sic_authentication_flow_acceptance_test.go | 6 +- .../secret_with_client_credentials_flow.go | 169 ----------------- ...ith_oauth_authorization_code_grant_flow.go | 97 +++++++--- ...ization_code_grant_flow_acceptance_test.go | 112 +++++++++-- ...cret_with_oauth_client_credentials_flow.go | 15 +- ...client_credentials_flow_acceptance_test.go | 8 +- 19 files changed, 220 insertions(+), 730 deletions(-) rename pkg/acceptance/bettertestspoc/assert/resourceassert/{secret_with_authorization_code_resource_ext.go => secret_with_authorization_code_grant_resource_ext.go} (79%) rename pkg/acceptance/bettertestspoc/assert/resourceassert/{secret_with_authorization_code_resource_gen.go => secret_with_authorization_code_grant_resource_gen.go} (100%) delete mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go rename pkg/acceptance/bettertestspoc/config/model/{secret_with_authorization_code_model_gen.go => secret_with_oauth_authorization_code_grant_model_gen.go} (53%) delete mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_model_gen.go delete mode 100644 pkg/resources/secret_with_authorization_code_grant_flow.go delete mode 100644 pkg/resources/secret_with_client_credentials_flow.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_grant_resource_ext.go similarity index 79% rename from pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go rename to pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_grant_resource_ext.go index 73989173af..0044880efd 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_grant_resource_ext.go @@ -4,7 +4,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" ) -func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenNoEmpty() *SecretWithAuthorizationCodeResourceAssert { +func (s *SecretWithAuthorizationCodeResourceAssert) HasOauthRefreshTokenExpiryTimeNotEmpty() *SecretWithAuthorizationCodeResourceAssert { s.AddAssertion(assert.ValuePresent("oauth_refresh_token_expiry_time")) return s } diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_grant_resource_gen.go similarity index 100% rename from pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_resource_gen.go rename to pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_authorization_code_grant_resource_gen.go diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go deleted file mode 100644 index 8601977c78..0000000000 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_client_credentials_model_gen.go +++ /dev/null @@ -1,143 +0,0 @@ -// Code generated by config model builder generator; DO NOT EDIT. - -package model - -import ( - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" -) - -type SecretWithClientCredentialsModel struct { - ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` - Comment tfconfig.Variable `json:"comment,omitempty"` - Database tfconfig.Variable `json:"database,omitempty"` - FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` - Name tfconfig.Variable `json:"name,omitempty"` - OauthScopes tfconfig.Variable `json:"oauth_scopes,omitempty"` - Schema tfconfig.Variable `json:"schema,omitempty"` - - *config.ResourceModelMeta -} - -///////////////////////////////////////////////// -// Basic builders (resource name and required) // -///////////////////////////////////////////////// - -func SecretWithClientCredentials( - resourceName string, - apiAuthentication string, - database string, - schema string, - name string, - oauthScopes []string, -) *SecretWithClientCredentialsModel { - s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithClientCredentials)} - s.WithApiAuthentication(apiAuthentication) - s.WithDatabase(database) - s.WithName(name) - s.WithOauthScopes(oauthScopes) - s.WithSchema(schema) - return s -} - -func SecretWithClientCredentialsWithDefaultMeta( - apiAuthentication string, - database string, - name string, - oauthScopes []string, - schema string, -) *SecretWithClientCredentialsModel { - s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithClientCredentials)} - s.WithApiAuthentication(apiAuthentication) - s.WithDatabase(database) - s.WithName(name) - s.WithOauthScopes(oauthScopes) - s.WithSchema(schema) - return s -} - -///////////////////////////////// -// below all the proper values // -///////////////////////////////// - -func (s *SecretWithClientCredentialsModel) WithApiAuthentication(apiAuthentication string) *SecretWithClientCredentialsModel { - s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) - return s -} - -func (s *SecretWithClientCredentialsModel) WithComment(comment string) *SecretWithClientCredentialsModel { - s.Comment = tfconfig.StringVariable(comment) - return s -} - -func (s *SecretWithClientCredentialsModel) WithDatabase(database string) *SecretWithClientCredentialsModel { - s.Database = tfconfig.StringVariable(database) - return s -} - -func (s *SecretWithClientCredentialsModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithClientCredentialsModel { - s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) - return s -} - -func (s *SecretWithClientCredentialsModel) WithName(name string) *SecretWithClientCredentialsModel { - s.Name = tfconfig.StringVariable(name) - return s -} - -// oauth_scopes attribute type is not yet supported, so WithOauthScopes can't be generated - -func (s *SecretWithClientCredentialsModel) WithSchema(schema string) *SecretWithClientCredentialsModel { - s.Schema = tfconfig.StringVariable(schema) - return s -} - -////////////////////////////////////////// -// below it's possible to set any value // -////////////////////////////////////////// - -func (s *SecretWithClientCredentialsModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.ApiAuthentication = value - return s -} - -func (s *SecretWithClientCredentialsModel) WithCommentValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.Comment = value - return s -} - -func (s *SecretWithClientCredentialsModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.Database = value - return s -} - -func (s *SecretWithClientCredentialsModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.FullyQualifiedName = value - return s -} - -func (s *SecretWithClientCredentialsModel) WithNameValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.Name = value - return s -} - -func (s *SecretWithClientCredentialsModel) WithOauthScopesValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.OauthScopes = value - return s -} - -func (s *SecretWithClientCredentialsModel) WithSchemaValue(value tfconfig.Variable) *SecretWithClientCredentialsModel { - s.Schema = value - return s -} -func (s*SecretWithClientCredentialsModel) WithOauthScopes(oauthScopes []string) *SecretWithClientCredentialsModel { - oauthScopesStringVariables := make([]tfconfig.Variable, len(oauthScopes)) - for i, v := range oauthScopes { - oauthScopesStringVariables[i] = tfconfig.StringVariable(v) - } - - s.OauthScopes = tfconfig.SetVariable(oauthScopesStringVariables...) - return s -} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_grant_model_gen.go similarity index 53% rename from pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go rename to pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_grant_model_gen.go index 65516f427d..212740cc5d 100644 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_authorization_code_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_grant_model_gen.go @@ -9,7 +9,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" ) -type SecretWithAuthorizationCodeModel struct { +type SecretWithAuthorizationCodeGrantModel struct { ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` Comment tfconfig.Variable `json:"comment,omitempty"` Database tfconfig.Variable `json:"database,omitempty"` @@ -26,7 +26,7 @@ type SecretWithAuthorizationCodeModel struct { // Basic builders (resource name and required) // ///////////////////////////////////////////////// -func SecretWithAuthorizationCode( +func SecretWithAuthorizationCodeGrant( resourceName string, apiAuthentication string, database string, @@ -34,8 +34,8 @@ func SecretWithAuthorizationCode( name string, oauthRefreshToken string, oauthRefreshTokenExpiryTime string, -) *SecretWithAuthorizationCodeModel { - s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCode)} +) *SecretWithAuthorizationCodeGrantModel { + s := &SecretWithAuthorizationCodeGrantModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCodeGrant)} s.WithApiAuthentication(apiAuthentication) s.WithDatabase(database) s.WithName(name) @@ -45,15 +45,15 @@ func SecretWithAuthorizationCode( return s } -func SecretWithAuthorizationCodeWithDefaultMeta( +func SecretWithAuthorizationCodeGrantWithDefaultMeta( apiAuthentication string, database string, name string, oauthRefreshToken string, oauthRefreshTokenExpiryTime string, schema string, -) *SecretWithAuthorizationCodeModel { - s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithAuthorizationCode)} +) *SecretWithAuthorizationCodeGrantModel { + s := &SecretWithAuthorizationCodeGrantModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithAuthorizationCodeGrant)} s.WithApiAuthentication(apiAuthentication) s.WithDatabase(database) s.WithName(name) @@ -67,42 +67,42 @@ func SecretWithAuthorizationCodeWithDefaultMeta( // below all the proper values // ///////////////////////////////// -func (s *SecretWithAuthorizationCodeModel) WithApiAuthentication(apiAuthentication string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithApiAuthentication(apiAuthentication string) *SecretWithAuthorizationCodeGrantModel { s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) return s } -func (s *SecretWithAuthorizationCodeModel) WithComment(comment string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithComment(comment string) *SecretWithAuthorizationCodeGrantModel { s.Comment = tfconfig.StringVariable(comment) return s } -func (s *SecretWithAuthorizationCodeModel) WithDatabase(database string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithDatabase(database string) *SecretWithAuthorizationCodeGrantModel { s.Database = tfconfig.StringVariable(database) return s } -func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithAuthorizationCodeGrantModel { s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) return s } -func (s *SecretWithAuthorizationCodeModel) WithName(name string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithName(name string) *SecretWithAuthorizationCodeGrantModel { s.Name = tfconfig.StringVariable(name) return s } -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshToken(oauthRefreshToken string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithOauthRefreshToken(oauthRefreshToken string) *SecretWithAuthorizationCodeGrantModel { s.OauthRefreshToken = tfconfig.StringVariable(oauthRefreshToken) return s } -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime string) *SecretWithAuthorizationCodeGrantModel { s.OauthRefreshTokenExpiryTime = tfconfig.StringVariable(oauthRefreshTokenExpiryTime) return s } -func (s *SecretWithAuthorizationCodeModel) WithSchema(schema string) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithSchema(schema string) *SecretWithAuthorizationCodeGrantModel { s.Schema = tfconfig.StringVariable(schema) return s } @@ -111,42 +111,42 @@ func (s *SecretWithAuthorizationCodeModel) WithSchema(schema string) *SecretWith // below it's possible to set any value // ////////////////////////////////////////// -func (s *SecretWithAuthorizationCodeModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.ApiAuthentication = value return s } -func (s *SecretWithAuthorizationCodeModel) WithCommentValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithCommentValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.Comment = value return s } -func (s *SecretWithAuthorizationCodeModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.Database = value return s } -func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.FullyQualifiedName = value return s } -func (s *SecretWithAuthorizationCodeModel) WithNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.Name = value return s } -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithOauthRefreshTokenValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.OauthRefreshToken = value return s } -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTimeValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithOauthRefreshTokenExpiryTimeValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.OauthRefreshTokenExpiryTime = value return s } -func (s *SecretWithAuthorizationCodeModel) WithSchemaValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { +func (s *SecretWithAuthorizationCodeGrantModel) WithSchemaValue(value tfconfig.Variable) *SecretWithAuthorizationCodeGrantModel { s.Schema = value return s } diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_model_gen.go deleted file mode 100644 index 65516f427d..0000000000 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_authorization_code_model_gen.go +++ /dev/null @@ -1,152 +0,0 @@ -// Code generated by config model builder generator; DO NOT EDIT. - -package model - -import ( - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" -) - -type SecretWithAuthorizationCodeModel struct { - ApiAuthentication tfconfig.Variable `json:"api_authentication,omitempty"` - Comment tfconfig.Variable `json:"comment,omitempty"` - Database tfconfig.Variable `json:"database,omitempty"` - FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` - Name tfconfig.Variable `json:"name,omitempty"` - OauthRefreshToken tfconfig.Variable `json:"oauth_refresh_token,omitempty"` - OauthRefreshTokenExpiryTime tfconfig.Variable `json:"oauth_refresh_token_expiry_time,omitempty"` - Schema tfconfig.Variable `json:"schema,omitempty"` - - *config.ResourceModelMeta -} - -///////////////////////////////////////////////// -// Basic builders (resource name and required) // -///////////////////////////////////////////////// - -func SecretWithAuthorizationCode( - resourceName string, - apiAuthentication string, - database string, - schema string, - name string, - oauthRefreshToken string, - oauthRefreshTokenExpiryTime string, -) *SecretWithAuthorizationCodeModel { - s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithAuthorizationCode)} - s.WithApiAuthentication(apiAuthentication) - s.WithDatabase(database) - s.WithName(name) - s.WithOauthRefreshToken(oauthRefreshToken) - s.WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime) - s.WithSchema(schema) - return s -} - -func SecretWithAuthorizationCodeWithDefaultMeta( - apiAuthentication string, - database string, - name string, - oauthRefreshToken string, - oauthRefreshTokenExpiryTime string, - schema string, -) *SecretWithAuthorizationCodeModel { - s := &SecretWithAuthorizationCodeModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithAuthorizationCode)} - s.WithApiAuthentication(apiAuthentication) - s.WithDatabase(database) - s.WithName(name) - s.WithOauthRefreshToken(oauthRefreshToken) - s.WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime) - s.WithSchema(schema) - return s -} - -///////////////////////////////// -// below all the proper values // -///////////////////////////////// - -func (s *SecretWithAuthorizationCodeModel) WithApiAuthentication(apiAuthentication string) *SecretWithAuthorizationCodeModel { - s.ApiAuthentication = tfconfig.StringVariable(apiAuthentication) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithComment(comment string) *SecretWithAuthorizationCodeModel { - s.Comment = tfconfig.StringVariable(comment) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithDatabase(database string) *SecretWithAuthorizationCodeModel { - s.Database = tfconfig.StringVariable(database) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithAuthorizationCodeModel { - s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithName(name string) *SecretWithAuthorizationCodeModel { - s.Name = tfconfig.StringVariable(name) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshToken(oauthRefreshToken string) *SecretWithAuthorizationCodeModel { - s.OauthRefreshToken = tfconfig.StringVariable(oauthRefreshToken) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTime(oauthRefreshTokenExpiryTime string) *SecretWithAuthorizationCodeModel { - s.OauthRefreshTokenExpiryTime = tfconfig.StringVariable(oauthRefreshTokenExpiryTime) - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithSchema(schema string) *SecretWithAuthorizationCodeModel { - s.Schema = tfconfig.StringVariable(schema) - return s -} - -////////////////////////////////////////// -// below it's possible to set any value // -////////////////////////////////////////// - -func (s *SecretWithAuthorizationCodeModel) WithApiAuthenticationValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.ApiAuthentication = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithCommentValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.Comment = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.Database = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.FullyQualifiedName = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithNameValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.Name = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.OauthRefreshToken = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithOauthRefreshTokenExpiryTimeValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.OauthRefreshTokenExpiryTime = value - return s -} - -func (s *SecretWithAuthorizationCodeModel) WithSchemaValue(value tfconfig.Variable) *SecretWithAuthorizationCodeModel { - s.Schema = value - return s -} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go index 54d25ff47a..9e093b48d0 100644 --- a/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_oauth_client_credentials_model_gen.go @@ -30,8 +30,8 @@ func SecretWithClientCredentials( apiAuthentication string, database string, schema string, - oauthScopes []string, name string, + oauthScopes []string, ) *SecretWithClientCredentialsModel { s := &SecretWithClientCredentialsModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithClientCredentials)} s.WithApiAuthentication(apiAuthentication) diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index adb17e6275..087c814dc6 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -173,7 +173,7 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ScimSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, - resources.SecretWithAuthorizationCode: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + resources.SecretWithAuthorizationCodeGrant: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { @@ -182,10 +182,7 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.SecretWithBasicAuthentication: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, - resources.SecretWithAuthorizationCode: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { - return runShowById(ctx, id, client.Secrets.ShowByID) - }, - resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + resources.SecretWithGenericString: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index 51652c3c10..f0d1594f30 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -70,7 +70,7 @@ func (c *SecretClient) CreateWithBasicAuthenticationFlow(t *testing.T, id sdk.Sc func (c *SecretClient) CreateWithGenericString(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) (*sdk.Secret, func()) { t.Helper() ctx := context.Background() - request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) + request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString).WithOrReplace(true) err := c.client().CreateWithGenericString(ctx, request) require.NoError(t, err) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 891d13d1e2..efc8632b0b 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -471,8 +471,9 @@ func getResources() map[string]*schema.Resource { "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), - "snowflake_secret_with_authorization_code": resources.SecretWithAuthorizationCode(), + "snowflake_secret_with_authorization_code_grant": resources.SecretWithAuthorizationCodeGrant(), "snowflake_secret_with_basic_authentication": resources.SecretWithBasicAuthentication(), + "snowflake_secret_with_generic_string": resources.SecretWithGenericString(), "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 3a6f7fd1a5..c494c5eeb0 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -40,8 +40,9 @@ const ( Schema resource = "snowflake_schema" ScimSecurityIntegration resource = "snowflake_scim_integration" SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" - SecretWithAuthorizationCode resource = "snowflake_secret_with_authorization_code" + SecretWithAuthorizationCodeGrant resource = "snowflake_secret_with_authorization_code_grant" SecretWithBasicAuthentication resource = "snowflake_secret_with_basic_authentication" + SecretWithGenericString resource = "snowflake_secret_with_generic_string" SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index 169cf2f625..fe526ce1e8 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -101,7 +101,7 @@ func handleSecretUpdate(id sdk.SchemaObjectIdentifier, d *schema.ResourceData) * comment := d.Get("comment").(string) request := sdk.NewAlterSecretRequest(id) if len(comment) == 0 { - unsetRequest := sdk.NewSecretUnsetRequest().WithComment(*sdk.Bool(true)) + unsetRequest := sdk.NewSecretUnsetRequest().WithComment(true) return request.WithUnset(*unsetRequest) } else { setRequest := sdk.NewSecretSetRequest().WithComment(comment) diff --git a/pkg/resources/secret_with_authorization_code_grant_flow.go b/pkg/resources/secret_with_authorization_code_grant_flow.go deleted file mode 100644 index 922dc870fa..0000000000 --- a/pkg/resources/secret_with_authorization_code_grant_flow.go +++ /dev/null @@ -1,179 +0,0 @@ -package resources - -import ( - "context" - "errors" - "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "time" -) - -var secretAuthorizationCodeSchema = func() map[string]*schema.Schema { - secretAuthorizationCode := map[string]*schema.Schema{ - "oauth_refresh_token": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", - }, - "oauth_refresh_token_expiry_time": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", - // has to be ignored since snowflake changes the date format to different one than is required in the input - //DiffSuppressFunc: IgnoreAfterCreation, - }, - "api_authentication": { - Type: schema.TypeString, - ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", - }, - } - return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) -}() - -func SecretWithAuthorizationCode() *schema.Resource { - return &schema.Resource{ - CreateContext: CreateContextSecretWithAuthorizationCode, - ReadContext: ReadContextSecretWithAuthorizationCode, - UpdateContext: UpdateContextSecretWithAuthorizationCode, - DeleteContext: DeleteContextSecretWithAuthorizationCode, - - Schema: secretAuthorizationCodeSchema, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - } -} - -func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - commonCreate := handleSecretCreate(d) - - id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) - - apiIntegrationString := d.Get("api_authentication").(string) - apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) - if err != nil { - return diag.FromErr(err) - } - - refreshToken := d.Get("oauth_refresh_token").(string) - refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) - - request := sdk.NewCreateWithOAuthAuthorizationCodeFlowSecretRequest(id, refreshToken, refreshTokenExpiryTime, apiIntegration) - if v, ok := d.GetOk("comment"); ok { - request.WithComment(v.(string)) - } - - err = client.Secrets.CreateWithOAuthAuthorizationCodeFlow(ctx, request) - if err != nil { - return diag.FromErr(err) - } - - d.SetId(helpers.EncodeResourceIdentifier(id)) - - return ReadContextSecretWithAuthorizationCode(ctx, d, meta) -} - -func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - secret, err := client.Secrets.ShowByID(ctx, id) - if secret == nil || err != nil { - if errors.Is(err, sdk.ErrObjectNotFound) { - d.SetId("") - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", - }, - } - } - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), - }, - } - } - - if err := handleSecretRead(d, id, secret); err != nil { - return diag.FromErr(err) - } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { - return diag.FromErr(err) - } - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { - return diag.FromErr(err) - } - return nil -} - -func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - - if d.HasChange("oauth_refresh_token") { - refreshToken := d.Get("oauth_refresh_token").(string) - - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshToken(refreshToken) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) - - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - - if d.HasChange("oauth_refresh_token_expiry_time") { - refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(time.Time) - - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime.Format(time.DateOnly)) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) - - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - - return ReadContextSecretWithAuthorizationCode(ctx, d, meta) -} - -func DeleteContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil -} diff --git a/pkg/resources/secret_with_basic_authentication_flow.go b/pkg/resources/secret_with_basic_authentication_flow.go index a56511ace1..52cedbf673 100644 --- a/pkg/resources/secret_with_basic_authentication_flow.go +++ b/pkg/resources/secret_with_basic_authentication_flow.go @@ -38,6 +38,8 @@ func SecretWithBasicAuthentication() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + + Description: "Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.'", } } diff --git a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go index 2424914c9e..cde468dc68 100644 --- a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go @@ -26,7 +26,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { secretModel := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "foo", id.SchemaName(), "foo") secretModelWithoutComment := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "bar", id.SchemaName(), "bar") - secretModelEmpty := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "", id.SchemaName(), "") + secretModelEmptyCredentials := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "", id.SchemaName(), "") resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -102,10 +102,10 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { }, // create with empty username and password { - Config: config.FromModel(t, secretModelEmpty), + Config: config.FromModel(t, secretModelEmptyCredentials), Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithBasicAuthenticationResource(t, secretModelEmpty.ResourceReference()). + resourceassert.SecretWithBasicAuthenticationResource(t, secretModelEmptyCredentials.ResourceReference()). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). diff --git a/pkg/resources/secret_with_client_credentials_flow.go b/pkg/resources/secret_with_client_credentials_flow.go deleted file mode 100644 index 8e987fa694..0000000000 --- a/pkg/resources/secret_with_client_credentials_flow.go +++ /dev/null @@ -1,169 +0,0 @@ -package resources - -import ( - "context" - "errors" - "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -var secretClientCredentialsSchema = func() map[string]*schema.Schema { - secretClientCredentials := map[string]*schema.Schema{ - "api_authentication": { - Type: schema.TypeString, - ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", - }, - "oauth_scopes": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Required: true, - Description: "Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow.", - }, - } - return helpers.MergeMaps(secretCommonSchema, secretClientCredentials) -}() - -func SecretWithClientCredentials() *schema.Resource { - return &schema.Resource{ - CreateContext: CreateContextSecretWithClientCredentials, - ReadContext: ReadContextSecretWithClientCredentials, - UpdateContext: UpdateContextSecretWithClientCredentials, - DeleteContext: DeleteContextSecretWithClientCredentials, - - Schema: secretClientCredentialsSchema, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - } -} - -func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - commonCreate := handleSecretCreate(d) - - apiIntegrationString := d.Get("api_authentication").(string) - apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) - if err != nil { - return diag.FromErr(err) - } - - id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) - - stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) - oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) - for i, scope := range stringScopes { - oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} - } - - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration, oauthScopes) - - if commonCreate.comment != nil { - request.WithComment(*commonCreate.comment) - } - - err = client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) - if err != nil { - return diag.FromErr(err) - } - - d.SetId(helpers.EncodeResourceIdentifier(id)) - - return ReadContextSecretWithClientCredentials(ctx, d, meta) -} - -func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - secret, err := client.Secrets.ShowByID(ctx, id) - if secret == nil || err != nil { - if errors.Is(err, sdk.ErrObjectNotFound) { - d.SetId("") - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", - }, - } - } - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), - }, - } - } - - if err := handleSecretRead(d, id, secret); err != nil { - return diag.FromErr(err) - } - - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { - return diag.FromErr(err) - } - if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { - return diag.FromErr(err) - } - return nil -} - -func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if d.HasChange("oauth_scopes") { - stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) - oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) - for i, scope := range stringScopes { - oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} - } - - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest(oauthScopes) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthClientCredentialsFlow(*setRequest)) - - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - - return ReadContextSecretWithClientCredentials(ctx, d, meta) -} - -func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil -} diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go b/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go index 32e60449f0..5f01bb37a8 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go @@ -5,24 +5,26 @@ import ( "errors" "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "time" ) -var secretAuthorizationCodeSchema = func() map[string]*schema.Schema { - secretAuthorizationCode := map[string]*schema.Schema{ +var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { + secretAuthorizationCodeGrant := map[string]*schema.Schema{ "oauth_refresh_token": { Type: schema.TypeString, Required: true, Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", }, "oauth_refresh_token_expiry_time": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted formats: YYYY-MM-DD, YYYY-MM-DD HH:MI:SS", + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("oauth_refresh_token_expiry_time"), + Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI PDT", }, "api_authentication": { Type: schema.TypeString, @@ -31,24 +33,63 @@ var secretAuthorizationCodeSchema = func() map[string]*schema.Schema { Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", }, } - return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) + return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCodeGrant) }() -func SecretWithAuthorizationCode() *schema.Resource { +func SecretWithAuthorizationCodeGrant() *schema.Resource { return &schema.Resource{ - CreateContext: CreateContextSecretWithAuthorizationCode, - ReadContext: ReadContextSecretWithAuthorizationCode, - UpdateContext: UpdateContextSecretWithAuthorizationCode, - DeleteContext: DeleteContextSecretWithAuthorizationCode, + CreateContext: CreateContextSecretWithAuthorizationCodeGrant, + ReadContext: ReadContextSecretWithAuthorizationCodeGrant, + UpdateContext: UpdateContextSecretWithAuthorizationCodeGrant, + DeleteContext: DeleteContextSecretWithAuthorizationCodeGrant, - Schema: secretAuthorizationCodeSchema, + Schema: secretAuthorizationCodeGrantSchema, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: ImportSecretWithAuthorizationCodeGrant, }, + Description: "Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.'", + + CustomizeDiff: customdiff.All( + ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "oauth_refresh_token_expiry_time"), + ), } } -func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ImportSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting secret with authorization code import") + client := meta.(*provider.Context).Client + + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return nil, err + } + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return nil, err + } + + // cannot import oauth_refresh_token because it is not present both in SHOW or DESCRIBE + + if err := d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { + return nil, err + } + if err := d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return nil, err + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if err != nil { + return nil, err + } + + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return nil, err + } + return []*schema.ResourceData{d}, nil +} + +func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client commonCreate := handleSecretCreate(d) @@ -75,10 +116,10 @@ func CreateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadContextSecretWithAuthorizationCode(ctx, d, meta) + return ReadContextSecretWithAuthorizationCodeGrant(ctx, d, meta) } -func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -108,19 +149,29 @@ func ReadContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Resou if err != nil { return diag.FromErr(err) } - if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + + if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { return diag.FromErr(err) } - if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.In(time.UTC).Format(time.DateOnly)); err != nil { + /* + // Possible limitation + // Accepted formats are: YYYY-MM-DD; YYYY-MM-DD HH:MI:SS + // But snowflake holds this value as timestamp, so with this code below we can parse it and keep in state only with one of time.DateOnly or time.DateTime + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.In(time.UTC).Format(time.DateOnly)); err != nil { + return diag.FromErr(err) + } + */ + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { return diag.FromErr(err) } + return nil } -func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -157,10 +208,10 @@ func UpdateContextSecretWithAuthorizationCode(ctx context.Context, d *schema.Res } } - return ReadContextSecretWithAuthorizationCode(ctx, d, meta) + return ReadContextSecretWithAuthorizationCodeGrant(ctx, d, meta) } -func DeleteContextSecretWithAuthorizationCode(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go index af03ec3b84..c814427972 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go @@ -1,13 +1,13 @@ package resources_test import ( + "fmt" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" @@ -20,13 +20,13 @@ import ( "time" ) -func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { +func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() name := id.Name() comment := random.Comment() newComment := random.Comment() - refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) - newRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + refreshTokenExpiryDateTime := time.Now().Add(24 * time.Hour).Format(time.DateTime) + newRefreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) refreshToken := "test_token" newRefreshToken := "new_test_token" @@ -36,7 +36,7 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithAuthorizationCode("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryTime).WithComment(comment) + secretModel := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryDateTime).WithComment(comment) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -44,7 +44,7 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: acc.CheckDestroy(t, resources.SecretWithAuthorizationCode), + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithAuthorizationCodeGrant), Steps: []resource.TestStep{ // create { @@ -57,15 +57,16 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(refreshToken). - HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryTime). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateTime). HasCommentString(comment), + assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), ), ), }, // set all { Config: config.FromModel(t, secretModel. - WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryTime). + WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryDateOnly). WithOauthRefreshToken(newRefreshToken). WithComment(newComment), ), @@ -73,7 +74,7 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { PreApply: []plancheck.PlanCheck{ planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String(refreshToken), sdk.String(newRefreshToken)), - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryTime), sdk.String(newRefreshTokenExpiryTime)), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryDateTime), sdk.String(newRefreshTokenExpiryDateOnly)), }, }, Check: resource.ComposeTestCheckFunc( @@ -84,8 +85,9 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(newRefreshToken). - HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryTime). + HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), ), ), }, @@ -95,12 +97,90 @@ func TestAcc_SecretWithAuthorizationCode_BasicFlow(t *testing.T) { ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"oauth_refresh_token"}, - ImportStateCheck: importchecks.ComposeImportStateCheck( - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "oauth_refresh_token_expiry_time", newRefreshTokenExpiryTime), - importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", newComment), + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedSecretWithAuthorizationCodeResource(t, helpers.EncodeResourceIdentifier(id)). + HasNameString(id.Name()). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasCommentString(newComment). + HasOauthRefreshTokenExpiryTimeNotEmpty(), + ), + }, + }, + }) +} + +func TestAcc_SecretWithAuthorizationCodeGrant_DifferentTimeFormats(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + + refreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + refreshTokenExpiryWithoutSeconds := time.Now().Add(4 * 24 * time.Hour).Format("2006-01-02 15:04") + refreshTokenExpiryDateTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateTime) + refreshTokenExpiryWithPDT := fmt.Sprintf("%s %s", time.Now().Add(4*24*time.Hour).Format("2006-01-02 15:04"), "PDT") + + secretModelDateOnly := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryDateOnly) + secretModelWithoutSeconds := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryWithoutSeconds) + secretModelDateTime := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryDateTime) + secretModelWithPDT := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, "test_token", refreshTokenExpiryWithPDT) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithAuthorizationCodeGrant), + Steps: []resource.TestStep{ + // create with DateOnly + { + Config: config.FromModel(t, secretModelDateOnly), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModelDateOnly.ResourceReference()). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateOnly), + assert.Check(resource.TestCheckResourceAttrSet(secretModelDateOnly.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), + ), + }, + // update with DateTime without seconds + { + Config: config.FromModel(t, secretModelWithoutSeconds), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModelWithoutSeconds.ResourceReference()). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryWithoutSeconds), + assert.Check(resource.TestCheckResourceAttrSet(secretModelWithoutSeconds.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), + ), + }, + // update with DateTime + { + Config: config.FromModel(t, secretModelDateTime), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModelDateTime.ResourceReference()). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateTime), + assert.Check(resource.TestCheckResourceAttrSet(secretModelDateTime.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), + ), + }, + // update with DateTime with PDT timezone + { + Config: config.FromModel(t, secretModelWithPDT), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModelWithPDT.ResourceReference()). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryWithPDT), + assert.Check(resource.TestCheckResourceAttrSet(secretModelWithPDT.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), ), }, }, diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow.go b/pkg/resources/secret_with_oauth_client_credentials_flow.go index 98327344ee..d0f4d1a41a 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow.go @@ -40,6 +40,7 @@ func SecretWithClientCredentials() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + Description: "Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.'", } } @@ -126,6 +127,12 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res return diag.FromErr(err) } + if request := handleSecretUpdate(id, d); request != nil { + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + if d.HasChange("oauth_scopes") { stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) @@ -134,7 +141,7 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res } request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest().WithOauthScopes(sdk.OauthScopesListRequest{oauthScopes}) + setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest().WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthClientCredentialsFlow(*setRequest)) if err := client.Secrets.Alter(ctx, request); err != nil { @@ -142,12 +149,6 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res } } - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } - } - return ReadContextSecretWithClientCredentials(ctx, d, meta) } diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go index 2be356fbbf..a8e2e37f7e 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go @@ -48,16 +48,16 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModel), Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasCommentString(comment), ), - resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "bar"), ), }, // set oauth_scopes and comment in config From 356d98cdaee76e9d4213e2b57b2724759bf264d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 2 Oct 2024 15:11:27 +0200 Subject: [PATCH 50/65] linter pre-push changes --- ...secret_with_generic_string_resource_gen.go | 97 ++++++++++++ .../secret_with_generic_string_model_gen.go | 122 +++++++++++++++ pkg/acceptance/helpers/secret_client.go | 3 +- .../secret_with_basic_authentication_flow.go | 1 + ...sic_authentication_flow_acceptance_test.go | 3 +- pkg/resources/secret_with_generic_string.go | 143 ++++++++++++++++++ ...ret_with_generic_string_acceptance_test.go | 114 ++++++++++++++ ...ith_oauth_authorization_code_grant_flow.go | 1 + ...ization_code_grant_flow_acceptance_test.go | 5 +- ...cret_with_oauth_client_credentials_flow.go | 1 + ...client_credentials_flow_acceptance_test.go | 3 +- pkg/sdk/secrets_impl_gen.go | 1 + 12 files changed, 489 insertions(+), 5 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_generic_string_resource_gen.go create mode 100644 pkg/acceptance/bettertestspoc/config/model/secret_with_generic_string_model_gen.go create mode 100644 pkg/resources/secret_with_generic_string.go create mode 100644 pkg/resources/secret_with_generic_string_acceptance_test.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_generic_string_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_generic_string_resource_gen.go new file mode 100644 index 0000000000..d2d682aa1f --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_generic_string_resource_gen.go @@ -0,0 +1,97 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package resourceassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +type SecretWithGenericStringResourceAssert struct { + *assert.ResourceAssert +} + +func SecretWithGenericStringResource(t *testing.T, name string) *SecretWithGenericStringResourceAssert { + t.Helper() + + return &SecretWithGenericStringResourceAssert{ + ResourceAssert: assert.NewResourceAssert(name, "resource"), + } +} + +func ImportedSecretWithGenericStringResource(t *testing.T, id string) *SecretWithGenericStringResourceAssert { + t.Helper() + + return &SecretWithGenericStringResourceAssert{ + ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"), + } +} + +/////////////////////////////////// +// Attribute value string checks // +/////////////////////////////////// + +func (s *SecretWithGenericStringResourceAssert) HasCommentString(expected string) *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueSet("comment", expected)) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasDatabaseString(expected string) *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueSet("database", expected)) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasFullyQualifiedNameString(expected string) *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueSet("fully_qualified_name", expected)) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasNameString(expected string) *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueSet("name", expected)) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasSchemaString(expected string) *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueSet("schema", expected)) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasSecretStringString(expected string) *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueSet("secret_string", expected)) + return s +} + +//////////////////////////// +// Attribute empty checks // +//////////////////////////// + +func (s *SecretWithGenericStringResourceAssert) HasNoComment() *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueNotSet("comment")) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasNoDatabase() *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueNotSet("database")) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasNoFullyQualifiedName() *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueNotSet("fully_qualified_name")) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasNoName() *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueNotSet("name")) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasNoSchema() *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueNotSet("schema")) + return s +} + +func (s *SecretWithGenericStringResourceAssert) HasNoSecretString() *SecretWithGenericStringResourceAssert { + s.AddAssertion(assert.ValueNotSet("secret_string")) + return s +} diff --git a/pkg/acceptance/bettertestspoc/config/model/secret_with_generic_string_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/secret_with_generic_string_model_gen.go new file mode 100644 index 0000000000..e2f441ef3f --- /dev/null +++ b/pkg/acceptance/bettertestspoc/config/model/secret_with_generic_string_model_gen.go @@ -0,0 +1,122 @@ +// Code generated by config model builder generator; DO NOT EDIT. + +package model + +import ( + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" +) + +type SecretWithGenericStringModel struct { + Comment tfconfig.Variable `json:"comment,omitempty"` + Database tfconfig.Variable `json:"database,omitempty"` + FullyQualifiedName tfconfig.Variable `json:"fully_qualified_name,omitempty"` + Name tfconfig.Variable `json:"name,omitempty"` + Schema tfconfig.Variable `json:"schema,omitempty"` + SecretString tfconfig.Variable `json:"secret_string,omitempty"` + + *config.ResourceModelMeta +} + +///////////////////////////////////////////////// +// Basic builders (resource name and required) // +///////////////////////////////////////////////// + +func SecretWithGenericString( + resourceName string, + database string, + name string, + schema string, + secretString string, +) *SecretWithGenericStringModel { + s := &SecretWithGenericStringModel{ResourceModelMeta: config.Meta(resourceName, resources.SecretWithGenericString)} + s.WithDatabase(database) + s.WithName(name) + s.WithSchema(schema) + s.WithSecretString(secretString) + return s +} + +func SecretWithGenericStringWithDefaultMeta( + database string, + name string, + schema string, + secretString string, +) *SecretWithGenericStringModel { + s := &SecretWithGenericStringModel{ResourceModelMeta: config.DefaultMeta(resources.SecretWithGenericString)} + s.WithDatabase(database) + s.WithName(name) + s.WithSchema(schema) + s.WithSecretString(secretString) + return s +} + +///////////////////////////////// +// below all the proper values // +///////////////////////////////// + +func (s *SecretWithGenericStringModel) WithComment(comment string) *SecretWithGenericStringModel { + s.Comment = tfconfig.StringVariable(comment) + return s +} + +func (s *SecretWithGenericStringModel) WithDatabase(database string) *SecretWithGenericStringModel { + s.Database = tfconfig.StringVariable(database) + return s +} + +func (s *SecretWithGenericStringModel) WithFullyQualifiedName(fullyQualifiedName string) *SecretWithGenericStringModel { + s.FullyQualifiedName = tfconfig.StringVariable(fullyQualifiedName) + return s +} + +func (s *SecretWithGenericStringModel) WithName(name string) *SecretWithGenericStringModel { + s.Name = tfconfig.StringVariable(name) + return s +} + +func (s *SecretWithGenericStringModel) WithSchema(schema string) *SecretWithGenericStringModel { + s.Schema = tfconfig.StringVariable(schema) + return s +} + +func (s *SecretWithGenericStringModel) WithSecretString(secretString string) *SecretWithGenericStringModel { + s.SecretString = tfconfig.StringVariable(secretString) + return s +} + +////////////////////////////////////////// +// below it's possible to set any value // +////////////////////////////////////////// + +func (s *SecretWithGenericStringModel) WithCommentValue(value tfconfig.Variable) *SecretWithGenericStringModel { + s.Comment = value + return s +} + +func (s *SecretWithGenericStringModel) WithDatabaseValue(value tfconfig.Variable) *SecretWithGenericStringModel { + s.Database = value + return s +} + +func (s *SecretWithGenericStringModel) WithFullyQualifiedNameValue(value tfconfig.Variable) *SecretWithGenericStringModel { + s.FullyQualifiedName = value + return s +} + +func (s *SecretWithGenericStringModel) WithNameValue(value tfconfig.Variable) *SecretWithGenericStringModel { + s.Name = value + return s +} + +func (s *SecretWithGenericStringModel) WithSchemaValue(value tfconfig.Variable) *SecretWithGenericStringModel { + s.Schema = value + return s +} + +func (s *SecretWithGenericStringModel) WithSecretStringValue(value tfconfig.Variable) *SecretWithGenericStringModel { + s.SecretString = value + return s +} diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index f0d1594f30..c0edac4cd1 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -2,10 +2,11 @@ package helpers import ( "context" + "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) type SecretClient struct { diff --git a/pkg/resources/secret_with_basic_authentication_flow.go b/pkg/resources/secret_with_basic_authentication_flow.go index 52cedbf673..90448a6e2e 100644 --- a/pkg/resources/secret_with_basic_authentication_flow.go +++ b/pkg/resources/secret_with_basic_authentication_flow.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" diff --git a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go index cde468dc68..9759c823bb 100644 --- a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go @@ -1,6 +1,8 @@ package resources_test import ( + "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/tfversion" - "testing" ) func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go new file mode 100644 index 0000000000..41381fbc5e --- /dev/null +++ b/pkg/resources/secret_with_generic_string.go @@ -0,0 +1,143 @@ +package resources + +import ( + "context" + "errors" + "fmt" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var secretGenericStringSchema = func() map[string]*schema.Schema { + secretAuthorizationCode := map[string]*schema.Schema{ + "secret_string": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the string to store in the secret.\n\nThe string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. " + + "For details, see Creating and using an external access integration.\n\nYou should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", + }, + } + return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) +}() + +func SecretWithGenericString() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextSecretWithGenericString, + ReadContext: ReadContextSecretWithGenericString, + UpdateContext: UpdateContextSecretWithGenericString, + DeleteContext: DeleteContextSecretWithGenericString, + + Schema: secretGenericStringSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Description: "Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.'", + } +} + +func CreateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + commonCreate := handleSecretCreate(d) + + id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) + + secretSting := d.Get("secret_string").(string) + + request := sdk.NewCreateWithGenericStringSecretRequest(id, secretSting) + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) + } + + err := client.Secrets.CreateWithGenericString(ctx, request) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeResourceIdentifier(id)) + + return ReadContextSecretWithGenericString(ctx, d, meta) +} + +func ReadContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + secret, err := client.Secrets.ShowByID(ctx, id) + if secret == nil || err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, + } + } + + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if request := handleSecretUpdate(id, d); request != nil { + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + if d.HasChange("secret_string") { + secretString := d.Get("secret_string").(string) + + request := sdk.NewAlterSecretRequest(id) + setRequest := sdk.NewSetForGenericStringRequest().WithSecretString(secretString) + request.WithSet(*sdk.NewSecretSetRequest().WithSetForGenericString(*setRequest)) + + if err := client.Secrets.Alter(ctx, request); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextSecretWithGenericString(ctx, d, meta) +} + +func DeleteContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_generic_string_acceptance_test.go b/pkg/resources/secret_with_generic_string_acceptance_test.go new file mode 100644 index 0000000000..94223a20d3 --- /dev/null +++ b/pkg/resources/secret_with_generic_string_acceptance_test.go @@ -0,0 +1,114 @@ +package resources_test + +import ( + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + + secretModel := model.SecretWithGenericString("s", id.DatabaseName(), name, id.SchemaName(), "foo") + secretModelEmptySecretString := model.SecretWithGenericString("s", id.DatabaseName(), name, id.SchemaName(), "") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithGenericString), + Steps: []resource.TestStep{ + // create + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithGenericStringResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasSecretStringString("foo"), + ), + ), + }, + // set secret_string and comment + { + Config: config.FromModel(t, secretModel. + WithSecretString("bar"). + WithComment(comment), + ), + Check: assert.AssertThat(t, + resourceassert.SecretWithGenericStringResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasSecretStringString("bar"). + HasCommentString(comment), + ), + }, + // import + { + ResourceName: secretModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"secret_string"}, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", comment), + ), + }, + // unset comment + { + Config: config.FromModel(t, secretModelEmptySecretString), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelEmptySecretString.ResourceReference()). + HasCommentString(""), + ), + }, + // destroy + { + Config: config.FromModel(t, secretModel), + Destroy: true, + }, + // create with empty secret_string + { + Config: config.FromModel(t, secretModelEmptySecretString), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithGenericStringResource(t, secretModelEmptySecretString.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasSecretStringString(""). + HasCommentString(""), + ), + ), + }, + }, + }) +} diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go b/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go index 5f01bb37a8..d892ddcd73 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go index c814427972..ba4dc12b9c 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go @@ -2,6 +2,9 @@ package resources_test import ( "fmt" + "testing" + "time" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" @@ -16,8 +19,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/tfversion" - "testing" - "time" ) func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow.go b/pkg/resources/secret_with_oauth_client_credentials_flow.go index d0f4d1a41a..9e0a7913f1 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go index a8e2e37f7e..e6094f0112 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go @@ -1,6 +1,8 @@ package resources_test import ( + "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" @@ -8,7 +10,6 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" tfjson "github.com/hashicorp/terraform-json" "github.com/hashicorp/terraform-plugin-testing/plancheck" - "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go index b184550976..3fb45b58d9 100644 --- a/pkg/sdk/secrets_impl_gen.go +++ b/pkg/sdk/secrets_impl_gen.go @@ -2,6 +2,7 @@ package sdk import ( "context" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" ) From 83ccafc16e98e87613ab660b10ec40799879695d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 2 Oct 2024 15:26:53 +0200 Subject: [PATCH 51/65] generated documentation for each secret flow --- .../secret_with_authorization_code_grant.md | 93 +++++++++++++++++++ .../secret_with_basic_authentication.md | 91 ++++++++++++++++++ .../secret_with_client_credentials.md | 91 ++++++++++++++++++ docs/resources/secret_with_generic_string.md | 93 +++++++++++++++++++ .../import.sh | 1 + .../resource.tf | 10 ++ .../import.sh | 1 + .../resource.tf | 9 ++ .../import.sh | 1 + .../resource.tf | 9 ++ .../import.sh | 1 + .../resource.tf | 8 ++ 12 files changed, 408 insertions(+) create mode 100644 docs/resources/secret_with_authorization_code_grant.md create mode 100644 docs/resources/secret_with_basic_authentication.md create mode 100644 docs/resources/secret_with_client_credentials.md create mode 100644 docs/resources/secret_with_generic_string.md create mode 100644 examples/resources/snowflake_secret_with_authorization_code_grant/import.sh create mode 100644 examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf create mode 100644 examples/resources/snowflake_secret_with_basic_authentication/import.sh create mode 100644 examples/resources/snowflake_secret_with_basic_authentication/resource.tf create mode 100644 examples/resources/snowflake_secret_with_client_credentials/import.sh create mode 100644 examples/resources/snowflake_secret_with_client_credentials/resource.tf create mode 100644 examples/resources/snowflake_secret_with_generic_string/import.sh create mode 100644 examples/resources/snowflake_secret_with_generic_string/resource.tf diff --git a/docs/resources/secret_with_authorization_code_grant.md b/docs/resources/secret_with_authorization_code_grant.md new file mode 100644 index 0000000000..498c22ff88 --- /dev/null +++ b/docs/resources/secret_with_authorization_code_grant.md @@ -0,0 +1,93 @@ +--- +page_title: "snowflake_secret_with_authorization_code_grant Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.' +--- + +# snowflake_secret_with_authorization_code_grant (Resource) + +Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.' + +## Example Usage + +```terraform +# basic resource +resource "snowflake_secret_with_authorization_code_grant" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_refresh_token = "EXAMPLE_TOKEN" + oauth_refresh_token_expiry_time = "2025-01-02 15:04:01" + comment = "EXAMPLE_COMMENT" +} +``` + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + + +## Schema + +### Required + +- `api_authentication` (String) Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2. +- `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `oauth_refresh_token` (String) Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires. +- `oauth_refresh_token_expiry_time` (String) Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI PDT +- `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` + +### Optional + +- `comment` (String) Specifies a comment for the secret. + +### Read-Only + +- `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) +- `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). +- `id` (String) The ID of this resource. +- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) + + +### Nested Schema for `describe_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `integration_name` (String) +- `name` (String) +- `oauth_access_token_expiry_time` (String) +- `oauth_refresh_token_expiry_time` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `schema_name` (String) +- `secret_type` (String) +- `username` (String) + + + +### Nested Schema for `show_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `name` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `owner_role_type` (String) +- `schema_name` (String) +- `secret_type` (String) + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_secret_with_authorization_code_grant.example '""."".""' +``` diff --git a/docs/resources/secret_with_basic_authentication.md b/docs/resources/secret_with_basic_authentication.md new file mode 100644 index 0000000000..fe72563235 --- /dev/null +++ b/docs/resources/secret_with_basic_authentication.md @@ -0,0 +1,91 @@ +--- +page_title: "snowflake_secret_with_basic_authentication Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.' +--- + +# snowflake_secret_with_basic_authentication (Resource) + +Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.' + +## Example Usage + +```terraform +# basic resource +resource "snowflake_secret_with_basic_authentication" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + username = "EXAMPLE_USERNAME" + password = "EXAMPLE_PASSWORD" + comment = "EXAMPLE_COMMENT" +} +``` + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + + +## Schema + +### Required + +- `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `password` (String) Specifies the password value to store in the secret when setting the TYPE value to PASSWORD. +- `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `username` (String) Specifies the username value to store in the secret when setting the TYPE value to PASSWORD. + +### Optional + +- `comment` (String) Specifies a comment for the secret. + +### Read-Only + +- `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) +- `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). +- `id` (String) The ID of this resource. +- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) + + +### Nested Schema for `describe_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `integration_name` (String) +- `name` (String) +- `oauth_access_token_expiry_time` (String) +- `oauth_refresh_token_expiry_time` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `schema_name` (String) +- `secret_type` (String) +- `username` (String) + + + +### Nested Schema for `show_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `name` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `owner_role_type` (String) +- `schema_name` (String) +- `secret_type` (String) + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_secret_with_basic_authentication.example '""."".""' +``` diff --git a/docs/resources/secret_with_client_credentials.md b/docs/resources/secret_with_client_credentials.md new file mode 100644 index 0000000000..4d5b22dba5 --- /dev/null +++ b/docs/resources/secret_with_client_credentials.md @@ -0,0 +1,91 @@ +--- +page_title: "snowflake_secret_with_client_credentials Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.' +--- + +# snowflake_secret_with_client_credentials (Resource) + +Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.' + +## Example Usage + +```terraform +# basic resource +resource "snowflake_secret_with_client_credentials" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_scopes = ["useraccount", "testscope"] + comment = "EXAMPLE_COMMENT" +} +``` + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + + +## Schema + +### Required + +- `api_authentication` (String) Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2. +- `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `oauth_scopes` (Set of String) Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow. +- `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` + +### Optional + +- `comment` (String) Specifies a comment for the secret. + +### Read-Only + +- `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) +- `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). +- `id` (String) The ID of this resource. +- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) + + +### Nested Schema for `describe_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `integration_name` (String) +- `name` (String) +- `oauth_access_token_expiry_time` (String) +- `oauth_refresh_token_expiry_time` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `schema_name` (String) +- `secret_type` (String) +- `username` (String) + + + +### Nested Schema for `show_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `name` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `owner_role_type` (String) +- `schema_name` (String) +- `secret_type` (String) + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_secret_with_client_credentials.example '""."".""' +``` diff --git a/docs/resources/secret_with_generic_string.md b/docs/resources/secret_with_generic_string.md new file mode 100644 index 0000000000..b54404299c --- /dev/null +++ b/docs/resources/secret_with_generic_string.md @@ -0,0 +1,93 @@ +--- +page_title: "snowflake_secret_with_generic_string Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.' +--- + +# snowflake_secret_with_generic_string (Resource) + +Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.' + +## Example Usage + +```terraform +# basic resource +resource "snowflake_secret_with_client_credentials" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + secret_string = "EXAMPLE_SECRET_STRING" + comment = "EXAMPLE_COMMENT" +} +``` + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + + +## Schema + +### Required + +- `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` +- `secret_string` (String) Specifies the string to store in the secret. + +The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see Creating and using an external access integration. + +You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases. + +### Optional + +- `comment` (String) Specifies a comment for the secret. + +### Read-Only + +- `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) +- `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). +- `id` (String) The ID of this resource. +- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) + + +### Nested Schema for `describe_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `integration_name` (String) +- `name` (String) +- `oauth_access_token_expiry_time` (String) +- `oauth_refresh_token_expiry_time` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `schema_name` (String) +- `secret_type` (String) +- `username` (String) + + + +### Nested Schema for `show_output` + +Read-Only: + +- `comment` (String) +- `created_on` (String) +- `database_name` (String) +- `name` (String) +- `oauth_scopes` (Set of String) +- `owner` (String) +- `owner_role_type` (String) +- `schema_name` (String) +- `secret_type` (String) + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_secret_with_generic_string.example '""."".""' +``` diff --git a/examples/resources/snowflake_secret_with_authorization_code_grant/import.sh b/examples/resources/snowflake_secret_with_authorization_code_grant/import.sh new file mode 100644 index 0000000000..eca025de9e --- /dev/null +++ b/examples/resources/snowflake_secret_with_authorization_code_grant/import.sh @@ -0,0 +1 @@ +terraform import snowflake_secret_with_authorization_code_grant.example '""."".""' diff --git a/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf b/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf new file mode 100644 index 0000000000..cd15032502 --- /dev/null +++ b/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf @@ -0,0 +1,10 @@ +# basic resource +resource "snowflake_secret_with_authorization_code_grant" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_refresh_token = "EXAMPLE_TOKEN" + oauth_refresh_token_expiry_time = "2025-01-02 15:04:01" + comment = "EXAMPLE_COMMENT" +} diff --git a/examples/resources/snowflake_secret_with_basic_authentication/import.sh b/examples/resources/snowflake_secret_with_basic_authentication/import.sh new file mode 100644 index 0000000000..caffffdfe8 --- /dev/null +++ b/examples/resources/snowflake_secret_with_basic_authentication/import.sh @@ -0,0 +1 @@ +terraform import snowflake_secret_with_basic_authentication.example '""."".""' diff --git a/examples/resources/snowflake_secret_with_basic_authentication/resource.tf b/examples/resources/snowflake_secret_with_basic_authentication/resource.tf new file mode 100644 index 0000000000..1685bd003c --- /dev/null +++ b/examples/resources/snowflake_secret_with_basic_authentication/resource.tf @@ -0,0 +1,9 @@ +# basic resource +resource "snowflake_secret_with_basic_authentication" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + username = "EXAMPLE_USERNAME" + password = "EXAMPLE_PASSWORD" + comment = "EXAMPLE_COMMENT" +} diff --git a/examples/resources/snowflake_secret_with_client_credentials/import.sh b/examples/resources/snowflake_secret_with_client_credentials/import.sh new file mode 100644 index 0000000000..61dc5191ca --- /dev/null +++ b/examples/resources/snowflake_secret_with_client_credentials/import.sh @@ -0,0 +1 @@ +terraform import snowflake_secret_with_client_credentials.example '""."".""' diff --git a/examples/resources/snowflake_secret_with_client_credentials/resource.tf b/examples/resources/snowflake_secret_with_client_credentials/resource.tf new file mode 100644 index 0000000000..dcea57a1fc --- /dev/null +++ b/examples/resources/snowflake_secret_with_client_credentials/resource.tf @@ -0,0 +1,9 @@ +# basic resource +resource "snowflake_secret_with_client_credentials" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_scopes = ["useraccount", "testscope"] + comment = "EXAMPLE_COMMENT" +} diff --git a/examples/resources/snowflake_secret_with_generic_string/import.sh b/examples/resources/snowflake_secret_with_generic_string/import.sh new file mode 100644 index 0000000000..10b0f75023 --- /dev/null +++ b/examples/resources/snowflake_secret_with_generic_string/import.sh @@ -0,0 +1 @@ +terraform import snowflake_secret_with_generic_string.example '""."".""' diff --git a/examples/resources/snowflake_secret_with_generic_string/resource.tf b/examples/resources/snowflake_secret_with_generic_string/resource.tf new file mode 100644 index 0000000000..2707ced05f --- /dev/null +++ b/examples/resources/snowflake_secret_with_generic_string/resource.tf @@ -0,0 +1,8 @@ +# basic resource +resource "snowflake_secret_with_client_credentials" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + secret_string = "EXAMPLE_SECRET_STRING" + comment = "EXAMPLE_COMMENT" +} From de99b6866c25a7816a85a1daf59e6346aef3c4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 3 Oct 2024 14:36:49 +0200 Subject: [PATCH 52/65] delete unused _ext assert --- .../secret_with_client_credentials_resource_ext.go | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go deleted file mode 100644 index 543d330d5e..0000000000 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go +++ /dev/null @@ -1,13 +0,0 @@ -package resourceassert - -import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" -) - -func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopes(expected []sdk.ApiIntegrationScope) *SecretWithClientCredentialsResourceAssert { - for _, v := range expected { - s.AddAssertion(assert.ValueSet("oauth_scopes.*", v.Scope)) - } - return s -} From 5c6d9ee8b438ea658b934cbd02c5f6bccbc83dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 3 Oct 2024 15:21:23 +0200 Subject: [PATCH 53/65] self-review cleanup --- ...n_flow.go => secret_with_basic_authentication.go} | 4 ++-- ...ret_with_basic_authentication_acceptance_test.go} | 0 pkg/resources/secret_with_generic_string.go | 11 +++++------ ...=> secret_with_oauth_authorization_code_grant.go} | 12 +----------- ...auth_authorization_code_grant_acceptance_test.go} | 0 ...ow.go => secret_with_oauth_client_credentials.go} | 0 ...with_oauth_client_credentials_acceptance_test.go} | 0 7 files changed, 8 insertions(+), 19 deletions(-) rename pkg/resources/{secret_with_basic_authentication_flow.go => secret_with_basic_authentication.go} (97%) rename pkg/resources/{secret_with_basic_authentication_flow_acceptance_test.go => secret_with_basic_authentication_acceptance_test.go} (100%) rename pkg/resources/{secret_with_oauth_authorization_code_grant_flow.go => secret_with_oauth_authorization_code_grant.go} (92%) rename pkg/resources/{secret_with_oauth_authorization_code_grant_flow_acceptance_test.go => secret_with_oauth_authorization_code_grant_acceptance_test.go} (100%) rename pkg/resources/{secret_with_oauth_client_credentials_flow.go => secret_with_oauth_client_credentials.go} (100%) rename pkg/resources/{secret_with_oauth_client_credentials_flow_acceptance_test.go => secret_with_oauth_client_credentials_acceptance_test.go} (100%) diff --git a/pkg/resources/secret_with_basic_authentication_flow.go b/pkg/resources/secret_with_basic_authentication.go similarity index 97% rename from pkg/resources/secret_with_basic_authentication_flow.go rename to pkg/resources/secret_with_basic_authentication.go index 90448a6e2e..fad9e9d05a 100644 --- a/pkg/resources/secret_with_basic_authentication_flow.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -13,7 +13,7 @@ import ( ) var secretBasicAuthenticationSchema = func() map[string]*schema.Schema { - secretAuthorizationCode := map[string]*schema.Schema{ + secretBasicAuthentication := map[string]*schema.Schema{ "username": { Type: schema.TypeString, Required: true, @@ -25,7 +25,7 @@ var secretBasicAuthenticationSchema = func() map[string]*schema.Schema { Description: "Specifies the password value to store in the secret when setting the TYPE value to PASSWORD.", }, } - return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) + return helpers.MergeMaps(secretCommonSchema, secretBasicAuthentication) }() func SecretWithBasicAuthentication() *schema.Resource { diff --git a/pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_acceptance_test.go similarity index 100% rename from pkg/resources/secret_with_basic_authentication_flow_acceptance_test.go rename to pkg/resources/secret_with_basic_authentication_acceptance_test.go diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 41381fbc5e..3a10f9a65f 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -13,15 +13,14 @@ import ( ) var secretGenericStringSchema = func() map[string]*schema.Schema { - secretAuthorizationCode := map[string]*schema.Schema{ + secretGenericString := map[string]*schema.Schema{ "secret_string": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the string to store in the secret.\n\nThe string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. " + - "For details, see Creating and using an external access integration.\n\nYou should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", + Type: schema.TypeString, + Required: true, + Description: "Specifies the string to store in the secret.\n\nThe string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure.\n\nFor details, see Creating and using an external access integration.\n\nYou should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", }, } - return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCode) + return helpers.MergeMaps(secretCommonSchema, secretGenericString) }() func SecretWithGenericString() *schema.Resource { diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go similarity index 92% rename from pkg/resources/secret_with_oauth_authorization_code_grant_flow.go rename to pkg/resources/secret_with_oauth_authorization_code_grant.go index d892ddcd73..18540d142c 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_flow.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -25,7 +25,7 @@ var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { Type: schema.TypeString, Required: true, DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("oauth_refresh_token_expiry_time"), - Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI PDT", + Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI ", }, "api_authentication": { Type: schema.TypeString, @@ -70,8 +70,6 @@ func ImportSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.Resou return nil, err } - // cannot import oauth_refresh_token because it is not present both in SHOW or DESCRIBE - if err := d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { return nil, err } @@ -154,14 +152,6 @@ func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema. if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { return diag.FromErr(err) } - /* - // Possible limitation - // Accepted formats are: YYYY-MM-DD; YYYY-MM-DD HH:MI:SS - // But snowflake holds this value as timestamp, so with this code below we can parse it and keep in state only with one of time.DateOnly or time.DateTime - if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.In(time.UTC).Format(time.DateOnly)); err != nil { - return diag.FromErr(err) - } - */ if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go similarity index 100% rename from pkg/resources/secret_with_oauth_authorization_code_grant_flow_acceptance_test.go rename to pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow.go b/pkg/resources/secret_with_oauth_client_credentials.go similarity index 100% rename from pkg/resources/secret_with_oauth_client_credentials_flow.go rename to pkg/resources/secret_with_oauth_client_credentials.go diff --git a/pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go similarity index 100% rename from pkg/resources/secret_with_oauth_client_credentials_flow_acceptance_test.go rename to pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go From 805c61e39315df81ff6f61aa9098a2b699ce26bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 3 Oct 2024 15:36:47 +0200 Subject: [PATCH 54/65] docs regenerate --- docs/resources/secret_with_authorization_code_grant.md | 2 +- docs/resources/secret_with_generic_string.md | 6 +----- pkg/resources/secret_with_generic_string.go | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/resources/secret_with_authorization_code_grant.md b/docs/resources/secret_with_authorization_code_grant.md index 498c22ff88..2d53030f2c 100644 --- a/docs/resources/secret_with_authorization_code_grant.md +++ b/docs/resources/secret_with_authorization_code_grant.md @@ -36,7 +36,7 @@ resource "snowflake_secret_with_authorization_code_grant" "test" { - `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `oauth_refresh_token` (String) Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires. -- `oauth_refresh_token_expiry_time` (String) Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI PDT +- `oauth_refresh_token_expiry_time` (String) Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI - `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` ### Optional diff --git a/docs/resources/secret_with_generic_string.md b/docs/resources/secret_with_generic_string.md index b54404299c..85f1e0d98f 100644 --- a/docs/resources/secret_with_generic_string.md +++ b/docs/resources/secret_with_generic_string.md @@ -33,11 +33,7 @@ resource "snowflake_secret_with_client_credentials" "test" { - `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` -- `secret_string` (String) Specifies the string to store in the secret. - -The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see Creating and using an external access integration. - -You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases. +- `secret_string` (String) Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases. ### Optional diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 3a10f9a65f..3fba5ad1ca 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -17,7 +17,7 @@ var secretGenericStringSchema = func() map[string]*schema.Schema { "secret_string": { Type: schema.TypeString, Required: true, - Description: "Specifies the string to store in the secret.\n\nThe string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure.\n\nFor details, see Creating and using an external access integration.\n\nYou should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", + Description: "Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", }, } return helpers.MergeMaps(secretCommonSchema, secretGenericString) From 044c4846d411d361ab761f43443b18bdaa26af4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 3 Oct 2024 15:48:25 +0200 Subject: [PATCH 55/65] example resource naming fix for secret with generic_string --- .../resources/snowflake_secret_with_generic_string/resource.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/resources/snowflake_secret_with_generic_string/resource.tf b/examples/resources/snowflake_secret_with_generic_string/resource.tf index 2707ced05f..96a0a07831 100644 --- a/examples/resources/snowflake_secret_with_generic_string/resource.tf +++ b/examples/resources/snowflake_secret_with_generic_string/resource.tf @@ -1,5 +1,5 @@ # basic resource -resource "snowflake_secret_with_client_credentials" "test" { +resource "snowflake_secret_with_generic_string" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" schema = "EXAMPLE_SCHEMA" From bc43a72c7b3e952fb5ee03505a9d93a9719281bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 3 Oct 2024 16:18:09 +0200 Subject: [PATCH 56/65] docs fixes --- docs/resources/secret_with_authorization_code_grant.md | 2 +- docs/resources/secret_with_basic_authentication.md | 2 +- docs/resources/secret_with_client_credentials.md | 2 +- docs/resources/secret_with_generic_string.md | 2 +- pkg/resources/secret_with_basic_authentication.go | 2 +- pkg/resources/secret_with_generic_string.go | 2 +- pkg/resources/secret_with_oauth_authorization_code_grant.go | 2 +- pkg/resources/secret_with_oauth_client_credentials.go | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/resources/secret_with_authorization_code_grant.md b/docs/resources/secret_with_authorization_code_grant.md index 2d53030f2c..be3b1e5223 100644 --- a/docs/resources/secret_with_authorization_code_grant.md +++ b/docs/resources/secret_with_authorization_code_grant.md @@ -7,7 +7,7 @@ description: |- # snowflake_secret_with_authorization_code_grant (Resource) -Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.' +Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'. ## Example Usage diff --git a/docs/resources/secret_with_basic_authentication.md b/docs/resources/secret_with_basic_authentication.md index fe72563235..d8269f5943 100644 --- a/docs/resources/secret_with_basic_authentication.md +++ b/docs/resources/secret_with_basic_authentication.md @@ -7,7 +7,7 @@ description: |- # snowflake_secret_with_basic_authentication (Resource) -Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.' +Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'. ## Example Usage diff --git a/docs/resources/secret_with_client_credentials.md b/docs/resources/secret_with_client_credentials.md index 4d5b22dba5..0011dc5788 100644 --- a/docs/resources/secret_with_client_credentials.md +++ b/docs/resources/secret_with_client_credentials.md @@ -7,7 +7,7 @@ description: |- # snowflake_secret_with_client_credentials (Resource) -Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.' +Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'. ## Example Usage diff --git a/docs/resources/secret_with_generic_string.md b/docs/resources/secret_with_generic_string.md index 85f1e0d98f..e17cf6c092 100644 --- a/docs/resources/secret_with_generic_string.md +++ b/docs/resources/secret_with_generic_string.md @@ -7,7 +7,7 @@ description: |- # snowflake_secret_with_generic_string (Resource) -Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.' +Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'. ## Example Usage diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index fad9e9d05a..a1b9cd373b 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -40,7 +40,7 @@ func SecretWithBasicAuthentication() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, - Description: "Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.'", + Description: "Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.", } } diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 3fba5ad1ca..fb9215c236 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -34,7 +34,7 @@ func SecretWithGenericString() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Description: "Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.'", + Description: "Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.", } } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 18540d142c..8b4b280c0a 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -48,7 +48,7 @@ func SecretWithAuthorizationCodeGrant() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: ImportSecretWithAuthorizationCodeGrant, }, - Description: "Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.'", + Description: "Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.", CustomizeDiff: customdiff.All( ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "oauth_refresh_token_expiry_time"), diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index 9e0a7913f1..c8f1e3a123 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -41,7 +41,7 @@ func SecretWithClientCredentials() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Description: "Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.'", + Description: "Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.", } } From 4ae36a482dc1a7721e769128cc8a2ad23dd79efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 3 Oct 2024 16:32:47 +0200 Subject: [PATCH 57/65] make docs-check error fix --- docs/resources/secret_with_authorization_code_grant.md | 4 ++-- docs/resources/secret_with_basic_authentication.md | 4 ++-- docs/resources/secret_with_client_credentials.md | 4 ++-- docs/resources/secret_with_generic_string.md | 6 +++--- pkg/resources/secret_with_basic_authentication.go | 2 +- pkg/resources/secret_with_generic_string.go | 2 +- pkg/resources/secret_with_oauth_authorization_code_grant.go | 2 +- pkg/resources/secret_with_oauth_client_credentials.go | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/resources/secret_with_authorization_code_grant.md b/docs/resources/secret_with_authorization_code_grant.md index be3b1e5223..0b4a0e3dfd 100644 --- a/docs/resources/secret_with_authorization_code_grant.md +++ b/docs/resources/secret_with_authorization_code_grant.md @@ -2,12 +2,12 @@ page_title: "snowflake_secret_with_authorization_code_grant Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.' + Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2. --- # snowflake_secret_with_authorization_code_grant (Resource) -Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'. +Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2. ## Example Usage diff --git a/docs/resources/secret_with_basic_authentication.md b/docs/resources/secret_with_basic_authentication.md index d8269f5943..d97e14e50e 100644 --- a/docs/resources/secret_with_basic_authentication.md +++ b/docs/resources/secret_with_basic_authentication.md @@ -2,12 +2,12 @@ page_title: "snowflake_secret_with_basic_authentication Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.' + Secret with Basic Authentication where Secrets Type attribute is set to PASSWORD. --- # snowflake_secret_with_basic_authentication (Resource) -Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'. +Secret with Basic Authentication where Secrets Type attribute is set to PASSWORD. ## Example Usage diff --git a/docs/resources/secret_with_client_credentials.md b/docs/resources/secret_with_client_credentials.md index 0011dc5788..eae8cd9aa7 100644 --- a/docs/resources/secret_with_client_credentials.md +++ b/docs/resources/secret_with_client_credentials.md @@ -2,12 +2,12 @@ page_title: "snowflake_secret_with_client_credentials Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.' + Secret with OAuth Client Credentials where Secrets Type attribute is set to OAUTH2. --- # snowflake_secret_with_client_credentials (Resource) -Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'. +Secret with OAuth Client Credentials where Secrets Type attribute is set to OAUTH2. ## Example Usage diff --git a/docs/resources/secret_with_generic_string.md b/docs/resources/secret_with_generic_string.md index e17cf6c092..53adccb675 100644 --- a/docs/resources/secret_with_generic_string.md +++ b/docs/resources/secret_with_generic_string.md @@ -2,18 +2,18 @@ page_title: "snowflake_secret_with_generic_string Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.' + Secret with Generic string where Secrets Type attribute is set to GENERIC_STRING. --- # snowflake_secret_with_generic_string (Resource) -Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'. +Secret with Generic string where Secrets Type attribute is set to GENERIC_STRING. ## Example Usage ```terraform # basic resource -resource "snowflake_secret_with_client_credentials" "test" { +resource "snowflake_secret_with_generic_string" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" schema = "EXAMPLE_SCHEMA" diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index a1b9cd373b..3297df3372 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -40,7 +40,7 @@ func SecretWithBasicAuthentication() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, - Description: "Secret with Basic Authentication where Secret's Type attribute is set to 'PASSWORD'.", + Description: "Secret with Basic Authentication where Secrets Type attribute is set to PASSWORD.", } } diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index fb9215c236..86e4ccda5f 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -34,7 +34,7 @@ func SecretWithGenericString() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Description: "Secret with Generic string where Secret's Type attribute is set to 'GENERIC_STRING'.", + Description: "Secret with Generic string where Secrets Type attribute is set to GENERIC_STRING.", } } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 8b4b280c0a..5b3d27d640 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -48,7 +48,7 @@ func SecretWithAuthorizationCodeGrant() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: ImportSecretWithAuthorizationCodeGrant, }, - Description: "Secret with OAuth authorization code grant where Secret's Type attribute is set to 'OAUTH2'.", + Description: "Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2.", CustomizeDiff: customdiff.All( ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "oauth_refresh_token_expiry_time"), diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index c8f1e3a123..9ce190b73f 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -41,7 +41,7 @@ func SecretWithClientCredentials() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - Description: "Secret with OAuth Client Credentials where Secret's Type attribute is set to 'OAUTH2'.", + Description: "Secret with OAuth Client Credentials where Secrets Type attribute is set to OAUTH2.", } } From f3cd4b1233b901c2837757ccc202b79eac539899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Tue, 8 Oct 2024 11:19:06 +0200 Subject: [PATCH 58/65] Jakub comments resolved --- pkg/acceptance/helpers/secret_client.go | 2 +- pkg/resources/secret_common.go | 59 +++---- .../secret_with_basic_authentication.go | 91 ++++++---- ...th_basic_authentication_acceptance_test.go | 36 +++- pkg/resources/secret_with_generic_string.go | 62 +++++-- ...ret_with_generic_string_acceptance_test.go | 36 +++- ...ret_with_oauth_authorization_code_grant.go | 87 ++++------ ...uthorization_code_grant_acceptance_test.go | 21 ++- .../secret_with_oauth_client_credentials.go | 164 ++++++++++++------ ...auth_client_credentials_acceptance_test.go | 84 ++++++++- pkg/schemas/secret.go | 5 +- 11 files changed, 442 insertions(+), 205 deletions(-) diff --git a/pkg/acceptance/helpers/secret_client.go b/pkg/acceptance/helpers/secret_client.go index c0edac4cd1..184498784b 100644 --- a/pkg/acceptance/helpers/secret_client.go +++ b/pkg/acceptance/helpers/secret_client.go @@ -71,7 +71,7 @@ func (c *SecretClient) CreateWithBasicAuthenticationFlow(t *testing.T, id sdk.Sc func (c *SecretClient) CreateWithGenericString(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) (*sdk.Secret, func()) { t.Helper() ctx := context.Background() - request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString).WithOrReplace(true) + request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString) err := c.client().CreateWithGenericString(ctx, request) require.NoError(t, err) diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index fe526ce1e8..5c1a245c7a 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -1,6 +1,7 @@ package resources import ( + "context" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -10,6 +11,7 @@ var secretCommonSchema = map[string]*schema.Schema{ "name": { Type: schema.TypeString, Required: true, + ForceNew: true, Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the secret, must be unique in your schema."), DiffSuppressFunc: suppressIdentifierQuoting, }, @@ -35,7 +37,7 @@ var secretCommonSchema = map[string]*schema.Schema{ ShowOutputAttributeName: { Type: schema.TypeList, Computed: true, - Description: "Outputs the result of `SHOW SECRET` for the given secret.", + Description: "Outputs the result of `SHOW SECRETS` for the given secret.", Elem: &schema.Resource{ Schema: schemas.ShowSecretSchema, }, @@ -51,39 +53,21 @@ var secretCommonSchema = map[string]*schema.Schema{ FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema, } -type commonSecretCreate struct { - name string - database string - schema string - comment *string -} - -func handleSecretCreate(d *schema.ResourceData) commonSecretCreate { - create := commonSecretCreate{ - name: d.Get("name").(string), - database: d.Get("database").(string), - schema: d.Get("schema").(string), - } - if v, ok := d.GetOk("comment"); ok { - create.comment = sdk.Pointer(v.(string)) +func handleSecretImport(d *schema.ResourceData) error { + if _, err := ImportName[sdk.SchemaObjectIdentifier](context.Background(), d, nil); err != nil { + return err } + return nil +} - return create +func handleSecretCreate(d *schema.ResourceData) (database, schema, name string) { + return d.Get("database").(string), d.Get("schema").(string), d.Get("name").(string) } func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret, secretDescription *sdk.SecretDetails) error { if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { return err } - if err := d.Set("name", id.Name()); err != nil { - return err - } - if err := d.Set("database", secret.DatabaseName); err != nil { - return err - } - if err := d.Set("schema", secret.SchemaName); err != nil { - return err - } if err := d.Set("comment", secret.Comment); err != nil { return err } @@ -96,17 +80,22 @@ func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, sec return nil } -func handleSecretUpdate(id sdk.SchemaObjectIdentifier, d *schema.ResourceData) *sdk.AlterSecretRequest { +type commonSecretSet struct { + comment *string +} + +type commonSecretUnset struct { + comment *bool +} + +func handleSecretUpdate(d *schema.ResourceData) (commonSecretSet, commonSecretUnset) { + set, unset := commonSecretSet{}, commonSecretUnset{} if d.HasChange("comment") { - comment := d.Get("comment").(string) - request := sdk.NewAlterSecretRequest(id) - if len(comment) == 0 { - unsetRequest := sdk.NewSecretUnsetRequest().WithComment(true) - return request.WithUnset(*unsetRequest) + if v, ok := d.GetOk("comment"); ok { + set.comment = sdk.Pointer(v.(string)) } else { - setRequest := sdk.NewSecretSetRequest().WithComment(comment) - return request.WithSet(*setRequest) + unset.comment = sdk.Pointer(true) } } - return nil + return set, unset } diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index 3297df3372..a1cc45c5aa 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -4,12 +4,13 @@ import ( "context" "errors" "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "reflect" ) var secretBasicAuthenticationSchema = func() map[string]*schema.Schema { @@ -17,12 +18,13 @@ var secretBasicAuthenticationSchema = func() map[string]*schema.Schema { "username": { Type: schema.TypeString, Required: true, - Description: "Specifies the username value to store in the secret when setting the TYPE value to PASSWORD.", + Description: "Specifies the username value to store in the secret.", }, "password": { Type: schema.TypeString, Required: true, - Description: "Specifies the password value to store in the secret when setting the TYPE value to PASSWORD.", + Sensitive: true, + Description: externalChangesNotDetectedFieldDescription("Specifies the password value to store in the secret."), }, } return helpers.MergeMaps(secretCommonSchema, secretBasicAuthentication) @@ -34,21 +36,43 @@ func SecretWithBasicAuthentication() *schema.Resource { ReadContext: ReadContextSecretWithBasicAuthentication, UpdateContext: UpdateContextSecretWithBasicAuthentication, DeleteContext: DeleteContextSecretWithBasicAuthentication, + Description: "Resource used to manage secret objects with Basic Authentication. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", Schema: secretBasicAuthenticationSchema, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: ImportSecretWithBasicAuthentication, }, - - Description: "Secret with Basic Authentication where Secrets Type attribute is set to PASSWORD.", } } -func CreateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ImportSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting secret with basic authentication import") client := meta.(*provider.Context).Client - commonCreate := handleSecretCreate(d) - id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return nil, err + } + + if err := handleSecretImport(d); err != nil { + return nil, err + } + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return nil, err + } + + if err := d.Set("username", secretDescription.Username); err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} + +func CreateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + databaseName, schemaName, name := handleSecretCreate(d) + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) usernameString := d.Get("username").(string) passwordString := d.Get("password").(string) @@ -68,7 +92,7 @@ func CreateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.R return ReadContextSecretWithBasicAuthentication(ctx, d, meta) } -func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -76,7 +100,7 @@ func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.Res } secret, err := client.Secrets.ShowByID(ctx, id) - if secret == nil || err != nil { + if err != nil { if errors.Is(err, sdk.ErrObjectNotFound) { d.SetId("") return diag.Diagnostics{ @@ -98,7 +122,7 @@ func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.Res if err != nil { return diag.FromErr(err) } - if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + if err = handleSecretRead(d, id, secret, secretDescription); err != nil { return diag.FromErr(err) } if err = d.Set("username", secretDescription.Username); err != nil { @@ -107,39 +131,44 @@ func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.Res return nil } -func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } + commonSet, commonUnset := handleSecretUpdate(d) + set := &sdk.SecretSetRequest{ + Comment: commonSet.comment, + } + unset := &sdk.SecretUnsetRequest{ + Comment: commonUnset.comment, + } + setForFlow := &sdk.SetForFlowRequest{ + SetForBasicAuthentication: &sdk.SetForBasicAuthenticationRequest{}, } if d.HasChange("username") { username := d.Get("username").(string) - - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForBasicAuthenticationRequest().WithUsername(username) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForBasicAuthentication(*setRequest)) - - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } + setForFlow.SetForBasicAuthentication.WithUsername(username) + set.WithSetForFlow(*setForFlow) } if d.HasChange("password") { password := d.Get("password").(string) + setForFlow.SetForBasicAuthentication.WithPassword(password) + set.WithSetForFlow(*setForFlow) + } - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForBasicAuthenticationRequest().WithPassword(password) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForBasicAuthentication(*setRequest)) + if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } - if err := client.Secrets.Alter(ctx, request); err != nil { + if !reflect.DeepEqual(*unset, sdk.SecretUnsetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithUnset(*unset)); err != nil { return diag.FromErr(err) } } @@ -147,14 +176,14 @@ func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.R return ReadContextSecretWithBasicAuthentication(ctx, d, meta) } -func DeleteContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_basic_authentication_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_acceptance_test.go index 9759c823bb..ead220d2fe 100644 --- a/pkg/resources/secret_with_basic_authentication_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_acceptance_test.go @@ -1,6 +1,7 @@ package resources_test import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" @@ -49,14 +50,33 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasUsernameString("foo"). HasPasswordString("foo"). HasCommentString(""), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasName(name). + HasDatabaseName(id.DatabaseName()). + HasSecretType("PASSWORD"). + HasSchemaName(id.SchemaName()). + HasComment(""), ), + + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.secret_type", "PASSWORD"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", "foo"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "0"), ), }, // set username, password and comment { Config: config.FromModel(t, secretModel. - WithUsername("bar"). WithPassword("bar"). + WithUsername("bar"). WithComment(comment), ), Check: assert.AssertThat(t, @@ -96,6 +116,20 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasCommentString(""), ), }, + // import with no fields set + { + ResourceName: secretModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "username", "bar"), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), + ), + }, // destroy { Config: config.FromModel(t, secretModel), diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 86e4ccda5f..86cbe2e800 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" + "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" @@ -17,6 +19,7 @@ var secretGenericStringSchema = func() map[string]*schema.Schema { "secret_string": { Type: schema.TypeString, Required: true, + Sensitive: true, Description: "Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", }, } @@ -29,20 +32,33 @@ func SecretWithGenericString() *schema.Resource { ReadContext: ReadContextSecretWithGenericString, UpdateContext: UpdateContextSecretWithGenericString, DeleteContext: DeleteContextSecretWithGenericString, + Description: "Resource used to manage secret objects with Generic String. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", Schema: secretGenericStringSchema, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: ImportSecretWithGenericString, }, - Description: "Secret with Generic string where Secrets Type attribute is set to GENERIC_STRING.", } } -func CreateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - commonCreate := handleSecretCreate(d) +func ImportSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting secret with generic string import") - id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) + _, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return nil, err + } + + if err := handleSecretImport(d); err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} + +func CreateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := sdk.NewSchemaObjectIdentifier(handleSecretCreate(d)) secretSting := d.Get("secret_string").(string) @@ -61,7 +77,7 @@ func CreateContextSecretWithGenericString(ctx context.Context, d *schema.Resourc return ReadContextSecretWithGenericString(ctx, d, meta) } -func ReadContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -69,7 +85,7 @@ func ReadContextSecretWithGenericString(ctx context.Context, d *schema.ResourceD } secret, err := client.Secrets.ShowByID(ctx, id) - if secret == nil || err != nil { + if err != nil { if errors.Is(err, sdk.ErrObjectNotFound) { d.SetId("") return diag.Diagnostics{ @@ -98,27 +114,35 @@ func ReadContextSecretWithGenericString(ctx context.Context, d *schema.ResourceD return nil } -func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } + commonSet, commonUnset := handleSecretUpdate(d) + set := &sdk.SecretSetRequest{ + Comment: commonSet.comment, + } + unset := &sdk.SecretUnsetRequest{ + Comment: commonUnset.comment, } if d.HasChange("secret_string") { secretString := d.Get("secret_string").(string) + req := sdk.NewSetForFlowRequest().WithSetForGenericString(*sdk.NewSetForGenericStringRequest().WithSecretString(secretString)) + set.WithSetForFlow(*req) + } - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForGenericStringRequest().WithSecretString(secretString) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForGenericString(*setRequest)) + if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } - if err := client.Secrets.Alter(ctx, request); err != nil { + if !reflect.DeepEqual(*unset, sdk.SecretUnsetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithUnset(*unset)); err != nil { return diag.FromErr(err) } } @@ -126,14 +150,14 @@ func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.Resourc return ReadContextSecretWithGenericString(ctx, d, meta) } -func DeleteContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_generic_string_acceptance_test.go b/pkg/resources/secret_with_generic_string_acceptance_test.go index 94223a20d3..92ef1cda96 100644 --- a/pkg/resources/secret_with_generic_string_acceptance_test.go +++ b/pkg/resources/secret_with_generic_string_acceptance_test.go @@ -1,6 +1,7 @@ package resources_test import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" @@ -45,8 +46,28 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). - HasSecretStringString("foo"), + HasSecretStringString("foo"). + HasCommentString(""), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasName(name). + HasDatabaseName(id.DatabaseName()). + HasSecretType("GENERIC_STRING"). + HasSchemaName(id.SchemaName()). + HasComment(""), ), + + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.secret_type", "GENERIC_STRING"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "0"), ), }, // set secret_string and comment @@ -90,6 +111,19 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { HasCommentString(""), ), }, + // import with no fields set + { + ResourceName: secretModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"secret_string"}, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "database", id.DatabaseId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "schema", id.SchemaId().Name()), + importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), + ), + }, // destroy { Config: config.FromModel(t, secretModel), diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 5b3d27d640..0d90c2af2c 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" @@ -19,6 +20,7 @@ var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { "oauth_refresh_token": { Type: schema.TypeString, Required: true, + Sensitive: true, Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", }, "oauth_refresh_token_expiry_time": { @@ -31,7 +33,8 @@ var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { Type: schema.TypeString, ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service.", + DiffSuppressFunc: suppressIdentifierQuoting, }, } return helpers.MergeMaps(secretCommonSchema, secretAuthorizationCodeGrant) @@ -43,56 +46,31 @@ func SecretWithAuthorizationCodeGrant() *schema.Resource { ReadContext: ReadContextSecretWithAuthorizationCodeGrant, UpdateContext: UpdateContextSecretWithAuthorizationCodeGrant, DeleteContext: DeleteContextSecretWithAuthorizationCodeGrant, + Description: "Resource used to manage secret objects with OAuth Authorization Code Grant. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", Schema: secretAuthorizationCodeGrantSchema, Importer: &schema.ResourceImporter{ StateContext: ImportSecretWithAuthorizationCodeGrant, }, - Description: "Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2.", CustomizeDiff: customdiff.All( - ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "oauth_refresh_token_expiry_time"), + ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "oauth_refresh_token_expiry_time", "api_authentication"), ), } } func ImportSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { logging.DebugLogger.Printf("[DEBUG] Starting secret with authorization code import") - client := meta.(*provider.Context).Client - - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return nil, err - } - - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return nil, err - } - - if err := d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { - return nil, err - } - if err := d.Set("api_authentication", secretDescription.IntegrationName); err != nil { - return nil, err - } - - secret, err := client.Secrets.ShowByID(ctx, id) - if err != nil { + if err := handleSecretImport(d); err != nil { return nil, err } - if err := handleSecretRead(d, id, secret, secretDescription); err != nil { - return nil, err - } return []*schema.ResourceData{d}, nil } -func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - commonCreate := handleSecretCreate(d) - - id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) + id := sdk.NewSchemaObjectIdentifier(handleSecretCreate(d)) apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) @@ -118,7 +96,7 @@ func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schem return ReadContextSecretWithAuthorizationCodeGrant(ctx, d, meta) } -func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { @@ -126,7 +104,7 @@ func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema. } secret, err := client.Secrets.ShowByID(ctx, id) - if secret == nil || err != nil { + if err != nil { if errors.Is(err, sdk.ErrObjectNotFound) { d.SetId("") return diag.Diagnostics{ @@ -152,6 +130,7 @@ func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema. if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { return diag.FromErr(err) } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } @@ -162,39 +141,43 @@ func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema. return nil } -func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } + commonSet, commonUnset := handleSecretUpdate(d) + set := &sdk.SecretSetRequest{ + Comment: commonSet.comment, + SetForFlow: &sdk.SetForFlowRequest{ + SetForOAuthAuthorization: &sdk.SetForOAuthAuthorizationRequest{}, + }, + } + + unset := &sdk.SecretUnsetRequest{ + Comment: commonUnset.comment, } if d.HasChange("oauth_refresh_token") { refreshToken := d.Get("oauth_refresh_token").(string) - - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshToken(refreshToken) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) - - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } + set.SetForFlow.SetForOAuthAuthorization.WithOauthRefreshToken(refreshToken) } if d.HasChange("oauth_refresh_token_expiry_time") { refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) + set.SetForFlow.SetForOAuthAuthorization.WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + } - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthAuthorizationFlowRequest().WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthAuthorizationFlow(*setRequest)) + if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } - if err := client.Secrets.Alter(ctx, request); err != nil { + if !reflect.DeepEqual(*unset, sdk.SecretUnsetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithUnset(*unset)); err != nil { return diag.FromErr(err) } } @@ -202,14 +185,14 @@ func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schem return ReadContextSecretWithAuthorizationCodeGrant(ctx, d, meta) } -func DeleteContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index ba4dc12b9c..0e68126eb3 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -2,6 +2,7 @@ package resources_test import ( "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" "time" @@ -60,8 +61,26 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasOauthRefreshTokenString(refreshToken). HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateTime). HasCommentString(comment), - assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasName(name). + HasDatabaseName(id.DatabaseName()). + HasSecretType("OAUTH2"). + HasSchemaName(id.SchemaName()). + HasComment(comment), ), + + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.secret_type", "OAUTH2"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", integrationId.Name()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "0"), ), }, // set all diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index 9ce190b73f..2deff9370f 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" + "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" @@ -18,7 +20,8 @@ var secretClientCredentialsSchema = func() map[string]*schema.Schema { Type: schema.TypeString, ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), Required: true, - Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2.", + Description: "Specifies the name value of the Snowflake security integration that connects Snowflake to an external service.", + DiffSuppressFunc: suppressIdentifierQuoting, }, "oauth_scopes": { Type: schema.TypeSet, @@ -33,21 +36,48 @@ var secretClientCredentialsSchema = func() map[string]*schema.Schema { func SecretWithClientCredentials() *schema.Resource { return &schema.Resource{ CreateContext: CreateContextSecretWithClientCredentials, - ReadContext: ReadContextSecretWithClientCredentials, + ReadContext: ReadContextSecretWithClientCredentials(true), UpdateContext: UpdateContextSecretWithClientCredentials, DeleteContext: DeleteContextSecretWithClientCredentials, + Description: "Resource used to manage secret objects with OAuth Client Credentials. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", Schema: secretClientCredentialsSchema, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: ImportSecretWithClientCredentials, }, - Description: "Secret with OAuth Client Credentials where Secrets Type attribute is set to OAUTH2.", } } -func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ImportSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting secret with client credentials import") client := meta.(*provider.Context).Client - commonCreate := handleSecretCreate(d) + + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return nil, err + } + + if err := handleSecretImport(d); err != nil { + return nil, err + } + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return nil, err + } + + if err := d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return nil, err + } + + if err := d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} +func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := sdk.NewSchemaObjectIdentifier(handleSecretCreate(d)) apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) @@ -55,18 +85,17 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res return diag.FromErr(err) } - id := sdk.NewSchemaObjectIdentifier(commonCreate.database, commonCreate.schema, commonCreate.name) + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration) stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) oauthScopes := make([]sdk.ApiIntegrationScope, len(stringScopes)) for i, scope := range stringScopes { oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} } + request.WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) - request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, apiIntegration).WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) - - if commonCreate.comment != nil { - request.WithComment(*commonCreate.comment) + if v, ok := d.GetOk("comment"); ok { + request.WithComment(v.(string)) } err = client.Secrets.CreateWithOAuthClientCredentialsFlow(ctx, request) @@ -76,62 +105,79 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadContextSecretWithClientCredentials(ctx, d, meta) + return ReadContextSecretWithClientCredentials(false)(ctx, d, meta) } -func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } +func ReadContextSecretWithClientCredentials(withExternalChangesMarking bool) schema.ReadContextFunc { + return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } - secret, err := client.Secrets.ShowByID(ctx, id) - if secret == nil || err != nil { - if errors.Is(err, sdk.ErrObjectNotFound) { - d.SetId("") + secret, err := client.Secrets.ShowByID(ctx, id) + if err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + }, + } + } return diag.Diagnostics{ diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + Severity: diag.Error, + Summary: "Failed to retrieve secret.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), }, } } - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), - }, + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) } + + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } + + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } + + if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { + return diag.FromErr(err) + } + + if withExternalChangesMarking { + if err = handleExternalChangesToObjectInShow(d, + showMapping{"oauth_scopes", "oauth_scopes", secretDescription.OauthScopes, secretDescription.OauthScopes, nil}, + showMapping{"comment", "comment", secretDescription.Comment, secretDescription.Comment, nil}, + ); err != nil { + return diag.FromErr(err) + } + } + + return nil } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - if err := handleSecretRead(d, id, secret, secretDescription); err != nil { - return diag.FromErr(err) - } - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { - return diag.FromErr(err) - } - if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { - return diag.FromErr(err) - } - return nil } -func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if request := handleSecretUpdate(id, d); request != nil { - if err := client.Secrets.Alter(ctx, request); err != nil { - return diag.FromErr(err) - } + commonSet, commonUnset := handleSecretUpdate(d) + set := &sdk.SecretSetRequest{ + Comment: commonSet.comment, + } + unset := &sdk.SecretUnsetRequest{ + Comment: commonUnset.comment, } if d.HasChange("oauth_scopes") { @@ -140,27 +186,33 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res for i, scope := range stringScopes { oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} } + req := sdk.NewSetForOAuthClientCredentialsRequest().WithOauthScopes(*sdk.NewOauthScopesListRequest(oauthScopes)) + set.WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthClientCredentials(*req)) + } - request := sdk.NewAlterSecretRequest(id) - setRequest := sdk.NewSetForOAuthClientCredentialsFlowRequest().WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) - request.WithSet(*sdk.NewSecretSetRequest().WithSetForOAuthClientCredentialsFlow(*setRequest)) + if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } - if err := client.Secrets.Alter(ctx, request); err != nil { + if !reflect.DeepEqual(*unset, sdk.SecretUnsetRequest{}) { + if err := client.Secrets.Alter(ctx, sdk.NewAlterSecretRequest(id).WithUnset(*unset)); err != nil { return diag.FromErr(err) } } - return ReadContextSecretWithClientCredentials(ctx, d, meta) + return ReadContextSecretWithClientCredentials(false)(ctx, d, meta) } -func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err != nil { return diag.FromErr(err) } - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil { + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go index e6094f0112..206b818e6e 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go @@ -1,6 +1,7 @@ package resources_test import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" @@ -30,7 +31,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). - WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}, {Scope: "test"}}), ) t.Cleanup(apiIntegrationCleanup) @@ -54,27 +55,84 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). + HasOauthScopesLength(len([]string{"foo", "bar"})). HasCommentString(comment), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasName(name). + HasDatabaseName(id.DatabaseName()). + HasSecretType("OAUTH2"). + HasSchemaName(id.SchemaName()), ), resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "2"), resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo"), resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "bar"), + + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", integrationId.Name()), + resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.*", "bar"), ), }, // set oauth_scopes and comment in config { Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{"foo"}). + WithOauthScopes([]string{"test"}). WithComment(newComment)), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar foo]"), sdk.String("[test]")), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthScopesLength(len([]string{"test"})). + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "test")), + ), + }, + // set oauth_scopes and comment externally + { + PreConfig: func() { + req := sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). + WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthClientCredentials( + *sdk.NewSetForOAuthClientCredentialsRequest().WithOauthScopes( + *sdk.NewOauthScopesListRequest([]sdk.ApiIntegrationScope{{Scope: "bar"}}), + ), + )), + ) + acc.TestClient().Secret.Alter(t, req) + }, + Config: config.FromModel(t, secretModel.WithOauthScopes([]string{"foo"})), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectDrift(secretModel.ResourceReference(), "oauth_scopes", sdk.String("[test]"), sdk.String("[bar]")), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar]"), sdk.String("[foo]")), + }, + }, Check: assert.AssertThat(t, resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). + HasOauthScopesLength(len([]string{"foo"})). HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo")), + assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo")), ), }, // unset comment @@ -90,6 +148,23 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasCommentString(""), ), }, + // unset comment externally + { + PreConfig: func() { + req := sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest().WithComment("aaa")) + acc.TestClient().Secret.Alter(t, req) + }, + Config: config.FromModel(t, secretModelWithoutComment), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("aaa"), nil), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). + HasCommentString(""), + ), + }, // destroy { Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), @@ -105,6 +180,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). + HasOauthScopesLength(len([]string{"foo", "bar"})). HasCommentString(""), ), resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), diff --git a/pkg/schemas/secret.go b/pkg/schemas/secret.go index 9b5158d95c..2ba91ef9fa 100644 --- a/pkg/schemas/secret.go +++ b/pkg/schemas/secret.go @@ -37,17 +37,14 @@ var DescribeSecretSchema = map[string]*schema.Schema{ }, "username": { Type: schema.TypeString, - Optional: true, Computed: true, }, "oauth_access_token_expiry_time": { Type: schema.TypeString, - Optional: true, Computed: true, }, "oauth_refresh_token_expiry_time": { Type: schema.TypeString, - Optional: true, Computed: true, }, "oauth_scopes": { @@ -57,13 +54,13 @@ var DescribeSecretSchema = map[string]*schema.Schema{ }, "integration_name": { Type: schema.TypeString, - Optional: true, Computed: true, }, } func SecretDescriptionToSchema(details sdk.SecretDetails) map[string]any { s := map[string]any{ + "created_on": details.CreatedOn.String(), "name": details.Name, "database_name": details.DatabaseName, "schema_name": details.SchemaName, From e098f695e95dbfca88c0145a8b7eaf1f5438d86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 9 Oct 2024 10:45:39 +0200 Subject: [PATCH 59/65] comments resolved --- MIGRATION_GUIDE.md | 12 +++ .../secret_with_authorization_code_grant.md | 23 +++-- .../secret_with_basic_authentication.md | 22 +++-- .../secret_with_client_credentials.md | 20 +++-- docs/resources/secret_with_generic_string.md | 19 ++-- .../resource.tf | 10 +++ .../resource.tf | 9 ++ .../resource.tf | 9 ++ .../resource.tf | 8 ++ ...et_with_client_credentials_resource_ext.go | 11 +++ .../secret_with_basic_authentication.go | 5 +- ...th_basic_authentication_acceptance_test.go | 65 +++++++++++++- pkg/resources/secret_with_generic_string.go | 12 +-- ...ret_with_generic_string_acceptance_test.go | 23 +++++ ...ret_with_oauth_authorization_code_grant.go | 30 +++++-- ...uthorization_code_grant_acceptance_test.go | 57 ++++++++++++ .../secret_with_oauth_client_credentials.go | 87 +++++++++---------- ...auth_client_credentials_acceptance_test.go | 5 ++ ...cret_with_authorization_code_grant.md.tmpl | 35 ++++++++ .../secret_with_basic_authentication.md.tmpl | 35 ++++++++ .../secret_with_client_credentials.md.tmpl | 35 ++++++++ .../secret_with_generic_string.md.tmpl | 35 ++++++++ 22 files changed, 478 insertions(+), 89 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go create mode 100644 templates/resources/secret_with_authorization_code_grant.md.tmpl create mode 100644 templates/resources/secret_with_basic_authentication.md.tmpl create mode 100644 templates/resources/secret_with_client_credentials.md.tmpl create mode 100644 templates/resources/secret_with_generic_string.md.tmpl diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index d4d899b091..ae69fad1da 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -7,6 +7,18 @@ across different versions. > [!TIP] > We highly recommend upgrading the versions one by one instead of bulk upgrades. +## v0.96.0 âžž v0.97.0 + +### *(new feature)* Secret resources +Added a new secrets resources for managing secrets. +We decided to split each secret flow into individual resources, i.e.: +- `snowflake_secret_with_client_credentials` +- `snowflake_secret_with_authorization_code_grant` +- `snowflake_secret_with_basic_authentication` +- `snowflake_secret_with_generic_string` + +See reference [docs](https://docs.snowflake.com/en/sql-reference/sql/create-secret). + ## v0.95.0 âžž v0.96.0 ### snowflake_masking_policies data source changes diff --git a/docs/resources/secret_with_authorization_code_grant.md b/docs/resources/secret_with_authorization_code_grant.md index 0b4a0e3dfd..16e8aaa234 100644 --- a/docs/resources/secret_with_authorization_code_grant.md +++ b/docs/resources/secret_with_authorization_code_grant.md @@ -2,17 +2,29 @@ page_title: "snowflake_secret_with_authorization_code_grant Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2. + Resource used to manage secret objects with OAuth Authorization Code Grant. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + # snowflake_secret_with_authorization_code_grant (Resource) -Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2. +Resource used to manage secret objects with OAuth Authorization Code Grant. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret). ## Example Usage ```terraform # basic resource +resource "snowflake_secret_with_authorization_code_grant" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_refresh_token = "EXAMPLE_TOKEN" + oauth_refresh_token_expiry_time = "2025-01-02 15:04:01" +} + +# resource with all fields set resource "snowflake_secret_with_authorization_code_grant" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" @@ -23,7 +35,6 @@ resource "snowflake_secret_with_authorization_code_grant" "test" { comment = "EXAMPLE_COMMENT" } ``` - -> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). @@ -32,10 +43,10 @@ resource "snowflake_secret_with_authorization_code_grant" "test" { ### Required -- `api_authentication` (String) Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2. +- `api_authentication` (String) Specifies the name value of the Snowflake security integration that connects Snowflake to an external service. - `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` -- `oauth_refresh_token` (String) Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires. +- `oauth_refresh_token` (String, Sensitive) Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource manually using "terraform taint". - `oauth_refresh_token_expiry_time` (String) Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI - `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` @@ -48,7 +59,7 @@ resource "snowflake_secret_with_authorization_code_grant" "test" { - `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) - `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). - `id` (String) The ID of this resource. -- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) +- `show_output` (List of Object) Outputs the result of `SHOW SECRETS` for the given secret. (see [below for nested schema](#nestedatt--show_output)) ### Nested Schema for `describe_output` diff --git a/docs/resources/secret_with_basic_authentication.md b/docs/resources/secret_with_basic_authentication.md index d97e14e50e..c90c072dad 100644 --- a/docs/resources/secret_with_basic_authentication.md +++ b/docs/resources/secret_with_basic_authentication.md @@ -2,17 +2,28 @@ page_title: "snowflake_secret_with_basic_authentication Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with Basic Authentication where Secrets Type attribute is set to PASSWORD. + Resource used to manage secret objects with Basic Authentication. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + # snowflake_secret_with_basic_authentication (Resource) -Secret with Basic Authentication where Secrets Type attribute is set to PASSWORD. +Resource used to manage secret objects with Basic Authentication. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret). ## Example Usage ```terraform # basic resource +resource "snowflake_secret_with_basic_authentication" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + username = "EXAMPLE_USERNAME" + password = "EXAMPLE_PASSWORD" +} + +# resource with all fields set resource "snowflake_secret_with_basic_authentication" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" @@ -22,7 +33,6 @@ resource "snowflake_secret_with_basic_authentication" "test" { comment = "EXAMPLE_COMMENT" } ``` - -> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). @@ -33,9 +43,9 @@ resource "snowflake_secret_with_basic_authentication" "test" { - `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` -- `password` (String) Specifies the password value to store in the secret when setting the TYPE value to PASSWORD. +- `password` (String, Sensitive) Specifies the password value to store in the secret. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource manually using "terraform taint". - `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` -- `username` (String) Specifies the username value to store in the secret when setting the TYPE value to PASSWORD. +- `username` (String) Specifies the username value to store in the secret. ### Optional @@ -46,7 +56,7 @@ resource "snowflake_secret_with_basic_authentication" "test" { - `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) - `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). - `id` (String) The ID of this resource. -- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) +- `show_output` (List of Object) Outputs the result of `SHOW SECRETS` for the given secret. (see [below for nested schema](#nestedatt--show_output)) ### Nested Schema for `describe_output` diff --git a/docs/resources/secret_with_client_credentials.md b/docs/resources/secret_with_client_credentials.md index eae8cd9aa7..3cd019f814 100644 --- a/docs/resources/secret_with_client_credentials.md +++ b/docs/resources/secret_with_client_credentials.md @@ -2,17 +2,28 @@ page_title: "snowflake_secret_with_client_credentials Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with OAuth Client Credentials where Secrets Type attribute is set to OAUTH2. + Resource used to manage secret objects with OAuth Client Credentials. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + # snowflake_secret_with_client_credentials (Resource) -Secret with OAuth Client Credentials where Secrets Type attribute is set to OAUTH2. +Resource used to manage secret objects with OAuth Client Credentials. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret). ## Example Usage ```terraform # basic resource +resource "snowflake_secret_with_client_credentials" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_scopes = ["useraccount", "testscope"] +} + +# resource with all fields set resource "snowflake_secret_with_client_credentials" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" @@ -22,7 +33,6 @@ resource "snowflake_secret_with_client_credentials" "test" { comment = "EXAMPLE_COMMENT" } ``` - -> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). @@ -31,7 +41,7 @@ resource "snowflake_secret_with_client_credentials" "test" { ### Required -- `api_authentication` (String) Specifies the name value of the Snowflake security integration that connects Snowflake to an external service when setting Type to OAUTH2. +- `api_authentication` (String) Specifies the name value of the Snowflake security integration that connects Snowflake to an external service. - `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `oauth_scopes` (Set of String) Specifies a list of scopes to use when making a request from the OAuth server by a role with USAGE on the integration during the OAuth client credentials flow. @@ -46,7 +56,7 @@ resource "snowflake_secret_with_client_credentials" "test" { - `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) - `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). - `id` (String) The ID of this resource. -- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) +- `show_output` (List of Object) Outputs the result of `SHOW SECRETS` for the given secret. (see [below for nested schema](#nestedatt--show_output)) ### Nested Schema for `describe_output` diff --git a/docs/resources/secret_with_generic_string.md b/docs/resources/secret_with_generic_string.md index 53adccb675..a75c599141 100644 --- a/docs/resources/secret_with_generic_string.md +++ b/docs/resources/secret_with_generic_string.md @@ -2,17 +2,27 @@ page_title: "snowflake_secret_with_generic_string Resource - terraform-provider-snowflake" subcategory: "" description: |- - Secret with Generic string where Secrets Type attribute is set to GENERIC_STRING. + Resource used to manage secret objects with Generic String. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + # snowflake_secret_with_generic_string (Resource) -Secret with Generic string where Secrets Type attribute is set to GENERIC_STRING. +Resource used to manage secret objects with Generic String. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret). ## Example Usage ```terraform # basic resource +resource "snowflake_secret_with_generic_string" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + secret_string = "EXAMPLE_SECRET_STRING" +} + +# resource with all fields set resource "snowflake_secret_with_generic_string" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" @@ -21,7 +31,6 @@ resource "snowflake_secret_with_generic_string" "test" { comment = "EXAMPLE_COMMENT" } ``` - -> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). @@ -33,7 +42,7 @@ resource "snowflake_secret_with_generic_string" "test" { - `database` (String) The database in which to create the secret Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `name` (String) String that specifies the identifier (i.e. name) for the secret, must be unique in your schema. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` - `schema` (String) The schema in which to create the secret. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"` -- `secret_string` (String) Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases. +- `secret_string` (String, Sensitive) Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource manually using "terraform taint". ### Optional @@ -44,7 +53,7 @@ resource "snowflake_secret_with_generic_string" "test" { - `describe_output` (List of Object) Outputs the result of `DESCRIBE SECRET` for the given secret. (see [below for nested schema](#nestedatt--describe_output)) - `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). - `id` (String) The ID of this resource. -- `show_output` (List of Object) Outputs the result of `SHOW SECRET` for the given secret. (see [below for nested schema](#nestedatt--show_output)) +- `show_output` (List of Object) Outputs the result of `SHOW SECRETS` for the given secret. (see [below for nested schema](#nestedatt--show_output)) ### Nested Schema for `describe_output` diff --git a/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf b/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf index cd15032502..bb45a36e87 100644 --- a/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf +++ b/examples/resources/snowflake_secret_with_authorization_code_grant/resource.tf @@ -1,4 +1,14 @@ # basic resource +resource "snowflake_secret_with_authorization_code_grant" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_refresh_token = "EXAMPLE_TOKEN" + oauth_refresh_token_expiry_time = "2025-01-02 15:04:01" +} + +# resource with all fields set resource "snowflake_secret_with_authorization_code_grant" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" diff --git a/examples/resources/snowflake_secret_with_basic_authentication/resource.tf b/examples/resources/snowflake_secret_with_basic_authentication/resource.tf index 1685bd003c..df60aa83b5 100644 --- a/examples/resources/snowflake_secret_with_basic_authentication/resource.tf +++ b/examples/resources/snowflake_secret_with_basic_authentication/resource.tf @@ -1,4 +1,13 @@ # basic resource +resource "snowflake_secret_with_basic_authentication" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + username = "EXAMPLE_USERNAME" + password = "EXAMPLE_PASSWORD" +} + +# resource with all fields set resource "snowflake_secret_with_basic_authentication" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" diff --git a/examples/resources/snowflake_secret_with_client_credentials/resource.tf b/examples/resources/snowflake_secret_with_client_credentials/resource.tf index dcea57a1fc..baaf605e67 100644 --- a/examples/resources/snowflake_secret_with_client_credentials/resource.tf +++ b/examples/resources/snowflake_secret_with_client_credentials/resource.tf @@ -1,4 +1,13 @@ # basic resource +resource "snowflake_secret_with_client_credentials" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME" + oauth_scopes = ["useraccount", "testscope"] +} + +# resource with all fields set resource "snowflake_secret_with_client_credentials" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" diff --git a/examples/resources/snowflake_secret_with_generic_string/resource.tf b/examples/resources/snowflake_secret_with_generic_string/resource.tf index 96a0a07831..fab00a4340 100644 --- a/examples/resources/snowflake_secret_with_generic_string/resource.tf +++ b/examples/resources/snowflake_secret_with_generic_string/resource.tf @@ -1,4 +1,12 @@ # basic resource +resource "snowflake_secret_with_generic_string" "test" { + name = "EXAMPLE_SECRET" + database = "EXAMPLE_DB" + schema = "EXAMPLE_SCHEMA" + secret_string = "EXAMPLE_SECRET_STRING" +} + +# resource with all fields set resource "snowflake_secret_with_generic_string" "test" { name = "EXAMPLE_SECRET" database = "EXAMPLE_DB" diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go new file mode 100644 index 0000000000..e40570cdb2 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go @@ -0,0 +1,11 @@ +package resourceassert + +import ( + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +func (s *SecretWithClientCredentialsResourceAssert) HasOauthScopesLength(len int) *SecretWithClientCredentialsResourceAssert { + s.AddAssertion(assert.ValueSet("oauth_scopes.#", fmt.Sprintf("%d", len))) + return s +} diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index a1cc45c5aa..fd9fcc7482 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -106,7 +106,8 @@ func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.Res return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + Summary: "Failed to retrieve secret with basic authentication. Target object not found. Marking the resource as removed.", + Detail: fmt.Sprintf("Secret with basic authentication name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } @@ -114,7 +115,7 @@ func ReadContextSecretWithBasicAuthentication(ctx context.Context, d *schema.Res diag.Diagnostic{ Severity: diag.Error, Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + Detail: fmt.Sprintf("Secret with basic authentication name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } diff --git a/pkg/resources/secret_with_basic_authentication_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_acceptance_test.go index ead220d2fe..745315dce7 100644 --- a/pkg/resources/secret_with_basic_authentication_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_acceptance_test.go @@ -89,6 +89,35 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasCommentString(comment), ), }, + // set username and comment externally + { + PreConfig: func() { + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). + WithComment("test_comment"). + WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForBasicAuthentication(*sdk.NewSetForBasicAuthenticationRequest().WithUsername("test_username"))), + )) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectDrift(secretModel.ResourceReference(), "comment", sdk.String(comment), sdk.String("test_comment")), + planchecks.ExpectDrift(secretModel.ResourceReference(), "username", sdk.String("bar"), sdk.String("test_username")), + + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("test_comment"), sdk.String(comment)), + planchecks.ExpectChange(secretModel.ResourceReference(), "username", tfjson.ActionUpdate, sdk.String("test_username"), sdk.String("bar")), + }, + }, + Config: config.FromModel(t, secretModel), + Check: assert.AssertThat(t, + resourceassert.SecretWithBasicAuthenticationResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasUsernameString("bar"). + HasPasswordString("bar"). + HasCommentString(comment), + ), + }, // import { ResourceName: secretModel.ResourceReference(), @@ -108,6 +137,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModelWithoutComment), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), }, }, @@ -130,12 +160,39 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), ), }, - // destroy + // create with empty username and password { - Config: config.FromModel(t, secretModel), - Destroy: true, + Config: config.FromModel(t, secretModelEmptyCredentials), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithBasicAuthenticationResource(t, secretModelEmptyCredentials.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasUsernameString(""). + HasPasswordString(""). + HasCommentString(""), + ), + ), }, - // create with empty username and password + }, + }) +} + +func TestAcc_SecretWithBasicAuthentication_EmptyCredentials(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + secretModelEmptyCredentials := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "", id.SchemaName(), "") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithBasicAuthentication), + Steps: []resource.TestStep{ + { Config: config.FromModel(t, secretModelEmptyCredentials), Check: resource.ComposeTestCheckFunc( diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 86cbe2e800..87f2f2aa14 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -20,7 +20,7 @@ var secretGenericStringSchema = func() map[string]*schema.Schema { Type: schema.TypeString, Required: true, Sensitive: true, - Description: "Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases.", + Description: externalChangesNotDetectedFieldDescription("Specifies the string to store in the secret. The string can be an API token or a string of sensitive value that can be used in the handler code of a UDF or stored procedure. For details, see [Creating and using an external access integration](https://docs.snowflake.com/en/developer-guide/external-network-access/creating-using-external-network-access). You should not use this property to store any kind of OAuth token; use one of the other secret types for your OAuth use cases."), }, } return helpers.MergeMaps(secretCommonSchema, secretGenericString) @@ -58,7 +58,8 @@ func ImportSecretWithGenericString(ctx context.Context, d *schema.ResourceData, func CreateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - id := sdk.NewSchemaObjectIdentifier(handleSecretCreate(d)) + databaseName, schemaName, name := handleSecretCreate(d) + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) secretSting := d.Get("secret_string").(string) @@ -91,15 +92,16 @@ func ReadContextSecretWithGenericString(ctx context.Context, d *schema.ResourceD return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + Summary: "Failed to retrieve secret with generic string. Target object not found. Marking the resource as removed.", + Detail: fmt.Sprintf("Secret with generic string name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, - Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + Summary: "Failed to retrieve secret generic string.", + Detail: fmt.Sprintf("Secret with generic string name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } diff --git a/pkg/resources/secret_with_generic_string_acceptance_test.go b/pkg/resources/secret_with_generic_string_acceptance_test.go index 92ef1cda96..f4f6553cb7 100644 --- a/pkg/resources/secret_with_generic_string_acceptance_test.go +++ b/pkg/resources/secret_with_generic_string_acceptance_test.go @@ -85,6 +85,28 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { HasCommentString(comment), ), }, + // set comment externally, external changes for secret_string are not being detected + { + PreConfig: func() { + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest().WithComment("test_comment"))) + }, + Config: config.FromModel(t, secretModel), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectDrift(secretModel.ResourceReference(), "comment", sdk.String(comment), sdk.String("test_comment")), + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("test_comment"), sdk.String(comment)), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithGenericStringResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasSecretStringString("bar"). + HasCommentString(comment), + ), + }, // import { ResourceName: secretModel.ResourceReference(), @@ -103,6 +125,7 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModelEmptySecretString), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), }, }, diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 0d90c2af2c..dffe730426 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -21,12 +21,12 @@ var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { Type: schema.TypeString, Required: true, Sensitive: true, - Description: "Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires.", + Description: externalChangesNotDetectedFieldDescription("Specifies the token as a string that is used to obtain a new access token from the OAuth authorization server when the access token expires."), }, "oauth_refresh_token_expiry_time": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("oauth_refresh_token_expiry_time"), + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInDescribe("oauth_refresh_token_expiry_time"), Description: "Specifies the timestamp as a string when the OAuth refresh token expires. Accepted string formats: YYYY-MM-DD, YYYY-MM-DD HH:MI, YYYY-MM-DD HH:MI:SS, YYYY-MM-DD HH:MI ", }, "api_authentication": { @@ -61,16 +61,28 @@ func SecretWithAuthorizationCodeGrant() *schema.Resource { func ImportSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { logging.DebugLogger.Printf("[DEBUG] Starting secret with authorization code import") + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) if err := handleSecretImport(d); err != nil { return nil, err } + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return nil, err + } + + if err = d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil { + return nil, err + } + return []*schema.ResourceData{d}, nil } func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - id := sdk.NewSchemaObjectIdentifier(handleSecretCreate(d)) + databaseName, schemaName, name := handleSecretCreate(d) + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) @@ -110,15 +122,16 @@ func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema. return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", + Summary: "Failed to retrieve secret with authorization code grant. Target object not found. Marking the resource as removed.", + Detail: fmt.Sprintf("Secret with authorization code grant name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, - Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + Summary: "Failed to retrieve secret with authorization code grant.", + Detail: fmt.Sprintf("Secret with authorization code grant name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } @@ -127,13 +140,14 @@ func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema. return diag.FromErr(err) } - if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { return diag.FromErr(err) } - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { return diag.FromErr(err) } + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index 0e68126eb3..bb7874e794 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -92,6 +92,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { ), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String(refreshToken), sdk.String(newRefreshToken)), planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryDateTime), sdk.String(newRefreshTokenExpiryDateOnly)), @@ -111,6 +112,62 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { ), ), }, + // set comment externally + { + PreConfig: func() { + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). + WithComment("secret resource - changed comment"), + )) + }, + Config: config.FromModel(t, secretModel), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String(newComment)), + }, + }, + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString(newRefreshToken). + HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), + ), + }, + // set refresh_token_expiry_time externally + { + PreConfig: func() { + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). + WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(24 * time.Hour).Format(time.DateTime)))), + )) + }, + Config: config.FromModel(t, secretModel), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectComputed(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", true), + }, + }, + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString(newRefreshToken). + HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), + ), + }, // import { ResourceName: secretModel.ResourceReference(), diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index 2deff9370f..6ea8521a44 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -36,7 +36,7 @@ var secretClientCredentialsSchema = func() map[string]*schema.Schema { func SecretWithClientCredentials() *schema.Resource { return &schema.Resource{ CreateContext: CreateContextSecretWithClientCredentials, - ReadContext: ReadContextSecretWithClientCredentials(true), + ReadContext: ReadContextSecretWithClientCredentials, UpdateContext: UpdateContextSecretWithClientCredentials, DeleteContext: DeleteContextSecretWithClientCredentials, Description: "Resource used to manage secret objects with OAuth Client Credentials. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", @@ -77,7 +77,8 @@ func ImportSecretWithClientCredentials(ctx context.Context, d *schema.ResourceDa } func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - id := sdk.NewSchemaObjectIdentifier(handleSecretCreate(d)) + databaseName, schemaName, name := handleSecretCreate(d) + id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) apiIntegrationString := d.Get("api_authentication").(string) apiIntegration, err := sdk.ParseAccountObjectIdentifier(apiIntegrationString) @@ -105,64 +106,54 @@ func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadContextSecretWithClientCredentials(false)(ctx, d, meta) + return ReadContextSecretWithClientCredentials(ctx, d, meta) } -func ReadContextSecretWithClientCredentials(withExternalChangesMarking bool) schema.ReadContextFunc { - return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } +func ReadContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } - secret, err := client.Secrets.ShowByID(ctx, id) - if err != nil { - if errors.Is(err, sdk.ErrObjectNotFound) { - d.SetId("") - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.", - }, - } - } + secret, err := client.Secrets.ShowByID(ctx, id) + if err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") return diag.Diagnostics{ diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to retrieve secret.", - Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + Severity: diag.Warning, + Summary: "Failed to retrieve secret with client credentials. Target object not found. Marking the resource as removed.", + Detail: fmt.Sprintf("Secret with client credentials name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - - if err := handleSecretRead(d, id, secret, secretDescription); err != nil { - return diag.FromErr(err) - } - - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { - return diag.FromErr(err) + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to retrieve secret with client credentials.", + Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), + }, } + } + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } - if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { - return diag.FromErr(err) - } + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } - if withExternalChangesMarking { - if err = handleExternalChangesToObjectInShow(d, - showMapping{"oauth_scopes", "oauth_scopes", secretDescription.OauthScopes, secretDescription.OauthScopes, nil}, - showMapping{"comment", "comment", secretDescription.Comment, secretDescription.Comment, nil}, - ); err != nil { - return diag.FromErr(err) - } - } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } - return nil + if err = d.Set("oauth_scopes", secretDescription.OauthScopes); err != nil { + return diag.FromErr(err) } + + return nil } func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { @@ -202,7 +193,7 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res } } - return ReadContextSecretWithClientCredentials(false)(ctx, d, meta) + return ReadContextSecretWithClientCredentials(ctx, d, meta) } func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { diff --git a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go index 206b818e6e..ae1089cbb3 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go @@ -89,6 +89,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { WithComment(newComment)), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar foo]"), sdk.String("[test]")), }, }, @@ -119,6 +120,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModel.WithOauthScopes([]string{"foo"})), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectDrift(secretModel.ResourceReference(), "oauth_scopes", sdk.String("[test]"), sdk.String("[bar]")), planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar]"), sdk.String("[foo]")), }, @@ -140,6 +142,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(newComment), nil), }, }, @@ -157,6 +160,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModelWithoutComment), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("aaa"), nil), }, }, @@ -254,6 +258,7 @@ func TestAcc_SecretWithClientCredentials_EmptyScopesList(t *testing.T) { ), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[]"), sdk.String("[foo]")), }, }, diff --git a/templates/resources/secret_with_authorization_code_grant.md.tmpl b/templates/resources/secret_with_authorization_code_grant.md.tmpl new file mode 100644 index 0000000000..6189ef94ee --- /dev/null +++ b/templates/resources/secret_with_authorization_code_grant.md.tmpl @@ -0,0 +1,35 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} diff --git a/templates/resources/secret_with_basic_authentication.md.tmpl b/templates/resources/secret_with_basic_authentication.md.tmpl new file mode 100644 index 0000000000..6189ef94ee --- /dev/null +++ b/templates/resources/secret_with_basic_authentication.md.tmpl @@ -0,0 +1,35 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} diff --git a/templates/resources/secret_with_client_credentials.md.tmpl b/templates/resources/secret_with_client_credentials.md.tmpl new file mode 100644 index 0000000000..6189ef94ee --- /dev/null +++ b/templates/resources/secret_with_client_credentials.md.tmpl @@ -0,0 +1,35 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} diff --git a/templates/resources/secret_with_generic_string.md.tmpl b/templates/resources/secret_with_generic_string.md.tmpl new file mode 100644 index 0000000000..6189ef94ee --- /dev/null +++ b/templates/resources/secret_with_generic_string.md.tmpl @@ -0,0 +1,35 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} From d5b6db064fb3565746fce3893a0937f638f9e07b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Wed, 9 Oct 2024 15:29:47 +0200 Subject: [PATCH 60/65] added tests to check for external changes to refresh_token_expiry_time and updated MIGRATION_GUIDE.md --- MIGRATION_GUIDE.md | 2 - ...uthorization_code_grant_acceptance_test.go | 121 +++++++++++++----- 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index c4202a6f44..f730b63191 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -47,8 +47,6 @@ resource "snowflake_stream_on_table" "stream" { Then, follow our [Resource migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). -## v0.96.0 âžž v0.97.0 - ### *(new feature)* Secret resources Added a new secrets resources for managing secrets. We decided to split each secret flow into individual resources, i.e.: diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index bb7874e794..a22d4b07d4 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -3,6 +3,7 @@ package resources_test import ( "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" + r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "testing" "time" @@ -29,6 +30,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { newComment := random.Comment() refreshTokenExpiryDateTime := time.Now().Add(24 * time.Hour).Format(time.DateTime) newRefreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + externallyChangedRefreshTokenExpiryTime := time.Now().Add(10 * 24 * time.Hour).Format(time.DateOnly) refreshToken := "test_token" newRefreshToken := "new_test_token" @@ -112,18 +114,20 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { ), ), }, - // set comment externally + // set comment and refresh_token_expiry_time externally { PreConfig: func() { acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). - WithComment("secret resource - changed comment"), + WithComment("secret resource - changed comment"). + WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(24 * time.Hour).Format(time.DateOnly)))), )) }, - Config: config.FromModel(t, secretModel), + Config: config.FromModel(t, secretModel.WithOauthRefreshTokenExpiryTime(externallyChangedRefreshTokenExpiryTime).WithComment("comment")), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String(newComment)), + planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String("comment")), + planchecks.ExpectComputed(secretModel.ResourceReference(), r.DescribeOutputAttributeName, true), }, }, Check: resource.ComposeTestCheckFunc( @@ -134,36 +138,8 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(newRefreshToken). - HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). - HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), - ), - ), - }, - // set refresh_token_expiry_time externally - { - PreConfig: func() { - acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). - WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(24 * time.Hour).Format(time.DateTime)))), - )) - }, - Config: config.FromModel(t, secretModel), - ConfigPlanChecks: resource.ConfigPlanChecks{ - PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectComputed(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", true), - }, - }, - Check: resource.ComposeTestCheckFunc( - assert.AssertThat(t, - resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasOauthRefreshTokenString(newRefreshToken). - HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). - HasCommentString(newComment), + HasOauthRefreshTokenExpiryTimeString(externallyChangedRefreshTokenExpiryTime). + HasCommentString("comment"), assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), ), ), @@ -180,7 +156,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). - HasCommentString(newComment). + HasCommentString("comment"). HasOauthRefreshTokenExpiryTimeNotEmpty(), ), }, @@ -263,3 +239,78 @@ func TestAcc_SecretWithAuthorizationCodeGrant_DifferentTimeFormats(t *testing.T) }, }) } + +func TestAcc_SecretWithAuthorizationCodeGrant_ExternalChange(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + name := id.Name() + comment := random.Comment() + //newComment := random.Comment() + refreshTokenExpiryDateTime := time.Now().Add(24 * time.Hour).Format(time.DateTime) + newRefreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + refreshToken := "test_token" + + integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() + _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"), + ) + t.Cleanup(apiIntegrationCleanup) + + secretModel := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryDateTime).WithComment(comment) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.SecretWithAuthorizationCodeGrant), + Steps: []resource.TestStep{ + // create + { + Config: config.FromModel(t, secretModel), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthRefreshTokenString(refreshToken). + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateTime). + HasCommentString(comment), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasName(name). + HasDatabaseName(id.DatabaseName()). + HasSecretType("OAUTH2"). + HasSchemaName(id.SchemaName()). + HasComment(comment), + ), + ), + }, + { + PreConfig: func() { + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). + WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(10 * 24 * time.Hour).Format(time.DateOnly)))), + )) + }, + Config: config.FromModel(t, secretModel. + WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryDateOnly), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectComputed(secretModel.ResourceReference(), r.DescribeOutputAttributeName, true), + }, + }, + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly), + assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + ), + ), + }, + }, + }) +} From e30479352812518031bac82b179b4d22a41b40dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 10 Oct 2024 11:46:55 +0200 Subject: [PATCH 61/65] resolved to Arturs comments --- .../secret_with_authorization_code_grant.md | 2 +- .../secret_with_basic_authentication.md | 2 +- .../secret_with_client_credentials.md | 2 +- docs/resources/secret_with_generic_string.md | 2 +- pkg/acceptance/check_destroy.go | 10 +- pkg/provider/provider.go | 4 +- pkg/provider/resources/resources.go | 4 +- pkg/resources/secret_common.go | 35 ++---- .../secret_with_basic_authentication.go | 13 +- ...th_basic_authentication_acceptance_test.go | 65 +++++----- pkg/resources/secret_with_generic_string.go | 10 +- ...ret_with_generic_string_acceptance_test.go | 36 +++--- ...ret_with_oauth_authorization_code_grant.go | 20 ++- ...uthorization_code_grant_acceptance_test.go | 71 ++++++----- .../secret_with_oauth_client_credentials.go | 12 +- ...auth_client_credentials_acceptance_test.go | 119 ++++++++++-------- ...cret_with_authorization_code_grant.md.tmpl | 2 +- .../secret_with_basic_authentication.md.tmpl | 2 +- .../secret_with_client_credentials.md.tmpl | 2 +- .../secret_with_generic_string.md.tmpl | 2 +- 20 files changed, 201 insertions(+), 214 deletions(-) diff --git a/docs/resources/secret_with_authorization_code_grant.md b/docs/resources/secret_with_authorization_code_grant.md index 16e8aaa234..45ddc3b4b9 100644 --- a/docs/resources/secret_with_authorization_code_grant.md +++ b/docs/resources/secret_with_authorization_code_grant.md @@ -5,7 +5,7 @@ description: |- Resource used to manage secret objects with OAuth Authorization Code Grant. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # snowflake_secret_with_authorization_code_grant (Resource) diff --git a/docs/resources/secret_with_basic_authentication.md b/docs/resources/secret_with_basic_authentication.md index c90c072dad..2fca76f371 100644 --- a/docs/resources/secret_with_basic_authentication.md +++ b/docs/resources/secret_with_basic_authentication.md @@ -5,7 +5,7 @@ description: |- Resource used to manage secret objects with Basic Authentication. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # snowflake_secret_with_basic_authentication (Resource) diff --git a/docs/resources/secret_with_client_credentials.md b/docs/resources/secret_with_client_credentials.md index 3cd019f814..eaf8dedbd8 100644 --- a/docs/resources/secret_with_client_credentials.md +++ b/docs/resources/secret_with_client_credentials.md @@ -5,7 +5,7 @@ description: |- Resource used to manage secret objects with OAuth Client Credentials. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # snowflake_secret_with_client_credentials (Resource) diff --git a/docs/resources/secret_with_generic_string.md b/docs/resources/secret_with_generic_string.md index a75c599141..01336731f7 100644 --- a/docs/resources/secret_with_generic_string.md +++ b/docs/resources/secret_with_generic_string.md @@ -5,7 +5,7 @@ description: |- Resource used to manage secret objects with Generic String. For more information, check secret documentation https://docs.snowflake.com/en/sql-reference/sql/create-secret. --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # snowflake_secret_with_generic_string (Resource) diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index 760c047bee..bb9a09a49c 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -173,21 +173,21 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ScimSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) }, + resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.Databases.ShowByID) + }, resources.SecretWithAuthorizationCodeGrant: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, - resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + resources.SecretWithBasicAuthentication: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, - resources.SecretWithBasicAuthentication: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + resources.SecretWithClientCredentials: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, resources.SecretWithGenericString: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Secrets.ShowByID) }, - resources.SecondaryDatabase: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { - return runShowById(ctx, id, client.Databases.ShowByID) - }, resources.Sequence: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.Sequences.ShowByID) }, diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index ed2a6bc8f5..efa6ea4b81 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -470,11 +470,11 @@ func getResources() map[string]*schema.Resource { "snowflake_saml2_integration": resources.SAML2Integration(), "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), - "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), + "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_secret_with_authorization_code_grant": resources.SecretWithAuthorizationCodeGrant(), "snowflake_secret_with_basic_authentication": resources.SecretWithBasicAuthentication(), + "snowflake_secret_with_client_credentials": resources.SecretWithClientCredentials(), "snowflake_secret_with_generic_string": resources.SecretWithGenericString(), - "snowflake_secondary_database": resources.SecondaryDatabase(), "snowflake_sequence": resources.Sequence(), "snowflake_session_parameter": resources.SessionParameter(), "snowflake_share": resources.Share(), diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index 07aed2d343..0b69fbc8d1 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -39,11 +39,11 @@ const ( Saml2SecurityIntegration resource = "snowflake_saml2_integration" Schema resource = "snowflake_schema" ScimSecurityIntegration resource = "snowflake_scim_integration" - SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" + SecondaryDatabase resource = "snowflake_secondary_database" SecretWithAuthorizationCodeGrant resource = "snowflake_secret_with_authorization_code_grant" SecretWithBasicAuthentication resource = "snowflake_secret_with_basic_authentication" + SecretWithClientCredentials resource = "snowflake_secret_with_client_credentials" SecretWithGenericString resource = "snowflake_secret_with_generic_string" - SecondaryDatabase resource = "snowflake_secondary_database" Sequence resource = "snowflake_sequence" Share resource = "snowflake_share" SharedDatabase resource = "snowflake_shared_database" diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index 5c1a245c7a..3931e50550 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -2,6 +2,8 @@ package resources import ( "context" + "errors" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -65,37 +67,24 @@ func handleSecretCreate(d *schema.ResourceData) (database, schema, name string) } func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret, secretDescription *sdk.SecretDetails) error { - if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { - return err - } - if err := d.Set("comment", secret.Comment); err != nil { - return err - } - if err := d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}); err != nil { - return err - } - if err := d.Set(DescribeOutputAttributeName, []map[string]any{schemas.SecretDescriptionToSchema(*secretDescription)}); err != nil { + err := errors.Join( + d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()), + d.Set("comment", secret.Comment), + d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}), + d.Set(DescribeOutputAttributeName, []map[string]any{schemas.SecretDescriptionToSchema(*secretDescription)}), + ) + if err != nil { return err } return nil } -type commonSecretSet struct { - comment *string -} - -type commonSecretUnset struct { - comment *bool -} - -func handleSecretUpdate(d *schema.ResourceData) (commonSecretSet, commonSecretUnset) { - set, unset := commonSecretSet{}, commonSecretUnset{} +func handleSecretUpdate(d *schema.ResourceData, set *sdk.SecretSetRequest, unset *sdk.SecretUnsetRequest) { if d.HasChange("comment") { if v, ok := d.GetOk("comment"); ok { - set.comment = sdk.Pointer(v.(string)) + set.WithComment(v.(string)) } else { - unset.comment = sdk.Pointer(true) + unset.WithComment(true) } } - return set, unset } diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index fd9fcc7482..d3ff178a1a 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -4,13 +4,14 @@ import ( "context" "errors" "fmt" + "reflect" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "reflect" ) var secretBasicAuthenticationSchema = func() map[string]*schema.Schema { @@ -139,13 +140,9 @@ func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.R return diag.FromErr(err) } - commonSet, commonUnset := handleSecretUpdate(d) - set := &sdk.SecretSetRequest{ - Comment: commonSet.comment, - } - unset := &sdk.SecretUnsetRequest{ - Comment: commonUnset.comment, - } + set := &sdk.SecretSetRequest{} + unset := &sdk.SecretUnsetRequest{} + handleSecretUpdate(d, set, unset) setForFlow := &sdk.SetForFlowRequest{ SetForBasicAuthentication: &sdk.SetForBasicAuthenticationRequest{}, } diff --git a/pkg/resources/secret_with_basic_authentication_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_acceptance_test.go index 745315dce7..31954504c8 100644 --- a/pkg/resources/secret_with_basic_authentication_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_acceptance_test.go @@ -27,9 +27,12 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { comment := random.Comment() secretModel := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "foo", id.SchemaName(), "foo") + secretModelDifferentCredentialsWithComment := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "bar", id.SchemaName(), "bar").WithComment(comment) secretModelWithoutComment := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "bar", id.SchemaName(), "bar") secretModelEmptyCredentials := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "", id.SchemaName(), "") + secretName := secretModel.ResourceReference() + resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -43,7 +46,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModel), Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithBasicAuthenticationResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithBasicAuthenticationResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -51,7 +54,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasPasswordString("foo"). HasCommentString(""), - resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + resourceshowoutputassert.SecretShowOutput(t, secretName). HasName(name). HasDatabaseName(id.DatabaseName()). HasSecretType("PASSWORD"). @@ -59,28 +62,24 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { HasComment(""), ), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), - resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.secret_type", "PASSWORD"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", "foo"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "0"), + resource.TestCheckResourceAttr(secretName, "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretName, "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", "PASSWORD"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.username", "foo"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_refresh_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_scopes.#", "0"), ), }, // set username, password and comment { - Config: config.FromModel(t, secretModel. - WithPassword("bar"). - WithUsername("bar"). - WithComment(comment), - ), + Config: config.FromModel(t, secretModelDifferentCredentialsWithComment), Check: assert.AssertThat(t, - resourceassert.SecretWithBasicAuthenticationResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithBasicAuthenticationResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -99,17 +98,17 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { }, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectDrift(secretModel.ResourceReference(), "comment", sdk.String(comment), sdk.String("test_comment")), - planchecks.ExpectDrift(secretModel.ResourceReference(), "username", sdk.String("bar"), sdk.String("test_username")), + plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), + planchecks.ExpectDrift(secretName, "comment", sdk.String(comment), sdk.String("test_comment")), + planchecks.ExpectDrift(secretName, "username", sdk.String("bar"), sdk.String("test_username")), - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("test_comment"), sdk.String(comment)), - planchecks.ExpectChange(secretModel.ResourceReference(), "username", tfjson.ActionUpdate, sdk.String("test_username"), sdk.String("bar")), + planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, sdk.String("test_comment"), sdk.String(comment)), + planchecks.ExpectChange(secretName, "username", tfjson.ActionUpdate, sdk.String("test_username"), sdk.String("bar")), }, }, - Config: config.FromModel(t, secretModel), + Config: config.FromModel(t, secretModelDifferentCredentialsWithComment), Check: assert.AssertThat(t, - resourceassert.SecretWithBasicAuthenticationResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithBasicAuthenticationResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -120,7 +119,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { }, // import { - ResourceName: secretModel.ResourceReference(), + ResourceName: secretName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"password"}, @@ -137,18 +136,18 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModelWithoutComment), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), + plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, sdk.String(comment), nil), }, }, Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). + resourceassert.SecretWithClientCredentialsResource(t, secretName). HasCommentString(""), ), }, // import with no fields set { - ResourceName: secretModel.ResourceReference(), + ResourceName: secretName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"password"}, @@ -165,7 +164,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModelEmptyCredentials), Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithBasicAuthenticationResource(t, secretModelEmptyCredentials.ResourceReference()). + resourceassert.SecretWithBasicAuthenticationResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -179,7 +178,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { }) } -func TestAcc_SecretWithBasicAuthentication_EmptyCredentials(t *testing.T) { +func TestAcc_SecretWithBasicAuthentication_CreateWithEmptyCredentials(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() name := id.Name() secretModelEmptyCredentials := model.SecretWithBasicAuthentication("s", id.DatabaseName(), name, "", id.SchemaName(), "") diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 87f2f2aa14..ec6b10acf2 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -123,13 +123,9 @@ func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.Resourc return diag.FromErr(err) } - commonSet, commonUnset := handleSecretUpdate(d) - set := &sdk.SecretSetRequest{ - Comment: commonSet.comment, - } - unset := &sdk.SecretUnsetRequest{ - Comment: commonUnset.comment, - } + set := &sdk.SecretSetRequest{} + unset := &sdk.SecretUnsetRequest{} + handleSecretUpdate(d, set, unset) if d.HasChange("secret_string") { secretString := d.Get("secret_string").(string) diff --git a/pkg/resources/secret_with_generic_string_acceptance_test.go b/pkg/resources/secret_with_generic_string_acceptance_test.go index f4f6553cb7..0a9c23dba7 100644 --- a/pkg/resources/secret_with_generic_string_acceptance_test.go +++ b/pkg/resources/secret_with_generic_string_acceptance_test.go @@ -29,6 +29,8 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { secretModel := model.SecretWithGenericString("s", id.DatabaseName(), name, id.SchemaName(), "foo") secretModelEmptySecretString := model.SecretWithGenericString("s", id.DatabaseName(), name, id.SchemaName(), "") + secretName := secretModel.ResourceReference() + resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -57,17 +59,17 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { HasComment(""), ), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), - resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.secret_type", "GENERIC_STRING"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "0"), + resource.TestCheckResourceAttr(secretName, "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretName, "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", "GENERIC_STRING"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_refresh_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_scopes.#", "0"), ), }, // set secret_string and comment @@ -77,7 +79,7 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { WithComment(comment), ), Check: assert.AssertThat(t, - resourceassert.SecretWithGenericStringResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithGenericStringResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -93,13 +95,13 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { Config: config.FromModel(t, secretModel), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectDrift(secretModel.ResourceReference(), "comment", sdk.String(comment), sdk.String("test_comment")), - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("test_comment"), sdk.String(comment)), + plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), + planchecks.ExpectDrift(secretName, "comment", sdk.String(comment), sdk.String("test_comment")), + planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, sdk.String("test_comment"), sdk.String(comment)), }, }, Check: assert.AssertThat(t, - resourceassert.SecretWithGenericStringResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithGenericStringResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). @@ -109,7 +111,7 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { }, // import { - ResourceName: secretModel.ResourceReference(), + ResourceName: secretName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"secret_string"}, diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index dffe730426..3c177d4e21 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -162,26 +162,24 @@ func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schem return diag.FromErr(err) } - commonSet, commonUnset := handleSecretUpdate(d) - set := &sdk.SecretSetRequest{ - Comment: commonSet.comment, - SetForFlow: &sdk.SetForFlowRequest{ - SetForOAuthAuthorization: &sdk.SetForOAuthAuthorizationRequest{}, - }, - } + set := &sdk.SecretSetRequest{} + unset := &sdk.SecretUnsetRequest{} + handleSecretUpdate(d, set, unset) - unset := &sdk.SecretUnsetRequest{ - Comment: commonUnset.comment, + setForFlow := &sdk.SetForFlowRequest{ + SetForOAuthAuthorization: &sdk.SetForOAuthAuthorizationRequest{}, } if d.HasChange("oauth_refresh_token") { refreshToken := d.Get("oauth_refresh_token").(string) - set.SetForFlow.SetForOAuthAuthorization.WithOauthRefreshToken(refreshToken) + setForFlow.SetForOAuthAuthorization.WithOauthRefreshToken(refreshToken) + set.WithSetForFlow(*setForFlow) } if d.HasChange("oauth_refresh_token_expiry_time") { refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) - set.SetForFlow.SetForOAuthAuthorization.WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + setForFlow.SetForOAuthAuthorization.WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + set.WithSetForFlow(*setForFlow) } if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index a22d4b07d4..efab7f20d5 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -40,7 +40,11 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { ) t.Cleanup(apiIntegrationCleanup) - secretModel := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryDateTime).WithComment(comment) + secretModel := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryDateTime) + secretModelAllSet := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, newRefreshToken, newRefreshTokenExpiryDateOnly).WithComment(comment) + secretModelExternalChanges := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, newRefreshToken, externallyChangedRefreshTokenExpiryTime).WithComment(newComment) + + secretName := secretModel.ResourceReference() resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -62,55 +66,51 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(refreshToken). HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateTime). - HasCommentString(comment), + HasCommentString(""), resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). HasName(name). HasDatabaseName(id.DatabaseName()). HasSecretType("OAUTH2"). HasSchemaName(id.SchemaName()). - HasComment(comment), + HasComment(""), ), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), - resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.secret_type", "OAUTH2"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), - resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", integrationId.Name()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "0"), + resource.TestCheckResourceAttr(secretName, "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretName, "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", "OAUTH2"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", integrationId.Name()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_scopes.#", "0"), ), }, // set all { - Config: config.FromModel(t, secretModel. - WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryDateOnly). - WithOauthRefreshToken(newRefreshToken). - WithComment(newComment), - ), + Config: config.FromModel(t, secretModelAllSet), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), sdk.String(newComment)), - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token", tfjson.ActionUpdate, sdk.String(refreshToken), sdk.String(newRefreshToken)), - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryDateTime), sdk.String(newRefreshTokenExpiryDateOnly)), + plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, nil, sdk.String(comment)), + planchecks.ExpectChange(secretName, "oauth_refresh_token", tfjson.ActionUpdate, sdk.String(refreshToken), sdk.String(newRefreshToken)), + planchecks.ExpectChange(secretName, "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryDateTime), sdk.String(newRefreshTokenExpiryDateOnly)), }, }, Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithAuthorizationCodeResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(newRefreshToken). HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). - HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + HasCommentString(comment), + assert.Check(resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time")), ), ), }, @@ -122,31 +122,31 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(24 * time.Hour).Format(time.DateOnly)))), )) }, - Config: config.FromModel(t, secretModel.WithOauthRefreshTokenExpiryTime(externallyChangedRefreshTokenExpiryTime).WithComment("comment")), + Config: config.FromModel(t, secretModelExternalChanges), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModel.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String("comment")), - planchecks.ExpectComputed(secretModel.ResourceReference(), r.DescribeOutputAttributeName, true), + plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String(newComment)), + planchecks.ExpectComputed(secretName, r.DescribeOutputAttributeName, true), }, }, Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, - resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). + resourceassert.SecretWithAuthorizationCodeResource(t, secretName). HasNameString(name). HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(newRefreshToken). HasOauthRefreshTokenExpiryTimeString(externallyChangedRefreshTokenExpiryTime). - HasCommentString("comment"), - assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time")), ), ), }, // import { - ResourceName: secretModel.ResourceReference(), + ResourceName: secretName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"oauth_refresh_token"}, @@ -156,7 +156,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). - HasCommentString("comment"). + HasCommentString(newComment). HasOauthRefreshTokenExpiryTimeNotEmpty(), ), }, @@ -244,7 +244,6 @@ func TestAcc_SecretWithAuthorizationCodeGrant_ExternalChange(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() name := id.Name() comment := random.Comment() - //newComment := random.Comment() refreshTokenExpiryDateTime := time.Now().Add(24 * time.Hour).Format(time.DateTime) newRefreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) refreshToken := "test_token" diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index 6ea8521a44..42c866569c 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -4,10 +4,10 @@ import ( "context" "errors" "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -163,13 +163,9 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res return diag.FromErr(err) } - commonSet, commonUnset := handleSecretUpdate(d) - set := &sdk.SecretSetRequest{ - Comment: commonSet.comment, - } - unset := &sdk.SecretUnsetRequest{ - Comment: commonUnset.comment, - } + set := &sdk.SecretSetRequest{} + unset := &sdk.SecretUnsetRequest{} + handleSecretUpdate(d, set, unset) if d.HasChange("oauth_scopes") { stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) diff --git a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go index ae1089cbb3..cf6ff2bb11 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go @@ -1,42 +1,45 @@ package resources_test import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - tfjson "github.com/hashicorp/terraform-json" - "github.com/hashicorp/terraform-plugin-testing/plancheck" - - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + tfjson "github.com/hashicorp/terraform-json" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() name := id.Name() - comment := "aaa" + comment := random.Comment() newComment := random.Comment() integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() _, apiIntegrationCleanup := acc.TestClient().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, - sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "test_client_id", "test_client_secret"). WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}, {Scope: "test"}}), ) t.Cleanup(apiIntegrationCleanup) secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}).WithComment(comment) + secretModelTestInScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"test"}).WithComment(newComment) + secretModelFooInScopesWithComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo"}).WithComment(newComment) + secretModelFooInScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo"}) secretModelWithoutComment := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{"foo", "bar"}) + secretName := secretModel.ResourceReference() resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -64,33 +67,31 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasSecretType("OAUTH2"). HasSchemaName(id.SchemaName()), ), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "2"), - resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo"), - resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "bar"), + resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "bar"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "fully_qualified_name", id.FullyQualifiedName()), - resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.created_on"), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.name", name), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.database_name", id.DatabaseName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.username", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_access_token_expiry_time", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time", ""), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.integration_name", integrationId.Name()), - resource.TestCheckResourceAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.#", "2"), - resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.*", "foo"), - resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "describe_output.0.oauth_scopes.*", "bar"), + resource.TestCheckResourceAttr(secretName, "fully_qualified_name", id.FullyQualifiedName()), + resource.TestCheckResourceAttrSet(secretName, "describe_output.0.created_on"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), + resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_refresh_token_expiry_time", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", integrationId.Name()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr(secretName, "describe_output.0.oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr(secretName, "describe_output.0.oauth_scopes.*", "bar"), ), }, // set oauth_scopes and comment in config { - Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{"test"}). - WithComment(newComment)), + Config: config.FromModel(t, secretModelTestInScopes), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar foo]"), sdk.String("[test]")), + plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretName, "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar foo]"), sdk.String("[test]")), }, }, Check: assert.AssertThat(t, @@ -101,8 +102,8 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasApiAuthenticationString(integrationId.Name()). HasOauthScopesLength(len([]string{"test"})). HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "test")), + assert.Check(resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "test")), ), }, // set oauth_scopes and comment externally @@ -117,7 +118,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { ) acc.TestClient().Secret.Alter(t, req) }, - Config: config.FromModel(t, secretModel.WithOauthScopes([]string{"foo"})), + Config: config.FromModel(t, secretModelFooInScopesWithComment), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), @@ -133,35 +134,35 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasApiAuthenticationString(integrationId.Name()). HasOauthScopesLength(len([]string{"foo"})). HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo")), + assert.Check(resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "foo")), ), }, // unset comment { - Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), + Config: config.FromModel(t, secretModelFooInScopes), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(newComment), nil), + plancheck.ExpectResourceAction(secretModelFooInScopes.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretModelFooInScopes.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(newComment), nil), }, }, Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, secretModelWithoutComment.ResourceReference()). + resourceassert.SecretWithClientCredentialsResource(t, secretModelFooInScopes.ResourceReference()). HasCommentString(""), ), }, // unset comment externally { PreConfig: func() { - req := sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest().WithComment("aaa")) + req := sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest().WithComment(comment)) acc.TestClient().Secret.Alter(t, req) }, Config: config.FromModel(t, secretModelWithoutComment), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String("aaa"), nil), + planchecks.ExpectChange(secretModelWithoutComment.ResourceReference(), "comment", tfjson.ActionUpdate, sdk.String(comment), nil), }, }, Check: assert.AssertThat(t, @@ -169,11 +170,6 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasCommentString(""), ), }, - // destroy - { - Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo"})), - Destroy: true, - }, // create without comment { Config: config.FromModel(t, secretModelWithoutComment.WithOauthScopes([]string{"foo", "bar"})), @@ -187,9 +183,9 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasOauthScopesLength(len([]string{"foo", "bar"})). HasCommentString(""), ), - resource.TestCheckResourceAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.#", "2"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "foo"), - resource.TestCheckTypeSetElemAttr("snowflake_secret_with_client_credentials.s", "oauth_scopes.*", "bar"), + resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "2"), + resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "foo"), + resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "bar"), ), }, // import @@ -206,11 +202,6 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), ), }, - // destroy - { - Config: config.FromModel(t, secretModel), - Destroy: true, - }, }, }) } @@ -272,6 +263,26 @@ func TestAcc_SecretWithClientCredentials_EmptyScopesList(t *testing.T) { assert.Check(resource.TestCheckTypeSetElemAttr(secretModel.ResourceReference(), "oauth_scopes.*", "foo")), ), }, + // Set empty oauth_scopes + { + Config: config.FromModel(t, secretModel. + WithOauthScopes([]string{}), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), + planchecks.ExpectChange(secretModel.ResourceReference(), "oauth_scopes", tfjson.ActionUpdate, sdk.String("[foo]"), sdk.String("[]")), + }, + }, + Check: assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, secretModel.ResourceReference()). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()), + assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "0")), + ), + }, }, }) } diff --git a/templates/resources/secret_with_authorization_code_grant.md.tmpl b/templates/resources/secret_with_authorization_code_grant.md.tmpl index 6189ef94ee..58d5a6d4b6 100644 --- a/templates/resources/secret_with_authorization_code_grant.md.tmpl +++ b/templates/resources/secret_with_authorization_code_grant.md.tmpl @@ -9,7 +9,7 @@ description: |- {{- end }} --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # {{.Name}} ({{.Type}}) diff --git a/templates/resources/secret_with_basic_authentication.md.tmpl b/templates/resources/secret_with_basic_authentication.md.tmpl index 6189ef94ee..58d5a6d4b6 100644 --- a/templates/resources/secret_with_basic_authentication.md.tmpl +++ b/templates/resources/secret_with_basic_authentication.md.tmpl @@ -9,7 +9,7 @@ description: |- {{- end }} --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # {{.Name}} ({{.Type}}) diff --git a/templates/resources/secret_with_client_credentials.md.tmpl b/templates/resources/secret_with_client_credentials.md.tmpl index 6189ef94ee..58d5a6d4b6 100644 --- a/templates/resources/secret_with_client_credentials.md.tmpl +++ b/templates/resources/secret_with_client_credentials.md.tmpl @@ -9,7 +9,7 @@ description: |- {{- end }} --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # {{.Name}} ({{.Type}}) diff --git a/templates/resources/secret_with_generic_string.md.tmpl b/templates/resources/secret_with_generic_string.md.tmpl index 6189ef94ee..58d5a6d4b6 100644 --- a/templates/resources/secret_with_generic_string.md.tmpl +++ b/templates/resources/secret_with_generic_string.md.tmpl @@ -9,7 +9,7 @@ description: |- {{- end }} --- -!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +!> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0960--v0970) to use it. # {{.Name}} ({{.Type}}) From c1115fa265c830f4d0a599c85dfc1e9fcc0f98dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Thu, 10 Oct 2024 11:50:19 +0200 Subject: [PATCH 62/65] linter suggestions --- .../secret_with_client_credentials_resource_ext.go | 1 + .../secret_with_basic_authentication_acceptance_test.go | 3 +-- pkg/resources/secret_with_generic_string.go | 2 +- pkg/resources/secret_with_generic_string_acceptance_test.go | 2 +- pkg/resources/secret_with_oauth_authorization_code_grant.go | 5 ++++- ...et_with_oauth_authorization_code_grant_acceptance_test.go | 4 ++-- pkg/resources/secret_with_oauth_client_credentials.go | 1 + 7 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go index e40570cdb2..27db6d5790 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/secret_with_client_credentials_resource_ext.go @@ -2,6 +2,7 @@ package resourceassert import ( "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" ) diff --git a/pkg/resources/secret_with_basic_authentication_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_acceptance_test.go index 31954504c8..b527659458 100644 --- a/pkg/resources/secret_with_basic_authentication_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_acceptance_test.go @@ -1,12 +1,12 @@ package resources_test import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" @@ -191,7 +191,6 @@ func TestAcc_SecretWithBasicAuthentication_CreateWithEmptyCredentials(t *testing }, CheckDestroy: acc.CheckDestroy(t, resources.SecretWithBasicAuthentication), Steps: []resource.TestStep{ - { Config: config.FromModel(t, secretModelEmptyCredentials), Check: resource.ComposeTestCheckFunc( diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index ec6b10acf2..98ff62b643 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -4,10 +4,10 @@ import ( "context" "errors" "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" diff --git a/pkg/resources/secret_with_generic_string_acceptance_test.go b/pkg/resources/secret_with_generic_string_acceptance_test.go index 0a9c23dba7..62d22b188c 100644 --- a/pkg/resources/secret_with_generic_string_acceptance_test.go +++ b/pkg/resources/secret_with_generic_string_acceptance_test.go @@ -1,12 +1,12 @@ package resources_test import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 3c177d4e21..7bb99cdc22 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -63,7 +63,10 @@ func ImportSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.Resou logging.DebugLogger.Printf("[DEBUG] Starting secret with authorization code import") client := meta.(*provider.Context).Client id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err := handleSecretImport(d); err != nil { + if err != nil { + return nil, err + } + if err = handleSecretImport(d); err != nil { return nil, err } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index efab7f20d5..1498dccba5 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -2,20 +2,20 @@ package resources_test import ( "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" - r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "testing" "time" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" tfjson "github.com/hashicorp/terraform-json" "github.com/hashicorp/terraform-plugin-testing/helper/resource" diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index 42c866569c..74b2b12ad5 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -75,6 +75,7 @@ func ImportSecretWithClientCredentials(ctx context.Context, d *schema.ResourceDa return []*schema.ResourceData{d}, nil } + func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client databaseName, schemaName, name := handleSecretCreate(d) From bdb7fff2c01205bae4d263b4cf812b38d332b582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 11 Oct 2024 16:02:55 +0200 Subject: [PATCH 63/65] refactor after Kuba re-review comments --- MIGRATION_GUIDE.md | 23 ++-- pkg/acceptance/bettertestspoc/README.md | 1 + .../secret_show_output_gen.go | 7 - pkg/resources/diff_suppressions.go | 7 +- pkg/resources/diff_suppressions_test.go | 3 +- pkg/resources/secret_common.go | 28 ++-- .../secret_with_basic_authentication.go | 40 +++--- ...th_basic_authentication_acceptance_test.go | 47 ++++--- pkg/resources/secret_with_generic_string.go | 34 +++-- ...ret_with_generic_string_acceptance_test.go | 34 +++-- ...ret_with_oauth_authorization_code_grant.go | 122 +++++++++--------- ...uthorization_code_grant_acceptance_test.go | 45 ++++--- .../secret_with_oauth_client_credentials.go | 34 +++-- ...auth_client_credentials_acceptance_test.go | 44 ++++--- pkg/resources/show_and_describe_handlers.go | 22 ++++ pkg/sdk/secrets_def.go | 18 ++- .../testint/secrets_gen_integration_test.go | 31 +++-- 17 files changed, 305 insertions(+), 235 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index dd3e14f24a..899ee43c18 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -9,6 +9,19 @@ across different versions. ## v0.96.0 âžž v0.97.0 +### *(new feature)* Secret resources +Added a new secrets resources for managing secrets. +We decided to split each secret flow into individual resources. +This segregation was based on the secret flows in CREATE SECRET. i.e.: +- `snowflake_secret_with_client_credentials` +- `snowflake_secret_with_authorization_code_grant` +- `snowflake_secret_with_basic_authentication` +- `snowflake_secret_with_generic_string` + + +See reference [docs](https://docs.snowflake.com/en/sql-reference/sql/create-secret). + + ### *(new feature)* snowflake_stream_on_table resource To enhance clarity and functionality, the new resource `snowflake_stream_on_table` has been introduced to replace the previous `snowflake_stream`. Recognizing that the old resource carried multiple responsibilities within a single entity, we opted to divide it into more specialized resources. @@ -47,16 +60,6 @@ resource "snowflake_stream_on_table" "stream" { Then, follow our [Resource migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). -### *(new feature)* Secret resources -Added a new secrets resources for managing secrets. -We decided to split each secret flow into individual resources, i.e.: -- `snowflake_secret_with_client_credentials` -- `snowflake_secret_with_authorization_code_grant` -- `snowflake_secret_with_basic_authentication` -- `snowflake_secret_with_generic_string` - -See reference [docs](https://docs.snowflake.com/en/sql-reference/sql/create-secret). - ### *(new feature)* new snowflake_service_user and snowflake_legacy_service_user resources Release v0.95.0 introduced reworked `snowflake_user` resource. As [noted](#note-user-types), the new `SERVICE` and `LEGACY_SERVICE` user types were not supported. diff --git a/pkg/acceptance/bettertestspoc/README.md b/pkg/acceptance/bettertestspoc/README.md index 82b203f4d3..fcc4179cf2 100644 --- a/pkg/acceptance/bettertestspoc/README.md +++ b/pkg/acceptance/bettertestspoc/README.md @@ -325,6 +325,7 @@ it will result in: - Add support for datasource tests (assertions and config builders). - Consider overriding the assertions when invoking same check multiple times with different params (e.g. `Warehouse(...).HasType(X).HasType(Y)`; it could use the last-check-wins approach, to more easily reuse complex checks between the test steps). - Consider not adding the check for `show_output` presence on creation (same with `parameters`). The majority of the use cases need it to be present but there are a few others (like conditional presence in the datasources). Currently, it seems that they should be always present in the resources, so no change is made. Later, with adding the support for the datasource tests, consider simple destructive implementation like: +- Add support for `set` so that assertions like e.g. `oauth_scopes.*` could be done. ```go func (w *WarehouseDatasourceShowOutputAssert) IsEmpty() { w.assertions = make([]resourceAssertion, 0) diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go index f1182d6d9e..665693da07 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/secret_show_output_gen.go @@ -76,13 +76,6 @@ func (s *SecretShowOutputAssert) HasSecretType(expected string) *SecretShowOutpu return s } -func (s *SecretShowOutputAssert) HasOauthScopes(expected []string) *SecretShowOutputAssert { - for _, v := range expected { - s.AddAssertion(assert.ValueSet("oauth_scopes.*", v)) - } - return s -} - func (s *SecretShowOutputAssert) HasOwnerRoleType(expected string) *SecretShowOutputAssert { s.AddAssertion(assert.ResourceShowOutputValueSet("owner_role_type", expected)) return s diff --git a/pkg/resources/diff_suppressions.go b/pkg/resources/diff_suppressions.go index 3e23be3ee0..05c30c66d5 100644 --- a/pkg/resources/diff_suppressions.go +++ b/pkg/resources/diff_suppressions.go @@ -2,14 +2,13 @@ package resources import ( "fmt" - "log" - "slices" - "strings" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + "slices" + "strings" ) func NormalizeAndCompare[T comparable](normalize func(string) (T, error)) schema.SchemaDiffSuppressFunc { diff --git a/pkg/resources/diff_suppressions_test.go b/pkg/resources/diff_suppressions_test.go index 7c54f03938..b236bd00a3 100644 --- a/pkg/resources/diff_suppressions_test.go +++ b/pkg/resources/diff_suppressions_test.go @@ -2,13 +2,12 @@ package resources_test import ( "fmt" - "testing" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "testing" ) func Test_NormalizeAndCompare(t *testing.T) { diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index 3931e50550..81d9368ae9 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -3,9 +3,10 @@ package resources import ( "context" "errors" - + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -62,19 +63,15 @@ func handleSecretImport(d *schema.ResourceData) error { return nil } -func handleSecretCreate(d *schema.ResourceData) (database, schema, name string) { - return d.Get("database").(string), d.Get("schema").(string), d.Get("name").(string) -} - func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret, secretDescription *sdk.SecretDetails) error { - err := errors.Join( + errs := errors.Join( d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()), d.Set("comment", secret.Comment), d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}), d.Set(DescribeOutputAttributeName, []map[string]any{schemas.SecretDescriptionToSchema(*secretDescription)}), ) - if err != nil { - return err + if errs != nil { + return errs } return nil } @@ -88,3 +85,18 @@ func handleSecretUpdate(d *schema.ResourceData, set *sdk.SecretSetRequest, unset } } } + +func DeleteContextSecret(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index d3ff178a1a..ce8aca14b4 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -36,9 +37,15 @@ func SecretWithBasicAuthentication() *schema.Resource { CreateContext: CreateContextSecretWithBasicAuthentication, ReadContext: ReadContextSecretWithBasicAuthentication, UpdateContext: UpdateContextSecretWithBasicAuthentication, - DeleteContext: DeleteContextSecretWithBasicAuthentication, + DeleteContext: DeleteContextSecret, Description: "Resource used to manage secret objects with Basic Authentication. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", + CustomizeDiff: customdiff.All( + ComputedIfAnyAttributeChanged(secretBasicAuthenticationSchema, ShowOutputAttributeName, "name", "comment"), + ComputedIfAnyAttributeChanged(secretBasicAuthenticationSchema, DescribeOutputAttributeName, "name", "username"), + ComputedIfAnyAttributeChanged(secretBasicAuthenticationSchema, FullyQualifiedNameAttributeName, "name"), + ), + Schema: secretBasicAuthenticationSchema, Importer: &schema.ResourceImporter{ StateContext: ImportSecretWithBasicAuthentication, @@ -72,7 +79,7 @@ func ImportSecretWithBasicAuthentication(ctx context.Context, d *schema.Resource func CreateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName, schemaName, name := handleSecretCreate(d) + databaseName, schemaName, name := d.Get("database").(string), d.Get("schema").(string), d.Get("name").(string) id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) usernameString := d.Get("username").(string) @@ -143,20 +150,20 @@ func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.R set := &sdk.SecretSetRequest{} unset := &sdk.SecretUnsetRequest{} handleSecretUpdate(d, set, unset) - setForFlow := &sdk.SetForFlowRequest{ - SetForBasicAuthentication: &sdk.SetForBasicAuthenticationRequest{}, - } + setForBasicAuthentication := &sdk.SetForBasicAuthenticationRequest{} if d.HasChange("username") { username := d.Get("username").(string) - setForFlow.SetForBasicAuthentication.WithUsername(username) - set.WithSetForFlow(*setForFlow) + setForBasicAuthentication.WithUsername(username) } if d.HasChange("password") { password := d.Get("password").(string) - setForFlow.SetForBasicAuthentication.WithPassword(password) - set.WithSetForFlow(*setForFlow) + setForBasicAuthentication.WithPassword(password) + } + + if !reflect.DeepEqual(*setForBasicAuthentication, sdk.SetForBasicAuthenticationRequest{}) { + set.WithSetForFlow(sdk.SetForFlowRequest{SetForBasicAuthentication: setForBasicAuthentication}) } if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { @@ -173,18 +180,3 @@ func UpdateContextSecretWithBasicAuthentication(ctx context.Context, d *schema.R return ReadContextSecretWithBasicAuthentication(ctx, d, meta) } - -func DeleteContextSecretWithBasicAuthentication(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil -} diff --git a/pkg/resources/secret_with_basic_authentication_acceptance_test.go b/pkg/resources/secret_with_basic_authentication_acceptance_test.go index b527659458..5c0aa401cb 100644 --- a/pkg/resources/secret_with_basic_authentication_acceptance_test.go +++ b/pkg/resources/secret_with_basic_authentication_acceptance_test.go @@ -57,7 +57,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { resourceshowoutputassert.SecretShowOutput(t, secretName). HasName(name). HasDatabaseName(id.DatabaseName()). - HasSecretType("PASSWORD"). + HasSecretType(sdk.SecretTypePassword). HasSchemaName(id.SchemaName()). HasComment(""), ), @@ -67,8 +67,9 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", "PASSWORD"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", sdk.SecretTypePassword), resource.TestCheckResourceAttr(secretName, "describe_output.0.username", "foo"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_refresh_token_expiry_time", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", ""), @@ -78,23 +79,39 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { // set username, password and comment { Config: config.FromModel(t, secretModelDifferentCredentialsWithComment), - Check: assert.AssertThat(t, - resourceassert.SecretWithBasicAuthenticationResource(t, secretName). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasUsernameString("bar"). - HasPasswordString("bar"). - HasCommentString(comment), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + + resourceassert.SecretWithBasicAuthenticationResource(t, secretName). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasUsernameString("bar"). + HasPasswordString("bar"). + HasCommentString(comment), + + resourceshowoutputassert.SecretShowOutput(t, secretName). + HasSecretType(sdk.SecretTypePassword). + HasComment(comment), + ), + + resource.TestCheckResourceAttr(secretName, "describe_output.0.username", "bar"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", comment), ), }, // set username and comment externally { PreConfig: func() { - acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). - WithComment("test_comment"). - WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForBasicAuthentication(*sdk.NewSetForBasicAuthenticationRequest().WithUsername("test_username"))), - )) + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id). + WithSet(*sdk.NewSecretSetRequest(). + WithComment("test_comment"). + WithSetForFlow(*sdk.NewSetForFlowRequest(). + WithSetForBasicAuthentication(*sdk.NewSetForBasicAuthenticationRequest(). + WithUsername("test_username"), + ), + ), + ), + ) }, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ @@ -159,7 +176,7 @@ func TestAcc_SecretWithBasicAuthentication_BasicFlow(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "comment", ""), ), }, - // create with empty username and password + // set empty username and password { Config: config.FromModel(t, secretModelEmptyCredentials), Check: resource.ComposeTestCheckFunc( diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 98ff62b643..5d30de4065 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -31,9 +32,15 @@ func SecretWithGenericString() *schema.Resource { CreateContext: CreateContextSecretWithGenericString, ReadContext: ReadContextSecretWithGenericString, UpdateContext: UpdateContextSecretWithGenericString, - DeleteContext: DeleteContextSecretWithGenericString, + DeleteContext: DeleteContextSecret, Description: "Resource used to manage secret objects with Generic String. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", + CustomizeDiff: customdiff.All( + ComputedIfAnyAttributeChanged(secretGenericStringSchema, DescribeOutputAttributeName, "name"), + ComputedIfAnyAttributeChanged(secretGenericStringSchema, ShowOutputAttributeName, "name", "comment"), + ComputedIfAnyAttributeChanged(secretGenericStringSchema, FullyQualifiedNameAttributeName, "name"), + ), + Schema: secretGenericStringSchema, Importer: &schema.ResourceImporter{ StateContext: ImportSecretWithGenericString, @@ -58,7 +65,7 @@ func ImportSecretWithGenericString(ctx context.Context, d *schema.ResourceData, func CreateContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName, schemaName, name := handleSecretCreate(d) + databaseName, schemaName, name := d.Get("database").(string), d.Get("schema").(string), d.Get("name").(string) id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) secretSting := d.Get("secret_string").(string) @@ -126,11 +133,15 @@ func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.Resourc set := &sdk.SecretSetRequest{} unset := &sdk.SecretUnsetRequest{} handleSecretUpdate(d, set, unset) + setForGenericString := &sdk.SetForGenericStringRequest{} if d.HasChange("secret_string") { secretString := d.Get("secret_string").(string) - req := sdk.NewSetForFlowRequest().WithSetForGenericString(*sdk.NewSetForGenericStringRequest().WithSecretString(secretString)) - set.WithSetForFlow(*req) + setForGenericString.WithSecretString(secretString) + } + + if !reflect.DeepEqual(setForGenericString, sdk.SetForGenericStringRequest{}) { + set.WithSetForFlow(sdk.SetForFlowRequest{SetForGenericString: setForGenericString}) } if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { @@ -147,18 +158,3 @@ func UpdateContextSecretWithGenericString(ctx context.Context, d *schema.Resourc return ReadContextSecretWithGenericString(ctx, d, meta) } - -func DeleteContextSecretWithGenericString(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil -} diff --git a/pkg/resources/secret_with_generic_string_acceptance_test.go b/pkg/resources/secret_with_generic_string_acceptance_test.go index 62d22b188c..d2eb8a54cf 100644 --- a/pkg/resources/secret_with_generic_string_acceptance_test.go +++ b/pkg/resources/secret_with_generic_string_acceptance_test.go @@ -54,7 +54,7 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). HasName(name). HasDatabaseName(id.DatabaseName()). - HasSecretType("GENERIC_STRING"). + HasSecretType(sdk.SecretTypeGenericString). HasSchemaName(id.SchemaName()). HasComment(""), ), @@ -64,8 +64,9 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", "GENERIC_STRING"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", sdk.SecretTypeGenericString), resource.TestCheckResourceAttr(secretName, "describe_output.0.username", ""), + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_refresh_token_expiry_time", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", ""), @@ -78,19 +79,32 @@ func TestAcc_SecretWithGenericString_BasicFlow(t *testing.T) { WithSecretString("bar"). WithComment(comment), ), - Check: assert.AssertThat(t, - resourceassert.SecretWithGenericStringResource(t, secretName). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasSecretStringString("bar"). - HasCommentString(comment), + + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithGenericStringResource(t, secretName). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasSecretStringString("bar"). + HasCommentString(comment), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasSecretType(sdk.SecretTypeGenericString). + HasComment(comment), + ), + + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", comment), ), }, // set comment externally, external changes for secret_string are not being detected { PreConfig: func() { - acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest().WithComment("test_comment"))) + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id). + WithSet(*sdk.NewSecretSetRequest(). + WithComment("test_comment"), + ), + ) }, Config: config.FromModel(t, secretModel), ConfigPlanChecks: resource.ConfigPlanChecks{ diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 7bb99cdc22..1b9ac3c1a5 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -4,8 +4,6 @@ import ( "context" "errors" "fmt" - "reflect" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" @@ -13,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "reflect" ) var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { @@ -43,9 +42,9 @@ var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { func SecretWithAuthorizationCodeGrant() *schema.Resource { return &schema.Resource{ CreateContext: CreateContextSecretWithAuthorizationCodeGrant, - ReadContext: ReadContextSecretWithAuthorizationCodeGrant, + ReadContext: ReadContextSecretWithAuthorizationCodeGrant(true), UpdateContext: UpdateContextSecretWithAuthorizationCodeGrant, - DeleteContext: DeleteContextSecretWithAuthorizationCodeGrant, + DeleteContext: DeleteContextSecret, Description: "Resource used to manage secret objects with OAuth Authorization Code Grant. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", Schema: secretAuthorizationCodeGrantSchema, @@ -54,7 +53,9 @@ func SecretWithAuthorizationCodeGrant() *schema.Resource { }, CustomizeDiff: customdiff.All( - ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "oauth_refresh_token_expiry_time", "api_authentication"), + ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, DescribeOutputAttributeName, "name", "oauth_refresh_token_expiry_time", "api_authentication"), + ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, ShowOutputAttributeName, "name", "comment"), + ComputedIfAnyAttributeChanged(secretAuthorizationCodeGrantSchema, FullyQualifiedNameAttributeName, "name"), ), } } @@ -84,7 +85,7 @@ func ImportSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.Resou func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName, schemaName, name := handleSecretCreate(d) + databaseName, schemaName, name := d.Get("database").(string), d.Get("schema").(string), d.Get("name").(string) id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) apiIntegrationString := d.Get("api_authentication").(string) @@ -108,54 +109,64 @@ func CreateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schem d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadContextSecretWithAuthorizationCodeGrant(ctx, d, meta) + return ReadContextSecretWithAuthorizationCodeGrant(false)(ctx, d, meta) } -func ReadContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } +func ReadContextSecretWithAuthorizationCodeGrant(withExternalChangesMarking bool) schema.ReadContextFunc { + return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } - secret, err := client.Secrets.ShowByID(ctx, id) - if err != nil { - if errors.Is(err, sdk.ErrObjectNotFound) { - d.SetId("") + secret, err := client.Secrets.ShowByID(ctx, id) + if err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to retrieve secret with authorization code grant. Target object not found. Marking the resource as removed.", + Detail: fmt.Sprintf("Secret with authorization code grant name: %s, Err: %s", id.FullyQualifiedName(), err), + }, + } + } return diag.Diagnostics{ diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Failed to retrieve secret with authorization code grant. Target object not found. Marking the resource as removed.", + Severity: diag.Error, + Summary: "Failed to retrieve secret with authorization code grant.", Detail: fmt.Sprintf("Secret with authorization code grant name: %s, Err: %s", id.FullyQualifiedName(), err), }, } } - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to retrieve secret with authorization code grant.", - Detail: fmt.Sprintf("Secret with authorization code grant name: %s, Err: %s", id.FullyQualifiedName(), err), - }, + secretDescription, err := client.Secrets.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) } - } - secretDescription, err := client.Secrets.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { - return diag.FromErr(err) - } + if withExternalChangesMarking { + if err = handleExternalValueChangesToObjectInDescribe(d, + describeMapping{"oauth_refresh_token_expiry_time", "oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String(), secretDescription.OauthRefreshTokenExpiryTime.String(), nil}, + ); err != nil { + return diag.FromErr(err) + } + } - if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { - return diag.FromErr(err) - } + if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil { + return diag.FromErr(err) + } - if err := handleSecretRead(d, id, secret, secretDescription); err != nil { - return diag.FromErr(err) - } + if err = d.Set("api_authentication", secretDescription.IntegrationName); err != nil { + return diag.FromErr(err) + } - return nil + if err := handleSecretRead(d, id, secret, secretDescription); err != nil { + return diag.FromErr(err) + } + + return nil + } } func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { @@ -168,21 +179,19 @@ func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schem set := &sdk.SecretSetRequest{} unset := &sdk.SecretUnsetRequest{} handleSecretUpdate(d, set, unset) - - setForFlow := &sdk.SetForFlowRequest{ - SetForOAuthAuthorization: &sdk.SetForOAuthAuthorizationRequest{}, - } + setForOAuthAuthorization := &sdk.SetForOAuthAuthorizationRequest{} if d.HasChange("oauth_refresh_token") { refreshToken := d.Get("oauth_refresh_token").(string) - setForFlow.SetForOAuthAuthorization.WithOauthRefreshToken(refreshToken) - set.WithSetForFlow(*setForFlow) + setForOAuthAuthorization.WithOauthRefreshToken(refreshToken) } if d.HasChange("oauth_refresh_token_expiry_time") { refreshTokenExpiryTime := d.Get("oauth_refresh_token_expiry_time").(string) - setForFlow.SetForOAuthAuthorization.WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) - set.WithSetForFlow(*setForFlow) + setForOAuthAuthorization.WithOauthRefreshTokenExpiryTime(refreshTokenExpiryTime) + } + if !reflect.DeepEqual(setForOAuthAuthorization, sdk.SetForOAuthAuthorizationRequest{}) { + set.WithSetForFlow(sdk.SetForFlowRequest{SetForOAuthAuthorization: setForOAuthAuthorization}) } if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { @@ -197,20 +206,5 @@ func UpdateContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schem } } - return ReadContextSecretWithAuthorizationCodeGrant(ctx, d, meta) -} - -func DeleteContextSecretWithAuthorizationCodeGrant(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil + return ReadContextSecretWithAuthorizationCodeGrant(false)(ctx, d, meta) } diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index 1498dccba5..d5f5e9a25b 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -71,7 +71,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). HasName(name). HasDatabaseName(id.DatabaseName()). - HasSecretType("OAUTH2"). + HasSecretType(sdk.SecretTypeOAuth2). HasSchemaName(id.SchemaName()). HasComment(""), ), @@ -81,11 +81,12 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { resource.TestCheckResourceAttr(secretName, "describe_output.0.name", name), resource.TestCheckResourceAttr(secretName, "describe_output.0.database_name", id.DatabaseName()), resource.TestCheckResourceAttr(secretName, "describe_output.0.schema_name", id.SchemaName()), - resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", "OAUTH2"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.secret_type", sdk.SecretTypeOAuth2), + resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", integrationId.Name()), resource.TestCheckResourceAttr(secretName, "describe_output.0.username", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_access_token_expiry_time", ""), resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time"), - resource.TestCheckResourceAttr(secretName, "describe_output.0.integration_name", integrationId.Name()), + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", ""), resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_scopes.#", "0"), ), }, @@ -97,7 +98,6 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, nil, sdk.String(comment)), planchecks.ExpectChange(secretName, "oauth_refresh_token", tfjson.ActionUpdate, sdk.String(refreshToken), sdk.String(newRefreshToken)), - planchecks.ExpectChange(secretName, "oauth_refresh_token_expiry_time", tfjson.ActionUpdate, sdk.String(refreshTokenExpiryDateTime), sdk.String(newRefreshTokenExpiryDateOnly)), }, }, Check: resource.ComposeTestCheckFunc( @@ -110,8 +110,13 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasOauthRefreshTokenString(newRefreshToken). HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). HasCommentString(comment), - assert.Check(resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time")), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasSecretType(sdk.SecretTypeOAuth2). + HasComment(comment), ), + resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time"), + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", comment), ), }, // set comment and refresh_token_expiry_time externally @@ -119,7 +124,11 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { PreConfig: func() { acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). WithComment("secret resource - changed comment"). - WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(24 * time.Hour).Format(time.DateOnly)))), + WithSetForFlow(*sdk.NewSetForFlowRequest(). + WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest(). + WithOauthRefreshTokenExpiryTime(time.Now().Add(24 * time.Hour).Format(time.DateOnly)), + ), + ), )) }, Config: config.FromModel(t, secretModelExternalChanges), @@ -245,7 +254,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_ExternalChange(t *testing.T) { name := id.Name() comment := random.Comment() refreshTokenExpiryDateTime := time.Now().Add(24 * time.Hour).Format(time.DateTime) - newRefreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + externalRefreshTokenExpiryTime := time.Now().Add(10 * 24 * time.Hour) refreshToken := "test_token" integrationId := acc.TestClient().Ids.RandomAccountObjectIdentifier() @@ -281,7 +290,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_ExternalChange(t *testing.T) { resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). HasName(name). HasDatabaseName(id.DatabaseName()). - HasSecretType("OAUTH2"). + HasSecretType(sdk.SecretTypeOAuth2). HasSchemaName(id.SchemaName()). HasComment(comment), ), @@ -289,23 +298,27 @@ func TestAcc_SecretWithAuthorizationCodeGrant_ExternalChange(t *testing.T) { }, { PreConfig: func() { - acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). - WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest().WithOauthRefreshTokenExpiryTime(time.Now().Add(10 * 24 * time.Hour).Format(time.DateOnly)))), - )) + acc.TestClient().Secret.Alter(t, sdk.NewAlterSecretRequest(id). + WithSet(*sdk.NewSecretSetRequest(). + WithSetForFlow(*sdk.NewSetForFlowRequest(). + WithSetForOAuthAuthorization(*sdk.NewSetForOAuthAuthorizationRequest(). + WithOauthRefreshTokenExpiryTime(externalRefreshTokenExpiryTime.Format(time.DateOnly)), + ), + ), + ), + ) }, - Config: config.FromModel(t, secretModel. - WithOauthRefreshTokenExpiryTime(newRefreshTokenExpiryDateOnly), - ), + Config: config.FromModel(t, secretModel), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), - planchecks.ExpectComputed(secretModel.ResourceReference(), r.DescribeOutputAttributeName, true), + // cannot check before value due to snowflake timestamp format }, }, Check: resource.ComposeTestCheckFunc( assert.AssertThat(t, resourceassert.SecretWithAuthorizationCodeResource(t, secretModel.ResourceReference()). - HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly), + HasOauthRefreshTokenExpiryTimeString(refreshTokenExpiryDateTime), assert.Check(resource.TestCheckResourceAttrSet(secretModel.ResourceReference(), "describe_output.0.oauth_refresh_token_expiry_time")), ), ), diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index 74b2b12ad5..b8882144ef 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -38,9 +39,15 @@ func SecretWithClientCredentials() *schema.Resource { CreateContext: CreateContextSecretWithClientCredentials, ReadContext: ReadContextSecretWithClientCredentials, UpdateContext: UpdateContextSecretWithClientCredentials, - DeleteContext: DeleteContextSecretWithClientCredentials, + DeleteContext: DeleteContextSecret, Description: "Resource used to manage secret objects with OAuth Client Credentials. For more information, check [secret documentation](https://docs.snowflake.com/en/sql-reference/sql/create-secret).", + CustomizeDiff: customdiff.All( + ComputedIfAnyAttributeChanged(secretClientCredentialsSchema, DescribeOutputAttributeName, "name", "oauth_scopes", "api_authentication"), + ComputedIfAnyAttributeChanged(secretClientCredentialsSchema, ShowOutputAttributeName, "name", "comment"), + ComputedIfAnyAttributeChanged(secretClientCredentialsSchema, FullyQualifiedNameAttributeName, "name"), + ), + Schema: secretClientCredentialsSchema, Importer: &schema.ResourceImporter{ StateContext: ImportSecretWithClientCredentials, @@ -78,7 +85,7 @@ func ImportSecretWithClientCredentials(ctx context.Context, d *schema.ResourceDa func CreateContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - databaseName, schemaName, name := handleSecretCreate(d) + databaseName, schemaName, name := d.Get("database").(string), d.Get("schema").(string), d.Get("name").(string) id := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, name) apiIntegrationString := d.Get("api_authentication").(string) @@ -167,6 +174,7 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res set := &sdk.SecretSetRequest{} unset := &sdk.SecretUnsetRequest{} handleSecretUpdate(d, set, unset) + setForClientCredentials := &sdk.SetForOAuthClientCredentialsRequest{} if d.HasChange("oauth_scopes") { stringScopes := expandStringList(d.Get("oauth_scopes").(*schema.Set).List()) @@ -174,8 +182,11 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res for i, scope := range stringScopes { oauthScopes[i] = sdk.ApiIntegrationScope{Scope: scope} } - req := sdk.NewSetForOAuthClientCredentialsRequest().WithOauthScopes(*sdk.NewOauthScopesListRequest(oauthScopes)) - set.WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthClientCredentials(*req)) + setForClientCredentials.WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: oauthScopes}) + } + + if !reflect.DeepEqual(*setForClientCredentials, sdk.SetForOAuthClientCredentialsRequest{}) { + set.WithSetForFlow(sdk.SetForFlowRequest{SetForOAuthClientCredentials: setForClientCredentials}) } if !reflect.DeepEqual(*set, sdk.SecretSetRequest{}) { @@ -192,18 +203,3 @@ func UpdateContextSecretWithClientCredentials(ctx context.Context, d *schema.Res return ReadContextSecretWithClientCredentials(ctx, d, meta) } - -func DeleteContextSecretWithClientCredentials(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseSchemaObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(true)); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil -} diff --git a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go index cf6ff2bb11..f020a278c7 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go @@ -64,7 +64,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). HasName(name). HasDatabaseName(id.DatabaseName()). - HasSecretType("OAUTH2"). + HasSecretType(sdk.SecretTypeOAuth2). HasSchemaName(id.SchemaName()), ), resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "2"), @@ -94,27 +94,39 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { planchecks.ExpectChange(secretName, "oauth_scopes", tfjson.ActionUpdate, sdk.String("[bar foo]"), sdk.String("[test]")), }, }, - Check: assert.AssertThat(t, - resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). - HasNameString(name). - HasDatabaseString(id.DatabaseName()). - HasSchemaString(id.SchemaName()). - HasApiAuthenticationString(integrationId.Name()). - HasOauthScopesLength(len([]string{"test"})). - HasCommentString(newComment), - assert.Check(resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "1")), - assert.Check(resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "test")), + Check: resource.ComposeTestCheckFunc( + assert.AssertThat(t, + resourceassert.SecretWithClientCredentialsResource(t, "snowflake_secret_with_client_credentials.s"). + HasNameString(name). + HasDatabaseString(id.DatabaseName()). + HasSchemaString(id.SchemaName()). + HasApiAuthenticationString(integrationId.Name()). + HasOauthScopesLength(len([]string{"test"})). + HasCommentString(newComment), + assert.Check(resource.TestCheckResourceAttr(secretName, "oauth_scopes.#", "1")), + assert.Check(resource.TestCheckTypeSetElemAttr(secretName, "oauth_scopes.*", "test")), + + resourceshowoutputassert.SecretShowOutput(t, secretModel.ResourceReference()). + HasSecretType(sdk.SecretTypeOAuth2). + HasComment(newComment), + ), + + resource.TestCheckResourceAttr(secretName, "describe_output.0.comment", newComment), + resource.TestCheckResourceAttr(secretName, "describe_output.0.oauth_scopes.#", "1"), + resource.TestCheckTypeSetElemAttr(secretName, "describe_output.0.oauth_scopes.*", "test"), ), }, // set oauth_scopes and comment externally { PreConfig: func() { req := sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest(). - WithSetForFlow(*sdk.NewSetForFlowRequest().WithSetForOAuthClientCredentials( - *sdk.NewSetForOAuthClientCredentialsRequest().WithOauthScopes( - *sdk.NewOauthScopesListRequest([]sdk.ApiIntegrationScope{{Scope: "bar"}}), + WithSetForFlow(*sdk.NewSetForFlowRequest(). + WithSetForOAuthClientCredentials( + *sdk.NewSetForOAuthClientCredentialsRequest().WithOauthScopes( + *sdk.NewOauthScopesListRequest([]sdk.ApiIntegrationScope{{Scope: "bar"}}), + ), ), - )), + ), ) acc.TestClient().Secret.Alter(t, req) }, @@ -152,7 +164,7 @@ func TestAcc_SecretWithClientCredentials_BasicFlow(t *testing.T) { HasCommentString(""), ), }, - // unset comment externally + // set comment externally { PreConfig: func() { req := sdk.NewAlterSecretRequest(id).WithSet(*sdk.NewSecretSetRequest().WithComment(comment)) diff --git a/pkg/resources/show_and_describe_handlers.go b/pkg/resources/show_and_describe_handlers.go index fcf546ee69..b915b82345 100644 --- a/pkg/resources/show_and_describe_handlers.go +++ b/pkg/resources/show_and_describe_handlers.go @@ -43,6 +43,28 @@ type showMapping struct { normalizeFunc func(any) any } +// handleExternalValueChangesToObjectInDescribe assumes that describe output is kept in DescribeOutputAttributeName attribute +func handleExternalValueChangesToObjectInDescribe(d *schema.ResourceData, mappings ...describeMapping) error { + if descOutput, ok := d.GetOk(DescribeOutputAttributeName); ok { + descOutputList := descOutput.([]any) + if len(descOutputList) == 1 { + result := descOutputList[0].(map[string]any) + for _, mapping := range mappings { + valueToCompareFrom := result[mapping.nameInDescribe] + if mapping.normalizeFunc != nil { + valueToCompareFrom = mapping.normalizeFunc(valueToCompareFrom) + } + if valueToCompareFrom != mapping.valueToCompare { + if err := d.Set(mapping.nameInConfig, mapping.valueToSet); err != nil { + return err + } + } + } + } + } + return nil +} + // handleExternalChangesToObjectInDescribe assumes that show output is kept in DescribeOutputAttributeName attribute func handleExternalChangesToObjectInDescribe(d *schema.ResourceData, mappings ...describeMapping) error { if describeOutput, ok := d.GetOk(DescribeOutputAttributeName); ok { diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index a1c2ec1a2f..2f87073b5d 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -1,8 +1,16 @@ package sdk -import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" +import ( + "fmt" + g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" +) //go:generate go run ./poc/main.go +const ( + SecretTypePassword = "PASSWORD" + SecretTypeOAuth2 = "OAUTH2" + SecretTypeGenericString = "GENERIC_STRING" +) var secretDbRow = g.DbStruct("secretDBRow"). Field("created_on", "time.Time"). @@ -112,7 +120,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL(fmt.Sprintf("TYPE = %s", SecretTypeOAuth2))). Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). OptionalQueryStructField("OauthScopes", oauthScopesListDef, g.ParameterOptions().SQL("OAUTH_SCOPES").Parentheses()). OptionalComment(). @@ -128,7 +136,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL(fmt.Sprintf("TYPE = %s", SecretTypeOAuth2))). TextAssignment("OAUTH_REFRESH_TOKEN", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). TextAssignment("OAUTH_REFRESH_TOKEN_EXPIRY_TIME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). @@ -144,7 +152,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = PASSWORD")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL(fmt.Sprintf("TYPE = %s", SecretTypePassword))). TextAssignment("USERNAME", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). TextAssignment("PASSWORD", g.ParameterOptions().NoParentheses().SingleQuotes().Required()). OptionalComment(). @@ -159,7 +167,7 @@ var SecretsDef = g.NewInterface( SQL("SECRET"). IfNotExists(). Name(). - PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = GENERIC_STRING")). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL(fmt.Sprintf("TYPE = %s", SecretTypeGenericString))). TextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes().Required()). OptionalComment(). WithValidation(g.ValidIdentifier, "name"). diff --git a/pkg/sdk/testint/secrets_gen_integration_test.go b/pkg/sdk/testint/secrets_gen_integration_test.go index fef05740ba..fa8042701c 100644 --- a/pkg/sdk/testint/secrets_gen_integration_test.go +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -19,7 +19,6 @@ func TestInt_Secrets(t *testing.T) { integrationId := testClientHelper().Ids.RandomAccountObjectIdentifier() - // "YYYY-MM-DD" or "YYYY-MM-DD HH:MI-SS" format has to be used, otherwise Snowflake returns error: "Invalid date/time format" refreshTokenExpiryTime := time.Now().Add(24 * time.Hour).Format(time.DateOnly) _, apiIntegrationCleanup := testClientHelper().SecurityIntegration.CreateApiAuthenticationClientCredentialsWithRequest(t, @@ -76,7 +75,7 @@ func TestInt_Secrets(t *testing.T) { objectassert.Secret(t, id). HasName(id.Name()). HasComment("a"). - HasSecretType("OAUTH2"). + HasSecretType(sdk.SecretTypeOAuth2). HasOauthScopes([]string{"foo", "bar"}). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), @@ -88,7 +87,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), Comment: sdk.String("a"), - SecretType: "OAUTH2", + SecretType: sdk.SecretTypeOAuth2, OauthScopes: []string{"foo", "bar"}, IntegrationName: sdk.String(integrationId.Name()), }) @@ -115,7 +114,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "OAUTH2", + SecretType: sdk.SecretTypeOAuth2, IntegrationName: sdk.String(integrationId.Name()), }) }) @@ -165,7 +164,7 @@ func TestInt_Secrets(t *testing.T) { objectassert.Secret(t, id). HasName(id.Name()). HasComment("a"). - HasSecretType("OAUTH2"). + HasSecretType(sdk.SecretTypeOAuth2). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), ) @@ -175,7 +174,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "OAUTH2", + SecretType: sdk.SecretTypeOAuth2, Comment: sdk.String("a"), OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, refreshTokenExpiryTime), IntegrationName: sdk.String(integrationId.Name()), @@ -197,7 +196,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "OAUTH2", + SecretType: sdk.SecretTypeOAuth2, OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateTime, refreshTokenWithTime), IntegrationName: sdk.String(integrationId.Name()), }) @@ -221,7 +220,7 @@ func TestInt_Secrets(t *testing.T) { objectassert.SecretFromObject(t, secret). HasName(id.Name()). HasComment(comment). - HasSecretType("PASSWORD"). + HasSecretType(sdk.SecretTypePassword). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), ) @@ -232,7 +231,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), Comment: sdk.String(comment), - SecretType: "PASSWORD", + SecretType: sdk.SecretTypePassword, Username: sdk.String("foo"), }) }) @@ -251,7 +250,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "PASSWORD", + SecretType: sdk.SecretTypePassword, Username: sdk.String(""), }) }) @@ -274,7 +273,7 @@ func TestInt_Secrets(t *testing.T) { objectassert.Secret(t, id). HasName(id.Name()). HasComment(comment). - HasSecretType("GENERIC_STRING"). + HasSecretType(sdk.SecretTypeGenericString). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), ) @@ -291,7 +290,7 @@ func TestInt_Secrets(t *testing.T) { assertions.AssertThat(t, objectassert.Secret(t, id). HasName(id.Name()). - HasSecretType("GENERIC_STRING"). + HasSecretType(sdk.SecretTypeGenericString). HasDatabaseName(id.DatabaseName()). HasSchemaName(id.SchemaName()), ) @@ -321,7 +320,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "OAUTH2", + SecretType: sdk.SecretTypeOAuth2, Comment: sdk.String(comment), OauthScopes: []string{"foo", "bar"}, IntegrationName: sdk.String(integrationId.Name()), @@ -368,7 +367,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "OAUTH2", + SecretType: sdk.SecretTypeOAuth2, Comment: sdk.String(comment), OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, alteredRefreshTokenExpiryTime), IntegrationName: sdk.String(integrationId.Name()), @@ -415,7 +414,7 @@ func TestInt_Secrets(t *testing.T) { // Cannot check password property since show and describe on secret do not have access to it assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "PASSWORD", + SecretType: sdk.SecretTypePassword, Comment: sdk.String(comment), Username: sdk.String("bar"), }) @@ -713,7 +712,7 @@ func TestInt_Secrets(t *testing.T) { assertSecretDetails(details, secretDetails{ Name: id.Name(), - SecretType: "GENERIC_STRING", + SecretType: sdk.SecretTypeGenericString, }) }) } From dba847879fca3cd32173cf41ecd3c4d570a5d9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 14 Oct 2024 09:29:17 +0200 Subject: [PATCH 64/65] changes not to abuse pointer on models --- pkg/resources/secret_common.go | 6 +----- ...auth_authorization_code_grant_acceptance_test.go | 13 +++++-------- ...with_oauth_client_credentials_acceptance_test.go | 5 ++--- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index 81d9368ae9..0ceb2cd87b 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -64,16 +64,12 @@ func handleSecretImport(d *schema.ResourceData) error { } func handleSecretRead(d *schema.ResourceData, id sdk.SchemaObjectIdentifier, secret *sdk.Secret, secretDescription *sdk.SecretDetails) error { - errs := errors.Join( + return errors.Join( d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()), d.Set("comment", secret.Comment), d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecretToSchema(secret)}), d.Set(DescribeOutputAttributeName, []map[string]any{schemas.SecretDescriptionToSchema(*secretDescription)}), ) - if errs != nil { - return errs - } - return nil } func handleSecretUpdate(d *schema.ResourceData, set *sdk.SecretSetRequest, unset *sdk.SecretUnsetRequest) { diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go index d5f5e9a25b..b8f6373a46 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant_acceptance_test.go @@ -27,10 +27,8 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() name := id.Name() comment := random.Comment() - newComment := random.Comment() refreshTokenExpiryDateTime := time.Now().Add(24 * time.Hour).Format(time.DateTime) newRefreshTokenExpiryDateOnly := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) - externallyChangedRefreshTokenExpiryTime := time.Now().Add(10 * 24 * time.Hour).Format(time.DateOnly) refreshToken := "test_token" newRefreshToken := "new_test_token" @@ -42,7 +40,6 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { secretModel := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, refreshToken, refreshTokenExpiryDateTime) secretModelAllSet := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, newRefreshToken, newRefreshTokenExpiryDateOnly).WithComment(comment) - secretModelExternalChanges := model.SecretWithAuthorizationCodeGrant("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, newRefreshToken, externallyChangedRefreshTokenExpiryTime).WithComment(newComment) secretName := secretModel.ResourceReference() @@ -131,11 +128,11 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { ), )) }, - Config: config.FromModel(t, secretModelExternalChanges), + Config: config.FromModel(t, secretModelAllSet), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction(secretName, plancheck.ResourceActionUpdate), - planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String(newComment)), + planchecks.ExpectChange(secretName, "comment", tfjson.ActionUpdate, sdk.String("secret resource - changed comment"), sdk.String(comment)), planchecks.ExpectComputed(secretName, r.DescribeOutputAttributeName, true), }, }, @@ -147,8 +144,8 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). HasOauthRefreshTokenString(newRefreshToken). - HasOauthRefreshTokenExpiryTimeString(externallyChangedRefreshTokenExpiryTime). - HasCommentString(newComment), + HasOauthRefreshTokenExpiryTimeString(newRefreshTokenExpiryDateOnly). + HasCommentString(comment), assert.Check(resource.TestCheckResourceAttrSet(secretName, "describe_output.0.oauth_refresh_token_expiry_time")), ), ), @@ -165,7 +162,7 @@ func TestAcc_SecretWithAuthorizationCodeGrant_BasicFlow(t *testing.T) { HasDatabaseString(id.DatabaseName()). HasSchemaString(id.SchemaName()). HasApiAuthenticationString(integrationId.Name()). - HasCommentString(newComment). + HasCommentString(comment). HasOauthRefreshTokenExpiryTimeNotEmpty(), ), }, diff --git a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go index f020a278c7..265f479d1c 100644 --- a/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go +++ b/pkg/resources/secret_with_oauth_client_credentials_acceptance_test.go @@ -230,6 +230,7 @@ func TestAcc_SecretWithClientCredentials_EmptyScopesList(t *testing.T) { t.Cleanup(apiIntegrationCleanup) secretModel := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{}) + secretModelEmptyScopes := model.SecretWithClientCredentials("s", integrationId.Name(), id.DatabaseName(), id.SchemaName(), name, []string{}) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -277,9 +278,7 @@ func TestAcc_SecretWithClientCredentials_EmptyScopesList(t *testing.T) { }, // Set empty oauth_scopes { - Config: config.FromModel(t, secretModel. - WithOauthScopes([]string{}), - ), + Config: config.FromModel(t, secretModelEmptyScopes), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate), From 40ecd49f655ebc63e647f745ad0a9847b2ea8585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Mon, 14 Oct 2024 09:32:07 +0200 Subject: [PATCH 65/65] linter adjustments --- pkg/resources/diff_suppressions.go | 7 ++++--- pkg/resources/diff_suppressions_test.go | 3 ++- pkg/resources/secret_common.go | 1 + pkg/resources/secret_with_basic_authentication.go | 2 +- pkg/resources/secret_with_generic_string.go | 2 +- .../secret_with_oauth_authorization_code_grant.go | 3 ++- pkg/resources/secret_with_oauth_client_credentials.go | 2 +- pkg/sdk/secrets_def.go | 1 + 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pkg/resources/diff_suppressions.go b/pkg/resources/diff_suppressions.go index 05c30c66d5..3e23be3ee0 100644 --- a/pkg/resources/diff_suppressions.go +++ b/pkg/resources/diff_suppressions.go @@ -2,13 +2,14 @@ package resources import ( "fmt" + "log" + "slices" + "strings" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "log" - "slices" - "strings" ) func NormalizeAndCompare[T comparable](normalize func(string) (T, error)) schema.SchemaDiffSuppressFunc { diff --git a/pkg/resources/diff_suppressions_test.go b/pkg/resources/diff_suppressions_test.go index b236bd00a3..7c54f03938 100644 --- a/pkg/resources/diff_suppressions_test.go +++ b/pkg/resources/diff_suppressions_test.go @@ -2,12 +2,13 @@ package resources_test import ( "fmt" + "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func Test_NormalizeAndCompare(t *testing.T) { diff --git a/pkg/resources/secret_common.go b/pkg/resources/secret_common.go index 0ceb2cd87b..95ceebc949 100644 --- a/pkg/resources/secret_common.go +++ b/pkg/resources/secret_common.go @@ -3,6 +3,7 @@ package resources import ( "context" "errors" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" diff --git a/pkg/resources/secret_with_basic_authentication.go b/pkg/resources/secret_with_basic_authentication.go index ce8aca14b4..2a401ab705 100644 --- a/pkg/resources/secret_with_basic_authentication.go +++ b/pkg/resources/secret_with_basic_authentication.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -12,6 +11,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) diff --git a/pkg/resources/secret_with_generic_string.go b/pkg/resources/secret_with_generic_string.go index 5d30de4065..42c2fc6f92 100644 --- a/pkg/resources/secret_with_generic_string.go +++ b/pkg/resources/secret_with_generic_string.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -12,6 +11,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) diff --git a/pkg/resources/secret_with_oauth_authorization_code_grant.go b/pkg/resources/secret_with_oauth_authorization_code_grant.go index 1b9ac3c1a5..97025a1bc8 100644 --- a/pkg/resources/secret_with_oauth_authorization_code_grant.go +++ b/pkg/resources/secret_with_oauth_authorization_code_grant.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "reflect" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" @@ -11,7 +13,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "reflect" ) var secretAuthorizationCodeGrantSchema = func() map[string]*schema.Schema { diff --git a/pkg/resources/secret_with_oauth_client_credentials.go b/pkg/resources/secret_with_oauth_client_credentials.go index b8882144ef..1a387efe0e 100644 --- a/pkg/resources/secret_with_oauth_client_credentials.go +++ b/pkg/resources/secret_with_oauth_client_credentials.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "reflect" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -12,6 +11,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go index 2f87073b5d..44c80475c6 100644 --- a/pkg/sdk/secrets_def.go +++ b/pkg/sdk/secrets_def.go @@ -2,6 +2,7 @@ package sdk import ( "fmt" + g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" )