diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9fe3190..e59edead 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,10 +9,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.5.0](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.4.2...v1.5.0)
+## Added
+Support for a new [Transformation Management API](https://fivetran.com/docs/rest-api/api-reference/transformation-management)
+- New resource `fivetran_transformation_project` instead of deprecated `fivetran_dbt_project`
+- New resource `fivetran_transformation` instead of deprecated `fivetran_dbt_transformation`
+- New data source `fivetran_transformation_project` instead of deprecated `fivetran_dbt_project`
+- New data source `fivetran_transformation_projects` instead of deprecated `fivetran_dbt_projects`
+- New data source `fivetran_transformation` instead of deprecated `fivetran_dbt_transformation`
+
- New data source `fivetran_connectors` that allows to retrieve the list of existing Connections available for the current account.
- New data source `fivetran_destinations` that allows to retrieve the list of existing Destinations available for the current account.
- New data source `fivetran_external_logs` that allows to retrieve the list of existing External Logging Services available for the current account.
+## Deprecated
+- Datasources `fivetran_dbt_project`, `fivetran_dbt_projects`, `fivetran_dbt_transformation`, `fivetran_dbt_models`
+- Resources `fivetran_dbt_project`, `fivetran_dbt_transformation`
+
## [1.4.2](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.4.1...v1.4.2)
## Added
diff --git a/docs/data-sources/quickstart_package.md b/docs/data-sources/quickstart_package.md
new file mode 100644
index 00000000..000a80f5
--- /dev/null
+++ b/docs/data-sources/quickstart_package.md
@@ -0,0 +1,29 @@
+---
+page_title: "Data Source: fivetran_quickstart_package"
+---
+
+# Data Source: fivetran_quickstart_package
+
+This data source returns the metadata details of the Quickstart transformation package if a valid identifier is provided
+
+## Example Usage
+
+```hcl
+data "fivetran_quickstart_package" "test" {
+ id = "id"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `id` (String) The unique identifier for the Quickstart transformation package definition within the Fivetran system
+
+### Read-Only
+
+- `connector_types` (Set of String) The set of connector types
+- `name` (String) The Quickstart transformation package name
+- `output_model_names` (Set of String) The list of transformation output models
+- `version` (String) The Quickstart package definition version
\ No newline at end of file
diff --git a/docs/data-sources/quickstart_packages.md b/docs/data-sources/quickstart_packages.md
new file mode 100644
index 00000000..d00d1004
--- /dev/null
+++ b/docs/data-sources/quickstart_packages.md
@@ -0,0 +1,32 @@
+---
+page_title: "Data Source: fivetran_quickstart_packages"
+---
+
+# Data Source: fivetran_quickstart_packages
+
+Returns a list of available Quickstart transformation package metadata details
+
+## Example Usage
+
+```hcl
+data "fivetran_quickstart_packages" "all_packages_metadata" {
+}
+```
+
+
+## Schema
+
+### Read-Only
+
+- `packages` (Block List) (see [below for nested schema](#nestedblock--packages))
+
+
+### Nested Schema for `packages`
+
+Read-Only:
+
+- `connector_types` (Set of String) The set of connector types
+- `id` (String) The unique identifier for the Quickstart transformation package definition within the Fivetran system
+- `name` (String) The Quickstart transformation package name
+- `output_model_names` (Set of String) The list of transformation output models
+- `version` (String) The Quickstart package definition version
\ No newline at end of file
diff --git a/docs/data-sources/transformation.md b/docs/data-sources/transformation.md
new file mode 100644
index 00000000..a291c733
--- /dev/null
+++ b/docs/data-sources/transformation.md
@@ -0,0 +1,68 @@
+---
+page_title: "Data Source: fivetran_transformation"
+---
+
+# Data Source: fivetran_transformation
+
+Returns transformation details if a valid identifier was provided
+
+## Example Usage
+
+```hcl
+data "fivetran_transformation" "test" {
+ id = "id"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `id` (String) The unique identifier for the Transformation within the Fivetran system.
+
+### Read-Only
+
+- `created_at` (String) The timestamp of when the transformation was created in your account.
+- `created_by_id` (String) The unique identifier for the User within the Fivetran system who created the transformation.
+- `output_model_names` (Set of String) Identifiers of related models.
+- `paused` (Boolean) The field indicating whether the transformation will be set into the paused state. By default, the value is false.
+- `schedule` (Block, Read-only) (see [below for nested schema](#nestedblock--schedule))
+- `status` (String) Status of transformation Project (NOT_READY, READY, ERROR).
+- `transformation_config` (Block, Read-only) (see [below for nested schema](#nestedblock--transformation_config))
+- `type` (String) Transformation type. The following values are supported: DBT_CORE, QUICKSTART.
+
+
+### Nested Schema for `schedule`
+
+Read-Only:
+
+- `connection_ids` (Set of String) The list of the connection identifiers to be used for the integrated schedule. Not expected for QUICKSTART transformations
+- `cron` (Set of String) Cron schedule: list of CRON strings. Used for for CRON schedule type
+- `days_of_week` (Set of String) The set of the days of the week the transformation should be launched on. The following values are supported: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY. Used for for INTEGRATED schedule type
+- `interval` (Number) The time interval in minutes between subsequent transformation runs. Used for for INTERVAL schedule type
+- `schedule_type` (String) The type of the schedule to run the Transformation on. The following values are supported: INTEGRATED, TIME_OF_DAY, INTERVAL, CRON.
+- `smart_syncing` (Boolean) The boolean flag that enables the Smart Syncing schedule
+- `time_of_day` (String) The time of the day the transformation should be launched at. Supported values are: "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00". Used for for TIME_OF_DAY schedule type
+
+
+
+### Nested Schema for `transformation_config`
+
+Read-Only:
+
+- `connection_ids` (Set of String) The list of the connection identifiers to be used for the integrated schedule. Also used to identify package_name automatically if package_name was not specified
+- `excluded_models` (Set of String) The list of excluded output model names
+- `name` (String) The transformation name
+- `package_name` (String) The Quickstart transformation package name
+- `project_id` (String) The unique identifier for the dbt Core project within the Fivetran system
+- `steps` (Attributes List) (see [below for nested schema](#nestedatt--transformation_config--steps))
+- `upgrade_available` (Boolean) The boolean flag indicating that a newer version is available for the transformation package
+
+
+### Nested Schema for `transformation_config.steps`
+
+Read-Only:
+
+- `command` (String) The dbt command in the transformation step
+- `name` (String) The step name
\ No newline at end of file
diff --git a/docs/data-sources/transformation_project.md b/docs/data-sources/transformation_project.md
new file mode 100644
index 00000000..2a7ae450
--- /dev/null
+++ b/docs/data-sources/transformation_project.md
@@ -0,0 +1,47 @@
+---
+page_title: "Data Source: fivetran_transformation_project"
+---
+
+# Data Source: fivetran_transformation_project
+
+Returns transformation project details if a valid identifier was provided
+
+## Example Usage
+
+```hcl
+data "fivetran_transformation_project" "test" {
+ id = "id"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `id` (String) The unique identifier for the transformation Project within the Fivetran system.
+
+### Read-Only
+
+- `created_at` (String) The timestamp of the transformation Project creation.
+- `created_by_id` (String) The unique identifier for the User within the Fivetran system who created the dbt Project.
+- `errors` (Set of String) List of environment variables defined as key-value pairs in the raw string format using = as a separator. The variable name should have the DBT_ prefix and can contain A-Z, 0-9, dash, underscore, or dot characters. Example: "DBT_VARIABLE=variable_value"
+- `group_id` (String) The unique identifier for the group within the Fivetran system.
+- `project_config` (Block, Read-only) (see [below for nested schema](#nestedblock--project_config))
+- `status` (String) Status of transformation Project (NOT_READY, READY, ERROR).
+- `type` (String) Transformation project type.
+
+
+### Nested Schema for `project_config`
+
+Read-Only:
+
+- `dbt_version` (String) The version of transformation that should run the project
+- `default_schema` (String) Default schema in destination. This production schema will contain your transformed data.
+- `environment_vars` (Set of String) List of environment variables defined as key-value pairs in the raw string format using = as a separator. The variable name should have the DBT_ prefix and can contain A-Z, 0-9, dash, underscore, or dot characters. Example: "DBT_VARIABLE=variable_value"
+- `folder_path` (String) Folder in Git repo with your transformation project
+- `git_branch` (String) Git branch
+- `git_remote_url` (String) Git remote URL with your transformation project
+- `public_key` (String) Public key to grant Fivetran SSH access to git repository.
+- `target_name` (String) Target name to set or override the value from the deployment.yaml
+- `threads` (Number) The number of threads transformation will use (from 1 to 32). Make sure this value is compatible with your destination type. For example, Snowflake supports only 8 concurrent queries on an X-Small warehouse.
\ No newline at end of file
diff --git a/docs/data-sources/transformation_projects.md b/docs/data-sources/transformation_projects.md
new file mode 100644
index 00000000..51e2ada5
--- /dev/null
+++ b/docs/data-sources/transformation_projects.md
@@ -0,0 +1,32 @@
+---
+page_title: "Data Source: fivetran_transformation_projects"
+---
+
+# Data Source: fivetran_transformation_projects
+
+Returns a list of all transformation projects available via API within your Fivetran account.
+
+## Example Usage
+
+```hcl
+data "fivetran_transformation_projects" "test" {
+}
+```
+
+
+## Schema
+
+### Read-Only
+
+- `projects` (Attributes List) (see [below for nested schema](#nestedatt--projects))
+
+
+### Nested Schema for `projects`
+
+Read-Only:
+
+- `created_at` (String) The timestamp of when the project was created in your account.
+- `created_by_id` (String) The unique identifier for the User within the Fivetran system who created the transformation Project.
+- `group_id` (String) The name of the group within your account related to the project.
+- `id` (String) The unique identifier for the transformation project within the Fivetran system.
+- `type` (String) Transformation project type.
\ No newline at end of file
diff --git a/docs/data-sources/transformations.md b/docs/data-sources/transformations.md
new file mode 100644
index 00000000..a6fc3892
--- /dev/null
+++ b/docs/data-sources/transformations.md
@@ -0,0 +1,74 @@
+---
+page_title: "Data Source: fivetran_transformations"
+---
+
+# Data Source: fivetran_transformations
+
+Returns a list of all transformations available via API within your Fivetran account.
+
+## Example Usage
+
+```hcl
+data "fivetran_transformations" "test" {
+}
+```
+
+
+## Schema
+
+### Optional
+
+- `transformations` (Block List) (see [below for nested schema](#nestedblock--transformations))
+
+
+### Nested Schema for `transformations`
+
+Required:
+
+- `id` (String) The unique identifier for the Transformation within the Fivetran system.
+
+Read-Only:
+
+- `created_at` (String) The timestamp of when the transformation was created in your account.
+- `created_by_id` (String) The unique identifier for the User within the Fivetran system who created the transformation.
+- `output_model_names` (Set of String) Identifiers of related models.
+- `paused` (Boolean) The field indicating whether the transformation will be set into the paused state. By default, the value is false.
+- `schedule` (Block, Read-only) (see [below for nested schema](#nestedblock--transformations--schedule))
+- `status` (String) Status of transformation Project (NOT_READY, READY, ERROR).
+- `transformation_config` (Block, Read-only) (see [below for nested schema](#nestedblock--transformations--transformation_config))
+- `type` (String) Transformation type. The following values are supported: DBT_CORE, QUICKSTART.
+
+
+### Nested Schema for `transformations.schedule`
+
+Read-Only:
+
+- `connection_ids` (Set of String) The list of the connection identifiers to be used for the integrated schedule. Not expected for QUICKSTART transformations
+- `cron` (Set of String) Cron schedule: list of CRON strings. Used for for CRON schedule type
+- `days_of_week` (Set of String) The set of the days of the week the transformation should be launched on. The following values are supported: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY. Used for for INTEGRATED schedule type
+- `interval` (Number) The time interval in minutes between subsequent transformation runs. Used for for INTERVAL schedule type
+- `schedule_type` (String) The type of the schedule to run the Transformation on. The following values are supported: INTEGRATED, TIME_OF_DAY, INTERVAL, CRON.
+- `smart_syncing` (Boolean) The boolean flag that enables the Smart Syncing schedule
+- `time_of_day` (String) The time of the day the transformation should be launched at. Supported values are: "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00". Used for for TIME_OF_DAY schedule type
+
+
+
+### Nested Schema for `transformations.transformation_config`
+
+Read-Only:
+
+- `connection_ids` (Set of String) The list of the connection identifiers to be used for the integrated schedule. Also used to identify package_name automatically if package_name was not specified
+- `excluded_models` (Set of String) The list of excluded output model names
+- `name` (String) The transformation name
+- `package_name` (String) The Quickstart transformation package name
+- `project_id` (String) The unique identifier for the dbt Core project within the Fivetran system
+- `steps` (Attributes List) (see [below for nested schema](#nestedatt--transformations--transformation_config--steps))
+- `upgrade_available` (Boolean) The boolean flag indicating that a newer version is available for the transformation package
+
+
+### Nested Schema for `transformations.transformation_config.steps`
+
+Read-Only:
+
+- `command` (String) The dbt command in the transformation step
+- `name` (String) The step name
\ No newline at end of file
diff --git a/docs/guides/transformation_private_git_deploy_key.md b/docs/guides/transformation_private_git_deploy_key.md
new file mode 100644
index 00000000..8fe4b2ef
--- /dev/null
+++ b/docs/guides/transformation_private_git_deploy_key.md
@@ -0,0 +1,60 @@
+----
+page_title: "Transformation Project Setup With Git Private Repo"
+subcategory: "Getting Started"
+---
+
+# How to set up a Transformation Project with private Git Repo.
+
+To be able to use private Transformation Project Git repository you have to grant Fivetran access to this repo.
+To do that you need to add a Deploy Key to your repository.
+To get SSH key from Fivetran create `fivetran_transformation_project` resource:
+
+```hcl
+resource "fivetran_group" "my_group" {
+ name = "My_Group"
+}
+
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 0
+ target_name = "target_name"
+ environment_vars = ["environment_var"]
+ }
+}
+```
+
+Then you need to set up the Transformation Project public key (field `public_key` in created resource) as a deploy key into your repo using:
+
+[GitHub Provider Repository Deploy Key Resource](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key):
+```hcl
+resource "github_repository_deploy_key" "example_repository_deploy_key" {
+ title = "Repository test key"
+ repository = "repo-owner/repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ read_only = true
+}
+```
+
+or
+
+[Bitbucket Provider Repository Deploy Key Resource]https://registry.terraform.io/providers/DrFaust92/bitbucket/latest/docs/resources/deploy_key)
+```hcl
+resource "bitbucket_deploy_key" "test" {
+ workspace = "repo-owner"
+ repository = "repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ label = "Repository test key"
+}
+```
+
+Since we recommend using third-party providers in this case, please make sure that access to the repositories is provided correctly and the providers are configured correctly for connection.
diff --git a/docs/guides/version_1.5.0_update_guides.md b/docs/guides/version_1.5.0_update_guides.md
new file mode 100644
index 00000000..97bc7c8b
--- /dev/null
+++ b/docs/guides/version_1.5.0_update_guides.md
@@ -0,0 +1,180 @@
+----
+page_title: "Version Update 1.5.0"
+subcategory: "Upgrade Guides"
+---
+
+# Version 1.5.0
+
+## What's new in 1.5.0
+
+In version `1.5.0` of Fivetran Terraform provider, цe have implemented new resources for managing Transformations:
+
+## Migration guide
+
+### Provider
+
+Update your provider configuration in the following way:
+
+Previous configuration:
+
+```hcl
+required_providers {
+ fivetran = {
+ version = "~> 1.4.2"
+ source = "fivetran/fivetran"
+ }
+ }
+```
+
+Updated configuration:
+
+```hcl
+required_providers {
+ fivetran = {
+ version = ">= 1.5.0"
+ source = "fivetran/fivetran"
+ }
+ }
+```
+
+### Resource `fivetran_dbt_project`
+
+Replace all your resources `fivetran_dbt_project` with `fivetran_transformation_project`
+
+Previous configuration:
+
+```hcl
+resource "fivetran_dbt_project" "test_project" {
+ provider = fivetran-provider
+ group_id = fivetran_destination.test_destination.id
+ dbt_version = "1.0.1"
+ threads = 1
+ default_schema = "dbt_demo_test_e2e_terraform"
+ type = "GIT"
+ project_config {
+ folder_path = "/folder/path"
+ git_remote_url = "git@github.com:fivetran/repo-name.git"
+ git_branch = "main"
+ }
+}
+```
+
+Updated configuration:
+
+```hcl
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git@github.com:fivetran/repo-name.git"
+ git_branch = "main"
+ folder_path = "/folder/path"
+ dbt_version = "1.0.1"
+ default_schema = "dbt_demo_test_e2e_terraform"
+ threads = 1
+ target_name = "target_name"
+ environment_vars = ["environment_var"]
+ }
+}
+```
+
+Then you need to set up the Transformation Project public key (field `public_key` in created resource) as a deploy key into your repo using:
+
+[GitHub Provider Repository Deploy Key Resource](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key):
+```hcl
+resource "github_repository_deploy_key" "example_repository_deploy_key" {
+ title = "Repository test key"
+ repository = "repo-owner/repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ read_only = true
+}
+```
+
+or
+
+[Bitbucket Provider Repository Deploy Key Resource]https://registry.terraform.io/providers/DrFaust92/bitbucket/latest/docs/resources/deploy_key)
+```hcl
+resource "bitbucket_deploy_key" "test" {
+ workspace = "repo-owner"
+ repository = "repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ label = "Repository test key"
+}
+```
+
+Since we recommend using third-party providers in this case, please make sure that access to the repositories is provided correctly and the providers are configured correctly for connection.
+
+### Resource `fivetran_dbt_transformation`
+
+Replace all your resources `fivetran_dbt_transformation` with `fivetran_transformation`
+
+Previous configuration:
+
+```hcl
+resource "fivetran_dbt_transformation" "transformation" {
+ dbt_model_name = "dbt_model_name"
+ dbt_project_id = "dbt_project_id"
+ run_tests = "false"
+ paused = "false"
+ schedule {
+ schedule_type = "TIME_OF_DAY"
+ time_of_day = "12:00"
+ days_of_week = ["MONDAY", "SATURDAY"]
+ }
+}
+```
+
+Updated configuration:
+
+```hcl
+resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "DBT_CORE"
+ paused = false
+
+ schedule {
+ cron = ["cron1","cron2"]
+ interval = 60
+ smart_syncing = true
+ connection_ids = ["connection_id1", "connection_id2"]
+ schedule_type = "TIME_OF_DAY"
+ days_of_week = ["MONDAY", "SATURDAY"]
+ time_of_day = "14:00"
+ }
+
+ transformation_config {
+ project_id = "dbt_project_id"
+ name = "name"
+ steps = [
+ {
+ name = "name1"
+ command = "command1"
+ },
+ {
+ name = "name2"
+ command = "command2"
+ }
+ ]
+ }
+}
+```
+
+### Datasources `fivetran_dbt_project`, `fivetran_dbt_projects`, `fivetran_dbt_transformation`, `fivetran_dbt_models`
+
+Replace datasources:
+- `fivetran_dbt_project` with `fivetran_transformation_project`
+- `fivetran_dbt_projects` with `fivetran_transformation_projects`
+- `fivetran_dbt_transformation` with `fivetran_transformation`
+Remove datasource `fivetran_dbt_models`
+
+### Update terraform state
+
+Once all configurations have been updated, run:
+
+```
+terraform init -upgrade
+```
\ No newline at end of file
diff --git a/docs/resources/dbt_git_project_config.md b/docs/resources/dbt_git_project_config.md
index 81f29780..d0e8e203 100644
--- a/docs/resources/dbt_git_project_config.md
+++ b/docs/resources/dbt_git_project_config.md
@@ -4,7 +4,7 @@ page_title: "Resource: fivetran_dbt_git_project_config"
# Resource: fivetran_dbt_git_project_config
-Resource is in ALPHA state.
+This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema.
This resource allows you to add and manage dbt Git Projects Configs.
diff --git a/docs/resources/dbt_project.md b/docs/resources/dbt_project.md
index f6eccf5b..074677d0 100644
--- a/docs/resources/dbt_project.md
+++ b/docs/resources/dbt_project.md
@@ -4,7 +4,7 @@ page_title: "Resource: fivetran_dbt_project"
# Resource: fivetran_dbt_project
-Resource is in ALPHA state.
+This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema.
This resource allows you to add, manage and delete dbt Projects in your account.
diff --git a/docs/resources/dbt_transformation.md b/docs/resources/dbt_transformation.md
index 98845ee1..e3a1dab9 100644
--- a/docs/resources/dbt_transformation.md
+++ b/docs/resources/dbt_transformation.md
@@ -4,7 +4,7 @@ page_title: "Resource: fivetran_dbt_transformation"
# Resource: fivetran_dbt_transformation
-Resource is in ALPHA state.
+This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema.
This resource allows you to add, manage and delete dbt Transformations for existing dbt Model.
To retrieve available dbt Models use this [Retrieve dbt Project models](https://fivetran.com/docs/rest-api/dbt-transformation-management#retrievedbtprojectmodels) endpoint.
diff --git a/docs/resources/transformation.md b/docs/resources/transformation.md
new file mode 100644
index 00000000..7a703098
--- /dev/null
+++ b/docs/resources/transformation.md
@@ -0,0 +1,173 @@
+---
+page_title: "Resource: fivetran_transformation"
+---
+
+# Resource: fivetran_transformation
+
+Resource is in ALPHA state.
+
+This resource allows you to add, manage and delete transformation projects in your account.
+
+## Example Usage for dbt Core Transformation
+
+```hcl
+resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "DBT_CORE"
+ paused = true
+
+ schedule {
+ schedule_type = "TIME_OF_DAY"
+ time_of_day = "11:00"
+ }
+
+ transformation_config {
+ project_id = "project_id"
+ name = "name"
+ steps = [
+ {
+ name = "name1"
+ command = "command1"
+ },
+ {
+ name = "name2"
+ command = "command2"
+ }
+ ]
+ }
+}
+```
+
+## Example Usage for Quickstart Transformation
+
+```hcl
+resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "QUICKSTART"
+ paused = true
+
+ schedule {
+ schedule_type = "TIME_OF_DAY"
+ time_of_day = "11:00"
+ }
+
+ transformation_config {
+ package_name = "package_name"
+ connection_ids = ["connection_id1", "connection_id2"]
+ excluded_models = ["excluded_model1", "excluded_model2"]
+ }
+}
+```
+
+## Example Usages for Transformation Schedule section
+
+```hcl
+schedule {
+ schedule_type = "TIME_OF_DAY"
+ days_of_week = ["MONDAY", "FRIDAY"]
+ time_of_day = "11:00"
+}
+```
+
+```hcl
+schedule {
+ schedule_type = "INTEGRATED"
+ connection_ids = ["connection_id1", "connection_id2"]
+}
+```
+
+```hcl
+schedule {
+ schedule_type = "INTERVAL"
+ interval = 601
+}
+```
+
+```hcl
+schedule {
+ schedule_type = "CRON"
+ cron = ["0 */1 * * *"]
+}
+```
+
+
+## Schema
+
+### Optional
+
+- `paused` (Boolean) The field indicating whether the transformation will be set into the paused state. By default, the value is false.
+- `schedule` (Block, Optional) (see [below for nested schema](#nestedblock--schedule))
+- `transformation_config` (Block, Optional) (see [below for nested schema](#nestedblock--transformation_config))
+- `type` (String) Transformation type. The following values are supported: DBT_CORE, QUICKSTART.
+
+### Read-Only
+
+- `created_at` (String) The timestamp of when the transformation was created in your account.
+- `created_by_id` (String) The unique identifier for the User within the Fivetran system who created the transformation.
+- `id` (String) The unique identifier for the Transformation within the Fivetran system.
+- `output_model_names` (Set of String) Identifiers of related models.
+- `status` (String) Status of transformation Project (NOT_READY, READY, ERROR).
+
+
+### Nested Schema for `schedule`
+
+Optional:
+
+- `connection_ids` (Set of String) The list of the connection identifiers to be used for the integrated schedule. Not expected for QUICKSTART transformations
+- `cron` (Set of String) Cron schedule: list of CRON strings. Used for for CRON schedule type
+- `days_of_week` (Set of String) The set of the days of the week the transformation should be launched on. The following values are supported: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY. Used for for INTEGRATED schedule type
+- `interval` (Number) The time interval in minutes between subsequent transformation runs. Used for for INTERVAL schedule type
+- `schedule_type` (String) The type of the schedule to run the Transformation on. The following values are supported: INTEGRATED, TIME_OF_DAY, INTERVAL, CRON.
+- `smart_syncing` (Boolean) The boolean flag that enables the Smart Syncing schedule
+- `time_of_day` (String) The time of the day the transformation should be launched at. Supported values are: "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00". Used for for TIME_OF_DAY schedule type
+
+
+
+### Nested Schema for `transformation_config`
+
+Optional:
+
+- `connection_ids` (Set of String) The list of the connection identifiers to be used for the integrated schedule. Also used to identify package_name automatically if package_name was not specified
+- `excluded_models` (Set of String) The list of excluded output model names
+- `name` (String) The transformation name
+- `package_name` (String) The Quickstart transformation package name
+- `project_id` (String) The unique identifier for the dbt Core project within the Fivetran system
+- `steps` (Attributes List) (see [below for nested schema](#nestedatt--transformation_config--steps))
+- `upgrade_available` (Boolean) The boolean flag indicating that a newer version is available for the transformation package
+
+
+### Nested Schema for `transformation_config.steps`
+
+Optional:
+
+- `command` (String) The dbt command in the transformation step
+- `name` (String) The step name
+
+## Import
+
+1. To import an existing `fivetran_transformation` resource into your Terraform state, you need to get **Transformation ID** via API call `GET https://api.fivetran.com/v1/transformations` to retrieve available projects.
+2. Fetch transformation details for particular `transformation-id` using `GET https://api.fivetran.com/v1/transformations/{transformation-id}` to ensure that this is the transformation you want to import.
+3. Define an empty resource in your `.tf` configuration:
+
+```hcl
+resource "fivetran_transformation" "my_imported_fivetran_transformation" {
+
+}
+```
+
+4. Run the `terraform import` command:
+
+```
+terraform import fivetran_transformation.my_imported_fivetran_transformation {Transformation ID}
+```
+
+4. Use the `terraform state show` command to get the values from the state:
+
+```
+terraform state show 'fivetran_transformation.my_imported_fivetran_transformation'
+```
+
+5. Copy the values and paste them to your `.tf` configuration.
+
diff --git a/docs/resources/transformation_project.md b/docs/resources/transformation_project.md
new file mode 100644
index 00000000..ce29c1d8
--- /dev/null
+++ b/docs/resources/transformation_project.md
@@ -0,0 +1,153 @@
+---
+page_title: "Resource: fivetran_transformation_project"
+---
+
+# Resource: fivetran_transformation_project
+
+Resource is in ALPHA state.
+
+This resource allows you to add, manage and delete transformation projects in your account.
+
+## Example Usage
+
+```hcl
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 1
+ target_name = "target_name"
+ environment_vars = ["DBT_VARIABLE=variable_value"]
+ }
+}
+```
+
+
+## Schema
+
+### Required
+
+- `group_id` (String) The unique identifier for the group within the Fivetran system.
+- `type` (String) Transformation project type.
+
+### Optional
+
+- `project_config` (Block, Optional) (see [below for nested schema](#nestedblock--project_config))
+- `run_tests` (Boolean) Specifies whether the setup tests should be run automatically. The default value is TRUE.
+
+### Read-Only
+
+- `created_at` (String) The timestamp of the transformation Project creation.
+- `created_by_id` (String) The unique identifier for the User within the Fivetran system who created the dbt Project.
+- `errors` (Set of String) List of environment variables defined as key-value pairs in the raw string format using = as a separator. The variable name should have the DBT_ prefix and can contain A-Z, 0-9, dash, underscore, or dot characters. Example: "DBT_VARIABLE=variable_value"
+- `id` (String) The unique identifier for the transformation Project within the Fivetran system.
+- `status` (String) Status of transformation Project (NOT_READY, READY, ERROR).
+
+
+### Nested Schema for `project_config`
+
+Optional:
+
+- `dbt_version` (String) The version of transformation that should run the project
+- `default_schema` (String) Default schema in destination. This production schema will contain your transformed data.
+- `environment_vars` (Set of String) List of environment variables defined as key-value pairs in the raw string format using = as a separator. The variable name should have the DBT_ prefix and can contain A-Z, 0-9, dash, underscore, or dot characters. Example: "DBT_VARIABLE=variable_value"
+- `folder_path` (String) Folder in Git repo with your transformation project
+- `git_branch` (String) Git branch
+- `git_remote_url` (String) Git remote URL with your transformation project
+- `target_name` (String) Target name to set or override the value from the deployment.yaml
+- `threads` (Number) The number of threads transformation will use (from 1 to 32). Make sure this value is compatible with your destination type. For example, Snowflake supports only 8 concurrent queries on an X-Small warehouse.
+
+Read-Only:
+
+- `public_key` (String) Public key to grant Fivetran SSH access to git repository.
+
+## Import
+
+1. To import an existing `fivetran_transformation_project` resource into your Terraform state, you need to get **Transformation Project ID** via API call `GET https://api.fivetran.com/v1/transformation-projects` to retrieve available projects.
+2. Fetch project details for particular `project-id` using `GET https://api.fivetran.com/v1/transformation-projects/{project-id}` to ensure that this is the project you want to import.
+3. Define an empty resource in your `.tf` configuration:
+
+```hcl
+resource "fivetran_transformation_project" "my_imported_fivetran_transformation_project" {
+
+}
+```
+
+4. Run the `terraform import` command:
+
+```
+terraform import fivetran_transformation_project.my_imported_fivetran_transformation_project {Transformation Project ID}
+```
+
+4. Use the `terraform state show` command to get the values from the state:
+
+```
+terraform state show 'fivetran_transformation_project.my_imported_fivetran_transformation_project'
+```
+
+5. Copy the values and paste them to your `.tf` configuration.
+
+
+## How to set up a Transformation Project with private Git Repo.
+
+To be able to use private Transformation Project Git repository you have to grant Fivetran access to this repo.
+To do that you need to add a Deploy Key to your repository.
+To get SSH key from Fivetran create `fivetran_transformation_project` resource:
+
+```hcl
+resource "fivetran_group" "my_group" {
+ name = "My_Group"
+}
+
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 1
+ target_name = "target_name"
+ environment_vars = ["DBT_VARIABLE=variable_value"]
+ }
+}
+```
+
+Then you need to set up the Transformation Project public key (field `public_key` in created resource) as a deploy key into your repo using:
+
+[GitHub Provider Repository Deploy Key Resource](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key):
+```hcl
+resource "github_repository_deploy_key" "example_repository_deploy_key" {
+ title = "Repository test key"
+ repository = "repo-owner/repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ read_only = true
+}
+```
+
+or
+
+[Bitbucket Provider Repository Deploy Key Resource]https://registry.terraform.io/providers/DrFaust92/bitbucket/latest/docs/resources/deploy_key)
+```hcl
+resource "bitbucket_deploy_key" "test" {
+ workspace = "repo-owner"
+ repository = "repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ label = "Repository test key"
+}
+```
+
+Since we recommend using third-party providers in this case, please make sure that access to the repositories is provided correctly and the providers are configured correctly for connection.
\ No newline at end of file
diff --git a/fivetran/framework/core/model/quickstart_package.go b/fivetran/framework/core/model/quickstart_package.go
new file mode 100644
index 00000000..64c53ef3
--- /dev/null
+++ b/fivetran/framework/core/model/quickstart_package.go
@@ -0,0 +1,35 @@
+package model
+
+import (
+ "context"
+
+ "github.com/fivetran/go-fivetran/transformations"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type QuickstartPackage struct {
+ Id types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ Version types.String `tfsdk:"version"`
+ ConnectorTypes types.Set `tfsdk:"connector_types"`
+ OutputModelNames types.Set `tfsdk:"output_model_names"`
+}
+
+func (d *QuickstartPackage) ReadFromResponse(ctx context.Context, resp transformations.QuickstartPackageResponse) {
+ d.Id = types.StringValue(resp.Data.Id)
+ d.Name = types.StringValue(resp.Data.Name)
+ d.Version = types.StringValue(resp.Data.Version)
+
+ connectors := []attr.Value{}
+ for _, connector := range resp.Data.ConnectorTypes {
+ connectors = append(connectors, types.StringValue(connector))
+ }
+ d.ConnectorTypes, _ = types.SetValue(types.StringType, connectors)
+
+ models := []attr.Value{}
+ for _, connector := range resp.Data.OutputModelNames {
+ models = append(models, types.StringValue(connector))
+ }
+ d.OutputModelNames, _ = types.SetValue(types.StringType, models)
+}
\ No newline at end of file
diff --git a/fivetran/framework/core/model/quickstart_packages.go b/fivetran/framework/core/model/quickstart_packages.go
new file mode 100644
index 00000000..e2b6de6f
--- /dev/null
+++ b/fivetran/framework/core/model/quickstart_packages.go
@@ -0,0 +1,62 @@
+package model
+
+import (
+ "context"
+
+ "github.com/fivetran/go-fivetran/transformations"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type QuickstartPackages struct {
+ Packages types.List `tfsdk:"packages"`
+}
+
+func (d *QuickstartPackages) ReadFromResponse(ctx context.Context, resp transformations.QuickstartPackagesListResponse) {
+ elemTypeAttrs := map[string]attr.Type{
+ "id": types.StringType,
+ "name": types.StringType,
+ "version": types.StringType,
+ "connector_types": types.SetType{ElemType: types.StringType},
+ "output_model_names": types.SetType{ElemType: types.StringType},
+ }
+
+ if resp.Data.Items == nil {
+ d.Packages = types.ListNull(types.ObjectType{AttrTypes: elemTypeAttrs})
+ } else {
+ items := []attr.Value{}
+ for _, v := range resp.Data.Items {
+ item := map[string]attr.Value{}
+ item["id"] = types.StringValue(v.Id)
+ item["name"] = types.StringValue(v.Name)
+ item["version"] = types.StringValue(v.Version)
+
+ connectors := []attr.Value{}
+ for _, el := range v.ConnectorTypes {
+ connectors = append(connectors, types.StringValue(el))
+ }
+
+ if len(connectors) > 0 {
+ item["connector_types"] = types.SetValueMust(types.StringType, connectors)
+ } else {
+ item["connector_types"] = types.SetNull(types.StringType)
+ }
+
+ models := []attr.Value{}
+ for _, el := range v.OutputModelNames {
+ models = append(models, types.StringValue(el))
+ }
+
+ if len(models) > 0 {
+ item["output_model_names"] = types.SetValueMust(types.StringType, models)
+ } else {
+ item["output_model_names"] = types.SetNull(types.StringType)
+ }
+
+ objectValue, _ := types.ObjectValue(elemTypeAttrs, item)
+ items = append(items, objectValue)
+ }
+
+ d.Packages, _ = types.ListValue(types.ObjectType{AttrTypes: elemTypeAttrs}, items)
+ }
+}
diff --git a/fivetran/framework/core/model/transformation.go b/fivetran/framework/core/model/transformation.go
new file mode 100644
index 00000000..fbccbafd
--- /dev/null
+++ b/fivetran/framework/core/model/transformation.go
@@ -0,0 +1,221 @@
+package model
+
+import (
+ "context"
+
+ sdk "github.com/fivetran/go-fivetran/transformations"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type Transformation struct {
+ Id types.String `tfsdk:"id"`
+ Status types.String `tfsdk:"status"`
+ ProjectType types.String `tfsdk:"type"`
+ Paused types.Bool `tfsdk:"paused"`
+ CreatedAt types.String `tfsdk:"created_at"`
+ CreatedById types.String `tfsdk:"created_by_id"`
+ OutputModelNames types.Set `tfsdk:"output_model_names"`
+ Schedule types.Object `tfsdk:"schedule"`
+ Config types.Object `tfsdk:"transformation_config"`
+}
+
+var (
+ stepAttrTypes = map[string]attr.Type{
+ "name": types.StringType,
+ "command": types.StringType,
+ }
+
+ stepSetAttrType = types.ObjectType{
+ AttrTypes: stepAttrTypes,
+ }
+
+ scheduleAttrs = map[string]attr.Type{
+ "schedule_type": types.StringType,
+ "days_of_week": types.SetType{ElemType: types.StringType},
+ "cron": types.SetType{ElemType: types.StringType},
+ "connection_ids": types.SetType{ElemType: types.StringType},
+ "interval": types.Int64Type,
+ "time_of_day": types.StringType,
+ "smart_syncing": types.BoolType,
+ }
+
+ configAttrs = map[string]attr.Type{
+ "project_id": types.StringType,
+ "package_name": types.StringType,
+ "name": types.StringType,
+ "excluded_models": types.SetType{ElemType: types.StringType},
+ "connection_ids": types.SetType{ElemType: types.StringType},
+ "steps": types.ListType{ElemType: types.ObjectType{AttrTypes: stepAttrTypes}},
+ "upgrade_available": types.BoolType,
+ }
+)
+
+func (d *Transformation) ReadFromResponse(ctx context.Context, resp sdk.TransformationResponse) {
+ d.Id = types.StringValue(resp.Data.Id)
+ d.Status = types.StringValue(resp.Data.Status)
+ d.ProjectType = types.StringValue(resp.Data.ProjectType)
+ d.CreatedAt = types.StringValue(resp.Data.CreatedAt)
+ d.CreatedById = types.StringValue(resp.Data.CreatedById)
+ d.Paused = types.BoolValue(resp.Data.Paused)
+
+ if resp.Data.OutputModelNames != nil {
+ d.OutputModelNames = types.SetValueMust(types.StringType, stringListToAttrList(resp.Data.OutputModelNames))
+ } else {
+ d.OutputModelNames = types.SetNull(types.StringType)
+ }
+
+ scheduleAttrValues := map[string]attr.Value{}
+ scheduleAttrValues["smart_syncing"] = types.BoolValue(resp.Data.TransformationSchedule.SmartSyncing)
+
+ if resp.Data.TransformationSchedule.ScheduleType == "INTERVAL" || resp.Data.TransformationSchedule.Interval > 0 {
+ scheduleAttrValues["interval"] = types.Int64Value(int64(resp.Data.TransformationSchedule.Interval))
+ } else {
+ if !d.Schedule.Attributes()["interval"].IsUnknown() {
+ scheduleAttrValues["interval"] = d.Schedule.Attributes()["interval"]
+ } else {
+ scheduleAttrValues["interval"] = types.Int64Null()
+ }
+ }
+
+ if resp.Data.TransformationSchedule.TimeOfDay != "" {
+ scheduleAttrValues["time_of_day"] = types.StringValue(resp.Data.TransformationSchedule.TimeOfDay)
+ } else {
+ if !d.Schedule.Attributes()["time_of_day"].IsUnknown() {
+ scheduleAttrValues["time_of_day"] = d.Schedule.Attributes()["time_of_day"]
+ } else {
+ scheduleAttrValues["time_of_day"] = types.StringNull()
+ }
+ }
+
+ if resp.Data.TransformationSchedule.ScheduleType != "" {
+ scheduleAttrValues["schedule_type"] = types.StringValue(resp.Data.TransformationSchedule.ScheduleType)
+ } else {
+ if !d.Schedule.Attributes()["schedule_type"].IsUnknown() {
+ scheduleAttrValues["schedule_type"] = d.Schedule.Attributes()["schedule_type"]
+ } else {
+ scheduleAttrValues["schedule_type"] = types.StringNull()
+ }
+ }
+
+ if resp.Data.TransformationSchedule.Cron != nil {
+ vars := []attr.Value{}
+ for _, el := range resp.Data.TransformationSchedule.Cron {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ scheduleAttrValues["cron"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ scheduleAttrValues["cron"] = types.SetNull(types.StringType)
+ }
+ } else {
+ if !d.Schedule.Attributes()["cron"].IsUnknown() {
+ scheduleAttrValues["cron"] = d.Schedule.Attributes()["cron"]
+ } else {
+ scheduleAttrValues["cron"] = types.SetNull(types.StringType)
+ }
+ }
+
+ if resp.Data.TransformationSchedule.ConnectionIds != nil {
+ vars := []attr.Value{}
+ for _, el := range resp.Data.TransformationSchedule.ConnectionIds {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ scheduleAttrValues["connection_ids"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ scheduleAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+ } else {
+ if !d.Schedule.Attributes()["connection_ids"].IsUnknown() {
+ scheduleAttrValues["connection_ids"] = d.Schedule.Attributes()["connection_ids"]
+ } else {
+ scheduleAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+ }
+
+ if resp.Data.TransformationSchedule.DaysOfWeek != nil {
+ vars := []attr.Value{}
+ for _, el := range resp.Data.TransformationSchedule.DaysOfWeek {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ scheduleAttrValues["days_of_week"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ scheduleAttrValues["days_of_week"] = types.SetNull(types.StringType)
+ }
+ } else {
+ if !d.Schedule.Attributes()["days_of_week"].IsUnknown() {
+ scheduleAttrValues["days_of_week"] = d.Schedule.Attributes()["days_of_week"]
+ } else {
+ scheduleAttrValues["days_of_week"] = types.SetNull(types.StringType)
+ }
+ }
+
+ d.Schedule = types.ObjectValueMust(scheduleAttrs, scheduleAttrValues)
+
+ configAttrValues := map[string]attr.Value{}
+ configAttrValues["upgrade_available"] = types.BoolValue(resp.Data.TransformationConfig.UpgradeAvailable)
+ if resp.Data.TransformationConfig.ProjectId != "" {
+ configAttrValues["project_id"] = types.StringValue(resp.Data.TransformationConfig.ProjectId)
+ } else {
+ configAttrValues["project_id"] = types.StringNull()
+ }
+
+ if resp.Data.TransformationConfig.PackageName != "" {
+ configAttrValues["package_name"] = types.StringValue(resp.Data.TransformationConfig.PackageName)
+ } else {
+ configAttrValues["package_name"] = types.StringNull()
+ }
+
+ if resp.Data.TransformationConfig.Name != "" {
+ configAttrValues["name"] = types.StringValue(resp.Data.TransformationConfig.Name)
+ } else {
+ configAttrValues["name"] = types.StringNull()
+ }
+
+ if resp.Data.TransformationConfig.ConnectionIds != nil {
+ vars := []attr.Value{}
+ for _, el := range resp.Data.TransformationConfig.ConnectionIds {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ configAttrValues["connection_ids"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ configAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+ } else {
+ configAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+
+ if resp.Data.TransformationConfig.ExcludedModels != nil {
+ vars := []attr.Value{}
+ for _, el := range resp.Data.TransformationConfig.ExcludedModels {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ configAttrValues["excluded_models"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ configAttrValues["excluded_models"] = types.SetNull(types.StringType)
+ }
+ } else {
+ configAttrValues["excluded_models"] = types.SetNull(types.StringType)
+ }
+
+ if resp.Data.TransformationConfig.Steps != nil {
+ subItems := []attr.Value{}
+ for _, sub := range resp.Data.TransformationConfig.Steps {
+ subItem := map[string]attr.Value{}
+ subItem["name"] = types.StringValue(sub.Name)
+ subItem["command"] = types.StringValue(sub.Command)
+
+ subObjectValue, _ := types.ObjectValue(stepAttrTypes, subItem)
+ subItems = append(subItems, subObjectValue)
+ }
+ configAttrValues["steps"], _ = types.ListValue(stepSetAttrType, subItems)
+ } else {
+ configAttrValues["steps"] = types.ListNull(stepSetAttrType)
+ }
+
+ d.Config = types.ObjectValueMust(configAttrs, configAttrValues)
+}
diff --git a/fivetran/framework/core/model/transformation_project.go b/fivetran/framework/core/model/transformation_project.go
new file mode 100644
index 00000000..e2ef282b
--- /dev/null
+++ b/fivetran/framework/core/model/transformation_project.go
@@ -0,0 +1,174 @@
+package model
+
+import (
+ "context"
+
+ "github.com/fivetran/go-fivetran/transformations"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type TransformationResourceProject struct {
+ Id types.String `tfsdk:"id"`
+ GroupId types.String `tfsdk:"group_id"`
+ Type types.String `tfsdk:"type"`
+ Status types.String `tfsdk:"status"`
+ CreatedAt types.String `tfsdk:"created_at"`
+ CreatedById types.String `tfsdk:"created_by_id"`
+ Errors types.Set `tfsdk:"errors"`
+ RunTests types.Bool `tfsdk:"run_tests"`
+ ProjectConfig types.Object `tfsdk:"project_config"`
+}
+
+type TransformationDatasourceProject struct {
+ Id types.String `tfsdk:"id"`
+ GroupId types.String `tfsdk:"group_id"`
+ Type types.String `tfsdk:"type"`
+ Status types.String `tfsdk:"status"`
+ CreatedAt types.String `tfsdk:"created_at"`
+ CreatedById types.String `tfsdk:"created_by_id"`
+ Errors types.Set `tfsdk:"errors"`
+ ProjectConfig types.Object `tfsdk:"project_config"`
+}
+
+func (d *TransformationResourceProject) ReadFromResponse(ctx context.Context, resp transformations.TransformationProjectResponse) {
+ d.Id = types.StringValue(resp.Data.Id)
+ d.GroupId = types.StringValue(resp.Data.GroupId)
+ d.Type = types.StringValue(resp.Data.ProjectType)
+ d.CreatedAt = types.StringValue(resp.Data.CreatedAt)
+ d.CreatedById = types.StringValue(resp.Data.CreatedById)
+ d.Status = types.StringValue(resp.Data.Status)
+
+ errors := []attr.Value{}
+ for _, el := range resp.Data.Errors {
+ errors = append(errors, types.StringValue(el))
+ }
+ if len(errors) > 0 {
+ d.Errors = types.SetValueMust(types.StringType, errors)
+ } else {
+ if d.Errors.IsUnknown() {
+ d.Errors = types.SetNull(types.StringType)
+ }
+ }
+
+ projectConfigTypes := map[string]attr.Type{
+ "dbt_version": types.StringType,
+ "default_schema": types.StringType,
+ "git_remote_url": types.StringType,
+ "folder_path": types.StringType,
+ "git_branch": types.StringType,
+ "target_name": types.StringType,
+ "environment_vars": types.SetType{ElemType: types.StringType},
+ "public_key": types.StringType,
+ "threads": types.Int64Type,
+ }
+ projectConfigItems := map[string]attr.Value{}
+ if resp.Data.ProjectConfig.DbtVersion != "" {
+ projectConfigItems["dbt_version"] = types.StringValue(resp.Data.ProjectConfig.DbtVersion)
+ } else {
+ projectConfigItems["dbt_version"] = types.StringNull()
+ }
+
+ if resp.Data.ProjectConfig.DefaultSchema != "" {
+ projectConfigItems["default_schema"] = types.StringValue(resp.Data.ProjectConfig.DefaultSchema)
+ } else {
+ projectConfigItems["default_schema"] = types.StringNull()
+ }
+
+ if resp.Data.ProjectConfig.GitRemoteUrl != "" {
+ projectConfigItems["git_remote_url"] = types.StringValue(resp.Data.ProjectConfig.GitRemoteUrl)
+ } else {
+ projectConfigItems["git_remote_url"] = types.StringNull()
+ }
+
+ if resp.Data.ProjectConfig.FolderPath != "" {
+ projectConfigItems["folder_path"] = types.StringValue(resp.Data.ProjectConfig.FolderPath)
+ } else {
+ projectConfigItems["folder_path"] = types.StringNull()
+ }
+
+ if resp.Data.ProjectConfig.GitBranch != "" {
+ projectConfigItems["git_branch"] = types.StringValue(resp.Data.ProjectConfig.GitBranch)
+ } else {
+ projectConfigItems["git_branch"] = types.StringNull()
+ }
+
+ if resp.Data.ProjectConfig.TargetName != "" {
+ projectConfigItems["target_name"] = types.StringValue(resp.Data.ProjectConfig.TargetName)
+ } else {
+ projectConfigItems["target_name"] = types.StringNull()
+ }
+
+ if resp.Data.ProjectConfig.PublicKey != "" {
+ projectConfigItems["public_key"] = types.StringValue(resp.Data.ProjectConfig.PublicKey)
+ } else {
+ projectConfigItems["public_key"] = types.StringNull()
+ }
+
+ projectConfigItems["threads"] = types.Int64Value(int64(resp.Data.ProjectConfig.Threads))
+ envVars := []attr.Value{}
+ for _, el := range resp.Data.ProjectConfig.EnvironmentVars {
+ envVars = append(envVars, types.StringValue(el))
+ }
+ if len(envVars) > 0 {
+ projectConfigItems["environment_vars"] = types.SetValueMust(types.StringType, envVars)
+ } else {
+ projectConfigItems["environment_vars"] = types.SetNull(types.StringType)
+ }
+
+ d.ProjectConfig, _ = types.ObjectValue(projectConfigTypes, projectConfigItems)
+}
+
+func (d *TransformationDatasourceProject) ReadFromResponse(ctx context.Context, resp transformations.TransformationProjectResponse) {
+ d.Id = types.StringValue(resp.Data.Id)
+ d.GroupId = types.StringValue(resp.Data.GroupId)
+ d.Type = types.StringValue(resp.Data.ProjectType)
+ d.CreatedAt = types.StringValue(resp.Data.CreatedAt)
+ d.CreatedById = types.StringValue(resp.Data.CreatedById)
+ d.Status = types.StringValue(resp.Data.Status)
+
+ errors := []attr.Value{}
+ for _, el := range resp.Data.Errors {
+ errors = append(errors, types.StringValue(el))
+ }
+ if len(errors) > 0 {
+ d.Errors = types.SetValueMust(types.StringType, errors)
+ } else {
+ if d.Errors.IsUnknown() {
+ d.Errors = types.SetNull(types.StringType)
+ }
+ }
+
+ projectConfigTypes := map[string]attr.Type{
+ "dbt_version": types.StringType,
+ "default_schema": types.StringType,
+ "git_remote_url": types.StringType,
+ "folder_path": types.StringType,
+ "git_branch": types.StringType,
+ "target_name": types.StringType,
+ "environment_vars": types.SetType{ElemType: types.StringType},
+ "public_key": types.StringType,
+ "threads": types.Int64Type,
+ }
+ projectConfigItems := map[string]attr.Value{}
+ projectConfigItems["dbt_version"] = types.StringValue(resp.Data.ProjectConfig.DbtVersion)
+ projectConfigItems["default_schema"] = types.StringValue(resp.Data.ProjectConfig.DefaultSchema)
+ projectConfigItems["git_remote_url"] = types.StringValue(resp.Data.ProjectConfig.GitRemoteUrl)
+ projectConfigItems["folder_path"] = types.StringValue(resp.Data.ProjectConfig.FolderPath)
+ projectConfigItems["git_branch"] = types.StringValue(resp.Data.ProjectConfig.GitBranch)
+ projectConfigItems["target_name"] = types.StringValue(resp.Data.ProjectConfig.TargetName)
+ projectConfigItems["public_key"] = types.StringValue(resp.Data.ProjectConfig.PublicKey)
+ projectConfigItems["threads"] = types.Int64Value(int64(resp.Data.ProjectConfig.Threads))
+
+ envVars := []attr.Value{}
+ for _, el := range resp.Data.ProjectConfig.EnvironmentVars {
+ envVars = append(envVars, types.StringValue(el))
+ }
+ if len(envVars) > 0 {
+ projectConfigItems["environment_vars"] = types.SetValueMust(types.StringType, envVars)
+ } else {
+ projectConfigItems["environment_vars"] = types.SetNull(types.StringType)
+ }
+
+ d.ProjectConfig, _ = types.ObjectValue(projectConfigTypes, projectConfigItems)
+}
\ No newline at end of file
diff --git a/fivetran/framework/core/model/transformation_projects.go b/fivetran/framework/core/model/transformation_projects.go
new file mode 100644
index 00000000..f36a7e9c
--- /dev/null
+++ b/fivetran/framework/core/model/transformation_projects.go
@@ -0,0 +1,40 @@
+package model
+
+import (
+ "context"
+
+ "github.com/fivetran/go-fivetran/transformations"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type TransformationProjects struct {
+ Projects types.List `tfsdk:"projects"`
+}
+
+func (d *TransformationProjects) ReadFromResponse(ctx context.Context, resp transformations.TransformationProjectsListResponse) {
+ elemTypeAttrs := map[string]attr.Type{
+ "id": types.StringType,
+ "type": types.StringType,
+ "group_id": types.StringType,
+ "created_at": types.StringType,
+ "created_by_id": types.StringType,
+ }
+
+ if resp.Data.Items == nil {
+ d.Projects = types.ListNull(types.ObjectType{AttrTypes: elemTypeAttrs})
+ } else {
+ items := []attr.Value{}
+ for _, v := range resp.Data.Items {
+ item := map[string]attr.Value{}
+ item["id"] = types.StringValue(v.Id)
+ item["type"] = types.StringValue(v.ProjectType)
+ item["group_id"] = types.StringValue(v.GroupId)
+ item["created_at"] = types.StringValue(v.CreatedAt)
+ item["created_by_id"] = types.StringValue(v.CreatedById)
+ objectValue, _ := types.ObjectValue(elemTypeAttrs, item)
+ items = append(items, objectValue)
+ }
+ d.Projects, _ = types.ListValue(types.ObjectType{AttrTypes: elemTypeAttrs}, items)
+ }
+}
\ No newline at end of file
diff --git a/fivetran/framework/core/model/transformations.go b/fivetran/framework/core/model/transformations.go
new file mode 100644
index 00000000..303db289
--- /dev/null
+++ b/fivetran/framework/core/model/transformations.go
@@ -0,0 +1,183 @@
+package model
+
+import (
+ "context"
+
+ "github.com/fivetran/go-fivetran/transformations"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+type Transformations struct {
+ Transformations types.List `tfsdk:"transformations"`
+}
+
+var (
+ elemTypeAttrs = map[string]attr.Type{
+ "id": types.StringType,
+ "status": types.StringType,
+ "type": types.StringType,
+ "created_at": types.StringType,
+ "created_by_id": types.StringType,
+ "paused": types.BoolType,
+ "output_model_names": types.SetType{ElemType: types.StringType},
+ "schedule": types.ObjectType{AttrTypes: scheduleAttrs},
+ "transformation_config": types.ObjectType{AttrTypes: configAttrs},
+ }
+)
+func (d *Transformations) ReadFromResponse(ctx context.Context, resp transformations.TransformationsListResponse) {
+ if resp.Data.Items == nil {
+ d.Transformations = types.ListNull(types.ObjectType{AttrTypes: elemTypeAttrs})
+ } else {
+ items := []attr.Value{}
+ for _, v := range resp.Data.Items {
+ item := map[string]attr.Value{}
+ item["id"] = types.StringValue(v.Id)
+ item["status"] = types.StringValue(v.Status)
+ item["type"] = types.StringValue(v.ProjectType)
+ item["created_at"] = types.StringValue(v.CreatedAt)
+ item["created_by_id"] = types.StringValue(v.CreatedById)
+ item["paused"] = types.BoolValue(v.Paused)
+
+ if v.OutputModelNames != nil {
+ item["output_model_names"] = types.SetValueMust(types.StringType, stringListToAttrList(v.OutputModelNames))
+ } else {
+ item["output_model_names"] = types.SetNull(types.StringType)
+ }
+
+ scheduleAttrValues := map[string]attr.Value{}
+ scheduleAttrValues["smart_syncing"] = types.BoolValue(v.TransformationSchedule.SmartSyncing)
+
+ if v.TransformationSchedule.ScheduleType == "INTERVAL" || v.TransformationSchedule.Interval > 0 {
+ scheduleAttrValues["interval"] = types.Int64Value(int64(v.TransformationSchedule.Interval))
+ } else {
+ scheduleAttrValues["interval"] = types.Int64Null()
+ }
+
+ if v.TransformationSchedule.TimeOfDay != "" {
+ scheduleAttrValues["time_of_day"] = types.StringValue(v.TransformationSchedule.TimeOfDay)
+ } else {
+ scheduleAttrValues["time_of_day"] = types.StringNull()
+ }
+
+ if v.TransformationSchedule.ScheduleType != "" {
+ scheduleAttrValues["schedule_type"] = types.StringValue(v.TransformationSchedule.ScheduleType)
+ } else {
+ scheduleAttrValues["schedule_type"] = types.StringNull()
+ }
+
+ if v.TransformationSchedule.Cron != nil {
+ vars := []attr.Value{}
+ for _, el := range v.TransformationSchedule.Cron {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ scheduleAttrValues["cron"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ scheduleAttrValues["cron"] = types.SetNull(types.StringType)
+ }
+ } else {
+ scheduleAttrValues["cron"] = types.SetNull(types.StringType)
+ }
+
+ if v.TransformationSchedule.ConnectionIds != nil {
+ vars := []attr.Value{}
+ for _, el := range v.TransformationSchedule.ConnectionIds {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ scheduleAttrValues["connection_ids"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ scheduleAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+ } else {
+ scheduleAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+
+ if v.TransformationSchedule.DaysOfWeek != nil {
+ vars := []attr.Value{}
+ for _, el := range v.TransformationSchedule.DaysOfWeek {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ scheduleAttrValues["days_of_week"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ scheduleAttrValues["days_of_week"] = types.SetNull(types.StringType)
+ }
+ } else {
+ scheduleAttrValues["days_of_week"] = types.SetNull(types.StringType)
+ }
+
+ item["schedule"] = types.ObjectValueMust(scheduleAttrs, scheduleAttrValues)
+
+ configAttrValues := map[string]attr.Value{}
+ configAttrValues["upgrade_available"] = types.BoolValue(v.TransformationConfig.UpgradeAvailable)
+ if v.TransformationConfig.ProjectId != "" {
+ configAttrValues["project_id"] = types.StringValue(v.TransformationConfig.ProjectId)
+ } else {
+ configAttrValues["project_id"] = types.StringNull()
+ }
+
+ if v.TransformationConfig.PackageName != "" {
+ configAttrValues["package_name"] = types.StringValue(v.TransformationConfig.PackageName)
+ } else {
+ configAttrValues["package_name"] = types.StringNull()
+ }
+
+ if v.TransformationConfig.Name != "" {
+ configAttrValues["name"] = types.StringValue(v.TransformationConfig.Name)
+ } else {
+ configAttrValues["name"] = types.StringNull()
+ }
+
+ if v.TransformationConfig.ConnectionIds != nil {
+ vars := []attr.Value{}
+ for _, el := range v.TransformationConfig.ConnectionIds {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ configAttrValues["connection_ids"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ configAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+ } else {
+ configAttrValues["connection_ids"] = types.SetNull(types.StringType)
+ }
+
+ if v.TransformationConfig.ExcludedModels != nil {
+ vars := []attr.Value{}
+ for _, el := range v.TransformationConfig.ExcludedModels {
+ vars = append(vars, types.StringValue(el))
+ }
+ if len(vars) > 0 {
+ configAttrValues["excluded_models"] = types.SetValueMust(types.StringType, vars)
+ } else {
+ configAttrValues["excluded_models"] = types.SetNull(types.StringType)
+ }
+ } else {
+ configAttrValues["excluded_models"] = types.SetNull(types.StringType)
+ }
+
+ if v.TransformationConfig.Steps != nil {
+ subItems := []attr.Value{}
+ for _, sub := range v.TransformationConfig.Steps {
+ subItem := map[string]attr.Value{}
+ subItem["name"] = types.StringValue(sub.Name)
+ subItem["command"] = types.StringValue(sub.Command)
+
+ subObjectValue, _ := types.ObjectValue(stepAttrTypes, subItem)
+ subItems = append(subItems, subObjectValue)
+ }
+ configAttrValues["steps"], _ = types.ListValue(stepSetAttrType, subItems)
+ } else {
+ configAttrValues["steps"] = types.ListNull(stepSetAttrType)
+ }
+
+ item["transformation_config"] = types.ObjectValueMust(configAttrs, configAttrValues)
+
+ objectValue, _ := types.ObjectValue(elemTypeAttrs, item)
+ items = append(items, objectValue)
+ }
+ d.Transformations, _ = types.ListValue(types.ObjectType{AttrTypes: elemTypeAttrs}, items)
+ }
+}
\ No newline at end of file
diff --git a/fivetran/framework/core/schema/dbt_git_project_config.go b/fivetran/framework/core/schema/dbt_git_project_config.go
index 875b4292..52082f50 100644
--- a/fivetran/framework/core/schema/dbt_git_project_config.go
+++ b/fivetran/framework/core/schema/dbt_git_project_config.go
@@ -7,6 +7,7 @@ import (
func DbtGitProjectConfigSchema() resourceSchema.Schema {
return resourceSchema.Schema{
+ DeprecationMessage: "This datasource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Attributes: dbtGitProjectConfigSchema().GetResourceSchema(),
}
}
diff --git a/fivetran/framework/core/schema/dbt_models.go b/fivetran/framework/core/schema/dbt_models.go
index 7d764a5e..99ba4e8f 100644
--- a/fivetran/framework/core/schema/dbt_models.go
+++ b/fivetran/framework/core/schema/dbt_models.go
@@ -7,6 +7,7 @@ import (
func DbtModelsDatasource() datasourceSchema.Schema {
return datasourceSchema.Schema{
+ DeprecationMessage: "This datasource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Attributes: map[string]datasourceSchema.Attribute{
"id": datasourceSchema.StringAttribute{
Computed: true,
diff --git a/fivetran/framework/core/schema/dbt_project.go b/fivetran/framework/core/schema/dbt_project.go
index 938f1ce2..5c73039a 100644
--- a/fivetran/framework/core/schema/dbt_project.go
+++ b/fivetran/framework/core/schema/dbt_project.go
@@ -19,6 +19,7 @@ func DbtProjectResource(ctx context.Context) resourceSchema.Schema {
}
return resourceSchema.Schema{
Attributes: attributes,
+ DeprecationMessage: "This datasource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Blocks: dbtProjectResourceBlocks(ctx),
Version: 1,
}
@@ -34,6 +35,7 @@ func DbtProjectDatasource() datasourceSchema.Schema {
}
return datasourceSchema.Schema{
Attributes: attributes,
+ DeprecationMessage: "This datasource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Blocks: dbtProjectDatasourceBlocks(),
}
}
diff --git a/fivetran/framework/core/schema/dbt_projects.go b/fivetran/framework/core/schema/dbt_projects.go
index 05806bf0..8d8fc36a 100644
--- a/fivetran/framework/core/schema/dbt_projects.go
+++ b/fivetran/framework/core/schema/dbt_projects.go
@@ -4,6 +4,7 @@ import datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasou
func DbtProjectsSchema() datasourceSchema.Schema {
return datasourceSchema.Schema{
+ DeprecationMessage: "This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Attributes: map[string]datasourceSchema.Attribute{
"projects": datasourceSchema.ListNestedAttribute{
Computed: true,
diff --git a/fivetran/framework/core/schema/dbt_transformation.go b/fivetran/framework/core/schema/dbt_transformation.go
index 2c00bd84..6949b6e5 100644
--- a/fivetran/framework/core/schema/dbt_transformation.go
+++ b/fivetran/framework/core/schema/dbt_transformation.go
@@ -11,6 +11,7 @@ import (
func DbtTransformationResourceSchema(ctx context.Context) resourceSchema.Schema {
return resourceSchema.Schema{
+ DeprecationMessage: "This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Attributes: dbtTransformationSchema().GetResourceSchema(),
Blocks: dbtTransformationResourceBlocks(ctx),
}
@@ -18,6 +19,7 @@ func DbtTransformationResourceSchema(ctx context.Context) resourceSchema.Schema
func DbtTransformationDatasourceSchema() datasourceSchema.Schema {
return datasourceSchema.Schema{
+ DeprecationMessage: "This datasource is Deprecated, please follow the 1.5.0 migration guide to update the schema",
Attributes: dbtTransformationSchema().GetDatasourceSchema(),
Blocks: dbtTransformationDatasourceBlocks(),
}
diff --git a/fivetran/framework/core/schema/quickstart_packages.go b/fivetran/framework/core/schema/quickstart_packages.go
new file mode 100644
index 00000000..f465296e
--- /dev/null
+++ b/fivetran/framework/core/schema/quickstart_packages.go
@@ -0,0 +1,70 @@
+package schema
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func QuickstartPackagesDatasource() datasourceSchema.Schema {
+ return datasourceSchema.Schema {
+ Blocks: map[string]datasourceSchema.Block{
+ "packages": datasourceSchema.ListNestedBlock{
+ NestedObject: datasourceSchema.NestedBlockObject{
+ Attributes: map[string]datasourceSchema.Attribute{
+ "id": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The unique identifier for the Quickstart transformation package definition within the Fivetran system",
+ },
+ "name": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The Quickstart transformation package name",
+ },
+ "version": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The Quickstart package definition version",
+ },
+ "connector_types": datasourceSchema.SetAttribute{
+ Computed: true,
+ Description: "The set of connector types",
+ ElementType: basetypes.StringType{},
+ },
+ "output_model_names": datasourceSchema.SetAttribute{
+ Computed: true,
+ Description: "The list of transformation output models",
+ ElementType: basetypes.StringType{},
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func QuickstartPackageDatasource() datasourceSchema.Schema {
+ return datasourceSchema.Schema {
+ Attributes: map[string]datasourceSchema.Attribute{
+ "id": datasourceSchema.StringAttribute{
+ Required: true,
+ Description: "The unique identifier for the Quickstart transformation package definition within the Fivetran system",
+ },
+ "name": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The Quickstart transformation package name",
+ },
+ "version": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The Quickstart package definition version",
+ },
+ "connector_types": datasourceSchema.SetAttribute{
+ Computed: true,
+ Description: "The set of connector types",
+ ElementType: basetypes.StringType{},
+ },
+ "output_model_names": datasourceSchema.SetAttribute{
+ Computed: true,
+ Description: "The list of transformation output models",
+ ElementType: basetypes.StringType{},
+ },
+ },
+ }
+}
\ No newline at end of file
diff --git a/fivetran/framework/core/schema/transformation.go b/fivetran/framework/core/schema/transformation.go
new file mode 100644
index 00000000..2a1668de
--- /dev/null
+++ b/fivetran/framework/core/schema/transformation.go
@@ -0,0 +1,231 @@
+package schema
+
+import (
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+)
+
+func TransformationResource() resourceSchema.Schema {
+ return resourceSchema.Schema{
+ Attributes: transformationSchema().GetResourceSchema(),
+ Blocks: transformationResourceBlocks(),
+ }
+}
+
+func TransformationDatasource() datasourceSchema.Schema {
+ return datasourceSchema.Schema{
+ Attributes: transformationSchema().GetDatasourceSchema(),
+ Blocks: transformationDatasourceBlocks(),
+ }
+}
+
+func TransformationListDatasource() datasourceSchema.Schema {
+ return datasourceSchema.Schema {
+ Blocks: map[string]datasourceSchema.Block{
+ "transformations": datasourceSchema.ListNestedBlock{
+ NestedObject: datasourceSchema.NestedBlockObject{
+ Attributes: transformationSchema().GetDatasourceSchema(),
+ Blocks: transformationDatasourceBlocks(),
+ },
+ },
+ },
+ }
+}
+
+func transformationSchema() core.Schema {
+ return core.Schema{
+ Fields: map[string]core.SchemaField{
+ "id": {
+ ValueType: core.String,
+ IsId: true,
+ Description: "The unique identifier for the Transformation within the Fivetran system.",
+ },
+ "paused": {
+ ValueType: core.Boolean,
+ Description: "The field indicating whether the transformation will be set into the paused state. By default, the value is false.",
+ },
+ "type": {
+ ValueType: core.StringEnum,
+ Description: "Transformation type. The following values are supported: DBT_CORE, QUICKSTART.",
+ },
+ "created_at": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "The timestamp of when the transformation was created in your account.",
+ },
+ "created_by_id": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "The unique identifier for the User within the Fivetran system who created the transformation.",
+ },
+ "status": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "Status of transformation Project (NOT_READY, READY, ERROR).",
+ },
+ "output_model_names": {
+ ValueType: core.StringsSet,
+ Readonly: true,
+ Description: "Identifiers of related models.",
+ },
+ },
+ }
+}
+
+func transformationScheduleSchema() core.Schema {
+ return core.Schema{
+ Fields: map[string]core.SchemaField{
+ "schedule_type": {
+ ValueType: core.StringEnum,
+ Description: "The type of the schedule to run the Transformation on. The following values are supported: INTEGRATED, TIME_OF_DAY, INTERVAL, CRON.",
+ },
+ "days_of_week": {
+ ValueType: core.StringsSet,
+ Description: "The set of the days of the week the transformation should be launched on. The following values are supported: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY. Used for for INTEGRATED schedule type",
+ },
+ "interval": {
+ ValueType: core.Integer,
+ Description: "The time interval in minutes between subsequent transformation runs. Used for for INTERVAL schedule type",
+ },
+ "time_of_day": {
+ ValueType: core.String,
+ Description: `The time of the day the transformation should be launched at. Supported values are: "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00". Used for for TIME_OF_DAY schedule type `,
+ },
+ "connection_ids": {
+ ValueType: core.StringsSet,
+ Description: "The list of the connection identifiers to be used for the integrated schedule. Not expected for QUICKSTART transformations",
+ },
+ "smart_syncing": {
+ ValueType: core.Boolean,
+ Description: "The boolean flag that enables the Smart Syncing schedule",
+ },
+ "cron": {
+ ValueType: core.StringsSet,
+ Description: "Cron schedule: list of CRON strings. Used for for CRON schedule type",
+ },
+ },
+ }
+}
+
+func transformationConfigDatasourceSchema() map[string]datasourceSchema.Attribute {
+ return map[string]datasourceSchema.Attribute{
+ "project_id": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The unique identifier for the dbt Core project within the Fivetran system",
+ },
+ "name": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The transformation name",
+ },
+ "package_name": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: `The Quickstart transformation package name`,
+ },
+ "connection_ids": datasourceSchema.SetAttribute{
+ ElementType: basetypes.StringType{},
+ Computed: true,
+ Description: "The list of the connection identifiers to be used for the integrated schedule. Also used to identify package_name automatically if package_name was not specified",
+ },
+ "excluded_models": datasourceSchema.SetAttribute{
+ ElementType: basetypes.StringType{},
+ Computed: true,
+ Description: "The list of excluded output model names",
+ },
+ "upgrade_available": datasourceSchema.BoolAttribute{
+ Computed: true,
+ Description: "The boolean flag indicating that a newer version is available for the transformation package",
+ },
+ "steps": datasourceSchema.ListNestedAttribute{
+ Computed: true,
+ NestedObject: datasourceSchema.NestedAttributeObject{
+ Attributes: map[string]datasourceSchema.Attribute{
+ "name": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The step name",
+ },
+ "command": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The dbt command in the transformation step",
+ },
+ },
+ },
+ },
+ }
+}
+
+func transformationConfigResourceSchema() map[string]resourceSchema.Attribute {
+ return map[string]resourceSchema.Attribute{
+ "project_id": resourceSchema.StringAttribute{
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
+ Optional: true,
+ Description: "The unique identifier for the dbt Core project within the Fivetran system",
+ },
+ "name": resourceSchema.StringAttribute{
+ Optional: true,
+ Description: "The transformation name",
+ },
+ "package_name": resourceSchema.StringAttribute{
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
+ Optional: true,
+ Description: `The Quickstart transformation package name`,
+ },
+ "connection_ids": resourceSchema.SetAttribute{
+ PlanModifiers: []planmodifier.Set{setplanmodifier.RequiresReplace()},
+ Optional: true,
+ ElementType: basetypes.StringType{},
+ Description: "The list of the connection identifiers to be used for the integrated schedule. Also used to identify package_name automatically if package_name was not specified",
+ },
+ "excluded_models": resourceSchema.SetAttribute{
+ Optional: true,
+ ElementType: basetypes.StringType{},
+ Description: "The list of excluded output model names",
+ },
+ "upgrade_available": resourceSchema.BoolAttribute{
+ Computed: true,
+ Optional: true,
+ Description: "The boolean flag indicating that a newer version is available for the transformation package",
+ },
+ "steps": resourceSchema.ListNestedAttribute{
+ Optional: true,
+ NestedObject: resourceSchema.NestedAttributeObject{
+ Attributes: map[string]resourceSchema.Attribute{
+ "name": resourceSchema.StringAttribute{
+ Optional: true,
+ Description: "The step name",
+ },
+ "command": resourceSchema.StringAttribute{
+ Optional: true,
+ Description: "The dbt command in the transformation step",
+ },
+ },
+ },
+ },
+ }
+}
+
+func transformationResourceBlocks() map[string]resourceSchema.Block {
+ return map[string]resourceSchema.Block{
+ "schedule": resourceSchema.SingleNestedBlock{
+ Attributes: transformationScheduleSchema().GetResourceSchema(),
+ },
+ "transformation_config": resourceSchema.SingleNestedBlock{
+ Attributes: transformationConfigResourceSchema(),
+ },
+ }
+}
+
+func transformationDatasourceBlocks() map[string]datasourceSchema.Block {
+ return map[string]datasourceSchema.Block{
+ "schedule": datasourceSchema.SingleNestedBlock{
+ Attributes: transformationScheduleSchema().GetDatasourceSchema(),
+ },
+ "transformation_config": datasourceSchema.SingleNestedBlock{
+ Attributes: transformationConfigDatasourceSchema(),
+ },
+ }
+}
diff --git a/fivetran/framework/core/schema/transformation_project.go b/fivetran/framework/core/schema/transformation_project.go
new file mode 100644
index 00000000..1ef8ad9d
--- /dev/null
+++ b/fivetran/framework/core/schema/transformation_project.go
@@ -0,0 +1,162 @@
+package schema
+
+import (
+ "context"
+
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+)
+
+func TransformationProjectResource(ctx context.Context) resourceSchema.Schema {
+ return resourceSchema.Schema{
+ Attributes: transformationProjectSchema().GetResourceSchema(),
+ Blocks: map[string]resourceSchema.Block{
+ "project_config": resourceSchema.SingleNestedBlock{
+ Attributes: transformationProjectConfigSchema().GetResourceSchema(),
+ },
+ },
+ }
+}
+
+func TransformationProjectDatasource() datasourceSchema.Schema {
+ return datasourceSchema.Schema{
+ Attributes: transformationProjectSchema().GetDatasourceSchema(),
+ Blocks: map[string]datasourceSchema.Block{
+ "project_config": datasourceSchema.SingleNestedBlock{
+ Attributes: transformationProjectConfigSchema().GetDatasourceSchema(),
+ },
+ },
+ }
+}
+
+func TransformationProjectListDatasource() datasourceSchema.Schema {
+ return datasourceSchema.Schema{
+ Attributes: map[string]datasourceSchema.Attribute{
+ "projects": datasourceSchema.ListNestedAttribute{
+ Computed: true,
+ NestedObject: datasourceSchema.NestedAttributeObject{
+ Attributes: map[string]datasourceSchema.Attribute{
+ "id": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The unique identifier for the transformation project within the Fivetran system.",
+ },
+ "group_id": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The name of the group within your account related to the project.",
+ },
+ "created_at": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The timestamp of when the project was created in your account.",
+ },
+ "created_by_id": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "The unique identifier for the User within the Fivetran system who created the transformation Project.",
+ },
+ "type": datasourceSchema.StringAttribute{
+ Computed: true,
+ Description: "Transformation project type.",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func transformationProjectSchema() core.Schema {
+ return core.Schema{
+ Fields: map[string]core.SchemaField{
+ "id": {
+ IsId: true,
+ ValueType: core.String,
+ Description: "The unique identifier for the transformation Project within the Fivetran system.",
+ },
+ "group_id": {
+ Required: true,
+ ForceNew: true,
+ ValueType: core.String,
+ Description: "The unique identifier for the group within the Fivetran system.",
+ },
+ "type": {
+ Required: true,
+ ForceNew: true,
+ ValueType: core.StringEnum,
+ Description: "Transformation project type.",
+ },
+ "status": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "Status of transformation Project (NOT_READY, READY, ERROR).",
+ },
+ "created_at": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "The timestamp of the transformation Project creation.",
+ },
+ "created_by_id": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "The unique identifier for the User within the Fivetran system who created the dbt Project.",
+ },
+ "errors": {
+ ValueType: core.StringsSet,
+ Readonly: true,
+ Description: "List of environment variables defined as key-value pairs in the raw string format using = as a separator. The variable name should have the DBT_ prefix and can contain A-Z, 0-9, dash, underscore, or dot characters. Example: \"DBT_VARIABLE=variable_value\"",
+ },
+ "run_tests": {
+ ValueType: core.Boolean,
+ ResourceOnly:true,
+ Description: "Specifies whether the setup tests should be run automatically. The default value is TRUE.",
+ },
+ },
+ }
+}
+
+func transformationProjectConfigSchema() core.Schema {
+ return core.Schema{
+ Fields: map[string]core.SchemaField{
+ "dbt_version": {
+ ValueType: core.String,
+ ForceNew: true,
+ Description: "The version of transformation that should run the project",
+ },
+ "default_schema": {
+ ValueType: core.String,
+ ForceNew: true,
+ Description: "Default schema in destination. This production schema will contain your transformed data.",
+ },
+ "git_remote_url": {
+ ValueType: core.String,
+ ForceNew: true,
+ Description: "Git remote URL with your transformation project",
+ },
+ "folder_path": {
+ ValueType: core.String,
+ Description: "Folder in Git repo with your transformation project",
+ },
+ "git_branch": {
+ ValueType: core.String,
+ Description: "Git branch",
+ },
+ "threads": {
+ ValueType: core.Integer,
+ Description: "The number of threads transformation will use (from 1 to 32). Make sure this value is compatible with your destination type. For example, Snowflake supports only 8 concurrent queries on an X-Small warehouse.",
+ },
+ "target_name": {
+ ValueType: core.String,
+ Description: "Target name to set or override the value from the deployment.yaml",
+ },
+ "environment_vars": {
+ ValueType: core.StringsSet,
+ Description: "List of environment variables defined as key-value pairs in the raw string format using = as a separator. The variable name should have the DBT_ prefix and can contain A-Z, 0-9, dash, underscore, or dot characters. Example: \"DBT_VARIABLE=variable_value\"",
+ },
+ "public_key": {
+ ValueType: core.String,
+ Readonly: true,
+ Description: "Public key to grant Fivetran SSH access to git repository.",
+ },
+
+ },
+ }
+}
\ No newline at end of file
diff --git a/fivetran/framework/datasources/quickstart_package.go b/fivetran/framework/datasources/quickstart_package.go
new file mode 100644
index 00000000..535c73e8
--- /dev/null
+++ b/fivetran/framework/datasources/quickstart_package.go
@@ -0,0 +1,58 @@
+package datasources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+func QuickstartPackage() datasource.DataSource {
+ return &quickstartPackage{}
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ datasource.DataSourceWithConfigure = &quickstartPackage{}
+
+type quickstartPackage struct {
+ core.ProviderDatasource
+}
+
+func (d *quickstartPackage) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = "fivetran_quickstart_package"
+}
+
+func (d *quickstartPackage) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = fivetranSchema.QuickstartPackageDatasource()
+}
+
+func (d *quickstartPackage) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ if d.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.QuickstartPackage
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ response, err := d.GetClient().NewQuickstartPackageDetails().PackageDefinitionId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "QuickstartPackage Read Error.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, response.Code, response.Message),
+ )
+ return
+ }
+
+ data.ReadFromResponse(ctx, response)
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
\ No newline at end of file
diff --git a/fivetran/framework/datasources/quickstart_package_test.go b/fivetran/framework/datasources/quickstart_package_test.go
new file mode 100644
index 00000000..1e29e9d6
--- /dev/null
+++ b/fivetran/framework/datasources/quickstart_package_test.go
@@ -0,0 +1,81 @@
+package datasources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+const (
+ quickstartPackageMappingResponse = `
+{
+ "id": "package_definition_id",
+ "name": "package_definition_name",
+ "version": "version",
+ "connector_types": [
+ "string"
+ ],
+ "output_model_names": [
+ "string"
+ ]
+ }
+ `
+)
+
+var (
+ quickstartPackageDataSourceMockGetHandler *mock.Handler
+
+ quickstartPackageDataSourceMockData map[string]interface{}
+)
+
+func setupMockClientQuickstartPackageDataSourceConfigMapping(t *testing.T) {
+ tfmock.MockClient().Reset()
+
+ quickstartPackageDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformations/package-metadata/package_definition_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ quickstartPackageDataSourceMockData = tfmock.CreateMapFromJsonString(t, quickstartPackageMappingResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", quickstartPackageDataSourceMockData), nil
+ },
+ )
+}
+
+func TestDataSourceQuickstartPackageConfigMappingMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ data "fivetran_quickstart_package" "test" {
+ provider = fivetran-provider
+ id = "package_definition_id"
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, quickstartPackageDataSourceMockGetHandler.Interactions, 1)
+ tfmock.AssertNotEmpty(t, quickstartPackageDataSourceMockData)
+ return nil
+ },
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_package.test", "id", "package_definition_id"),
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_package.test", "name", "package_definition_name"),
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_package.test", "version", "version"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientQuickstartPackageDataSourceConfigMapping(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/datasources/quickstart_packages.go b/fivetran/framework/datasources/quickstart_packages.go
new file mode 100644
index 00000000..79da2ac3
--- /dev/null
+++ b/fivetran/framework/datasources/quickstart_packages.go
@@ -0,0 +1,84 @@
+package datasources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ sdk "github.com/fivetran/go-fivetran/transformations"
+
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+)
+
+func QuickstartPackages() datasource.DataSource {
+ return &quickstartPackages{}
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ datasource.DataSourceWithConfigure = &quickstartPackages{}
+
+type quickstartPackages struct {
+ core.ProviderDatasource
+}
+
+func (d *quickstartPackages) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = "fivetran_quickstart_packages"
+}
+
+func (d *quickstartPackages) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = fivetranSchema.QuickstartPackagesDatasource()
+}
+
+func (d *quickstartPackages) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ if d.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.QuickstartPackages
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ var respNextCursor string
+ var listResponse sdk.QuickstartPackagesListResponse
+ limit := 1000
+
+ for {
+ var err error
+ var tmpResp sdk.QuickstartPackagesListResponse
+ svc := d.GetClient().NewQuickstartPackagesList()
+
+ if respNextCursor == "" {
+ tmpResp, err = svc.Limit(limit).Do(ctx)
+ }
+
+ if respNextCursor != "" {
+ tmpResp, err = svc.Limit(limit).Cursor(respNextCursor).Do(ctx)
+ }
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Read error.",
+ fmt.Sprintf("%v; code: %v", err, tmpResp.Code),
+ )
+ listResponse = sdk.QuickstartPackagesListResponse{}
+ }
+
+ listResponse.Data.Items = append(listResponse.Data.Items, tmpResp.Data.Items...)
+
+ if tmpResp.Data.NextCursor == "" {
+ break
+ }
+
+ respNextCursor = tmpResp.Data.NextCursor
+ }
+
+ data.ReadFromResponse(ctx, listResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/fivetran/framework/datasources/quickstart_packages_test.go b/fivetran/framework/datasources/quickstart_packages_test.go
new file mode 100644
index 00000000..1d08b718
--- /dev/null
+++ b/fivetran/framework/datasources/quickstart_packages_test.go
@@ -0,0 +1,98 @@
+package datasources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ QuickstartPackagesDataSourceMockGetHandler *mock.Handler
+ QuickstartPackagesDataSourceMockData map[string]interface{}
+)
+
+const (
+ QuickstartPackagesMappingResponse = `
+ {
+ "items": [
+ {
+ "id": "package_definition_id",
+ "name": "package_definition_name",
+ "version": "version",
+ "connector_types": [
+ "string"
+ ],
+ "output_model_names": [
+ "string"
+ ]
+ },
+ {
+ "id": "package_definition_id_2",
+ "name": "package_definition_name_2",
+ "version": "version_2",
+ "connector_types": [
+ "string_2"
+ ],
+ "output_model_names": [
+ "string_2"
+ ]
+ }
+ ],
+ "next_cursor": null
+ }`
+)
+
+func setupMockClientQuickstartPackagesDataSourceConfigMapping(t *testing.T) {
+ tfmock.MockClient().Reset()
+
+ QuickstartPackagesDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformations/package-metadata").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ QuickstartPackagesDataSourceMockData = tfmock.CreateMapFromJsonString(t, QuickstartPackagesMappingResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", QuickstartPackagesDataSourceMockData), nil
+ },
+ )
+}
+
+func TestDataSourceQuickstartPackagesMappingMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ data "fivetran_quickstart_packages" "test_quickstart_package" {
+ provider = fivetran-provider
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, QuickstartPackagesDataSourceMockGetHandler.Interactions, 1)
+ tfmock.AssertNotEmpty(t, QuickstartPackagesDataSourceMockData)
+ return nil
+ },
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_packages.test_quickstart_package", "packages.0.id", "package_definition_id"),
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_packages.test_quickstart_package", "packages.0.name", "package_definition_name"),
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_packages.test_quickstart_package", "packages.0.version", "version"),
+
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_packages.test_quickstart_package", "packages.1.id", "package_definition_id_2"),
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_packages.test_quickstart_package", "packages.1.name", "package_definition_name_2"),
+ resource.TestCheckResourceAttr("data.fivetran_quickstart_packages.test_quickstart_package", "packages.1.version", "version_2"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientQuickstartPackagesDataSourceConfigMapping(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/datasources/transformation.go b/fivetran/framework/datasources/transformation.go
new file mode 100644
index 00000000..8c51ef38
--- /dev/null
+++ b/fivetran/framework/datasources/transformation.go
@@ -0,0 +1,58 @@
+package datasources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+func Transformation() datasource.DataSource {
+ return &transformation{}
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ datasource.DataSourceWithConfigure = &transformation{}
+
+type transformation struct {
+ core.ProviderDatasource
+}
+
+func (d *transformation) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = "fivetran_transformation"
+}
+
+func (d *transformation) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = fivetranSchema.TransformationDatasource()
+}
+
+func (d *transformation) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ if d.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.Transformation
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ readResponse, err := d.GetClient().NewTransformationDetails().TransformationId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Transformation Read Error.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, readResponse.Code, readResponse.Message),
+ )
+ return
+ }
+
+ data.ReadFromResponse(ctx, readResponse)
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
\ No newline at end of file
diff --git a/fivetran/framework/datasources/transformation_project.go b/fivetran/framework/datasources/transformation_project.go
new file mode 100644
index 00000000..46ca4f7f
--- /dev/null
+++ b/fivetran/framework/datasources/transformation_project.go
@@ -0,0 +1,58 @@
+package datasources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+func TransformationProject() datasource.DataSource {
+ return &transformationProject{}
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ datasource.DataSourceWithConfigure = &transformationProject{}
+
+type transformationProject struct {
+ core.ProviderDatasource
+}
+
+func (d *transformationProject) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = "fivetran_transformation_project"
+}
+
+func (d *transformationProject) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = fivetranSchema.TransformationProjectDatasource()
+}
+
+func (d *transformationProject) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ if d.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.TransformationDatasourceProject
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ projectResponse, err := d.GetClient().NewTransformationProjectDetails().ProjectId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "TransformationProject Read Error.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, projectResponse.Code, projectResponse.Message),
+ )
+ return
+ }
+
+ data.ReadFromResponse(ctx, projectResponse)
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
\ No newline at end of file
diff --git a/fivetran/framework/datasources/transformation_project_test.go b/fivetran/framework/datasources/transformation_project_test.go
new file mode 100644
index 00000000..c8ae0464
--- /dev/null
+++ b/fivetran/framework/datasources/transformation_project_test.go
@@ -0,0 +1,108 @@
+package datasources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ transformationProjectDataSourceMockGetHandler *mock.Handler
+ transformationProjectDataSourceMockData map[string]interface{}
+)
+
+func setupMockClientTransformationProjectDataSourceMappingTest(t *testing.T) {
+ transformationProjectResponse := `
+{
+ "id": "projectId",
+ "type": "DBT_GIT",
+ "status": "NOT_READY",
+ "errors": [
+ "string"
+ ],
+ "created_at": "created_at",
+ "group_id": "group_id",
+ "setup_tests": [
+ {
+ "title": "Test Title",
+ "status": "FAILED",
+ "message": "Error message",
+ "details": "Error details"
+ }
+ ],
+ "created_by_id": "created_by_id",
+ "project_config": {
+ "dbt_version": "dbt_version",
+ "default_schema": "default_schema",
+ "git_remote_url": "git_remote_url",
+ "folder_path": "folder_path",
+ "git_branch": "git_branch",
+ "threads": 0,
+ "target_name": "target_name",
+ "environment_vars": [
+ "environment_var"
+ ],
+ "public_key": "public_key"
+ }
+}`
+ tfmock.MockClient().Reset()
+
+ transformationProjectDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformation-projects/projectId").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ transformationProjectDataSourceMockData = tfmock.CreateMapFromJsonString(t, transformationProjectResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationProjectDataSourceMockData), nil
+ },
+ )
+}
+
+func TestDataSourceTransformationProjectMappingMock(t *testing.T) {
+ // NOTE: the config is totally inconsistent and contains all possible values for mapping test
+ step1 := resource.TestStep{
+ Config: `
+ data "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ id = "projectId"
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationProjectDataSourceMockGetHandler.Interactions, 1)
+ tfmock.AssertNotEmpty(t, transformationProjectDataSourceMockData)
+ return nil
+ },
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "id", "projectId"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "group_id", "group_id"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "type", "DBT_GIT"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.dbt_version", "dbt_version"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.public_key", "public_key"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.default_schema", "default_schema"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.target_name", "target_name"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.environment_vars.0", "environment_var"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.git_remote_url", "git_remote_url"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.git_branch", "git_branch"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_project.project", "project_config.folder_path", "folder_path"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientTransformationProjectDataSourceMappingTest(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/datasources/transformation_projects.go b/fivetran/framework/datasources/transformation_projects.go
new file mode 100644
index 00000000..b977980a
--- /dev/null
+++ b/fivetran/framework/datasources/transformation_projects.go
@@ -0,0 +1,83 @@
+package datasources
+
+import (
+ "context"
+ "fmt"
+
+ sdk "github.com/fivetran/go-fivetran/transformations"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+func TransformationProjects() datasource.DataSource {
+ return &transformationProjects{}
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ datasource.DataSourceWithConfigure = &transformationProjects{}
+
+type transformationProjects struct {
+ core.ProviderDatasource
+}
+
+func (d *transformationProjects) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = "fivetran_transformation_projects"
+}
+
+func (d *transformationProjects) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = fivetranSchema.TransformationProjectListDatasource()
+}
+
+func (d *transformationProjects) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ if d.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.TransformationProjects
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ var respNextCursor string
+ var listResponse sdk.TransformationProjectsListResponse
+ limit := 1000
+
+ for {
+ var err error
+ var tmpResp sdk.TransformationProjectsListResponse
+ svc := d.GetClient().NewTransformationProjectsList()
+
+ if respNextCursor == "" {
+ tmpResp, err = svc.Limit(limit).Do(ctx)
+ }
+
+ if respNextCursor != "" {
+ tmpResp, err = svc.Limit(limit).Cursor(respNextCursor).Do(ctx)
+ }
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Read error.",
+ fmt.Sprintf("%v; code: %v", err, tmpResp.Code),
+ )
+ listResponse = sdk.TransformationProjectsListResponse{}
+ }
+
+ listResponse.Data.Items = append(listResponse.Data.Items, tmpResp.Data.Items...)
+
+ if tmpResp.Data.NextCursor == "" {
+ break
+ }
+
+ respNextCursor = tmpResp.Data.NextCursor
+ }
+
+ data.ReadFromResponse(ctx, listResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/fivetran/framework/datasources/transformation_projects_test.go b/fivetran/framework/datasources/transformation_projects_test.go
new file mode 100644
index 00000000..5669ad0e
--- /dev/null
+++ b/fivetran/framework/datasources/transformation_projects_test.go
@@ -0,0 +1,93 @@
+package datasources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ transformationProjectsDataSourceMockGetHandler *mock.Handler
+ transformationProjectsDataSourceMockData map[string]interface{}
+)
+
+const (
+ transformationProjectsMappingResponse = `
+ {
+ "items":[
+ {
+ "id": "string",
+ "type": "DBT_GIT",
+ "created_at": "created_at",
+ "created_by_id": "string",
+ "group_id": "string"
+ },
+ {
+ "id": "string2",
+ "type": "DBT_GIT",
+ "created_at": "created_at_2",
+ "created_by_id": "string2",
+ "group_id": "string2"
+ }
+ ],
+ "next_cursor": null
+ }
+ `
+)
+
+func setupMockClientTransformationProjectsDataSourceConfigMapping(t *testing.T) {
+ tfmock.MockClient().Reset()
+ transformationProjectsDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformation-projects").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ transformationProjectsDataSourceMockData = tfmock.CreateMapFromJsonString(t, transformationProjectsMappingResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationProjectsDataSourceMockData), nil
+ },
+ )
+}
+
+func TestDataSourceTransformationProjectsMappingMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ data "fivetran_transformation_projects" "test_projects" {
+ provider = fivetran-provider
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationProjectsDataSourceMockGetHandler.Interactions, 1)
+ return nil
+ },
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.0.id", "string"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.0.group_id", "string"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.0.created_at", "created_at"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.0.created_by_id", "string"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.0.type", "DBT_GIT"),
+
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.1.id", "string2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.1.group_id", "string2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.1.created_at", "created_at_2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.1.created_by_id", "string2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation_projects.test_projects", "projects.1.type", "DBT_GIT"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientTransformationProjectsDataSourceConfigMapping(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/datasources/transformation_test.go b/fivetran/framework/datasources/transformation_test.go
new file mode 100644
index 00000000..6a6de39f
--- /dev/null
+++ b/fivetran/framework/datasources/transformation_test.go
@@ -0,0 +1,147 @@
+package datasources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ transformationDataSourceMockGetHandler *mock.Handler
+ transformationDataSourceMockData map[string]interface{}
+)
+
+func setupMockClienttransformationDataSourceMappingTest(t *testing.T) {
+ transformationResponse := `
+{
+ "id": "transformation_id",
+ "status": "status",
+ "schedule": {
+ "cron": [
+ "cron1",
+ "cron2"
+ ],
+ "interval": 60,
+ "smart_syncing": true,
+ "connection_ids": [
+ "connection_id1",
+ "connection_id2"
+ ],
+ "schedule_type": "schedule_type",
+ "days_of_week": [
+ "days_of_week1",
+ "days_of_week2"
+ ],
+ "time_of_day": "time_of_day"
+ },
+ "type": "type",
+ "paused": true,
+ "created_at": "created_at",
+ "output_model_names": [
+ "output_model_name1",
+ "output_model_name2"
+ ],
+ "created_by_id": "created_by_id",
+ "transformation_config": {
+ "project_id": "project_id",
+ "name": "name",
+ "steps": [
+ {
+ "name": "name1",
+ "command": "command1"
+ },
+ {
+ "name": "name2",
+ "command": "command2"
+ }
+ ],
+ "package_name": "package_name",
+ "connection_ids": [
+ "connection_id1",
+ "connection_id2"
+ ],
+ "excluded_models": [
+ "excluded_model1",
+ "excluded_model2"
+ ],
+ "upgrade_available": true
+ }
+ }`
+ tfmock.MockClient().Reset()
+
+ transformationDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ transformationDataSourceMockData = tfmock.CreateMapFromJsonString(t, transformationResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationDataSourceMockData), nil
+ },
+ )
+}
+
+func TestDataSourcetransformationMappingMock(t *testing.T) {
+ // NOTE: the config is totally inconsistent and contains all possible values for mapping test
+ step1 := resource.TestStep{
+ Config: `
+ data "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+ id = "transformation_id"
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationDataSourceMockGetHandler.Interactions, 1)
+ tfmock.AssertNotEmpty(t, transformationDataSourceMockData)
+ return nil
+ },
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "id", "transformation_id"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "status", "status"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "type", "type"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "paused", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "output_model_names.0", "output_model_name1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "output_model_names.1", "output_model_name2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.project_id", "project_id"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.name", "name"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.steps.0.name", "name1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.steps.0.command", "command1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.steps.1.name", "name2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.steps.1.command", "command2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.package_name", "package_name"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.connection_ids.1", "connection_id2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.excluded_models.0", "excluded_model1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.excluded_models.1", "excluded_model2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "transformation_config.upgrade_available", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.interval", "60"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.schedule_type", "schedule_type"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.cron.0", "cron1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.cron.1", "cron2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.connection_ids.1", "connection_id2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.days_of_week.0", "days_of_week1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.days_of_week.1", "days_of_week2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformation.transformation", "schedule.time_of_day", "time_of_day"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClienttransformationDataSourceMappingTest(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/datasources/transformations.go b/fivetran/framework/datasources/transformations.go
new file mode 100644
index 00000000..5927f56a
--- /dev/null
+++ b/fivetran/framework/datasources/transformations.go
@@ -0,0 +1,83 @@
+package datasources
+
+import (
+ "context"
+ "fmt"
+
+ sdk "github.com/fivetran/go-fivetran/transformations"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+)
+
+func Transformations() datasource.DataSource {
+ return &transformations{}
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ datasource.DataSourceWithConfigure = &transformations{}
+
+type transformations struct {
+ core.ProviderDatasource
+}
+
+func (d *transformations) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = "fivetran_transformations"
+}
+
+func (d *transformations) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = fivetranSchema.TransformationListDatasource()
+}
+
+func (d *transformations) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ if d.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.Transformations
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ var respNextCursor string
+ var listResponse sdk.TransformationsListResponse
+ limit := 1000
+
+ for {
+ var err error
+ var tmpResp sdk.TransformationsListResponse
+ svc := d.GetClient().NewTransformationsList()
+
+ if respNextCursor == "" {
+ tmpResp, err = svc.Limit(limit).Do(ctx)
+ }
+
+ if respNextCursor != "" {
+ tmpResp, err = svc.Limit(limit).Cursor(respNextCursor).Do(ctx)
+ }
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Read error.",
+ fmt.Sprintf("%v; code: %v", err, tmpResp.Code),
+ )
+ listResponse = sdk.TransformationsListResponse{}
+ }
+
+ listResponse.Data.Items = append(listResponse.Data.Items, tmpResp.Data.Items...)
+
+ if tmpResp.Data.NextCursor == "" {
+ break
+ }
+
+ respNextCursor = tmpResp.Data.NextCursor
+ }
+
+ data.ReadFromResponse(ctx, listResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/fivetran/framework/datasources/transformations_test.go b/fivetran/framework/datasources/transformations_test.go
new file mode 100644
index 00000000..8f2780f3
--- /dev/null
+++ b/fivetran/framework/datasources/transformations_test.go
@@ -0,0 +1,187 @@
+package datasources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ transformationsDataSourceMockGetHandler *mock.Handler
+ transformationsDataSourceMockData map[string]interface{}
+)
+
+const (
+ transformationsMappingResponse = `
+ {
+ "items": [
+ {
+ "id": "transformation_id1",
+ "status": "status1",
+ "schedule": {
+ "cron": [
+ "cron1"
+ ],
+ "interval": 601,
+ "smart_syncing": true,
+ "connection_ids": [
+ "connection_id1"
+ ],
+ "schedule_type": "schedule_type1",
+ "days_of_week": [
+ "days_of_week01",
+ "days_of_week11"
+ ],
+ "time_of_day": "time_of_day1"
+ },
+ "type": "type1",
+ "paused": true,
+ "created_at": "created_at1",
+ "output_model_names": [
+ "output_model_name1"
+ ],
+ "created_by_id": "created_by_id1",
+ "transformation_config": {
+ "project_id": "project_id1",
+ "name": "name1",
+ "steps": [
+ {
+ "name": "name01",
+ "command": "command01"
+ },
+ {
+ "name": "name02",
+ "command": "command02"
+ }
+ ]
+ }
+ },
+{
+ "id": "transformation_id2",
+ "status": "status2",
+ "schedule": {
+ "cron": [
+ "cron2"
+ ],
+ "interval": 602,
+ "smart_syncing": true,
+ "connection_ids": [
+ "connection_id2"
+ ],
+ "schedule_type": "schedule_type2",
+ "days_of_week": [
+ "days_of_week02",
+ "days_of_week12"
+ ],
+ "time_of_day": "time_of_day2"
+ },
+ "type": "type2",
+ "paused": true,
+ "created_at": "created_at2",
+ "output_model_names": [
+ "output_model_name2"
+ ],
+ "created_by_id": "created_by_id2",
+ "transformation_config": {
+ "package_name": "package_name2",
+ "connection_ids": [
+ "connection_id2"
+ ],
+ "excluded_models": [
+ "excluded_model2"
+ ],
+ "upgrade_available": true
+ }
+ }
+ ],
+ "next_cursor": null
+ }
+ `
+)
+
+func setupMockClienttransformationsDataSourceConfigMapping(t *testing.T) {
+ tfmock.MockClient().Reset()
+ transformationsDataSourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformations").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ transformationsDataSourceMockData = tfmock.CreateMapFromJsonString(t, transformationsMappingResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationsDataSourceMockData), nil
+ },
+ )
+}
+
+func TestDataSourcetransformationsMappingMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ data "fivetran_transformations" "transformation" {
+ provider = fivetran-provider
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationsDataSourceMockGetHandler.Interactions, 1)
+ return nil
+ },
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.id", "transformation_id1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.status", "status1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.created_at", "created_at1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.created_by_id", "created_by_id1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.type", "type1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.paused", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.output_model_names.0", "output_model_name1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.transformation_config.project_id", "project_id1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.transformation_config.name", "name1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.transformation_config.steps.0.name", "name01"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.transformation_config.steps.0.command", "command01"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.transformation_config.steps.1.name", "name02"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.transformation_config.steps.1.command", "command02"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.interval", "601"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.schedule_type", "schedule_type1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.cron.0", "cron1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.days_of_week.0", "days_of_week01"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.days_of_week.1", "days_of_week11"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.0.schedule.time_of_day", "time_of_day1"),
+
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.id", "transformation_id2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.status", "status2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.created_at", "created_at2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.created_by_id", "created_by_id2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.type", "type2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.paused", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.output_model_names.0", "output_model_name2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.transformation_config.package_name", "package_name2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.transformation_config.connection_ids.0", "connection_id2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.transformation_config.excluded_models.0", "excluded_model2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.transformation_config.upgrade_available", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.interval", "602"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.schedule_type", "schedule_type2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.cron.0", "cron2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.connection_ids.0", "connection_id2"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.days_of_week.0", "days_of_week02"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.days_of_week.1", "days_of_week12"),
+ resource.TestCheckResourceAttr("data.fivetran_transformations.transformation", "transformations.1.schedule.time_of_day", "time_of_day2"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClienttransformationsDataSourceConfigMapping(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/provider.go b/fivetran/framework/provider.go
index 9071dba7..2fc83d91 100644
--- a/fivetran/framework/provider.go
+++ b/fivetran/framework/provider.go
@@ -17,7 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
)
-const Version = "1.4.3" // Current provider version
+const Version = "1.5.0" // Current provider version
type fivetranProvider struct {
mockClient httputils.HttpClient
@@ -123,6 +123,8 @@ func (p *fivetranProvider) Resources(ctx context.Context) []func() resource.Reso
resources.HybridDeploymentAgent,
resources.DbtGitProjectConfig,
resources.PrivateLink,
+ resources.TransformationProject,
+ resources.Transformation,
}
}
@@ -169,5 +171,11 @@ func (p *fivetranProvider) DataSources(ctx context.Context) []func() datasource.
datasources.Connectors,
datasources.Destinations,
datasources.ExternalLogs,
+ datasources.QuickstartPackage,
+ datasources.QuickstartPackages,
+ datasources.TransformationProject,
+ datasources.TransformationProjects,
+ datasources.Transformation,
+ datasources.Transformations,
}
}
diff --git a/fivetran/framework/resources/transformation.go b/fivetran/framework/resources/transformation.go
new file mode 100644
index 00000000..55595812
--- /dev/null
+++ b/fivetran/framework/resources/transformation.go
@@ -0,0 +1,515 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fivetran/go-fivetran"
+ "github.com/fivetran/go-fivetran/transformations"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+)
+
+func Transformation() resource.Resource {
+ return &transformation{}
+}
+
+type transformation struct {
+ core.ProviderResource
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ resource.ResourceWithConfigure = &transformation{}
+var _ resource.ResourceWithImportState = &transformation{}
+
+func (r *transformation) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = "fivetran_transformation"
+}
+
+func (r *transformation) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = fivetranSchema.TransformationResource()
+}
+
+func (r *transformation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *transformation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.Transformation
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ transformationType := data.ProjectType.ValueString()
+ client := r.GetClient()
+ svc := client.NewTransformationCreate()
+ svc.ProjectType(transformationType)
+ svc.Paused(data.Paused.ValueBool())
+
+ if !data.Config.IsNull() && !data.Config.IsUnknown() {
+ config := fivetran.NewTransformationConfig()
+ configAttributes := data.Config.Attributes()
+ /* DBT_CORE */
+ if !configAttributes["project_id"].(basetypes.StringValue).IsNull() && !configAttributes["project_id"].(basetypes.StringValue).IsUnknown() {
+ if transformationType != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "transformation_config.project_id"),
+ )
+ return
+ }
+
+ config.ProjectId(configAttributes["project_id"].(basetypes.StringValue).ValueString())
+ }
+
+ if !configAttributes["name"].(basetypes.StringValue).IsNull() && !configAttributes["name"].(basetypes.StringValue).IsUnknown() {
+ if transformationType != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "transformation_config.name"),
+ )
+ return
+ }
+
+ config.Name(configAttributes["name"].(basetypes.StringValue).ValueString())
+ }
+
+ if !configAttributes["steps"].IsUnknown() && !configAttributes["steps"].IsNull() {
+ if transformationType != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "transformation_config.steps"),
+ )
+ return
+ }
+
+ evars := []transformations.TransformationStep{}
+
+ for _, ev := range configAttributes["steps"].(basetypes.ListValue).Elements() {
+ if element, ok := ev.(basetypes.ObjectValue); ok {
+ step := transformations.TransformationStep{}
+ step.Name = element.Attributes()["name"].(basetypes.StringValue).ValueString()
+ step.Command = element.Attributes()["command"].(basetypes.StringValue).ValueString()
+ evars = append(evars, step)
+ }
+ }
+
+ config.Steps(evars)
+ }
+
+ /* QUICKSTART */
+ packageName := ""
+ if !configAttributes["package_name"].(basetypes.StringValue).IsNull() && !configAttributes["package_name"].(basetypes.StringValue).IsUnknown() {
+ if transformationType != "QUICKSTART" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for QUICKSTART type transformation", "transformation_config.package_name"),
+ )
+ return
+ }
+
+ packageName = configAttributes["package_name"].(basetypes.StringValue).ValueString()
+
+ config.PackageName(packageName)
+ }
+
+ connectionIds := []string{}
+ if !configAttributes["connection_ids"].IsUnknown() && !configAttributes["connection_ids"].IsNull() {
+ if transformationType != "QUICKSTART" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for QUICKSTART type transformation", "transformation_config.connection_ids"),
+ )
+ return
+ }
+
+ for _, ev := range configAttributes["connection_ids"].(basetypes.SetValue).Elements() {
+ connectionIds = append(connectionIds, ev.(basetypes.StringValue).ValueString())
+ }
+
+
+ config.ConnectionIds(connectionIds)
+ }
+
+ if len(connectionIds) == 0 && packageName == "" && transformationType == "QUICKSTART" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("For a QUICKSTART type transformation, at least one of the `%v` or `%v` parameters must be set.", "transformation_config.package_name", "transformation_config.connection_ids"),
+ )
+ return
+ }
+
+ if !configAttributes["excluded_models"].IsUnknown() && !configAttributes["excluded_models"].IsNull() {
+ if transformationType != "QUICKSTART" {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for QUICKSTART type transformation", "transformation_config.excluded_models"),
+ )
+ return
+ }
+
+ evars := []string{}
+ for _, ev := range configAttributes["excluded_models"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ config.ExcludedModels(evars)
+ }
+
+ svc.TransformationConfig(config)
+ }
+
+ if !data.Schedule.IsNull() && !data.Schedule.IsUnknown() {
+ schedule := fivetran.NewTransformationSchedule()
+ scheduleAttributes := data.Schedule.Attributes()
+
+ if !scheduleAttributes["time_of_day"].IsNull() && !scheduleAttributes["time_of_day"].IsUnknown() {
+ schedule.TimeOfDay(scheduleAttributes["time_of_day"].(basetypes.StringValue).ValueString())
+ }
+ if !scheduleAttributes["schedule_type"].IsNull() && !scheduleAttributes["schedule_type"].IsUnknown() {
+ schedule.ScheduleType(scheduleAttributes["schedule_type"].(basetypes.StringValue).ValueString())
+ }
+ if !scheduleAttributes["interval"].IsNull() && !scheduleAttributes["interval"].IsUnknown() {
+ schedule.Interval(int(scheduleAttributes["interval"].(basetypes.Int64Value).ValueInt64()))
+ }
+ if !scheduleAttributes["smart_syncing"].IsNull() && !scheduleAttributes["smart_syncing"].IsUnknown() {
+ schedule.SmartSyncing(scheduleAttributes["smart_syncing"].(basetypes.BoolValue).ValueBool())
+ }
+
+ if !scheduleAttributes["connection_ids"].IsUnknown() && !scheduleAttributes["connection_ids"].IsNull() {
+ if transformationType != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "schedule.connection_ids"),
+ )
+ return
+ }
+
+ evars := []string{}
+ for _, ev := range scheduleAttributes["connection_ids"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ schedule.ConnectionIds(evars)
+ }
+
+ if !scheduleAttributes["days_of_week"].IsUnknown() && !scheduleAttributes["days_of_week"].IsNull() {
+ evars := []string{}
+ for _, ev := range scheduleAttributes["days_of_week"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ schedule.DaysOfWeek(evars)
+ }
+
+ if !scheduleAttributes["cron"].IsUnknown() && !scheduleAttributes["cron"].IsNull() {
+ evars := []string{}
+ for _, ev := range scheduleAttributes["cron"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ schedule.Cron(evars)
+ }
+
+ svc.TransformationSchedule(schedule)
+ }
+
+ createResponse, err := svc.Do(ctx)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, createResponse.Code, createResponse.Message),
+ )
+
+ return
+ }
+
+ data.ReadFromResponse(ctx, createResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ // Do cleanup on error
+ deleteResponse, err := client.NewTransformationDelete().TransformationId(createResponse.Data.Id).Do(ctx)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Cleanup Transformation Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, deleteResponse.Code, deleteResponse.Message),
+ )
+ }
+ }
+}
+
+func (r *transformation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.Transformation
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ readResponse, err := r.GetClient().NewTransformationDetails().TransformationId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Read Transformation Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, readResponse.Code, readResponse.Message),
+ )
+ return
+ }
+
+ data.ReadFromResponse(ctx, readResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *transformation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var state model.Transformation
+ var plan model.Transformation
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ svc := r.GetClient().NewTransformationUpdate().TransformationId(state.Id.ValueString())
+
+ hasChanges := false
+ pausedPlan := core.GetBoolOrDefault(plan.Paused, true)
+ pausedState := core.GetBoolOrDefault(state.Paused, true)
+
+ if pausedPlan != pausedState {
+ svc.Paused(pausedPlan)
+ hasChanges = true
+ }
+
+ if !plan.Config.IsNull() && !plan.Config.IsUnknown() && !plan.Config.Equal(state.Config) {
+ config := fivetran.NewTransformationConfig()
+ configPlanAttributes := plan.Config.Attributes()
+ configStateAttributes := state.Config.Attributes()
+
+ if !configPlanAttributes["name"].IsNull() &&
+ !configPlanAttributes["name"].IsUnknown() &&
+ !configStateAttributes["name"].(basetypes.StringValue).Equal(configPlanAttributes["name"].(basetypes.StringValue)) {
+ if state.ProjectType.ValueString() != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "transformation_config.name"),
+ )
+ return
+ }
+
+ hasChanges = true
+ config.Name(configPlanAttributes["name"].(basetypes.StringValue).ValueString())
+ }
+
+ if !configPlanAttributes["steps"].IsUnknown() &&
+ !configPlanAttributes["steps"].IsNull() &&
+ !configStateAttributes["steps"].(basetypes.ListValue).Equal(configPlanAttributes["steps"].(basetypes.ListValue)) {
+ if state.ProjectType.ValueString() != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "transformation_config.steps"),
+ )
+ return
+ }
+
+ evars := []transformations.TransformationStep{}
+ for _, ev := range configPlanAttributes["steps"].(basetypes.ListValue).Elements() {
+ if element, ok := ev.(basetypes.ObjectValue); ok {
+ var step transformations.TransformationStep
+ step.Name = element.Attributes()["name"].(basetypes.StringValue).ValueString()
+ step.Command = element.Attributes()["command"].(basetypes.StringValue).ValueString()
+ evars = append(evars, step)
+ }
+ }
+
+ hasChanges = true
+ config.Steps(evars)
+ }
+
+ if !configPlanAttributes["excluded_models"].IsUnknown() &&
+ !configPlanAttributes["excluded_models"].IsNull() &&
+ !configStateAttributes["excluded_models"].(basetypes.SetValue).Equal(configPlanAttributes["excluded_models"].(basetypes.SetValue)) {
+ if state.ProjectType.ValueString() != "QUICKSTART" {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for QUICKSTART type transformation", "transformation_config.excluded_models"),
+ )
+ return
+ }
+
+ evars := []string{}
+ for _, ev := range configPlanAttributes["excluded_models"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+
+ hasChanges = true
+ config.ExcludedModels(evars)
+ }
+
+ if hasChanges {
+ svc.TransformationConfig(config)
+ }
+ }
+
+ if !plan.Schedule.IsNull() && !plan.Schedule.IsUnknown() && !plan.Schedule.Equal(state.Schedule) {
+ schedule := fivetran.NewTransformationSchedule()
+ schedulePlanAttributes := plan.Schedule.Attributes()
+ scheduleStateAttributes := state.Schedule.Attributes()
+
+ if !schedulePlanAttributes["time_of_day"].IsNull() &&
+ !schedulePlanAttributes["time_of_day"].IsUnknown() &&
+ !scheduleStateAttributes["time_of_day"].(basetypes.StringValue).Equal(schedulePlanAttributes["time_of_day"].(basetypes.StringValue)) {
+ hasChanges = true
+ schedule.TimeOfDay(schedulePlanAttributes["time_of_day"].(basetypes.StringValue).ValueString())
+ }
+
+ if !schedulePlanAttributes["schedule_type"].IsNull() &&
+ !schedulePlanAttributes["schedule_type"].IsUnknown() &&
+ !scheduleStateAttributes["schedule_type"].(basetypes.StringValue).Equal(schedulePlanAttributes["schedule_type"].(basetypes.StringValue)) {
+ hasChanges = true
+ schedule.ScheduleType(schedulePlanAttributes["schedule_type"].(basetypes.StringValue).ValueString())
+ }
+
+ if !schedulePlanAttributes["interval"].IsNull() &&
+ !schedulePlanAttributes["interval"].IsUnknown() &&
+ !scheduleStateAttributes["interval"].(basetypes.Int64Value).Equal(schedulePlanAttributes["interval"].(basetypes.Int64Value)) {
+ hasChanges = true
+ schedule.Interval(int(schedulePlanAttributes["interval"].(basetypes.Int64Value).ValueInt64()))
+ }
+
+ if !schedulePlanAttributes["smart_syncing"].IsNull() &&
+ !schedulePlanAttributes["smart_syncing"].IsUnknown() &&
+ !scheduleStateAttributes["smart_syncing"].(basetypes.BoolValue).Equal(schedulePlanAttributes["smart_syncing"].(basetypes.BoolValue)) {
+ hasChanges = true
+ schedule.SmartSyncing(schedulePlanAttributes["smart_syncing"].(basetypes.BoolValue).ValueBool())
+ }
+
+ if !schedulePlanAttributes["connection_ids"].IsUnknown() &&
+ !schedulePlanAttributes["connection_ids"].IsNull() &&
+ !scheduleStateAttributes["connection_ids"].(basetypes.SetValue).Equal(schedulePlanAttributes["connection_ids"].(basetypes.SetValue)) {
+ if plan.ProjectType.ValueString() != "DBT_CORE" {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Resource.",
+ fmt.Sprintf("The parameter `%v` can be set only for DBT_CORE type transformation", "schedule.connection_ids"),
+ )
+ return
+ }
+
+ evars := []string{}
+ for _, ev := range schedulePlanAttributes["connection_ids"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ hasChanges = true
+ schedule.ConnectionIds(evars)
+ }
+
+ if !schedulePlanAttributes["days_of_week"].IsUnknown() &&
+ !schedulePlanAttributes["days_of_week"].IsNull() &&
+ !scheduleStateAttributes["days_of_week"].(basetypes.SetValue).Equal(schedulePlanAttributes["days_of_week"].(basetypes.SetValue)) {
+ evars := []string{}
+ for _, ev := range schedulePlanAttributes["days_of_week"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ hasChanges = true
+ schedule.DaysOfWeek(evars)
+ }
+
+ if !schedulePlanAttributes["cron"].IsUnknown() &&
+ !schedulePlanAttributes["cron"].IsNull() &&
+ !scheduleStateAttributes["cron"].(basetypes.SetValue).Equal(schedulePlanAttributes["cron"].(basetypes.SetValue)) {
+ evars := []string{}
+ for _, ev := range schedulePlanAttributes["cron"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ hasChanges = true
+ schedule.Cron(evars)
+ }
+
+ if hasChanges {
+ svc.TransformationSchedule(schedule)
+ }
+ }
+
+ if hasChanges {
+ updateResponse, err := svc.Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, updateResponse.Code, updateResponse.Message),
+ )
+ return
+ }
+
+ plan.ReadFromResponse(ctx, updateResponse)
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+}
+
+func (r *transformation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.Transformation
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ deleteResponse, err := r.GetClient().NewTransformationDelete().TransformationId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Delete transformation Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, deleteResponse.Code, deleteResponse.Message),
+ )
+ return
+ }
+}
diff --git a/fivetran/framework/resources/transformation_project.go b/fivetran/framework/resources/transformation_project.go
new file mode 100644
index 00000000..817f165a
--- /dev/null
+++ b/fivetran/framework/resources/transformation_project.go
@@ -0,0 +1,293 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fivetran/go-fivetran"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core"
+ "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/model"
+ fivetranSchema "github.com/fivetran/terraform-provider-fivetran/fivetran/framework/core/schema"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+)
+
+func TransformationProject() resource.Resource {
+ return &transformationProject{}
+}
+
+type transformationProject struct {
+ core.ProviderResource
+}
+
+// Ensure the implementation satisfies the desired interfaces.
+var _ resource.ResourceWithConfigure = &transformationProject{}
+var _ resource.ResourceWithImportState = &transformationProject{}
+
+func (r *transformationProject) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = "fivetran_transformation_project"
+}
+
+func (r *transformationProject) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = fivetranSchema.TransformationProjectResource(ctx)
+}
+
+func (r *transformationProject) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *transformationProject) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.TransformationResourceProject
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ client := r.GetClient()
+ svc := client.NewTransformationProjectCreate()
+
+ svc.GroupId(data.GroupId.ValueString())
+ svc.ProjectType(data.Type.ValueString())
+ svc.RunTests(data.RunTests.ValueBool())
+
+ if !data.ProjectConfig.IsNull() && !data.ProjectConfig.IsUnknown() {
+ projectConfig := fivetran.NewTransformationProjectConfig()
+ projectConfigAttributes := data.ProjectConfig.Attributes()
+
+ if !projectConfigAttributes["dbt_version"].IsNull() && !projectConfigAttributes["dbt_version"].IsUnknown() {
+ projectConfig.DbtVersion(projectConfigAttributes["dbt_version"].(basetypes.StringValue).ValueString())
+ }
+
+ if !projectConfigAttributes["default_schema"].IsNull() && !projectConfigAttributes["default_schema"].IsUnknown() {
+ projectConfig.DefaultSchema(projectConfigAttributes["default_schema"].(basetypes.StringValue).ValueString())
+ }
+
+ if !projectConfigAttributes["git_remote_url"].IsNull() && !projectConfigAttributes["git_remote_url"].IsUnknown() {
+ projectConfig.GitRemoteUrl(projectConfigAttributes["git_remote_url"].(basetypes.StringValue).ValueString())
+ }
+
+ if !projectConfigAttributes["folder_path"].IsNull() && !projectConfigAttributes["folder_path"].IsUnknown() {
+ projectConfig.FolderPath(projectConfigAttributes["folder_path"].(basetypes.StringValue).ValueString())
+ }
+
+ if !projectConfigAttributes["git_branch"].IsNull() && !projectConfigAttributes["git_branch"].IsUnknown() {
+ projectConfig.GitBranch(projectConfigAttributes["git_branch"].(basetypes.StringValue).ValueString())
+ }
+
+ if !projectConfigAttributes["target_name"].IsNull() && !projectConfigAttributes["target_name"].IsUnknown() {
+ projectConfig.TargetName(projectConfigAttributes["target_name"].(basetypes.StringValue).ValueString())
+ }
+
+ if !projectConfigAttributes["threads"].IsNull() && !projectConfigAttributes["threads"].IsUnknown() {
+ projectConfig.Threads(int(projectConfigAttributes["threads"].(basetypes.Int64Value).ValueInt64()))
+ }
+
+ if !projectConfigAttributes["environment_vars"].IsUnknown() && !projectConfigAttributes["environment_vars"].IsNull() {
+ evars := []string{}
+ for _, ev := range projectConfigAttributes["environment_vars"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ projectConfig.EnvironmentVars(evars)
+ }
+
+ svc.ProjectConfig(projectConfig)
+ }
+
+ projectResponse, err := svc.Do(ctx)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Create Transformation Project Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, projectResponse.Code, projectResponse.Message),
+ )
+ return
+ }
+
+ data.ReadFromResponse(ctx, projectResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ // Do cleanup on error
+ deleteResponse, err := client.NewTransformationProjectDelete().ProjectId(projectResponse.Data.Id).Do(ctx)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Cleanup Transformation Project Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, deleteResponse.Code, deleteResponse.Message),
+ )
+ }
+ }
+}
+
+func (r *transformationProject) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.TransformationResourceProject
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ projectResponse, err := r.GetClient().NewTransformationProjectDetails().ProjectId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Read Transformation Project Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, projectResponse.Code, projectResponse.Message),
+ )
+ return
+ }
+
+ data.ReadFromResponse(ctx, projectResponse)
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *transformationProject) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var state model.TransformationResourceProject
+ var plan model.TransformationResourceProject
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ svc := r.GetClient().NewTransformationProjectUpdate()
+ svc.ProjectId(state.Id.ValueString())
+ hasChanges := false
+ runTestsPlan := core.GetBoolOrDefault(plan.RunTests, true)
+ runTestsState := core.GetBoolOrDefault(state.RunTests, true)
+
+ if runTestsPlan != runTestsState {
+ hasChanges = true
+ svc.RunTests(runTestsPlan)
+ }
+
+ if !plan.ProjectConfig.IsUnknown() && !state.ProjectConfig.Equal(plan.ProjectConfig) {
+ projectConfig := fivetran.NewTransformationProjectConfig()
+ configPlanAttributes := plan.ProjectConfig.Attributes()
+ configStateAttributes := state.ProjectConfig.Attributes()
+
+ if !configPlanAttributes["folder_path"].IsNull() &&
+ !configPlanAttributes["folder_path"].IsUnknown() &&
+ !configStateAttributes["folder_path"].(basetypes.StringValue).Equal(configPlanAttributes["folder_path"].(basetypes.StringValue)) {
+ hasChanges = true
+ projectConfig.FolderPath(configPlanAttributes["folder_path"].(basetypes.StringValue).ValueString())
+ }
+
+ if !configPlanAttributes["git_branch"].IsNull() &&
+ !configPlanAttributes["git_branch"].IsUnknown() &&
+ !configStateAttributes["git_branch"].(basetypes.StringValue).Equal(configPlanAttributes["git_branch"].(basetypes.StringValue)) {
+ hasChanges = true
+ projectConfig.GitBranch(configPlanAttributes["git_branch"].(basetypes.StringValue).ValueString())
+ }
+
+ if !configPlanAttributes["target_name"].IsNull() &&
+ !configPlanAttributes["target_name"].IsUnknown() &&
+ !configStateAttributes["target_name"].(basetypes.StringValue).Equal(configPlanAttributes["target_name"].(basetypes.StringValue)) {
+ hasChanges = true
+ projectConfig.TargetName(configPlanAttributes["target_name"].(basetypes.StringValue).ValueString())
+ }
+
+ if !configPlanAttributes["threads"].IsNull() &&
+ !configPlanAttributes["threads"].IsUnknown() &&
+ !configStateAttributes["threads"].(basetypes.Int64Value).Equal(configPlanAttributes["threads"].(basetypes.Int64Value)) {
+ hasChanges = true
+ projectConfig.Threads(int(configPlanAttributes["threads"].(basetypes.Int64Value).ValueInt64()))
+ }
+
+ if !configPlanAttributes["environment_vars"].IsNull() &&
+ !configPlanAttributes["environment_vars"].IsUnknown() &&
+ !configStateAttributes["environment_vars"].(basetypes.SetValue).Equal(configPlanAttributes["environment_vars"].(basetypes.SetValue)) {
+ evars := []string{}
+ for _, ev := range configPlanAttributes["environment_vars"].(basetypes.SetValue).Elements() {
+ evars = append(evars, ev.(basetypes.StringValue).ValueString())
+ }
+ hasChanges = true
+ projectConfig.EnvironmentVars(evars)
+ }
+
+ if hasChanges {
+ svc.ProjectConfig(projectConfig)
+ }
+ }
+
+ if hasChanges {
+ projectResponse, err := svc.Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Update Transformation Project Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, projectResponse.Code, projectResponse.Message),
+ )
+ return
+ }
+
+ plan.ReadFromResponse(ctx, projectResponse)
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+}
+
+func (r *transformationProject) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ if r.GetClient() == nil {
+ resp.Diagnostics.AddError(
+ "Unconfigured Fivetran Client",
+ "Please report this issue to the provider developers.",
+ )
+
+ return
+ }
+
+ var data model.TransformationResourceProject
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ deleteResponse, err := r.GetClient().NewTransformationProjectDelete().ProjectId(data.Id.ValueString()).Do(ctx)
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Delete transformation Project Resource.",
+ fmt.Sprintf("%v; code: %v; message: %v", err, deleteResponse.Code, deleteResponse.Message),
+ )
+ return
+ }
+}
diff --git a/fivetran/framework/resources/transformation_project_test.go b/fivetran/framework/resources/transformation_project_test.go
new file mode 100644
index 00000000..e704f297
--- /dev/null
+++ b/fivetran/framework/resources/transformation_project_test.go
@@ -0,0 +1,250 @@
+package resources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ transformationProjectResourceMockGetHandler *mock.Handler
+ transformationProjectResourceMockPostHandler *mock.Handler
+ transformationProjectResourceMockPatchHandler *mock.Handler
+ transformationProjectResourceMockDeleteHandler *mock.Handler
+
+ transformationProjectResourceMockData map[string]interface{}
+)
+
+func setupMockClientTransformationProjectResourceMappingTest(t *testing.T) {
+ transformationProjectResponse := `
+{
+ "id": "project_id",
+ "type": "DBT_GIT",
+ "status": "NOT_READY",
+ "errors": [
+ "string"
+ ],
+ "created_at": "created_at",
+ "group_id": "group_id",
+ "setup_tests": [
+ {
+ "title": "Test Title",
+ "status": "FAILED",
+ "message": "Error message",
+ "details": "Error details"
+ }
+ ],
+ "created_by_id": "created_by_id",
+ "project_config": {
+ "dbt_version": "dbt_version",
+ "default_schema": "default_schema",
+ "git_remote_url": "git_remote_url",
+ "git_branch": "git_branch",
+ "threads": 0,
+ "target_name": "target_name",
+ "environment_vars": [
+ "environment_var"
+ ],
+ "public_key": "public_key"
+ }
+ }`
+
+ transformationProjectPatchedResponse := `
+{
+ "id": "project_id",
+ "type": "DBT_GIT",
+ "status": "NOT_READY",
+ "errors": [
+ "string"
+ ],
+ "created_at": "created_at",
+ "group_id": "group_id",
+ "setup_tests": [
+ {
+ "title": "Test Title",
+ "status": "FAILED",
+ "message": "Error message",
+ "details": "Error details"
+ }
+ ],
+ "created_by_id": "created_by_id",
+ "project_config": {
+ "dbt_version": "dbt_version",
+ "default_schema": "default_schema",
+ "git_remote_url": "git_remote_url",
+ "folder_path": "folder_path",
+ "git_branch": "git_branch1",
+ "threads": 1,
+ "target_name": "target_name1",
+ "environment_vars": [
+ "environment_var1"
+ ],
+ "public_key": "public_key"
+ }
+ }`
+
+ tfmock.MockClient().Reset()
+
+ transformationProjectResourceMockGetHandler = tfmock.MockClient().When(http.MethodGet, "/v1/transformation-projects/project_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationProjectResourceMockData), nil
+ },
+ )
+
+ transformationProjectResourceMockPostHandler = tfmock.MockClient().When(http.MethodPost, "/v1/transformation-projects").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ body := tfmock.RequestBodyToJson(t, req)
+ tfmock.AssertKeyExistsAndHasValue(t, body, "group_id", "group_id")
+ tfmock.AssertKeyExistsAndHasValue(t, body, "type", "DBT_GIT")
+
+ tfmock.AssertKeyExists(t, body, "project_config")
+ config := body["project_config"].(map[string]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, config, "git_remote_url", "git_remote_url")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "git_branch", "git_branch")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "dbt_version", "dbt_version")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "default_schema", "default_schema")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "target_name", "target_name")
+
+ transformationProjectResourceMockData = tfmock.CreateMapFromJsonString(t, transformationProjectResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusCreated, "Success", transformationProjectResourceMockData), nil
+ },
+ )
+
+ transformationProjectResourceMockPatchHandler = tfmock.MockClient().When(http.MethodPatch, "/v1/transformation-projects/project_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ body := tfmock.RequestBodyToJson(t, req)
+
+ tfmock.AssertKeyDoesNotExist(t, body, "group_id")
+ tfmock.AssertKeyDoesNotExist(t, body, "type")
+
+ tfmock.AssertKeyExists(t, body, "project_config")
+ config := body["project_config"].(map[string]interface{})
+ tfmock.AssertKeyDoesNotExist(t, config, "git_remote_url")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "git_branch", "git_branch1")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "folder_path", "folder_path")
+ tfmock.AssertKeyDoesNotExist(t, config, "dbt_version")
+ tfmock.AssertKeyDoesNotExist(t, config, "default_schema")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "target_name", "target_name1")
+
+ transformationProjectResourceMockData = tfmock.CreateMapFromJsonString(t, transformationProjectPatchedResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationProjectResourceMockData), nil
+ },
+ )
+
+ transformationProjectResourceMockDeleteHandler = tfmock.MockClient().When(http.MethodDelete,
+ "/v1/transformation-projects/project_id",
+ ).ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ transformationProjectResourceMockData = nil
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", nil), nil
+ },
+ )
+}
+
+func TestResourceTransformationProjectMappingMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 0
+ target_name = "target_name"
+ environment_vars = ["environment_var"]
+ }
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationProjectResourceMockPostHandler.Interactions, 1)
+ tfmock.AssertEqual(t, transformationProjectResourceMockGetHandler.Interactions, 0)
+ tfmock.AssertNotEmpty(t, transformationProjectResourceMockData)
+ return nil
+ },
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "id", "project_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "group_id", "group_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "type", "DBT_GIT"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.dbt_version", "dbt_version"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.public_key", "public_key"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.default_schema", "default_schema"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.target_name", "target_name"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.environment_vars.0", "environment_var"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.git_remote_url", "git_remote_url"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.git_branch", "git_branch"),
+ ),
+ }
+
+ step2 := resource.TestStep{
+ Config: `
+ resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch1"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 1
+ target_name = "target_name1"
+ environment_vars = ["environment_var1"]
+ }
+ }`,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationProjectResourceMockPostHandler.Interactions, 1)
+ tfmock.AssertEqual(t, transformationProjectResourceMockPatchHandler.Interactions, 1)
+ tfmock.AssertEqual(t, transformationProjectResourceMockGetHandler.Interactions, 2)
+ tfmock.AssertNotEmpty(t, transformationProjectResourceMockData)
+ return nil
+ },
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "id", "project_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "group_id", "group_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "type", "DBT_GIT"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.dbt_version", "dbt_version"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.public_key", "public_key"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.default_schema", "default_schema"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.target_name", "target_name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.environment_vars.0", "environment_var1"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.git_remote_url", "git_remote_url"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.git_branch", "git_branch1"),
+ resource.TestCheckResourceAttr("fivetran_transformation_project.project", "project_config.folder_path", "folder_path"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientTransformationProjectResourceMappingTest(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ return nil
+ },
+ Steps: []resource.TestStep{
+ step1,
+ step2,
+ },
+ },
+ )
+}
diff --git a/fivetran/framework/resources/transformation_test.go b/fivetran/framework/resources/transformation_test.go
new file mode 100644
index 00000000..13f83f3b
--- /dev/null
+++ b/fivetran/framework/resources/transformation_test.go
@@ -0,0 +1,642 @@
+package resources_test
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/fivetran/go-fivetran/tests/mock"
+ tfmock "github.com/fivetran/terraform-provider-fivetran/fivetran/tests/mock"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+var (
+ transformationGitPostHandler *mock.Handler
+ transformationGitPatchHandler *mock.Handler
+ transformationQuickstartPatchHandler *mock.Handler
+ transformationQuickstartPostHandler *mock.Handler
+ transformationGitData map[string]interface{}
+ transformationQuickstartData map[string]interface{}
+
+ transformationGitDeleteHandler *mock.Handler
+ transformationQuickstartDeleteHandler *mock.Handler
+
+ gitResponse = `{
+ "id": "transformation_id",
+ "status": "status",
+ "schedule": {
+ "cron": [
+ "cron1","cron2"
+ ],
+ "interval": 601,
+ "smart_syncing": true,
+ "connection_ids": [
+ "connection_id1",
+ "connection_id2"
+ ],
+ "schedule_type": "schedule_type1",
+ "days_of_week": [
+ "days_of_week1",
+ "days_of_week2"
+ ],
+ "time_of_day": "time_of_day1"
+ },
+ "type": "DBT_CORE",
+ "paused": true,
+ "created_at": "created_at",
+ "output_model_names": [
+ "output_model_name1",
+ "output_model_name2"
+ ],
+ "created_by_id": "created_by_id",
+ "transformation_config": {
+ "project_id": "project_id",
+ "name": "name",
+ "steps": [
+ {
+ "name": "name1",
+ "command": "command1"
+ },
+ {
+ "name": "name2",
+ "command": "command2"
+ }
+ ]
+ }
+ }`
+
+ gitPatchedResponse = `{
+ "id": "transformation_id",
+ "status": "status",
+ "schedule": {
+ "cron": [
+ "cron1","cron2"
+ ],
+ "interval": 601,
+ "smart_syncing": true,
+ "connection_ids": [
+ "connection_id1",
+ "connection_id2"
+ ],
+ "schedule_type": "schedule_type1",
+ "days_of_week": [
+ "days_of_week1",
+ "days_of_week2"
+ ],
+ "time_of_day": "time_of_day1"
+ },
+ "type": "DBT_CORE",
+ "paused": true,
+ "created_at": "created_at",
+ "output_model_names": [
+ "output_model_name1",
+ "output_model_name2"
+ ],
+ "created_by_id": "created_by_id",
+ "transformation_config": {
+ "project_id": "project_id",
+ "name": "name2",
+ "steps": [
+ {
+ "name": "name1",
+ "command": "command1"
+ },
+ {
+ "name": "name3",
+ "command": "command3"
+ }
+ ]
+ }
+ }`
+
+ quickstartResponse = `{
+ "id": "transformation_id",
+ "status": "status",
+ "schedule": {
+ "cron": [
+ "cron1","cron2"
+ ],
+ "interval": 601,
+ "smart_syncing": true,
+ "schedule_type": "schedule_type1",
+ "days_of_week": [
+ "days_of_week1",
+ "days_of_week2"
+ ],
+ "time_of_day": "time_of_day1"
+ },
+ "type": "QUICKSTART",
+ "paused": true,
+ "created_at": "created_at",
+ "output_model_names": [
+ "output_model_name1",
+ "output_model_name2"
+ ],
+ "created_by_id": "created_by_id",
+ "transformation_config": {
+ "package_name": "package_name",
+ "connection_ids": [
+ "connection_id1",
+ "connection_id2"
+ ],
+ "excluded_models": [
+ "excluded_model1","excluded_model2"
+ ],
+ "upgrade_available": true
+ }
+ }`
+
+ quickstartPatchedResponse = `{
+ "id": "transformation_id",
+ "status": "status",
+ "schedule": {
+ "cron": [
+ "cron1","cron2"
+ ],
+ "smart_syncing": true,
+ "schedule_type": "schedule_type1",
+ "days_of_week": [
+ "days_of_week1",
+ "days_of_week2"
+ ],
+ "time_of_day": "14:00"
+ },
+ "type": "QUICKSTART",
+ "paused": true,
+ "created_at": "created_at",
+ "output_model_names": [
+ "output_model_name1",
+ "output_model_name2"
+ ],
+ "created_by_id": "created_by_id",
+ "transformation_config": {
+ "package_name": "package_name",
+ "connection_ids": [
+ "connection_id1",
+ "connection_id2"
+ ],
+ "excluded_models": [
+ "excluded_model1","excluded_model2"
+ ],
+ "upgrade_available": true
+ }
+ }`
+)
+
+func setupMockClientTransformationGitResource(t *testing.T) {
+ tfmock.MockClient().Reset()
+
+ transformationGitPostHandler = tfmock.MockClient().When(http.MethodPost, "/v1/transformations").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ body := tfmock.RequestBodyToJson(t, req)
+ tfmock.AssertKeyExistsAndHasValue(t, body, "type", "DBT_CORE")
+ tfmock.AssertKeyExistsAndHasValue(t, body, "paused", true)
+
+ tfmock.AssertKeyExists(t, body, "transformation_config")
+ config := body["transformation_config"].(map[string]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, config, "project_id", "project_id")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "name", "name")
+
+ steps := config["steps"].([]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, steps[0].(map[string]interface{}), "name", "name1")
+ tfmock.AssertKeyExistsAndHasValue(t, steps[0].(map[string]interface{}), "command", "command1")
+ tfmock.AssertKeyExistsAndHasValue(t, steps[1].(map[string]interface{}), "name", "name2")
+ tfmock.AssertKeyExistsAndHasValue(t, steps[1].(map[string]interface{}), "command", "command2")
+
+ tfmock.AssertKeyExists(t, body, "schedule")
+ schedule := body["schedule"].(map[string]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "interval", float64(601))
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "smart_syncing", true)
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "schedule_type", "schedule_type1")
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "time_of_day", "time_of_day1")
+
+ cron := schedule["cron"].([]interface{})
+ tfmock.AssertEqual(t, len(cron), 2)
+ tfmock.AssertEqual(t, cron[0], "cron1")
+ tfmock.AssertEqual(t, cron[1], "cron2")
+
+ connectionIds := schedule["connection_ids"].([]interface{})
+ tfmock.AssertEqual(t, len(connectionIds), 2)
+ tfmock.AssertEqual(t, connectionIds[0], "connection_id1")
+ tfmock.AssertEqual(t, connectionIds[1], "connection_id2")
+
+ daysOfWeek := schedule["days_of_week"].([]interface{})
+ tfmock.AssertEqual(t, len(daysOfWeek), 2)
+ tfmock.AssertEqual(t, daysOfWeek[0], "days_of_week1")
+ tfmock.AssertEqual(t, daysOfWeek[1], "days_of_week2")
+
+ transformationGitData = tfmock.CreateMapFromJsonString(t, gitResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusCreated, "Success", transformationGitData), nil
+ },
+ )
+
+ transformationGitPatchHandler = tfmock.MockClient().When(http.MethodPatch, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ body := tfmock.RequestBodyToJson(t, req)
+ tfmock.AssertKeyDoesNotExist(t, body, "type")
+ tfmock.AssertKeyDoesNotExist(t, body, "paused")
+
+ tfmock.AssertKeyExists(t, body, "transformation_config")
+ config := body["transformation_config"].(map[string]interface{})
+ tfmock.AssertKeyDoesNotExist(t, config, "project_id")
+ tfmock.AssertKeyExistsAndHasValue(t, config, "name", "name2")
+ steps := config["steps"].([]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, steps[0].(map[string]interface{}), "name", "name1")
+ tfmock.AssertKeyExistsAndHasValue(t, steps[0].(map[string]interface{}), "command", "command1")
+ tfmock.AssertKeyExistsAndHasValue(t, steps[1].(map[string]interface{}), "name", "name3")
+ tfmock.AssertKeyExistsAndHasValue(t, steps[1].(map[string]interface{}), "command", "command3")
+
+ tfmock.AssertKeyDoesNotExist(t, body, "schedule")
+
+ transformationGitData = tfmock.CreateMapFromJsonString(t, gitPatchedResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationGitData), nil
+ },
+ )
+
+ tfmock.MockClient().When(http.MethodGet, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ tfmock.AssertNotEmpty(t, transformationGitData)
+ response := tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "", transformationGitData)
+ return response, nil
+ },
+ )
+
+ transformationGitDeleteHandler = tfmock.MockClient().When(http.MethodDelete, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ tfmock.AssertNotEmpty(t, transformationGitData)
+ transformationGitData = nil
+ response := tfmock.FivetranSuccessResponse(t, req, 200, "", nil)
+ return response, nil
+ },
+ )
+}
+
+func setupMockClientTransformationQuickstartResource(t *testing.T) {
+ tfmock.MockClient().Reset()
+
+ transformationQuickstartPostHandler = tfmock.MockClient().When(http.MethodPost, "/v1/transformations").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ body := tfmock.RequestBodyToJson(t, req)
+ tfmock.AssertKeyExistsAndHasValue(t, body, "type", "QUICKSTART")
+ tfmock.AssertKeyExistsAndHasValue(t, body, "paused", true)
+
+ tfmock.AssertKeyExists(t, body, "transformation_config")
+ config := body["transformation_config"].(map[string]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, config, "package_name", "package_name")
+ connectionIds := config["connection_ids"].([]interface{})
+ tfmock.AssertEqual(t, len(connectionIds), 2)
+ tfmock.AssertEqual(t, connectionIds[0], "connection_id1")
+ tfmock.AssertEqual(t, connectionIds[1], "connection_id2")
+ excludedModels := config["excluded_models"].([]interface{})
+ tfmock.AssertEqual(t, len(excludedModels), 2)
+ tfmock.AssertEqual(t, excludedModels[0], "excluded_model1")
+ tfmock.AssertEqual(t, excludedModels[1], "excluded_model2")
+
+ tfmock.AssertKeyExists(t, body, "schedule")
+ schedule := body["schedule"].(map[string]interface{})
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "interval", float64(601))
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "smart_syncing", true)
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "schedule_type", "schedule_type1")
+ tfmock.AssertKeyExistsAndHasValue(t, schedule, "time_of_day", "time_of_day1")
+
+ cron := schedule["cron"].([]interface{})
+ tfmock.AssertEqual(t, len(cron), 2)
+ tfmock.AssertEqual(t, cron[0], "cron1")
+ tfmock.AssertEqual(t, cron[1], "cron2")
+
+ daysOfWeek := schedule["days_of_week"].([]interface{})
+ tfmock.AssertEqual(t, len(daysOfWeek), 2)
+ tfmock.AssertEqual(t, daysOfWeek[0], "days_of_week1")
+ tfmock.AssertEqual(t, daysOfWeek[1], "days_of_week2")
+
+ transformationQuickstartData = tfmock.CreateMapFromJsonString(t, quickstartResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusCreated, "Success", transformationQuickstartData), nil
+ },
+ )
+
+ transformationQuickstartPatchHandler = tfmock.MockClient().When(http.MethodPatch, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ body := tfmock.RequestBodyToJson(t, req)
+ tfmock.AssertKeyDoesNotExist(t, body, "type")
+ tfmock.AssertKeyDoesNotExist(t, body, "paused")
+
+ tfmock.AssertKeyDoesNotExist(t, body, "transformation_config")
+
+ tfmock.AssertKeyExists(t, body, "schedule")
+ schedule := body["schedule"].(map[string]interface{})
+ tfmock.AssertKeyDoesNotExist(t, schedule, "interval")
+ tfmock.AssertKeyDoesNotExist(t, schedule, "smart_syncing")
+ tfmock.AssertKeyDoesNotExist(t, schedule, "schedule_type")
+ tfmock.AssertKeyExists(t, schedule, "time_of_day")
+ tfmock.AssertKeyDoesNotExist(t, schedule, "cron")
+ tfmock.AssertKeyDoesNotExist(t, schedule, "days_of_week")
+
+ transformationQuickstartData = tfmock.CreateMapFromJsonString(t, quickstartPatchedResponse)
+ return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", transformationQuickstartData), nil
+ },
+ )
+
+ tfmock.MockClient().When(http.MethodGet, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ tfmock.AssertNotEmpty(t, transformationQuickstartData)
+ response := tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "", transformationQuickstartData)
+ return response, nil
+ },
+ )
+
+ transformationQuickstartDeleteHandler = tfmock.MockClient().When(http.MethodDelete, "/v1/transformations/transformation_id").ThenCall(
+ func(req *http.Request) (*http.Response, error) {
+ tfmock.AssertNotEmpty(t, transformationQuickstartData)
+ transformationQuickstartData = nil
+ response := tfmock.FivetranSuccessResponse(t, req, 200, "", nil)
+ return response, nil
+ },
+ )
+}
+
+func TestResourceTransformationGitMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "DBT_CORE"
+ paused = true
+
+ schedule {
+ cron = ["cron1","cron2"]
+ interval = 601
+ smart_syncing = true
+ connection_ids = ["connection_id1", "connection_id2"]
+ schedule_type = "schedule_type1"
+ days_of_week = ["days_of_week1","days_of_week2"]
+ time_of_day = "time_of_day1"
+ }
+
+ transformation_config {
+ project_id = "project_id"
+ name = "name"
+ steps = [
+ {
+ name = "name1"
+ command = "command1"
+ },
+ {
+ name = "name2"
+ command = "command2"
+ }
+ ]
+ }
+ }
+ `,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationGitPostHandler.Interactions, 1)
+ return nil
+ },
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "id", "transformation_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "status", "status"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "type", "DBT_CORE"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "paused", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.0", "output_model_name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.1", "output_model_name2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.project_id", "project_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.name", "name"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.0.name", "name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.0.command", "command1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.1.name", "name2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.1.command", "command2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.interval", "601"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.schedule_type", "schedule_type1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.0", "cron1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.1", "cron2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.connection_ids.1", "connection_id2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.0", "days_of_week1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.1", "days_of_week2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.time_of_day", "time_of_day1"),
+ ),
+ }
+
+ step2 := resource.TestStep{
+ Config: `
+ resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "DBT_CORE"
+ paused = true
+
+ schedule {
+ cron = ["cron1","cron2"]
+ interval = 601
+ smart_syncing = true
+ connection_ids = ["connection_id1", "connection_id2"]
+ schedule_type = "schedule_type1"
+ days_of_week = ["days_of_week1","days_of_week2"]
+ time_of_day = "time_of_day1"
+ }
+
+ transformation_config {
+ project_id = "project_id"
+ name = "name2"
+ steps = [
+ {
+ name = "name1"
+ command = "command1"
+ },
+ {
+ name = "name3"
+ command = "command3"
+ }
+ ]
+ }
+ }
+ `,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationGitPostHandler.Interactions, 1)
+ return nil
+ },
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "id", "transformation_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "status", "status"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "type", "DBT_CORE"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "paused", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.0", "output_model_name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.1", "output_model_name2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.project_id", "project_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.name", "name2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.0.name", "name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.0.command", "command1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.1.name", "name3"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.steps.1.command", "command3"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.interval", "601"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.schedule_type", "schedule_type1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.0", "cron1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.1", "cron2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.connection_ids.1", "connection_id2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.0", "days_of_week1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.1", "days_of_week2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.time_of_day", "time_of_day1"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientTransformationGitResource(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationGitDeleteHandler.Interactions, 1)
+ tfmock.AssertEmpty(t, transformationData)
+ return nil
+ },
+
+ Steps: []resource.TestStep{
+ step1,
+ step2,
+ },
+ },
+ )
+}
+
+func TestResourceTransformationQuickstartMock(t *testing.T) {
+ step1 := resource.TestStep{
+ Config: `
+ resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "QUICKSTART"
+ paused = true
+
+ schedule {
+ cron = ["cron1","cron2"]
+ interval = 601
+ smart_syncing = true
+ schedule_type = "schedule_type1"
+ days_of_week = ["days_of_week1","days_of_week2"]
+ time_of_day = "time_of_day1"
+ }
+
+ transformation_config {
+ package_name = "package_name"
+ connection_ids = ["connection_id1", "connection_id2"]
+ excluded_models = ["excluded_model1", "excluded_model2"]
+ }
+ }
+ `,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationQuickstartPostHandler.Interactions, 1)
+ return nil
+ },
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "id", "transformation_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "status", "status"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "type", "QUICKSTART"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "paused", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.0", "output_model_name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.1", "output_model_name2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.package_name", "package_name"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.connection_ids.1", "connection_id2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.excluded_models.0", "excluded_model1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.excluded_models.1", "excluded_model2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.upgrade_available", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.interval", "601"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.schedule_type", "schedule_type1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.0", "cron1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.1", "cron2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.0", "days_of_week1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.1", "days_of_week2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.time_of_day", "time_of_day1"),
+ ),
+ }
+
+ step2 := resource.TestStep{
+ Config: `
+ resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "QUICKSTART"
+ paused = true
+
+ schedule {
+ cron = ["cron1","cron2"]
+ interval = 601
+ smart_syncing = true
+ schedule_type = "schedule_type1"
+ days_of_week = ["days_of_week1","days_of_week2"]
+ time_of_day = "14:00"
+ }
+
+ transformation_config {
+ package_name = "package_name"
+ connection_ids = ["connection_id1", "connection_id2"]
+ excluded_models = ["excluded_model1", "excluded_model2"]
+ }
+ }
+ `,
+
+ Check: resource.ComposeAggregateTestCheckFunc(
+ func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationQuickstartPostHandler.Interactions, 1)
+ return nil
+ },
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "id", "transformation_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "status", "status"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_at", "created_at"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "created_by_id", "created_by_id"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "type", "QUICKSTART"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "paused", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.0", "output_model_name1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "output_model_names.1", "output_model_name2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.package_name", "package_name"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.connection_ids.0", "connection_id1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.connection_ids.1", "connection_id2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.excluded_models.0", "excluded_model1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.excluded_models.1", "excluded_model2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "transformation_config.upgrade_available", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.smart_syncing", "true"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.interval", "601"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.schedule_type", "schedule_type1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.0", "cron1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.cron.1", "cron2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.0", "days_of_week1"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.days_of_week.1", "days_of_week2"),
+ resource.TestCheckResourceAttr("fivetran_transformation.transformation", "schedule.time_of_day", "14:00"),
+ ),
+ }
+
+ resource.Test(
+ t,
+ resource.TestCase{
+ PreCheck: func() {
+ setupMockClientTransformationQuickstartResource(t)
+ },
+ ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
+ CheckDestroy: func(s *terraform.State) error {
+ tfmock.AssertEqual(t, transformationQuickstartDeleteHandler.Interactions, 1)
+ return nil
+ },
+
+ Steps: []resource.TestStep{
+ step1,
+ step2,
+ },
+ },
+ )
+}
\ No newline at end of file
diff --git a/templates/data-sources/quickstart_package.md.tmpl b/templates/data-sources/quickstart_package.md.tmpl
new file mode 100644
index 00000000..c77d8fd0
--- /dev/null
+++ b/templates/data-sources/quickstart_package.md.tmpl
@@ -0,0 +1,17 @@
+---
+page_title: "Data Source: fivetran_quickstart_package"
+---
+
+# Data Source: fivetran_quickstart_package
+
+This data source returns the metadata details of the Quickstart transformation package if a valid identifier is provided
+
+## Example Usage
+
+```hcl
+data "fivetran_quickstart_package" "test" {
+ id = "id"
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/data-sources/quickstart_packages.md.tmpl b/templates/data-sources/quickstart_packages.md.tmpl
new file mode 100644
index 00000000..c9557d2a
--- /dev/null
+++ b/templates/data-sources/quickstart_packages.md.tmpl
@@ -0,0 +1,16 @@
+---
+page_title: "Data Source: fivetran_quickstart_packages"
+---
+
+# Data Source: fivetran_quickstart_packages
+
+Returns a list of available Quickstart transformation package metadata details
+
+## Example Usage
+
+```hcl
+data "fivetran_quickstart_packages" "all_packages_metadata" {
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/data-sources/transformation.md.tmpl b/templates/data-sources/transformation.md.tmpl
new file mode 100644
index 00000000..2e0ba9d4
--- /dev/null
+++ b/templates/data-sources/transformation.md.tmpl
@@ -0,0 +1,17 @@
+---
+page_title: "Data Source: fivetran_transformation"
+---
+
+# Data Source: fivetran_transformation
+
+Returns transformation details if a valid identifier was provided
+
+## Example Usage
+
+```hcl
+data "fivetran_transformation" "test" {
+ id = "id"
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/data-sources/transformation_project.md.tmpl b/templates/data-sources/transformation_project.md.tmpl
new file mode 100644
index 00000000..4cec7e91
--- /dev/null
+++ b/templates/data-sources/transformation_project.md.tmpl
@@ -0,0 +1,17 @@
+---
+page_title: "Data Source: fivetran_transformation_project"
+---
+
+# Data Source: fivetran_transformation_project
+
+Returns transformation project details if a valid identifier was provided
+
+## Example Usage
+
+```hcl
+data "fivetran_transformation_project" "test" {
+ id = "id"
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/data-sources/transformation_projects.md.tmpl b/templates/data-sources/transformation_projects.md.tmpl
new file mode 100644
index 00000000..5f7ac08f
--- /dev/null
+++ b/templates/data-sources/transformation_projects.md.tmpl
@@ -0,0 +1,16 @@
+---
+page_title: "Data Source: fivetran_transformation_projects"
+---
+
+# Data Source: fivetran_transformation_projects
+
+Returns a list of all transformation projects available via API within your Fivetran account.
+
+## Example Usage
+
+```hcl
+data "fivetran_transformation_projects" "test" {
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/data-sources/transformations.md.tmpl b/templates/data-sources/transformations.md.tmpl
new file mode 100644
index 00000000..2ab6dca3
--- /dev/null
+++ b/templates/data-sources/transformations.md.tmpl
@@ -0,0 +1,16 @@
+---
+page_title: "Data Source: fivetran_transformations"
+---
+
+# Data Source: fivetran_transformations
+
+Returns a list of all transformations available via API within your Fivetran account.
+
+## Example Usage
+
+```hcl
+data "fivetran_transformations" "test" {
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
\ No newline at end of file
diff --git a/templates/guides/transformation_private_git_deploy_key.md.tmpl b/templates/guides/transformation_private_git_deploy_key.md.tmpl
new file mode 100644
index 00000000..8fe4b2ef
--- /dev/null
+++ b/templates/guides/transformation_private_git_deploy_key.md.tmpl
@@ -0,0 +1,60 @@
+----
+page_title: "Transformation Project Setup With Git Private Repo"
+subcategory: "Getting Started"
+---
+
+# How to set up a Transformation Project with private Git Repo.
+
+To be able to use private Transformation Project Git repository you have to grant Fivetran access to this repo.
+To do that you need to add a Deploy Key to your repository.
+To get SSH key from Fivetran create `fivetran_transformation_project` resource:
+
+```hcl
+resource "fivetran_group" "my_group" {
+ name = "My_Group"
+}
+
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 0
+ target_name = "target_name"
+ environment_vars = ["environment_var"]
+ }
+}
+```
+
+Then you need to set up the Transformation Project public key (field `public_key` in created resource) as a deploy key into your repo using:
+
+[GitHub Provider Repository Deploy Key Resource](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key):
+```hcl
+resource "github_repository_deploy_key" "example_repository_deploy_key" {
+ title = "Repository test key"
+ repository = "repo-owner/repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ read_only = true
+}
+```
+
+or
+
+[Bitbucket Provider Repository Deploy Key Resource]https://registry.terraform.io/providers/DrFaust92/bitbucket/latest/docs/resources/deploy_key)
+```hcl
+resource "bitbucket_deploy_key" "test" {
+ workspace = "repo-owner"
+ repository = "repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ label = "Repository test key"
+}
+```
+
+Since we recommend using third-party providers in this case, please make sure that access to the repositories is provided correctly and the providers are configured correctly for connection.
diff --git a/templates/guides/version_1.5.0_update_guides.md.tmpl b/templates/guides/version_1.5.0_update_guides.md.tmpl
new file mode 100644
index 00000000..97bc7c8b
--- /dev/null
+++ b/templates/guides/version_1.5.0_update_guides.md.tmpl
@@ -0,0 +1,180 @@
+----
+page_title: "Version Update 1.5.0"
+subcategory: "Upgrade Guides"
+---
+
+# Version 1.5.0
+
+## What's new in 1.5.0
+
+In version `1.5.0` of Fivetran Terraform provider, цe have implemented new resources for managing Transformations:
+
+## Migration guide
+
+### Provider
+
+Update your provider configuration in the following way:
+
+Previous configuration:
+
+```hcl
+required_providers {
+ fivetran = {
+ version = "~> 1.4.2"
+ source = "fivetran/fivetran"
+ }
+ }
+```
+
+Updated configuration:
+
+```hcl
+required_providers {
+ fivetran = {
+ version = ">= 1.5.0"
+ source = "fivetran/fivetran"
+ }
+ }
+```
+
+### Resource `fivetran_dbt_project`
+
+Replace all your resources `fivetran_dbt_project` with `fivetran_transformation_project`
+
+Previous configuration:
+
+```hcl
+resource "fivetran_dbt_project" "test_project" {
+ provider = fivetran-provider
+ group_id = fivetran_destination.test_destination.id
+ dbt_version = "1.0.1"
+ threads = 1
+ default_schema = "dbt_demo_test_e2e_terraform"
+ type = "GIT"
+ project_config {
+ folder_path = "/folder/path"
+ git_remote_url = "git@github.com:fivetran/repo-name.git"
+ git_branch = "main"
+ }
+}
+```
+
+Updated configuration:
+
+```hcl
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git@github.com:fivetran/repo-name.git"
+ git_branch = "main"
+ folder_path = "/folder/path"
+ dbt_version = "1.0.1"
+ default_schema = "dbt_demo_test_e2e_terraform"
+ threads = 1
+ target_name = "target_name"
+ environment_vars = ["environment_var"]
+ }
+}
+```
+
+Then you need to set up the Transformation Project public key (field `public_key` in created resource) as a deploy key into your repo using:
+
+[GitHub Provider Repository Deploy Key Resource](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key):
+```hcl
+resource "github_repository_deploy_key" "example_repository_deploy_key" {
+ title = "Repository test key"
+ repository = "repo-owner/repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ read_only = true
+}
+```
+
+or
+
+[Bitbucket Provider Repository Deploy Key Resource]https://registry.terraform.io/providers/DrFaust92/bitbucket/latest/docs/resources/deploy_key)
+```hcl
+resource "bitbucket_deploy_key" "test" {
+ workspace = "repo-owner"
+ repository = "repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ label = "Repository test key"
+}
+```
+
+Since we recommend using third-party providers in this case, please make sure that access to the repositories is provided correctly and the providers are configured correctly for connection.
+
+### Resource `fivetran_dbt_transformation`
+
+Replace all your resources `fivetran_dbt_transformation` with `fivetran_transformation`
+
+Previous configuration:
+
+```hcl
+resource "fivetran_dbt_transformation" "transformation" {
+ dbt_model_name = "dbt_model_name"
+ dbt_project_id = "dbt_project_id"
+ run_tests = "false"
+ paused = "false"
+ schedule {
+ schedule_type = "TIME_OF_DAY"
+ time_of_day = "12:00"
+ days_of_week = ["MONDAY", "SATURDAY"]
+ }
+}
+```
+
+Updated configuration:
+
+```hcl
+resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "DBT_CORE"
+ paused = false
+
+ schedule {
+ cron = ["cron1","cron2"]
+ interval = 60
+ smart_syncing = true
+ connection_ids = ["connection_id1", "connection_id2"]
+ schedule_type = "TIME_OF_DAY"
+ days_of_week = ["MONDAY", "SATURDAY"]
+ time_of_day = "14:00"
+ }
+
+ transformation_config {
+ project_id = "dbt_project_id"
+ name = "name"
+ steps = [
+ {
+ name = "name1"
+ command = "command1"
+ },
+ {
+ name = "name2"
+ command = "command2"
+ }
+ ]
+ }
+}
+```
+
+### Datasources `fivetran_dbt_project`, `fivetran_dbt_projects`, `fivetran_dbt_transformation`, `fivetran_dbt_models`
+
+Replace datasources:
+- `fivetran_dbt_project` with `fivetran_transformation_project`
+- `fivetran_dbt_projects` with `fivetran_transformation_projects`
+- `fivetran_dbt_transformation` with `fivetran_transformation`
+Remove datasource `fivetran_dbt_models`
+
+### Update terraform state
+
+Once all configurations have been updated, run:
+
+```
+terraform init -upgrade
+```
\ No newline at end of file
diff --git a/templates/resources/dbt_git_project_config.md.tmpl b/templates/resources/dbt_git_project_config.md.tmpl
index 5a717ffd..b3c5a8b8 100644
--- a/templates/resources/dbt_git_project_config.md.tmpl
+++ b/templates/resources/dbt_git_project_config.md.tmpl
@@ -4,7 +4,7 @@ page_title: "Resource: fivetran_dbt_git_project_config"
# Resource: fivetran_dbt_git_project_config
-Resource is in ALPHA state.
+This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema.
This resource allows you to add and manage dbt Git Projects Configs.
diff --git a/templates/resources/dbt_project.md.tmpl b/templates/resources/dbt_project.md.tmpl
index b1249ff3..5a1dda23 100644
--- a/templates/resources/dbt_project.md.tmpl
+++ b/templates/resources/dbt_project.md.tmpl
@@ -4,7 +4,7 @@ page_title: "Resource: fivetran_dbt_project"
# Resource: fivetran_dbt_project
-Resource is in ALPHA state.
+This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema.
This resource allows you to add, manage and delete dbt Projects in your account.
diff --git a/templates/resources/dbt_transformation.md.tmpl b/templates/resources/dbt_transformation.md.tmpl
index b8a3ef04..ff02d565 100644
--- a/templates/resources/dbt_transformation.md.tmpl
+++ b/templates/resources/dbt_transformation.md.tmpl
@@ -4,7 +4,7 @@ page_title: "Resource: fivetran_dbt_transformation"
# Resource: fivetran_dbt_transformation
-Resource is in ALPHA state.
+This resource is Deprecated, please follow the 1.5.0 migration guide to update the schema.
This resource allows you to add, manage and delete dbt Transformations for existing dbt Model.
To retrieve available dbt Models use this [Retrieve dbt Project models](https://fivetran.com/docs/rest-api/dbt-transformation-management#retrievedbtprojectmodels) endpoint.
diff --git a/templates/resources/transformation.md.tmpl b/templates/resources/transformation.md.tmpl
new file mode 100644
index 00000000..e6c44808
--- /dev/null
+++ b/templates/resources/transformation.md.tmpl
@@ -0,0 +1,122 @@
+---
+page_title: "Resource: fivetran_transformation"
+---
+
+# Resource: fivetran_transformation
+
+Resource is in ALPHA state.
+
+This resource allows you to add, manage and delete transformation projects in your account.
+
+## Example Usage for dbt Core Transformation
+
+```hcl
+resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "DBT_CORE"
+ paused = true
+
+ schedule {
+ schedule_type = "TIME_OF_DAY"
+ time_of_day = "11:00"
+ }
+
+ transformation_config {
+ project_id = "project_id"
+ name = "name"
+ steps = [
+ {
+ name = "name1"
+ command = "command1"
+ },
+ {
+ name = "name2"
+ command = "command2"
+ }
+ ]
+ }
+}
+```
+
+## Example Usage for Quickstart Transformation
+
+```hcl
+resource "fivetran_transformation" "transformation" {
+ provider = fivetran-provider
+
+ type = "QUICKSTART"
+ paused = true
+
+ schedule {
+ schedule_type = "TIME_OF_DAY"
+ time_of_day = "11:00"
+ }
+
+ transformation_config {
+ package_name = "package_name"
+ connection_ids = ["connection_id1", "connection_id2"]
+ excluded_models = ["excluded_model1", "excluded_model2"]
+ }
+}
+```
+
+## Example Usages for Transformation Schedule section
+
+```hcl
+schedule {
+ schedule_type = "TIME_OF_DAY"
+ days_of_week = ["MONDAY", "FRIDAY"]
+ time_of_day = "11:00"
+}
+```
+
+```hcl
+schedule {
+ schedule_type = "INTEGRATED"
+ connection_ids = ["connection_id1", "connection_id2"]
+}
+```
+
+```hcl
+schedule {
+ schedule_type = "INTERVAL"
+ interval = 601
+}
+```
+
+```hcl
+schedule {
+ schedule_type = "CRON"
+ cron = ["0 */1 * * *"]
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
+
+## Import
+
+1. To import an existing `fivetran_transformation` resource into your Terraform state, you need to get **Transformation ID** via API call `GET https://api.fivetran.com/v1/transformations` to retrieve available projects.
+2. Fetch transformation details for particular `transformation-id` using `GET https://api.fivetran.com/v1/transformations/{transformation-id}` to ensure that this is the transformation you want to import.
+3. Define an empty resource in your `.tf` configuration:
+
+```hcl
+resource "fivetran_transformation" "my_imported_fivetran_transformation" {
+
+}
+```
+
+4. Run the `terraform import` command:
+
+```
+terraform import fivetran_transformation.my_imported_fivetran_transformation {Transformation ID}
+```
+
+4. Use the `terraform state show` command to get the values from the state:
+
+```
+terraform state show 'fivetran_transformation.my_imported_fivetran_transformation'
+```
+
+5. Copy the values and paste them to your `.tf` configuration.
+
diff --git a/templates/resources/transformation_project.md.tmpl b/templates/resources/transformation_project.md.tmpl
new file mode 100644
index 00000000..cff2011f
--- /dev/null
+++ b/templates/resources/transformation_project.md.tmpl
@@ -0,0 +1,116 @@
+---
+page_title: "Resource: fivetran_transformation_project"
+---
+
+# Resource: fivetran_transformation_project
+
+Resource is in ALPHA state.
+
+This resource allows you to add, manage and delete transformation projects in your account.
+
+## Example Usage
+
+```hcl
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 1
+ target_name = "target_name"
+ environment_vars = ["DBT_VARIABLE=variable_value"]
+ }
+}
+```
+
+{{ .SchemaMarkdown | trimspace }}
+
+## Import
+
+1. To import an existing `fivetran_transformation_project` resource into your Terraform state, you need to get **Transformation Project ID** via API call `GET https://api.fivetran.com/v1/transformation-projects` to retrieve available projects.
+2. Fetch project details for particular `project-id` using `GET https://api.fivetran.com/v1/transformation-projects/{project-id}` to ensure that this is the project you want to import.
+3. Define an empty resource in your `.tf` configuration:
+
+```hcl
+resource "fivetran_transformation_project" "my_imported_fivetran_transformation_project" {
+
+}
+```
+
+4. Run the `terraform import` command:
+
+```
+terraform import fivetran_transformation_project.my_imported_fivetran_transformation_project {Transformation Project ID}
+```
+
+4. Use the `terraform state show` command to get the values from the state:
+
+```
+terraform state show 'fivetran_transformation_project.my_imported_fivetran_transformation_project'
+```
+
+5. Copy the values and paste them to your `.tf` configuration.
+
+
+## How to set up a Transformation Project with private Git Repo.
+
+To be able to use private Transformation Project Git repository you have to grant Fivetran access to this repo.
+To do that you need to add a Deploy Key to your repository.
+To get SSH key from Fivetran create `fivetran_transformation_project` resource:
+
+```hcl
+resource "fivetran_group" "my_group" {
+ name = "My_Group"
+}
+
+resource "fivetran_transformation_project" "project" {
+ provider = fivetran-provider
+ group_id = "group_id"
+ type = "DBT_GIT"
+ run_tests = true
+
+ project_config {
+ git_remote_url = "git_remote_url"
+ git_branch = "git_branch"
+ folder_path = "folder_path"
+ dbt_version = "dbt_version"
+ default_schema = "default_schema"
+ threads = 1
+ target_name = "target_name"
+ environment_vars = ["DBT_VARIABLE=variable_value"]
+ }
+}
+```
+
+Then you need to set up the Transformation Project public key (field `public_key` in created resource) as a deploy key into your repo using:
+
+[GitHub Provider Repository Deploy Key Resource](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_deploy_key):
+```hcl
+resource "github_repository_deploy_key" "example_repository_deploy_key" {
+ title = "Repository test key"
+ repository = "repo-owner/repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ read_only = true
+}
+```
+
+or
+
+[Bitbucket Provider Repository Deploy Key Resource]https://registry.terraform.io/providers/DrFaust92/bitbucket/latest/docs/resources/deploy_key)
+```hcl
+resource "bitbucket_deploy_key" "test" {
+ workspace = "repo-owner"
+ repository = "repo-name"
+ key = fivetran_transformation_project.test_project.project_config.public_key
+ label = "Repository test key"
+}
+```
+
+Since we recommend using third-party providers in this case, please make sure that access to the repositories is provided correctly and the providers are configured correctly for connection.
\ No newline at end of file