├── .changes ├── unreleased │ ├── .gitkeep │ └── Dependency-20250201-204222.yaml ├── 0.18.3.md ├── v1.0.1.md ├── 0.21.2.md ├── 0.1.1.md ├── 0.18.0.md ├── v1.11.1.md ├── 0.20.0.md ├── 0.8.0.md ├── v0.25.0.md ├── v1.10.0.md ├── v1.11.2.md ├── 0.2.0.md ├── 0.4.0.md ├── 0.9.0.md ├── v0.29.3.md ├── v1.18.1.md ├── 0.13.1.md ├── 0.18.1.md ├── 0.5.2.md ├── v0.25.2.md ├── v1.8.0.md ├── 0.13.0.md ├── 0.15.1.md ├── v1.7.1.md ├── 0.12.1.md ├── 0.19.0.md ├── 0.7.1.md ├── v0.29.1.md ├── v1.11.3.md ├── 0.11.1.md ├── 0.21.1.md ├── 0.22.1.md ├── 0.4.2.md ├── v0.25.1.md ├── v1.4.1.md ├── 0.10.0.md ├── v1.14.2.md ├── v1.16.0.md ├── 0.5.4.md ├── 0.12.0.md ├── v0.29.0.md ├── 0.11.0.md ├── 0.18.2.md ├── v1.14.0.md ├── v1.15.0.md ├── v1.4.2.md ├── v0.24.1.md ├── v1.11.4.md ├── v1.2.1.md ├── 0.14.0.md ├── v0.1.0.md ├── v0.26.1.md ├── v0.29.2.md ├── v1.11.0.md ├── v1.6.7.md ├── 0.5.3.md ├── v0.25.3.md ├── v1.6.5.md ├── 0.5.1.md ├── v1.13.1.md ├── 0.4.1.md ├── v0.24.0.md ├── v1.1.0.md ├── v1.15.1.md ├── v1.6.9.md ├── 0.17.0.md ├── 0.16.0.md ├── 0.7.0.md ├── v0.23.0.md ├── v1.6.6.md ├── 0.1.0.md ├── v1.12.0.md ├── v1.14.1.md ├── v1.6.8.md ├── 0.6.0.md ├── 0.15.0.md ├── 0.22.0.md ├── v1.6.4.md ├── v0.30.0.md ├── 0.5.0.md ├── v1.7.0.md ├── v1.6.1.md ├── v0.28.0.md ├── v1.6.2.md ├── v1.4.4.md ├── v1.5.1.md ├── v1.6.3.md ├── v1.10.1.md ├── v1.17.0.md ├── v0.26.0.md ├── v0.27.0.md ├── header.tpl.md ├── v1.19.0.md ├── v1.9.0.md ├── v1.13.0.md ├── v1.14.3.md ├── 0.21.0.md ├── v1.2.0.md ├── v1.18.0.md ├── 0.3.0.md ├── v1.4.0.md ├── v1.6.0.md ├── v1.0.0.md ├── v1.5.0.md └── v1.3.0.md ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── support-request.md │ ├── feature-request.md │ └── bug-report.md ├── FUNDING.yml ├── pull-request-template.md ├── dependabot.yaml └── workflows │ ├── triage.yaml │ ├── release.yaml │ ├── dependabot-changie.yaml │ └── tests.yaml ├── codecov.yaml ├── docker-compose.yaml ├── internal ├── utils │ ├── value.go │ ├── container.go │ ├── hcl.go │ ├── errors_test.go │ ├── decode.go │ ├── mutex.go │ ├── fields.go │ └── errors.go ├── acctest │ ├── utils.go │ ├── provider_test.go │ └── client.go ├── customtypes │ └── localized_string_test.go ├── models │ └── models.go ├── sharedtypes │ └── store_key_reference.go ├── resources │ ├── state_transition │ │ └── model.go │ ├── subscription │ │ ├── upgrade_v0.go │ │ └── downgrade_v2.go │ ├── state │ │ └── resource_test.go │ ├── product_selection │ │ ├── model_test.go │ │ └── model.go │ └── attribute_group │ │ └── model.go ├── custommodifiers │ ├── booldefault.go │ └── emptylist.go ├── datasource │ ├── state │ │ ├── resource_test.go │ │ └── resource.go │ └── type │ │ └── resource.go └── customvalidator │ ├── requires.go │ └── field.go ├── tools └── tools.go ├── examples ├── resources │ ├── commercetools_custom_object │ │ └── resource.tf │ ├── commercetools_api_client │ │ └── resource.tf │ ├── commercetools_tax_category │ │ └── resource.tf │ ├── commercetools_channel │ │ └── resource.tf │ ├── commercetools_customer_group │ │ └── resource.tf │ ├── commercetools_shipping_zone │ │ └── resource.tf │ ├── commercetools_attribute_group │ │ └── resource.tf │ ├── commercetools_shipping_method │ │ └── resource.tf │ ├── commercetools_subscription │ │ └── resource.tf │ ├── commercetools_product_selection │ │ └── resource.tf │ ├── commercetools_project_settings │ │ └── resource.tf │ ├── commercetools_tax_category_rate │ │ └── resource.tf │ ├── commercetools_discount_code │ │ └── resource.tf │ ├── commercetools_state │ │ └── resource.tf │ ├── commercetools_category │ │ └── resource.tf │ ├── commercetools_state_transitions │ │ └── resource.tf │ ├── commercetools_product_discount │ │ └── resource.tf │ ├── commercetools_store │ │ └── resource.tf │ ├── commercetools_type │ │ └── resource.tf │ ├── commercetools_shipping_zone_rate │ │ └── resource.tf │ ├── commercetools_associate_role │ │ └── resource.tf │ ├── commercetools_business_unit_company │ │ └── resource.tf │ ├── commercetools_business_unit_division │ │ └── resource.tf │ ├── commercetools_api_extension │ │ └── resource.tf │ └── commercetools_product_type │ │ └── resource.tf └── data-sources │ ├── commercetools_type │ └── data-source.tf │ └── commercetools_state │ └── data-source.tf ├── commercetools ├── testdata │ └── custom_fields_test │ │ ├── commercetools_product_type.tmpl │ │ ├── commercetools_store.tmpl │ │ ├── commercetools_customer_group.tmpl │ │ ├── commercetools_category.tmpl │ │ ├── commercetools_channel.tmpl │ │ ├── commercetools_shipping_method.tmpl │ │ ├── commercetools_cart_discount.tmpl │ │ ├── main.tmpl │ │ ├── commercetools_discount_code.tmpl │ │ └── commercetools_custom_fields.tmpl ├── helpers_test.go ├── resource_cart_discount_utils_test.go ├── provider_test.go ├── resource_cart_discount_giftlineitem_test.go ├── resource_cart_discount_totalprice_test.go ├── resource_api_extension_migrate.go ├── resource_cart_discount_absolute_test.go ├── resource_tax_category_test.go ├── resource_cart_discount_stores_test.go ├── marshalling.go └── utils_test.go ├── .gitignore ├── .golangci.yml ├── .vscode ├── settings.json └── launch.json ├── Dockerfile ├── templates ├── resources │ └── shipping_zone_rate.md.tmpl ├── index.md.tmpl └── guides │ ├── extensions.md.tmpl │ └── custom-fields.md.tmpl ├── .changie.yaml ├── docs ├── data-sources │ ├── type.md │ └── state.md ├── resources │ ├── tax_category.md │ ├── shipping_zone.md │ ├── custom_object.md │ ├── customer_group.md │ ├── api_client.md │ ├── attribute_group.md │ ├── tax_category_rate.md │ ├── state.md │ ├── product_selection.md │ ├── shipping_method.md │ ├── channel.md │ └── state_transitions.md ├── guides │ ├── extensions.md │ └── custom-fields.md └── index.md ├── .goreleaser.yml ├── Taskfile.yml └── main.go /.changes/unreleased/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @labd/go 2 | -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- 1 | comment: off 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.changes/0.18.3.md: -------------------------------------------------------------------------------- 1 | ## 0.18.3 (2019-09-11) 2 | 3 | - Use Terraform 0.12.8 dependency 4 | -------------------------------------------------------------------------------- /.changes/v1.0.1.md: -------------------------------------------------------------------------------- 1 | ## v1.0.1 (2022-05-25) 2 | 3 | - Minor release to fix hash error 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platform 2 | github: [labd] 3 | -------------------------------------------------------------------------------- /.changes/0.21.2.md: -------------------------------------------------------------------------------- 1 | ## 0.21.2 (2020-06-11) 2 | 3 | - Resource store: Add `languages` field 4 | -------------------------------------------------------------------------------- /.changes/0.1.1.md: -------------------------------------------------------------------------------- 1 | ## 0.1.1 (2018-10-04) 2 | ### Added 3 | - **New resource:** `commercetools_type` 4 | -------------------------------------------------------------------------------- /.changes/0.18.0.md: -------------------------------------------------------------------------------- 1 | ## 0.18.0 (2019-08-14) 2 | 3 | - Resource state: Add `transitions` field (#74) 4 | -------------------------------------------------------------------------------- /.changes/v1.11.1.md: -------------------------------------------------------------------------------- 1 | ## v1.11.1 - 2023-08-25 2 | ### Fixed 3 | * Change store countries to set type 4 | -------------------------------------------------------------------------------- /.changes/0.20.0.md: -------------------------------------------------------------------------------- 1 | ## 0.20.0 (2020-02-22) 2 | 3 | - Resource subscription: Add Azure Event Grid support 4 | -------------------------------------------------------------------------------- /.changes/0.8.0.md: -------------------------------------------------------------------------------- 1 | ## 0.8.0 (2019-05-20) 2 | ### Added 3 | - **New resource:** `commercetools_state` 4 | -------------------------------------------------------------------------------- /.changes/v0.25.0.md: -------------------------------------------------------------------------------- 1 | ## v0.25.0 (2020-11-27) 2 | 3 | - **New resource:** `commercetools_custom_object` 4 | -------------------------------------------------------------------------------- /.changes/v1.10.0.md: -------------------------------------------------------------------------------- 1 | ## v1.10.0 - 2023-08-11 2 | ### Added 3 | * **New data source:** commercetools_state 4 | -------------------------------------------------------------------------------- /.changes/v1.11.2.md: -------------------------------------------------------------------------------- 1 | ## v1.11.2 - 2023-08-31 2 | ### Fixed 3 | * NotFound error not handled correctly 4 | -------------------------------------------------------------------------------- /.changes/0.2.0.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 (2018-12-10) 2 | ### Added 3 | - **New resource:** `commercetools_product_type` 4 | -------------------------------------------------------------------------------- /.changes/0.4.0.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 (2019-01-10) 2 | ### Fixed 3 | - Use auto-generated commercetools-go-sdk types 4 | -------------------------------------------------------------------------------- /.changes/0.9.0.md: -------------------------------------------------------------------------------- 1 | ## 0.9.0 (2019-05-20) 2 | 3 | - Use Terraform 0.12 dependency to prepare for 0.12 release 4 | -------------------------------------------------------------------------------- /.changes/v0.29.3.md: -------------------------------------------------------------------------------- 1 | ## v0.29.3 (2021-06-16) 2 | 3 | - Fix custom object not being read / updated correctly 4 | -------------------------------------------------------------------------------- /.changes/v1.18.1.md: -------------------------------------------------------------------------------- 1 | ## v1.18.1 - 2024-12-04 2 | ### Fixed 3 | * Fixed default shipping_method active field 4 | -------------------------------------------------------------------------------- /.changes/0.13.1.md: -------------------------------------------------------------------------------- 1 | ## 0.13.1 (2019-07-02) 2 | 3 | - Small fix for incorrect binary name in homebrew installation 4 | -------------------------------------------------------------------------------- /.changes/0.18.1.md: -------------------------------------------------------------------------------- 1 | ## 0.18.1 (2019-08-19) 2 | 3 | - Change Linux release artifact back to default archive format 4 | -------------------------------------------------------------------------------- /.changes/0.5.2.md: -------------------------------------------------------------------------------- 1 | ## 0.5.2 (2019-03-26) 2 | ### Fixed 3 | - Resource type: Fix error reading field type `Money` 4 | -------------------------------------------------------------------------------- /.changes/v0.25.2.md: -------------------------------------------------------------------------------- 1 | ## v0.25.2 (2020-12-17) 2 | 3 | - Resource channel: Add support for updating the `key` field 4 | -------------------------------------------------------------------------------- /.changes/v1.8.0.md: -------------------------------------------------------------------------------- 1 | ## v1.8.0 - 2023-08-03 2 | ### Added 3 | * **New resource:** `commercetools_attribute_group` 4 | -------------------------------------------------------------------------------- /.changes/0.13.0.md: -------------------------------------------------------------------------------- 1 | ## 0.13.0 (2019-07-02) 2 | 3 | - Add brew install option to goreleaser, see README for more info 4 | -------------------------------------------------------------------------------- /.changes/0.15.1.md: -------------------------------------------------------------------------------- 1 | ## 0.15.1 (2019-07-16) 2 | 3 | - Trying to fix Brew release now that version number is in binary 4 | -------------------------------------------------------------------------------- /.changes/v1.7.1.md: -------------------------------------------------------------------------------- 1 | ## v1.7.1 - 2023-07-13 2 | ### Fixed 3 | * Fix discount_code resource when no predicate is given 4 | -------------------------------------------------------------------------------- /.changes/0.12.1.md: -------------------------------------------------------------------------------- 1 | ## 0.12.1 (2019-06-26) 2 | 3 | - Resource api_client: Small fix in creating api client with new scopes 4 | -------------------------------------------------------------------------------- /.changes/0.19.0.md: -------------------------------------------------------------------------------- 1 | ## 0.19.0 (2019-10-02) 2 | 3 | - Update all dependencies (use go 1.13, switch to terraform plugin sdk) 4 | -------------------------------------------------------------------------------- /.changes/0.7.1.md: -------------------------------------------------------------------------------- 1 | ## 0.7.1 (2019-05-14) 2 | ### Added 3 | - Resource shipping_zone_rate: Add validation for currency codes 4 | -------------------------------------------------------------------------------- /.changes/v0.29.1.md: -------------------------------------------------------------------------------- 1 | ## v0.29.1 (2021-05-19) 2 | 3 | - Fix category create not working with only name and slug filled in 4 | -------------------------------------------------------------------------------- /.changes/v1.11.3.md: -------------------------------------------------------------------------------- 1 | ## v1.11.3 - 2023-09-20 2 | ### Fixed 3 | * Added fix to update countryTaxRateFallbackEnabled separately 4 | -------------------------------------------------------------------------------- /.changes/0.11.1.md: -------------------------------------------------------------------------------- 1 | ## 0.11.1 (2019-06-26) 2 | 3 | - Resource shipping_zone: Fix creation and deletion, thanks to @sshibani ! 4 | -------------------------------------------------------------------------------- /.changes/0.21.1.md: -------------------------------------------------------------------------------- 1 | ## 0.21.1 (2020-04-22) 2 | 3 | - Resource channel: Fix read null pointer exception. Name should be optional. 4 | -------------------------------------------------------------------------------- /.changes/0.22.1.md: -------------------------------------------------------------------------------- 1 | ## 0.22.1 (2020-07-21) 2 | 3 | - Resource store: Fix edge case where distribution channels were not updated 4 | -------------------------------------------------------------------------------- /.changes/0.4.2.md: -------------------------------------------------------------------------------- 1 | ## 0.4.2 (2019-02-11) 2 | 3 | ### Fixed 4 | * Resource tax_category: Fix tax rate 0.0 case not being handled 5 | -------------------------------------------------------------------------------- /.changes/v0.25.1.md: -------------------------------------------------------------------------------- 1 | ## v0.25.1 (2020-12-05) 2 | 3 | - Resource type: Fix a bug when the `input_hint` of a field was modified. 4 | -------------------------------------------------------------------------------- /.changes/v1.4.1.md: -------------------------------------------------------------------------------- 1 | ## v1.4.1 (2022-08-19) 2 | 3 | - `resource_product_type` fix parsing the enums from the state file (#294) 4 | -------------------------------------------------------------------------------- /.changes/0.10.0.md: -------------------------------------------------------------------------------- 1 | ## 0.10.0 (2019-06-17) 2 | ### Added 3 | - Use Terraform 0.12.2 dependency for compatability with latest version 4 | -------------------------------------------------------------------------------- /.changes/v1.14.2.md: -------------------------------------------------------------------------------- 1 | ## v1.14.2 - 2024-04-19 2 | ### Fixed 3 | * Added documentation hint on importing commercetools_shipping_zone_rate 4 | -------------------------------------------------------------------------------- /.changes/v1.16.0.md: -------------------------------------------------------------------------------- 1 | ## v1.16.0 - 2024-10-10 2 | ### Added 3 | * `resource_api_extension` add support for triggers on `shopping-list`` 4 | -------------------------------------------------------------------------------- /.changes/0.5.4.md: -------------------------------------------------------------------------------- 1 | ## 0.5.4 (2019-04-14) 2 | 3 | - Resource product_type: Fixed localized enum values being updated even if not changed 4 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | commercetools: 3 | image: labdigital/commercetools-mock-server 4 | ports: 5 | - 8989:8989 6 | -------------------------------------------------------------------------------- /.changes/0.12.0.md: -------------------------------------------------------------------------------- 1 | ## 0.12.0 (2019-06-26) 2 | 3 | **Breaking chanages** 4 | 5 | - Resource api_client: Changed scope type from string to set 6 | -------------------------------------------------------------------------------- /.changes/v0.29.0.md: -------------------------------------------------------------------------------- 1 | ## v0.29.0 (2021-04-23) 2 | 3 | - Resource Project: Add project level cart `delete_days_after_last_modification` setting 4 | -------------------------------------------------------------------------------- /.changes/0.11.0.md: -------------------------------------------------------------------------------- 1 | ## 0.11.0 (2019-06-20) 2 | ### Added 3 | - Use new Commercetools Go SDK to set the User-Agent header on Commercetools HTTP requests. 4 | -------------------------------------------------------------------------------- /.changes/0.18.2.md: -------------------------------------------------------------------------------- 1 | ## 0.18.2 (2019-09-10) 2 | 3 | - Brew release Linux has incorrect artifact name 4 | - Set GOPROXY for possible unreachable go packages 5 | -------------------------------------------------------------------------------- /.changes/v1.14.0.md: -------------------------------------------------------------------------------- 1 | ## v1.14.0 - 2024-01-26 2 | ### Added 3 | * Added support for confluence cloud subscriptions 4 | ### Fixed 5 | * Updated documentation 6 | -------------------------------------------------------------------------------- /.changes/v1.15.0.md: -------------------------------------------------------------------------------- 1 | ## v1.15.0 - 2024-08-30 2 | ### Added 3 | * Added resources for associate role and business units and extended project setting options 4 | -------------------------------------------------------------------------------- /.changes/v1.4.2.md: -------------------------------------------------------------------------------- 1 | ## v1.4.2 (2022-08-24) 2 | 3 | - Fix setting custom field values on supported resources when the fiedl type 4 | is a set (#299) 5 | -------------------------------------------------------------------------------- /.changes/v0.24.1.md: -------------------------------------------------------------------------------- 1 | ## v0.24.1 (2020-11-13) 2 | 3 | - Resource tax_rate: Add a workaround to handle an issue with changing id's after a tax category update 4 | -------------------------------------------------------------------------------- /.changes/v1.11.4.md: -------------------------------------------------------------------------------- 1 | ## v1.11.4 - 2023-09-26 2 | ### Fixed 3 | * prevented issue with bool valueunknown check 4 | * Custom Field Unset Causing Provider Crash 5 | -------------------------------------------------------------------------------- /.changes/v1.2.1.md: -------------------------------------------------------------------------------- 1 | ## v1.2.1 (2022-06-16) 2 | 3 | - Fix api_extension resource to not error out when the new condition field is 4 | not defined. (#261) 5 | -------------------------------------------------------------------------------- /.changes/0.14.0.md: -------------------------------------------------------------------------------- 1 | ## 0.14.0 (2019-07-04) 2 | 3 | - Use new Commercetools Go SDK definitions (main change is Reference is now 4 | ResourceIdentifier resource) 5 | -------------------------------------------------------------------------------- /internal/utils/value.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // GetRef gets the reference of value with generic type 4 | func GetRef[T any](value T) *T { 5 | return &value 6 | } 7 | -------------------------------------------------------------------------------- /.changes/unreleased/Dependency-20250201-204222.yaml: -------------------------------------------------------------------------------- 1 | kind: Dependency 2 | body: 'chore(deps): bump the go group with 4 updates' 3 | time: 2025-02-01T20:42:22.21194155Z 4 | -------------------------------------------------------------------------------- /.changes/v0.1.0.md: -------------------------------------------------------------------------------- 1 | 2 | ## 0.1.0 (2018-09-14) 3 | ### Added 4 | * `commercetools_api_extension` 5 | * `commercetools_subscription` 6 | * `commercetools_project_settings` 7 | -------------------------------------------------------------------------------- /.changes/v0.26.1.md: -------------------------------------------------------------------------------- 1 | ## v0.26.1 (2021-01-21) 2 | 3 | - Resource api_extension: Fixed typo in `trigger` field name that caused updates to actions in triggers to fail 4 | -------------------------------------------------------------------------------- /.changes/v0.29.2.md: -------------------------------------------------------------------------------- 1 | ## v0.29.2 (2021-05-19) 2 | 3 | - Fix orderHint not being set but key on category being cleared. Note this will clear orderHint if it's not set. 4 | -------------------------------------------------------------------------------- /.changes/v1.11.0.md: -------------------------------------------------------------------------------- 1 | ## v1.11.0 - 2023-08-25 2 | ### Added 3 | * Added associate role resource 4 | ### Fixed 5 | * Fixed bad handling of secrets on subscription import 6 | -------------------------------------------------------------------------------- /.changes/v1.6.7.md: -------------------------------------------------------------------------------- 1 | ## v1.6.7 (2023-01-24) 2 | ### Fixed 3 | * `resource_project` Add extra checks to make sure we have a `message` block 4 | before reading it(#340) 5 | -------------------------------------------------------------------------------- /.changes/0.5.3.md: -------------------------------------------------------------------------------- 1 | ## 0.5.3 (2019-03-27) 2 | 3 | - Resource product_type: Implement description update 4 | - Resource product_type: Implement localized enum label change 5 | -------------------------------------------------------------------------------- /.changes/v0.25.3.md: -------------------------------------------------------------------------------- 1 | ## v0.25.3 (2020-12-17) 2 | 3 | - Resource store: Force creation of new resource when changing the keyL there is no update action for this available. 4 | -------------------------------------------------------------------------------- /.changes/v1.6.5.md: -------------------------------------------------------------------------------- 1 | ## v1.6.5 (2023-01-24) 2 | 3 | ### Added 4 | * Add new data source data `commercetools_type` to retrieve information 5 | about a type based on the key. 6 | -------------------------------------------------------------------------------- /.changes/0.5.1.md: -------------------------------------------------------------------------------- 1 | ## 0.5.1 (2019-03-20) 2 | 3 | - Resource tax_category_rate: Fix import existing instance 4 | - Resource tax_category_rate: Fix tax rate edge case for 0 amount 5 | -------------------------------------------------------------------------------- /.changes/v1.13.1.md: -------------------------------------------------------------------------------- 1 | ## v1.13.1 - 2024-01-22 2 | ### Fixed 3 | * Added more support for IETF BCP 47 language tags 4 | * Added support for totalPrice target on cart_discount resource 5 | -------------------------------------------------------------------------------- /.changes/0.4.1.md: -------------------------------------------------------------------------------- 1 | ## 0.4.1 (2019-01-28) 2 | ### Added 3 | * **New resource:** `commercetools_shipping_zone` 4 | 5 | ### Fixed 6 | * Fix resource\_type attribute label not mapping correctly 7 | -------------------------------------------------------------------------------- /.changes/v0.24.0.md: -------------------------------------------------------------------------------- 1 | ## v0.24.0 (2020-11-13) 2 | 3 | - Resource store: Add `supply_channels` field 4 | - Resource tax_category_rate: Handle non-existing tax rates when refreshing state 5 | -------------------------------------------------------------------------------- /.changes/v1.1.0.md: -------------------------------------------------------------------------------- 1 | ## v1.1.0 (2022-06-01) 2 | 3 | - Fix out of bounds error in the commercetools_type resource (#241) 4 | - Handle changes to access_secret in api_extension resource (#243) 5 | -------------------------------------------------------------------------------- /.changes/v1.15.1.md: -------------------------------------------------------------------------------- 1 | ## v1.15.1 - 2024-09-03 2 | ### Fixed 3 | * Fixed shipping_zone_rate freeAbove handling when empty 4 | ### Dependency 5 | * chore(deps): bump the go group with 5 updates 6 | -------------------------------------------------------------------------------- /.changes/v1.6.9.md: -------------------------------------------------------------------------------- 1 | ## v1.6.9 (2023-02-27) 2 | ### Fixed 3 | * `resource_subscription` Fix handling of missing subscriptions available in 4 | the state but not in commercetools. (#345) 5 | -------------------------------------------------------------------------------- /internal/acctest/utils.go: -------------------------------------------------------------------------------- 1 | package acctest 2 | 3 | import "github.com/google/uuid" 4 | 5 | func IsValidUUID(value string) error { 6 | _, err := uuid.Parse(value) 7 | return err 8 | } 9 | -------------------------------------------------------------------------------- /.changes/0.17.0.md: -------------------------------------------------------------------------------- 1 | ## 0.17.0 (2019-08-06) 2 | 3 | - Resource api_extension: Update Extension resource to add `timeout_in_ms` (#80) 4 | - Resource shipping_method: Add `predicate` field (#82) 5 | -------------------------------------------------------------------------------- /.changes/0.16.0.md: -------------------------------------------------------------------------------- 1 | ## 0.16.0 (2019-07-22) 2 | 3 | - Resource project: Add support for setting the externalOAuth field (#73) 4 | - Resource state: Add support for the StateRole Return item (#77) 5 | -------------------------------------------------------------------------------- /.changes/0.7.0.md: -------------------------------------------------------------------------------- 1 | ## 0.7.0 (2019-05-08) 2 | ### Added 3 | - **New resource:** `commercetools_store` **This is an alpha Commercetools resource** 4 | 5 | ### Fixed 6 | - Use latest commercetools Go SDK 7 | -------------------------------------------------------------------------------- /.changes/v0.23.0.md: -------------------------------------------------------------------------------- 1 | ## v0.23.0 (2020-08-28) 2 | 3 | - New tag naming scheme (prefix with v) to conform to terraform repository requirements 4 | - Update terraform-plugin-sdk for compatibility with terraform 0.13 5 | -------------------------------------------------------------------------------- /.changes/v1.6.6.md: -------------------------------------------------------------------------------- 1 | ## v1.6.6 (2023-01-24) 2 | ### Fixed 3 | * `resource_subscription` Remove requirement to specify `access_key` when 4 | the `access_secret` is defined. The latter is only needed for EventGrid. 5 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import ( 7 | // document generation 8 | 9 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" 10 | ) 11 | -------------------------------------------------------------------------------- /.changes/0.1.0.md: -------------------------------------------------------------------------------- 1 | ## 0.1.0 (2018-09-14) 2 | ### Added 3 | * **New resource:** `commercetools_api_extension` 4 | * **New resource:** `commercetools_subscription` 5 | * **New resource:** `commercetools_project_settings` 6 | -------------------------------------------------------------------------------- /.changes/v1.12.0.md: -------------------------------------------------------------------------------- 1 | ## v1.12.0 - 2023-10-06 2 | ### Added 3 | * Add new resource `commercetools_product_selection` 4 | * Add property `product_selection` to `commercetools_store` for binding product selections to stores 5 | -------------------------------------------------------------------------------- /.changes/v1.14.1.md: -------------------------------------------------------------------------------- 1 | ## v1.14.1 - 2024-02-09 2 | ### Fixed 3 | * Properly handle resource not found errors for `subscription`, `state`, `state_transitions`, `product_selection`, `attribute_group` and `associate_role`. 4 | -------------------------------------------------------------------------------- /.changes/v1.6.8.md: -------------------------------------------------------------------------------- 1 | ## v1.6.8 (2023-02-02) 2 | ### Fixed 3 | * `resource_subscription` Fix removing requirement to specify `access_key` when 4 | the `access_secret` is defined. The latter is only needed for EventGrid. 5 | -------------------------------------------------------------------------------- /examples/resources/commercetools_custom_object/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_custom_object" "my-custom-object" { 2 | container = "my-container" 3 | key = "my-key" 4 | value = jsonencode(10) 5 | } 6 | -------------------------------------------------------------------------------- /.changes/0.6.0.md: -------------------------------------------------------------------------------- 1 | ## 0.6.0 (2019-04-26) 2 | 3 | - **New resource:** `commercetools_shipping_method` 4 | - **New resource:** `commercetools_shipping_zone_rate`, *Subject to changes, tiers/validation is not yet implemented* 5 | -------------------------------------------------------------------------------- /examples/resources/commercetools_api_client/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_api_client" "my-api-client" { 2 | name = "My API Client" 3 | scope = ["manage_orders:my-ct-project-key", "manage_payments:my-ct-project-key"] 4 | } 5 | -------------------------------------------------------------------------------- /.changes/0.15.0.md: -------------------------------------------------------------------------------- 1 | ## 0.15.0 (2019-07-16) 2 | 3 | - Use new Commercetools Go SDK definitions (main change is auto generated 4 | services, most CRUD actions are renamed) 5 | - Fix Goreleaser not putting version number in released binary 6 | -------------------------------------------------------------------------------- /.changes/0.22.0.md: -------------------------------------------------------------------------------- 1 | ## 0.22.0 (2020-07-20) 2 | 3 | - Resource store: Add `distribution_channels` field 4 | - Update commercetools-go-sdk dependency to v0.2.0. This version now properly 5 | handles oauth2 authentication failures (#117) 6 | -------------------------------------------------------------------------------- /.changes/v1.6.4.md: -------------------------------------------------------------------------------- 1 | ## v1.6.4 (2023-01-23) 2 | 3 | - `resource_subscription` fix issue in the state upgrader when upgrading 4 | from schema version 0 to 1. 5 | - `resource_state` fix handling of both the name and description attributes. 6 | -------------------------------------------------------------------------------- /internal/utils/container.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/labd/commercetools-go-sdk/platform" 5 | ) 6 | 7 | type ProviderData struct { 8 | Client *platform.ByProjectKeyRequestBuilder 9 | Mutex *MutexKV 10 | } 11 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_product_type.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_product_type"}} 2 | resource "commercetools_product_type" "test" { 3 | key = "product-type" 4 | name = "Product Type" 5 | } 6 | {{end}} -------------------------------------------------------------------------------- /examples/resources/commercetools_tax_category/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_tax_category" "my-tax-category" { 2 | key = "my-tax-category-key" 3 | name = "Standard tax category" 4 | description = "Example category" 5 | } 6 | -------------------------------------------------------------------------------- /.changes/v0.30.0.md: -------------------------------------------------------------------------------- 1 | ## v0.30.0 (2021-08-04) 2 | 3 | - Resource project: Add `shipping_rate_input_type` setting to enable tiered pricing for a project 4 | - Resource shipping_zone_rate: Add `shipping_rate_price_tier` setting to set up tiered pricing 5 | 6 | -------------------------------------------------------------------------------- /.changes/0.5.0.md: -------------------------------------------------------------------------------- 1 | ## 0.5.0 (2019-03-19) 2 | 3 | - **New resource** `commercetools_tax_category_rate` 4 | - Resource tax_category: removed `rate` in favour of `commercetools_tax_category_rate` 5 | - Resource shipping_zone: Fix add/remove location logic. 6 | -------------------------------------------------------------------------------- /.changes/v1.7.0.md: -------------------------------------------------------------------------------- 1 | ## v1.7.0 (2023-05-22) 2 | ### Fixed 3 | * fix: remove duplicate project_key in example. (#350) 4 | * chore: update to latest packages 5 | * fix: set `URI` for destination of EventGrid (#364) 6 | * fix: improve README for local run (#370) 7 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_store.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_store"}} 2 | resource "commercetools_store" "{{.resource_name}}" { 3 | key = "{{.resource_key}}" 4 | {{if .custom}}{{template "custom" .}}{{end}} 5 | } 6 | {{end}} -------------------------------------------------------------------------------- /.changes/v1.6.1.md: -------------------------------------------------------------------------------- 1 | ## v1.6.1 (2023-01-11) 2 | 3 | - `resource_project_settings` fix validation of values for the 4 | `delete_days_after_creation` field (should be between 1-90) 5 | - `resource_subscription` fix errors when the `format` block was missing (#331) 6 | 7 | -------------------------------------------------------------------------------- /.changes/v0.28.0.md: -------------------------------------------------------------------------------- 1 | ## v0.28.0 (2021-04-08) 2 | 3 | - **New resource:** `commercetools_category` 4 | - Resource API Extension: Removed unused `azure_functions` type 5 | - Add CheckDestroy funcs to all tests 6 | - Add TFDocs documentation parallel to readthedocs documentation 7 | -------------------------------------------------------------------------------- /.changes/v1.6.2.md: -------------------------------------------------------------------------------- 1 | ## v1.6.2 (2023-01-11) 2 | 3 | - `resource_state` rewrite both the `state` and `state_transitions` resource 4 | and move the code to the new plugin-framework. This should fix some related 5 | to mismatching version values by always refreshing these. (#333) 6 | -------------------------------------------------------------------------------- /examples/resources/commercetools_channel/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_channel" "my-channel" { 2 | key = "my-channel-key" 3 | roles = ["ProductDistribution"] 4 | name = { 5 | nl-NL = "Channel" 6 | } 7 | description = { 8 | nl-NL = "Channel" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.changes/v1.4.4.md: -------------------------------------------------------------------------------- 1 | ## v1.4.4 (2022-09-23) 2 | 3 | - Additional fixes to setting custom field values on supported resources. (#303) 4 | - `resource_api_extension` Fix handling masked values for `azure_authentication` (#306) 5 | - `resource_subscription` Fix handling masked values for `event_grid` (#306) 6 | -------------------------------------------------------------------------------- /.changes/v1.5.1.md: -------------------------------------------------------------------------------- 1 | ## v1.5.1 (2022-10-04) 2 | 3 | - `resource_state_transitions` fix error when we tried to set the transitions 4 | value to a value already set in commercetools, causing an error. See #312 5 | - `resource_state_transitions` add support for importing existing state 6 | transitions 7 | -------------------------------------------------------------------------------- /.changes/v1.6.3.md: -------------------------------------------------------------------------------- 1 | ## v1.6.3 (2023-01-20) 2 | 3 | - `resource_subscription` fix the `GoogleCloudPubSub` integration where the 4 | wrong topic value was used (#337). 5 | - `resource_project_settings` fix continous terraform updates for the 6 | `shipping_rate_cart_classification_value` block (#335) 7 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_customer_group.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_customer_group"}} 2 | resource "commercetools_customer_group" "{{.resource_name}}" { 3 | key = "{{.resource_key}}" 4 | name = "Test Customer Group" 5 | {{if .custom}}{{template "custom" .}}{{end}} 6 | } 7 | {{end}} -------------------------------------------------------------------------------- /.changes/v1.10.1.md: -------------------------------------------------------------------------------- 1 | ## v1.10.1 - 2023-08-17 2 | ### Fixed 3 | * Fixed forced replacement issue in commercetools_category when name or key change 4 | * Fixed custom field encoding with non-string field values 5 | * Added correct handling of azure_authentication to prevent unnecessary updates 6 | * Added custom field for cart discount 7 | -------------------------------------------------------------------------------- /.changes/v1.17.0.md: -------------------------------------------------------------------------------- 1 | ## v1.17.0 - 2024-11-01 2 | ### Added 3 | * Added handling of product search and customer search indexes 4 | * Added stores to cart discounts 5 | ### Dependency 6 | * chore(deps): bump labd/changie-release-action from 0.4.0 to 0.5.0 in the github-actions group 7 | * chore(deps): bump the go group with 4 updates 8 | -------------------------------------------------------------------------------- /.changes/v0.26.0.md: -------------------------------------------------------------------------------- 1 | ## v0.26.0 (2021-01-12) 2 | 3 | - **New resource** `commercetools_customer_group` (#141) 4 | - Resource type: Allow updating the label of an existing Enum value 5 | - Resource type: Add support to update a set of enum in a custom type 6 | - Fix ProductType and DiscountCode tests with real commercetools environment 7 | -------------------------------------------------------------------------------- /.changes/v0.27.0.md: -------------------------------------------------------------------------------- 1 | ## v0.27.0 (2021-03-01) 2 | 3 | - Resource project: Add `carts` field with countryTaxRateFallBackEnabled setting 4 | - Resource project: Fix updating of `messages` field to explicitly set `false` when deleted or set to false in terraform instead of relying on commercetools default settings for project in these scenarios 5 | -------------------------------------------------------------------------------- /examples/resources/commercetools_customer_group/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_customer_group" "standard" { 2 | key = "my-customer-group-key" 3 | name = "Standard Customer Group" 4 | } 5 | 6 | resource "commercetools_customer_group" "golden" { 7 | key = "my-customer-group-key" 8 | name = "Golden Customer Group" 9 | } 10 | -------------------------------------------------------------------------------- /.changes/header.tpl.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), 6 | and is generated by [Changie](https://github.com/miniscruff/changie). 7 | -------------------------------------------------------------------------------- /examples/resources/commercetools_shipping_zone/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_shipping_zone" "de-us" { 2 | key = "some-key" 3 | name = "DE and US" 4 | description = "Germany and US" 5 | location { 6 | country = "DE" 7 | } 8 | location { 9 | country = "US" 10 | state = "Nevada" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/acctest/provider_test.go: -------------------------------------------------------------------------------- 1 | package acctest 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/labd/terraform-provider-commercetools/internal/provider" 9 | ) 10 | 11 | func TestProvider(t *testing.T) { 12 | p := provider.New("version") 13 | 14 | assert.NotNil(t, p) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support request 3 | about: When you have a generic question 4 | title: '' 5 | labels: question, triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | Describe your question here. Please provide as much detail as possible. If you 11 | have a specific use case, please provide that as well. 12 | ``` 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform-provider-commercetools 2 | terraform-provider-commercetools_* 3 | *.backup 4 | /dist/* 5 | coverage.txt 6 | 7 | # Terraform files 8 | .terraform/ 9 | terraform.* 10 | crash.log 11 | 12 | 13 | # Local files 14 | /local/* 15 | !/local/*.example 16 | 17 | # Go modules 18 | vendor/ 19 | 20 | /.idea 21 | /.env 22 | /go.work* 23 | -------------------------------------------------------------------------------- /internal/utils/hcl.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | ) 7 | 8 | func HCLTemplate(data string, params map[string]any) string { 9 | var out bytes.Buffer 10 | tmpl := template.Must(template.New("hcl").Parse(data)) 11 | err := tmpl.Execute(&out, params) 12 | if err != nil { 13 | panic(err) 14 | } 15 | return out.String() 16 | } 17 | -------------------------------------------------------------------------------- /.changes/v1.19.0.md: -------------------------------------------------------------------------------- 1 | ## v1.19.0 - 2025-01-10 2 | ### Added 3 | * Added generic custom field support to all newer resources 4 | ### Fixed 5 | * This adds a sorting of the permissions we get from the commercetools API response, so that it's identical to the one from the plan. 6 | * Fixed broken links in documentation 7 | ### Dependency 8 | * chore(deps): bump the go group with 3 updates 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: When you are missing a specific feature 4 | title: '' 5 | labels: enhancement, triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | Describe the feature you would like to see implemented. Please provide as much 11 | detail as possible. If you have a specific use case, please provide that as 12 | well. 13 | -------------------------------------------------------------------------------- /commercetools/helpers_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | ) 7 | 8 | func hclTemplate(data string, params map[string]any) string { 9 | var out bytes.Buffer 10 | tmpl := template.Must(template.New("hcl").Parse(data)) 11 | err := tmpl.Execute(&out, params) 12 | if err != nil { 13 | panic(err) 14 | } 15 | return out.String() 16 | } 17 | -------------------------------------------------------------------------------- /examples/data-sources/commercetools_type/data-source.tf: -------------------------------------------------------------------------------- 1 | data "commercetools_type" "existing_type" { 2 | key = "test" 3 | } 4 | 5 | resource "commercetools_channel" "test" { 6 | key = "test" 7 | roles = ["ProductDistribution"] 8 | custom { 9 | type_id = data.commercetools_type.existing_type.id 10 | fields = { 11 | "my-field" = "foobar" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.changes/v1.9.0.md: -------------------------------------------------------------------------------- 1 | ## v1.9.0 - 2023-08-09 2 | ### Added 3 | * **commercertools_api_extension:** added googlecloudfunction as option 4 | * **commercertools_cart_discount:** added multiBuyLineItems and multiBuyCustomLineItems 5 | * **commercertools_store:** added countries field 6 | ### Fixed 7 | * **commercertools_type:** doc changes to source files 8 | * **commercertools_cart_discount:** fixed money list issue 9 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - asciicheck 5 | - bodyclose 6 | - contextcheck 7 | - cyclop 8 | - errcheck 9 | - exhaustive 10 | - exportloopref 11 | - forcetypeassert 12 | - goimports 13 | - gosimple 14 | - govet 15 | - ineffassign 16 | - predeclared 17 | - staticcheck 18 | - tenv 19 | - typecheck 20 | - unused 21 | - whitespace 22 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_category.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_category"}} 2 | resource "commercetools_category" "{{.resource_name}}" { 3 | key = "{{.resource_key}}" 4 | name = { 5 | en = "commercetools category name" 6 | } 7 | slug = { 8 | en = "commercetools_category" 9 | } 10 | {{if .custom}}{{template "custom" .}}{{end}} 11 | } 12 | {{end}} -------------------------------------------------------------------------------- /.changes/v1.13.0.md: -------------------------------------------------------------------------------- 1 | ## v1.13.0 - 2023-11-16 2 | ### Added 3 | * update to latest commercetools go sdk 4 | * Code cleanup 5 | ### Fixed 6 | * Added additional check for unset values on discount_code resource 7 | * ensure an empty slice is set if subscription messages or changes are removed 8 | * fixed check on project ExternalOAuth setting when processing state and plan differences 9 | * Updated deprecated code 10 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_channel.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_channel"}} 2 | resource "commercetools_channel" {{.resource_name}} { 3 | key = "{{.resource_key}}" 4 | roles = ["ProductDistribution"] 5 | name = { 6 | nl-NL = "Channel" 7 | } 8 | description = { 9 | nl-NL = "Channel" 10 | } 11 | {{if .custom}}{{template "custom" .}}{{end}} 12 | } 13 | {{end}} 14 | -------------------------------------------------------------------------------- /.changes/v1.14.3.md: -------------------------------------------------------------------------------- 1 | ## v1.14.3 - 2024-05-17 2 | ### Dependency 3 | * chore(deps): bump the go group across 1 directory with 9 updates 4 | * chore(deps): bump the github-actions group across 1 directory with 7 updates 5 | * chore(deps): bump golang.org/x/net from 0.22.0 to 0.23.0 6 | * chore(deps): bump the go group with 3 updates 7 | * chore(deps): bump actions/add-to-project from 0.5.0 to 1.0.1 in the github-actions group 8 | -------------------------------------------------------------------------------- /examples/resources/commercetools_attribute_group/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_attribute_group" "my-attribute-group" { 2 | key = "my-attribute-group-key" 3 | name = { 4 | en = "my-attribute-group-name" 5 | } 6 | description = { 7 | en = "my-attribute-group-description" 8 | } 9 | 10 | attribute { 11 | key = "attribute-key-1" 12 | } 13 | 14 | attribute { 15 | key = "attribute-key-2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.testEnvVars": { 3 | "TF_ACC": "1", 4 | "TF_LOG": "INFO", 5 | "CTP_DEBUG": "1", 6 | "CTP_CLIENT_ID": "unittest", 7 | "CTP_CLIENT_SECRET": "x", 8 | "CTP_PROJECT_KEY": "unittest", 9 | "CTP_SCOPES": "manage_project:projectkey", 10 | "CTP_API_URL": "http://localhost:8989", 11 | "CTP_AUTH_URL": "http://localhost:8989", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.changes/0.21.0.md: -------------------------------------------------------------------------------- 1 | ## 0.21.0 (2020-02-27) 2 | 3 | - Provider arguments (`client_id`, `client_secret`, `project_key`, 4 | `scopes`, `token_url` and `api_url`) are now required 5 | - Resource api_client: Updating now recreates the resource since 6 | it cannot be updated. 7 | - Don't retry various calls if Commercetools returns an error (resulting in 8 | unnecessary retries/waiting times). 9 | - Dependency update: use terraform-plugin-sdk 1.7.0 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.1-stretch AS build-env 2 | WORKDIR /terraform-provider 3 | 4 | ADD . /terraform-provider 5 | 6 | ENV GOPROXY=https://proxy.golang.org 7 | RUN go mod download 8 | RUN go build -o terraform-provider-commercetools 9 | 10 | # final stage 11 | FROM hashicorp/terraform:0.12.9 12 | 13 | RUN apk add libc6-compat 14 | 15 | WORKDIR /config 16 | 17 | COPY --from=build-env /terraform-provider/terraform-provider-commercetools /bin 18 | -------------------------------------------------------------------------------- /.changes/v1.2.0.md: -------------------------------------------------------------------------------- 1 | ## v1.2.0 (2022-06-15) 2 | 3 | - Fix shipping_zone locations ordering by switching to a set instead of a list 4 | of locations (#259) 5 | - Add aliases for destination and platform on subscription and extension 6 | resources (#245, #247, #251) 7 | - Add condition field to api extension resource 8 | - Add support for terraform import on the api_extension resource 9 | - Improve error handling, show errors returned by commercetools in terraform 10 | output. 11 | -------------------------------------------------------------------------------- /.changes/v1.18.0.md: -------------------------------------------------------------------------------- 1 | ## v1.18.0 - 2024-12-03 2 | ### Added 3 | * `resource_api_client` add support for setting and managing token validity via Terraform. 4 | * Added the attribute ShippingMethod.active to the resource shipping_method. 5 | ### Dependency 6 | * chore(deps): bump the go group with 6 updates 7 | * chore(deps): bump actions/add-to-project from 0.5.0 to 1.0.2 in the github-actions group 8 | * chore(deps): bump the go group with 6 updates 9 | * chore(deps): bump codecov/codecov-action from 4 to 5 in the github-actions group 10 | -------------------------------------------------------------------------------- /.github/pull-request-template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | Fixes # 14 | 15 | ### NEW FEATURES | UPGRADE NOTES | ENHANCEMENTS | BUG FIXES | EXPERIMENTS 16 | 17 | 26 | 27 | - 28 | -------------------------------------------------------------------------------- /examples/resources/commercetools_shipping_method/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_tax_category" "some-tax-category" { 2 | key = "some-tax-category-key" 3 | name = "some test cateogry" 4 | description = "test category" 5 | } 6 | 7 | resource "commercetools_shipping_method" "standard" { 8 | key = "standard-key" 9 | name = "Standard tax category" 10 | description = "Standard tax category" 11 | is_default = true 12 | tax_category_id = commercetools_tax_category.some-tax-category.id 13 | predicate = "1 = 1" 14 | } 15 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | day: tuesday 8 | commit-message: 9 | prefix: "chore(deps)" 10 | groups: 11 | go: 12 | patterns: 13 | - "*" 14 | 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "monthly" 19 | day: tuesday 20 | commit-message: 21 | prefix: "chore(deps)" 22 | groups: 23 | github-actions: 24 | patterns: 25 | - "*" 26 | -------------------------------------------------------------------------------- /examples/resources/commercetools_subscription/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_subscription" "my-sqs-subscription" { 2 | key = "my-sqs-subscription-key" 3 | destination { 4 | type = "SQS" 5 | queue_url = aws_sqs_queue.your-queue.id 6 | access_key = aws_iam_access_key.ct.id 7 | access_secret = aws_iam_access_key.ct.secret 8 | region = "eu-west-1" 9 | } 10 | 11 | changes { 12 | resource_type_ids = ["product"] 13 | } 14 | 15 | message { 16 | resource_type_id = "product" 17 | types = ["ProductPublished", "ProductCreated"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.changes/0.3.0.md: -------------------------------------------------------------------------------- 1 | ## 0.3.0 (2018-12-10) 2 | ### Added 3 | - **New resource:** `commercetools_channel` 4 | - **New resource:** `commercetools_tax_category` 5 | 6 | ### Fixed 7 | - Resource product_type: made `attribute` elements optional 8 | - Resource product_type: Validate/protect `required` element on Product type attribute 9 | - Resource product_type: Avoid `changeAttributeOrder` update action when new attribute gets added 10 | - Resource product_type: Added support for Nested types 11 | - Resource type: Validate/protect `required` element on Type attribute 12 | - Resource type: Avoid `changeAttributeOrder` update action when new attribute gets added 13 | -------------------------------------------------------------------------------- /templates/resources/shipping_zone_rate.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" 3 | subcategory: "" 4 | description: |- 5 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }} 6 | --- 7 | 8 | # {{.Name}} ({{.Type}}) 9 | 10 | {{ .Description | trimspace }} 11 | 12 | ## Example Usage 13 | 14 | {{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} 15 | 16 | {{ .SchemaMarkdown | trimspace }} 17 | 18 | ## Import 19 | 20 | Import is supported using the following syntax: 21 | 22 | ```shell 23 | terraform import commercetools_shipping_zone_rate.my-shipping-zone-rate {my-shipping-method-id}@{my-shipping-zone-id}@{currency} 24 | ``` 25 | -------------------------------------------------------------------------------- /.changes/v1.4.0.md: -------------------------------------------------------------------------------- 1 | ## v1.4.0 (2022-08-18) 2 | 3 | - `resource_product_discount` new resource to manage product discounts (#266) 4 | - `resource_subscription`: Fix a bug where remove the `changes` or `messages` 5 | from the resource was resulting in an invalid request. (#138) 6 | - `resource_shipping_zone_rate` Fix persisting the shipping rate tiers in the 7 | terraform state (#184) 8 | - `resource_api_extension` Fix handling of retrieving secrets from 9 | commercetools (#284) 10 | - `resource_subscription` Fix handling changes in both `changes` and `messages` 11 | attributes (#138) 12 | - Fix setting custom fields on the various resources when the type is not a 13 | string (#289) 14 | -------------------------------------------------------------------------------- /examples/data-sources/commercetools_state/data-source.tf: -------------------------------------------------------------------------------- 1 | # The Initial state is a state that is provided in a new commercetools environment by default 2 | data "commercetools_state" "initial_state" { 3 | key = "Initial" 4 | } 5 | 6 | resource "commercetools_state_transitions" "from_created_to_allocated" { 7 | from = data.commercetools_state.initial_state.id 8 | to = [ 9 | commercetools_state.backorder.id, 10 | ] 11 | } 12 | 13 | resource "commercetools_state" "backorder" { 14 | key = "backorder" 15 | type = "LineItemState" 16 | name = { 17 | en = "Back Order", 18 | 19 | } 20 | description = { 21 | en = "Not available - on back order" 22 | } 23 | initial = false 24 | } 25 | -------------------------------------------------------------------------------- /.changie.yaml: -------------------------------------------------------------------------------- 1 | changesDir: .changes 2 | unreleasedDir: unreleased 3 | headerPath: header.tpl.md 4 | changelogPath: CHANGELOG.md 5 | versionExt: md 6 | versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}' 7 | kindFormat: '### {{.Kind}}' 8 | changeFormat: '* {{.Body}}' 9 | kinds: 10 | - label: Added 11 | auto: minor 12 | - label: Changed 13 | auto: major 14 | - label: Deprecated 15 | auto: minor 16 | - label: Removed 17 | auto: major 18 | - label: Fixed 19 | auto: patch 20 | - label: Security 21 | auto: patch 22 | - label: Dependency 23 | auto: patch 24 | newlines: 25 | afterChangelogHeader: 1 26 | beforeChangelogVersion: 1 27 | endOfVersion: 1 28 | envPrefix: CHANGIE_ 29 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_shipping_method.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_shipping_method"}} 2 | resource "commercetools_tax_category" "test_tax_category" { 3 | key = "test-tax-category-key" 4 | name = "test-tax-cateogry" 5 | description = "test tax category" 6 | } 7 | resource "commercetools_shipping_method" "{{.resource_name}}" { 8 | key = "{{.resource_key}}" 9 | name = "Test tax category" 10 | description = "Test tax category" 11 | is_default = true 12 | tax_category_id = commercetools_tax_category.test_tax_category.id 13 | predicate = "1 = 1" 14 | {{if .custom}}{{template "custom" .}}{{end}} 15 | } 16 | {{end}} 17 | -------------------------------------------------------------------------------- /internal/utils/errors_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/labd/commercetools-go-sdk/platform" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestIsResourceNotFoundError(t *testing.T) { 10 | var cases = []struct { 11 | err error 12 | expected bool 13 | }{ 14 | {platform.ErrNotFound, true}, 15 | {platform.ResourceNotFoundError{}, true}, 16 | {platform.ErrorResponse{StatusCode: 404}, true}, 17 | {platform.ErrorResponse{StatusCode: 500}, false}, 18 | {platform.GenericRequestError{StatusCode: 404}, true}, 19 | {platform.GenericRequestError{StatusCode: 500}, false}, 20 | } 21 | 22 | for _, tt := range cases { 23 | assert.Equal(t, tt.expected, IsResourceNotFoundError(tt.err)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.changes/v1.6.0.md: -------------------------------------------------------------------------------- 1 | ## v1.6.0 (2023-01-09) 2 | 3 | - `resource_project_settings` migrate the resource to the new 4 | terraform-plugin-framework. 5 | - `resource_project_settings` add property `delete_days_after_creation` to 6 | the `messages` block (#322) 7 | - `resource_subscription` Support using IAM authentication for SQS and SNS by 8 | making the access_key and access_secret attributes optional. (#316) 9 | - `resource_subscription` migrate the resource to the new 10 | terraform-plugin-framework. 11 | - `resource_subscription` fix handling of changes in the `connection_string` 12 | value when the `AzureServiceBus` is used (#320) 13 | - `resource_api_extension` add support for triggers on `business-unit`, 14 | `quote-request`, `quote`, `staged-quote` (#326) 15 | -------------------------------------------------------------------------------- /.github/workflows/triage.yaml: -------------------------------------------------------------------------------- 1 | name: Triage 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add_to_project: 10 | name: Push issue or PR to board 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: get app token 14 | id: get-app-token 15 | uses: labd/action-gh-app-token@main 16 | with: 17 | app-id: ${{ secrets.RD_APP_ID }} 18 | private-key: ${{ secrets.RD_APP_PRIVATE_KEY }} 19 | installation-id: ${{ secrets.RD_APP_INSTALLATION_ID }} 20 | - name: set to project board 21 | uses: actions/add-to-project@v1.0.2 22 | with: 23 | project-url: https://github.com/orgs/labd/projects/3 24 | github-token: ${{ steps.get-app-token.outputs.app-token }} 25 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_cart_discount.tmpl: -------------------------------------------------------------------------------- 1 | {{define "commercetools_cart_discount"}} 2 | resource "commercetools_cart_discount" "{{.resource_name}}" { 3 | key = "{{.resource_key}}" 4 | name = { 5 | en = "relative name" 6 | } 7 | description = { 8 | en = "relative description" 9 | } 10 | sort_order = "0.9" 11 | predicate = "1=1" 12 | stacking_mode = "Stacking" 13 | requires_discount_code = true 14 | valid_from = "2018-01-02T15:04:05Z" 15 | valid_until = "2019-01-02T15:04:05Z" 16 | target { 17 | type = "lineItems" 18 | predicate = "1=1" 19 | } 20 | value { 21 | type = "relative" 22 | permyriad = 1000 23 | } 24 | {{if .custom}}{{template "custom" .}}{{end}} 25 | } 26 | {{end}} -------------------------------------------------------------------------------- /examples/resources/commercetools_product_selection/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_type" "my-type" { 2 | key = "my-type" 3 | name = { 4 | en = "My type" 5 | nl = "Mijn type" 6 | } 7 | 8 | resource_type_ids = ["product-selection"] 9 | 10 | field { 11 | name = "my-field" 12 | label = { 13 | en = "My field" 14 | nl = "Mijn veld" 15 | } 16 | type { 17 | name = "String" 18 | } 19 | } 20 | } 21 | 22 | resource "commercetools_product_selection" "product-selection-us" { 23 | key = "product-selection-us" 24 | name = { 25 | en = "US Product Selection" 26 | } 27 | mode = "Individual" 28 | 29 | custom { 30 | type_id = commercetools_type.my-type.id 31 | fields = { 32 | my-field = "my-value" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.changes/v1.0.0.md: -------------------------------------------------------------------------------- 1 | ## v1.0.0 (2022-05-23) 2 | 3 | - Use terraform plugin sdk v2. This changed required various changes and should 4 | have made the codebase more robust. 5 | - Fix marshalling the commercetools to terraform state for various resources. 6 | - Move documentation to the terraform registry, see 7 | https://registry.terraform.io/providers/labd/commercetools/latest/docs 8 | - Use Go 1.18 9 | - Add support for AWS EventBridge subscription 10 | - Resource updates: 11 | - project_settings: do case insensitive comparison of the languages, currencies 12 | and countries 13 | - shipping_zone: make the name required 14 | - api_extension: Fix handling of timeout_in_ms when empty 15 | - category: add support for setting external_id 16 | - category: fix empty key being set on creation 17 | 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: When you are running into an issue 4 | title: '' 5 | labels: bug, triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Version information 11 | - **terraform**: _Please specify the version of Terraform you are using._ 12 | - **terraform provider**: _Please specify the version of the provider you are using._ 13 | 14 | ### Describe the bug 15 | A clear and concise description of what the bug is. 16 | 17 | ### To Reproduce 18 | Steps to reproduce the behavior. 19 | 20 | ### Expected behavior 21 | A clear and concise description of what you expected to happen. 22 | 23 | ### Screenshots 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | ### Additional context 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /examples/resources/commercetools_project_settings/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_project_settings" "my-project" { 2 | key = "my-project-key" 3 | name = "My project" 4 | countries = ["NL", "DE", "US", "CA"] 5 | currencies = ["EUR", "USD", "CAD"] 6 | languages = ["nl", "de", "en", "fr-CA"] 7 | external_oauth { 8 | url = "https://example.com/oauth/introspection" 9 | authorization_header = "Bearer secret" 10 | } 11 | messages { 12 | enabled = true 13 | } 14 | carts { 15 | country_tax_rate_fallback_enabled = true 16 | } 17 | shipping_rate_input_type = "CartClassification" 18 | 19 | shipping_rate_cart_classification_value { 20 | key = "Small" 21 | label = { 22 | "en" = "Small" 23 | "nl" = "Klein" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.changes/v1.5.0.md: -------------------------------------------------------------------------------- 1 | ## v1.5.0 (2022-09-30) 2 | 3 | - `resource_state_transitions` New resource to manage transitions between states. 4 | This was previously part of the `resource_state` but that made it imposible 5 | to have recursive transitions. This means that `transitions` attribute is now 6 | removed from the `resource_state` resource. 7 | 8 | example: 9 | ```hcl 10 | // Only allow transition from sale to clearance 11 | resource "commercetools_state_transitions" "transition-1" { 12 | from = commercetools_state.product_for_sale.id 13 | to = [ 14 | commercetools_state.product_clearance.id, 15 | ] 16 | } 17 | ``` 18 | See #86 for more information 19 | - `resource_shipping_zone_rate` Add support for `price_function` when the type 20 | is `CartScore` (#202) 21 | 22 | -------------------------------------------------------------------------------- /commercetools/resource_cart_discount_utils_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 7 | ) 8 | 9 | func testAccCheckCartDiscountDestroy(s *terraform.State) error { 10 | client := getClient(testAccProvider.Meta()) 11 | 12 | for _, rs := range s.RootModule().Resources { 13 | if rs.Type != "commercetools_cart_discount" { 14 | continue 15 | } 16 | response, err := client.CartDiscounts().WithId(rs.Primary.ID).Get().Execute(context.Background()) 17 | if err == nil { 18 | if response != nil && response.ID == rs.Primary.ID { 19 | return fmt.Errorf("cart discount (%s) still exists", rs.Primary.ID) 20 | } 21 | return nil 22 | } 23 | if newErr := checkApiResult(err); newErr != nil { 24 | return newErr 25 | } 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /examples/resources/commercetools_tax_category_rate/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_tax_category" "my-tax-category" { 2 | name = "Standard tax category" 3 | description = "Example category" 4 | } 5 | 6 | resource "commercetools_tax_category_rate" "standard-tax-category-DE" { 7 | tax_category_id = commercetools_tax_category.my-tax-category.id 8 | name = "19% MwSt" 9 | amount = 0.19 10 | included_in_price = false 11 | country = "DE" 12 | sub_rate { 13 | name = "example" 14 | amount = 0.19 15 | } 16 | } 17 | 18 | resource "commercetools_tax_category_rate" "standard-tax-category-NL" { 19 | tax_category_id = commercetools_tax_category.my-tax-category.id 20 | name = "21% BTW" 21 | amount = 0.21 22 | included_in_price = true 23 | country = "NL" 24 | } 25 | -------------------------------------------------------------------------------- /examples/resources/commercetools_discount_code/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_discount_code" "my_discount_code" { 2 | name = { 3 | en = "My Discount code name" 4 | } 5 | description = { 6 | en = "My Discount code description" 7 | } 8 | code = "2" 9 | valid_from = "2020-01-02T15:04:05.000Z" 10 | valid_until = "2021-01-02T15:04:05.000Z" 11 | is_active = true 12 | predicate = "1=1" 13 | max_applications_per_customer = 3 14 | max_applications = 100 15 | groups = ["0", "1"] 16 | cart_discounts = ["cart-discount-id-1", "cart-discount-id-2"] 17 | } 18 | 19 | resource "commercetools_discount_code" "my_discount_code" { 20 | code = "2" 21 | cart_discounts = ["cart-discount-id-1"] 22 | } 23 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/main.tmpl: -------------------------------------------------------------------------------- 1 | {{define "main"}} 2 | {{template "commercetools_product_type"}} 3 | {{template "commercetools_type" .}} 4 | {{if eq .resource_type "commercetools_channel"}}{{template "commercetools_channel" .}}{{end}} 5 | {{if eq .resource_type "commercetools_cart_discount"}}{{template "commercetools_cart_discount" .}}{{end}} 6 | {{if eq .resource_type "commercetools_category"}}{{template "commercetools_category" .}}{{end}} 7 | {{if eq .resource_type "commercetools_customer_group"}}{{template "commercetools_customer_group" .}}{{end}} 8 | {{if eq .resource_type "commercetools_discount_code"}}{{template "commercetools_discount_code" .}}{{end}} 9 | {{if eq .resource_type "commercetools_shipping_method"}}{{template "commercetools_shipping_method" .}}{{end}} 10 | {{if eq .resource_type "commercetools_store"}}{{template "commercetools_store" .}}{{end}} 11 | {{end}} -------------------------------------------------------------------------------- /internal/customtypes/localized_string_test.go: -------------------------------------------------------------------------------- 1 | package customtypes 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/attr" 7 | "github.com/hashicorp/terraform-plugin-framework/types" 8 | "github.com/labd/commercetools-go-sdk/platform" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestLocalizedString(t *testing.T) { 13 | val := NewLocalizedStringValue(map[string]attr.Value{ 14 | "nl": types.StringValue("foobar"), 15 | }) 16 | 17 | result := val.ValueLocalizedString() 18 | expected := platform.LocalizedString{ 19 | "nl": "foobar", 20 | } 21 | assert.Equal(t, expected, result) 22 | } 23 | 24 | func TestLocalizedStringUnknown(t *testing.T) { 25 | val := NewLocalizedStringNull() 26 | 27 | result := val.ValueLocalizedString() 28 | expected := platform.LocalizedString(nil) 29 | assert.Equal(t, expected, result) 30 | } 31 | -------------------------------------------------------------------------------- /examples/resources/commercetools_state/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_state" "review_unreviewed" { 2 | key = "review-unreviewed" 3 | type = "ReviewState" 4 | name = { 5 | en = "Unreviewed" 6 | } 7 | description = { 8 | en = "Not reviewed yet" 9 | } 10 | initial = true 11 | roles = ["ReviewIncludedInStatistics"] 12 | } 13 | 14 | resource "commercetools_state" "product_for_sale" { 15 | key = "product-for-sale" 16 | type = "ProductState" 17 | name = { 18 | en = "For Sale" 19 | } 20 | description = { 21 | en = "Regularly stocked product." 22 | } 23 | initial = true 24 | } 25 | 26 | resource "commercetools_state" "product_clearance" { 27 | key = "product-clearance" 28 | type = "ProductState" 29 | name = { 30 | en = "On Clearance" 31 | } 32 | description = { 33 | en = "The product line will not be ordered again." 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/data-sources/type.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_type Data Source - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Fetches type information 7 | --- 8 | 9 | # commercetools_type (Data Source) 10 | 11 | Fetches type information 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "commercetools_type" "existing_type" { 17 | key = "test" 18 | } 19 | 20 | resource "commercetools_channel" "test" { 21 | key = "test" 22 | roles = ["ProductDistribution"] 23 | custom { 24 | type_id = data.commercetools_type.existing_type.id 25 | fields = { 26 | "my-field" = "foobar" 27 | } 28 | } 29 | } 30 | ``` 31 | 32 | 33 | ## Schema 34 | 35 | ### Required 36 | 37 | - `key` (String) Key of the custom type 38 | 39 | ### Read-Only 40 | 41 | - `id` (String) ID of the custom type 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | goreleaser: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version-file: go.mod 18 | 19 | - name: Import GPG key 20 | id: import_gpg 21 | uses: paultyng/ghaction-import-gpg@v2.1.0 22 | env: 23 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 24 | PASSPHRASE: ${{ secrets.PASSPHRASE }} 25 | 26 | - name: Run GoReleaser 27 | uses: goreleaser/goreleaser-action@v6 28 | with: 29 | version: latest 30 | args: release --clean 31 | env: 32 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_discount_code.tmpl: -------------------------------------------------------------------------------- 1 | 2 | {{define "commercetools_discount_code"}} 3 | resource "commercetools_cart_discount" "commercetools_cart_discount_test" { 4 | key = "commercetools_cart_discount_test" 5 | name = { 6 | en = "relative name" 7 | } 8 | description = { 9 | en = "relative description" 10 | } 11 | sort_order = "0.9" 12 | predicate = "1=1" 13 | stacking_mode = "Stacking" 14 | requires_discount_code = true 15 | valid_from = "2018-01-02T15:04:05Z" 16 | valid_until = "2019-01-02T15:04:05Z" 17 | target { 18 | type = "lineItems" 19 | predicate = "1=1" 20 | } 21 | value { 22 | type = "relative" 23 | permyriad = 1000 24 | } 25 | } 26 | 27 | resource "commercetools_discount_code" "{{.resource_name}}" { 28 | code = "2" 29 | cart_discounts = [commercetools_cart_discount.commercetools_cart_discount_test.id] 30 | {{if .custom}}{{template "custom" .}}{{end}} 31 | } 32 | {{end}} -------------------------------------------------------------------------------- /examples/resources/commercetools_category/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_category" "my-category" { 2 | key = "my-category-key" 3 | 4 | name = { 5 | en = "My category" 6 | } 7 | description = { 8 | en = "Standard description" 9 | } 10 | slug = { 11 | en = "my_category" 12 | } 13 | meta_title = { 14 | en = "Meta title" 15 | } 16 | } 17 | 18 | resource "commercetools_category" "my-second-category" { 19 | key = "my-category-key" 20 | 21 | name = { 22 | en = "Second category" 23 | } 24 | description = { 25 | en = "Standard description" 26 | } 27 | parent = commercetools_category.my-category.id 28 | slug = { 29 | en = "my_second_category" 30 | } 31 | meta_title = { 32 | en = "Meta title" 33 | } 34 | assets { 35 | key = "some_key" 36 | name = { 37 | en = "Image name" 38 | } 39 | description = { 40 | en = "Image description" 41 | } 42 | sources { 43 | uri = "https://example.com/test.jpg" 44 | key = "image" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/resources/commercetools_state_transitions/resource.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "commercetools_state" "product_for_sale" { 3 | key = "product-for-sale" 4 | type = "ProductState" 5 | name = { 6 | en = "For Sale" 7 | } 8 | description = { 9 | en = "Regularly stocked product." 10 | } 11 | initial = true 12 | } 13 | 14 | resource "commercetools_state" "product_clearance" { 15 | key = "product-clearance" 16 | type = "ProductState" 17 | name = { 18 | en = "On Clearance" 19 | } 20 | description = { 21 | en = "The product line will not be ordered again." 22 | } 23 | } 24 | 25 | 26 | // Only allow transition from sale to clearance 27 | resource "commercetools_state_transitions" "transition_1" { 28 | from = commercetools_state.product_for_sale.id 29 | to = [ 30 | commercetools_state.product_clearance.id, 31 | ] 32 | } 33 | 34 | // Disable transitions from product clearance to other 35 | resource "commercetools_state_transitions" "transition_2" { 36 | from = commercetools_state.product_clearance.id 37 | to = [] 38 | } 39 | -------------------------------------------------------------------------------- /internal/models/models.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-framework/types" 5 | "github.com/labd/commercetools-go-sdk/platform" 6 | 7 | "github.com/labd/terraform-provider-commercetools/internal/customtypes" 8 | "github.com/labd/terraform-provider-commercetools/internal/utils" 9 | ) 10 | 11 | type CustomFieldLocalizedEnumValue struct { 12 | Key types.String `tfsdk:"key"` 13 | Label customtypes.LocalizedStringValue `tfsdk:"label"` 14 | } 15 | 16 | func (c CustomFieldLocalizedEnumValue) ToNative() platform.CustomFieldLocalizedEnumValue { 17 | return platform.CustomFieldLocalizedEnumValue{ 18 | Key: c.Key.ValueString(), 19 | Label: c.Label.ValueLocalizedString(), 20 | } 21 | } 22 | 23 | func NewCustomFieldLocalizedEnumValue(s platform.CustomFieldLocalizedEnumValue) CustomFieldLocalizedEnumValue { 24 | label := utils.FromOptionalLocalizedString(&s.Label) 25 | result := CustomFieldLocalizedEnumValue{ 26 | Key: types.StringValue(s.Key), 27 | Label: label, 28 | } 29 | 30 | return result 31 | } 32 | -------------------------------------------------------------------------------- /examples/resources/commercetools_product_discount/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_product_discount" "my-product-discount" { 2 | key = "my-product-discount-key" 3 | name = { 4 | en = "Product discount name" 5 | } 6 | description = { 7 | en = "Product discount description" 8 | } 9 | predicate = "1=1" 10 | sort_order = "0.9" 11 | is_active = true 12 | valid_from = "2018-01-02T15:04:05Z" 13 | valid_until = "2019-01-02T15:04:05Z" 14 | 15 | value { 16 | type = "relative" 17 | permyriad = 1000 18 | } 19 | } 20 | 21 | resource "commercetools_product_discount" "my-product-discount-absolute" { 22 | key = "my-product-discount-absolute-key" 23 | name = { 24 | en = "Product discount name" 25 | } 26 | description = { 27 | en = "Product discount description" 28 | } 29 | predicate = "1=1" 30 | sort_order = "0.9" 31 | is_active = true 32 | valid_from = "2018-01-02T15:04:05Z" 33 | valid_until = "2019-01-02T15:04:05Z" 34 | 35 | value { 36 | type = "absolute" 37 | money { 38 | currency_code = "EUR" 39 | cent_amount = 500 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-changie.yaml: -------------------------------------------------------------------------------- 1 | name: Dependabot add changie file 2 | on: 3 | pull_request: 4 | types: [opened] 5 | 6 | permissions: 7 | pull-requests: write 8 | issues: write 9 | repository-projects: write 10 | contents: write 11 | 12 | jobs: 13 | dependabot-changie: 14 | runs-on: ubuntu-latest 15 | if: github.actor == 'dependabot[bot]' 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Fetch Dependabot metadata 21 | id: dependabot-metadata 22 | uses: dependabot/fetch-metadata@v2 23 | with: 24 | github-token: "${{ secrets.GITHUB_TOKEN }}" 25 | 26 | - name: Create change file 27 | uses: miniscruff/changie-action@v2 28 | with: 29 | version: latest 30 | args: new --body "${{ github.event.pull_request.title }}" --kind Dependency 31 | 32 | - uses: stefanzweifel/git-auto-commit-action@v5 33 | with: 34 | commit_message: "chore(deps): add changelog for dependabot updates" 35 | commit_user_name: "dependabot[bot]" 36 | commit_user_email: "dependabot[bot]@users.noreply.github.com" 37 | -------------------------------------------------------------------------------- /docs/resources/tax_category.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_tax_category Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Tax Categories define how products are to be taxed in different countries. 7 | See also the Tax Category API Documentation https://docs.commercetools.com/api/projects/taxCategories 8 | --- 9 | 10 | # commercetools_tax_category (Resource) 11 | 12 | Tax Categories define how products are to be taxed in different countries. 13 | 14 | See also the [Tax Category API Documentation](https://docs.commercetools.com/api/projects/taxCategories) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_tax_category" "my-tax-category" { 20 | key = "my-tax-category-key" 21 | name = "Standard tax category" 22 | description = "Example category" 23 | } 24 | ``` 25 | 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `name` (String) 32 | 33 | ### Optional 34 | 35 | - `description` (String) 36 | - `key` (String) User-specific unique identifier for the category 37 | 38 | ### Read-Only 39 | 40 | - `id` (String) The ID of this resource. 41 | - `version` (Number) 42 | -------------------------------------------------------------------------------- /internal/sharedtypes/store_key_reference.go: -------------------------------------------------------------------------------- 1 | package sharedtypes 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 5 | "github.com/hashicorp/terraform-plugin-framework/types" 6 | "github.com/labd/commercetools-go-sdk/platform" 7 | ) 8 | 9 | var ( 10 | StoreKeyReferenceBlockSchema = schema.ListNestedBlock{ 11 | MarkdownDescription: "Sets the Stores the Business Unit is associated with. \n\nIf the Business Unit has Stores defined, " + 12 | "then all of its Carts, Orders, Quotes, or Quote Requests must belong to one of the Business Unit's " + 13 | "Stores.\n\nIf the Business Unit has no Stores, then all of its Carts, Orders, Quotes, or Quote Requests " + 14 | "must not belong to any Store.", 15 | NestedObject: schema.NestedBlockObject{ 16 | Attributes: map[string]schema.Attribute{ 17 | "key": schema.StringAttribute{ 18 | MarkdownDescription: "User-defined unique identifier of the Store", 19 | Optional: true, 20 | }, 21 | }, 22 | }, 23 | } 24 | ) 25 | 26 | // StoreKeyReference is a type to model the fields that all types of StoreKeyReference have in common. 27 | type StoreKeyReference struct { 28 | Key types.String `tfsdk:"key"` 29 | } 30 | 31 | func NewStoreKeyReferenceFromNative(n *platform.StoreKeyReference) StoreKeyReference { 32 | return StoreKeyReference{ 33 | Key: types.StringValue(n.Key), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.changes/v1.3.0.md: -------------------------------------------------------------------------------- 1 | ## v1.3.0 (2022-08-03) 2 | 3 | - **Backwards incompatible** Use a list type for enum values instead of a map 4 | to keep the ordering intact. This change requires an update to the way the 5 | values are defined (#98, #278): 6 | 7 | ```hcl 8 | type { 9 | name = "enum" 10 | values { 11 | FLAG-1 = "Flag 1" 12 | FLAG-2 = "Flag 2" 13 | } 14 | } 15 | ``` 16 | 17 | to 18 | 19 | ```hcl 20 | type { 21 | name = "enum" 22 | value { 23 | key = "FLAG-1" 24 | label = "Flag 1" 25 | } 26 | value { 27 | key = "FLAG-2" 28 | label = "FLAG-2" 29 | } 30 | } 31 | ``` 32 | 33 | - Update documentation and examples 34 | - Add support for custom fields on category, channel, customer_group, 35 | discount_code, shipping_method and store resources. (#265) 36 | - Improve logic to set the user-agent used in the requests. We now use the 37 | provider version. For example: 38 | `User-Agent: terraform-provider-commercetools/1.3.0 (bd9cae0)` 39 | - Improve the error handling by better communicating the errors raised by 40 | commercetools. 41 | - Accept a trailing slash in the token url (#182) 42 | - Large rewrite of the `type` and `product_type` resources to fix a number 43 | of issues (#165, #262, #263, #267) 44 | -------------------------------------------------------------------------------- /docs/data-sources/state.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_state Data Source - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Fetches state information for the given key. This is an easy way to import the id of an existing state for a given key. 7 | --- 8 | 9 | # commercetools_state (Data Source) 10 | 11 | Fetches state information for the given key. This is an easy way to import the id of an existing state for a given key. 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | # The Initial state is a state that is provided in a new commercetools environment by default 17 | data "commercetools_state" "initial_state" { 18 | key = "Initial" 19 | } 20 | 21 | resource "commercetools_state_transitions" "from_created_to_allocated" { 22 | from = data.commercetools_state.initial_state.id 23 | to = [ 24 | commercetools_state.backorder.id, 25 | ] 26 | } 27 | 28 | resource "commercetools_state" "backorder" { 29 | key = "backorder" 30 | type = "LineItemState" 31 | name = { 32 | en = "Back Order", 33 | 34 | } 35 | description = { 36 | en = "Not available - on back order" 37 | } 38 | initial = false 39 | } 40 | ``` 41 | 42 | 43 | ## Schema 44 | 45 | ### Required 46 | 47 | - `key` (String) Key of the state 48 | 49 | ### Read-Only 50 | 51 | - `id` (String) ID of the state 52 | -------------------------------------------------------------------------------- /docs/resources/shipping_zone.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_shipping_zone Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # commercetools_shipping_zone (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "commercetools_shipping_zone" "de-us" { 17 | key = "some-key" 18 | name = "DE and US" 19 | description = "Germany and US" 20 | location { 21 | country = "DE" 22 | } 23 | location { 24 | country = "US" 25 | state = "Nevada" 26 | } 27 | } 28 | ``` 29 | 30 | 31 | ## Schema 32 | 33 | ### Required 34 | 35 | - `name` (String) 36 | 37 | ### Optional 38 | 39 | - `description` (String) 40 | - `key` (String) User-specific unique identifier for a zone. Must be unique across a project 41 | - `location` (Block Set) [Location](https://docs.commercetoolstools.pi/projects/zones#location) (see [below for nested schema](#nestedblock--location)) 42 | 43 | ### Read-Only 44 | 45 | - `id` (String) The ID of this resource. 46 | - `version` (Number) 47 | 48 | 49 | ### Nested Schema for `location` 50 | 51 | Required: 52 | 53 | - `country` (String) A two-digit country code as per [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) 54 | 55 | Optional: 56 | 57 | - `state` (String) 58 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "Acceptance Tests", 10 | "type": "go", 11 | "request": "launch", 12 | "mode": "test", 13 | // this assumes your workspace is the root of the repo 14 | "program": "${fileDirname}", 15 | "env": { 16 | "TF_ACC": "1", 17 | "CTP_CLIENT_ID": "unittest", 18 | "CTP_CLIENT_SECRET": "x", 19 | "CTP_PROJECT_KEY": "unittest", 20 | "CTP_SCOPES": "manage_project:projectkey", 21 | "CTP_API_URL": "http://localhost:8989", 22 | "CTP_AUTH_URL": "http://localhost:8989", 23 | }, 24 | "args": [], 25 | }, 26 | { 27 | "name": "Debug - Attach External CLI", 28 | "type": "go", 29 | "request": "launch", 30 | "mode": "debug", 31 | // this assumes your workspace is the root of the repo 32 | "program": "${workspaceFolder}", 33 | "env": {}, 34 | "args": [ 35 | // pass the debug flag for reattaching 36 | "-debug", 37 | ], 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /commercetools/provider_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | ) 11 | 12 | var testAccProviders map[string]func() (*schema.Provider, error) 13 | var testAccProvider *schema.Provider 14 | 15 | func init() { 16 | testAccProvider = New("snapshot")() 17 | testAccProviders = map[string]func() (*schema.Provider, error){ 18 | "commercetools": func() (*schema.Provider, error) { 19 | return testAccProvider, nil 20 | }, 21 | } 22 | } 23 | 24 | func TestProvider(t *testing.T) { 25 | provider := New("snapshot")() 26 | if err := provider.InternalValidate(); err != nil { 27 | t.Fatalf("err: %s", err) 28 | } 29 | } 30 | 31 | func testAccPreCheck(t *testing.T) { 32 | requiredEnvs := []string{ 33 | "CTP_CLIENT_ID", 34 | "CTP_CLIENT_SECRET", 35 | "CTP_PROJECT_KEY", 36 | "CTP_SCOPES", 37 | "CTP_API_URL", 38 | "CTP_AUTH_URL", 39 | } 40 | for _, val := range requiredEnvs { 41 | if os.Getenv(val) == "" { 42 | t.Fatalf("%v must be set for acceptance tests", val) 43 | } 44 | } 45 | 46 | cfg := map[string]any{ 47 | "client_id": "dummy-client-id", 48 | "client_secret": "dummy-client-secret", 49 | "project_key": "terraform-provider-commercetools", 50 | } 51 | 52 | err := testAccProvider.Configure(context.Background(), terraform.NewResourceConfigRaw(cfg)) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /internal/resources/state_transition/model.go: -------------------------------------------------------------------------------- 1 | package state_transition 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/elliotchance/pie/v2" 7 | "github.com/hashicorp/terraform-plugin-framework/types" 8 | "github.com/labd/commercetools-go-sdk/platform" 9 | 10 | "github.com/labd/terraform-provider-commercetools/internal/utils" 11 | ) 12 | 13 | type StateTransition struct { 14 | ID types.String `tfsdk:"id"` 15 | From types.String `tfsdk:"from"` 16 | To []types.String `tfsdk:"to"` 17 | Version types.Int64 `tfsdk:"-"` 18 | } 19 | 20 | func (s StateTransition) updateActions(plan StateTransition) platform.StateUpdate { 21 | result := platform.StateUpdate{ 22 | Version: int(s.Version.ValueInt64()), 23 | Actions: []platform.StateUpdateAction{}, 24 | } 25 | 26 | if !reflect.DeepEqual(s.To, plan.To) { 27 | result.Actions = append( 28 | result.Actions, 29 | platform.StateSetTransitionsAction{ 30 | Transitions: pie.Map(plan.To, func(v types.String) platform.StateResourceIdentifier { 31 | return platform.StateResourceIdentifier{ 32 | ID: utils.StringRef(v.ValueString()), 33 | } 34 | }), 35 | }) 36 | } 37 | 38 | return result 39 | } 40 | 41 | func NewStateTransitionFromNative(n *platform.State) StateTransition { 42 | return StateTransition{ 43 | ID: types.StringValue(n.ID), 44 | From: types.StringValue(n.ID), 45 | To: pie.Map(n.Transitions, func(ref platform.StateReference) types.String { 46 | return types.StringValue(ref.ID) 47 | }), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/resources/commercetools_store/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_channel" "us-supply-channel" { 2 | key = "US-SUP" 3 | roles = ["InventorySupply"] 4 | name = { 5 | en-US = "Supply channel" 6 | } 7 | description = { 8 | en-US = "Supply channel desc" 9 | } 10 | } 11 | 12 | resource "commercetools_channel" "us-dist-channel" { 13 | key = "US-DIST" 14 | roles = ["ProductDistribution"] 15 | name = { 16 | en-US = "Dist channel" 17 | } 18 | description = { 19 | en-US = "Dist channel desc" 20 | } 21 | } 22 | 23 | resource "commercetools_type" "my-store-type" { 24 | key = "my-custom-store-type" 25 | name = { 26 | en = "My Store Type" 27 | } 28 | description = { 29 | en = "A custom store type" 30 | } 31 | 32 | resource_type_ids = ["store"] 33 | 34 | field { 35 | name = "some-field" 36 | label = { 37 | en = "Some Field" 38 | } 39 | type { 40 | name = "String" 41 | } 42 | } 43 | } 44 | 45 | 46 | resource "commercetools_store" "my-store" { 47 | key = "my-store" 48 | name = { 49 | en-US = "My store" 50 | } 51 | countries = ["NL", "BE"] 52 | languages = ["en-US"] 53 | distribution_channels = ["US-DIST"] 54 | supply_channels = ["US-SUP"] 55 | 56 | custom { 57 | type_id = commercetools_type.my-store-type.id 58 | fields = { 59 | my-field = "ja" 60 | } 61 | } 62 | 63 | depends_on = [ 64 | commercetools_channel.us-supply-channel, 65 | commercetools_channel.us-dist-channel, 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | project_name: terraform-provider-commercetools 3 | 4 | builds: 5 | - env: 6 | # goreleaser does not work with CGO, it could also complicate 7 | # usage by users in CI/CD systems like Terraform Cloud where 8 | # they are unable to install libraries. 9 | - CGO_ENABLED=0 10 | mod_timestamp: '{{ .CommitTimestamp }}' 11 | flags: 12 | - -trimpath 13 | ldflags: 14 | - '-s -w -X main.version={{.Version}} -X main.commit={{.ShortCommit}}' 15 | goos: 16 | - freebsd 17 | - windows 18 | - linux 19 | - darwin 20 | goarch: 21 | - amd64 22 | - '386' 23 | - arm 24 | - arm64 25 | ignore: 26 | - goos: darwin 27 | goarch: '386' 28 | binary: '{{ .ProjectName }}_v{{ .Version }}' 29 | 30 | archives: 31 | - format: zip 32 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 33 | 34 | checksum: 35 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 36 | algorithm: sha256 37 | 38 | changelog: 39 | sort: asc 40 | filters: 41 | exclude: 42 | - "^docs:" 43 | - "^test:" 44 | 45 | signs: 46 | - artifacts: checksum 47 | args: 48 | # if you are using this is a GitHub action or some other automated pipeline, you 49 | # need to pass the batch flag to indicate its not interactive. 50 | - "--batch" 51 | - "--local-user" 52 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 53 | - "--output" 54 | - "${signature}" 55 | - "--detach-sign" 56 | - "${artifact}" 57 | -------------------------------------------------------------------------------- /internal/resources/subscription/upgrade_v0.go: -------------------------------------------------------------------------------- 1 | package subscription 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/resource" 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | "github.com/hashicorp/terraform-plugin-go/tftypes" 9 | ) 10 | 11 | // Upgrade from V0 to V1 12 | func upgradeStateV0(_ context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { 13 | rawStateValue, err := req.RawState.Unmarshal(SubscriptionResourceV2) 14 | if err != nil { 15 | resp.Diagnostics.AddError( 16 | "Unable to Unmarshal Prior State", 17 | err.Error(), 18 | ) 19 | return 20 | } 21 | 22 | var rawState map[string]tftypes.Value 23 | if err := rawStateValue.As(&rawState); err != nil { 24 | resp.Diagnostics.AddError( 25 | "Unable to Convert Prior State", 26 | err.Error(), 27 | ) 28 | return 29 | } 30 | 31 | dynamicValue, err := tfprotov6.NewDynamicValue( 32 | SubscriptionResourceV1, 33 | tftypes.NewValue(SubscriptionResourceV1, map[string]tftypes.Value{ 34 | "id": rawState["id"], 35 | "key": rawState["key"], 36 | "version": rawState["version"], 37 | "changes": rawState["changes"], 38 | "destination": valueDestinationV1(rawState, "destination"), 39 | "format": valueToFormatV1(rawState, "format"), 40 | "message": rawState["message"], 41 | }), 42 | ) 43 | 44 | if err != nil { 45 | resp.Diagnostics.AddError( 46 | "Unable to Convert Upgraded State", 47 | err.Error(), 48 | ) 49 | return 50 | } 51 | 52 | resp.DynamicValue = &dynamicValue 53 | } 54 | -------------------------------------------------------------------------------- /internal/utils/decode.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/mitchellh/mapstructure" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | func toTimeHookFunc() mapstructure.DecodeHookFunc { 10 | return func( 11 | f reflect.Type, 12 | t reflect.Type, 13 | data interface{}) (interface{}, error) { 14 | if t != reflect.TypeOf(time.Time{}) { 15 | return data, nil 16 | } 17 | 18 | switch f.Kind() { 19 | case reflect.String: 20 | return time.Parse(time.RFC3339, data.(string)) 21 | case reflect.Float64: 22 | return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil 23 | case reflect.Int64: 24 | return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil 25 | default: 26 | return data, nil 27 | } 28 | // Convert it by parsing 29 | } 30 | } 31 | 32 | func DecodeStruct(input interface{}, result interface{}) error { 33 | meta := &mapstructure.Metadata{} 34 | decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 35 | Metadata: meta, 36 | DecodeHook: mapstructure.ComposeDecodeHookFunc( 37 | toTimeHookFunc()), 38 | Result: result, 39 | }) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | if err := decoder.Decode(input); err != nil { 45 | return err 46 | } 47 | 48 | if val, ok := result.(Decoder); ok { 49 | if raw, ok := input.(map[string]interface{}); ok { 50 | unused := make(map[string]interface{}) 51 | for _, key := range meta.Unused { 52 | unused[key] = raw[key] 53 | } 54 | val.DecodeStruct(unused) 55 | } 56 | } 57 | 58 | return err 59 | } 60 | 61 | type Decoder interface { 62 | DecodeStruct(map[string]interface{}) error 63 | } 64 | -------------------------------------------------------------------------------- /internal/utils/mutex.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | ) 7 | 8 | // Copied from https://www.terraform.io/plugin/sdkv2/guides/v2-upgrade-guide#removal-of-helper-mutexkv-package 9 | // 10 | // MutexKV is a simple key/value store for arbitrary mutexes. It can be used to 11 | // serialize changes across arbitrary collaborators that share knowledge of the 12 | // keys they must serialize on. 13 | // 14 | // The initial use case is to let aws_security_group_rule resources serialize 15 | // their access to individual security groups based on SG ID. 16 | type MutexKV struct { 17 | lock sync.Mutex 18 | store map[string]*sync.Mutex 19 | } 20 | 21 | // Locks the mutex for the given key. Caller is responsible for calling Unlock 22 | // for the same key 23 | func (m *MutexKV) Lock(key string) { 24 | log.Printf("[DEBUG] Locking %q", key) 25 | m.get(key).Lock() 26 | log.Printf("[DEBUG] Locked %q", key) 27 | } 28 | 29 | // Unlock the mutex for the given key. Caller must have called Lock for the same key first 30 | func (m *MutexKV) Unlock(key string) { 31 | log.Printf("[DEBUG] Unlocking %q", key) 32 | m.get(key).Unlock() 33 | log.Printf("[DEBUG] Unlocked %q", key) 34 | } 35 | 36 | // Returns a mutex for the given key, no guarantee of its lock status 37 | func (m *MutexKV) get(key string) *sync.Mutex { 38 | m.lock.Lock() 39 | defer m.lock.Unlock() 40 | mutex, ok := m.store[key] 41 | if !ok { 42 | mutex = &sync.Mutex{} 43 | m.store[key] = mutex 44 | } 45 | return mutex 46 | } 47 | 48 | // Returns a properly initialized MutexKV 49 | func NewMutexKV() *MutexKV { 50 | return &MutexKV{ 51 | store: make(map[string]*sync.Mutex), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/resources/commercetools_type/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_type" "my-custom-type" { 2 | key = "my-custom-type" 3 | name = { 4 | en = "Contact info" 5 | nl = "Contact informatie" 6 | } 7 | description = { 8 | en = "All things related communication" 9 | nl = "Alle communicatie-gerelateerde zaken" 10 | } 11 | 12 | resource_type_ids = ["customer"] 13 | 14 | field { 15 | name = "skype_name" 16 | label = { 17 | en = "Skype name" 18 | nl = "Skype naam" 19 | } 20 | type { 21 | name = "String" 22 | } 23 | } 24 | 25 | field { 26 | name = "contact_time" 27 | label = { 28 | en = "Contact time" 29 | nl = "Contact tijd" 30 | } 31 | type { 32 | name = "Enum" 33 | value { 34 | key = "day" 35 | label = "Daytime" 36 | } 37 | value { 38 | key = "evening" 39 | label = "Evening" 40 | } 41 | } 42 | } 43 | 44 | field { 45 | name = "emails" 46 | 47 | label = { 48 | en = "Emails" 49 | nl = "Emails" 50 | } 51 | 52 | type { 53 | name = "Set" 54 | element_type { 55 | name = "String" 56 | } 57 | } 58 | } 59 | 60 | field { 61 | name = "contact_preference" 62 | label = { 63 | en = "Contact preference" 64 | nl = "Contact voorkeur" 65 | } 66 | type { 67 | name = "LocalizedEnum" 68 | localized_value { 69 | key = "phone" 70 | label = { 71 | en = "Phone" 72 | nl = "Telefoon" 73 | } 74 | } 75 | localized_value { 76 | key = "skype" 77 | label = { 78 | en = "Skype" 79 | nl = "Skype" 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /docs/resources/custom_object.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_custom_object Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Custom objects are a way to store arbitrary JSON-formatted data on the commercetools platform. It allows you to persist data that does not fit the standard data model. This frees your application completely from any third-party persistence solution and means that all your data stays on the commercetools platform. 7 | See also the Custom Object API Documentation https://docs.commercetools.com/api/projects/custom-objects 8 | --- 9 | 10 | # commercetools_custom_object (Resource) 11 | 12 | Custom objects are a way to store arbitrary JSON-formatted data on the commercetools platform. It allows you to persist data that does not fit the standard data model. This frees your application completely from any third-party persistence solution and means that all your data stays on the commercetools platform. 13 | 14 | See also the [Custom Object API Documentation](https://docs.commercetools.com/api/projects/custom-objects) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_custom_object" "my-custom-object" { 20 | container = "my-container" 21 | key = "my-key" 22 | value = jsonencode(10) 23 | } 24 | ``` 25 | 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `container` (String) A namespace to group custom objects matching the pattern '[-_~.a-zA-Z0-9]+' 32 | - `key` (String) String matching the pattern '[-_~.a-zA-Z0-9]+' 33 | - `value` (String) JSON types Number, String, Boolean, Array, Object 34 | 35 | ### Read-Only 36 | 37 | - `id` (String) The ID of this resource. 38 | - `version` (Number) 39 | -------------------------------------------------------------------------------- /internal/acctest/client.go: -------------------------------------------------------------------------------- 1 | package acctest 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/http" 7 | "os" 8 | "strings" 9 | 10 | "github.com/labd/commercetools-go-sdk/ctutils" 11 | "github.com/labd/commercetools-go-sdk/platform" 12 | "golang.org/x/oauth2/clientcredentials" 13 | ) 14 | 15 | func GetClient() (*platform.ByProjectKeyRequestBuilder, error) { 16 | clientID := os.Getenv("CTP_CLIENT_ID") 17 | clientSecret := os.Getenv("CTP_CLIENT_SECRET") 18 | projectKey := os.Getenv("CTP_PROJECT_KEY") 19 | authURL := os.Getenv("CTP_AUTH_URL") 20 | apiURL := os.Getenv("CTP_API_URL") 21 | scopesRaw := os.Getenv("CTP_SCOPES") 22 | 23 | oauthScopes := strings.Split(scopesRaw, " ") 24 | oauth2Config := &clientcredentials.Config{ 25 | ClientID: clientID, 26 | ClientSecret: clientSecret, 27 | Scopes: oauthScopes, 28 | TokenURL: fmt.Sprintf("%s/oauth/token", authURL), 29 | } 30 | 31 | httpClient := &http.Client{ 32 | Transport: ctutils.DebugTransport, 33 | } 34 | 35 | client, err := platform.NewClient(&platform.ClientConfig{ 36 | URL: apiURL, 37 | Credentials: oauth2Config, 38 | UserAgent: "terraform-provider-commercetools/testing", 39 | HTTPClient: httpClient, 40 | }) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return client.WithProjectKey(projectKey), nil 46 | } 47 | 48 | func CheckApiResult(err error) error { 49 | if errors.Is(err, platform.ErrNotFound) { 50 | return nil 51 | } 52 | 53 | switch v := err.(type) { 54 | case platform.GenericRequestError: 55 | if v.StatusCode == 404 { 56 | return nil 57 | } 58 | return fmt.Errorf("unhandled error generic error returned (%d)", v.StatusCode) 59 | case platform.ResourceNotFoundError: 60 | return nil 61 | default: 62 | return fmt.Errorf("unexpected result returned") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/resources/commercetools_shipping_zone_rate/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_tax_category" "my-tax-category" { 2 | key = "some-tax-category-key" 3 | name = "My tax category" 4 | description = "Example" 5 | } 6 | 7 | resource "commercetools_shipping_method" "my-shipping-method" { 8 | key = "some-shipping-method-key" 9 | name = "My shipping method" 10 | description = "Standard method" 11 | is_default = true 12 | tax_category_id = commercetools_tax_category.my-tax-category.id 13 | predicate = "1 = 1" 14 | } 15 | 16 | resource "commercetools_shipping_zone" "my-shipping-zone" { 17 | key = "some-shipping-zone-key" 18 | name = "DE" 19 | description = "My shipping zone" 20 | location { 21 | country = "DE" 22 | } 23 | } 24 | 25 | resource "commercetools_shipping_zone_rate" "my-shipping-zone-rate" { 26 | shipping_method_id = commercetools_shipping_method.my-shipping-method.id 27 | shipping_zone_id = commercetools_shipping_zone.my-shipping-zone.id 28 | 29 | price { 30 | cent_amount = 5000 31 | currency_code = "EUR" 32 | } 33 | 34 | free_above { 35 | cent_amount = 50000 36 | currency_code = "EUR" 37 | } 38 | 39 | shipping_rate_price_tier { 40 | type = "CartScore" 41 | score = 10 42 | 43 | price { 44 | cent_amount = 5000 45 | currency_code = "EUR" 46 | } 47 | } 48 | 49 | shipping_rate_price_tier { 50 | type = "CartScore" 51 | score = 20 52 | 53 | price { 54 | cent_amount = 2000 55 | currency_code = "EUR" 56 | } 57 | } 58 | 59 | shipping_rate_price_tier { 60 | type = "CartScore" 61 | score = 30 62 | 63 | price_function { 64 | function = "x + 1" 65 | currency_code = "EUR" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /internal/custommodifiers/booldefault.go: -------------------------------------------------------------------------------- 1 | package custommodifiers 2 | 3 | // From https://developer.hashicorp.com/terraform/plugin/framework/resources/plan-modification#creating-attribute-plan-modifiers 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 10 | "github.com/hashicorp/terraform-plugin-framework/types/basetypes" 11 | ) 12 | 13 | // stringDefaultModifier is a plan modifier that sets a default value for a 14 | // types.StringType attribute when it is not configured. The attribute must be 15 | // marked as Optional and Computed. When setting the state during the resource 16 | // Create, Read, or Update methods, this default value must also be included or 17 | // the Terraform CLI will generate an error. 18 | type boolDefaultModifier struct { 19 | Default bool 20 | } 21 | 22 | // Description returns a plain text description of the validator's behavior, suitable for a practitioner to understand its impact. 23 | func (m boolDefaultModifier) Description(ctx context.Context) string { 24 | return fmt.Sprintf("If value is not configured, defaults to %v", m.Default) 25 | } 26 | 27 | // MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact. 28 | func (m boolDefaultModifier) MarkdownDescription(ctx context.Context) string { 29 | return fmt.Sprintf("If value is not configured, defaults to `%v`", m.Default) 30 | } 31 | 32 | func (m boolDefaultModifier) PlanModifyBool(ctx context.Context, req planmodifier.BoolRequest, resp *planmodifier.BoolResponse) { 33 | if req.ConfigValue.IsNull() { 34 | resp.PlanValue = basetypes.NewBoolValue(m.Default) 35 | } 36 | } 37 | 38 | func BoolDefault(defaultValue bool) planmodifier.Bool { 39 | return boolDefaultModifier{ 40 | Default: defaultValue, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /commercetools/testdata/custom_fields_test/commercetools_custom_fields.tmpl: -------------------------------------------------------------------------------- 1 | {{define "custom"}} 2 | custom { 3 | type_id = commercetools_type.test.id 4 | fields = { 5 | {{range $key, $value := .custom}} 6 | {{if eq $value "Boolean"}} 7 | "Boolean-field" : true 8 | {{end}} 9 | {{if eq $value "Number"}} 10 | "Number-field" : 1234 11 | {{end}} 12 | {{if eq $value "String"}} 13 | "String-field" : "foobar" 14 | {{end}} 15 | {{if eq $value "LocalizedString"}} 16 | "LocalizedString-field" = jsonencode({ 17 | en = "Localized String" 18 | fr = "Chaîne localisée" 19 | }) 20 | {{end}} 21 | {{if eq $value "Enum"}} 22 | "Enum-field" : "value2" 23 | {{end}} 24 | {{if eq $value "LocalizedEnum"}} 25 | "LocalizedEnum-field" = "value1" 26 | {{end}} 27 | {{if eq $value "Money"}} 28 | "Money-field" = jsonencode({ 29 | "type" : "centPrecision", 30 | "currencyCode" : "EUR", 31 | "centAmount" : 150000, 32 | "fractionDigits" : 2 33 | }) 34 | {{end}} 35 | {{if eq $value "Date"}} 36 | "Date-field" : "2023-08-29" 37 | {{end}} 38 | {{if eq $value "Time"}} 39 | "Time-field" : "20:22:11.123" 40 | {{end}} 41 | {{if eq $value "DateTime"}} 42 | "DateTime-field" : "2023-08-29T20:22:11.123Z" 43 | {{end}} 44 | {{if eq $value "Reference"}} 45 | "Reference-field" = jsonencode({ 46 | "typeId": "product-type", 47 | "id": commercetools_product_type.test.id 48 | }) 49 | {{end}} 50 | {{if eq $value "Set"}} 51 | "Set-field" = jsonencode(["ENUM-1", "ENUM-3"]) 52 | {{end}} 53 | {{end}} 54 | } 55 | } 56 | {{end}} 57 | -------------------------------------------------------------------------------- /docs/resources/customer_group.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_customer_group Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | A Customer can be a member of a customer group (for example reseller, gold member). Special prices can be assigned to specific products based on a customer group. 7 | See also the Customer Group API Documentation https://docs.commercetools.com/api/projects/customerGroups 8 | --- 9 | 10 | # commercetools_customer_group (Resource) 11 | 12 | A Customer can be a member of a customer group (for example reseller, gold member). Special prices can be assigned to specific products based on a customer group. 13 | 14 | See also the [Customer Group API Documentation](https://docs.commercetools.com/api/projects/customerGroups) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_customer_group" "standard" { 20 | key = "my-customer-group-key" 21 | name = "Standard Customer Group" 22 | } 23 | 24 | resource "commercetools_customer_group" "golden" { 25 | key = "my-customer-group-key" 26 | name = "Golden Customer Group" 27 | } 28 | ``` 29 | 30 | 31 | ## Schema 32 | 33 | ### Required 34 | 35 | - `name` (String) Unique within the project 36 | 37 | ### Optional 38 | 39 | - `custom` (Block List, Max: 1) (see [below for nested schema](#nestedblock--custom)) 40 | - `key` (String) User-specific unique identifier for the customer group 41 | 42 | ### Read-Only 43 | 44 | - `id` (String) The ID of this resource. 45 | - `version` (Number) 46 | 47 | 48 | ### Nested Schema for `custom` 49 | 50 | Required: 51 | 52 | - `type_id` (String) 53 | 54 | Optional: 55 | 56 | - `fields` (Map of String) Custom fields for this resource. Note that the values need to be provided as JSON encoded strings: `my-value = jsonencode({"key": "value"})` 57 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | 7 | test: 8 | runs-on: ubuntu-latest 9 | 10 | services: 11 | commercetools: 12 | image: labdigital/commercetools-mock-server 13 | ports: 14 | - 8989:8989 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version-file: go.mod 23 | 24 | - name: golangci-lint 25 | continue-on-error: true 26 | uses: golangci/golangci-lint-action@v6 27 | with: 28 | args: --issues-exit-code=0 --timeout=5m 29 | 30 | - name: Run tests 31 | run: go test -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./... -v ./... 32 | env: 33 | TF_ACC: 1 34 | CTP_CLIENT_ID: unittest 35 | CTP_CLIENT_SECRET: x 36 | CTP_PROJECT_KEY: unittest 37 | CTP_SCOPES: manage_project:unittest 38 | CTP_API_URL: http://localhost:8989 39 | CTP_AUTH_URL: http://localhost:8989 40 | 41 | - name: Upload to codecov 42 | uses: codecov/codecov-action@v5 43 | with: 44 | verbose: true 45 | 46 | - name: build binary 47 | uses: goreleaser/goreleaser-action@v6 48 | with: 49 | args: build --snapshot --clean --single-target 50 | env: 51 | GOPATH: ${{ env.GOPATH }} 52 | 53 | changie: 54 | runs-on: ubuntu-latest 55 | needs: test 56 | if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' 57 | permissions: 58 | contents: write 59 | pull-requests: write 60 | actions: write 61 | steps: 62 | - uses: actions/checkout@v4 63 | with: 64 | fetch-depth: 0 65 | 66 | - name: Prepare release 67 | uses: labd/changie-release-action@v0.5.0 68 | with: 69 | github-token: ${{ secrets.GITHUB_TOKEN }} 70 | release-workflow: 'release.yaml' 71 | -------------------------------------------------------------------------------- /examples/resources/commercetools_associate_role/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_type" "my-type" { 2 | key = "my-type" 3 | name = { 4 | en = "My type" 5 | nl = "Mijn type" 6 | } 7 | 8 | resource_type_ids = ["associate-role"] 9 | 10 | field { 11 | name = "my-field" 12 | label = { 13 | en = "My field" 14 | nl = "Mijn veld" 15 | } 16 | type { 17 | name = "String" 18 | } 19 | } 20 | } 21 | 22 | resource "commercetools_associate_role" "my-role" { 23 | key = "my-role" 24 | buyer_assignable = false 25 | name = "My Role" 26 | permissions = [ 27 | "AddChildUnits", 28 | "UpdateAssociates", 29 | "UpdateBusinessUnitDetails", 30 | "UpdateParentUnit", 31 | "ViewMyCarts", 32 | "ViewOthersCarts", 33 | "UpdateMyCarts", 34 | "UpdateOthersCarts", 35 | "CreateMyCarts", 36 | "CreateOthersCarts", 37 | "DeleteMyCarts", 38 | "DeleteOthersCarts", 39 | "ViewMyOrders", 40 | "ViewOthersOrders", 41 | "UpdateMyOrders", 42 | "UpdateOthersOrders", 43 | "CreateMyOrdersFromMyCarts", 44 | "CreateMyOrdersFromMyQuotes", 45 | "CreateOrdersFromOthersCarts", 46 | "CreateOrdersFromOthersQuotes", 47 | "ViewMyQuotes", 48 | "ViewOthersQuotes", 49 | "AcceptMyQuotes", 50 | "AcceptOthersQuotes", 51 | "DeclineMyQuotes", 52 | "DeclineOthersQuotes", 53 | "RenegotiateMyQuotes", 54 | "RenegotiateOthersQuotes", 55 | "ReassignMyQuotes", 56 | "ReassignOthersQuotes", 57 | "ViewMyQuoteRequests", 58 | "ViewOthersQuoteRequests", 59 | "UpdateMyQuoteRequests", 60 | "UpdateOthersQuoteRequests", 61 | "CreateMyQuoteRequestsFromMyCarts", 62 | "CreateQuoteRequestsFromOthersCarts", 63 | "CreateApprovalRules", 64 | "UpdateApprovalRules", 65 | "UpdateApprovalFlows", 66 | ] 67 | 68 | custom { 69 | type_id = commercetools_type.my-type.id 70 | fields = { 71 | my_field = "My value" 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /internal/custommodifiers/emptylist.go: -------------------------------------------------------------------------------- 1 | package custommodifiers 2 | 3 | // From https://developer.hashicorp.com/terraform/plugin/framework/resources/plan-modification#creating-attribute-plan-modifiers 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/hashicorp/terraform-plugin-framework/attr" 9 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 10 | "github.com/hashicorp/terraform-plugin-framework/types" 11 | "github.com/hashicorp/terraform-plugin-framework/types/basetypes" 12 | ) 13 | 14 | // stringDefaultModifier is a plan modifier that sets a default value for a 15 | // types.StringType attribute when it is not configured. The attribute must be 16 | // marked as Optional and Computed. When setting the state during the resource 17 | // Create, Read, or Update methods, this default value must also be included or 18 | // the Terraform CLI will generate an error. 19 | type listEmptyListModifier struct{} 20 | 21 | // Description returns a plain text description of the validator's behavior, suitable for a practitioner to understand its impact. 22 | func (m listEmptyListModifier) Description(ctx context.Context) string { 23 | return "If value is not configured, defaults to empty list" 24 | } 25 | 26 | // MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact. 27 | func (m listEmptyListModifier) MarkdownDescription(ctx context.Context) string { 28 | return "If value is not configured, defaults to empty list" 29 | } 30 | 31 | func (m listEmptyListModifier) PlanModifyList(ctx context.Context, req planmodifier.ListRequest, resp *planmodifier.ListResponse) { 32 | if resp.PlanValue.IsNull() { 33 | val, diags := basetypes.NewListValue(types.StringType, []attr.Value{}) 34 | resp.Diagnostics = append(resp.Diagnostics, diags...) 35 | if diags.HasError() { 36 | return 37 | } 38 | resp.PlanValue = val 39 | } 40 | } 41 | 42 | func EmptyList() planmodifier.List { 43 | return listEmptyListModifier{} 44 | } 45 | -------------------------------------------------------------------------------- /docs/resources/api_client.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_api_client Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Create a new API client. Note that Commercetools might return slightly different scopes, resulting in a new API client being created everytime Terraform is run. In this case, fix your scopes accordingly to match what is returned by Commercetools. 7 | Also see the API client HTTP API documentation https://docs.commercetools.com/api/projects/api-clients. 8 | --- 9 | 10 | # commercetools_api_client (Resource) 11 | 12 | Create a new API client. Note that Commercetools might return slightly different scopes, resulting in a new API client being created everytime Terraform is run. In this case, fix your scopes accordingly to match what is returned by Commercetools. 13 | 14 | Also see the [API client HTTP API documentation](https://docs.commercetools.com/api/projects/api-clients). 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_api_client" "my-api-client" { 20 | name = "My API Client" 21 | scope = ["manage_orders:my-ct-project-key", "manage_payments:my-ct-project-key"] 22 | } 23 | ``` 24 | 25 | 26 | ## Schema 27 | 28 | ### Required 29 | 30 | - `name` (String) Name of the API client 31 | - `scope` (Set of String) A list of the [OAuth scopes](https://docs.commercetools.com/api/scopes) 32 | 33 | ### Optional 34 | 35 | - `access_token_validity_seconds` (Number) Expiration time in seconds for each access token obtained by the APIClient. Only present when set with the APIClientDraft. If not present the default value applies. 36 | - `refresh_token_validity_seconds` (Number) Inactivity expiration time in seconds for each refresh token obtained by the APIClient. Only present when set with the APIClientDraft. If not present the default value applies. 37 | 38 | ### Read-Only 39 | 40 | - `id` (String) The ID of this resource. 41 | - `secret` (String, Sensitive) 42 | -------------------------------------------------------------------------------- /commercetools/resource_cart_discount_giftlineitem_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 7 | ) 8 | 9 | func TestAccCartDiscountGiftLineItem(t *testing.T) { 10 | identifier := "gift_line_item" 11 | resourceName := "commercetools_cart_discount.gift_line_item" 12 | 13 | resource.Test(t, resource.TestCase{ 14 | PreCheck: func() { testAccPreCheck(t) }, 15 | ProviderFactories: testAccProviders, 16 | CheckDestroy: testAccCheckCartDiscountDestroy, 17 | Steps: []resource.TestStep{ 18 | { 19 | Config: testAccCartDiscountGiftLineItemConfig(identifier), 20 | Check: resource.ComposeTestCheckFunc( 21 | resource.TestCheckResourceAttr(resourceName, "name.en", "giftLineItem name"), 22 | resource.TestCheckResourceAttr(resourceName, "value.0.type", "giftLineItem"), 23 | resource.TestCheckResourceAttr(resourceName, "value.0.product_id", "product-id"), 24 | resource.TestCheckResourceAttr(resourceName, "value.0.variant_id", "1"), 25 | resource.TestCheckResourceAttr(resourceName, "value.0.supply_channel_id", "supply-channel-id"), 26 | resource.TestCheckResourceAttr(resourceName, "value.0.distribution_channel_id", "distribution-channel-id"), 27 | resource.TestCheckResourceAttr(resourceName, "target.#", "0"), 28 | ), 29 | }, 30 | }, 31 | }) 32 | } 33 | 34 | func testAccCartDiscountGiftLineItemConfig(identifier string) string { 35 | return hclTemplate(` 36 | resource "commercetools_cart_discount" "{{ .identifier }}" { 37 | name = { 38 | en = "giftLineItem name" 39 | } 40 | sort_order = "0.9" 41 | predicate = "1=1" 42 | 43 | value { 44 | type = "giftLineItem" 45 | product_id = "product-id" 46 | variant_id = 1 47 | supply_channel_id = "supply-channel-id" 48 | distribution_channel_id = "distribution-channel-id" 49 | } 50 | } 51 | `, map[string]any{ 52 | "identifier": identifier, 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /commercetools/resource_cart_discount_totalprice_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 7 | ) 8 | 9 | func TestAccCartDiscountTotalPrice(t *testing.T) { 10 | identifier := "totalPrice" 11 | resourceName := "commercetools_cart_discount.totalPrice" 12 | 13 | resource.Test(t, resource.TestCase{ 14 | PreCheck: func() { testAccPreCheck(t) }, 15 | ProviderFactories: testAccProviders, 16 | CheckDestroy: testAccCheckCartDiscountDestroy, 17 | Steps: []resource.TestStep{ 18 | { 19 | Config: testAccCartDiscountTotalPriceConfig(identifier), 20 | Check: resource.ComposeTestCheckFunc( 21 | resource.TestCheckResourceAttr(resourceName, "name.en", "absolute name"), 22 | resource.TestCheckResourceAttr(resourceName, "value.0.type", "absolute"), 23 | resource.TestCheckResourceAttr(resourceName, "value.0.money.0.currency_code", "USD"), 24 | resource.TestCheckResourceAttr(resourceName, "value.0.money.0.cent_amount", "1000"), 25 | resource.TestCheckResourceAttr(resourceName, "value.0.money.1.currency_code", "EUR"), 26 | resource.TestCheckResourceAttr(resourceName, "value.0.money.1.cent_amount", "2000"), 27 | resource.TestCheckResourceAttr(resourceName, "target.0.type", "totalPrice"), 28 | ), 29 | }, 30 | }, 31 | }) 32 | } 33 | 34 | func testAccCartDiscountTotalPriceConfig(identifier string) string { 35 | return hclTemplate(` 36 | resource "commercetools_cart_discount" "{{ .identifier }}" { 37 | name = { 38 | en = "absolute name" 39 | } 40 | sort_order = "0.9" 41 | predicate = "1=1" 42 | 43 | target { 44 | type = "totalPrice" 45 | } 46 | 47 | value { 48 | type = "absolute" 49 | money { 50 | currency_code = "USD" 51 | cent_amount = 1000 52 | } 53 | money { 54 | currency_code = "EUR" 55 | cent_amount = 2000 56 | } 57 | } 58 | } 59 | `, map[string]any{ 60 | "identifier": identifier, 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | build-local: 5 | cmds: 6 | - go build -o terraform-provider-{{ .NAME }}_{{ .VERSION }} 7 | - mkdir -p ~/.terraform.d/plugins/registry.terraform.io/labd/{{ .NAME }}/{{ .VERSION }}/{{ .PLATFORM }}/ 8 | - mv terraform-provider-{{ .NAME }}_{{ .VERSION }} ~/.terraform.d/plugins/registry.terraform.io/labd/{{ .NAME }}/{{ .VERSION }}/{{ .PLATFORM }}/terraform-provider-{{ .NAME }}_v{{ .VERSION }} 9 | - cmd: codesign --deep --force -s - ~/.terraform.d/plugins/registry.terraform.io/labd/{{ .NAME }}/{{ .VERSION }}/{{ .PLATFORM }}/terraform-provider-{{ .NAME }}_v{{ .VERSION }} 10 | platforms: [darwin] 11 | vars: 12 | VERSION: 99.0.0 13 | NAME: commercetools 14 | PLATFORM: 15 | sh: echo "$(go env GOOS)_$(go env GOARCH)" 16 | 17 | build: 18 | env: 19 | GORELEASER_CURRENT_TAG: "v0.0.0" 20 | cmd: goreleaser build --snapshot --clean --single-target --output mach-composer 21 | 22 | format: 23 | cmds: 24 | - go fmt ./... 25 | 26 | test: 27 | cmds: 28 | - go test -v ./... 29 | 30 | docs: 31 | cmds: 32 | - go generate 33 | 34 | coverage-html: 35 | cmds: 36 | - go test -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... ./... 37 | - go tool cover -html=coverage.txt 38 | 39 | coverage: 40 | cmds: 41 | - go test -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... ./... 42 | - go tool cover -func=coverage.txt 43 | 44 | testacc: 45 | cmds: 46 | - TF_ACC=1 go test -v ./... 47 | 48 | testacct: 49 | cmds: 50 | - TF_ACC=1 go test -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... -v ./... 51 | 52 | mockacc: 53 | cmds: 54 | - go test -count=1 -v ./... 55 | env: 56 | TF_ACC: 1 57 | CTP_CLIENT_ID: unittest 58 | CTP_CLIENT_SECRET: x 59 | CTP_PROJECT_KEY: unittest 60 | CTP_SCOPES: manage_project:projectkey 61 | CTP_API_URL: http://localhost:8989 62 | CTP_AUTH_URL: http://localhost:8989 63 | -------------------------------------------------------------------------------- /examples/resources/commercetools_business_unit_company/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_store" "my-store" { 2 | key = "my-store" 3 | name = { 4 | en-US = "My store" 5 | } 6 | countries = ["NL", "BE"] 7 | languages = ["en-GB"] 8 | } 9 | 10 | resource "commercetools_type" "my-type" { 11 | key = "my-type" 12 | name = { 13 | en = "My type" 14 | nl = "Mijn type" 15 | } 16 | 17 | resource_type_ids = ["business-unit"] 18 | 19 | field { 20 | name = "my-field" 21 | label = { 22 | en = "My field" 23 | nl = "Mijn veld" 24 | } 25 | type { 26 | name = "String" 27 | } 28 | } 29 | } 30 | 31 | resource "commercetools_business_unit_company" "my-company" { 32 | key = "my-company" 33 | name = "My company" 34 | contact_email = "main@my-company.com" 35 | 36 | address { 37 | key = "my-company-address-1" 38 | country = "NL" 39 | state = "Noord-Holland" 40 | city = "Amsterdam" 41 | street_name = "Keizersgracht" 42 | street_number = "3" 43 | additional_street_info = "4th floor" 44 | postal_code = "1015 CJ" 45 | } 46 | 47 | address { 48 | key = "my-company-address-2" 49 | country = "NL" 50 | state = "Utrecht" 51 | city = "Utrecht" 52 | street_name = "Oudegracht" 53 | street_number = "1" 54 | postal_code = "3511 AA" 55 | additional_street_info = "Main floor" 56 | } 57 | 58 | store { 59 | key = commercetools_store.my-store.key 60 | } 61 | 62 | billing_address_keys = ["my-company-address-1"] 63 | shipping_address_keys = ["my-company-address-1", "my-company-address-2"] 64 | default_billing_address_key = "my-company-address-1" 65 | default_shipping_address_key = "my-company-address-1" 66 | 67 | custom { 68 | type_id = commercetools_type.my-type.id 69 | fields = { 70 | my_field = "My value" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /commercetools/resource_api_extension_migrate.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" 7 | ) 8 | 9 | func resourceAPIExtensionResourceV0() *schema.Resource { 10 | return &schema.Resource{ 11 | Schema: map[string]*schema.Schema{ 12 | "key": { 13 | Description: "User-specific unique identifier for the extension", 14 | Type: schema.TypeString, 15 | Optional: true, 16 | }, 17 | "destination": { 18 | Description: "[Destination](https://docs.commercetools.com/api/projects/api-extensions#destination) " + 19 | "Details where the extension can be reached", 20 | Type: schema.TypeSet, 21 | MaxItems: 1, 22 | Required: true, 23 | Elem: &schema.Schema{ 24 | Type: schema.TypeString, 25 | }, 26 | }, 27 | "trigger": { 28 | Description: "Array of [Trigger](https://docs.commercetools.com/api/projects/api-extensions#trigger) " + 29 | "Describes what triggers the extension", 30 | Type: schema.TypeList, 31 | Required: true, 32 | Elem: &schema.Resource{ 33 | Schema: map[string]*schema.Schema{ 34 | "resource_type_id": { 35 | Description: "Currently, cart, order, payment, customer, quote-request, staged-quote, quote and business-unit are supported", 36 | Type: schema.TypeString, 37 | Required: true, 38 | }, 39 | "actions": { 40 | Description: "Currently, Create and Update are supported", 41 | Type: schema.TypeList, 42 | Required: true, 43 | Elem: &schema.Schema{Type: schema.TypeString}, 44 | }, 45 | }, 46 | }, 47 | }, 48 | "timeout_in_ms": { 49 | Description: "Extension timeout in milliseconds", 50 | Type: schema.TypeInt, 51 | Optional: true, 52 | }, 53 | "version": { 54 | Type: schema.TypeInt, 55 | Computed: true, 56 | }, 57 | }, 58 | } 59 | } 60 | 61 | func migrateAPIExtensionStateV0toV1(_ context.Context, rawState map[string]any, _ any) (map[string]any, error) { 62 | transformToList(rawState, "destination") 63 | return rawState, nil 64 | } 65 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | 9 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 10 | "github.com/hashicorp/terraform-plugin-go/tfprotov5" 11 | "github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server" 12 | "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" 13 | 14 | "github.com/labd/terraform-provider-commercetools/commercetools" 15 | "github.com/labd/terraform-provider-commercetools/internal/provider" 16 | ) 17 | 18 | // Run "go generate" to format example terraform files and generate the docs for the registry/website 19 | 20 | // If you do not have terraform installed, you can remove the formatting command, but its suggested to 21 | // ensure the documentation is formatted properly. 22 | //go:generate terraform fmt -recursive ./examples/ 23 | 24 | // Run the docs generation tool, check its repository for more information on how it works and how docs 25 | // can be customized. 26 | //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs 27 | 28 | var ( 29 | // these will be set by the goreleaser configuration 30 | // to appropriate values for the compiled binary 31 | version string = "dev" 32 | commit string = "snapshot" 33 | ) 34 | 35 | func main() { 36 | debugFlag := flag.Bool("debug", false, "Start provider in debug mode.") 37 | flag.Parse() 38 | 39 | fullVersion := fmt.Sprintf("%s (%s)", version, commit) 40 | 41 | sdkProvider := commercetools.New(fullVersion) 42 | 43 | ctx := context.Background() 44 | providers := []func() tfprotov5.ProviderServer{ 45 | providerserver.NewProtocol5(provider.New(fullVersion)), 46 | sdkProvider().GRPCProvider, 47 | } 48 | 49 | muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | var serveOpts []tf5server.ServeOpt 55 | if *debugFlag { 56 | serveOpts = append(serveOpts, tf5server.WithManagedDebug()) 57 | } 58 | 59 | err = tf5server.Serve( 60 | "registry.terraform.io/labd/commercetools", 61 | muxServer.ProviderServer, 62 | serveOpts..., 63 | ) 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /commercetools/resource_cart_discount_absolute_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 7 | ) 8 | 9 | func TestAccCartDiscountAbsolute(t *testing.T) { 10 | identifier := "absolute" 11 | resourceName := "commercetools_cart_discount.absolute" 12 | 13 | resource.Test(t, resource.TestCase{ 14 | PreCheck: func() { testAccPreCheck(t) }, 15 | ProviderFactories: testAccProviders, 16 | CheckDestroy: testAccCheckCartDiscountDestroy, 17 | Steps: []resource.TestStep{ 18 | { 19 | Config: testAccCartDiscountAbsoluteConfig(identifier), 20 | Check: resource.ComposeTestCheckFunc( 21 | resource.TestCheckResourceAttr(resourceName, "name.en", "absolute name"), 22 | resource.TestCheckResourceAttr(resourceName, "value.0.type", "absolute"), 23 | resource.TestCheckResourceAttr(resourceName, "value.0.money.0.currency_code", "USD"), 24 | resource.TestCheckResourceAttr(resourceName, "value.0.money.0.cent_amount", "1000"), 25 | resource.TestCheckResourceAttr(resourceName, "value.0.money.1.currency_code", "EUR"), 26 | resource.TestCheckResourceAttr(resourceName, "value.0.money.1.cent_amount", "2000"), 27 | resource.TestCheckResourceAttr(resourceName, "target.0.type", "customLineItems"), 28 | resource.TestCheckResourceAttr(resourceName, "target.0.predicate", "1=1"), 29 | ), 30 | }, 31 | }, 32 | }) 33 | } 34 | 35 | func testAccCartDiscountAbsoluteConfig(identifier string) string { 36 | return hclTemplate(` 37 | resource "commercetools_cart_discount" "{{ .identifier }}" { 38 | name = { 39 | en = "absolute name" 40 | } 41 | sort_order = "0.9" 42 | predicate = "1=1" 43 | 44 | target { 45 | type = "customLineItems" 46 | predicate = "1=1" 47 | } 48 | 49 | value { 50 | type = "absolute" 51 | money { 52 | currency_code = "USD" 53 | cent_amount = 1000 54 | } 55 | money { 56 | currency_code = "EUR" 57 | cent_amount = 2000 58 | } 59 | } 60 | } 61 | `, map[string]any{ 62 | "identifier": identifier, 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /internal/utils/fields.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-framework/attr" 5 | "github.com/hashicorp/terraform-plugin-framework/types" 6 | "github.com/hashicorp/terraform-plugin-framework/types/basetypes" 7 | "github.com/labd/commercetools-go-sdk/platform" 8 | 9 | "github.com/labd/terraform-provider-commercetools/internal/customtypes" 10 | ) 11 | 12 | func OptionalString(value types.String) *string { 13 | if value.IsUnknown() || value.IsNull() { 14 | return nil 15 | } 16 | 17 | val := value.ValueString() 18 | return &val 19 | } 20 | 21 | func OptionalInt(value types.Int64) *int { 22 | if value.IsUnknown() || value.IsNull() { 23 | return nil 24 | } 25 | 26 | val := int(value.ValueInt64()) 27 | return &val 28 | } 29 | 30 | func FromOptionalString(value *string) basetypes.StringValue { 31 | if value == nil { 32 | return types.StringNull() 33 | } 34 | return types.StringValue(*value) 35 | 36 | } 37 | func FromOptionalLocalizedString(value *platform.LocalizedString) customtypes.LocalizedStringValue { 38 | if value == nil { 39 | return customtypes.NewLocalizedStringNull() 40 | } 41 | 42 | return FromLocalizedString(*value) 43 | } 44 | 45 | func FromOptionalInt(value *int) basetypes.Int64Value { 46 | if value == nil { 47 | return types.Int64Null() 48 | } 49 | return types.Int64Value(int64(*value)) 50 | } 51 | 52 | func FromOptionalBool(value *bool) basetypes.BoolValue { 53 | if value == nil { 54 | return types.BoolNull() 55 | } 56 | return types.BoolValue(*value) 57 | } 58 | 59 | func FromLocalizedString(value platform.LocalizedString) customtypes.LocalizedStringValue { 60 | result := make(map[string]attr.Value, len(value)) 61 | for k, v := range value { 62 | result[k] = types.StringValue(v) 63 | } 64 | return customtypes.NewLocalizedStringValue(result) 65 | } 66 | 67 | func StringRef(value any) *string { 68 | if value == nil { 69 | return nil 70 | } 71 | result := value.(string) 72 | return &result 73 | } 74 | 75 | func IntRef(value any) *int { 76 | result := value.(int) 77 | return &result 78 | } 79 | 80 | func BoolRef(value any) *bool { 81 | result := value.(bool) 82 | return &result 83 | } 84 | 85 | func Ref[T comparable](value T) *T { 86 | return &value 87 | } 88 | -------------------------------------------------------------------------------- /docs/resources/attribute_group.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_attribute_group Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Attribute Groups allow you to define a set of referenced Attribute Definitions for the purpose of giving one or more dedicated teams access to edit Attribute values in Product Variants for those Attributes in the Merchant Center. Depending on the use case, editing permission can be granted to all Attributes, to Attributes that are ungrouped, or none. 7 | --- 8 | 9 | # commercetools_attribute_group (Resource) 10 | 11 | Attribute Groups allow you to define a set of referenced Attribute Definitions for the purpose of giving one or more dedicated teams access to edit Attribute values in Product Variants for those Attributes in the Merchant Center. Depending on the use case, editing permission can be granted to all Attributes, to Attributes that are ungrouped, or none. 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "commercetools_attribute_group" "my-attribute-group" { 17 | key = "my-attribute-group-key" 18 | name = { 19 | en = "my-attribute-group-name" 20 | } 21 | description = { 22 | en = "my-attribute-group-description" 23 | } 24 | 25 | attribute { 26 | key = "attribute-key-1" 27 | } 28 | 29 | attribute { 30 | key = "attribute-key-2" 31 | } 32 | } 33 | ``` 34 | 35 | 36 | ## Schema 37 | 38 | ### Required 39 | 40 | - `name` (Map of String) Name of the State as localized string. 41 | 42 | ### Optional 43 | 44 | - `attribute` (Block List) Attributes with unique values. (see [below for nested schema](#nestedblock--attribute)) 45 | - `description` (Map of String) Description of the State as localized string. 46 | - `key` (String) User-defined unique identifier of the AttributeGroup. 47 | 48 | ### Read-Only 49 | 50 | - `id` (String) Platform-generated unique identifier of the AttributeGroup. 51 | - `version` (Number) Current version of the AttributeGroup. 52 | 53 | 54 | ### Nested Schema for `attribute` 55 | 56 | Required: 57 | 58 | - `key` (String) The Attribute's name as given in its AttributeDefinition. 59 | -------------------------------------------------------------------------------- /examples/resources/commercetools_business_unit_division/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_store" "my-store" { 2 | key = "my-store" 3 | name = { 4 | en-US = "My store" 5 | } 6 | countries = ["NL", "BE"] 7 | languages = ["en-GB"] 8 | } 9 | 10 | resource "commercetools_type" "my-type" { 11 | key = "my-type" 12 | name = { 13 | en = "My type" 14 | nl = "Mijn type" 15 | } 16 | 17 | resource_type_ids = ["business-unit"] 18 | 19 | field { 20 | name = "my-field" 21 | label = { 22 | en = "My field" 23 | nl = "Mijn veld" 24 | } 25 | type { 26 | name = "String" 27 | } 28 | } 29 | } 30 | 31 | resource "commercetools_business_unit_company" "my-company" { 32 | key = "my-company" 33 | name = "My company" 34 | contact_email = "main@my-company.com" 35 | } 36 | 37 | resource "commercetools_business_unit_division" "my-division" { 38 | key = "my-division" 39 | name = "My division" 40 | contact_email = "my-division@my-company.com" 41 | store_mode = "Explicit" 42 | status = "Active" 43 | associate_mode = "Explicit" 44 | approval_rule_mode = "Explicit" 45 | 46 | parent_unit { 47 | key = commercetools_business_unit_company.my-company.key 48 | } 49 | 50 | store { 51 | key = commercetools_store.my-store.key 52 | } 53 | 54 | address { 55 | key = "my-div-address-1" 56 | country = "NL" 57 | state = "Utrecht" 58 | city = "Utrecht" 59 | street_name = "Oudegracht" 60 | street_number = "1" 61 | postal_code = "3511 AA" 62 | additional_street_info = "Main floor" 63 | } 64 | 65 | address { 66 | key = "my-div-address-2" 67 | country = "NL" 68 | state = "Zuid-Holland" 69 | city = "Leiden" 70 | street_name = "Breestraat" 71 | street_number = "1" 72 | postal_code = "2311 CH" 73 | } 74 | billing_address_keys = ["my-div-address-1"] 75 | shipping_address_keys = ["my-div-address-1", "my-div-address-2"] 76 | default_billing_address_key = "my-div-address-1" 77 | default_shipping_address_key = "my-div-address-1" 78 | 79 | custom { 80 | type_id = commercetools_type.my-type.id 81 | fields = { 82 | my_field = "My value" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /docs/resources/tax_category_rate.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_tax_category_rate Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Tax rate for Tax Category. 7 | See also Tax Rate API Documentation https://docs.commercetools.com/api/projects/taxCategories#taxrate 8 | --- 9 | 10 | # commercetools_tax_category_rate (Resource) 11 | 12 | Tax rate for Tax Category. 13 | 14 | See also [Tax Rate API Documentation](https://docs.commercetools.com/api/projects/taxCategories#taxrate) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_tax_category" "my-tax-category" { 20 | name = "Standard tax category" 21 | description = "Example category" 22 | } 23 | 24 | resource "commercetools_tax_category_rate" "standard-tax-category-DE" { 25 | tax_category_id = commercetools_tax_category.my-tax-category.id 26 | name = "19% MwSt" 27 | amount = 0.19 28 | included_in_price = false 29 | country = "DE" 30 | sub_rate { 31 | name = "example" 32 | amount = 0.19 33 | } 34 | } 35 | 36 | resource "commercetools_tax_category_rate" "standard-tax-category-NL" { 37 | tax_category_id = commercetools_tax_category.my-tax-category.id 38 | name = "21% BTW" 39 | amount = 0.21 40 | included_in_price = true 41 | country = "NL" 42 | } 43 | ``` 44 | 45 | 46 | ## Schema 47 | 48 | ### Required 49 | 50 | - `country` (String) A two-digit country code as per [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) 51 | - `included_in_price` (Boolean) 52 | - `name` (String) 53 | - `tax_category_id` (String) 54 | 55 | ### Optional 56 | 57 | - `amount` (Number) Number Percentage in the range of [0..1]. The sum of the amounts of all subRates, if there are any 58 | - `state` (String) The state in the country 59 | - `sub_rate` (Block List) For countries (for example the US) where the total tax is a combination of multiple taxes (for example state and local taxes) (see [below for nested schema](#nestedblock--sub_rate)) 60 | 61 | ### Read-Only 62 | 63 | - `id` (String) The ID of this resource. 64 | 65 | 66 | ### Nested Schema for `sub_rate` 67 | 68 | Required: 69 | 70 | - `amount` (Number) Number Percentage in the range of [0..1] 71 | - `name` (String) 72 | -------------------------------------------------------------------------------- /examples/resources/commercetools_api_extension/resource.tf: -------------------------------------------------------------------------------- 1 | # HTTP api extension 2 | resource "commercetools_api_extension" "my-http-extension" { 3 | key = "my-http-extension-key" 4 | 5 | destination { 6 | type = "HTTP" 7 | url = "https://example.com" 8 | authorization_header = "Basic 12345" 9 | } 10 | 11 | trigger { 12 | resource_type_id = "customer" 13 | actions = ["Create", "Update"] 14 | } 15 | } 16 | 17 | # AWS Lambda api extension 18 | resource "commercetools_api_extension" "my-awslambda-extension" { 19 | key = "my-awslambda-extension-key" 20 | 21 | destination { 22 | type = "awslambda" 23 | arn = "us-east-1:123456789012:mylambda" 24 | access_key = "mykey" 25 | access_secret = "mysecret" 26 | } 27 | 28 | trigger { 29 | resource_type_id = "customer" 30 | actions = ["Create", "Update"] 31 | } 32 | } 33 | 34 | # Google Cloud Function api extension 35 | resource "commercetools_api_extension" "my-googlecloudfunction-extension" { 36 | key = "my-googlecloudfunction-extension-key" 37 | 38 | destination { 39 | type = "googlecloudfunction" 40 | url = "https://example.com" 41 | } 42 | 43 | trigger { 44 | resource_type_id = "customer" 45 | actions = ["Create", "Update"] 46 | } 47 | } 48 | 49 | resource "google_cloudfunctions_function" "my_cloud_function" { 50 | name = "function-test" 51 | description = "My function" 52 | runtime = "nodejs16" 53 | 54 | # See https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions_function for any 55 | # further settings 56 | } 57 | 58 | resource "google_cloudfunctions_function_iam_member" "invoker" { 59 | # For GoogleCloudFunction destinations, you need to grant permissions to the 60 | # service account to invoke your function. 61 | project = "my-project" 62 | region = "europe-central2" 63 | cloud_function = google_cloudfunctions_function.my_cloud_function.name 64 | 65 | # If your function's version is 1st gen, grant the service account the IAM role Cloud Functions Invoker 66 | role = "roles/cloudfunctions.invoker" 67 | # For version 2nd gen, assign the IAM role Cloud Run Invoker 68 | # role = "roles/run.invoker" 69 | member = "serviceAccount:extensions@commercetools-platform.iam.gserviceaccount.com" 70 | } 71 | -------------------------------------------------------------------------------- /internal/datasource/state/resource_test.go: -------------------------------------------------------------------------------- 1 | package custom_type_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | acctest "github.com/labd/terraform-provider-commercetools/internal/acctest" 9 | "github.com/labd/terraform-provider-commercetools/internal/utils" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 12 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestAccState(t *testing.T) { 17 | resource.Test(t, resource.TestCase{ 18 | PreCheck: func() { acctest.TestAccPreCheck(t) }, 19 | ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, 20 | CheckDestroy: testAccCheckDestroy, 21 | Steps: []resource.TestStep{ 22 | { 23 | Config: testAccConfigLoadState(), 24 | Check: resource.ComposeTestCheckFunc( 25 | func(s *terraform.State) error { 26 | client, err := acctest.GetClient() 27 | if err != nil { 28 | return nil 29 | } 30 | result, err := client.States().WithKey("test").Get().Execute(context.Background()) 31 | if err != nil { 32 | return nil 33 | } 34 | assert.NotNil(t, result) 35 | assert.Equal(t, result.Key, "test") 36 | return nil 37 | }, 38 | ), 39 | }, 40 | }, 41 | }) 42 | } 43 | 44 | func testAccCheckDestroy(s *terraform.State) error { 45 | client, err := acctest.GetClient() 46 | if err != nil { 47 | return err 48 | } 49 | 50 | for _, rs := range s.RootModule().Resources { 51 | if rs.Type != "commercetools_state" { 52 | continue 53 | } 54 | response, err := client.States().WithId(rs.Primary.ID).Get().Execute(context.Background()) 55 | if err == nil { 56 | if response != nil && response.ID == rs.Primary.ID { 57 | return fmt.Errorf("state (%s) still exists", rs.Primary.ID) 58 | } 59 | return nil 60 | } 61 | if newErr := acctest.CheckApiResult(err); newErr != nil { 62 | return newErr 63 | } 64 | } 65 | return nil 66 | } 67 | 68 | func testAccConfigLoadState() string { 69 | return utils.HCLTemplate(` 70 | resource "commercetools_state" "test" { 71 | key = "test" 72 | type = "ReviewState" 73 | name = { 74 | en = "Unreviewed" 75 | } 76 | description = { 77 | en = "Not reviewed yet" 78 | } 79 | initial = true 80 | } 81 | 82 | data "commercetools_state" "test" { 83 | key = "test" 84 | 85 | depends_on = [ 86 | commercetools_state.test 87 | ] 88 | } 89 | `, map[string]any{}) 90 | } 91 | -------------------------------------------------------------------------------- /docs/guides/extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "" 3 | page_title: "API Extensions" 4 | description: |- 5 | Using AWS Lambda, Google Cloud Functions for API Extensions 6 | --- 7 | 8 | ## Example 9 | 10 | ```hcl 11 | locals { 12 | project = "" 13 | region = "europe-west1" 14 | } 15 | 16 | provider "commercetools" { 17 | client_id = "foo" 18 | client_secret = "bar" 19 | project_key = "some-project" 20 | scopes = "manage_project:some-project" 21 | token_url = "https://auth.sphere.io" 22 | api_url = "https://api.sphere.io" 23 | } 24 | 25 | provider "google" { 26 | project = local.project 27 | region = local.region 28 | } 29 | 30 | 31 | # Create the artifact 32 | data "archive_file" "source" { 33 | type = "zip" 34 | source_dir = "src" 35 | output_path = "functions-source.zip" 36 | } 37 | 38 | resource "google_storage_bucket" "bucket" { 39 | name = "${local.project}-gcf-source" 40 | location = "EU" 41 | uniform_bucket_level_access = true 42 | } 43 | 44 | resource "google_storage_bucket_object" "object" { 45 | name = "function-source-${data.archive_file.source.output_md5}.zip" 46 | bucket = google_storage_bucket.bucket.name 47 | source = "function-source.zip" 48 | } 49 | 50 | resource "google_cloudfunctions_function" "function" { 51 | name = "my-cart-extension" 52 | region = local.region 53 | description = "Extension for cart create / update" 54 | 55 | runtime = "nodejs16" 56 | trigger_http = true 57 | entry_point = "cartHandler" 58 | 59 | source_archive_bucket = google_storage_bucket.bucket.name 60 | source_archive_object = google_storage_bucket_object.object.name 61 | } 62 | 63 | 64 | # Allow everyone to access this function. Commercetools doesn't support auth 65 | # yet for cloud functions, so this is needed. 66 | resource "google_cloudfunctions_function_iam_member" "invoker" { 67 | project = google_cloudfunctions_function.function.project 68 | region = google_cloudfunctions_function.function.region 69 | cloud_function = google_cloudfunctions_function.function.name 70 | 71 | role = "roles/cloudfunctions.invoker" 72 | member = "allUsers" 73 | } 74 | 75 | resource "commercetools_api_extension" "cart_extension" { 76 | key = "my-cart-extension" 77 | 78 | destination { 79 | type = "HTTP" 80 | url = google_cloudfunctions_function.function.https_trigger_url 81 | } 82 | 83 | trigger { 84 | resource_type_id = "cart" 85 | actions = ["Create", "Update"] 86 | } 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /templates/index.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "commercetools provider" 3 | subcategory: "" 4 | description: |- 5 | The commercetools provider provides resources to interact with the commercetools API 6 | 7 | --- 8 | 9 | # commercetools provider 10 | 11 | ## Commercial support 12 | Need support implementing this terraform module in your organization? We are 13 | able to offer support. Please contact us at 14 | [opensource@labdigital.nl](opensource@labdigital.nl)! 15 | 16 | ## Installation 17 | Terraform automatically downloads providers from the terraform registry. Add the 18 | following to your terraform project 19 | 20 | 21 | ```hcl 22 | terraform { 23 | required_providers { 24 | commercetools = { 25 | source = "labd/commercetools" 26 | } 27 | } 28 | } 29 | ``` 30 | 31 | Packages of the releases are available at [the GitHub Repo](https://github.com/labd/terraform-provider-commercetools/releases). 32 | See the [terraform documentation](https://www.terraform.io/docs/configuration/providers.html#third-party-plugins) 33 | for more information about installing third-party providers. 34 | 35 | 36 | ## Using the provider 37 | The provider attempts to read the required values from environment variables: 38 | - `CTP_CLIENT_ID` 39 | - `CTP_CLIENT_SECRET` 40 | - `CTP_PROJECT_KEY` 41 | - `CTP_SCOPES` 42 | - `CTP_API_URL` 43 | - `CTP_AUTH_URL` 44 | 45 | Alternatively, you can set it up directly in the terraform file: 46 | 47 | ```hcl 48 | provider "commercetools" { 49 | client_id = "" 50 | client_secret = "" 51 | project_key = "" 52 | scopes = "" 53 | api_url = "" 54 | token_url = "" 55 | } 56 | ``` 57 | 58 | {{ .SchemaMarkdown | trimspace }} 59 | 60 | ## Using with docker 61 | 62 | The included `Dockerfile` bundles the official [`hashicorp/terraform:light`](https://hub.docker.com/r/hashicorp/terraform/) docker image with 63 | our `terraform-provider-commercetools`. 64 | 65 | To build the docker image file locally, use: 66 | ```sh 67 | docker build . -t terraform-with-provider-commercetools:latest 68 | ``` 69 | Then you can run a terraform command on files in the current directory with: 70 | ```sh 71 | docker run -v "${pwd}:/config" terraform-with-provider-commercetools:latest 72 | ``` 73 | 74 | ## Authors 75 | This project is developed by [Lab Digital](https://www.labdigital.nl). We 76 | welcome additional contributors. Please see our 77 | [GitHub repository](https://github.com/labd/terraform-provider-commercetools) 78 | for more information. 79 | -------------------------------------------------------------------------------- /templates/guides/extensions.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "" 3 | page_title: "API Extensions" 4 | description: |- 5 | Using AWS Lambda, Google Cloud Functions for API Extensions 6 | --- 7 | 8 | ## Example 9 | 10 | ```hcl 11 | locals { 12 | project = "" 13 | region = "europe-west1" 14 | } 15 | 16 | provider "commercetools" { 17 | client_id = "foo" 18 | client_secret = "bar" 19 | project_key = "some-project" 20 | scopes = "manage_project:some-project" 21 | token_url = "https://auth.sphere.io" 22 | api_url = "https://api.sphere.io" 23 | } 24 | 25 | provider "google" { 26 | project = local.project 27 | region = local.region 28 | } 29 | 30 | 31 | # Create the artifact 32 | data "archive_file" "source" { 33 | type = "zip" 34 | source_dir = "src" 35 | output_path = "functions-source.zip" 36 | } 37 | 38 | resource "google_storage_bucket" "bucket" { 39 | name = "${local.project}-gcf-source" 40 | location = "EU" 41 | uniform_bucket_level_access = true 42 | } 43 | 44 | resource "google_storage_bucket_object" "object" { 45 | name = "function-source-${data.archive_file.source.output_md5}.zip" 46 | bucket = google_storage_bucket.bucket.name 47 | source = "function-source.zip" 48 | } 49 | 50 | resource "google_cloudfunctions_function" "function" { 51 | name = "my-cart-extension" 52 | region = local.region 53 | description = "Extension for cart create / update" 54 | 55 | runtime = "nodejs16" 56 | trigger_http = true 57 | entry_point = "cartHandler" 58 | 59 | source_archive_bucket = google_storage_bucket.bucket.name 60 | source_archive_object = google_storage_bucket_object.object.name 61 | } 62 | 63 | 64 | # Allow everyone to access this function. Commercetools doesn't support auth 65 | # yet for cloud functions, so this is needed. 66 | resource "google_cloudfunctions_function_iam_member" "invoker" { 67 | project = google_cloudfunctions_function.function.project 68 | region = google_cloudfunctions_function.function.region 69 | cloud_function = google_cloudfunctions_function.function.name 70 | 71 | role = "roles/cloudfunctions.invoker" 72 | member = "allUsers" 73 | } 74 | 75 | resource "commercetools_api_extension" "cart_extension" { 76 | key = "my-cart-extension" 77 | 78 | destination { 79 | type = "HTTP" 80 | url = google_cloudfunctions_function.function.https_trigger_url 81 | } 82 | 83 | trigger { 84 | resource_type_id = "cart" 85 | actions = ["Create", "Update"] 86 | } 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/resources/state.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_state Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | The commercetools platform allows you to model states of certain objects, such as orders, line items, products, reviews, and payments to define finite state machines reflecting the business logic you'd like to implement. 7 | See also the State API Documentation https://docs.commercetools.com/api/projects/states 8 | --- 9 | 10 | # commercetools_state (Resource) 11 | 12 | The commercetools platform allows you to model states of certain objects, such as orders, line items, products, reviews, and payments to define finite state machines reflecting the business logic you'd like to implement. 13 | 14 | See also the [State API Documentation](https://docs.commercetools.com/api/projects/states) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_state" "review_unreviewed" { 20 | key = "review-unreviewed" 21 | type = "ReviewState" 22 | name = { 23 | en = "Unreviewed" 24 | } 25 | description = { 26 | en = "Not reviewed yet" 27 | } 28 | initial = true 29 | roles = ["ReviewIncludedInStatistics"] 30 | } 31 | 32 | resource "commercetools_state" "product_for_sale" { 33 | key = "product-for-sale" 34 | type = "ProductState" 35 | name = { 36 | en = "For Sale" 37 | } 38 | description = { 39 | en = "Regularly stocked product." 40 | } 41 | initial = true 42 | } 43 | 44 | resource "commercetools_state" "product_clearance" { 45 | key = "product-clearance" 46 | type = "ProductState" 47 | name = { 48 | en = "On Clearance" 49 | } 50 | description = { 51 | en = "The product line will not be ordered again." 52 | } 53 | } 54 | ``` 55 | 56 | 57 | ## Schema 58 | 59 | ### Required 60 | 61 | - `type` (String) [StateType](https://docs.commercetools.com/api/projects/states#statetype) 62 | 63 | ### Optional 64 | 65 | - `description` (Map of String) Description of the State as localized string. 66 | - `initial` (Boolean) A state can be declared as an initial state for any state machine. When a workflow starts, this first state must be an initial state 67 | - `key` (String) Timestamp of the last Terraform update of the order. 68 | - `name` (Map of String) Name of the State as localized string. 69 | - `roles` (List of String) [State Role](https://docs.commercetools.com/api/projects/states#staterole) 70 | 71 | ### Read-Only 72 | 73 | - `id` (String) The ID of this resource. 74 | - `version` (Number) 75 | -------------------------------------------------------------------------------- /docs/resources/product_selection.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_product_selection Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Product Selections can be used to manage individual assortments for different sales channels.See also the Product Selections API Documentation https://docs.commercetools.com/api/projects/product-selections 7 | --- 8 | 9 | # commercetools_product_selection (Resource) 10 | 11 | Product Selections can be used to manage individual assortments for different sales channels.See also the [Product Selections API Documentation](https://docs.commercetools.com/api/projects/product-selections) 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "commercetools_type" "my-type" { 17 | key = "my-type" 18 | name = { 19 | en = "My type" 20 | nl = "Mijn type" 21 | } 22 | 23 | resource_type_ids = ["product-selection"] 24 | 25 | field { 26 | name = "my-field" 27 | label = { 28 | en = "My field" 29 | nl = "Mijn veld" 30 | } 31 | type { 32 | name = "String" 33 | } 34 | } 35 | } 36 | 37 | resource "commercetools_product_selection" "product-selection-us" { 38 | key = "product-selection-us" 39 | name = { 40 | en = "US Product Selection" 41 | } 42 | mode = "Individual" 43 | 44 | custom { 45 | type_id = commercetools_type.my-type.id 46 | fields = { 47 | my-field = "my-value" 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | 54 | ## Schema 55 | 56 | ### Required 57 | 58 | - `name` (Map of String) Name of the ProductSelection. 59 | 60 | ### Optional 61 | 62 | - `custom` (Block, Optional) Custom fields for this resource. (see [below for nested schema](#nestedblock--custom)) 63 | - `key` (String) User-defined unique identifier of the ProductSelection. 64 | - `mode` (String) Specifies in which way the Products are assigned to the ProductSelection.Currently, the only way of doing this is to specify each Product individually, either by including or excluding them explicitly.Default: Individual 65 | 66 | ### Read-Only 67 | 68 | - `id` (String) Unique identifier of the ProductSelection. 69 | - `version` (Number) Current version of the ProductSelection. 70 | 71 | 72 | ### Nested Schema for `custom` 73 | 74 | Optional: 75 | 76 | - `fields` (Map of String) CustomValue fields for this resource. Note that the values need to be provided as JSON encoded strings: `my-value = jsonencode({"key": "value"})` 77 | - `type_id` (String) The ID of the custom type to use for this resource. 78 | -------------------------------------------------------------------------------- /internal/resources/state/resource_test.go: -------------------------------------------------------------------------------- 1 | package state_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | 11 | "github.com/labd/terraform-provider-commercetools/internal/acctest" 12 | "github.com/labd/terraform-provider-commercetools/internal/utils" 13 | ) 14 | 15 | func TestAccState_createAndUpdateWithID(t *testing.T) { 16 | name := "test state" 17 | key := "test-state" 18 | resourceName := "commercetools_state.acctest-state" 19 | 20 | newName := "new test state name" 21 | 22 | resource.Test(t, resource.TestCase{ 23 | PreCheck: func() { acctest.TestAccPreCheck(t) }, 24 | ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, 25 | CheckDestroy: testAccCheckStateDestroy, 26 | Steps: []resource.TestStep{ 27 | { 28 | Config: testAccStateConfig(t, name, key, false), 29 | Check: resource.ComposeTestCheckFunc( 30 | resource.TestCheckResourceAttr(resourceName, "name.en", name), 31 | resource.TestCheckResourceAttr(resourceName, "key", key), 32 | ), 33 | }, 34 | { 35 | Config: testAccStateConfig(t, newName, key, true), 36 | Check: resource.ComposeTestCheckFunc( 37 | resource.TestCheckResourceAttr(resourceName, "name.en", newName), 38 | resource.TestCheckResourceAttr(resourceName, "key", key), 39 | ), 40 | }, 41 | }, 42 | }) 43 | } 44 | 45 | func testAccStateConfig(t *testing.T, name string, key string, addRole bool) string { 46 | return utils.HCLTemplate(` 47 | resource "commercetools_state" "acctest-state" { 48 | key = "{{ .key }}" 49 | type = "ReviewState" 50 | name = { 51 | en = "{{ .name }}" 52 | nl = "{{ .name }}" 53 | } 54 | 55 | {{ if .addRole }} 56 | roles = ["ReviewIncludedInStatistics"] 57 | {{ end }} 58 | } 59 | `, 60 | map[string]any{ 61 | "key": key, 62 | "name": name, 63 | "addRole": addRole, 64 | }) 65 | } 66 | 67 | func testAccCheckStateDestroy(s *terraform.State) error { 68 | client, err := acctest.GetClient() 69 | if err != nil { 70 | return err 71 | } 72 | 73 | for _, rs := range s.RootModule().Resources { 74 | if rs.Type != "commercetools_state" { 75 | continue 76 | } 77 | response, err := client.States().WithId(rs.Primary.ID).Get().Execute(context.Background()) 78 | if err == nil { 79 | if response != nil && response.ID == rs.Primary.ID { 80 | return fmt.Errorf("state (%s) still exists", rs.Primary.ID) 81 | } 82 | return nil 83 | } 84 | if newErr := acctest.CheckApiResult(err); newErr != nil { 85 | return newErr 86 | } 87 | } 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /commercetools/resource_tax_category_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" 10 | ) 11 | 12 | func TestAccTaxCategory_createAndUpdateWithID(t *testing.T) { 13 | resourceName := "commercetools_tax_category.standard" 14 | name := "test category" 15 | key := "test-category" 16 | description := "test category description" 17 | 18 | newName := "new test category" 19 | newKey := "new-test-category" 20 | newDescription := "new test category description" 21 | 22 | resource.Test(t, resource.TestCase{ 23 | PreCheck: func() { testAccPreCheck(t) }, 24 | ProviderFactories: testAccProviders, 25 | CheckDestroy: testAccCheckTaxCategoryDestroy, 26 | Steps: []resource.TestStep{ 27 | { 28 | Config: testAccTaxCategoryConfig(name, key, description), 29 | Check: resource.ComposeTestCheckFunc( 30 | resource.TestCheckResourceAttr(resourceName, "name", name), 31 | resource.TestCheckResourceAttr(resourceName, "key", key), 32 | resource.TestCheckResourceAttr(resourceName, "description", description), 33 | ), 34 | }, 35 | { 36 | Config: testAccTaxCategoryConfig(newName, newKey, newDescription), 37 | Check: resource.ComposeTestCheckFunc( 38 | resource.TestCheckResourceAttr(resourceName, "name", newName), 39 | resource.TestCheckResourceAttr(resourceName, "key", newKey), 40 | resource.TestCheckResourceAttr(resourceName, "description", newDescription), 41 | ), 42 | }, 43 | }, 44 | }) 45 | } 46 | 47 | func testAccTaxCategoryConfig(name, key, description string) string { 48 | return hclTemplate(` 49 | resource "commercetools_tax_category" "standard" { 50 | name = "{{ .name }}" 51 | key = "{{ .key }}" 52 | description = "{{ .description }}" 53 | } 54 | `, map[string]any{ 55 | "key": key, 56 | "name": name, 57 | "description": description, 58 | }) 59 | 60 | } 61 | 62 | func testAccCheckTaxCategoryDestroy(s *terraform.State) error { 63 | client := getClient(testAccProvider.Meta()) 64 | 65 | for _, rs := range s.RootModule().Resources { 66 | if rs.Type != "commercetools_tax_category" { 67 | continue 68 | } 69 | response, err := client.TaxCategories().WithId(rs.Primary.ID).Get().Execute(context.Background()) 70 | if err == nil { 71 | if response != nil && response.ID == rs.Primary.ID { 72 | return fmt.Errorf("tax category (%s) still exists", rs.Primary.ID) 73 | } 74 | return nil 75 | } 76 | if newErr := checkApiResult(err); newErr != nil { 77 | return newErr 78 | } 79 | } 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /commercetools/resource_cart_discount_stores_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 7 | ) 8 | 9 | func TestAccCartDiscountStores(t *testing.T) { 10 | identifier := "stores" 11 | resourceName := "commercetools_cart_discount.stores" 12 | 13 | resource.Test(t, resource.TestCase{ 14 | PreCheck: func() { testAccPreCheck(t) }, 15 | ProviderFactories: testAccProviders, 16 | CheckDestroy: testAccCheckCartDiscountDestroy, 17 | Steps: []resource.TestStep{ 18 | { 19 | Config: testAccCartDiscountWithoutStores(identifier), 20 | Check: resource.ComposeTestCheckFunc( 21 | resource.TestCheckResourceAttr(resourceName, "stores.#", "0"), 22 | ), 23 | }, 24 | { 25 | Config: testAccCartDiscountWithStores(identifier), 26 | Check: resource.ComposeTestCheckFunc( 27 | resource.TestCheckTypeSetElemAttr(resourceName, "stores.*", "my-store"), 28 | ), 29 | }, 30 | { 31 | Config: testAccCartDiscountWithoutStores(identifier), 32 | Check: resource.ComposeTestCheckFunc( 33 | resource.TestCheckResourceAttr(resourceName, "stores.#", "0"), 34 | ), 35 | }, 36 | }, 37 | }) 38 | } 39 | 40 | func testAccCartDiscountWithoutStores(identifier string) string { 41 | return hclTemplate(` 42 | resource "commercetools_cart_discount" "{{ .identifier }}" { 43 | name = { 44 | en = "fixed name" 45 | } 46 | sort_order = "0.9" 47 | predicate = "1=1" 48 | 49 | target { 50 | type = "shipping" 51 | } 52 | 53 | value { 54 | type = "fixed" 55 | money { 56 | currency_code = "USD" 57 | cent_amount = 1000 58 | } 59 | } 60 | } 61 | `, map[string]any{ 62 | "identifier": identifier, 63 | }) 64 | } 65 | 66 | func testAccCartDiscountWithStores(identifier string) string { 67 | return hclTemplate(` 68 | resource "commercetools_store" "my-store-{{ .identifier }}" { 69 | key = "my-store" 70 | name = { 71 | en-US = "My store" 72 | } 73 | countries = ["NL", "BE"] 74 | languages = ["nl-NL"] 75 | } 76 | 77 | resource "commercetools_cart_discount" "{{ .identifier }}" { 78 | name = { 79 | en = "fixed name" 80 | } 81 | stores = [commercetools_store.my-store-{{ .identifier }}.key] 82 | sort_order = "0.9" 83 | predicate = "1=1" 84 | 85 | target { 86 | type = "shipping" 87 | } 88 | 89 | value { 90 | type = "fixed" 91 | money { 92 | currency_code = "USD" 93 | cent_amount = 1000 94 | } 95 | } 96 | } 97 | `, map[string]any{ 98 | "identifier": identifier, 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /internal/resources/product_selection/model_test.go: -------------------------------------------------------------------------------- 1 | package product_selection 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/attr" 7 | "github.com/hashicorp/terraform-plugin-framework/types" 8 | "github.com/labd/commercetools-go-sdk/platform" 9 | "github.com/labd/terraform-provider-commercetools/internal/customtypes" 10 | "github.com/labd/terraform-provider-commercetools/internal/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestProductSelection_UpdateActions(t *testing.T) { 15 | cases := []struct { 16 | name string 17 | state ProductSelection 18 | plan ProductSelection 19 | expected platform.ProductSelectionUpdate 20 | }{ 21 | { 22 | "product selection update name", 23 | ProductSelection{ 24 | Name: customtypes.NewLocalizedStringValue(map[string]attr.Value{ 25 | "en-US": types.StringValue("Example product selection"), 26 | }), 27 | }, 28 | ProductSelection{ 29 | Name: customtypes.NewLocalizedStringValue(map[string]attr.Value{ 30 | "en-US": types.StringValue("Example other product selection"), 31 | }), 32 | }, 33 | platform.ProductSelectionUpdate{ 34 | Actions: []platform.ProductSelectionUpdateAction{ 35 | platform.ProductSelectionChangeNameAction{ 36 | Name: map[string]string{"en-US": "Example other product selection"}, 37 | }, 38 | }, 39 | }, 40 | }, 41 | } 42 | 43 | for _, c := range cases { 44 | t.Run(c.name, func(t *testing.T) { 45 | result, err := c.state.updateActions(nil, c.plan) 46 | assert.NoError(t, err) 47 | assert.EqualValues(t, c.expected, result) 48 | }) 49 | } 50 | } 51 | 52 | func TestNewProductSelectionFromNative(t *testing.T) { 53 | cases := []struct { 54 | name string 55 | res *platform.ProductSelection 56 | expect ProductSelection 57 | }{ 58 | { 59 | "decode remote product selection representation into local resource", 60 | &platform.ProductSelection{ 61 | ID: "rand-uuid-or-other-string", 62 | Version: 1, 63 | Key: utils.StringRef("ps-1"), 64 | Name: map[string]string{"en-US": "the selection"}, 65 | Mode: platform.ProductSelectionModeIndividual, 66 | }, 67 | ProductSelection{ 68 | ID: types.StringValue("rand-uuid-or-other-string"), 69 | Key: types.StringValue("ps-1"), 70 | Version: types.Int64Value(1), 71 | Name: customtypes.NewLocalizedStringValue(map[string]attr.Value{ 72 | "en-US": types.StringValue("the selection"), 73 | }), 74 | Mode: types.StringValue("Individual"), 75 | }, 76 | }, 77 | } 78 | 79 | for _, c := range cases { 80 | t.Run(c.name, func(t *testing.T) { 81 | got, err := NewProductSelectionFromNative(c.res) 82 | assert.NoError(t, err) 83 | assert.EqualValues(t, got, c.expect) 84 | }) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /internal/customvalidator/requires.go: -------------------------------------------------------------------------------- 1 | package customvalidator 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" 8 | "github.com/hashicorp/terraform-plugin-framework/attr" 9 | "github.com/hashicorp/terraform-plugin-framework/path" 10 | "github.com/hashicorp/terraform-plugin-framework/schema/validator" 11 | "github.com/hashicorp/terraform-plugin-framework/types/basetypes" 12 | ) 13 | 14 | func RequireValueValidator(value string, expressions ...path.Expression) validator.List { 15 | return requireValueValidator{ 16 | value: value, 17 | pathExpressions: expressions, 18 | } 19 | } 20 | 21 | var _ validator.List = requireValueValidator{} 22 | 23 | // anyValidator implements the validator. 24 | type requireValueValidator struct { 25 | value string 26 | pathExpressions path.Expressions 27 | } 28 | 29 | // Description describes the validation in plain text formatting. 30 | func (v requireValueValidator) Description(ctx context.Context) string { 31 | return fmt.Sprintf("Magic") 32 | } 33 | 34 | // MarkdownDescription describes the validation in Markdown formatting. 35 | func (v requireValueValidator) MarkdownDescription(ctx context.Context) string { 36 | return v.Description(ctx) 37 | } 38 | 39 | // ValidateString performs the validation. 40 | func (v requireValueValidator) ValidateList(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) { 41 | 42 | expressions := req.PathExpression.MergeExpressions(v.pathExpressions...) 43 | 44 | for _, expression := range expressions { 45 | matchedPaths, diags := req.Config.PathMatches(ctx, expression) 46 | resp.Diagnostics.Append(diags...) 47 | 48 | // Collect all errors 49 | if diags.HasError() { 50 | continue 51 | } 52 | 53 | for _, mp := range matchedPaths { 54 | // If the user specifies the same attribute this validator is applied to, 55 | // also as part of the input, skip it 56 | if mp.Equal(req.Path) { 57 | continue 58 | } 59 | 60 | var mpVal attr.Value 61 | diags := req.Config.GetAttribute(ctx, mp, &mpVal) 62 | resp.Diagnostics.Append(diags...) 63 | 64 | // Collect all errors 65 | if diags.HasError() { 66 | continue 67 | } 68 | 69 | val, ok := mpVal.(basetypes.StringValue) 70 | if !ok { 71 | resp.Diagnostics.Append(validatordiag.BugInProviderDiagnostic( 72 | fmt.Sprintf("Expression %q must resolve to a string", mp), 73 | )) 74 | } 75 | 76 | if len(req.ConfigValue.Elements()) == 0 { 77 | continue 78 | } 79 | 80 | if val.ValueString() != v.value { 81 | resp.Diagnostics.Append(validatordiag.InvalidAttributeCombinationDiagnostic( 82 | req.Path, 83 | fmt.Sprintf("Block %q can only be specified when %q is %q", req.Path, mp, v.value), 84 | )) 85 | } 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /docs/resources/shipping_method.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_shipping_method Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | With Shipping Methods you can specify which shipping services you want to provide to your customers for deliveries to different areas of the world at rates you can define. 7 | See also the Shipping Methods API Documentation https://docs.commercetools.com/api/projects/shippingMethods 8 | --- 9 | 10 | # commercetools_shipping_method (Resource) 11 | 12 | With Shipping Methods you can specify which shipping services you want to provide to your customers for deliveries to different areas of the world at rates you can define. 13 | 14 | See also the [Shipping Methods API Documentation](https://docs.commercetools.com/api/projects/shippingMethods) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_tax_category" "some-tax-category" { 20 | key = "some-tax-category-key" 21 | name = "some test cateogry" 22 | description = "test category" 23 | } 24 | 25 | resource "commercetools_shipping_method" "standard" { 26 | key = "standard-key" 27 | name = "Standard tax category" 28 | description = "Standard tax category" 29 | is_default = true 30 | tax_category_id = commercetools_tax_category.some-tax-category.id 31 | predicate = "1 = 1" 32 | } 33 | ``` 34 | 35 | 36 | ## Schema 37 | 38 | ### Required 39 | 40 | - `name` (String) 41 | - `tax_category_id` (String) ID of a [Tax Category](https://docs.commercetools.com/api/projects/taxCategories#taxcategory) 42 | 43 | ### Optional 44 | 45 | - `active` (Boolean) Activate or deactivate a shipping method. Default is active. 46 | - `custom` (Block List, Max: 1) (see [below for nested schema](#nestedblock--custom)) 47 | - `description` (String) 48 | - `is_default` (Boolean) One shipping method in a project can be default 49 | - `key` (String) User-specific unique identifier for the shipping method 50 | - `localized_description` (Map of String) [LocalizedString](https://docs.commercetools.com/api/types#localizedstring) 51 | - `localized_name` (Map of String) [LocalizedString](https://docs.commercetools.com/api/types#localizedstring) 52 | - `predicate` (String) A Cart predicate which can be used to more precisely select a shipping method for a cart 53 | 54 | ### Read-Only 55 | 56 | - `id` (String) The ID of this resource. 57 | - `version` (Number) 58 | 59 | 60 | ### Nested Schema for `custom` 61 | 62 | Required: 63 | 64 | - `type_id` (String) 65 | 66 | Optional: 67 | 68 | - `fields` (Map of String) Custom fields for this resource. Note that the values need to be provided as JSON encoded strings: `my-value = jsonencode({"key": "value"})` 69 | -------------------------------------------------------------------------------- /commercetools/marshalling.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "time" 5 | 6 | "fmt" 7 | "github.com/labd/commercetools-go-sdk/platform" 8 | ) 9 | 10 | func flattenTime(val *time.Time) string { 11 | if val == nil { 12 | return "" 13 | } 14 | return val.Format(time.RFC3339) 15 | } 16 | 17 | func expandTime(input string) (time.Time, error) { 18 | return time.Parse(time.RFC3339, input) 19 | } 20 | 21 | func flattenTypedMoney(val platform.TypedMoney) map[string]any { 22 | switch v := val.(type) { 23 | case platform.HighPrecisionMoney: 24 | return map[string]any{ 25 | "currency_code": v.CurrencyCode, 26 | "cent_amount": v.CentAmount, 27 | } 28 | case platform.Money: 29 | return map[string]any{ 30 | "currency_code": v.CurrencyCode, 31 | "cent_amount": v.CentAmount, 32 | } 33 | case platform.CentPrecisionMoney: 34 | return map[string]any{ 35 | "currency_code": v.CurrencyCode, 36 | "cent_amount": v.CentAmount, 37 | } 38 | } 39 | panic(fmt.Sprintf("Unknown money type: %T", val)) 40 | } 41 | 42 | func expandTypedMoney(d map[string]any) []platform.Money { 43 | input := d["money"].([]any) 44 | var result []platform.Money 45 | 46 | for _, raw := range input { 47 | i := raw.(map[string]any) 48 | priceCurrencyCode := i["currency_code"].(string) 49 | 50 | result = append(result, platform.Money{ 51 | CurrencyCode: priceCurrencyCode, 52 | CentAmount: i["cent_amount"].(int), 53 | }) 54 | } 55 | 56 | return result 57 | } 58 | 59 | func expandTypedMoneyDraft(d map[string]any) []platform.TypedMoneyDraft { 60 | input := d["money"].([]any) 61 | var result []platform.TypedMoneyDraft 62 | 63 | for _, raw := range input { 64 | i := raw.(map[string]any) 65 | priceCurrencyCode := i["currency_code"].(string) 66 | 67 | result = append(result, platform.Money{ 68 | CurrencyCode: priceCurrencyCode, 69 | CentAmount: i["cent_amount"].(int), 70 | }) 71 | } 72 | 73 | return result 74 | } 75 | 76 | func expandLocalizedString(val any) platform.LocalizedString { 77 | values, ok := val.(map[string]any) 78 | if !ok { 79 | return platform.LocalizedString{} 80 | } 81 | 82 | result := make(platform.LocalizedString, len(values)) 83 | for k := range values { 84 | result[k] = values[k].(string) 85 | } 86 | return result 87 | } 88 | 89 | func expandMoneyDraft(d map[string]any) []platform.Money { 90 | input := d["money"].([]any) 91 | var result []platform.Money 92 | for _, raw := range input { 93 | data := raw.(map[string]any) 94 | item := platform.Money{} 95 | if currencyCode, ok := data["currency_code"].(string); ok { 96 | item.CurrencyCode = currencyCode 97 | } 98 | if centAmount, ok := data["cent_amount"].(int); ok { 99 | item.CentAmount = centAmount 100 | } 101 | result = append(result, item) 102 | } 103 | return result 104 | } 105 | -------------------------------------------------------------------------------- /internal/utils/errors.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | 9 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 10 | "github.com/labd/commercetools-go-sdk/platform" 11 | ) 12 | 13 | func ProcessRemoteError(err error) *resource.RetryError { 14 | if err == nil { 15 | return nil 16 | } 17 | 18 | switch e := err.(type) { 19 | case platform.ErrorResponse: 20 | if err := extractDetailedError(e); err != nil { 21 | return resource.NonRetryableError(err) 22 | } 23 | return resource.NonRetryableError(e) 24 | 25 | case platform.GenericRequestError: 26 | { 27 | if err := extractRawDetailedError(e.Content); err != nil { 28 | return resource.NonRetryableError(err) 29 | } 30 | return resource.NonRetryableError(e) 31 | } 32 | } 33 | 34 | return resource.RetryableError(err) 35 | } 36 | 37 | func extractDetailedError(e platform.ErrorResponse) error { 38 | for i := range e.Errors { 39 | item := e.Errors[i] 40 | 41 | metaValue := reflect.ValueOf(item) 42 | message := metaValue.FieldByName("Message") 43 | values := metaValue.FieldByName("ExtraValues") 44 | 45 | if message.IsValid() && values.IsValid() { 46 | data := values.Interface().(map[string]any) 47 | if msg, ok := data["detailedErrorMessage"]; ok { 48 | return fmt.Errorf("%s %s", message.String(), msg) 49 | } 50 | } 51 | } 52 | return e 53 | } 54 | 55 | func extractRawDetailedError(content []byte) error { 56 | data := map[string]any{} 57 | if err := json.Unmarshal(content, &data); err != nil { 58 | return nil 59 | } 60 | 61 | // Iterate over the errors. This is a list of objects containing the 62 | // code, message and detailedErrorMessage values. 63 | if val, ok := data["errors"].([]any); ok { 64 | for i := range val { 65 | if error, ok := val[i].(map[string]any); ok { 66 | var message string 67 | 68 | if detail, ok := error["message"].(string); ok { 69 | message = detail 70 | } 71 | 72 | if detail, ok := error["detailedErrorMessage"].(string); ok { 73 | if message != "" { 74 | return fmt.Errorf("%s %s", message, detail) 75 | } 76 | return errors.New(detail) 77 | } 78 | } 79 | } 80 | } 81 | 82 | // Fallback to the generic error message 83 | if val, ok := data["message"].(string); ok { 84 | return errors.New(val) 85 | } 86 | 87 | return nil 88 | } 89 | 90 | // IsResourceNotFoundError returns true if commercetools returned a 404 error 91 | func IsResourceNotFoundError(err error) bool { 92 | //Occasionally the SDK returns a sentinel value instead of the parsed error response for 404. 93 | //This is a workaround to handle that case. 94 | if errors.Is(err, platform.ErrNotFound) { 95 | return true 96 | } 97 | 98 | switch e := err.(type) { 99 | case platform.ResourceNotFoundError: 100 | return true 101 | 102 | case platform.ErrorResponse: 103 | return e.StatusCode == 404 104 | 105 | case platform.GenericRequestError: 106 | return e.StatusCode == 404 107 | } 108 | return false 109 | } 110 | -------------------------------------------------------------------------------- /examples/resources/commercetools_product_type/resource.tf: -------------------------------------------------------------------------------- 1 | resource "commercetools_product_type" "some-generic-properties-product-type" { 2 | key = "some-key" 3 | name = "Some generic product properties" 4 | description = "All the generic product properties" 5 | attribute { 6 | name = "perishable" 7 | label = { 8 | en = "Is perishable" 9 | nl = "Is perishable" 10 | } 11 | required = true 12 | type { 13 | name = "boolean" 14 | } 15 | } 16 | } 17 | 18 | resource "commercetools_product_type" "my-product-type" { 19 | key = "my-product-type-key" 20 | name = "Lens specification" 21 | description = "All the specific info concerning the lens" 22 | 23 | attribute { 24 | name = "autofocus" 25 | label = { 26 | en = "Has autofocus" 27 | nl = "Heeft autofocus" 28 | } 29 | required = true 30 | type { 31 | name = "boolean" 32 | } 33 | } 34 | 35 | attribute { 36 | name = "lens_product_no" 37 | label = { 38 | en = "Lens product number" 39 | nl = "Objectief productnummer" 40 | } 41 | required = true 42 | type { 43 | name = "text" 44 | } 45 | constraint = "Unique" 46 | input_tip = { 47 | en = "Enter the product code" 48 | nl = "Voer de product code in" 49 | } 50 | searchable = true 51 | } 52 | 53 | attribute { 54 | name = "previous_model" 55 | label = { 56 | en = "Previous model" 57 | nl = "Vorig model" 58 | } 59 | type { 60 | name = "reference" 61 | reference_type_id = "product" 62 | } 63 | } 64 | 65 | attribute { 66 | name = "product_properties" 67 | label = { 68 | en = "Product properties" 69 | nl = "Product eigenschappen" 70 | } 71 | required = false 72 | type { 73 | name = "nested" 74 | type_reference = commercetools_product_type.some-generic-properties-product-type.id 75 | } 76 | } 77 | 78 | attribute { 79 | name = "some-flag" 80 | label = { 81 | en = "Some flag" 82 | nl = "Een vlag" 83 | } 84 | required = false 85 | type { 86 | name = "enum" 87 | value { 88 | key = "FLAG-1" 89 | label = "Flag 1" 90 | } 91 | value { 92 | key = "FLAG-2" 93 | label = "FLAG-2" 94 | } 95 | } 96 | } 97 | 98 | attribute { 99 | name = "origin" 100 | label = { 101 | en = "Origin country" 102 | nl = "Land van herkomst" 103 | } 104 | required = false 105 | type { 106 | name = "set" 107 | element_type { 108 | name = "lenum" 109 | localized_value { 110 | key = "NL" 111 | label = { 112 | en = "Netherlands" 113 | nl = "Nederland" 114 | } 115 | } 116 | localized_value { 117 | key = "DE" 118 | label = { 119 | en = "Germany" 120 | nl = "Duitsland" 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /internal/customvalidator/field.go: -------------------------------------------------------------------------------- 1 | package customvalidator 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" 8 | "github.com/hashicorp/terraform-plugin-framework/attr" 9 | "github.com/hashicorp/terraform-plugin-framework/path" 10 | "github.com/hashicorp/terraform-plugin-framework/schema/validator" 11 | ) 12 | 13 | func DependencyValidator(value string, expressions ...path.Expression) validator.String { 14 | return dependencyValidator{ 15 | value: value, 16 | pathExpressions: expressions, 17 | } 18 | } 19 | 20 | var _ validator.String = dependencyValidator{} 21 | 22 | // anyValidator implements the validator. 23 | type dependencyValidator struct { 24 | value string 25 | pathExpressions path.Expressions 26 | } 27 | 28 | // Description describes the validation in plain text formatting. 29 | func (v dependencyValidator) Description(ctx context.Context) string { 30 | return "Validate the existence of specific attributes when the attribute has the given value" 31 | } 32 | 33 | // MarkdownDescription describes the validation in Markdown formatting. 34 | func (v dependencyValidator) MarkdownDescription(ctx context.Context) string { 35 | return v.Description(ctx) 36 | } 37 | 38 | // ValidateString performs the validation. 39 | func (v dependencyValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { 40 | sourceVal := req.ConfigValue.ValueString() 41 | if sourceVal != v.value { 42 | return 43 | } 44 | 45 | expressions := req.PathExpression.MergeExpressions(v.pathExpressions...) 46 | 47 | for _, expression := range expressions { 48 | matchedPaths, diags := req.Config.PathMatches(ctx, expression) 49 | 50 | if diags.HasError() { 51 | if diags.Errors()[0].Summary() == "Invalid Path Expression for Schema Data" { 52 | _, steps := expression.Steps().LastStep() 53 | resp.Diagnostics.Append(validatordiag.InvalidAttributeCombinationDiagnostic( 54 | req.Path.ParentPath(), 55 | fmt.Sprintf("Block %q must be specified when %q is %q", steps, req.Path, sourceVal), 56 | )) 57 | continue 58 | } 59 | } 60 | resp.Diagnostics.Append(diags...) 61 | 62 | // Collect all errors 63 | if diags.HasError() { 64 | continue 65 | } 66 | 67 | for _, mp := range matchedPaths { 68 | // If the user specifies the same attribute this validator is applied to, 69 | // also as part of the input, skip it 70 | if mp.Equal(req.Path) { 71 | continue 72 | } 73 | 74 | var mpVal attr.Value 75 | diags := req.Config.GetAttribute(ctx, mp, &mpVal) 76 | resp.Diagnostics.Append(diags...) 77 | 78 | // Collect all errors 79 | if diags.HasError() { 80 | continue 81 | } 82 | 83 | // Delay validation until all involved attribute have a known value 84 | if mpVal.IsUnknown() { 85 | return 86 | } 87 | 88 | if mpVal.IsNull() { 89 | resp.Diagnostics.Append(validatordiag.InvalidAttributeCombinationDiagnostic( 90 | req.Path, 91 | fmt.Sprintf("Attribute %q must be specified when %q is %q", mp, req.Path, sourceVal), 92 | )) 93 | } 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /docs/guides/custom-fields.md: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "" 3 | page_title: "Custom Fields" 4 | --- 5 | 6 | ## Creating a custom type 7 | 8 | To extend the commercetools platform with custom fields, you can create custom 9 | types that can be attached to resources. The following example shows how to 10 | create a custom type that can be attached to categories. 11 | 12 | For more information see 13 | the [commercetools documentation](https://docs.commercetools.com/api/projects/types). 14 | 15 | ```hcl 16 | resource "commercetools_type" "my-category" { 17 | key = "my-category" 18 | 19 | resource_type_ids = ["category"] 20 | 21 | name = { 22 | en = "myCategory" 23 | } 24 | 25 | description = { 26 | en = "My Category" 27 | } 28 | 29 | field { 30 | name = "myBoolean" 31 | label = { 32 | en = "myBoolean" 33 | } 34 | required = false 35 | type { 36 | name = "Boolean" 37 | } 38 | input_hint = "SingleLine" 39 | } 40 | 41 | field { 42 | name = "myNumber" 43 | label = { 44 | en = "myNumber" 45 | } 46 | required = false 47 | type { 48 | name = "Number" 49 | } 50 | input_hint = "SingleLine" 51 | } 52 | 53 | field { 54 | name = "mySet" 55 | 56 | label = { 57 | en = "mySet" 58 | } 59 | 60 | type { 61 | name = "Set" 62 | element_type { 63 | name = "String" 64 | } 65 | } 66 | } 67 | 68 | field { 69 | name = "myLocalizedString" 70 | label = { 71 | en = "myLocalizedString" 72 | } 73 | required = false 74 | type { 75 | name = "LocalizedString" 76 | } 77 | input_hint = "SingleLine" 78 | } 79 | 80 | field { 81 | name = "mySetOfLocalizedStrings" 82 | label = { 83 | en = "mySetOfLocalizedStrings" 84 | } 85 | required = false 86 | type { 87 | name = "Set" 88 | element_type { 89 | name = "LocalizedString" 90 | } 91 | } 92 | input_hint = "SingleLine" 93 | } 94 | } 95 | ``` 96 | 97 | ## Setting custom fields 98 | 99 | Due to constrains within the old terraform provider framework, the custom fields 100 | are set in terraform state as a map of strings. This means that the actual 101 | values need to be serialized to JSON before they can be set. The following 102 | example shows how to set the custom fields for a category with the custom type 103 | given above. 104 | 105 | ```hcl 106 | resource "commercetools_category" "my-category" { 107 | name = { 108 | en = "My category" 109 | } 110 | slug = { 111 | en = "my-category" 112 | } 113 | 114 | custom { 115 | type_id = commercetools_type.my-category.id 116 | fields = { 117 | myBoolean = jsonencode(true) 118 | myNumber = jsonencode(123) 119 | mySet = jsonencode(["a", "b", "c"]) 120 | myLocalizedString = jsonencode({ 121 | en = "English" 122 | }) 123 | mySetOfLocalizedStrings = jsonencode([ 124 | { en = "English 1" }, 125 | { en = "English 2" } 126 | ]) 127 | } 128 | } 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /internal/datasource/type/resource.go: -------------------------------------------------------------------------------- 1 | package custom_type 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/datasource" 7 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 8 | "github.com/hashicorp/terraform-plugin-framework/types" 9 | "github.com/labd/commercetools-go-sdk/platform" 10 | 11 | "github.com/labd/terraform-provider-commercetools/internal/utils" 12 | ) 13 | 14 | // Ensure the implementation satisfies the expected interfaces. 15 | var ( 16 | _ datasource.DataSource = &CustomTypeSource{} 17 | _ datasource.DataSourceWithConfigure = &CustomTypeSource{} 18 | ) 19 | 20 | // NewDataSource is a helper function to simplify the provider implementation. 21 | func NewDataSource() datasource.DataSource { 22 | return &CustomTypeSource{} 23 | } 24 | 25 | // CustomTypeSource is the data source implementation. 26 | type CustomTypeSource struct { 27 | client *platform.ByProjectKeyRequestBuilder 28 | mutex *utils.MutexKV 29 | } 30 | 31 | // CustomTypeSourceModel maps the data source schema data. 32 | type CustomTypeSourceModel struct { 33 | ID types.String `tfsdk:"id"` 34 | Key types.String `tfsdk:"key"` 35 | } 36 | 37 | // Metadata returns the data source type name. 38 | func (d *CustomTypeSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 39 | resp.TypeName = req.ProviderTypeName + "_type" 40 | } 41 | 42 | // Schema defines the schema for the data source. 43 | func (d *CustomTypeSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { 44 | resp.Schema = schema.Schema{ 45 | Description: "Fetches type information", 46 | Attributes: map[string]schema.Attribute{ 47 | "id": schema.StringAttribute{ 48 | Description: "ID of the custom type", 49 | Computed: true, 50 | }, 51 | "key": schema.StringAttribute{ 52 | Description: "Key of the custom type", 53 | Required: true, 54 | }, 55 | }, 56 | } 57 | } 58 | 59 | // Configure adds the provider configured client to the data source. 60 | func (d *CustomTypeSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { 61 | if req.ProviderData == nil { 62 | return 63 | } 64 | data := req.ProviderData.(*utils.ProviderData) 65 | d.client = data.Client 66 | d.mutex = data.Mutex 67 | } 68 | 69 | // Read refreshes the Terraform state with the latest data. 70 | func (d *CustomTypeSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 71 | var state CustomTypeSourceModel 72 | 73 | diags := req.Config.Get(ctx, &state) 74 | resp.Diagnostics.Append(diags...) 75 | if resp.Diagnostics.HasError() { 76 | return 77 | } 78 | 79 | resource, err := d.client.Types().WithKey(state.Key.ValueString()).Get().Execute(ctx) 80 | if err != nil { 81 | resp.Diagnostics.AddError( 82 | "Unable to Read Type", 83 | err.Error(), 84 | ) 85 | return 86 | } 87 | 88 | state.ID = types.StringValue(resource.ID) 89 | 90 | // Set state 91 | diags = resp.State.Set(ctx, &state) 92 | resp.Diagnostics.Append(diags...) 93 | if resp.Diagnostics.HasError() { 94 | return 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /templates/guides/custom-fields.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | subcategory: "" 3 | page_title: "Custom Fields" 4 | --- 5 | 6 | ## Creating a custom type 7 | 8 | To extend the commercetools platform with custom fields, you can create custom 9 | types that can be attached to resources. The following example shows how to 10 | create a custom type that can be attached to categories. 11 | 12 | For more information see 13 | the [commercetools documentation](https://docs.commercetools.com/api/projects/types). 14 | 15 | ```hcl 16 | resource "commercetools_type" "my-category" { 17 | key = "my-category" 18 | 19 | resource_type_ids = ["category"] 20 | 21 | name = { 22 | en = "myCategory" 23 | } 24 | 25 | description = { 26 | en = "My Category" 27 | } 28 | 29 | field { 30 | name = "myBoolean" 31 | label = { 32 | en = "myBoolean" 33 | } 34 | required = false 35 | type { 36 | name = "Boolean" 37 | } 38 | input_hint = "SingleLine" 39 | } 40 | 41 | field { 42 | name = "myNumber" 43 | label = { 44 | en = "myNumber" 45 | } 46 | required = false 47 | type { 48 | name = "Number" 49 | } 50 | input_hint = "SingleLine" 51 | } 52 | 53 | field { 54 | name = "mySet" 55 | 56 | label = { 57 | en = "mySet" 58 | } 59 | 60 | type { 61 | name = "Set" 62 | element_type { 63 | name = "String" 64 | } 65 | } 66 | } 67 | 68 | field { 69 | name = "myLocalizedString" 70 | label = { 71 | en = "myLocalizedString" 72 | } 73 | required = false 74 | type { 75 | name = "LocalizedString" 76 | } 77 | input_hint = "SingleLine" 78 | } 79 | 80 | field { 81 | name = "mySetOfLocalizedStrings" 82 | label = { 83 | en = "mySetOfLocalizedStrings" 84 | } 85 | required = false 86 | type { 87 | name = "Set" 88 | element_type { 89 | name = "LocalizedString" 90 | } 91 | } 92 | input_hint = "SingleLine" 93 | } 94 | } 95 | ``` 96 | 97 | ## Setting custom fields 98 | 99 | Due to constrains within the old terraform provider framework, the custom fields 100 | are set in terraform state as a map of strings. This means that the actual 101 | values need to be serialized to JSON before they can be set. The following 102 | example shows how to set the custom fields for a category with the custom type 103 | given above. 104 | 105 | ```hcl 106 | resource "commercetools_category" "my-category" { 107 | name = { 108 | en = "My category" 109 | } 110 | slug = { 111 | en = "my-category" 112 | } 113 | 114 | custom { 115 | type_id = commercetools_type.my-category.id 116 | fields = { 117 | myBoolean = jsonencode(true) 118 | myNumber = jsonencode(123) 119 | mySet = jsonencode(["a", "b", "c"]) 120 | myLocalizedString = jsonencode({ 121 | en = "English" 122 | }) 123 | mySetOfLocalizedStrings = jsonencode([ 124 | { en = "English 1" }, 125 | { en = "English 2" } 126 | ]) 127 | } 128 | } 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /internal/datasource/state/resource.go: -------------------------------------------------------------------------------- 1 | package custom_type 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/datasource" 7 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 8 | "github.com/hashicorp/terraform-plugin-framework/types" 9 | "github.com/labd/commercetools-go-sdk/platform" 10 | 11 | "github.com/labd/terraform-provider-commercetools/internal/utils" 12 | ) 13 | 14 | // Ensure the implementation satisfies the expected interfaces. 15 | var ( 16 | _ datasource.DataSource = &StateSource{} 17 | _ datasource.DataSourceWithConfigure = &StateSource{} 18 | ) 19 | 20 | // NewDataSource is a helper function to simplify the data source implementation. 21 | func NewDataSource() datasource.DataSource { 22 | return &StateSource{} 23 | } 24 | 25 | // StateSource is the data source implementation. 26 | type StateSource struct { 27 | client *platform.ByProjectKeyRequestBuilder 28 | mutex *utils.MutexKV 29 | } 30 | 31 | // StateModel maps the data source schema data. 32 | type StateModel struct { 33 | ID types.String `tfsdk:"id"` 34 | Key types.String `tfsdk:"key"` 35 | } 36 | 37 | // Metadata returns the data source type name. 38 | func (d *StateSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 39 | resp.TypeName = req.ProviderTypeName + "_state" 40 | } 41 | 42 | // Schema defines the schema for the data source. 43 | func (d *StateSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { 44 | resp.Schema = schema.Schema{ 45 | Description: "Fetches state information for the given key. " + 46 | "This is an easy way to import the id of an existing state for a given key.", 47 | Attributes: map[string]schema.Attribute{ 48 | "id": schema.StringAttribute{ 49 | Description: "ID of the state", 50 | Computed: true, 51 | }, 52 | "key": schema.StringAttribute{ 53 | Description: "Key of the state", 54 | Required: true, 55 | }, 56 | }, 57 | } 58 | } 59 | 60 | // Configure adds the provider configured client to the data source. 61 | func (d *StateSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { 62 | if req.ProviderData == nil { 63 | return 64 | } 65 | data := req.ProviderData.(*utils.ProviderData) 66 | d.client = data.Client 67 | d.mutex = data.Mutex 68 | } 69 | 70 | // Read refreshes the Terraform state with the latest data. 71 | func (d *StateSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 72 | var state StateModel 73 | 74 | diags := req.Config.Get(ctx, &state) 75 | resp.Diagnostics.Append(diags...) 76 | if resp.Diagnostics.HasError() { 77 | return 78 | } 79 | 80 | resource, err := d.client.States().WithKey(state.Key.ValueString()).Get().Execute(ctx) 81 | if err != nil { 82 | resp.Diagnostics.AddError( 83 | "Unable to read state", 84 | err.Error(), 85 | ) 86 | return 87 | } 88 | 89 | state.ID = types.StringValue(resource.ID) 90 | 91 | // Set state 92 | diags = resp.State.Set(ctx, &state) 93 | resp.Diagnostics.Append(diags...) 94 | if resp.Diagnostics.HasError() { 95 | return 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /internal/resources/product_selection/model.go: -------------------------------------------------------------------------------- 1 | package product_selection 2 | 3 | import ( 4 | "github.com/labd/terraform-provider-commercetools/internal/sharedtypes" 5 | "reflect" 6 | 7 | "github.com/hashicorp/terraform-plugin-framework/types" 8 | "github.com/labd/commercetools-go-sdk/platform" 9 | "github.com/labd/terraform-provider-commercetools/internal/customtypes" 10 | "github.com/labd/terraform-provider-commercetools/internal/utils" 11 | ) 12 | 13 | // ProductSelection represents the main schema data. 14 | type ProductSelection struct { 15 | ID types.String `tfsdk:"id"` 16 | Key types.String `tfsdk:"key"` 17 | Version types.Int64 `tfsdk:"version"` 18 | Name customtypes.LocalizedStringValue `tfsdk:"name"` 19 | Mode types.String `tfsdk:"mode"` 20 | Custom *sharedtypes.Custom `tfsdk:"custom"` 21 | } 22 | 23 | func NewProductSelectionFromNative(ps *platform.ProductSelection) (ProductSelection, error) { 24 | custom, err := sharedtypes.NewCustomFromNative(ps.Custom) 25 | if err != nil { 26 | return ProductSelection{}, err 27 | } 28 | 29 | return ProductSelection{ 30 | ID: types.StringValue(ps.ID), 31 | Version: types.Int64Value(int64(ps.Version)), 32 | Name: utils.FromLocalizedString(ps.Name), 33 | Key: utils.FromOptionalString(ps.Key), 34 | Mode: types.StringValue(string(ps.Mode)), 35 | Custom: custom, 36 | }, err 37 | } 38 | 39 | func (ps ProductSelection) draft(t *platform.Type) (platform.ProductSelectionDraft, error) { 40 | custom, err := ps.Custom.Draft(t) 41 | if err != nil { 42 | return platform.ProductSelectionDraft{}, err 43 | } 44 | 45 | return platform.ProductSelectionDraft{ 46 | Key: ps.Key.ValueStringPointer(), 47 | Name: ps.Name.ValueLocalizedString(), 48 | Mode: (*platform.ProductSelectionMode)(ps.Mode.ValueStringPointer()), 49 | Custom: custom, 50 | }, nil 51 | } 52 | 53 | func (ps ProductSelection) updateActions(t *platform.Type, plan ProductSelection) (platform.ProductSelectionUpdate, error) { 54 | result := platform.ProductSelectionUpdate{ 55 | Version: int(ps.Version.ValueInt64()), 56 | Actions: []platform.ProductSelectionUpdateAction{}, 57 | } 58 | 59 | // setName 60 | if !reflect.DeepEqual(ps.Name, plan.Name) { 61 | result.Actions = append( 62 | result.Actions, 63 | platform.ProductSelectionChangeNameAction{ 64 | Name: plan.Name.ValueLocalizedString(), 65 | }) 66 | } 67 | 68 | // changeKey 69 | if ps.Key != plan.Key { 70 | result.Actions = append( 71 | result.Actions, 72 | platform.ProductSelectionSetKeyAction{Key: plan.Key.ValueStringPointer()}) 73 | } 74 | 75 | // setCustomFields 76 | if !reflect.DeepEqual(ps.Custom, plan.Custom) { 77 | actions, err := sharedtypes.CustomFieldUpdateActions[ 78 | platform.ProductSelectionSetCustomTypeAction, 79 | platform.ProductSelectionSetCustomFieldAction, 80 | ](t, ps.Custom, plan.Custom) 81 | if err != nil { 82 | return platform.ProductSelectionUpdate{}, err 83 | } 84 | 85 | for i := range actions { 86 | result.Actions = append(result.Actions, actions[i].(platform.AssociateRoleUpdateAction)) 87 | } 88 | } 89 | 90 | return result, nil 91 | } 92 | -------------------------------------------------------------------------------- /commercetools/utils_test.go: -------------------------------------------------------------------------------- 1 | package commercetools 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | 9 | "github.com/labd/commercetools-go-sdk/platform" 10 | ) 11 | 12 | func TestCreateLookup(t *testing.T) { 13 | input := []any{ 14 | map[string]any{ 15 | "name": "name1", 16 | "value": "Value 1", 17 | }, 18 | map[string]any{ 19 | "name": "name2", 20 | "value": "Value 2", 21 | }, 22 | } 23 | result := createLookup(input, "name") 24 | if _, ok := result["name1"]; !ok { 25 | t.Error("Could not lookup name1") 26 | } 27 | if _, ok := result["name2"]; !ok { 28 | t.Error("Could not lookup name1") 29 | } 30 | } 31 | 32 | func TestCompareDateString(t *testing.T) { 33 | type testCase struct { 34 | a string 35 | b string 36 | expected bool 37 | } 38 | 39 | testCases := []testCase{ 40 | {"2018-01-02T15:04:05.000Z", "2018-01-02T15:04:05.000Z", true}, 41 | {"2017-03-04T10:01:02.000Z", "2018-01-02T15:04:05.000Z", false}, 42 | {"2018-01-02T15:04:05.000Z", "2018-01-02T15:04:05Z", true}, 43 | {"2018-01-02T15:04:05Z", "2018-01-02T15:04:05Z", true}, 44 | {"2018-01-02T15:04:05Z", "2018-01-02T15:04:05.999Z", true}, 45 | {"2018-01-02T15:04:04Z", "2018-01-02T15:04:05Z", false}, 46 | {"2018-01-02T15:06:04Z", "2018-01-02T15:04:05.999Z", false}, 47 | {"", "2018-01-02T15:04:05.999Z", false}, 48 | {"", "xxx", false}, 49 | {"", "", true}, 50 | } 51 | 52 | var res bool 53 | for _, tt := range testCases { 54 | res = compareDateString(tt.a, tt.b) 55 | if res != tt.expected { 56 | t.Errorf("failures %v, got %v", tt.expected, res) 57 | } 58 | } 59 | 60 | } 61 | 62 | func checkApiResult(err error) error { 63 | if errors.Is(err, platform.ErrNotFound) { 64 | return nil 65 | } 66 | 67 | switch v := err.(type) { 68 | case platform.GenericRequestError: 69 | if v.StatusCode == 404 { 70 | return nil 71 | } 72 | return fmt.Errorf("unhandled error generic error returned (%d)", v.StatusCode) 73 | case platform.ResourceNotFoundError: 74 | return nil 75 | default: 76 | return fmt.Errorf("unexpected result returned") 77 | } 78 | } 79 | 80 | func TestIntNilIfEmpty(t *testing.T) { 81 | testCases := []struct { 82 | input *int 83 | expected *int 84 | }{ 85 | {nil, nil}, 86 | {intRef(0), nil}, 87 | {intRef(1), intRef(1)}, 88 | } 89 | 90 | for _, tt := range testCases { 91 | v := intNilIfEmpty(tt.input) 92 | assert.Equal(t, tt.expected, v) 93 | } 94 | } 95 | 96 | func TestValidateLocalizedStringKey(t *testing.T) { 97 | testCases := []struct { 98 | input interface{} 99 | failures int 100 | }{ 101 | {map[string]any{"en": "English"}, 0}, 102 | {map[string]any{"en-US": "English (United States)"}, 0}, 103 | {map[string]any{"es-419": "Spanish (Latin America)"}, 0}, 104 | {map[string]any{"rm-sursilv": "Romansh Sursilvan"}, 0}, 105 | {map[string]any{"sr-Cyrl": "Serbian in Cyrillic"}, 0}, 106 | {map[string]any{"foobar": "Fail"}, 1}, 107 | {"foobar", 1}, 108 | {1, 1}, 109 | {map[string]any{"es-409": "Spanish (Latin America)"}, 1}, 110 | } 111 | 112 | for _, tt := range testCases { 113 | diag := validateLocalizedStringKey(tt.input, nil) 114 | assert.Equal(t, tt.failures, len(diag), fmt.Sprintf("%+v", diag)) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /docs/resources/channel.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_channel Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Channels represent a source or destination of different entities. They can be used to model warehouses or stores. 7 | See also the Channels API Documentation https://docs.commercetools.com/api/projects/channels 8 | --- 9 | 10 | # commercetools_channel (Resource) 11 | 12 | Channels represent a source or destination of different entities. They can be used to model warehouses or stores. 13 | 14 | See also the [Channels API Documentation](https://docs.commercetools.com/api/projects/channels) 15 | 16 | ## Example Usage 17 | 18 | ```terraform 19 | resource "commercetools_channel" "my-channel" { 20 | key = "my-channel-key" 21 | roles = ["ProductDistribution"] 22 | name = { 23 | nl-NL = "Channel" 24 | } 25 | description = { 26 | nl-NL = "Channel" 27 | } 28 | } 29 | ``` 30 | 31 | 32 | ## Schema 33 | 34 | ### Required 35 | 36 | - `key` (String) Any arbitrary string key that uniquely identifies this channel within the project 37 | - `roles` (List of String) The [roles](https://docs.commercetools.com/api/projects/channels#channelroleenum) of this channel. Each channel must have at least one role 38 | 39 | ### Optional 40 | 41 | - `address` (Block List, Max: 1) (see [below for nested schema](#nestedblock--address)) 42 | - `custom` (Block List, Max: 1) (see [below for nested schema](#nestedblock--custom)) 43 | - `description` (Map of String) [LocalizedString](https://docs.commercetools.com/api/types#localizedstring) 44 | - `geolocation` (Block List, Max: 1) (see [below for nested schema](#nestedblock--geolocation)) 45 | - `name` (Map of String) [LocalizedString](https://docs.commercetools.com/api/types#localizedstring) 46 | 47 | ### Read-Only 48 | 49 | - `id` (String) The ID of this resource. 50 | - `version` (Number) 51 | 52 | 53 | ### Nested Schema for `address` 54 | 55 | Required: 56 | 57 | - `country` (String) 58 | 59 | Optional: 60 | 61 | - `additional_address_info` (String) 62 | - `additional_street_info` (String) 63 | - `apartment` (String) 64 | - `building` (String) 65 | - `city` (String) 66 | - `company` (String) 67 | - `department` (String) 68 | - `email` (String) 69 | - `external_id` (String) 70 | - `fax` (String) 71 | - `first_name` (String) 72 | - `key` (String) 73 | - `last_name` (String) 74 | - `mobile` (String) 75 | - `phone` (String) 76 | - `po_box` (String) 77 | - `postal_code` (String) 78 | - `region` (String) 79 | - `salutation` (String) 80 | - `state` (String) 81 | - `street_name` (String) 82 | - `street_number` (String) 83 | - `title` (String) 84 | 85 | Read-Only: 86 | 87 | - `id` (String) 88 | 89 | 90 | 91 | ### Nested Schema for `custom` 92 | 93 | Required: 94 | 95 | - `type_id` (String) 96 | 97 | Optional: 98 | 99 | - `fields` (Map of String) Custom fields for this resource. Note that the values need to be provided as JSON encoded strings: `my-value = jsonencode({"key": "value"})` 100 | 101 | 102 | 103 | ### Nested Schema for `geolocation` 104 | 105 | Required: 106 | 107 | - `coordinates` (List of Number) 108 | -------------------------------------------------------------------------------- /docs/resources/state_transitions.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "commercetools_state_transitions Resource - terraform-provider-commercetools" 4 | subcategory: "" 5 | description: |- 6 | Transitions are a way to describe possible transformations of the current state to other states of the same type (for example: Initial -> Shipped). When performing a transitionState update action and transitions is set, the currently referenced state must have a transition to the new state. 7 | If transitions is an empty list, it means the current state is a final state and no further transitions are allowed. 8 | If transitions is not set, the validation is turned off. When performing a transitionState update action, any other state of the same type can be transitioned to. 9 | Note: Only one resource can be created for each state 10 | --- 11 | 12 | # commercetools_state_transitions (Resource) 13 | 14 | Transitions are a way to describe possible transformations of the current state to other states of the same type (for example: Initial -> Shipped). When performing a transitionState update action and transitions is set, the currently referenced state must have a transition to the new state. 15 | If transitions is an empty list, it means the current state is a final state and no further transitions are allowed. 16 | If transitions is not set, the validation is turned off. When performing a transitionState update action, any other state of the same type can be transitioned to. 17 | 18 | Note: Only one resource can be created for each state 19 | 20 | ## Example Usage 21 | 22 | ```terraform 23 | resource "commercetools_state" "product_for_sale" { 24 | key = "product-for-sale" 25 | type = "ProductState" 26 | name = { 27 | en = "For Sale" 28 | } 29 | description = { 30 | en = "Regularly stocked product." 31 | } 32 | initial = true 33 | } 34 | 35 | resource "commercetools_state" "product_clearance" { 36 | key = "product-clearance" 37 | type = "ProductState" 38 | name = { 39 | en = "On Clearance" 40 | } 41 | description = { 42 | en = "The product line will not be ordered again." 43 | } 44 | } 45 | 46 | 47 | // Only allow transition from sale to clearance 48 | resource "commercetools_state_transitions" "transition_1" { 49 | from = commercetools_state.product_for_sale.id 50 | to = [ 51 | commercetools_state.product_clearance.id, 52 | ] 53 | } 54 | 55 | // Disable transitions from product clearance to other 56 | resource "commercetools_state_transitions" "transition_2" { 57 | from = commercetools_state.product_clearance.id 58 | to = [] 59 | } 60 | ``` 61 | 62 | 63 | ## Schema 64 | 65 | ### Required 66 | 67 | - `from` (String) ID of the state to transition from 68 | - `to` (Set of String) Transitions are a way to describe possible transformations of the current state to other states of the same type (for example: Initial -> Shipped). When performing a transitionState update action and transitions is set, the currently referenced state must have a transition to the new state. 69 | If transitions is an empty list, it means the current state is a final state and no further transitions are allowed. 70 | If transitions is not set, the validation is turned off. When performing a transitionState update action, any other state of the same type can be transitioned to 71 | 72 | ### Read-Only 73 | 74 | - `id` (String) ID of the state to transition from 75 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "commercetools provider" 3 | subcategory: "" 4 | description: |- 5 | The commercetools provider provides resources to interact with the commercetools API 6 | 7 | --- 8 | 9 | # commercetools provider 10 | 11 | ## Commercial support 12 | Need support implementing this terraform module in your organization? We are 13 | able to offer support. Please contact us at 14 | [opensource@labdigital.nl](opensource@labdigital.nl)! 15 | 16 | ## Installation 17 | Terraform automatically downloads providers from the terraform registry. Add the 18 | following to your terraform project 19 | 20 | 21 | ```hcl 22 | terraform { 23 | required_providers { 24 | commercetools = { 25 | source = "labd/commercetools" 26 | } 27 | } 28 | } 29 | ``` 30 | 31 | Packages of the releases are available at [the GitHub Repo](https://github.com/labd/terraform-provider-commercetools/releases). 32 | See the [terraform documentation](https://www.terraform.io/docs/configuration/providers.html#third-party-plugins) 33 | for more information about installing third-party providers. 34 | 35 | 36 | ## Using the provider 37 | The provider attempts to read the required values from environment variables: 38 | - `CTP_CLIENT_ID` 39 | - `CTP_CLIENT_SECRET` 40 | - `CTP_PROJECT_KEY` 41 | - `CTP_SCOPES` 42 | - `CTP_API_URL` 43 | - `CTP_AUTH_URL` 44 | 45 | Alternatively, you can set it up directly in the terraform file: 46 | 47 | ```hcl 48 | provider "commercetools" { 49 | client_id = "" 50 | client_secret = "" 51 | project_key = "" 52 | scopes = "" 53 | api_url = "" 54 | token_url = "" 55 | } 56 | ``` 57 | 58 | 59 | ## Schema 60 | 61 | ### Optional 62 | 63 | - `api_url` (String) The API URL of the commercetools platform. https://docs.commercetools.com/api/general-concepts#hosts 64 | - `client_id` (String, Sensitive) The OAuth Client ID for a commercetools platform project. https://docs.commercetools.com/api/authorization 65 | - `client_secret` (String, Sensitive) The OAuth Client Secret for a commercetools platform project. https://docs.commercetools.com/api/authorization 66 | - `project_key` (String, Sensitive) The project key of commercetools platform project. https://docs.commercetools.com/getting-started 67 | - `scopes` (String) A list as string of OAuth scopes assigned to a project key, to access resources in a commercetools platform project. https://docs.commercetools.com/api/authorization 68 | - `token_url` (String) The authentication URL of the commercetools platform. https://docs.commercetools.com/api/authorization 69 | 70 | ## Using with docker 71 | 72 | The included `Dockerfile` bundles the official [`hashicorp/terraform:light`](https://hub.docker.com/r/hashicorp/terraform/) docker image with 73 | our `terraform-provider-commercetools`. 74 | 75 | To build the docker image file locally, use: 76 | ```sh 77 | docker build . -t terraform-with-provider-commercetools:latest 78 | ``` 79 | Then you can run a terraform command on files in the current directory with: 80 | ```sh 81 | docker run -v "${pwd}:/config" terraform-with-provider-commercetools:latest 82 | ``` 83 | 84 | ## Authors 85 | This project is developed by [Lab Digital](https://www.labdigital.nl). We 86 | welcome additional contributors. Please see our 87 | [GitHub repository](https://github.com/labd/terraform-provider-commercetools) 88 | for more information. 89 | -------------------------------------------------------------------------------- /internal/resources/subscription/downgrade_v2.go: -------------------------------------------------------------------------------- 1 | package subscription 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/resource" 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | "github.com/hashicorp/terraform-plugin-go/tftypes" 9 | ) 10 | 11 | var SubscriptionResourceV2 = tftypes.Object{ 12 | AttributeTypes: map[string]tftypes.Type{ 13 | "id": tftypes.String, 14 | "key": tftypes.String, 15 | "version": tftypes.Number, 16 | 17 | "changes": tftypes.Set{ 18 | ElementType: tftypes.Object{ 19 | AttributeTypes: map[string]tftypes.Type{ 20 | "resource_type_ids": tftypes.List{ 21 | ElementType: tftypes.String, 22 | }, 23 | }, 24 | }, 25 | }, 26 | "destination": tftypes.Object{ 27 | AttributeTypes: map[string]tftypes.Type{ 28 | "type": tftypes.String, 29 | "topic_arn": tftypes.String, 30 | "queue_url": tftypes.String, 31 | "region": tftypes.String, 32 | "account_id": tftypes.String, 33 | "access_key": tftypes.String, 34 | "access_secret": tftypes.String, 35 | "uri": tftypes.String, 36 | "connection_string": tftypes.String, 37 | "project_id": tftypes.String, 38 | "topic": tftypes.String, 39 | }, 40 | }, 41 | "format": tftypes.Object{ 42 | AttributeTypes: map[string]tftypes.Type{ 43 | "type": tftypes.String, 44 | "cloud_events_version": tftypes.String, 45 | }, 46 | }, 47 | "message": tftypes.Set{ 48 | ElementType: tftypes.Object{ 49 | AttributeTypes: map[string]tftypes.Type{ 50 | "resource_type_id": tftypes.String, 51 | "types": tftypes.List{ 52 | ElementType: tftypes.String, 53 | }, 54 | }, 55 | }, 56 | }, 57 | }, 58 | } 59 | 60 | // Schema version 1 used a list for destination and format since 61 | // that single nested blocks were not supported in sdk v2 (it was in sdk v1) 62 | // Schema version 2 moves us to Single nested blocks, but it turned out to be 63 | // not working correctly in terraform for now. So we moved back to the v1 64 | // approach 65 | func downgradeStateV2(_ context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { 66 | rawStateValue, err := req.RawState.Unmarshal(SubscriptionResourceV2) 67 | if err != nil { 68 | resp.Diagnostics.AddError( 69 | "Unable to Unmarshal Prior State", 70 | err.Error(), 71 | ) 72 | return 73 | } 74 | 75 | var rawState map[string]tftypes.Value 76 | if err := rawStateValue.As(&rawState); err != nil { 77 | resp.Diagnostics.AddError( 78 | "Unable to Convert Prior State", 79 | err.Error(), 80 | ) 81 | return 82 | } 83 | 84 | dynamicValue, err := tfprotov6.NewDynamicValue( 85 | SubscriptionResourceV1, 86 | tftypes.NewValue(SubscriptionResourceV1, map[string]tftypes.Value{ 87 | "id": rawState["id"], 88 | "key": rawState["key"], 89 | "version": rawState["version"], 90 | "changes": rawState["changes"], 91 | "destination": valueDestinationV1(rawState, "destination"), 92 | "format": valueToFormatV1(rawState, "format"), 93 | "message": rawState["message"], 94 | }), 95 | ) 96 | if err != nil { 97 | resp.Diagnostics.AddError( 98 | "Unable to Convert Upgraded State", 99 | err.Error(), 100 | ) 101 | return 102 | } 103 | 104 | resp.DynamicValue = &dynamicValue 105 | } 106 | -------------------------------------------------------------------------------- /internal/resources/attribute_group/model.go: -------------------------------------------------------------------------------- 1 | package attribute_group 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-framework/types" 5 | "github.com/labd/commercetools-go-sdk/platform" 6 | "github.com/labd/terraform-provider-commercetools/internal/customtypes" 7 | "github.com/labd/terraform-provider-commercetools/internal/utils" 8 | "reflect" 9 | ) 10 | 11 | // AttributeGroup is the main resource schema data 12 | type AttributeGroup struct { 13 | ID types.String `tfsdk:"id"` 14 | Version types.Int64 `tfsdk:"version"` 15 | Key types.String `tfsdk:"key"` 16 | Name customtypes.LocalizedStringValue `tfsdk:"name"` 17 | Description customtypes.LocalizedStringValue `tfsdk:"description"` 18 | Attributes []AttributeReference `tfsdk:"attribute"` 19 | } 20 | 21 | type AttributeReference struct { 22 | Key types.String `tfsdk:"key"` 23 | } 24 | 25 | func toDraft(g *AttributeGroup) platform.AttributeGroupDraft { 26 | var attributes []platform.AttributeReference 27 | for _, v := range g.Attributes { 28 | attributes = append(attributes, platform.AttributeReference{ 29 | Key: v.Key.ValueString(), 30 | }) 31 | } 32 | 33 | return platform.AttributeGroupDraft{ 34 | Name: g.Name.ValueLocalizedString(), 35 | Description: g.Description.ValueLocalizedStringRef(), 36 | Key: g.Key.ValueStringPointer(), 37 | Attributes: attributes, 38 | } 39 | } 40 | 41 | func fromNative(n *platform.AttributeGroup) AttributeGroup { 42 | var attributes []AttributeReference 43 | for _, v := range n.Attributes { 44 | attributes = append(attributes, AttributeReference{ 45 | Key: types.StringValue(v.Key), 46 | }) 47 | } 48 | 49 | return AttributeGroup{ 50 | ID: types.StringValue(n.ID), 51 | Version: types.Int64Value(int64(n.Version)), 52 | Key: types.StringPointerValue(n.Key), 53 | Name: utils.FromLocalizedString(n.Name), 54 | Description: utils.FromOptionalLocalizedString(n.Description), 55 | Attributes: attributes, 56 | } 57 | } 58 | 59 | func toUpdateActions(state *AttributeGroup, plan *AttributeGroup) platform.AttributeGroupUpdate { 60 | update := platform.AttributeGroupUpdate{ 61 | Version: int(state.Version.ValueInt64()), 62 | Actions: []platform.AttributeGroupUpdateAction{}, 63 | } 64 | 65 | if !reflect.DeepEqual(state.Name, plan.Name) { 66 | update.Actions = append( 67 | update.Actions, 68 | platform.AttributeGroupChangeNameAction{ 69 | Name: plan.Name.ValueLocalizedString(), 70 | }, 71 | ) 72 | } 73 | 74 | if !reflect.DeepEqual(state.Description, plan.Description) { 75 | update.Actions = append( 76 | update.Actions, 77 | platform.AttributeGroupSetDescriptionAction{ 78 | Description: plan.Description.ValueLocalizedStringRef(), 79 | }, 80 | ) 81 | } 82 | 83 | if !reflect.DeepEqual(state.Key, plan.Key) { 84 | update.Actions = append( 85 | update.Actions, 86 | platform.AttributeGroupSetKeyAction{ 87 | Key: utils.StringRef(plan.Key.ValueString()), 88 | }, 89 | ) 90 | } 91 | 92 | if !reflect.DeepEqual(state.Attributes, plan.Attributes) { 93 | var attributes []platform.AttributeReference 94 | for _, v := range plan.Attributes { 95 | attributes = append(attributes, platform.AttributeReference{ 96 | Key: v.Key.ValueString(), 97 | }) 98 | } 99 | 100 | update.Actions = append( 101 | update.Actions, 102 | platform.AttributeGroupSetAttributesAction{ 103 | Attributes: attributes, 104 | }, 105 | ) 106 | } 107 | 108 | return update 109 | } 110 | --------------------------------------------------------------------------------