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 5596f476a6..a5aae66a99 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{}, + }, { IdType: "sdk.SchemaObjectIdentifier", ObjectType: sdk.ObjectTypeStream, 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..44ebf3c3ef --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/secret_snowflake_gen.go @@ -0,0 +1,135 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package objectassert + +import ( + "fmt" + "slices" + "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/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 time.Time) *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 !slices.Equal(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 new file mode 100644 index 0000000000..e3eb87c8f1 --- /dev/null +++ b/pkg/acceptance/helpers/secret_client.go @@ -0,0 +1,100 @@ +package helpers + +import ( + "context" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +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 +} + +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). + WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: 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) + + err := c.client().CreateWithBasicAuthentication(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) 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() + + return func() { + 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/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/acceptance/helpers/test_client.go b/pkg/acceptance/helpers/test_client.go index 90fb4f7e8f..9ddfd21102 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 @@ -113,6 +114,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..b5a76425c9 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 @@ -235,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} 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/poc/README.md b/pkg/sdk/poc/README.md index 44af1e130b..28786b72d7 100644 --- a/pkg/sdk/poc/README.md +++ b/pkg/sdk/poc/README.md @@ -132,6 +132,29 @@ type SomeReq struct { } ``` +- Wrong generated validations for `ConflictingFields` for more complicated validations + - For now `everyValueSet()` should be manually changed to `moreThanOneValueSet()` if there is a need to validate only one of many options + - Consider following example for `secret`: +```go +// For validation Conflicting fields: +WithValidation(g.ConflictingFields, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") +``` +```go +// Generation results in: +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")) + } +} + +// For now needs to be manually changed to: +if valueSet(opts.Set) { + if moreThanOneValueSet(opts.Set.SetForOAuthClientCredentialsFlow, opts.Set.SetForOAuthAuthorizationFlow, opts.Set.SetForBasicAuthentication, opts.Set.SetForGenericString) { + errs = append(errs, errOneOf("AlterSecretOptions.Set", "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString")) + } +} +``` + ##### 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 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/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", diff --git a/pkg/sdk/secrets_def.go b/pkg/sdk/secrets_def.go new file mode 100644 index 0000000000..d816f19dcd --- /dev/null +++ b/pkg/sdk/secrets_def.go @@ -0,0 +1,207 @@ +package sdk + +import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" + +//go:generate go run ./poc/main.go + +var secretDbRow = g.DbStruct("secretDBRow"). + Field("created_on", "time.Time"). + 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"). + Field("owner_role_type", "string") + +var secret = g.PlainStruct("Secret"). + Field("CreatedOn", "time.Time"). + Field("Name", "string"). + Field("SchemaName", "string"). + Field("DatabaseName", "string"). + Field("Owner", "string"). + Field("Comment", "*string"). + Field("SecretType", "string"). + Field("OauthScopes", "[]string"). + Field("OwnerRoleType", "string") + +var secretDetailsDbRow = g.DbStruct("secretDetailsDBRow"). + Field("created_on", "time.Time"). + 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", "*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", "time.Time"). + Field("Name", "string"). + Field("SchemaName", "string"). + Field("DatabaseName", "string"). + Field("Owner", "string"). + Field("Comment", "*string"). + Field("SecretType", "string"). + Field("Username", "*string"). + Field("OauthAccessTokenExpiryTime", "*time.Time"). + Field("OauthRefreshTokenExpiryTime", "*time.Time"). + 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"). + OptionalQueryStructField("OauthScopes", oauthScopesListDef, g.ParameterOptions().SQL("OAUTH_SCOPES").Parentheses()), + g.KeywordOptions(), + ). + OptionalQueryStructField( + "SetForOAuthAuthorizationFlow", + g.NewQueryStruct("SetForOAuthAuthorizationFlow"). + 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.ConflictingFields, "SetForOAuthClientCredentialsFlow", "SetForOAuthAuthorizationFlow", "SetForBasicAuthentication", "SetForGenericString") + +// UNSET doest work, need to use "SET COMMENT = NULL" +var secretUnset = g.NewQueryStruct("SecretUnset"). + PredefinedQueryStructField("Comment", "*bool", g.KeywordOptions().SQL("SET COMMENT = NULL")) + +var SecretsDef = g.NewInterface( + "Secrets", + "Secret", + g.KindOfT[SchemaObjectIdentifier](), +).CustomOperation( + "CreateWithOAuthClientCredentialsFlow", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithOAuthClientCredentialsFlow"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = OAUTH2")). + Identifier("ApiIntegration", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required().Equals().SQL("API_AUTHENTICATION")). + OptionalQueryStructField("OauthScopes", oauthScopesListDef, g.ParameterOptions().SQL("OAUTH_SCOPES").Parentheses()). + OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), + secretsApiIntegrationScopeDef, +).CustomOperation( + "CreateWithOAuthAuthorizationCodeFlow", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithOAuthAuthorizationCodeFlow"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + 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")). + OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), +).CustomOperation( + "CreateWithBasicAuthentication", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithBasicAuthentication"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = PASSWORD")). + 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", + "https://docs.snowflake.com/en/sql-reference/sql/create-secret", + g.NewQueryStruct("CreateWithGenericString"). + Create(). + OrReplace(). + SQL("SECRET"). + IfNotExists(). + Name(). + PredefinedQueryStructField("secretType", "string", g.StaticOptions().SQL("TYPE = GENERIC_STRING")). + TextAssignment("SECRET_STRING", g.ParameterOptions().SingleQuotes().Required()). + 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(), + ). + 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-secrets", + secretDbRow, + secret, + g.NewQueryStruct("ShowSecret"). + Show(). + SQL("SECRETS"). + OptionalLike(). + OptionalExtendedIn(), +).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 new file mode 100644 index 0000000000..25eff46b72 --- /dev/null +++ b/pkg/sdk/secrets_dto_builders_gen.go @@ -0,0 +1,266 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewCreateWithOAuthClientCredentialsFlowSecretRequest( + name SchemaObjectIdentifier, + ApiIntegration AccountObjectIdentifier, +) *CreateWithOAuthClientCredentialsFlowSecretRequest { + s := CreateWithOAuthClientCredentialsFlowSecretRequest{} + s.name = name + s.ApiIntegration = ApiIntegration + 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 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, + OauthRefreshTokenExpiryTime string, + ApiIntegration AccountObjectIdentifier, +) *CreateWithOAuthAuthorizationCodeFlowSecretRequest { + s := CreateWithOAuthAuthorizationCodeFlowSecretRequest{} + s.name = name + s.OauthRefreshToken = OauthRefreshToken + s.OauthRefreshTokenExpiryTime = OauthRefreshTokenExpiryTime + s.ApiIntegration = ApiIntegration + 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) 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 NewSetForOAuthClientCredentialsFlowRequest() *SetForOAuthClientCredentialsFlowRequest { + return &SetForOAuthClientCredentialsFlowRequest{} +} + +func (s *SetForOAuthClientCredentialsFlowRequest) WithOauthScopes(OauthScopes OauthScopesListRequest) *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 *SetForOAuthAuthorizationFlowRequest) WithOauthRefreshTokenExpiryTime(OauthRefreshTokenExpiryTime string) *SetForOAuthAuthorizationFlowRequest { + s.OauthRefreshTokenExpiryTime = &OauthRefreshTokenExpiryTime + return s +} + +func NewSetForBasicAuthenticationRequest() *SetForBasicAuthenticationRequest { + return &SetForBasicAuthenticationRequest{} +} + +func (s *SetForBasicAuthenticationRequest) WithUsername(Username string) *SetForBasicAuthenticationRequest { + s.Username = &Username + return s +} + +func (s *SetForBasicAuthenticationRequest) WithPassword(Password string) *SetForBasicAuthenticationRequest { + s.Password = &Password + return s +} + +func NewSetForGenericStringRequest() *SetForGenericStringRequest { + return &SetForGenericStringRequest{} +} + +func (s *SetForGenericStringRequest) WithSecretString(SecretString string) *SetForGenericStringRequest { + s.SecretString = &SecretString + return s +} + +func NewSecretUnsetRequest() *SecretUnsetRequest { + return &SecretUnsetRequest{} +} + +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 ExtendedIn) *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 new file mode 100644 index 0000000000..656ca9ac69 --- /dev/null +++ b/pkg/sdk/secrets_dto_gen.go @@ -0,0 +1,105 @@ +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) + _ optionsProvider[DropSecretOptions] = new(DropSecretRequest) + _ optionsProvider[ShowSecretOptions] = new(ShowSecretRequest) + _ optionsProvider[DescribeSecretOptions] = new(DescribeSecretRequest) +) + +type CreateWithOAuthClientCredentialsFlowSecretRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + ApiIntegration AccountObjectIdentifier // required + OauthScopes *OauthScopesListRequest + Comment *string +} + +type OauthScopesListRequest struct { + OauthScopesList []ApiIntegrationScope // required +} + +type CreateWithOAuthAuthorizationCodeFlowSecretRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + OauthRefreshToken string // required + OauthRefreshTokenExpiryTime string // required + ApiIntegration 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 + SetForOAuthClientCredentialsFlow *SetForOAuthClientCredentialsFlowRequest + SetForOAuthAuthorizationFlow *SetForOAuthAuthorizationFlowRequest + SetForBasicAuthentication *SetForBasicAuthenticationRequest + SetForGenericString *SetForGenericStringRequest +} + +type SetForOAuthClientCredentialsFlowRequest struct { + OauthScopes *OauthScopesListRequest +} + +type SetForOAuthAuthorizationFlowRequest struct { + OauthRefreshToken *string + OauthRefreshTokenExpiryTime *string +} + +type SetForBasicAuthenticationRequest struct { + Username *string + Password *string +} + +type SetForGenericStringRequest struct { + SecretString *string +} + +type SecretUnsetRequest struct { + Comment *bool +} + +type DropSecretRequest struct { + IfExists *bool + name SchemaObjectIdentifier // required +} + +type ShowSecretRequest struct { + Like *Like + In *ExtendedIn +} + +type DescribeSecretRequest struct { + name SchemaObjectIdentifier // required +} diff --git a/pkg/sdk/secrets_gen.go b/pkg/sdk/secrets_gen.go new file mode 100644 index 0000000000..db2d30df35 --- /dev/null +++ b/pkg/sdk/secrets_gen.go @@ -0,0 +1,192 @@ +package sdk + +import ( + "context" + "database/sql" + "time" +) + +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 + 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. +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"` + secretType string `ddl:"static" sql:"TYPE = OAUTH2"` + ApiIntegration AccountObjectIdentifier `ddl:"identifier,equals" sql:"API_AUTHENTICATION"` + 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 { + 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"` + 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"` + 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"` + 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"` +} + +// 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"` + 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"` +} + +// 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"` +} +type SecretSet struct { + 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 SetForOAuthClientCredentialsFlow struct { + OauthScopes *OauthScopesList `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 { + 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-secrets. +type ShowSecretOptions struct { + 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 time.Time `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 time.Time + Name string + SchemaName string + DatabaseName string + Owner string + Comment *string + SecretType string + OauthScopes []string + OwnerRoleType string +} + +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"` + secret bool `ddl:"static" sql:"SECRET"` + name SchemaObjectIdentifier `ddl:"identifier"` +} +type secretDetailsDBRow struct { + CreatedOn time.Time `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 *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"` +} +type SecretDetails struct { + CreatedOn time.Time + Name string + SchemaName string + DatabaseName string + Owner string + Comment *string + SecretType string + Username *string + OauthAccessTokenExpiryTime *time.Time + OauthRefreshTokenExpiryTime *time.Time + OauthScopes []string + IntegrationName *string +} diff --git a/pkg/sdk/secrets_gen_test.go b/pkg/sdk/secrets_gen_test.go new file mode 100644 index 0000000000..458d505af0 --- /dev/null +++ b/pkg/sdk/secrets_gen_test.go @@ -0,0 +1,356 @@ +package sdk + +import "testing" + +func TestSecrets_CreateWithOAuthClientCredentialsFlow(t *testing.T) { + id := randomSchemaObjectIdentifier() + + 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: invalid identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + 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) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthClientCredentialsFlowSecretOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("all options", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + + opts := defaultOpts() + opts.IfNotExists = Bool(true) + opts.ApiIntegration = integration + 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()) + }) + + t.Run("empty oauth scopes list", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + + opts := defaultOpts() + opts.IfNotExists = Bool(true) + opts.ApiIntegration = integration + opts.OauthScopes = &OauthScopesList{[]ApiIntegrationScope{}} + opts.Comment = String("foo") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECRET IF NOT EXISTS %s TYPE = OAUTH2 API_AUTHENTICATION = %s OAUTH_SCOPES = () COMMENT = 'foo'", id.FullyQualifiedName(), integration.FullyQualifiedName()) + }) +} + +func TestSecrets_CreateWithOAuthAuthorizationCodeFlow(t *testing.T) { + id := randomSchemaObjectIdentifier() + + 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: invalid identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + 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) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithOAuthAuthorizationCodeFlowSecretOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("all options", func(t *testing.T) { + integration := randomAccountObjectIdentifier() + + 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()) + }) +} + +func TestSecrets_CreateWithBasicAuthentication(t *testing.T) { + id := randomSchemaObjectIdentifier() + + 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: invalid identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + 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) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithBasicAuthenticationSecretOptions", "OrReplace", "IfNotExists")) + }) + + 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()) + }) +} + +func TestSecrets_CreateWithGenericString(t *testing.T) { + id := randomSchemaObjectIdentifier() + + 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: invalid identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + 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) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateWithGenericStringSecretOptions", "OrReplace", "IfNotExists")) + }) + + 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' COMMENT = 'test'", id.FullyQualifiedName()) + }) +} + +func TestSecrets_Alter(t *testing.T) { + id := randomSchemaObjectIdentifier() + + 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() + 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: &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")} + 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{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: &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()) + }) + + t.Run("alter: set options for Oauth Authorization Flow", func(t *testing.T) { + opts := setOpts() + opts.Set.Comment = String("test") + opts.Set.SetForOAuthAuthorizationFlow = &SetForOAuthAuthorizationFlow{ + 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()) + }) + + 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() + + 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: invalid identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "DROP SECRET %s", id.FullyQualifiedName()) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "DROP SECRET IF EXISTS %s", id.FullyQualifiedName()) + }) +} + +func TestSecrets_Show(t *testing.T) { + 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() + 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'") + }) + + t.Run("show with in", func(t *testing.T) { + opts := defaultOpts() + opts.In = &ExtendedIn{ + In: In{ + Account: Bool(true), + }, + } + assertOptsValidAndSQLEquals(t, opts, "SHOW SECRETS IN ACCOUNT") + }) +} + +func TestSecrets_Describe(t *testing.T) { + id := randomSchemaObjectIdentifier() + + 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: invalid identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifier + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "DESCRIBE SECRET %s", id.FullyQualifiedName()) + }) +} diff --git a/pkg/sdk/secrets_impl_gen.go b/pkg/sdk/secrets_impl_gen.go new file mode 100644 index 0000000000..f6804181f0 --- /dev/null +++ b/pkg/sdk/secrets_impl_gen.go @@ -0,0 +1,258 @@ +package sdk + +import ( + "context" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" +) + +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 (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) { + 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 + } + 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, + IfNotExists: r.IfNotExists, + name: r.name, + ApiIntegration: r.ApiIntegration, + + Comment: r.Comment, + } + + if r.OauthScopes != nil { + opts.OauthScopes = &OauthScopesList{ + OauthScopesList: r.OauthScopes.OauthScopesList, + } + } + + return opts +} + +func (r *CreateWithOAuthAuthorizationCodeFlowSecretRequest) toOpts() *CreateWithOAuthAuthorizationCodeFlowSecretOptions { + opts := &CreateWithOAuthAuthorizationCodeFlowSecretOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + OauthRefreshToken: r.OauthRefreshToken, + OauthRefreshTokenExpiryTime: r.OauthRefreshTokenExpiryTime, + ApiIntegration: r.ApiIntegration, + 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, + } + + if r.Set.SetForOAuthClientCredentialsFlow != nil { + opts.Set.SetForOAuthClientCredentialsFlow = &SetForOAuthClientCredentialsFlow{} + + if r.Set.SetForOAuthClientCredentialsFlow.OauthScopes != nil { + opts.Set.SetForOAuthClientCredentialsFlow.OauthScopes = &OauthScopesList{ + OauthScopesList: r.Set.SetForOAuthClientCredentialsFlow.OauthScopes.OauthScopesList, + } + } + } + + 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 +} + +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 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, + Name: r.Name, + SchemaName: r.SchemaName, + DatabaseName: r.DatabaseName, + Owner: r.Owner, + SecretType: r.SecretType, + OwnerRoleType: r.OwnerRoleType, + } + if r.Comment.Valid { + s.Comment = String(r.Comment.String) + } + if r.OauthScopes.Valid { + s.OauthScopes = getOauthScopes(r.OauthScopes.String) + } + return s +} + +func (r *DescribeSecretRequest) toOpts() *DescribeSecretOptions { + opts := &DescribeSecretOptions{ + name: r.name, + } + return opts +} + +func (r secretDetailsDBRow) convert() *SecretDetails { + s := &SecretDetails{ + CreatedOn: r.CreatedOn, + Name: r.Name, + SchemaName: r.SchemaName, + DatabaseName: r.DatabaseName, + Owner: r.Owner, + SecretType: r.SecretType, + OauthAccessTokenExpiryTime: r.OauthAccessTokenExpiryTime, + OauthRefreshTokenExpiryTime: r.OauthRefreshTokenExpiryTime, + } + 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 new file mode 100644 index 0000000000..a324b74dee --- /dev/null +++ b/pkg/sdk/secrets_validations_gen.go @@ -0,0 +1,114 @@ +package sdk + +var ( + _ validatable = new(CreateWithOAuthClientCredentialsFlowSecretOptions) + _ validatable = new(CreateWithOAuthAuthorizationCodeFlowSecretOptions) + _ validatable = new(CreateWithBasicAuthenticationSecretOptions) + _ validatable = new(CreateWithGenericStringSecretOptions) + _ validatable = new(AlterSecretOptions) + _ validatable = new(DropSecretOptions) + _ validatable = new(ShowSecretOptions) + _ validatable = new(DescribeSecretOptions) +) + +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")) + } + if valueSet(opts.Set) { + if moreThanOneValueSet(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...) +} + +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...) +} 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 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..5e845368db --- /dev/null +++ b/pkg/sdk/testint/secrets_gen_integration_test.go @@ -0,0 +1,716 @@ +package testint + +import ( + "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" + "github.com/stretchr/testify/require" +) + +func TestInt_Secrets(t *testing.T) { + client := testClient(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, + sdk.NewCreateApiAuthenticationWithClientCredentialsFlowSecurityIntegrationRequest(integrationId, true, "foo", "foo"). + WithOauthAllowedScopes([]sdk.AllowedScope{{Scope: "foo"}, {Scope: "bar"}}), + ) + t.Cleanup(apiIntegrationCleanup) + + 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 + } + + type secretDetails struct { + Name string + Comment *string + SecretType string + 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.EqualValues(t, expected.Comment, actual.Comment) + assert.Equal(t, expected.SecretType, actual.SecretType) + assert.EqualValues(t, expected.Username, actual.Username) + assert.Equal(t, expected.OauthAccessTokenExpiryTime, actual.OauthAccessTokenExpiryTime) + assert.Equal(t, expected.OauthRefreshTokenExpiryTime, actual.OauthRefreshTokenExpiryTime) + assert.EqualValues(t, expected.OauthScopes, actual.OauthScopes) + assert.EqualValues(t, expected.IntegrationName, actual.IntegrationName) + } + + t.Run("Create: secretWithOAuthClientCredentialsFlow", func(t *testing.T) { + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithOAuthClientCredentialsFlowSecretRequest(id, integrationId). + WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: []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([]string{"foo", "bar"}). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + Comment: sdk.String("a"), + SecretType: "OAUTH2", + OauthScopes: []string{"foo", "bar"}, + IntegrationName: sdk.String(integrationId.Name()), + }) + }) + + // 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) + + 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", + IntegrationName: sdk.String(integrationId.Name()), + }) + }) + + // 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).WithOauthScopes(sdk.OauthScopesListRequest{}) + + 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([]string{}). + 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: "[]"}) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assert.NotContains(t, details.OauthScopes, "foo") + assert.NotContains(t, details.OauthScopes, "bar") + assert.Equal(t, []string{""}, details.OauthScopes) + }) + + 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) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "OAUTH2", + Comment: sdk.String("a"), + OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, refreshTokenExpiryTime), + IntegrationName: sdk.String(integrationId.Name()), + }) + }) + + 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)) + + secret, err := client.Secrets.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThat(t, + objectassert.SecretFromObject(t, secret). + HasName(id.Name()). + HasComment(comment). + HasSecretType("PASSWORD"). + HasDatabaseName(id.DatabaseName()). + HasSchemaName(id.SchemaName()), + ) + + details, err := client.Secrets.Describe(ctx, id) + require.NoError(t, err) + + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + Comment: sdk.String(comment), + SecretType: "PASSWORD", + Username: sdk.String("foo"), + }) + }) + + t.Run("Create: WithBasicAuthentication - Empty Username and Password", func(t *testing.T) { + 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(), + SecretType: "PASSWORD", + Username: sdk.String(""), + }) + }) + + t.Run("Create: WithGenericString", func(t *testing.T) { + comment := random.Comment() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + request := sdk.NewCreateWithGenericStringSecretRequest(id, "secret"). + WithComment(comment). + WithIfNotExists(true) + + 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) { + 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)) + + 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 := testClientHelper().Ids.RandomSchemaObjectIdentifier() + _, secretDropFunc := testClientHelper().Secret.CreateWithOAuthClientCredentialsFlow(t, id, integrationId, []sdk.ApiIntegrationScope{{Scope: "foo"}}) + t.Cleanup(secretDropFunc) + + setRequest := sdk.NewAlterSecretRequest(id). + WithSet( + *sdk.NewSecretSetRequest(). + WithComment(comment). + WithSetForOAuthClientCredentialsFlow( + *sdk.NewSetForOAuthClientCredentialsFlowRequest(). + WithOauthScopes(sdk.OauthScopesListRequest{OauthScopesList: []sdk.ApiIntegrationScope{{Scope: "foo"}, {Scope: "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: sdk.String(comment), + OauthScopes: []string{"foo", "bar"}, + IntegrationName: sdk.String(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.Empty(t, details.Comment) + }) + + t.Run("Alter: SecretWithOAuthAuthorizationCode", func(t *testing.T) { + comment := random.Comment() + alteredRefreshTokenExpiryTime := time.Now().Add(4 * 24 * time.Hour).Format(time.DateOnly) + + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + _, secretCleanup := testClientHelper().Secret.CreateWithOAuthAuthorizationCodeFlow(t, id, integrationId, "foo", refreshTokenExpiryTime) + t.Cleanup(secretCleanup) + + 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: sdk.String(comment), + OauthRefreshTokenExpiryTime: stringDateToSnowflakeTimeFormat(time.DateOnly, alteredRefreshTokenExpiryTime), + IntegrationName: sdk.String(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.Empty(t, details.Comment) + }) + + 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) + + 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 property since show and describe on secret do not have access to it + assertSecretDetails(details, secretDetails{ + Name: id.Name(), + SecretType: "PASSWORD", + Comment: sdk.String(comment), + Username: sdk.String("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.Empty(t, details.Comment) + }) + + t.Run("Alter: SecretWithGenericString", func(t *testing.T) { + comment := random.Comment() + id := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + _, secretCleanup := testClientHelper().Secret.CreateWithGenericString(t, id, "foo") + t.Cleanup(secretCleanup) + 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 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) + require.NoError(t, err) + + assert.Empty(t, details.Comment) + }) + + t.Run("Drop", func(t *testing.T) { + 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) + 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) { + 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) + 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) { + 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()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithOAuthAuthorization with Like", func(t *testing.T) { + 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()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithBasicAuthentication with Like", func(t *testing.T) { + 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()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithGenericString with Like", func(t *testing.T) { + 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()), + })) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret1) + require.NotContains(t, returnedSecrets, *secret2) + }) + + t.Run("Show: SecretWithOAuthClientCredentialsFlow with In", func(t *testing.T) { + 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) + require.Contains(t, returnedSecrets, *secret) + + 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.ExtendedIn{In: sdk.In{Schema: id.SchemaId()}})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + }) + + t.Run("Show: SecretWithOAuthAuthorizationCodeFlow with In", func(t *testing.T) { + 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) + require.Contains(t, returnedSecrets, *secret) + + 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.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) { + 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) + 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.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.ExtendedIn{In: 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) { + 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) + require.Contains(t, returnedSecrets, *secret) + + 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.ExtendedIn{In: sdk.In{Schema: id.SchemaId()}})) + require.NoError(t, err) + require.Contains(t, returnedSecrets, *secret) + }) + + t.Run("ShowByID - same name 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()) + + _, 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) + require.Equal(t, id1, secretShowResult1.ID()) + + secretShowResult2, err := client.Secrets.ShowByID(ctx, id2) + require.NoError(t, err) + require.Equal(t, id2, secretShowResult2.ID()) + }) + + t.Run("Describe", func(t *testing.T) { + 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(), + SecretType: "GENERIC_STRING", + }) + }) +}