diff --git a/docs/resources/job_definition.md b/docs/resources/job_definition.md index e06c0793..0a7f5e10 100644 --- a/docs/resources/job_definition.md +++ b/docs/resources/job_definition.md @@ -1020,6 +1020,7 @@ Optional: - `gcs_input_option` (Attributes) Attributes about source GCS (see [below for nested schema](#nestedatt--input_option--gcs_input_option)) - `mysql_input_option` (Attributes) Attributes of source mysql (see [below for nested schema](#nestedatt--input_option--mysql_input_option)) +- `snowflake_input_option` (Attributes) Attributes about source snowflake (see [below for nested schema](#nestedatt--input_option--snowflake_input_option)) ### Nested Schema for `input_option.gcs_input_option` @@ -1321,6 +1322,53 @@ Optional: + +### Nested Schema for `input_option.snowflake_input_option` + +Required: + +- `database` (String) Database name +- `input_option_columns` (Attributes List) List of columns to be retrieved and their types (see [below for nested schema](#nestedatt--input_option--snowflake_input_option--input_option_columns)) +- `query` (String) Query +- `snowflake_connection_id` (Number) Id of Snowflake connection +- `warehouse` (String) Warehouse name + +Optional: + +- `connect_timeout` (Number) Connection timeout (sec) +- `custom_variable_settings` (Attributes List) (see [below for nested schema](#nestedatt--input_option--snowflake_input_option--custom_variable_settings)) +- `fetch_rows` (Number) Number of records processed by the cursor at one time +- `schema` (String) Schema name +- `socket_timeout` (Number) Socket timeout (seconds) + + +### Nested Schema for `input_option.snowflake_input_option.input_option_columns` + +Required: + +- `name` (String) Column name +- `type` (String) Column type + + + +### Nested Schema for `input_option.snowflake_input_option.custom_variable_settings` + +Required: + +- `name` (String) Custom variable name. It must start and end with `$` +- `type` (String) Custom variable type. The following types are supported: `string`, `timestamp`, `timestamp_runtime` + +Optional: + +- `direction` (String) Direction of the diff from context_time. The following directions are supported: `ago`, `later`. Required in `timestamp` and `timestamp_runtime` types +- `format` (String) Format used to replace variables. Required in `timestamp` and `timestamp_runtime` types +- `quantity` (Number) Quantity used to calculate diff from context_time. Required in `timestamp` and `timestamp_runtime` types +- `time_zone` (String) Time zone used to format the timestamp. Required in `timestamp` and `timestamp_runtime` types +- `unit` (String) Time unit used to calculate diff from context_time. The following units are supported: `hour`, `date`, `month`. Required in `timestamp` and `timestamp_runtime` types +- `value` (String) Fixed string which will replace variables at runtime. Required in `string` type + + + ### Nested Schema for `output_option` @@ -1328,6 +1376,7 @@ Optional: Optional: - `bigquery_output_option` (Attributes) Attributes of destination BigQuery settings (see [below for nested schema](#nestedatt--output_option--bigquery_output_option)) +- `snowflake_output_option` (Attributes) Attributes of destination Snowflake settings (see [below for nested schema](#nestedatt--output_option--snowflake_output_option)) ### Nested Schema for `output_option.bigquery_output_option` @@ -1394,6 +1443,65 @@ Optional: + +### Nested Schema for `output_option.snowflake_output_option` + +Required: + +- `database` (String) Database name +- `schema` (String) Schema name +- `snowflake_connection_id` (Number) Snowflake connection ID +- `table` (String) Table name +- `warehouse` (String) Warehouse name + +Optional: + +- `batch_size` (Number) Batch size (MB) +- `custom_variable_settings` (Attributes List) (see [below for nested schema](#nestedatt--output_option--snowflake_output_option--custom_variable_settings)) +- `default_time_zone` (String) Default time zone +- `delete_stage_on_error` (Boolean) Delete temporary stage on error +- `empty_field_as_null` (Boolean) Replace empty string with NULL +- `max_retry_wait` (Number) Maximum retry wait time (milliseconds) +- `mode` (String) Transfer mode +- `retry_limit` (Number) Maximum retry limit +- `retry_wait` (Number) Retry wait time (milliseconds) +- `snowflake_output_option_column_options` (Attributes List) (see [below for nested schema](#nestedatt--output_option--snowflake_output_option--snowflake_output_option_column_options)) +- `snowflake_output_option_merge_keys` (List of String) Merge keys (only applicable if mode is 'merge') + + +### Nested Schema for `output_option.snowflake_output_option.custom_variable_settings` + +Required: + +- `name` (String) Custom variable name. It must start and end with `$` +- `type` (String) Custom variable type. The following types are supported: `string`, `timestamp`, `timestamp_runtime` + +Optional: + +- `direction` (String) Direction of the diff from context_time. The following directions are supported: `ago`, `later`. Required in `timestamp` and `timestamp_runtime` types +- `format` (String) Format used to replace variables. Required in `timestamp` and `timestamp_runtime` types +- `quantity` (Number) Quantity used to calculate diff from context_time. Required in `timestamp` and `timestamp_runtime` types +- `time_zone` (String) Time zone used to format the timestamp. Required in `timestamp` and `timestamp_runtime` types +- `unit` (String) Time unit used to calculate diff from context_time. The following units are supported: `hour`, `date`, `month`. Required in `timestamp` and `timestamp_runtime` types +- `value` (String) Fixed string which will replace variables at runtime. Required in `string` type + + + +### Nested Schema for `output_option.snowflake_output_option.snowflake_output_option_column_options` + +Required: + +- `name` (String) Column name +- `type` (String) Data type + +Optional: + +- `timestamp_format` (String) Timestamp format +- `timezone` (String) Time zone +- `value_type` (String) Value type + + + ### Nested Schema for `filter_add_time` diff --git a/internal/client/entity/job_definition/input_option/snowflake.go b/internal/client/entity/job_definition/input_option/snowflake.go new file mode 100644 index 00000000..15dfc499 --- /dev/null +++ b/internal/client/entity/job_definition/input_option/snowflake.go @@ -0,0 +1,23 @@ +package input_option + +import ( + "terraform-provider-trocco/internal/client/entity" +) + +type SnowflakeInputOption struct { + Warehouse string `json:"warehouse"` + Database string `json:"database"` + Schema string `json:"schema"` + Query string `json:"query"` + FetchRows *int64 `json:"fetch_rows"` + ConnectTimeout *int64 `json:"connect_timeout"` + SocketTimeout *int64 `json:"socket_timeout"` + SnowflakeConnectionID int64 `json:"snowflake_connection_id"` + InputOptionColumns []SnowflakeInputOptionColumn `json:"input_option_columns"` + CustomVariableSettings *[]entity.CustomVariableSetting `json:"custom_variable_settings"` +} + +type SnowflakeInputOptionColumn struct { + Name string `json:"name"` + Type string `json:"type"` +} diff --git a/internal/client/entity/job_definition/output_option/snowflake.go b/internal/client/entity/job_definition/output_option/snowflake.go new file mode 100644 index 00000000..8fb95a63 --- /dev/null +++ b/internal/client/entity/job_definition/output_option/snowflake.go @@ -0,0 +1,32 @@ +package output_option + +import ( + "terraform-provider-trocco/internal/client/entity" +) + +type SnowflakeOutputOption struct { + Warehouse string `json:"warehouse"` + Database string `json:"database"` + Schema string `json:"schema"` + Table string `json:"table"` + Mode *string `json:"mode"` + EmptyFieldAsNull *bool `json:"empty_field_as_null"` + DeleteStageOnError *bool `json:"delete_stage_on_error"` + BatchSize *int64 `json:"batch_size"` + RetryLimit *int64 `json:"retry_limit"` + RetryWait *int64 `json:"retry_wait"` + MaxRetryWait *int64 `json:"max_retry_wait"` + DefaultTimeZone *string `json:"default_time_zone"` + SnowflakeConnectionId int64 `json:"snowflake_connection_id"` + SnowflakeOutputOptionColumnOptions []SnowflakeOutputOptionColumnOption `json:"snowflake_output_option_column_options"` + SnowflakeOutputOptionMergeKeys []string `json:"snowflake_output_option_merge_keys"` + CustomVariableSettings *[]entity.CustomVariableSetting `json:"custom_variable_settings"` +} + +type SnowflakeOutputOptionColumnOption struct { + Name string `json:"name"` + Type string `json:"type"` + ValueType *string `json:"value_type"` + TimestampFormat *string `json:"timestamp_format"` + Timezone *string `json:"timezone"` +} diff --git a/internal/client/job_definition.go b/internal/client/job_definition.go index da7b750e..d0895c9c 100644 --- a/internal/client/job_definition.go +++ b/internal/client/job_definition.go @@ -87,30 +87,36 @@ type UpdateJobDefinitionInput struct { } type InputOption struct { - MySQLInputOption *inputOptionEntitites.MySQLInputOption `json:"mysql_input_option"` - GcsInputOption *inputOptionEntitites.GcsInputOption `json:"gcs_input_option"` + MySQLInputOption *inputOptionEntitites.MySQLInputOption `json:"mysql_input_option"` + GcsInputOption *inputOptionEntitites.GcsInputOption `json:"gcs_input_option"` + SnowflakeInputOption *inputOptionEntitites.SnowflakeInputOption `json:"snowflake_input_option"` } type InputOptionInput struct { - MySQLInputOption *parameter.NullableObject[input_options.MySQLInputOptionInput] `json:"mysql_input_option,omitempty"` - GcsInputOption *parameter.NullableObject[input_options.GcsInputOptionInput] `json:"gcs_input_option,omitempty"` + MySQLInputOption *parameter.NullableObject[input_options.MySQLInputOptionInput] `json:"mysql_input_option,omitempty"` + GcsInputOption *parameter.NullableObject[input_options.GcsInputOptionInput] `json:"gcs_input_option,omitempty"` + SnowflakeInputOption *parameter.NullableObject[input_options.SnowflakeInputOptionInput] `json:"snowflake_input_option,omitempty"` } type UpdateInputOptionInput struct { - MySQLInputOption *parameter.NullableObject[input_options.UpdateMySQLInputOptionInput] `json:"mysql_input_option,omitempty"` - GcsInputOption *parameter.NullableObject[input_options.UpdateGcsInputOptionInput] `json:"gcs_input_option,omitempty"` + MySQLInputOption *parameter.NullableObject[input_options.UpdateMySQLInputOptionInput] `json:"mysql_input_option,omitempty"` + GcsInputOption *parameter.NullableObject[input_options.UpdateGcsInputOptionInput] `json:"gcs_input_option,omitempty"` + SnowflakeInputOption *parameter.NullableObject[input_options.UpdateSnowflakeInputOptionInput] `json:"snowflake_input_option,omitempty"` } type OutputOption struct { - BigQueryOutputOption *outputOptionEntitites.BigQueryOutputOption `json:"bigquery_output_option"` + BigQueryOutputOption *outputOptionEntitites.BigQueryOutputOption `json:"bigquery_output_option"` + SnowflakeOutputOption *outputOptionEntitites.SnowflakeOutputOption `json:"snowflake_output_option"` } type OutputOptionInput struct { - BigQueryOutputOption *parameter.NullableObject[output_options.BigQueryOutputOptionInput] `json:"bigquery_output_option,omitempty"` + BigQueryOutputOption *parameter.NullableObject[output_options.BigQueryOutputOptionInput] `json:"bigquery_output_option,omitempty"` + SnowflakeOutputOption *parameter.NullableObject[output_options.SnowflakeOutputOptionInput] `json:"snowflake_output_option,omitempty"` } type UpdateOutputOptionInput struct { - BigQueryOutputOption *parameter.NullableObject[output_options.UpdateBigQueryOutputOptionInput] `json:"bigquery_output_option,omitempty"` + BigQueryOutputOption *parameter.NullableObject[output_options.UpdateBigQueryOutputOptionInput] `json:"bigquery_output_option,omitempty"` + SnowflakeOutputOption *parameter.NullableObject[output_options.UpdateSnowflakeOutputOptionInput] `json:"snowflake_output_option,omitempty"` } func (c *TroccoClient) CreateJobDefinition(in *CreateJobDefinitionInput) (*JobDefinition, error) { diff --git a/internal/client/parameter/job_definition/input_option/snowflake.go b/internal/client/parameter/job_definition/input_option/snowflake.go new file mode 100644 index 00000000..72988b46 --- /dev/null +++ b/internal/client/parameter/job_definition/input_option/snowflake.go @@ -0,0 +1,36 @@ +package input_options + +import ( + "terraform-provider-trocco/internal/client/parameter" +) + +type SnowflakeInputOptionInput struct { + Warehouse string `json:"warehouse"` + Database string `json:"database"` + Schema *parameter.NullableString `json:"schema"` + Query string `json:"query"` + FetchRows *parameter.NullableInt64 `json:"fetch_rows,omitempty"` + ConnectTimeout *parameter.NullableInt64 `json:"connect_timeout,omitempty"` + SocketTimeout *parameter.NullableInt64 `json:"socket_timeout,omitempty"` + SnowflakeConnectionID int64 `json:"snowflake_connection_id"` + InputOptionColumns []SnowflakeInputOptionColumn `json:"input_option_columns"` + CustomVariableSettings *[]parameter.CustomVariableSettingInput `json:"custom_variable_settings,omitempty"` +} + +type UpdateSnowflakeInputOptionInput struct { + Warehouse *string `json:"warehouse,omitempty"` + Database *string `json:"database,omitempty"` + Schema *parameter.NullableString `json:"schema,omitempty"` + Query *string `json:"query,omitempty"` + FetchRows *parameter.NullableInt64 `json:"fetch_rows,omitempty"` + ConnectTimeout *parameter.NullableInt64 `json:"connect_timeout,omitempty"` + SocketTimeout *parameter.NullableInt64 `json:"socket_timeout,omitempty"` + SnowflakeConnectionID *int64 `json:"snowflake_connection_id,omitempty"` + InputOptionColumns []SnowflakeInputOptionColumn `json:"input_option_columns,omitempty"` + CustomVariableSettings *[]parameter.CustomVariableSettingInput `json:"custom_variable_settings,omitempty"` +} + +type SnowflakeInputOptionColumn struct { + Name string `json:"name"` + Type string `json:"type"` +} diff --git a/internal/client/parameter/job_definition/output_option/snowflake.go b/internal/client/parameter/job_definition/output_option/snowflake.go new file mode 100644 index 00000000..8fb418c5 --- /dev/null +++ b/internal/client/parameter/job_definition/output_option/snowflake.go @@ -0,0 +1,51 @@ +package output_options + +import ( + "terraform-provider-trocco/internal/client/parameter" +) + +type SnowflakeOutputOptionInput struct { + Warehouse string `json:"warehouse"` + Database string `json:"database"` + Schema string `json:"schema"` + Table string `json:"table"` + Mode *parameter.NullableString `json:"mode,omitempty"` + EmptyFieldAsNull *parameter.NullableBool `json:"empty_field_as_null,omitempty"` + DeleteStageOnError *parameter.NullableBool `json:"delete_stage_on_error,omitempty"` + BatchSize *parameter.NullableInt64 `json:"batch_size,omitempty"` + RetryLimit *parameter.NullableInt64 `json:"retry_limit,omitempty"` + RetryWait *parameter.NullableInt64 `json:"retry_wait,omitempty"` + MaxRetryWait *parameter.NullableInt64 `json:"max_retry_wait,omitempty"` + DefaultTimeZone *parameter.NullableString `json:"default_time_zone,omitempty"` + SnowflakeConnectionId int64 `json:"snowflake_connection_id"` + SnowflakeOutputOptionColumnOptions *parameter.NullableObjectList[SnowflakeOutputOptionColumnOptionInput] `json:"snowflake_output_option_column_options,omitempty"` + SnowflakeOutputOptionMergeKeys *parameter.NullableObjectList[string] `json:"snowflake_output_option_merge_keys,omitempty"` + CustomVariableSettings *[]parameter.CustomVariableSettingInput `json:"custom_variable_settings,omitempty"` +} + +type UpdateSnowflakeOutputOptionInput struct { + Warehouse *string `json:"warehouse,omitempty"` + Database *string `json:"database,omitempty"` + Schema *string `json:"schema,omitempty"` + Table *string `json:"table,omitempty"` + Mode *parameter.NullableString `json:"mode,omitempty"` + EmptyFieldAsNull *parameter.NullableBool `json:"empty_field_as_null,omitempty"` + DeleteStageOnError *parameter.NullableBool `json:"delete_stage_on_error,omitempty"` + BatchSize *parameter.NullableInt64 `json:"batch_size,omitempty"` + RetryLimit *parameter.NullableInt64 `json:"retry_limit,omitempty"` + RetryWait *parameter.NullableInt64 `json:"retry_wait,omitempty"` + MaxRetryWait *parameter.NullableInt64 `json:"max_retry_wait,omitempty"` + DefaultTimeZone *parameter.NullableString `json:"default_time_zone,omitempty"` + SnowflakeConnectionId *int64 `json:"snowflake_connection_id,omitempty"` + SnowflakeOutputOptionColumnOptions *parameter.NullableObjectList[SnowflakeOutputOptionColumnOptionInput] `json:"snowflake_output_option_column_options,omitempty"` + SnowflakeOutputOptionMergeKeys *parameter.NullableObjectList[string] `json:"snowflake_output_option_merge_keys,omitempty"` + CustomVariableSettings *[]parameter.CustomVariableSettingInput `json:"custom_variable_settings,omitempty"` +} + +type SnowflakeOutputOptionColumnOptionInput struct { + Name string `json:"name"` + Type string `json:"type"` + ValueType *string `json:"value_type,omitempty"` + TimestampFormat *string `json:"timestamp_format,omitempty"` + Timezone *string `json:"timezone,omitempty"` +} diff --git a/internal/client/parameter/type.go b/internal/client/parameter/type.go index efe54746..4dc638cc 100644 --- a/internal/client/parameter/type.go +++ b/internal/client/parameter/type.go @@ -1,6 +1,8 @@ package parameter -import "encoding/json" +import ( + "encoding/json" +) type NullableInt64 struct { Value int64 @@ -50,3 +52,15 @@ func (n NullableObject[T]) MarshalJSON() ([]byte, error) { } return json.Marshal(n.Value) } + +type NullableObjectList[E any] struct { + Value *[]E + Valid bool +} + +func (n NullableObjectList[T]) MarshalJSON() ([]byte, error) { + if !n.Valid { + return []byte("[]"), nil + } + return json.Marshal(n.Value) +} diff --git a/internal/provider/model/custom_variable_setting.go b/internal/provider/model/custom_variable_setting.go index c186152d..1aee8196 100644 --- a/internal/provider/model/custom_variable_setting.go +++ b/internal/provider/model/custom_variable_setting.go @@ -64,23 +64,3 @@ func ToCustomVariableSettingInputs(settings *[]CustomVariableSetting) *[]paramet } return &inputs } - -func CustomVariableEntitiesToModels(customVariables *[]entity.CustomVariableSetting) *[]CustomVariableSetting { - if customVariables == nil { - return nil - } - outputs := make([]CustomVariableSetting, 0, len(*customVariables)) - for _, setting := range *customVariables { - outputs = append(outputs, CustomVariableSetting{ - Name: types.StringValue(setting.Name), - Type: types.StringValue(setting.Type), - Value: types.StringPointerValue(setting.Value), - Quantity: types.Int32PointerValue(setting.Quantity), - Unit: types.StringPointerValue(setting.Unit), - Direction: types.StringPointerValue(setting.Direction), - Format: types.StringPointerValue(setting.Format), - TimeZone: types.StringPointerValue(setting.TimeZone), - }) - } - return &outputs -} diff --git a/internal/provider/model/job_definition/input_option.go b/internal/provider/model/job_definition/input_option.go index 847675c6..321f4ac0 100644 --- a/internal/provider/model/job_definition/input_option.go +++ b/internal/provider/model/job_definition/input_option.go @@ -7,27 +7,31 @@ import ( ) type InputOption struct { - MySQLInputOption *input_options.MySQLInputOption `tfsdk:"mysql_input_option"` - GcsInputOption *input_options.GcsInputOption `tfsdk:"gcs_input_option"` + MySQLInputOption *input_options.MySQLInputOption `tfsdk:"mysql_input_option"` + GcsInputOption *input_options.GcsInputOption `tfsdk:"gcs_input_option"` + SnowflakeInputOption *input_options.SnowflakeInputOption `tfsdk:"snowflake_input_option"` } func NewInputOption(inputOption client.InputOption) *InputOption { return &InputOption{ - GcsInputOption: input_options.NewGcsInputOption(inputOption.GcsInputOption), - MySQLInputOption: input_options.NewMysqlInputOption(inputOption.MySQLInputOption), + GcsInputOption: input_options.NewGcsInputOption(inputOption.GcsInputOption), + MySQLInputOption: input_options.NewMysqlInputOption(inputOption.MySQLInputOption), + SnowflakeInputOption: input_options.NewSnowflakeInputOption(inputOption.SnowflakeInputOption), } } func (o InputOption) ToInput() client.InputOptionInput { return client.InputOptionInput{ - GcsInputOption: model.WrapObject(o.GcsInputOption.ToInput()), - MySQLInputOption: model.WrapObject(o.MySQLInputOption.ToInput()), + GcsInputOption: model.WrapObject(o.GcsInputOption.ToInput()), + MySQLInputOption: model.WrapObject(o.MySQLInputOption.ToInput()), + SnowflakeInputOption: model.WrapObject(o.SnowflakeInputOption.ToInput()), } } func (o InputOption) ToUpdateInput() *client.UpdateInputOptionInput { return &client.UpdateInputOptionInput{ - GcsInputOption: model.WrapObject(o.GcsInputOption.ToUpdateInput()), - MySQLInputOption: model.WrapObject(o.MySQLInputOption.ToUpdateInput()), + GcsInputOption: model.WrapObject(o.GcsInputOption.ToUpdateInput()), + MySQLInputOption: model.WrapObject(o.MySQLInputOption.ToUpdateInput()), + SnowflakeInputOption: model.WrapObject(o.SnowflakeInputOption.ToUpdateInput()), } } diff --git a/internal/provider/model/job_definition/input_option/mysql.go b/internal/provider/model/job_definition/input_option/mysql.go index bf6b7218..bc2d6900 100644 --- a/internal/provider/model/job_definition/input_option/mysql.go +++ b/internal/provider/model/job_definition/input_option/mysql.go @@ -86,7 +86,7 @@ func (mysqlInputOption *MySQLInputOption) ToInput() *input_options2.MySQLInputOp DefaultTimeZone: model.NewNullableString(mysqlInputOption.DefaultTimeZone), UseLegacyDatetimeCode: mysqlInputOption.UseLegacyDatetimeCode.ValueBool(), MySQLConnectionID: mysqlInputOption.MySQLConnectionID.ValueInt64(), - InputOptionColumns: toInputOptionColumnsInput(mysqlInputOption.InputOptionColumns), + InputOptionColumns: toMysqlInputOptionColumnsInput(mysqlInputOption.InputOptionColumns), CustomVariableSettings: model.ToCustomVariableSettingInputs(mysqlInputOption.CustomVariableSettings), } } @@ -96,7 +96,7 @@ func (mysqlInputOption *MySQLInputOption) ToUpdateInput() *input_options2.Update return nil } - inputOptionColumns := toInputOptionColumnsInput(mysqlInputOption.InputOptionColumns) + inputOptionColumns := toMysqlInputOptionColumnsInput(mysqlInputOption.InputOptionColumns) return &input_options2.UpdateMySQLInputOptionInput{ Database: mysqlInputOption.Database.ValueStringPointer(), @@ -116,7 +116,7 @@ func (mysqlInputOption *MySQLInputOption) ToUpdateInput() *input_options2.Update } } -func toInputOptionColumnsInput(columns []InputOptionColumn) []input_options2.InputOptionColumn { +func toMysqlInputOptionColumnsInput(columns []InputOptionColumn) []input_options2.InputOptionColumn { if columns == nil { return nil } diff --git a/internal/provider/model/job_definition/input_option/snowflake.go b/internal/provider/model/job_definition/input_option/snowflake.go new file mode 100644 index 00000000..eef857a5 --- /dev/null +++ b/internal/provider/model/job_definition/input_option/snowflake.go @@ -0,0 +1,116 @@ +package input_options + +import ( + "terraform-provider-trocco/internal/client/entity/job_definition/input_option" + param "terraform-provider-trocco/internal/client/parameter/job_definition/input_option" + "terraform-provider-trocco/internal/provider/model" + + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type SnowflakeInputOption struct { + Warehouse types.String `tfsdk:"warehouse"` + Database types.String `tfsdk:"database"` + Schema types.String `tfsdk:"schema"` + Query types.String `tfsdk:"query"` + FetchRows types.Int64 `tfsdk:"fetch_rows"` + ConnectTimeout types.Int64 `tfsdk:"connect_timeout"` + SocketTimeout types.Int64 `tfsdk:"socket_timeout"` + SnowflakeConnectionID types.Int64 `tfsdk:"snowflake_connection_id"` + InputOptionColumns []SnowflakeInputOptionColumn `tfsdk:"input_option_columns"` + CustomVariableSettings *[]model.CustomVariableSetting `tfsdk:"custom_variable_settings"` +} + +type SnowflakeInputOptionColumn struct { + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` +} + +func NewSnowflakeInputOption(snowflakeInputOption *input_option.SnowflakeInputOption) *SnowflakeInputOption { + if snowflakeInputOption == nil { + return nil + } + + return &SnowflakeInputOption{ + Warehouse: types.StringValue(snowflakeInputOption.Warehouse), + Database: types.StringValue(snowflakeInputOption.Database), + Schema: types.StringValue(snowflakeInputOption.Schema), + Query: types.StringValue(snowflakeInputOption.Query), + FetchRows: types.Int64PointerValue(snowflakeInputOption.FetchRows), + ConnectTimeout: types.Int64PointerValue(snowflakeInputOption.ConnectTimeout), + SocketTimeout: types.Int64PointerValue(snowflakeInputOption.SocketTimeout), + SnowflakeConnectionID: types.Int64Value(snowflakeInputOption.SnowflakeConnectionID), + InputOptionColumns: newSnowflakeInputOptionColumns(snowflakeInputOption.InputOptionColumns), + CustomVariableSettings: model.NewCustomVariableSettings(snowflakeInputOption.CustomVariableSettings), + } +} + +func newSnowflakeInputOptionColumns(inputOptionColumns []input_option.SnowflakeInputOptionColumn) []SnowflakeInputOptionColumn { + if inputOptionColumns == nil { + return nil + } + columns := make([]SnowflakeInputOptionColumn, 0, len(inputOptionColumns)) + for _, input := range inputOptionColumns { + column := SnowflakeInputOptionColumn{ + Name: types.StringValue(input.Name), + Type: types.StringValue(input.Type), + } + columns = append(columns, column) + } + return columns +} + +func (snowflakeInputOption *SnowflakeInputOption) ToInput() *param.SnowflakeInputOptionInput { + if snowflakeInputOption == nil { + return nil + } + + return ¶m.SnowflakeInputOptionInput{ + Warehouse: snowflakeInputOption.Warehouse.ValueString(), + Database: snowflakeInputOption.Database.ValueString(), + Schema: model.NewNullableString(snowflakeInputOption.Schema), + Query: snowflakeInputOption.Query.ValueString(), + FetchRows: model.NewNullableInt64(snowflakeInputOption.FetchRows), + ConnectTimeout: model.NewNullableInt64(snowflakeInputOption.ConnectTimeout), + SocketTimeout: model.NewNullableInt64(snowflakeInputOption.SocketTimeout), + SnowflakeConnectionID: snowflakeInputOption.SnowflakeConnectionID.ValueInt64(), + InputOptionColumns: toSnowflakeInputOptionColumnsInput(snowflakeInputOption.InputOptionColumns), + CustomVariableSettings: model.ToCustomVariableSettingInputs(snowflakeInputOption.CustomVariableSettings), + } +} + +func (snowflakeInputOption *SnowflakeInputOption) ToUpdateInput() *param.UpdateSnowflakeInputOptionInput { + if snowflakeInputOption == nil { + return nil + } + + inputOptionColumns := toSnowflakeInputOptionColumnsInput(snowflakeInputOption.InputOptionColumns) + + return ¶m.UpdateSnowflakeInputOptionInput{ + Warehouse: snowflakeInputOption.Warehouse.ValueStringPointer(), + Database: snowflakeInputOption.Database.ValueStringPointer(), + Schema: model.NewNullableString(snowflakeInputOption.Schema), + Query: snowflakeInputOption.Query.ValueStringPointer(), + FetchRows: model.NewNullableInt64(snowflakeInputOption.FetchRows), + ConnectTimeout: model.NewNullableInt64(snowflakeInputOption.ConnectTimeout), + SocketTimeout: model.NewNullableInt64(snowflakeInputOption.SocketTimeout), + SnowflakeConnectionID: snowflakeInputOption.SnowflakeConnectionID.ValueInt64Pointer(), + InputOptionColumns: inputOptionColumns, + CustomVariableSettings: model.ToCustomVariableSettingInputs(snowflakeInputOption.CustomVariableSettings), + } +} + +func toSnowflakeInputOptionColumnsInput(columns []SnowflakeInputOptionColumn) []param.SnowflakeInputOptionColumn { + if columns == nil { + return nil + } + + inputs := make([]param.SnowflakeInputOptionColumn, 0, len(columns)) + for _, column := range columns { + inputs = append(inputs, param.SnowflakeInputOptionColumn{ + Name: column.Name.ValueString(), + Type: column.Type.ValueString(), + }) + } + return inputs +} diff --git a/internal/provider/model/job_definition/output_option.go b/internal/provider/model/job_definition/output_option.go index da3e93b1..7f1bbf56 100644 --- a/internal/provider/model/job_definition/output_option.go +++ b/internal/provider/model/job_definition/output_option.go @@ -7,23 +7,27 @@ import ( ) type OutputOption struct { - BigQueryOutputOption *output_options.BigQueryOutputOption `tfsdk:"bigquery_output_option"` + BigQueryOutputOption *output_options.BigQueryOutputOption `tfsdk:"bigquery_output_option"` + SnowflakeOutputOption *output_options.SnowflakeOutputOption `tfsdk:"snowflake_output_option"` } func NewOutputOption(outputOption client.OutputOption) *OutputOption { return &OutputOption{ - BigQueryOutputOption: output_options.NewBigQueryOutputOption(outputOption.BigQueryOutputOption), + BigQueryOutputOption: output_options.NewBigQueryOutputOption(outputOption.BigQueryOutputOption), + SnowflakeOutputOption: output_options.NewSnowflakeOutputOption(outputOption.SnowflakeOutputOption), } } func (o OutputOption) ToInput() client.OutputOptionInput { return client.OutputOptionInput{ - BigQueryOutputOption: model.WrapObject(o.BigQueryOutputOption.ToInput()), + BigQueryOutputOption: model.WrapObject(o.BigQueryOutputOption.ToInput()), + SnowflakeOutputOption: model.WrapObject(o.SnowflakeOutputOption.ToInput()), } } func (o OutputOption) ToUpdateInput() *client.UpdateOutputOptionInput { return &client.UpdateOutputOptionInput{ - BigQueryOutputOption: model.WrapObject(o.BigQueryOutputOption.ToUpdateInput()), + BigQueryOutputOption: model.WrapObject(o.BigQueryOutputOption.ToUpdateInput()), + SnowflakeOutputOption: model.WrapObject(o.SnowflakeOutputOption.ToUpdateInput()), } } diff --git a/internal/provider/model/job_definition/output_option/snowflake.go b/internal/provider/model/job_definition/output_option/snowflake.go new file mode 100644 index 00000000..7e182959 --- /dev/null +++ b/internal/provider/model/job_definition/output_option/snowflake.go @@ -0,0 +1,178 @@ +package output_options + +import ( + "terraform-provider-trocco/internal/client/entity/job_definition/output_option" + output_options2 "terraform-provider-trocco/internal/client/parameter/job_definition/output_option" + "terraform-provider-trocco/internal/provider/model" + + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type SnowflakeOutputOption struct { + Warehouse types.String `tfsdk:"warehouse"` + Database types.String `tfsdk:"database"` + Schema types.String `tfsdk:"schema"` + Table types.String `tfsdk:"table"` + Mode types.String `tfsdk:"mode"` + EmptyFieldAsNull types.Bool `tfsdk:"empty_field_as_null"` + DeleteStageOnError types.Bool `tfsdk:"delete_stage_on_error"` + BatchSize types.Int64 `tfsdk:"batch_size"` + RetryLimit types.Int64 `tfsdk:"retry_limit"` + RetryWait types.Int64 `tfsdk:"retry_wait"` + MaxRetryWait types.Int64 `tfsdk:"max_retry_wait"` + DefaultTimeZone types.String `tfsdk:"default_time_zone"` + SnowflakeConnectionId types.Int64 `tfsdk:"snowflake_connection_id"` + SnowflakeOutputOptionColumnOptions []snowflakeOutputOptionColumnOption `tfsdk:"snowflake_output_option_column_options"` + SnowflakeOutputOptionMergeKeys []types.String `tfsdk:"snowflake_output_option_merge_keys"` + CustomVariableSettings *[]model.CustomVariableSetting `tfsdk:"custom_variable_settings"` +} + +type snowflakeOutputOptionColumnOption struct { + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` + ValueType types.String `tfsdk:"value_type"` + TimestampFormat types.String `tfsdk:"timestamp_format"` + Timezone types.String `tfsdk:"timezone"` +} + +func NewSnowflakeOutputOption(snowflakeOutputOption *output_option.SnowflakeOutputOption) *SnowflakeOutputOption { + if snowflakeOutputOption == nil { + return nil + } + + return &SnowflakeOutputOption{ + Warehouse: types.StringValue(snowflakeOutputOption.Warehouse), + Database: types.StringValue(snowflakeOutputOption.Database), + Schema: types.StringValue(snowflakeOutputOption.Schema), + Table: types.StringValue(snowflakeOutputOption.Table), + Mode: types.StringPointerValue(snowflakeOutputOption.Mode), + EmptyFieldAsNull: types.BoolPointerValue(snowflakeOutputOption.EmptyFieldAsNull), + DeleteStageOnError: types.BoolPointerValue(snowflakeOutputOption.DeleteStageOnError), + BatchSize: types.Int64PointerValue(snowflakeOutputOption.BatchSize), + RetryLimit: types.Int64PointerValue(snowflakeOutputOption.RetryLimit), + RetryWait: types.Int64PointerValue(snowflakeOutputOption.RetryWait), + MaxRetryWait: types.Int64PointerValue(snowflakeOutputOption.MaxRetryWait), + DefaultTimeZone: types.StringPointerValue(snowflakeOutputOption.DefaultTimeZone), + SnowflakeConnectionId: types.Int64Value(snowflakeOutputOption.SnowflakeConnectionId), + SnowflakeOutputOptionColumnOptions: newSnowflakeOutputOptionColumnOptions(snowflakeOutputOption.SnowflakeOutputOptionColumnOptions), + SnowflakeOutputOptionMergeKeys: newSnowflakeOutputOptionMergeKeys(snowflakeOutputOption.SnowflakeOutputOptionMergeKeys), + CustomVariableSettings: model.NewCustomVariableSettings(snowflakeOutputOption.CustomVariableSettings), + } +} + +func newSnowflakeOutputOptionMergeKeys(mergeKeys []string) []types.String { + if mergeKeys == nil { + return nil + } + + outputs := make([]types.String, 0, len(mergeKeys)) + for _, input := range mergeKeys { + outputs = append(outputs, types.StringValue(input)) + } + return outputs +} + +func newSnowflakeOutputOptionColumnOptions(snowflakeOutputOptionColumnOptions []output_option.SnowflakeOutputOptionColumnOption) []snowflakeOutputOptionColumnOption { + if snowflakeOutputOptionColumnOptions == nil { + return nil + } + + outputs := make([]snowflakeOutputOptionColumnOption, 0, len(snowflakeOutputOptionColumnOptions)) + for _, input := range snowflakeOutputOptionColumnOptions { + columnOption := snowflakeOutputOptionColumnOption{ + Name: types.StringValue(input.Name), + Type: types.StringValue(input.Type), + ValueType: types.StringPointerValue(input.ValueType), + TimestampFormat: types.StringPointerValue(input.TimestampFormat), + Timezone: types.StringPointerValue(input.Timezone), + } + outputs = append(outputs, columnOption) + } + return outputs +} + +func (snowflakeOutputOption *SnowflakeOutputOption) ToInput() *output_options2.SnowflakeOutputOptionInput { + if snowflakeOutputOption == nil { + return nil + } + + var mergeKeys *[]string + if snowflakeOutputOption.SnowflakeOutputOptionMergeKeys != nil { + mk := make([]string, 0, len(snowflakeOutputOption.SnowflakeOutputOptionMergeKeys)) + for _, input := range snowflakeOutputOption.SnowflakeOutputOptionMergeKeys { + mk = append(mk, input.ValueString()) + } + mergeKeys = &mk + } + + return &output_options2.SnowflakeOutputOptionInput{ + Warehouse: snowflakeOutputOption.Warehouse.ValueString(), + Database: snowflakeOutputOption.Database.ValueString(), + Schema: snowflakeOutputOption.Schema.ValueString(), + Table: snowflakeOutputOption.Table.ValueString(), + Mode: model.NewNullableString(snowflakeOutputOption.Mode), + EmptyFieldAsNull: model.NewNullableBool(snowflakeOutputOption.EmptyFieldAsNull), + DeleteStageOnError: model.NewNullableBool(snowflakeOutputOption.DeleteStageOnError), + BatchSize: model.NewNullableInt64(snowflakeOutputOption.BatchSize), + RetryLimit: model.NewNullableInt64(snowflakeOutputOption.RetryLimit), + RetryWait: model.NewNullableInt64(snowflakeOutputOption.RetryWait), + MaxRetryWait: model.NewNullableInt64(snowflakeOutputOption.MaxRetryWait), + DefaultTimeZone: model.NewNullableString(snowflakeOutputOption.DefaultTimeZone), + SnowflakeConnectionId: snowflakeOutputOption.SnowflakeConnectionId.ValueInt64(), + SnowflakeOutputOptionColumnOptions: model.WrapObjectList(toInputSnowflakeOutputOptionColumnOptions(snowflakeOutputOption.SnowflakeOutputOptionColumnOptions)), + SnowflakeOutputOptionMergeKeys: model.WrapObjectList(mergeKeys), + CustomVariableSettings: model.ToCustomVariableSettingInputs(snowflakeOutputOption.CustomVariableSettings), + } +} + +func (snowflakeOutputOption *SnowflakeOutputOption) ToUpdateInput() *output_options2.UpdateSnowflakeOutputOptionInput { + if snowflakeOutputOption == nil { + return nil + } + + var mergeKeys *[]string + if snowflakeOutputOption.SnowflakeOutputOptionMergeKeys != nil { + mk := make([]string, 0, len(snowflakeOutputOption.SnowflakeOutputOptionMergeKeys)) + for _, input := range snowflakeOutputOption.SnowflakeOutputOptionMergeKeys { + mk = append(mk, input.ValueString()) + } + mergeKeys = &mk + } + + return &output_options2.UpdateSnowflakeOutputOptionInput{ + Warehouse: snowflakeOutputOption.Warehouse.ValueStringPointer(), + Database: snowflakeOutputOption.Database.ValueStringPointer(), + Schema: snowflakeOutputOption.Schema.ValueStringPointer(), + Table: snowflakeOutputOption.Table.ValueStringPointer(), + Mode: model.NewNullableString(snowflakeOutputOption.Mode), + EmptyFieldAsNull: model.NewNullableBool(snowflakeOutputOption.EmptyFieldAsNull), + DeleteStageOnError: model.NewNullableBool(snowflakeOutputOption.DeleteStageOnError), + BatchSize: model.NewNullableInt64(snowflakeOutputOption.BatchSize), + RetryLimit: model.NewNullableInt64(snowflakeOutputOption.RetryLimit), + RetryWait: model.NewNullableInt64(snowflakeOutputOption.RetryWait), + MaxRetryWait: model.NewNullableInt64(snowflakeOutputOption.MaxRetryWait), + DefaultTimeZone: model.NewNullableString(snowflakeOutputOption.DefaultTimeZone), + SnowflakeConnectionId: snowflakeOutputOption.SnowflakeConnectionId.ValueInt64Pointer(), + SnowflakeOutputOptionColumnOptions: model.WrapObjectList(toInputSnowflakeOutputOptionColumnOptions(snowflakeOutputOption.SnowflakeOutputOptionColumnOptions)), + SnowflakeOutputOptionMergeKeys: model.WrapObjectList(mergeKeys), + CustomVariableSettings: model.ToCustomVariableSettingInputs(snowflakeOutputOption.CustomVariableSettings), + } +} + +func toInputSnowflakeOutputOptionColumnOptions(snowflakeOutputOptionColumnOptions []snowflakeOutputOptionColumnOption) *[]output_options2.SnowflakeOutputOptionColumnOptionInput { + if snowflakeOutputOptionColumnOptions == nil { + return nil + } + + outputs := make([]output_options2.SnowflakeOutputOptionColumnOptionInput, 0, len(snowflakeOutputOptionColumnOptions)) + for _, input := range snowflakeOutputOptionColumnOptions { + outputs = append(outputs, output_options2.SnowflakeOutputOptionColumnOptionInput{ + Name: input.Name.ValueString(), + Type: input.Type.ValueString(), + ValueType: input.ValueType.ValueStringPointer(), + TimestampFormat: input.TimestampFormat.ValueStringPointer(), + Timezone: input.Timezone.ValueStringPointer(), + }) + } + return &outputs +} diff --git a/internal/provider/model/types.go b/internal/provider/model/types.go index de8b4d2f..dc27cd2e 100644 --- a/internal/provider/model/types.go +++ b/internal/provider/model/types.go @@ -33,3 +33,7 @@ func NewNullableString(v types.String) *parameter.NullableString { func WrapObject[T any](v *T) *parameter.NullableObject[T] { return ¶meter.NullableObject[T]{Valid: v != nil, Value: v} } + +func WrapObjectList[T any](v *[]T) *parameter.NullableObjectList[T] { + return ¶meter.NullableObjectList[T]{Valid: v != nil, Value: v} +} diff --git a/internal/provider/planmodifier/bigquery_output_option_plan_modifier.go b/internal/provider/planmodifier/bigquery_output_option_plan_modifier.go index 37b1228d..e90d703e 100644 --- a/internal/provider/planmodifier/bigquery_output_option_plan_modifier.go +++ b/internal/provider/planmodifier/bigquery_output_option_plan_modifier.go @@ -12,7 +12,7 @@ var _ planmodifier.Object = &BigqueryOutputOptionPlanModifier{} type BigqueryOutputOptionPlanModifier struct{} func (d *BigqueryOutputOptionPlanModifier) Description(ctx context.Context) string { - return "Modifier for validating schedule attributes" + return "Modifier for validating bigquery output option attributes" } func (d *BigqueryOutputOptionPlanModifier) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/planmodifier/file_parser_plan_modifier.go b/internal/provider/planmodifier/file_parser_plan_modifier.go index 61069194..005a17f6 100644 --- a/internal/provider/planmodifier/file_parser_plan_modifier.go +++ b/internal/provider/planmodifier/file_parser_plan_modifier.go @@ -13,7 +13,7 @@ var _ planmodifier.Object = &FileParserPlanModifier{} type FileParserPlanModifier struct{} func (d *FileParserPlanModifier) Description(ctx context.Context) string { - return "Modifier for validating schedule attributes" + return "Modifier for validating file parser attributes" } func (d *FileParserPlanModifier) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/planmodifier/gcs_input_option_plan_modifier.go b/internal/provider/planmodifier/gcs_input_option_plan_modifier.go index 0371eaf6..813ceeb3 100644 --- a/internal/provider/planmodifier/gcs_input_option_plan_modifier.go +++ b/internal/provider/planmodifier/gcs_input_option_plan_modifier.go @@ -12,7 +12,7 @@ var _ planmodifier.Object = &GcsInputOptionPlanModifier{} type GcsInputOptionPlanModifier struct{} func (d *GcsInputOptionPlanModifier) Description(ctx context.Context) string { - return "Modifier for validating schedule attributes" + return "Modifier for validating gcs input option attributes" } func (d *GcsInputOptionPlanModifier) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/planmodifier/input_option_plan_modifier.go b/internal/provider/planmodifier/input_option_plan_modifier.go index a7f94d3f..dddb65a2 100644 --- a/internal/provider/planmodifier/input_option_plan_modifier.go +++ b/internal/provider/planmodifier/input_option_plan_modifier.go @@ -13,7 +13,7 @@ var _ planmodifier.Object = &InputOptionPlanModifier{} type InputOptionPlanModifier struct{} func (d *InputOptionPlanModifier) Description(ctx context.Context) string { - return "Modifier for validating schedule attributes" + return "Modifier for validating input option attributes" } func (d *InputOptionPlanModifier) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/planmodifier/mysql_input_option_plan_modifier.go b/internal/provider/planmodifier/mysql_input_option_plan_modifier.go index 93c0c030..2d3a6814 100644 --- a/internal/provider/planmodifier/mysql_input_option_plan_modifier.go +++ b/internal/provider/planmodifier/mysql_input_option_plan_modifier.go @@ -12,7 +12,7 @@ var _ planmodifier.Object = &MysqlInputOptionPlanModifier{} type MysqlInputOptionPlanModifier struct{} func (d *MysqlInputOptionPlanModifier) Description(ctx context.Context) string { - return "Modifier for validating schedule attributes" + return "Modifier for validating mysql input option attributes" } func (d *MysqlInputOptionPlanModifier) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/planmodifier/output_option_plan_modifier.go b/internal/provider/planmodifier/output_option_plan_modifier.go index 8ab4495f..c2fccff7 100644 --- a/internal/provider/planmodifier/output_option_plan_modifier.go +++ b/internal/provider/planmodifier/output_option_plan_modifier.go @@ -13,7 +13,7 @@ var _ planmodifier.Object = &OutputOptionPlanModifier{} type OutputOptionPlanModifier struct{} func (d *OutputOptionPlanModifier) Description(ctx context.Context) string { - return "Modifier for validating schedule attributes" + return "Modifier for validating output option attributes" } func (d *OutputOptionPlanModifier) MarkdownDescription(ctx context.Context) string { diff --git a/internal/provider/planmodifier/snowflake_output_option_column_plan_modifier.go b/internal/provider/planmodifier/snowflake_output_option_column_plan_modifier.go new file mode 100644 index 00000000..65ebfb54 --- /dev/null +++ b/internal/provider/planmodifier/snowflake_output_option_column_plan_modifier.go @@ -0,0 +1,63 @@ +package planmodifier + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ planmodifier.Object = &SnowflakeOutputOptionColumnPlanModifier{} + +type SnowflakeOutputOptionColumnPlanModifier struct{} + +func (d *SnowflakeOutputOptionColumnPlanModifier) Description(ctx context.Context) string { + return "Modifier for validating snowflake output option column attributes" +} + +func (d *SnowflakeOutputOptionColumnPlanModifier) MarkdownDescription(ctx context.Context) string { + return d.Description(ctx) +} + +func (d *SnowflakeOutputOptionColumnPlanModifier) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) { + var typ types.String + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, req.Path.AtName("type"), &typ)...) + if resp.Diagnostics.HasError() { + return + } + + var valueType types.String + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, req.Path.AtName("value_type"), &valueType)...) + if resp.Diagnostics.HasError() { + return + } + + var timestampFormat types.String + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, req.Path.AtName("timestamp_format"), ×tampFormat)...) + if resp.Diagnostics.HasError() { + return + } + + var timezone types.String + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, req.Path.AtName("timezone"), &timezone)...) + if resp.Diagnostics.HasError() { + return + } + + if typ.ValueString() != "TIMESTAMP" && !timezone.IsNull() { + addSnowflakeOutputOptionColumnAttributeError(req, resp, "timezone can only be set when type is 'TIMESTAMP'") + } + + if !timestampFormat.IsNull() && !(typ.ValueString() == "TIMESTAMP" && (valueType.ValueString() == "string" || valueType.ValueString() == "nstring")) { + addSnowflakeOutputOptionColumnAttributeError(req, resp, "timestamp_format can only be set when type is 'TIMESTAMP' and value_type is string or nstring") + } +} + +func addSnowflakeOutputOptionColumnAttributeError(req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse, message string) { + resp.Diagnostics.AddAttributeError( + req.Path, + "Snowflake output option column Validation Error", + fmt.Sprintf("Attribute %s %s", req.Path, message), + ) +} diff --git a/internal/provider/planmodifier/snowflake_output_option_plan_modifier.go b/internal/provider/planmodifier/snowflake_output_option_plan_modifier.go new file mode 100644 index 00000000..5aa9e97e --- /dev/null +++ b/internal/provider/planmodifier/snowflake_output_option_plan_modifier.go @@ -0,0 +1,45 @@ +package planmodifier + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ planmodifier.Object = &SnowflakeOutputOptionPlanModifier{} + +type SnowflakeOutputOptionPlanModifier struct{} + +func (d *SnowflakeOutputOptionPlanModifier) Description(ctx context.Context) string { + return "Modifier for validating snowflake output option attributes" +} + +func (d *SnowflakeOutputOptionPlanModifier) MarkdownDescription(ctx context.Context) string { + return d.Description(ctx) +} + +func (d *SnowflakeOutputOptionPlanModifier) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) { + var mode types.String + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, req.Path.AtName("mode"), &mode)...) + if resp.Diagnostics.HasError() { + return + } + var snowflakeOutputOptionMergeKeys types.List + resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, req.Path.AtName("snowflake_output_option_merge_keys"), &snowflakeOutputOptionMergeKeys)...) + if resp.Diagnostics.HasError() { + return + } + + if mode.ValueString() != "merge" && len(snowflakeOutputOptionMergeKeys.Elements()) > 0 { + addSnowflakeOutputOptionAttributeError(req, resp, "snowflake_output_option_merge_keys can only be set when mode is 'merge'") + } +} + +func addSnowflakeOutputOptionAttributeError(req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse, message string) { + resp.Diagnostics.AddAttributeError( + req.Path, + "Snowflake Output Option Validation Error", + fmt.Sprintf("Attribute %s %s", req.Path, message), + ) +} diff --git a/internal/provider/schema/job_definition/input_option.go b/internal/provider/schema/job_definition/input_option.go index d369c576..f23c0375 100644 --- a/internal/provider/schema/job_definition/input_option.go +++ b/internal/provider/schema/job_definition/input_option.go @@ -10,8 +10,9 @@ func InputOptionSchema() schema.Attribute { return schema.SingleNestedAttribute{ Required: true, Attributes: map[string]schema.Attribute{ - "mysql_input_option": MysqlInputOptionSchema(), - "gcs_input_option": GcsInputOptionSchema(), + "mysql_input_option": MysqlInputOptionSchema(), + "gcs_input_option": GcsInputOptionSchema(), + "snowflake_input_option": SnowflakeInputOptionSchema(), }, PlanModifiers: []planmodifier.Object{ &planmodifier2.InputOptionPlanModifier{}, diff --git a/internal/provider/schema/job_definition/output_option.go b/internal/provider/schema/job_definition/output_option.go index 39061103..e0205186 100644 --- a/internal/provider/schema/job_definition/output_option.go +++ b/internal/provider/schema/job_definition/output_option.go @@ -10,7 +10,8 @@ func OutputOptionSchema() schema.Attribute { return schema.SingleNestedAttribute{ Required: true, Attributes: map[string]schema.Attribute{ - "bigquery_output_option": BigqueryOutputOptionSchema(), + "bigquery_output_option": BigqueryOutputOptionSchema(), + "snowflake_output_option": SnowflakeOutputOptionSchema(), }, PlanModifiers: []planmodifier.Object{ &planmodifier2.OutputOptionPlanModifier{}, diff --git a/internal/provider/schema/job_definition/snowflake_input_option.go b/internal/provider/schema/job_definition/snowflake_input_option.go new file mode 100644 index 00000000..d2b0145d --- /dev/null +++ b/internal/provider/schema/job_definition/snowflake_input_option.go @@ -0,0 +1,110 @@ +package job_definition + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func SnowflakeInputOptionSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: true, + MarkdownDescription: "Attributes about source snowflake", + Attributes: map[string]schema.Attribute{ + "warehouse": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Warehouse name", + Validators: []validator.String{ + stringvalidator.UTF8LengthAtLeast(1), + }, + }, + "database": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.UTF8LengthAtLeast(1), + }, + MarkdownDescription: "Database name", + }, + "schema": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.UTF8LengthAtLeast(1), + }, + Default: stringdefault.StaticString("PUBLIC"), + MarkdownDescription: "Schema name", + }, + "query": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.UTF8LengthAtLeast(1), + }, + MarkdownDescription: "Query", + }, + "fetch_rows": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(10000), + MarkdownDescription: "Number of records processed by the cursor at one time", + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + "connect_timeout": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(300), + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + MarkdownDescription: "Connection timeout (sec)", + }, + "socket_timeout": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(1800), + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + MarkdownDescription: "Socket timeout (seconds)", + }, + "snowflake_connection_id": schema.Int64Attribute{ + Required: true, + MarkdownDescription: "Id of Snowflake connection", + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + }, + "custom_variable_settings": CustomVariableSettingsSchema(), + "input_option_columns": schema.ListNestedAttribute{ + Required: true, + MarkdownDescription: "List of columns to be retrieved and their types", + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.UTF8LengthAtLeast(1), + }, + MarkdownDescription: "Column name", + }, + "type": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Column type", + Validators: []validator.String{ + stringvalidator.OneOf("boolean", "long", "timestamp", "double", "string", "json"), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + }, + }, + }, + } +} diff --git a/internal/provider/schema/job_definition/snowflake_output_option.go b/internal/provider/schema/job_definition/snowflake_output_option.go new file mode 100644 index 00000000..2ddc37c4 --- /dev/null +++ b/internal/provider/schema/job_definition/snowflake_output_option.go @@ -0,0 +1,145 @@ +package job_definition + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + planmodifier2 "terraform-provider-trocco/internal/provider/planmodifier" +) + +func SnowflakeOutputOptionSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: true, + MarkdownDescription: "Attributes of destination Snowflake settings", + Attributes: map[string]schema.Attribute{ + "warehouse": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Warehouse name", + }, + "database": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Database name", + }, + "schema": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Schema name", + }, + "table": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Table name", + }, + "mode": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString("insert"), + Validators: []validator.String{ + stringvalidator.OneOf("insert", "insert_direct", "truncate_insert", "replace", "merge"), + }, + MarkdownDescription: "Transfer mode", + }, + "empty_field_as_null": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(true), + MarkdownDescription: "Replace empty string with NULL", + }, + "delete_stage_on_error": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + MarkdownDescription: "Delete temporary stage on error", + }, + "batch_size": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(50), + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + MarkdownDescription: "Batch size (MB)", + }, + "retry_limit": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(12), + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + MarkdownDescription: "Maximum retry limit", + }, + "retry_wait": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(1000), + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + MarkdownDescription: "Retry wait time (milliseconds)", + }, + "max_retry_wait": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(1800000), + Validators: []validator.Int64{ + int64validator.AtLeast(1), + }, + MarkdownDescription: "Maximum retry wait time (milliseconds)", + }, + "default_time_zone": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString("UTC"), + MarkdownDescription: "Default time zone", + }, + "snowflake_connection_id": schema.Int64Attribute{ + Required: true, + MarkdownDescription: "Snowflake connection ID", + }, + "snowflake_output_option_column_options": schema.ListNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Column name", + }, + "type": schema.StringAttribute{ + Required: true, + MarkdownDescription: "Data type", + }, + "value_type": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Value type", + }, + "timestamp_format": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Timestamp format", + }, + "timezone": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Time zone", + }, + }, + PlanModifiers: []planmodifier.Object{ + &planmodifier2.SnowflakeOutputOptionColumnPlanModifier{}, + }, + }, + }, + "snowflake_output_option_merge_keys": schema.ListAttribute{ + Optional: true, + ElementType: types.StringType, + MarkdownDescription: "Merge keys (only applicable if mode is 'merge')", + }, + "custom_variable_settings": CustomVariableSettingsSchema(), + }, + PlanModifiers: []planmodifier.Object{ + &planmodifier2.SnowflakeOutputOptionPlanModifier{}, + }, + } +}