diff --git a/pkg/sdk/applications_def.go b/pkg/sdk/applications_def.go new file mode 100644 index 0000000000..d809e22d55 --- /dev/null +++ b/pkg/sdk/applications_def.go @@ -0,0 +1,149 @@ +package sdk + +import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" + +//go:generate go run ./poc/main.go + +/* + * todo: add definition for `CREATE APPLICATION FROM LISTING [ COMMENT = '' ] [ WITH TAG ( = '' [ , ... ] ) ]` + */ + +var versionAndPatch = g.NewQueryStruct("VersionAndPatch"). + TextAssignment("VERSION", g.ParameterOptions().NoEquals().NoQuotes().Required()). + OptionalNumberAssignment("PATCH", g.ParameterOptions().NoEquals().Required()) + +var applicationVersion = g.NewQueryStruct("ApplicationVersion"). + OptionalText("VersionDirectory", g.KeywordOptions().SingleQuotes()). + OptionalQueryStructField("VersionAndPatch", versionAndPatch, g.KeywordOptions().NoQuotes()). + WithValidation(g.ExactlyOneValueSet, "VersionDirectory", "VersionAndPatch") + +var applicationSet = g.NewQueryStruct("ApplicationSet"). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("SHARE_EVENTS_WITH_PROVIDER", g.ParameterOptions()). + OptionalBooleanAssignment("DEBUG_MODE", g.ParameterOptions()) + +var applicationUnset = g.NewQueryStruct("ApplicationUnset"). + OptionalSQL("COMMENT"). + OptionalSQL("SHARE_EVENTS_WITH_PROVIDER"). + OptionalSQL("DEBUG_MODE") + +var applicationReferences = g.NewQueryStruct("ApplicationReferences").ListQueryStructField( + "References", + g.NewQueryStruct("ApplicationReference").Text("Reference", g.KeywordOptions().SingleQuotes()), + g.ParameterOptions().Parentheses().NoEquals(), +) + +var ApplicationsDef = g.NewInterface( + "Applications", + "Application", + g.KindOfT[AccountObjectIdentifier](), +).CreateOperation( + "https://docs.snowflake.com/en/sql-reference/sql/create-application", + g.NewQueryStruct("CreateApplication"). + Create(). + SQL("APPLICATION"). + Name(). + SQL("FROM APPLICATION PACKAGE"). + Identifier("PackageName", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Required()). + OptionalQueryStructField( + "Version", + applicationVersion, + g.KeywordOptions().SQL("USING"), + ). + OptionalBooleanAssignment("DEBUG_MODE", g.ParameterOptions()). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + OptionalTags(). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ValidIdentifier, "PackageName"), +).DropOperation( + "https://docs.snowflake.com/en/sql-reference/sql/drop-application", + g.NewQueryStruct("DropApplication"). + Drop(). + SQL("APPLICATION"). + IfExists(). + Name(). + OptionalSQL("CASCADE"). + WithValidation(g.ValidIdentifier, "name"), +).AlterOperation( + "https://docs.snowflake.com/en/sql-reference/sql/alter-application", + g.NewQueryStruct("AlterApplication"). + Alter(). + SQL("APPLICATION"). + IfExists(). + Name(). + OptionalQueryStructField( + "Set", + applicationSet, + g.KeywordOptions().SQL("SET"), + ). + OptionalQueryStructField( + "Unset", + applicationUnset, + g.ListOptions().NoParentheses().SQL("UNSET"), + ). + OptionalSQL("UPGRADE"). + OptionalQueryStructField( + "UpgradeVersion", + applicationVersion, + g.KeywordOptions().SQL("UPGRADE USING"), + ). + OptionalQueryStructField( + "UnsetReferences", + applicationReferences, + g.KeywordOptions().SQL("UNSET REFERENCES"), + ). + OptionalSetTags(). + OptionalUnsetTags(). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ExactlyOneValueSet, "Set", "Unset", "Upgrade", "UpgradeVersion", "UnsetReferences", "SetTags", "UnsetTags"), +).ShowOperation( + "https://docs.snowflake.com/en/sql-reference/sql/show-applications", + g.DbStruct("applicationRow"). + Field("created_on", "string"). + Field("name", "string"). + Field("is_default", "string"). + Field("is_current", "string"). + Field("source_type", "string"). + Field("source", "string"). + Field("owner", "string"). + Field("comment", "string"). + Field("version", "string"). + Field("label", "string"). + Field("patch", "int"). + Field("options", "string"). + Field("retention_time", "int"), + g.PlainStruct("Application"). + Field("CreatedOn", "string"). + Field("Name", "string"). + Field("IsDefault", "bool"). + Field("IsCurrent", "bool"). + Field("SourceType", "string"). + Field("Source", "string"). + Field("Owner", "string"). + Field("Comment", "string"). + Field("Version", "string"). + Field("Label", "string"). + Field("Patch", "int"). + Field("Options", "string"). + Field("RetentionTime", "int"), + g.NewQueryStruct("ShowApplications"). + Show(). + SQL("APPLICATIONS"). + OptionalLike(). + OptionalStartsWith(). + OptionalLimit(), +).ShowByIdOperation().DescribeOperation( + g.DescriptionMappingKindSlice, + "https://docs.snowflake.com/en/sql-reference/sql/desc-application", + g.DbStruct("applicationPropertyRow"). + Field("property", "string"). + Field("value", "sql.NullString"), + g.PlainStruct("ApplicationProperty"). + Field("Property", "string"). + Field("Value", "string"), + g.NewQueryStruct("DescribeApplication"). + Describe(). + SQL("APPLICATION"). + Name(). + WithValidation(g.ValidIdentifier, "name"), +) diff --git a/pkg/sdk/applications_dto_builders_gen.go b/pkg/sdk/applications_dto_builders_gen.go new file mode 100644 index 0000000000..8e20a566ab --- /dev/null +++ b/pkg/sdk/applications_dto_builders_gen.go @@ -0,0 +1,208 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewCreateApplicationRequest( + name AccountObjectIdentifier, + PackageName AccountObjectIdentifier, +) *CreateApplicationRequest { + s := CreateApplicationRequest{} + s.name = name + s.PackageName = PackageName + return &s +} + +func (s *CreateApplicationRequest) WithVersion(Version *ApplicationVersionRequest) *CreateApplicationRequest { + s.Version = Version + return s +} + +func (s *CreateApplicationRequest) WithDebugMode(DebugMode *bool) *CreateApplicationRequest { + s.DebugMode = DebugMode + return s +} + +func (s *CreateApplicationRequest) WithComment(Comment *string) *CreateApplicationRequest { + s.Comment = Comment + return s +} + +func (s *CreateApplicationRequest) WithTag(Tag []TagAssociation) *CreateApplicationRequest { + s.Tag = Tag + return s +} + +func NewApplicationVersionRequest() *ApplicationVersionRequest { + return &ApplicationVersionRequest{} +} + +func (s *ApplicationVersionRequest) WithVersionDirectory(VersionDirectory *string) *ApplicationVersionRequest { + s.VersionDirectory = VersionDirectory + return s +} + +func (s *ApplicationVersionRequest) WithVersionAndPatch(VersionAndPatch *VersionAndPatchRequest) *ApplicationVersionRequest { + s.VersionAndPatch = VersionAndPatch + return s +} + +func NewVersionAndPatchRequest( + Version string, + Patch *int, +) *VersionAndPatchRequest { + s := VersionAndPatchRequest{} + s.Version = Version + s.Patch = Patch + return &s +} + +func NewDropApplicationRequest( + name AccountObjectIdentifier, +) *DropApplicationRequest { + s := DropApplicationRequest{} + s.name = name + return &s +} + +func (s *DropApplicationRequest) WithIfExists(IfExists *bool) *DropApplicationRequest { + s.IfExists = IfExists + return s +} + +func (s *DropApplicationRequest) WithCascade(Cascade *bool) *DropApplicationRequest { + s.Cascade = Cascade + return s +} + +func NewAlterApplicationRequest( + name AccountObjectIdentifier, +) *AlterApplicationRequest { + s := AlterApplicationRequest{} + s.name = name + return &s +} + +func (s *AlterApplicationRequest) WithIfExists(IfExists *bool) *AlterApplicationRequest { + s.IfExists = IfExists + return s +} + +func (s *AlterApplicationRequest) WithSet(Set *ApplicationSetRequest) *AlterApplicationRequest { + s.Set = Set + return s +} + +func (s *AlterApplicationRequest) WithUnset(Unset *ApplicationUnsetRequest) *AlterApplicationRequest { + s.Unset = Unset + return s +} + +func (s *AlterApplicationRequest) WithUpgrade(Upgrade *bool) *AlterApplicationRequest { + s.Upgrade = Upgrade + return s +} + +func (s *AlterApplicationRequest) WithUpgradeVersion(UpgradeVersion *ApplicationVersionRequest) *AlterApplicationRequest { + s.UpgradeVersion = UpgradeVersion + return s +} + +func (s *AlterApplicationRequest) WithUnsetReferences(UnsetReferences *ApplicationReferencesRequest) *AlterApplicationRequest { + s.UnsetReferences = UnsetReferences + return s +} + +func (s *AlterApplicationRequest) WithSetTags(SetTags []TagAssociation) *AlterApplicationRequest { + s.SetTags = SetTags + return s +} + +func (s *AlterApplicationRequest) WithUnsetTags(UnsetTags []ObjectIdentifier) *AlterApplicationRequest { + s.UnsetTags = UnsetTags + return s +} + +func NewApplicationSetRequest() *ApplicationSetRequest { + return &ApplicationSetRequest{} +} + +func (s *ApplicationSetRequest) WithComment(Comment *string) *ApplicationSetRequest { + s.Comment = Comment + return s +} + +func (s *ApplicationSetRequest) WithShareEventsWithProvider(ShareEventsWithProvider *bool) *ApplicationSetRequest { + s.ShareEventsWithProvider = ShareEventsWithProvider + return s +} + +func (s *ApplicationSetRequest) WithDebugMode(DebugMode *bool) *ApplicationSetRequest { + s.DebugMode = DebugMode + return s +} + +func NewApplicationUnsetRequest() *ApplicationUnsetRequest { + return &ApplicationUnsetRequest{} +} + +func (s *ApplicationUnsetRequest) WithComment(Comment *bool) *ApplicationUnsetRequest { + s.Comment = Comment + return s +} + +func (s *ApplicationUnsetRequest) WithShareEventsWithProvider(ShareEventsWithProvider *bool) *ApplicationUnsetRequest { + s.ShareEventsWithProvider = ShareEventsWithProvider + return s +} + +func (s *ApplicationUnsetRequest) WithDebugMode(DebugMode *bool) *ApplicationUnsetRequest { + s.DebugMode = DebugMode + return s +} + +func NewApplicationReferencesRequest() *ApplicationReferencesRequest { + return &ApplicationReferencesRequest{} +} + +func (s *ApplicationReferencesRequest) WithReferences(References []ApplicationReferenceRequest) *ApplicationReferencesRequest { + s.References = References + return s +} + +func NewApplicationReferenceRequest() *ApplicationReferenceRequest { + return &ApplicationReferenceRequest{} +} + +func (s *ApplicationReferenceRequest) WithReference(Reference string) *ApplicationReferenceRequest { + s.Reference = Reference + return s +} + +func NewShowApplicationRequest() *ShowApplicationRequest { + return &ShowApplicationRequest{} +} + +func (s *ShowApplicationRequest) WithLike(Like *Like) *ShowApplicationRequest { + s.Like = Like + return s +} + +func (s *ShowApplicationRequest) WithStartsWith(StartsWith *string) *ShowApplicationRequest { + s.StartsWith = StartsWith + return s +} + +func (s *ShowApplicationRequest) WithLimit(Limit *LimitFrom) *ShowApplicationRequest { + s.Limit = Limit + return s +} + +func NewDescribeApplicationRequest( + name AccountObjectIdentifier, +) *DescribeApplicationRequest { + s := DescribeApplicationRequest{} + s.name = name + return &s +} diff --git a/pkg/sdk/applications_dto_gen.go b/pkg/sdk/applications_dto_gen.go new file mode 100644 index 0000000000..eb75e655db --- /dev/null +++ b/pkg/sdk/applications_dto_gen.go @@ -0,0 +1,78 @@ +package sdk + +//go:generate go run ./dto-builder-generator/main.go + +var ( + _ optionsProvider[CreateApplicationOptions] = new(CreateApplicationRequest) + _ optionsProvider[DropApplicationOptions] = new(DropApplicationRequest) + _ optionsProvider[AlterApplicationOptions] = new(AlterApplicationRequest) + _ optionsProvider[ShowApplicationOptions] = new(ShowApplicationRequest) + _ optionsProvider[DescribeApplicationOptions] = new(DescribeApplicationRequest) +) + +type CreateApplicationRequest struct { + name AccountObjectIdentifier // required + PackageName AccountObjectIdentifier // required + Version *ApplicationVersionRequest + DebugMode *bool + Comment *string + Tag []TagAssociation +} + +type ApplicationVersionRequest struct { + VersionDirectory *string + VersionAndPatch *VersionAndPatchRequest +} + +type VersionAndPatchRequest struct { + Version string // required + Patch *int // required +} + +type DropApplicationRequest struct { + IfExists *bool + name AccountObjectIdentifier // required + Cascade *bool +} + +type AlterApplicationRequest struct { + IfExists *bool + name AccountObjectIdentifier // required + Set *ApplicationSetRequest + Unset *ApplicationUnsetRequest + Upgrade *bool + UpgradeVersion *ApplicationVersionRequest + UnsetReferences *ApplicationReferencesRequest + SetTags []TagAssociation + UnsetTags []ObjectIdentifier +} + +type ApplicationSetRequest struct { + Comment *string + ShareEventsWithProvider *bool + DebugMode *bool +} + +type ApplicationUnsetRequest struct { + Comment *bool + ShareEventsWithProvider *bool + DebugMode *bool +} + +type ApplicationReferencesRequest struct { + References []ApplicationReferenceRequest +} + +type ApplicationReferenceRequest struct { + Reference string +} + +type ShowApplicationRequest struct { + Like *Like + StartsWith *string + Limit *LimitFrom +} + +type DescribeApplicationRequest struct { + name AccountObjectIdentifier // required +} diff --git a/pkg/sdk/applications_gen.go b/pkg/sdk/applications_gen.go new file mode 100644 index 0000000000..30485e3732 --- /dev/null +++ b/pkg/sdk/applications_gen.go @@ -0,0 +1,140 @@ +package sdk + +import ( + "context" + "database/sql" +) + +type Applications interface { + Create(ctx context.Context, request *CreateApplicationRequest) error + Drop(ctx context.Context, request *DropApplicationRequest) error + Alter(ctx context.Context, request *AlterApplicationRequest) error + Show(ctx context.Context, request *ShowApplicationRequest) ([]Application, error) + ShowByID(ctx context.Context, id AccountObjectIdentifier) (*Application, error) + Describe(ctx context.Context, id AccountObjectIdentifier) ([]ApplicationProperty, error) +} + +// CreateApplicationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-application. +type CreateApplicationOptions struct { + create bool `ddl:"static" sql:"CREATE"` + application bool `ddl:"static" sql:"APPLICATION"` + name AccountObjectIdentifier `ddl:"identifier"` + fromApplicationPackage bool `ddl:"static" sql:"FROM APPLICATION PACKAGE"` + PackageName AccountObjectIdentifier `ddl:"identifier"` + Version *ApplicationVersion `ddl:"keyword" sql:"USING"` + DebugMode *bool `ddl:"parameter" sql:"DEBUG_MODE"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` +} + +type ApplicationVersion struct { + VersionDirectory *string `ddl:"keyword,single_quotes"` + VersionAndPatch *VersionAndPatch `ddl:"keyword,no_quotes"` +} + +type VersionAndPatch struct { + Version string `ddl:"parameter,no_quotes,no_equals" sql:"VERSION"` + Patch *int `ddl:"parameter,no_equals" sql:"PATCH"` +} + +// DropApplicationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-application. +type DropApplicationOptions struct { + drop bool `ddl:"static" sql:"DROP"` + application bool `ddl:"static" sql:"APPLICATION"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` + Cascade *bool `ddl:"keyword" sql:"CASCADE"` +} + +// AlterApplicationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-application. +type AlterApplicationOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + application bool `ddl:"static" sql:"APPLICATION"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` + Set *ApplicationSet `ddl:"keyword" sql:"SET"` + Unset *ApplicationUnset `ddl:"list,no_parentheses" sql:"UNSET"` + Upgrade *bool `ddl:"keyword" sql:"UPGRADE"` + UpgradeVersion *ApplicationVersion `ddl:"keyword" sql:"UPGRADE USING"` + UnsetReferences *ApplicationReferences `ddl:"keyword" sql:"UNSET REFERENCES"` + SetTags []TagAssociation `ddl:"keyword" sql:"SET TAG"` + UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` +} + +type ApplicationSet struct { + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + ShareEventsWithProvider *bool `ddl:"parameter" sql:"SHARE_EVENTS_WITH_PROVIDER"` + DebugMode *bool `ddl:"parameter" sql:"DEBUG_MODE"` +} + +type ApplicationUnset struct { + Comment *bool `ddl:"keyword" sql:"COMMENT"` + ShareEventsWithProvider *bool `ddl:"keyword" sql:"SHARE_EVENTS_WITH_PROVIDER"` + DebugMode *bool `ddl:"keyword" sql:"DEBUG_MODE"` +} + +type ApplicationReferences struct { + References []ApplicationReference `ddl:"parameter,parentheses,no_equals"` +} + +type ApplicationReference struct { + Reference string `ddl:"keyword,single_quotes"` +} + +// ShowApplicationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-applications. +type ShowApplicationOptions struct { + show bool `ddl:"static" sql:"SHOW"` + applications bool `ddl:"static" sql:"APPLICATIONS"` + Like *Like `ddl:"keyword" sql:"LIKE"` + StartsWith *string `ddl:"parameter,single_quotes,no_equals" sql:"STARTS WITH"` + Limit *LimitFrom `ddl:"keyword" sql:"LIMIT"` +} + +type applicationRow struct { + CreatedOn string `db:"created_on"` + Name string `db:"name"` + IsDefault string `db:"is_default"` + IsCurrent string `db:"is_current"` + SourceType string `db:"source_type"` + Source string `db:"source"` + Owner string `db:"owner"` + Comment string `db:"comment"` + Version string `db:"version"` + Label string `db:"label"` + Patch int `db:"patch"` + Options string `db:"options"` + RetentionTime int `db:"retention_time"` +} + +type Application struct { + CreatedOn string + Name string + IsDefault bool + IsCurrent bool + SourceType string + Source string + Owner string + Comment string + Version string + Label string + Patch int + Options string + RetentionTime int +} + +// DescribeApplicationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-application. +type DescribeApplicationOptions struct { + describe bool `ddl:"static" sql:"DESCRIBE"` + application bool `ddl:"static" sql:"APPLICATION"` + name AccountObjectIdentifier `ddl:"identifier"` +} + +type applicationPropertyRow struct { + Property string `db:"property"` + Value sql.NullString `db:"value"` +} + +type ApplicationProperty struct { + Property string + Value string +} diff --git a/pkg/sdk/applications_gen_test.go b/pkg/sdk/applications_gen_test.go new file mode 100644 index 0000000000..0f64cfd209 --- /dev/null +++ b/pkg/sdk/applications_gen_test.go @@ -0,0 +1,312 @@ +package sdk + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" +) + +func TestApplications_Create(t *testing.T) { + id := RandomAccountObjectIdentifier() + pid := RandomAccountObjectIdentifier() + + defaultOpts := func() *CreateApplicationOptions { + return &CreateApplicationOptions{ + name: id, + PackageName: pid, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateApplicationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: exactly one field should be present", func(t *testing.T) { + opts := defaultOpts() + opts.Version = &ApplicationVersion{ + VersionAndPatch: &VersionAndPatch{ + Version: "1.0", + Patch: Int(1), + }, + VersionDirectory: String("@test"), + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateApplicationOptions.Version", "VersionDirectory", "VersionAndPatch")) + }) + + t.Run("validation: version must be set when debug mode is set", func(t *testing.T) { + opts := defaultOpts() + opts.DebugMode = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, NewError("CreateApplicationOptions.DebugMode can be set only when CreateApplicationOptions.Version is set")) + }) + + t.Run("all options", func(t *testing.T) { + tid := NewSchemaObjectIdentifier(random.StringN(4), random.StringN(4), random.StringN(4)) + + opts := defaultOpts() + opts.Comment = String("test") + opts.Tag = []TagAssociation{ + { + Name: tid, + Value: "v1", + }, + } + assertOptsValidAndSQLEquals(t, opts, `CREATE APPLICATION %s FROM APPLICATION PACKAGE %s COMMENT = 'test' TAG (%s = 'v1')`, id.FullyQualifiedName(), pid.FullyQualifiedName(), tid.FullyQualifiedName()) + + opts = defaultOpts() + opts.Comment = String("test") + opts.Version = &ApplicationVersion{ + VersionDirectory: String("@test"), + } + opts.DebugMode = Bool(true) + opts.Tag = []TagAssociation{ + { + Name: tid, + Value: "v1", + }, + } + assertOptsValidAndSQLEquals(t, opts, `CREATE APPLICATION %s FROM APPLICATION PACKAGE %s USING '@test' DEBUG_MODE = true COMMENT = 'test' TAG (%s = 'v1')`, id.FullyQualifiedName(), pid.FullyQualifiedName(), tid.FullyQualifiedName()) + + opts = defaultOpts() + opts.Comment = String("test") + opts.Version = &ApplicationVersion{ + VersionAndPatch: &VersionAndPatch{ + Version: "V001", + Patch: Int(1), + }, + } + opts.DebugMode = Bool(true) + opts.Tag = []TagAssociation{ + { + Name: tid, + Value: "v1", + }, + } + assertOptsValidAndSQLEquals(t, opts, `CREATE APPLICATION %s FROM APPLICATION PACKAGE %s USING VERSION V001 PATCH 1 DEBUG_MODE = true COMMENT = 'test' TAG (%s = 'v1')`, id.FullyQualifiedName(), pid.FullyQualifiedName(), tid.FullyQualifiedName()) + }) +} + +func TestApplications_Alter(t *testing.T) { + id := RandomAccountObjectIdentifier() + + defaultOpts := func() *AlterApplicationOptions { + return &AlterApplicationOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *AlterApplicationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: exactly one field should be present", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterApplicationOptions", "Set", "Unset", "Upgrade", "UpgradeVersion", "UnsetReferences", "SetTags", "UnsetTags")) + }) + + t.Run("validation: exactly one field should be present", func(t *testing.T) { + opts := defaultOpts() + opts.Upgrade = Bool(true) + opts.Unset = &ApplicationUnset{ + Comment: Bool(true), + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterApplicationOptions", "Set", "Unset", "Upgrade", "UpgradeVersion", "UnsetReferences", "SetTags", "UnsetTags")) + }) + + t.Run("validation: if exits can be set only when set or unset is set", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, NewError("AlterApplicationOptions.IfExists can be set only when AlterApplicationOptions.Set or AlterApplicationOptions.Unset is set")) + }) + + t.Run("alter: set options", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + opts.Set = &ApplicationSet{ + ShareEventsWithProvider: Bool(true), + DebugMode: Bool(true), + Comment: String("test"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION IF EXISTS %s SET COMMENT = 'test' SHARE_EVENTS_WITH_PROVIDER = true DEBUG_MODE = true`, id.FullyQualifiedName()) + }) + + t.Run("alter: unset options", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + opts.Unset = &ApplicationUnset{ + Comment: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION IF EXISTS %s UNSET COMMENT`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.IfExists = Bool(true) + opts.Unset = &ApplicationUnset{ + ShareEventsWithProvider: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION IF EXISTS %s UNSET SHARE_EVENTS_WITH_PROVIDER`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.IfExists = Bool(true) + opts.Unset = &ApplicationUnset{ + DebugMode: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION IF EXISTS %s UNSET DEBUG_MODE`, id.FullyQualifiedName()) + }) + + t.Run("alter: set tags", func(t *testing.T) { + opts := defaultOpts() + opts.SetTags = []TagAssociation{ + { + Name: NewAccountObjectIdentifier("tag1"), + Value: "value1", + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s SET TAG "tag1" = 'value1'`, id.FullyQualifiedName()) + }) + + t.Run("alter: unset tags", func(t *testing.T) { + opts := defaultOpts() + opts.UnsetTags = []ObjectIdentifier{ + NewAccountObjectIdentifier("tag1"), + NewAccountObjectIdentifier("tag2"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s UNSET TAG "tag1", "tag2"`, id.FullyQualifiedName()) + }) + + t.Run("alter: upgrade", func(t *testing.T) { + opts := defaultOpts() + opts.Upgrade = Bool(true) + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s UPGRADE`, id.FullyQualifiedName()) + }) + + t.Run("alter: upgrade version", func(t *testing.T) { + opts := defaultOpts() + opts.UpgradeVersion = &ApplicationVersion{ + VersionDirectory: String("@test"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s UPGRADE USING '@test'`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.UpgradeVersion = &ApplicationVersion{ + VersionAndPatch: &VersionAndPatch{ + Version: "V001", + Patch: Int(1), + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s UPGRADE USING VERSION V001 PATCH 1`, id.FullyQualifiedName()) + }) + + t.Run("alter: unset references", func(t *testing.T) { + opts := defaultOpts() + opts.UnsetReferences = &ApplicationReferences{} + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s UNSET REFERENCES`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.UnsetReferences = &ApplicationReferences{ + References: []ApplicationReference{ + { + Reference: "ref1", + }, + { + Reference: "ref2", + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER APPLICATION %s UNSET REFERENCES ('ref1', 'ref2')`, id.FullyQualifiedName()) + }) +} + +func TestApplications_Drop(t *testing.T) { + id := RandomAccountObjectIdentifier() + + defaultOpts := func() *DropApplicationOptions { + return &DropApplicationOptions{ + name: id, + } + } + t.Run("validation: nil options", func(t *testing.T) { + var opts *DropApplicationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + opts.Cascade = Bool(true) + assertOptsValidAndSQLEquals(t, opts, `DROP APPLICATION IF EXISTS %s CASCADE`, id.FullyQualifiedName()) + }) +} + +func TestApplications_Describe(t *testing.T) { + id := RandomAccountObjectIdentifier() + + defaultOpts := func() *DescribeApplicationOptions { + return &DescribeApplicationOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + opts := (*DescribeApplicationOptions)(nil) + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `DESCRIBE APPLICATION %s`, id.FullyQualifiedName()) + }) +} + +func TestApplications_Show(t *testing.T) { + defaultOpts := func() *ShowApplicationOptions { + return &ShowApplicationOptions{} + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *ShowApplicationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `SHOW APPLICATIONS`) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{ + Pattern: String("pattern"), + } + opts.StartsWith = String("A") + opts.Limit = &LimitFrom{ + Rows: Int(1), + From: String("B"), + } + assertOptsValidAndSQLEquals(t, opts, `SHOW APPLICATIONS LIKE 'pattern' STARTS WITH 'A' LIMIT 1 FROM 'B'`) + }) +} diff --git a/pkg/sdk/applications_impl_gen.go b/pkg/sdk/applications_impl_gen.go new file mode 100644 index 0000000000..474c9814e8 --- /dev/null +++ b/pkg/sdk/applications_impl_gen.go @@ -0,0 +1,182 @@ +package sdk + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" +) + +var _ Applications = (*applications)(nil) + +type applications struct { + client *Client +} + +func (v *applications) Create(ctx context.Context, request *CreateApplicationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *applications) Drop(ctx context.Context, request *DropApplicationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *applications) Alter(ctx context.Context, request *AlterApplicationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *applications) Show(ctx context.Context, request *ShowApplicationRequest) ([]Application, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[applicationRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + resultList := convertRows[applicationRow, Application](dbRows) + return resultList, nil +} + +func (v *applications) ShowByID(ctx context.Context, id AccountObjectIdentifier) (*Application, error) { + request := NewShowApplicationRequest().WithLike(&Like{String(id.Name())}) + applications, err := v.Show(ctx, request) + if err != nil { + return nil, err + } + return collections.FindOne(applications, func(r Application) bool { return r.Name == id.Name() }) +} + +func (v *applications) Describe(ctx context.Context, id AccountObjectIdentifier) ([]ApplicationProperty, error) { + opts := &DescribeApplicationOptions{ + name: id, + } + rows, err := validateAndQuery[applicationPropertyRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + return convertRows[applicationPropertyRow, ApplicationProperty](rows), nil +} + +func (r *CreateApplicationRequest) toOpts() *CreateApplicationOptions { + opts := &CreateApplicationOptions{ + name: r.name, + PackageName: r.PackageName, + + DebugMode: r.DebugMode, + Comment: r.Comment, + Tag: r.Tag, + } + if r.Version != nil { + opts.Version = &ApplicationVersion{ + VersionDirectory: r.Version.VersionDirectory, + } + if r.Version.VersionAndPatch != nil { + opts.Version.VersionAndPatch = &VersionAndPatch{ + Version: r.Version.VersionAndPatch.Version, + Patch: r.Version.VersionAndPatch.Patch, + } + } + } + return opts +} + +func (r *DropApplicationRequest) toOpts() *DropApplicationOptions { + opts := &DropApplicationOptions{ + IfExists: r.IfExists, + name: r.name, + Cascade: r.Cascade, + } + return opts +} + +func (r *AlterApplicationRequest) toOpts() *AlterApplicationOptions { + opts := &AlterApplicationOptions{ + IfExists: r.IfExists, + name: r.name, + + Upgrade: r.Upgrade, + + SetTags: r.SetTags, + UnsetTags: r.UnsetTags, + } + if r.Set != nil { + opts.Set = &ApplicationSet{ + Comment: r.Set.Comment, + ShareEventsWithProvider: r.Set.ShareEventsWithProvider, + DebugMode: r.Set.DebugMode, + } + } + if r.Unset != nil { + opts.Unset = &ApplicationUnset{ + Comment: r.Unset.Comment, + ShareEventsWithProvider: r.Unset.ShareEventsWithProvider, + DebugMode: r.Unset.DebugMode, + } + } + if r.UpgradeVersion != nil { + opts.UpgradeVersion = &ApplicationVersion{ + VersionDirectory: r.UpgradeVersion.VersionDirectory, + } + if r.UpgradeVersion.VersionAndPatch != nil { + opts.UpgradeVersion.VersionAndPatch = &VersionAndPatch{ + Version: r.UpgradeVersion.VersionAndPatch.Version, + Patch: r.UpgradeVersion.VersionAndPatch.Patch, + } + } + } + if r.UnsetReferences != nil { + opts.UnsetReferences = &ApplicationReferences{} + if r.UnsetReferences.References != nil { + s := make([]ApplicationReference, len(r.UnsetReferences.References)) + for i, v := range r.UnsetReferences.References { + s[i] = ApplicationReference(v) + } + opts.UnsetReferences.References = s + } + } + return opts +} + +func (r *ShowApplicationRequest) toOpts() *ShowApplicationOptions { + opts := &ShowApplicationOptions{ + Like: r.Like, + StartsWith: r.StartsWith, + Limit: r.Limit, + } + return opts +} + +func (r applicationRow) convert() *Application { + return &Application{ + CreatedOn: r.CreatedOn, + Name: r.Name, + IsDefault: r.IsDefault == "Y", + IsCurrent: r.IsCurrent == "Y", + SourceType: r.SourceType, + Source: r.Source, + Owner: r.Owner, + Comment: r.Comment, + Version: r.Version, + Label: r.Label, + Patch: r.Patch, + Options: r.Options, + RetentionTime: r.RetentionTime, + } +} + +func (r *DescribeApplicationRequest) toOpts() *DescribeApplicationOptions { + opts := &DescribeApplicationOptions{ + name: r.name, + } + return opts +} + +func (r applicationPropertyRow) convert() *ApplicationProperty { + e := &ApplicationProperty{ + Property: r.Property, + } + if r.Value.Valid { + e.Value = r.Value.String + } + return e +} diff --git a/pkg/sdk/applications_validations_gen.go b/pkg/sdk/applications_validations_gen.go new file mode 100644 index 0000000000..c33088bb75 --- /dev/null +++ b/pkg/sdk/applications_validations_gen.go @@ -0,0 +1,85 @@ +package sdk + +var ( + _ validatable = new(CreateApplicationOptions) + _ validatable = new(DropApplicationOptions) + _ validatable = new(AlterApplicationOptions) + _ validatable = new(ShowApplicationOptions) + _ validatable = new(DescribeApplicationOptions) +) + +func (opts *CreateApplicationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !ValidObjectIdentifier(opts.PackageName) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if valueSet(opts.Version) { + if !exactlyOneValueSet(opts.Version.VersionDirectory, opts.Version.VersionAndPatch) { + errs = append(errs, errExactlyOneOf("CreateApplicationOptions.Version", "VersionDirectory", "VersionAndPatch")) + } + } + if valueSet(opts.DebugMode) && !valueSet(opts.Version) { + errs = append(errs, NewError("CreateApplicationOptions.DebugMode can be set only when CreateApplicationOptions.Version is set")) + } + return JoinErrors(errs...) +} + +func (opts *DropApplicationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *AlterApplicationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !exactlyOneValueSet(opts.Set, opts.Unset, opts.Upgrade, opts.UpgradeVersion, opts.UnsetReferences, opts.SetTags, opts.UnsetTags) { + errs = append(errs, errExactlyOneOf("AlterApplicationOptions", "Set", "Unset", "Upgrade", "UpgradeVersion", "UnsetReferences", "SetTags", "UnsetTags")) + } + if valueSet(opts.UpgradeVersion) { + if !exactlyOneValueSet(opts.UpgradeVersion.VersionDirectory, opts.UpgradeVersion.VersionAndPatch) { + errs = append(errs, errExactlyOneOf("AlterApplicationOptions.UpgradeVersion", "VersionDirectory", "VersionAndPatch")) + } + } + if valueSet(opts.IfExists) { + if !valueSet(opts.Set) && !valueSet(opts.Unset) { + errs = append(errs, NewError("AlterApplicationOptions.IfExists can be set only when AlterApplicationOptions.Set or AlterApplicationOptions.Unset is set")) + } + } + return JoinErrors(errs...) +} + +func (opts *ShowApplicationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + return JoinErrors(errs...) +} + +func (opts *DescribeApplicationOptions) 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/client.go b/pkg/sdk/client.go index aede13ba58..d9a510b465 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -42,6 +42,7 @@ type Client struct { Alerts Alerts ApplicationPackages ApplicationPackages ApplicationRoles ApplicationRoles + Applications Applications Comments Comments DatabaseRoles DatabaseRoles Databases Databases @@ -186,6 +187,7 @@ func (c *Client) initialize() { c.Alerts = &alerts{client: c} c.ApplicationPackages = &applicationPackages{client: c} c.ApplicationRoles = &applicationRoles{client: c} + c.Applications = &applications{client: c} c.Comments = &comments{client: c} c.ContextFunctions = &contextFunctions{client: c} c.ConversionFunctions = &conversionFunctions{client: c} diff --git a/pkg/sdk/poc/main.go b/pkg/sdk/poc/main.go index 492bbfe201..959e963e8f 100644 --- a/pkg/sdk/poc/main.go +++ b/pkg/sdk/poc/main.go @@ -31,6 +31,7 @@ var definitionMapping = map[string]*generator.Interface{ "storage_integration_def.go": sdk.StorageIntegrationDef, "managed_accounts_def.go": sdk.ManagedAccountsDef, "row_access_policies_def.go": sdk.RowAccessPoliciesDef, + "applications_def.go": sdk.ApplicationsDef, "sequences_def.go": sdk.SequencesDef, } diff --git a/pkg/sdk/testint/applications_integration_test.go b/pkg/sdk/testint/applications_integration_test.go new file mode 100644 index 0000000000..a0699a7be6 --- /dev/null +++ b/pkg/sdk/testint/applications_integration_test.go @@ -0,0 +1,313 @@ +package testint + +import ( + "errors" + "fmt" + "strconv" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +/* + * todo: (SNOW-1015095) add integration test for `ALTER APPLICATION UPGRADE` + * todo: (SNOW-1016268) ALTER APPLICATION [ IF EXISTS ] SET [ SHARE_EVENTS_WITH_PROVIDER ] + * attention: SHARE_EVENTS_WITH_PROVIDER can only be set/unset if the application is created in a different account from the application package + */ + +func TestInt_Applications(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + databaseTest, schemaTest := testDb(t), testSchema(t) + tagTest, tagCleanup := createTag(t, client, databaseTest, schemaTest) + t.Cleanup(tagCleanup) + + cleanupApplicationHandle := func(id sdk.AccountObjectIdentifier) func() { + return func() { + err := client.Applications.Drop(ctx, sdk.NewDropApplicationRequest(id)) + if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { + return + } + require.NoError(t, err) + } + } + + createApplicationPackageHandle := func(t *testing.T, applicationPackageName, version string, patch int, defaultReleaseDirective bool) *sdk.Stage { + t.Helper() + + stage, cleanupStage := createStage(t, client, sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, random.AlphaN(12))) + t.Cleanup(cleanupStage) + putOnStageWithContent(t, client, stage.ID(), "manifest.yml", "") + putOnStageWithContent(t, client, stage.ID(), "setup.sql", "CREATE APPLICATION ROLE IF NOT EXISTS APP_HELLO_SNOWFLAKE;") + cleanupApplicationPackage := createApplicationPackage(t, client, applicationPackageName) + t.Cleanup(cleanupApplicationPackage) + addApplicationPackageVersion(t, client, stage, applicationPackageName, version) + + // set default release directive for application package + if defaultReleaseDirective { + _, err := client.ExecForTests(ctx, fmt.Sprintf(`ALTER APPLICATION PACKAGE "%s" SET DEFAULT RELEASE DIRECTIVE VERSION = %s PATCH = %d`, applicationPackageName, version, patch)) + require.NoError(t, err) + } + return stage + } + + createApplicationHandle := func(t *testing.T, applicationPackageName, version string, patch int, versionDirectory bool, debug bool, addPatch bool) (*sdk.Stage, *sdk.Application) { + t.Helper() + + stage := createApplicationPackageHandle(t, applicationPackageName, version, patch, false) + + id := sdk.NewAccountObjectIdentifier(random.AlphaN(4)) + vr := sdk.NewApplicationVersionRequest().WithVersionAndPatch(sdk.NewVersionAndPatchRequest(version, &patch)) + if versionDirectory { + vr = sdk.NewApplicationVersionRequest().WithVersionDirectory(sdk.String("@" + stage.ID().FullyQualifiedName())) + } + request := sdk.NewCreateApplicationRequest(id, sdk.NewAccountObjectIdentifier(applicationPackageName)).WithVersion(vr).WithDebugMode(&debug) + err := client.Applications.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupApplicationHandle(id)) + + if addPatch { + _, err := client.ExecForTests(ctx, fmt.Sprintf(`ALTER APPLICATION PACKAGE "%s" ADD PATCH FOR VERSION %s USING '@%s'`, applicationPackageName, version, stage.ID().FullyQualifiedName())) + require.NoError(t, err) + } + + application, err := client.Applications.ShowByID(ctx, id) + require.NoError(t, err) + return stage, application + } + + assertApplication := func(t *testing.T, id sdk.AccountObjectIdentifier, applicationPackageName, version string, patch int, comment string) { + t.Helper() + + e, err := client.Applications.ShowByID(ctx, id) + require.NoError(t, err) + + assert.NotEmpty(t, e.CreatedOn) + assert.Equal(t, id.Name(), e.Name) + assert.Equal(t, false, e.IsDefault) + assert.Equal(t, true, e.IsCurrent) + assert.Equal(t, "APPLICATION PACKAGE", e.SourceType) + assert.Equal(t, applicationPackageName, e.Source) + assert.Equal(t, version, e.Version) + assert.Equal(t, patch, e.Patch) + assert.Equal(t, "ACCOUNTADMIN", e.Owner) + assert.Equal(t, comment, e.Comment) + assert.Equal(t, 1, e.RetentionTime) + assert.Empty(t, e.Options) + } + + t.Run("create application: without version", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + createApplicationPackageHandle(t, applicationPackageName, version, patch, true) + + id := sdk.NewAccountObjectIdentifier(random.AlphaN(4)) + pid := sdk.NewAccountObjectIdentifier(applicationPackageName) + comment := random.StringN(4) + request := sdk.NewCreateApplicationRequest(id, pid). + WithComment(&comment). + WithTag([]sdk.TagAssociation{ + { + Name: tagTest.ID(), + Value: "v1", + }, + }) + err := client.Applications.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupApplicationHandle(id)) + + e, err := client.Applications.ShowByID(ctx, id) + require.NoError(t, err) + require.Equal(t, id.Name(), e.Name) + require.Equal(t, "ACCOUNTADMIN", e.Owner) + require.Equal(t, comment, e.Comment) + require.Equal(t, "APPLICATION PACKAGE", e.SourceType) + require.Equal(t, applicationPackageName, e.Source) + require.Equal(t, version, e.Version) + require.Equal(t, patch, e.Patch) + }) + + t.Run("create application: version and patch", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + createApplicationPackageHandle(t, applicationPackageName, version, patch, false) + + id := sdk.RandomAccountObjectIdentifier() + pid := sdk.NewAccountObjectIdentifier(applicationPackageName) + vr := sdk.NewApplicationVersionRequest().WithVersionAndPatch(sdk.NewVersionAndPatchRequest(version, &patch)) + comment := random.StringN(4) + request := sdk.NewCreateApplicationRequest(id, pid). + WithDebugMode(sdk.Bool(true)). + WithComment(&comment). + WithVersion(vr) + err := client.Applications.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupApplicationHandle(id)) + + assertApplication(t, id, applicationPackageName, version, patch, comment) + }) + + t.Run("create application: version directory", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + stage := createApplicationPackageHandle(t, applicationPackageName, version, patch, false) + + id := sdk.RandomAccountObjectIdentifier() + pid := sdk.NewAccountObjectIdentifier(applicationPackageName) + vr := sdk.NewApplicationVersionRequest().WithVersionDirectory(sdk.String("@" + stage.ID().FullyQualifiedName())) + comment := random.StringN(4) + request := sdk.NewCreateApplicationRequest(id, pid). + WithDebugMode(sdk.Bool(true)). + WithComment(&comment). + WithVersion(vr). + WithTag([]sdk.TagAssociation{ + { + Name: tagTest.ID(), + Value: "v1", + }, + }) + err := client.Applications.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupApplicationHandle(id)) + + assertApplication(t, id, applicationPackageName, "UNVERSIONED", patch, comment) + }) + + t.Run("show application: with like", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, false) + packages, err := client.Applications.Show(ctx, sdk.NewShowApplicationRequest().WithLike(&sdk.Like{Pattern: &e.Name})) + require.NoError(t, err) + require.Equal(t, 1, len(packages)) + require.Equal(t, *e, packages[0]) + }) + + t.Run("alter application: set", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, false) + id := sdk.NewAccountObjectIdentifier(e.Name) + + comment, mode := random.StringN(4), true + set := sdk.NewApplicationSetRequest(). + WithComment(&comment). + WithDebugMode(&mode) + err := client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithSet(set)) + require.NoError(t, err) + + details, err := client.Applications.Describe(ctx, id) + require.NoError(t, err) + pairs := make(map[string]string) + for _, detail := range details { + pairs[detail.Property] = detail.Value + } + require.Equal(t, e.SourceType, pairs["source_type"]) + require.Equal(t, e.Source, pairs["source"]) + require.Equal(t, e.Version, pairs["version"]) + require.Equal(t, strconv.Itoa(e.Patch), pairs["patch"]) + require.Equal(t, comment, pairs["comment"]) + require.Equal(t, strconv.FormatBool(mode), pairs["debug_mode"]) + }) + + t.Run("alter application: unset", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, false) + id := sdk.NewAccountObjectIdentifier(e.Name) + + unset := sdk.NewApplicationUnsetRequest().WithComment(sdk.Bool(true)).WithDebugMode(sdk.Bool(true)) + err := client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithUnset(unset)) + require.NoError(t, err) + + o, err := client.Applications.ShowByID(ctx, id) + require.NoError(t, err) + require.Empty(t, o.Comment) + + details, err := client.Applications.Describe(ctx, id) + require.NoError(t, err) + pairs := make(map[string]string) + for _, detail := range details { + pairs[detail.Property] = detail.Value + } + require.Equal(t, strconv.FormatBool(false), pairs["debug_mode"]) + }) + + t.Run("alter application: set and unset tags", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, false) + id := sdk.NewAccountObjectIdentifier(e.Name) + + setTags := []sdk.TagAssociation{ + { + Name: tagTest.ID(), + Value: "v1", + }, + } + err := client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithSetTags(setTags)) + require.NoError(t, err) + assertApplication(t, id, applicationPackageName, version, patch, "") + + // TODO: 391801 (0A000): SQL compilation error: Object tagging not supported for object type APPLICATION. + // tv, err := client.SystemFunctions.GetTag(ctx, tagTest.ID(), id, sdk.ObjectTypeApplication) + // require.NoError(t, err) + // assert.Equal(t, "v1", tv) + + unsetTags := []sdk.ObjectIdentifier{ + tagTest.ID(), + } + err = client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithUnsetTags(unsetTags)) + require.NoError(t, err) + assertApplication(t, id, applicationPackageName, version, patch, "") + }) + + t.Run("alter application: upgrade with version and patch", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, true) + id := sdk.NewAccountObjectIdentifier(e.Name) + + vr := sdk.NewVersionAndPatchRequest(version, sdk.Int(patch+1)) + av := sdk.NewApplicationVersionRequest().WithVersionAndPatch(vr) + err := client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithUpgradeVersion(av)) + require.NoError(t, err) + assertApplication(t, id, applicationPackageName, version, patch+1, "") + }) + + t.Run("alter application: upgrade with version directory", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + s, e := createApplicationHandle(t, applicationPackageName, version, patch, true, true, false) + id := sdk.NewAccountObjectIdentifier(e.Name) + + av := sdk.NewApplicationVersionRequest().WithVersionDirectory(sdk.String("@" + s.ID().FullyQualifiedName())) + err := client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithUpgradeVersion(av)) + require.NoError(t, err) + assertApplication(t, id, applicationPackageName, "UNVERSIONED", patch+1, "") + }) + + t.Run("alter application: unset references", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, false) + id := sdk.NewAccountObjectIdentifier(e.Name) + + err := client.Applications.Alter(ctx, sdk.NewAlterApplicationRequest(id).WithUnsetReferences(sdk.NewApplicationReferencesRequest())) + require.NoError(t, err) + assertApplication(t, id, applicationPackageName, version, patch, "") + }) + + t.Run("describe application", func(t *testing.T) { + applicationPackageName, version, patch := random.AlphaN(12), "V001", 0 + _, e := createApplicationHandle(t, applicationPackageName, version, patch, false, true, false) + id := sdk.NewAccountObjectIdentifier(e.Name) + + properties, err := client.Applications.Describe(ctx, id) + require.NoError(t, err) + pairs := make(map[string]string) + for _, item := range properties { + pairs[item.Property] = item.Value + } + require.Equal(t, e.SourceType, pairs["source_type"]) + require.Equal(t, e.Source, pairs["source"]) + require.Equal(t, e.Version, pairs["version"]) + require.Equal(t, e.Label, pairs["version_label"]) + require.Equal(t, e.Comment, pairs["comment"]) + require.Equal(t, strconv.Itoa(e.Patch), pairs["patch"]) + }) +} diff --git a/pkg/sdk/testint/helpers_test.go b/pkg/sdk/testint/helpers_test.go index 130a626ff6..32279584ce 100644 --- a/pkg/sdk/testint/helpers_test.go +++ b/pkg/sdk/testint/helpers_test.go @@ -693,6 +693,28 @@ func putOnStage(t *testing.T, client *sdk.Client, stage *sdk.Stage, filename str require.NoError(t, err) } +func putOnStageWithContent(t *testing.T, client *sdk.Client, id sdk.SchemaObjectIdentifier, filename string, content string) { + t.Helper() + ctx := context.Background() + + tf := fmt.Sprintf("/tmp/%s", filename) + f, err := os.Create(tf) + require.NoError(t, err) + if content != "" { + _, err = f.Write([]byte(content)) + require.NoError(t, err) + } + f.Close() + defer os.Remove(f.Name()) + + _, err = client.ExecForTests(ctx, fmt.Sprintf(`PUT file://%s @%s AUTO_COMPRESS = FALSE OVERWRITE = TRUE`, f.Name(), id.FullyQualifiedName())) + require.NoError(t, err) + t.Cleanup(func() { + _, err = client.ExecForTests(ctx, fmt.Sprintf(`REMOVE @%s/%s`, id.FullyQualifiedName(), filename)) + require.NoError(t, err) + }) +} + func createApplicationPackage(t *testing.T, client *sdk.Client, name string) func() { t.Helper() ctx := context.Background()