├── .changes ├── 0.1.0.md ├── 0.2.0.md ├── 0.3.0.md ├── 0.3.1.md ├── 0.4.0.md ├── 0.4.1.md └── unreleased │ └── .gitkeep ├── .changie.yaml ├── .copywrite.hcl ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── dependabot.yml └── workflows │ ├── ci-changie.yml │ ├── ci-github-actions.yml │ ├── ci-go.yml │ ├── ci-goreleaser.yml │ ├── compliance.yml │ ├── issue-comment-triage.yml │ ├── lock.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── CHANGELOG.md ├── LICENSE ├── META.d └── _summary.yaml ├── Makefile ├── README.md ├── cmd └── tfplugingen-framework │ ├── main.go │ └── version.go ├── go.mod ├── go.sum ├── internal ├── cmd │ ├── generate.go │ ├── generate_all.go │ ├── generate_all_test.go │ ├── generate_data_sources.go │ ├── generate_data_sources_test.go │ ├── generate_provider.go │ ├── generate_provider_test.go │ ├── generate_resources.go │ ├── generate_resources_test.go │ ├── generate_test.go │ ├── scaffold.go │ ├── scaffold_data_source.go │ ├── scaffold_data_source_test.go │ ├── scaffold_provider.go │ ├── scaffold_provider_test.go │ ├── scaffold_resource.go │ ├── scaffold_resource_test.go │ └── testdata │ │ ├── custom_and_external │ │ ├── all_output │ │ │ ├── default_pkg_name │ │ │ │ ├── datasource_example │ │ │ │ │ └── example_data_source_gen.go │ │ │ │ ├── provider_example │ │ │ │ │ └── example_provider_gen.go │ │ │ │ └── resource_example │ │ │ │ │ └── example_resource_gen.go │ │ │ └── specified_pkg_name │ │ │ │ ├── example_data_source_gen.go │ │ │ │ ├── example_provider_gen.go │ │ │ │ └── example_resource_gen.go │ │ ├── data_sources_output │ │ │ └── example_data_source_gen.go │ │ ├── ir.json │ │ ├── provider_output │ │ │ └── example_provider_gen.go │ │ └── resources_output │ │ │ └── example_resource_gen.go │ │ ├── provider_no_attributes │ │ ├── ir.json │ │ └── provider_output │ │ │ └── example_provider_gen.go │ │ └── scaffold │ │ ├── data_source │ │ └── thing_data_source.go │ │ ├── provider │ │ └── provider.go │ │ └── resource │ │ └── thing_resource.go ├── convert │ ├── computed_optional_required.go │ ├── custom_type_collection.go │ ├── custom_type_nested_collection.go │ ├── custom_type_nested_object.go │ ├── custom_type_object.go │ ├── custom_type_primitive.go │ ├── default_bool.go │ ├── default_custom.go │ ├── default_float64.go │ ├── default_int64.go │ ├── default_string.go │ ├── deprecation_message.go │ ├── description.go │ ├── element_type.go │ ├── nested_attribute_object.go │ ├── nested_block_object.go │ ├── object_attribute_types.go │ ├── optional_required.go │ ├── plan_modifiers.go │ ├── sensitive.go │ └── validators.go ├── datasource │ ├── bool_attribute.go │ ├── bool_attribute_test.go │ ├── convert.go │ ├── convert_test.go │ ├── float64_attribute.go │ ├── float64_attribute_test.go │ ├── int64_attribute.go │ ├── int64_attribute_test.go │ ├── list_attribute.go │ ├── list_attribute_test.go │ ├── list_nested_attribute.go │ ├── list_nested_attribute_test.go │ ├── list_nested_block.go │ ├── list_nested_block_test.go │ ├── map_attribute.go │ ├── map_attribute_test.go │ ├── map_nested_attribute.go │ ├── map_nested_attribute_test.go │ ├── number_attribute.go │ ├── number_attribute_test.go │ ├── object_attribute.go │ ├── object_attribute_test.go │ ├── schemas_test.go │ ├── set_attribute.go │ ├── set_attribute_test.go │ ├── set_nested_attribute.go │ ├── set_nested_attribute_test.go │ ├── set_nested_block.go │ ├── set_nested_block_test.go │ ├── single_nested_attribute.go │ ├── single_nested_attribute_test.go │ ├── single_nested_block.go │ ├── single_nested_block_test.go │ ├── string_attribute.go │ ├── string_attribute_test.go │ ├── testdata │ │ └── model.txt │ └── types.go ├── format │ └── format.go ├── input │ └── read.go ├── logging │ └── context.go ├── model │ └── model.go ├── output │ └── write.go ├── provider │ ├── bool_attribute.go │ ├── bool_attribute_test.go │ ├── convert.go │ ├── convert_test.go │ ├── float64_attribute.go │ ├── float64_attribute_test.go │ ├── int64_attribute.go │ ├── int64_attribute_test.go │ ├── list_attribute.go │ ├── list_attribute_test.go │ ├── list_nested_attribute.go │ ├── list_nested_attribute_test.go │ ├── list_nested_block.go │ ├── list_nested_block_test.go │ ├── map_attribute.go │ ├── map_attribute_test.go │ ├── map_nested_attribute.go │ ├── map_nested_attribute_test.go │ ├── number_attribute.go │ ├── number_attribute_test.go │ ├── object_attribute.go │ ├── object_attribute_test.go │ ├── schemas_test.go │ ├── set_attribute.go │ ├── set_attribute_test.go │ ├── set_nested_attribute.go │ ├── set_nested_attribute_test.go │ ├── set_nested_block.go │ ├── set_nested_block_test.go │ ├── single_nested_attribute.go │ ├── single_nested_attribute_test.go │ ├── single_nested_block.go │ ├── single_nested_block_test.go │ ├── string_attribute.go │ ├── string_attribute_test.go │ ├── testdata │ │ └── model.txt │ └── types.go ├── resource │ ├── bool_attribute.go │ ├── bool_attribute_test.go │ ├── convert.go │ ├── convert_test.go │ ├── float64_attribute.go │ ├── float64_attribute_test.go │ ├── int64_attribute.go │ ├── int64_attribute_test.go │ ├── list_attribute.go │ ├── list_attribute_test.go │ ├── list_nested_attribute.go │ ├── list_nested_attribute_test.go │ ├── list_nested_block.go │ ├── list_nested_block_test.go │ ├── map_attribute.go │ ├── map_attribute_test.go │ ├── map_nested_attribute.go │ ├── map_nested_attribute_test.go │ ├── nested_attribute_object.go │ ├── nested_block_object.go │ ├── number_attribute.go │ ├── number_attribute_test.go │ ├── object_attribute.go │ ├── object_attribute_test.go │ ├── schemas_test.go │ ├── set_attribute.go │ ├── set_attribute_test.go │ ├── set_nested_attribute.go │ ├── set_nested_attribute_test.go │ ├── set_nested_block.go │ ├── set_nested_block_test.go │ ├── single_nested_attribute.go │ ├── single_nested_attribute_test.go │ ├── single_nested_block.go │ ├── single_nested_block_test.go │ ├── string_attribute.go │ ├── string_attribute_test.go │ ├── testdata │ │ └── model.txt │ └── types.go ├── scaffold │ ├── data_source.go │ ├── embed.go │ ├── provider.go │ ├── resource.go │ └── templates │ │ ├── data_source_scaffold.gotmpl │ │ ├── provider_scaffold.gotmpl │ │ └── resource_scaffold.gotmpl ├── schema │ ├── associated_external_type.go │ ├── attributes.go │ ├── attrtypes.go │ ├── blocks.go │ ├── custom_bool.go │ ├── custom_bool_test.go │ ├── custom_float64.go │ ├── custom_float64_test.go │ ├── custom_int64.go │ ├── custom_int64_test.go │ ├── custom_list.go │ ├── custom_list_test.go │ ├── custom_map.go │ ├── custom_map_test.go │ ├── custom_nested_object.go │ ├── custom_nested_object_test.go │ ├── custom_number.go │ ├── custom_number_test.go │ ├── custom_object.go │ ├── custom_object_test.go │ ├── custom_set.go │ ├── custom_set_test.go │ ├── custom_string.go │ ├── custom_string_test.go │ ├── elements.go │ ├── embed.go │ ├── errors.go │ ├── framework_identifier.go │ ├── framework_identifier_test.go │ ├── import.go │ ├── schema.go │ ├── schemas.go │ ├── templates │ │ ├── bool_from.gotmpl │ │ ├── bool_to.gotmpl │ │ ├── bool_type_equal.gotmpl │ │ ├── bool_type_string.gotmpl │ │ ├── bool_type_typable.gotmpl │ │ ├── bool_type_type.gotmpl │ │ ├── bool_type_value_from_bool.gotmpl │ │ ├── bool_type_value_from_terraform.gotmpl │ │ ├── bool_type_value_type.gotmpl │ │ ├── bool_value_equal.gotmpl │ │ ├── bool_value_type.gotmpl │ │ ├── bool_value_valuable.gotmpl │ │ ├── bool_value_value.gotmpl │ │ ├── float64_from.gotmpl │ │ ├── float64_to.gotmpl │ │ ├── float64_type_equal.gotmpl │ │ ├── float64_type_string.gotmpl │ │ ├── float64_type_typable.gotmpl │ │ ├── float64_type_type.gotmpl │ │ ├── float64_type_value_from_float64.gotmpl │ │ ├── float64_type_value_from_terraform.gotmpl │ │ ├── float64_type_value_type.gotmpl │ │ ├── float64_value_equal.gotmpl │ │ ├── float64_value_type.gotmpl │ │ ├── float64_value_valuable.gotmpl │ │ ├── float64_value_value.gotmpl │ │ ├── int64_from.gotmpl │ │ ├── int64_to.gotmpl │ │ ├── int64_type_equal.gotmpl │ │ ├── int64_type_string.gotmpl │ │ ├── int64_type_typable.gotmpl │ │ ├── int64_type_type.gotmpl │ │ ├── int64_type_value_from_int64.gotmpl │ │ ├── int64_type_value_from_terraform.gotmpl │ │ ├── int64_type_value_type.gotmpl │ │ ├── int64_value_equal.gotmpl │ │ ├── int64_value_type.gotmpl │ │ ├── int64_value_valuable.gotmpl │ │ ├── int64_value_value.gotmpl │ │ ├── list_from.gotmpl │ │ ├── list_to.gotmpl │ │ ├── list_type_equal.gotmpl │ │ ├── list_type_string.gotmpl │ │ ├── list_type_typable.gotmpl │ │ ├── list_type_type.gotmpl │ │ ├── list_type_value_from_list.gotmpl │ │ ├── list_type_value_from_terraform.gotmpl │ │ ├── list_type_value_type.gotmpl │ │ ├── list_value_equal.gotmpl │ │ ├── list_value_type.gotmpl │ │ ├── list_value_valuable.gotmpl │ │ ├── list_value_value.gotmpl │ │ ├── map_from.gotmpl │ │ ├── map_to.gotmpl │ │ ├── map_type_equal.gotmpl │ │ ├── map_type_string.gotmpl │ │ ├── map_type_typable.gotmpl │ │ ├── map_type_type.gotmpl │ │ ├── map_type_value_from_map.gotmpl │ │ ├── map_type_value_from_terraform.gotmpl │ │ ├── map_type_value_type.gotmpl │ │ ├── map_value_equal.gotmpl │ │ ├── map_value_type.gotmpl │ │ ├── map_value_valuable.gotmpl │ │ ├── map_value_value.gotmpl │ │ ├── nested_object_from.gotmpl │ │ ├── nested_object_to.gotmpl │ │ ├── nested_object_type_equal.gotmpl │ │ ├── nested_object_type_string.gotmpl │ │ ├── nested_object_type_typable.gotmpl │ │ ├── nested_object_type_type.gotmpl │ │ ├── nested_object_type_value.gotmpl │ │ ├── nested_object_type_value_from_object.gotmpl │ │ ├── nested_object_type_value_from_terraform.gotmpl │ │ ├── nested_object_type_value_must.gotmpl │ │ ├── nested_object_type_value_null.gotmpl │ │ ├── nested_object_type_value_type.gotmpl │ │ ├── nested_object_type_value_unknown.gotmpl │ │ ├── nested_object_value_attribute_types.gotmpl │ │ ├── nested_object_value_equal.gotmpl │ │ ├── nested_object_value_is_null.gotmpl │ │ ├── nested_object_value_is_unknown.gotmpl │ │ ├── nested_object_value_string.gotmpl │ │ ├── nested_object_value_to_object_value.gotmpl │ │ ├── nested_object_value_to_terraform_value.gotmpl │ │ ├── nested_object_value_type.gotmpl │ │ ├── nested_object_value_valuable.gotmpl │ │ ├── nested_object_value_value.gotmpl │ │ ├── number_from.gotmpl │ │ ├── number_to.gotmpl │ │ ├── number_type_equal.gotmpl │ │ ├── number_type_string.gotmpl │ │ ├── number_type_typable.gotmpl │ │ ├── number_type_type.gotmpl │ │ ├── number_type_value_from_number.gotmpl │ │ ├── number_type_value_from_terraform.gotmpl │ │ ├── number_type_value_type.gotmpl │ │ ├── number_value_equal.gotmpl │ │ ├── number_value_type.gotmpl │ │ ├── number_value_valuable.gotmpl │ │ ├── number_value_value.gotmpl │ │ ├── object_from.gotmpl │ │ ├── object_to.gotmpl │ │ ├── object_type_equal.gotmpl │ │ ├── object_type_string.gotmpl │ │ ├── object_type_typable.gotmpl │ │ ├── object_type_type.gotmpl │ │ ├── object_type_value_from_object.gotmpl │ │ ├── object_type_value_from_terraform.gotmpl │ │ ├── object_type_value_type.gotmpl │ │ ├── object_value_attribute_types.gotmpl │ │ ├── object_value_equal.gotmpl │ │ ├── object_value_type.gotmpl │ │ ├── object_value_valuable.gotmpl │ │ ├── object_value_value.gotmpl │ │ ├── schema.gotmpl │ │ ├── set_from.gotmpl │ │ ├── set_to.gotmpl │ │ ├── set_type_equal.gotmpl │ │ ├── set_type_string.gotmpl │ │ ├── set_type_typable.gotmpl │ │ ├── set_type_type.gotmpl │ │ ├── set_type_value_from_set.gotmpl │ │ ├── set_type_value_from_terraform.gotmpl │ │ ├── set_type_value_type.gotmpl │ │ ├── set_value_equal.gotmpl │ │ ├── set_value_type.gotmpl │ │ ├── set_value_valuable.gotmpl │ │ ├── set_value_value.gotmpl │ │ ├── string_from.gotmpl │ │ ├── string_to.gotmpl │ │ ├── string_type_equal.gotmpl │ │ ├── string_type_string.gotmpl │ │ ├── string_type_typable.gotmpl │ │ ├── string_type_type.gotmpl │ │ ├── string_type_value_from_string.gotmpl │ │ ├── string_type_value_from_terraform.gotmpl │ │ ├── string_type_value_type.gotmpl │ │ ├── string_value_equal.gotmpl │ │ ├── string_value_type.gotmpl │ │ ├── string_value_valuable.gotmpl │ │ └── string_value_value.gotmpl │ ├── to_from_bool.go │ ├── to_from_bool_test.go │ ├── to_from_float64.go │ ├── to_from_float64_test.go │ ├── to_from_int64.go │ ├── to_from_int64_test.go │ ├── to_from_list.go │ ├── to_from_list_test.go │ ├── to_from_map.go │ ├── to_from_map_test.go │ ├── to_from_nested_object.go │ ├── to_from_nested_object_test.go │ ├── to_from_number.go │ ├── to_from_number_test.go │ ├── to_from_object.go │ ├── to_from_object_test.go │ ├── to_from_set.go │ ├── to_from_set_test.go │ ├── to_from_string.go │ ├── to_from_string_test.go │ └── types.go └── validate │ └── validate.go ├── questions.md └── tools ├── copywrite.go ├── go.mod └── go.sum /.changes/0.1.0.md: -------------------------------------------------------------------------------- 1 | ## 0.1.0 (October 17, 2023) 2 | 3 | NOTES: 4 | 5 | * Initial release of `tfplugingen-framework` CLI for Terraform Provider Code Generation tech preview ([#61](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/61)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/0.2.0.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 (October 24, 2023) 2 | 3 | ENHANCEMENTS: 4 | 5 | * Adds code generation for Bool, Float64, Int64, Number, and String attributes that have an associated external type ([#59](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/59)) 6 | * Adds usage of To/From methods for primitive attributes with an associated external type into To/From methods of nested attributes and blocks ([#73](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/73)) 7 | 8 | BUG FIXES: 9 | 10 | * Allow Go reserved keywords to be used as attribute names in nested attributes ([#77](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/77)) 11 | 12 | -------------------------------------------------------------------------------- /.changes/0.3.0.md: -------------------------------------------------------------------------------- 1 | ## 0.3.0 (November 14, 2023) 2 | 3 | ENHANCEMENTS: 4 | 5 | * Adds code generation for List, Map, Object, and Set attributes that have an associated external type ([#75](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/75)) 6 | 7 | BUG FIXES: 8 | 9 | * Fix nested attribute name and generated custom value method name conflicts ([#81](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/81)) 10 | 11 | -------------------------------------------------------------------------------- /.changes/0.3.1.md: -------------------------------------------------------------------------------- 1 | ## 0.3.1 (November 22, 2023) 2 | 3 | BUG FIXES: 4 | 5 | * schema: Prevent compilation errors due to the generation of unused variables ([#93](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/93)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/0.4.0.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 (May 16, 2024) 2 | 3 | ENHANCEMENTS: 4 | 5 | * schema: Added `Description`, `MarkdownDescription` and `DeprecationMessage` fields to resource, data source and provider schemas ([#112](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/112)) 6 | 7 | BUG FIXES: 8 | 9 | * schema: Fixed the generated object value method for map_nested and set_nested ([#125](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/125)) 10 | * Fix ToObjectValue function for nested objects for null or unknown values ([#138](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/138)) 11 | 12 | -------------------------------------------------------------------------------- /.changes/0.4.1.md: -------------------------------------------------------------------------------- 1 | ## 0.4.1 (September 24, 2024) 2 | 3 | BUG FIXES: 4 | 5 | * Fix conversion of unknown or null collections to empty in nested objects ([#161](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/161)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/unreleased/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashicorp/terraform-plugin-codegen-framework/bb44308d418d0467463f73e460f34e87adc57e1b/.changes/unreleased/.gitkeep -------------------------------------------------------------------------------- /.changie.yaml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | changesDir: .changes 4 | unreleasedDir: unreleased 5 | changelogPath: CHANGELOG.md 6 | versionExt: md 7 | versionFormat: '## {{.Version}} ({{.Time.Format "January 02, 2006"}})' 8 | kindFormat: '{{.Kind}}:' 9 | changeFormat: '* {{.Body}} ([#{{.Custom.Issue}}](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/{{.Custom.Issue}}))' 10 | custom: 11 | - key: Issue 12 | label: Issue/PR Number 13 | type: int 14 | minInt: 1 15 | kinds: 16 | - label: BREAKING CHANGES 17 | - label: NOTES 18 | - label: FEATURES 19 | - label: ENHANCEMENTS 20 | - label: BUG FIXES 21 | newlines: 22 | afterKind: 1 23 | beforeKind: 1 24 | endOfVersion: 2 25 | -------------------------------------------------------------------------------- /.copywrite.hcl: -------------------------------------------------------------------------------- 1 | schema_version = 1 2 | 3 | project { 4 | license = "MPL-2.0" 5 | copyright_year = 2023 6 | 7 | header_ignore = [ 8 | # internal catalog metadata (prose) 9 | "META.d/**/*.yaml", 10 | 11 | # changie tooling configuration and CHANGELOG entries (prose) 12 | ".changes/unreleased/*.yaml", 13 | ".changie.yaml", 14 | 15 | # GitHub issue template configuration 16 | ".github/ISSUE_TEMPLATE/*.yml", 17 | 18 | # GitHub Actions workflow-specific configurations 19 | ".github/labeler-*.yml", 20 | 21 | # golangci-lint tooling configuration 22 | ".golangci.yml", 23 | 24 | # GoReleaser tooling configuration 25 | ".goreleaser.yml", 26 | 27 | # Release Engineering tooling configuration 28 | ".release/*.hcl", 29 | 30 | # Ignore testdata files 31 | "internal/cmd/testdata/**", 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hashicorp/terraform-devex -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Let us know about an unexpected error, a crash, or an incorrect behavior. 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out this bug report! Please note that this issue tracker is only used for bug reports and feature requests. Other issues will be closed. 9 | 10 | If you have a question or want to provide general feedback about code generation, please go back to the issue chooser and select one of the discuss board links. 11 | - type: textarea 12 | id: version 13 | attributes: 14 | label: tfplugingen-framework CLI version 15 | description: What is the version of the Framework Code Generator CLI? 16 | placeholder: Output of `tfplugingen-framework --version` 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: provider-code-spec 21 | attributes: 22 | label: Provider Code Spec File 23 | description: Please copy and paste any relevant content of the provider code specification used 24 | render: JSON 25 | validations: 26 | required: false 27 | - type: textarea 28 | id: expected-behavior 29 | attributes: 30 | label: Expected Behavior 31 | description: What did you expect to happen? 32 | placeholder: Description of what should have happened. 33 | validations: 34 | required: true 35 | - type: textarea 36 | id: actual-behavior 37 | attributes: 38 | label: Actual Behavior 39 | description: What actually happened? 40 | placeholder: Description of what actually happened. 41 | validations: 42 | required: true 43 | - type: textarea 44 | id: additional-information 45 | attributes: 46 | label: Additional Information 47 | description: Are there any additional details about your environment, workflow, or recent changes that might be relevant? Have you discovered a workaround? Are there links to other related issues? 48 | validations: 49 | required: false 50 | - type: checkboxes 51 | id: terms 52 | attributes: 53 | label: Code of Conduct 54 | description: By submitting this issue, you agree to follow our [Community Guidelines](https://www.hashicorp.com/community-guidelines). 55 | options: 56 | - label: I agree to follow this project's Code of Conduct 57 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Code Generation Feedback 4 | url: https://discuss.hashicorp.com/c/terraform-providers/tf-plugin-sdk 5 | about: Please share any overall feedback relating to provider code generation by creating a new topic in the Plugin Development Community Forum. 6 | - name: ❓ Code Generation Questions 7 | url: https://discuss.hashicorp.com/c/terraform-providers/tf-plugin-sdk 8 | about: Please ask any general questions about provider code generation by creating a new topic in the Plugin Development Community Forum. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Something is missing or could be improved. 3 | labels: ["enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out this feature request! Please note that this issue tracker is only used for bug reports and feature requests. Other issues will be closed. 9 | 10 | If you have a question or want to provide general feedback about code generation, please go back to the issue chooser and select one of the discuss board links. 11 | - type: textarea 12 | id: use-case 13 | attributes: 14 | label: Use Cases or Problem Statement 15 | description: What use cases or problems are you trying to solve? 16 | placeholder: Description of use cases or problems. 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: proposal 21 | attributes: 22 | label: Proposal 23 | description: What solutions would you prefer? 24 | placeholder: Description of proposed solutions. 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: additional-information 29 | attributes: 30 | label: Additional Information 31 | description: Are there any additional details about your environment, workflow, or recent changes that might be relevant? Have you discovered a workaround? Are there links to other related issues? 32 | validations: 33 | required: false 34 | - type: checkboxes 35 | id: terms 36 | attributes: 37 | label: Code of Conduct 38 | description: By submitting this issue, you agree to follow our [Community Guidelines](https://www.hashicorp.com/community-guidelines). 39 | options: 40 | - label: I agree to follow this project's Code of Conduct 41 | required: true -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "gomod" 8 | directory: "/tools" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | -------------------------------------------------------------------------------- /.github/workflows/ci-changie.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | 4 | # Continuous integration handling for changie 5 | name: ci-changie 6 | 7 | on: 8 | pull_request: 9 | paths: 10 | - .changes/unreleased/*.yaml 11 | - .changie.yaml 12 | - .github/workflows/ci-changie.yml 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | check: 19 | runs-on: ubuntu-latest 20 | steps: 21 | # Ensure terraform-devex-repos is updated on version changes. 22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | # Ensure terraform-devex-repos is updated on version changes. 24 | - uses: miniscruff/changie-action@6dcc2533cac0495148ed4046c438487e4dceaa23 # v2.0.0 25 | with: 26 | version: latest 27 | args: batch patch --dry-run 28 | -------------------------------------------------------------------------------- /.github/workflows/ci-github-actions.yml: -------------------------------------------------------------------------------- 1 | # Continuous integration handling for GitHub Actions workflows 2 | name: ci-github-actions 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/*.yml 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | actionlint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 17 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 18 | with: 19 | go-version-file: 'go.mod' 20 | - run: go install github.com/rhysd/actionlint/cmd/actionlint@latest 21 | - run: actionlint -------------------------------------------------------------------------------- /.github/workflows/ci-go.yml: -------------------------------------------------------------------------------- 1 | # Continuous integration handling for Go 2 | name: ci-go 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/ci-go.yml 8 | - .golangci.yml 9 | - go.mod 10 | - '**.go' 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | golangci-lint: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 21 | with: 22 | go-version-file: 'go.mod' 23 | - run: go mod download 24 | - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 25 | 26 | test: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 30 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 31 | with: 32 | go-version-file: 'go.mod' 33 | - run: go mod download 34 | - run: go test -coverprofile=coverage.out ./... 35 | - run: go tool cover -html=coverage.out -o coverage.html 36 | - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 37 | with: 38 | name: go-coverage 39 | path: coverage.html -------------------------------------------------------------------------------- /.github/workflows/ci-goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Continuous integration handling for GoReleaser 2 | name: ci-goreleaser 3 | 4 | on: 5 | pull_request: 6 | paths: 7 | - .github/workflows/ci-goreleaser.yml 8 | - .goreleaser.yml 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 19 | with: 20 | go-version-file: 'go.mod' 21 | - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 22 | with: 23 | args: check -------------------------------------------------------------------------------- /.github/workflows/compliance.yml: -------------------------------------------------------------------------------- 1 | name: compliance 2 | 3 | on: 4 | pull_request: 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | # Reference: ENGSRV-059 11 | copywrite: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - uses: hashicorp/setup-copywrite@32638da2d4e81d56a0764aa1547882fc4d209636 # v1.1.3 16 | - run: copywrite headers --plan 17 | - run: copywrite license --plan -------------------------------------------------------------------------------- /.github/workflows/issue-comment-triage.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | name: Issue Comment Triage 4 | 5 | on: 6 | issue_comment: 7 | types: [created] 8 | 9 | jobs: 10 | issue_comment_triage: 11 | runs-on: ubuntu-latest 12 | env: 13 | # issue_comment events are triggered by comments on issues and pull requests. Checking the 14 | # value of github.event.issue.pull_request tells us whether the issue is an issue or is 15 | # actually a pull request, allowing us to dynamically set the gh subcommand: 16 | # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only 17 | COMMAND: ${{ github.event.issue.pull_request && 'pr' || 'issue' }} 18 | GH_TOKEN: ${{ github.token }} 19 | steps: 20 | - name: 'Remove waiting-response on comment' 21 | run: gh ${{ env.COMMAND }} edit ${{ github.event.issue.html_url }} --remove-label waiting-response 22 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | name: 'Lock Threads' 4 | 5 | on: 6 | schedule: 7 | - cron: '13 22 * * *' 8 | 9 | jobs: 10 | lock: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # NOTE: When TSCCR updates the GitHub action version, update the template workflow file to avoid drift: 14 | # https://github.com/hashicorp/terraform-devex-repos/blob/main/modules/repo/workflows/lock.tftpl 15 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 16 | with: 17 | github-token: ${{ github.token }} 18 | issue-inactive-days: '30' 19 | issue-lock-reason: resolved 20 | pr-inactive-days: '30' 21 | pr-lock-reason: resolved 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # OS generated files 15 | .DS_Store 16 | 17 | # JetBrains IDEs files 18 | .idea/ 19 | *.iws 20 | 21 | # VSCode files 22 | .vscode/ 23 | 24 | # Ignore any manual testing output from running codegen tool at root 25 | output/ -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | default: none 4 | enable: 5 | - copyloopvar 6 | - durationcheck 7 | - errcheck 8 | - forcetypeassert 9 | - govet 10 | - ineffassign 11 | - makezero 12 | - misspell 13 | - nilerr 14 | - paralleltest 15 | - predeclared 16 | - staticcheck 17 | - unconvert 18 | - unparam 19 | - unused 20 | - usetesting 21 | exclusions: 22 | generated: lax 23 | presets: 24 | - comments 25 | - common-false-positives 26 | - legacy 27 | - std-error-handling 28 | paths: 29 | - output 30 | - third_party$ 31 | - builtin$ 32 | - examples$ 33 | settings: 34 | staticcheck: 35 | checks: 36 | - all 37 | - '-QF1004' # "could use strings.ReplaceAll instead" -- https://staticcheck.dev/docs/checks/#QF1004 38 | - '-QF1008' # "could remove embedded field "Block" from selector" -- https://staticcheck.dev/docs/checks/#QF1008 39 | - '-QF1012' # "Use fmt.Fprintf(...) instead of WriteString(fmt.Sprintf(...))" -- https://staticcheck.dev/docs/checks/#QF1012 40 | - '-ST1003' # example: "const autoTFVarsJson should be autoTFVarsJSON" -- https://staticcheck.dev/docs/checks/#ST1003 41 | - '-ST1016' # example: "methods on the same type should have the same receiver name (seen 2x "r", 2x "s")" -- https://staticcheck.dev/docs/checks/#ST1016 42 | 43 | issues: 44 | max-issues-per-linter: 0 45 | max-same-issues: 0 46 | formatters: 47 | enable: 48 | - gofmt 49 | exclusions: 50 | generated: lax 51 | paths: 52 | - output 53 | - third_party$ 54 | - builtin$ 55 | - examples$ 56 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | project_name: tfplugingen-framework 3 | builds: 4 | - main: ./cmd/tfplugingen-framework 5 | env: 6 | - CGO_ENABLED=0 7 | mod_timestamp: '{{ .CommitTimestamp }}' 8 | flags: 9 | - -trimpath 10 | ldflags: 11 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 12 | goos: 13 | - windows 14 | - linux 15 | - darwin 16 | goarch: 17 | - amd64 18 | - arm64 19 | binary: '{{ .ProjectName }}' 20 | archives: 21 | - formats: [ 'zip' ] 22 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 23 | checksum: 24 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 25 | algorithm: sha256 26 | milestones: 27 | - close: true 28 | release: -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.1 (September 24, 2024) 2 | 3 | BUG FIXES: 4 | 5 | * Fix conversion of unknown or null collections to empty in nested objects ([#161](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/161)) 6 | 7 | ## 0.4.0 (May 16, 2024) 8 | 9 | ENHANCEMENTS: 10 | 11 | * schema: Added `Description`, `MarkdownDescription` and `DeprecationMessage` fields to resource, data source and provider schemas ([#112](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/112)) 12 | 13 | BUG FIXES: 14 | 15 | * schema: Fixed the generated object value method for map_nested and set_nested ([#125](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/125)) 16 | * Fix ToObjectValue function for nested objects for null or unknown values ([#138](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/138)) 17 | 18 | ## 0.3.1 (November 22, 2023) 19 | 20 | BUG FIXES: 21 | 22 | * schema: Prevent compilation errors due to the generation of unused variables ([#93](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/93)) 23 | 24 | ## 0.3.0 (November 14, 2023) 25 | 26 | ENHANCEMENTS: 27 | 28 | * Adds code generation for List, Map, Object, and Set attributes that have an associated external type ([#75](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/75)) 29 | 30 | BUG FIXES: 31 | 32 | * Fix nested attribute name and generated custom value method name conflicts ([#81](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/81)) 33 | 34 | ## 0.2.0 (October 24, 2023) 35 | 36 | ENHANCEMENTS: 37 | 38 | * Adds code generation for Bool, Float64, Int64, Number, and String attributes that have an associated external type ([#59](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/59)) 39 | * Adds usage of To/From methods for primitive attributes with an associated external type into To/From methods of nested attributes and blocks ([#73](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/73)) 40 | 41 | BUG FIXES: 42 | 43 | * Allow Go reserved keywords to be used as attribute names in nested attributes ([#77](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/77)) 44 | 45 | ## 0.1.0 (October 17, 2023) 46 | 47 | NOTES: 48 | 49 | * Initial release of `tfplugingen-framework` CLI for Terraform Provider Code Generation tech preview ([#61](https://github.com/hashicorp/terraform-plugin-codegen-framework/issues/61)) 50 | 51 | -------------------------------------------------------------------------------- /META.d/_summary.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schema: 1.1 3 | 4 | partition: tf-ecosystem 5 | 6 | summary: 7 | owner: team-tf-core-plugins 8 | description: | 9 | Terraform Provider Code Generation Specification to Framework 10 | visibility: public 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | go build ./cmd/tfplugingen-framework 3 | 4 | lint: 5 | golangci-lint run 6 | 7 | fmt: 8 | gofmt -s -w -e . 9 | 10 | test: 11 | go test $$(go list ./... | grep -v /output) -v -cover -timeout=120s -parallel=4 12 | 13 | # Generate copywrite headers 14 | generate: 15 | cd tools; go generate ./... 16 | 17 | # Regenerate testdata folder 18 | testdata: 19 | go run ./cmd/tfplugingen-framework generate all \ 20 | --input ./internal/cmd/testdata/custom_and_external/ir.json \ 21 | --package specified \ 22 | --output ./internal/cmd/testdata/custom_and_external/all_output/specified_pkg_name 23 | 24 | go run ./cmd/tfplugingen-framework generate all \ 25 | --input ./internal/cmd/testdata/custom_and_external/ir.json \ 26 | --output ./internal/cmd/testdata/custom_and_external/all_output/default_pkg_name 27 | 28 | go run ./cmd/tfplugingen-framework generate resources \ 29 | --input ./internal/cmd/testdata/custom_and_external/ir.json \ 30 | --package generated \ 31 | --output ./internal/cmd/testdata/custom_and_external/resources_output 32 | 33 | go run ./cmd/tfplugingen-framework generate data-sources \ 34 | --input ./internal/cmd/testdata/custom_and_external/ir.json \ 35 | --package generated \ 36 | --output ./internal/cmd/testdata/custom_and_external/data_sources_output 37 | 38 | go run ./cmd/tfplugingen-framework generate provider \ 39 | --input ./internal/cmd/testdata/custom_and_external/ir.json \ 40 | --package generated \ 41 | --output ./internal/cmd/testdata/custom_and_external/provider_output 42 | 43 | go run ./cmd/tfplugingen-framework scaffold resource \ 44 | --name thing \ 45 | --force \ 46 | --package scaffold \ 47 | --output-dir ./internal/cmd/testdata/scaffold/resource 48 | 49 | go run ./cmd/tfplugingen-framework scaffold data-source \ 50 | --name thing \ 51 | --force \ 52 | --package scaffold \ 53 | --output-dir ./internal/cmd/testdata/scaffold/data_source 54 | 55 | go run ./cmd/tfplugingen-framework scaffold provider \ 56 | --name examplecloud \ 57 | --force \ 58 | --package scaffold \ 59 | --output-dir ./internal/cmd/testdata/scaffold/provider 60 | 61 | .PHONY: lint fmt test 62 | -------------------------------------------------------------------------------- /cmd/tfplugingen-framework/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "io" 9 | "os" 10 | 11 | "github.com/hashicorp/cli" 12 | "github.com/mattn/go-colorable" 13 | 14 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 15 | ) 16 | 17 | func main() { 18 | name := "tfplugingen-framework" 19 | versionOutput := fmt.Sprintf("%s %s", name, getVersion()) 20 | 21 | os.Exit(runCLI( 22 | name, 23 | versionOutput, 24 | os.Args[1:], 25 | os.Stdin, 26 | colorable.NewColorableStdout(), 27 | colorable.NewColorableStderr(), 28 | )) 29 | } 30 | 31 | func runCLI(name, versionOutput string, args []string, stdin io.Reader, stdout, stderr io.Writer) int { 32 | ui := &cli.ColoredUi{ 33 | ErrorColor: cli.UiColorRed, 34 | WarnColor: cli.UiColorYellow, 35 | 36 | Ui: &cli.BasicUi{ 37 | Reader: stdin, 38 | Writer: stdout, 39 | ErrorWriter: stderr, 40 | }, 41 | } 42 | 43 | commands := initCommands(ui) 44 | frameworkGen := cli.CLI{ 45 | Name: name, 46 | Args: args, 47 | Commands: commands, 48 | HelpFunc: cli.BasicHelpFunc(name), 49 | HelpWriter: stderr, 50 | Version: versionOutput, 51 | } 52 | exitCode, err := frameworkGen.Run() 53 | if err != nil { 54 | return 1 55 | } 56 | 57 | return exitCode 58 | } 59 | 60 | func initCommands(ui cli.Ui) map[string]cli.CommandFactory { 61 | return map[string]cli.CommandFactory{ 62 | // Code generation commands 63 | "generate": commandFactory(&cmd.GenerateCommand{UI: ui}), 64 | "generate all": commandFactory(&cmd.GenerateAllCommand{UI: ui}), 65 | "generate resources": commandFactory(&cmd.GenerateResourcesCommand{UI: ui}), 66 | "generate data-sources": commandFactory(&cmd.GenerateDataSourcesCommand{UI: ui}), 67 | "generate provider": commandFactory(&cmd.GenerateProviderCommand{UI: ui}), 68 | // Code scaffolding commands 69 | "scaffold": commandFactory(&cmd.ScaffoldCommand{UI: ui}), 70 | "scaffold resource": commandFactory(&cmd.ScaffoldResourceCommand{UI: ui}), 71 | "scaffold data-source": commandFactory(&cmd.ScaffoldDataSourceCommand{UI: ui}), 72 | "scaffold provider": commandFactory(&cmd.ScaffoldProviderCommand{UI: ui}), 73 | } 74 | } 75 | 76 | func commandFactory(cmd cli.Command) cli.CommandFactory { 77 | return func() (cli.Command, error) { 78 | return cmd, nil 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cmd/tfplugingen-framework/version.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "runtime/debug" 9 | ) 10 | 11 | var ( 12 | // These vars will be set by goreleaser. 13 | version string 14 | commit string 15 | ) 16 | 17 | func getVersion() string { 18 | // Prefer global version as it's set by goreleaser via ldflags 19 | // https://goreleaser.com/cookbooks/using-main.version/ 20 | if version != "" { 21 | if commit != "" { 22 | version = fmt.Sprintf("%s from commit: %s", version, commit) 23 | } 24 | return version 25 | } 26 | 27 | // If not built with goreleaser, check the binary for VCS revision/module version info 28 | if info, ok := debug.ReadBuildInfo(); ok { 29 | for _, setting := range info.Settings { 30 | if setting.Key == "vcs.revision" { 31 | return fmt.Sprintf("commit: %s", setting.Value) 32 | } 33 | } 34 | 35 | return fmt.Sprintf("module: %s", info.Main.Version) 36 | } 37 | 38 | return "local" 39 | } 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hashicorp/terraform-plugin-codegen-framework 2 | 3 | go 1.22.7 4 | 5 | require ( 6 | github.com/google/go-cmp v0.7.0 7 | github.com/hashicorp/cli v1.1.7 8 | github.com/hashicorp/terraform-plugin-codegen-spec v0.2.0 9 | github.com/mattn/go-colorable v0.1.14 10 | ) 11 | 12 | require ( 13 | github.com/Masterminds/goutils v1.1.1 // indirect 14 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 15 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect 16 | github.com/armon/go-radix v1.0.0 // indirect 17 | github.com/bgentry/speakeasy v0.1.0 // indirect 18 | github.com/fatih/color v1.16.0 // indirect 19 | github.com/google/uuid v1.4.0 // indirect 20 | github.com/hashicorp/errwrap v1.1.0 // indirect 21 | github.com/hashicorp/go-multierror v1.1.1 // indirect 22 | github.com/huandu/xstrings v1.4.0 // indirect 23 | github.com/imdario/mergo v0.3.16 // indirect 24 | github.com/mattn/go-isatty v0.0.20 // indirect 25 | github.com/mitchellh/copystructure v1.2.0 // indirect 26 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 27 | github.com/posener/complete v1.2.3 // indirect 28 | github.com/shopspring/decimal v1.3.1 // indirect 29 | github.com/spf13/cast v1.5.1 // indirect 30 | github.com/stretchr/testify v1.7.2 // indirect 31 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect 32 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 33 | github.com/xeipuuv/gojsonschema v1.2.0 // indirect 34 | golang.org/x/crypto v0.32.0 // indirect 35 | golang.org/x/sys v0.29.0 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /internal/cmd/generate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "strings" 8 | 9 | "github.com/hashicorp/cli" 10 | ) 11 | 12 | type GenerateCommand struct { 13 | UI cli.Ui 14 | } 15 | 16 | func (cmd *GenerateCommand) Help() string { 17 | helpText := ` 18 | Usage: tfplugingen-framework generate [] 19 | 20 | This command has subcommands for Terraform Plugin Framework code generation. 21 | 22 | ` 23 | return strings.TrimSpace(helpText) 24 | } 25 | 26 | func (a *GenerateCommand) Synopsis() string { 27 | return "Terraform Plugin Framework code generation commands" 28 | } 29 | 30 | func (cmd *GenerateCommand) Run(args []string) int { 31 | return cli.RunResultHelp 32 | } 33 | -------------------------------------------------------------------------------- /internal/cmd/generate_all_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | 11 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 12 | ) 13 | 14 | func TestGenerateAllCommand(t *testing.T) { 15 | t.Parallel() 16 | 17 | testCases := map[string]struct { 18 | irInputPath string 19 | pkgName string 20 | goldenFileDir string 21 | }{ 22 | "specified_pkg_name": { 23 | irInputPath: "testdata/custom_and_external/ir.json", 24 | pkgName: "specified", 25 | goldenFileDir: "testdata/custom_and_external/all_output/specified_pkg_name", 26 | }, 27 | "default_pkg_name": { 28 | irInputPath: "testdata/custom_and_external/ir.json", 29 | goldenFileDir: "testdata/custom_and_external/all_output/default_pkg_name", 30 | }, 31 | } 32 | for name, testCase := range testCases { 33 | 34 | t.Run(name, func(t *testing.T) { 35 | t.Parallel() 36 | 37 | testOutputDir := t.TempDir() 38 | mockUi := cli.NewMockUi() 39 | c := cmd.GenerateAllCommand{ 40 | UI: mockUi, 41 | } 42 | 43 | args := []string{ 44 | "--input", testCase.irInputPath, 45 | "--package", testCase.pkgName, 46 | "--output", testOutputDir, 47 | } 48 | 49 | exitCode := c.Run(args) 50 | if exitCode != 0 { 51 | t.Fatalf("unexpected error running `generate all` cmd: %s", mockUi.ErrorWriter.String()) 52 | } 53 | 54 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /internal/cmd/generate_data_sources_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 11 | ) 12 | 13 | func TestGenerateDataSourcesCommand(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | irInputPath string 18 | goldenFileDir string 19 | }{ 20 | "custom_and_external": { 21 | irInputPath: "testdata/custom_and_external/ir.json", 22 | goldenFileDir: "testdata/custom_and_external/data_sources_output", 23 | }, 24 | } 25 | for name, testCase := range testCases { 26 | 27 | t.Run(name, func(t *testing.T) { 28 | t.Parallel() 29 | 30 | testOutputDir := t.TempDir() 31 | mockUi := cli.NewMockUi() 32 | c := cmd.GenerateDataSourcesCommand{ 33 | UI: mockUi, 34 | } 35 | 36 | args := []string{ 37 | "--input", testCase.irInputPath, 38 | "--package", "generated", 39 | "--output", testOutputDir, 40 | } 41 | 42 | exitCode := c.Run(args) 43 | if exitCode != 0 { 44 | t.Fatalf("unexpected error running `generate data-sources` cmd: %s", mockUi.ErrorWriter.String()) 45 | } 46 | 47 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/cmd/generate_provider_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 11 | ) 12 | 13 | func TestGenerateProviderCommand(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | irInputPath string 18 | goldenFileDir string 19 | }{ 20 | "custom_and_external": { 21 | irInputPath: "testdata/custom_and_external/ir.json", 22 | goldenFileDir: "testdata/custom_and_external/provider_output", 23 | }, 24 | "provider_no_attributes": { 25 | irInputPath: "testdata/provider_no_attributes/ir.json", 26 | goldenFileDir: "testdata/provider_no_attributes/provider_output", 27 | }, 28 | } 29 | for name, testCase := range testCases { 30 | 31 | t.Run(name, func(t *testing.T) { 32 | t.Parallel() 33 | 34 | testOutputDir := t.TempDir() 35 | mockUi := cli.NewMockUi() 36 | c := cmd.GenerateProviderCommand{ 37 | UI: mockUi, 38 | } 39 | 40 | args := []string{ 41 | "--input", testCase.irInputPath, 42 | "--package", "generated", 43 | "--output", testOutputDir, 44 | } 45 | 46 | exitCode := c.Run(args) 47 | if exitCode != 0 { 48 | t.Fatalf("unexpected error running `generate provider` cmd: %s", mockUi.ErrorWriter.String()) 49 | } 50 | 51 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /internal/cmd/generate_resources_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 11 | ) 12 | 13 | func TestGenerateResourcesCommand(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | irInputPath string 18 | goldenFileDir string 19 | }{ 20 | "custom_and_external": { 21 | irInputPath: "testdata/custom_and_external/ir.json", 22 | goldenFileDir: "testdata/custom_and_external/resources_output", 23 | }, 24 | } 25 | for name, testCase := range testCases { 26 | 27 | t.Run(name, func(t *testing.T) { 28 | t.Parallel() 29 | 30 | testOutputDir := t.TempDir() 31 | mockUi := cli.NewMockUi() 32 | c := cmd.GenerateResourcesCommand{ 33 | UI: mockUi, 34 | } 35 | 36 | args := []string{ 37 | "--input", testCase.irInputPath, 38 | "--package", "generated", 39 | "--output", testOutputDir, 40 | } 41 | 42 | exitCode := c.Run(args) 43 | if exitCode != 0 { 44 | t.Fatalf("unexpected error running `generate resources` cmd: %s", mockUi.ErrorWriter.String()) 45 | } 46 | 47 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/cmd/generate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "os" 8 | "path" 9 | "testing" 10 | 11 | "github.com/google/go-cmp/cmp" 12 | ) 13 | 14 | func compareDirectories(t *testing.T, wantDirPath, gotDirPath string) { 15 | t.Helper() 16 | 17 | wantDirEntries, err := os.ReadDir(wantDirPath) 18 | if err != nil { 19 | t.Fatalf("unexpected error reading `want` directory: %s", err) 20 | } 21 | 22 | gotDirEntries, err := os.ReadDir(gotDirPath) 23 | if err != nil { 24 | t.Fatalf("unexpected error reading `got` directory: %s", err) 25 | } 26 | 27 | if len(gotDirEntries) != len(wantDirEntries) { 28 | t.Fatalf("mismatched file count in output directory, golden directory: %d file(s), test directory: %d file(s)", len(wantDirEntries), len(gotDirEntries)) 29 | } 30 | 31 | for i, wantEntry := range wantDirEntries { 32 | gotEntry := gotDirEntries[i] 33 | 34 | if gotEntry.Name() != wantEntry.Name() { 35 | t.Errorf("mismatched file name, golden file name: %s, test file name: %s", wantEntry.Name(), gotEntry.Name()) 36 | continue 37 | } 38 | 39 | if gotEntry.Type() != wantEntry.Type() { 40 | t.Errorf("mismatched file type, golden file type: %s, test file type: %s", wantEntry.Type(), gotEntry.Type()) 41 | continue 42 | } 43 | 44 | compareFiles(t, path.Join(gotDirPath, gotEntry.Name()), path.Join(wantDirPath, wantEntry.Name())) 45 | } 46 | } 47 | 48 | func compareFiles(t *testing.T, got, want string) { 49 | gotFile, err := os.Open(got) 50 | 51 | if err != nil { 52 | t.Fatalf("unexpected error opening %s: %s", got, err) 53 | } 54 | 55 | gotFileInfo, err := gotFile.Stat() 56 | 57 | if err != nil { 58 | t.Fatalf("unexpected error stat %s: %s", got, err) 59 | } 60 | 61 | if gotFileInfo.IsDir() { 62 | dirEntries, err := os.ReadDir(got) 63 | 64 | if err != nil { 65 | t.Fatalf("unexpected error reading dir %s: %s", got, err) 66 | } 67 | 68 | for _, dirEntry := range dirEntries { 69 | compareFiles(t, path.Join(got, dirEntry.Name()), path.Join(want, dirEntry.Name())) 70 | } 71 | 72 | return 73 | } 74 | 75 | gotFileBytes, err := os.ReadFile(got) 76 | 77 | if err != nil { 78 | t.Fatalf("unexpected error reading `got` file: %s", err) 79 | } 80 | 81 | wantFileBytes, err := os.ReadFile(want) 82 | 83 | if err != nil { 84 | t.Fatalf("unexpected error reading `want` file: %s", err) 85 | } 86 | 87 | if diff := cmp.Diff(string(gotFileBytes), string(wantFileBytes)); diff != "" { 88 | t.Errorf("unexpected difference in %s: %s", got, diff) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /internal/cmd/scaffold.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd 5 | 6 | import ( 7 | "strings" 8 | 9 | "github.com/hashicorp/cli" 10 | ) 11 | 12 | type ScaffoldCommand struct { 13 | UI cli.Ui 14 | } 15 | 16 | func (cmd *ScaffoldCommand) Help() string { 17 | helpText := ` 18 | Usage: tfplugingen-framework scaffold [] 19 | 20 | This command has subcommands for scaffolding Terraform Plugin Framework code. 21 | 22 | ` 23 | return strings.TrimSpace(helpText) 24 | } 25 | 26 | func (a *ScaffoldCommand) Synopsis() string { 27 | return "Terraform Plugin Framework code scaffolding commands" 28 | } 29 | 30 | func (cmd *ScaffoldCommand) Run(args []string) int { 31 | return cli.RunResultHelp 32 | } 33 | -------------------------------------------------------------------------------- /internal/cmd/scaffold_data_source_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 11 | ) 12 | 13 | func TestScaffoldDataSourceCommand(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | goldenFileDir string 18 | }{ 19 | "data source scaffold": { 20 | goldenFileDir: "testdata/scaffold/data_source", 21 | }, 22 | } 23 | for name, testCase := range testCases { 24 | 25 | t.Run(name, func(t *testing.T) { 26 | t.Parallel() 27 | 28 | testOutputDir := t.TempDir() 29 | mockUi := cli.NewMockUi() 30 | c := cmd.ScaffoldDataSourceCommand{ 31 | UI: mockUi, 32 | } 33 | 34 | args := []string{ 35 | "--name", "thing", 36 | "--package", "scaffold", 37 | "--output-dir", testOutputDir, 38 | } 39 | 40 | exitCode := c.Run(args) 41 | if exitCode != 0 { 42 | t.Fatalf("unexpected error running `scaffold data-source` cmd: %s", mockUi.ErrorWriter.String()) 43 | } 44 | 45 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/cmd/scaffold_provider_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 11 | ) 12 | 13 | func TestScaffoldProviderCommand(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | goldenFileDir string 18 | }{ 19 | "provider scaffold": { 20 | goldenFileDir: "testdata/scaffold/provider", 21 | }, 22 | } 23 | for name, testCase := range testCases { 24 | 25 | t.Run(name, func(t *testing.T) { 26 | t.Parallel() 27 | 28 | testOutputDir := t.TempDir() 29 | mockUi := cli.NewMockUi() 30 | c := cmd.ScaffoldProviderCommand{ 31 | UI: mockUi, 32 | } 33 | 34 | args := []string{ 35 | "--name", "examplecloud", 36 | "--package", "scaffold", 37 | "--output-dir", testOutputDir, 38 | } 39 | 40 | exitCode := c.Run(args) 41 | if exitCode != 0 { 42 | t.Fatalf("unexpected error running `scaffold provider` cmd: %s", mockUi.ErrorWriter.String()) 43 | } 44 | 45 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/cmd/scaffold_resource_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package cmd_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/hashicorp/cli" 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/cmd" 11 | ) 12 | 13 | func TestScaffoldResourceCommand(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := map[string]struct { 17 | goldenFileDir string 18 | }{ 19 | "resource scaffold": { 20 | goldenFileDir: "testdata/scaffold/resource", 21 | }, 22 | } 23 | for name, testCase := range testCases { 24 | 25 | t.Run(name, func(t *testing.T) { 26 | t.Parallel() 27 | 28 | testOutputDir := t.TempDir() 29 | mockUi := cli.NewMockUi() 30 | c := cmd.ScaffoldResourceCommand{ 31 | UI: mockUi, 32 | } 33 | 34 | args := []string{ 35 | "--name", "thing", 36 | "--package", "scaffold", 37 | "--output-dir", testOutputDir, 38 | } 39 | 40 | exitCode := c.Run(args) 41 | if exitCode != 0 { 42 | t.Fatalf("unexpected error running `scaffold resource` cmd: %s", mockUi.ErrorWriter.String()) 43 | } 44 | 45 | compareDirectories(t, testCase.goldenFileDir, testOutputDir) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/cmd/testdata/provider_no_attributes/ir.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": { 3 | "name": "example" 4 | }, 5 | "version": "0.1" 6 | } 7 | -------------------------------------------------------------------------------- /internal/cmd/testdata/provider_no_attributes/provider_output/example_provider_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by terraform-plugin-framework-generator DO NOT EDIT. 2 | 3 | package generated 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/hashicorp/terraform-plugin-framework/provider/schema" 9 | ) 10 | 11 | func ExampleProviderSchema(ctx context.Context) schema.Schema { 12 | return schema.Schema{} 13 | } 14 | 15 | type ExampleModel struct { 16 | } 17 | -------------------------------------------------------------------------------- /internal/cmd/testdata/scaffold/data_source/thing_data_source.go: -------------------------------------------------------------------------------- 1 | package scaffold 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 | ) 10 | 11 | var _ datasource.DataSource = (*thingDataSource)(nil) 12 | 13 | func NewThingDataSource() datasource.DataSource { 14 | return &thingDataSource{} 15 | } 16 | 17 | type thingDataSource struct{} 18 | 19 | type thingDataSourceModel struct { 20 | Id types.String `tfsdk:"id"` 21 | } 22 | 23 | func (d *thingDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 24 | resp.TypeName = req.ProviderTypeName + "_thing" 25 | } 26 | 27 | func (d *thingDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 28 | resp.Schema = schema.Schema{ 29 | Attributes: map[string]schema.Attribute{ 30 | "id": schema.StringAttribute{ 31 | Computed: true, 32 | }, 33 | }, 34 | } 35 | } 36 | 37 | func (d *thingDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 38 | var data thingDataSourceModel 39 | 40 | // Read Terraform configuration data into the model 41 | resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) 42 | 43 | if resp.Diagnostics.HasError() { 44 | return 45 | } 46 | 47 | // Read API call logic 48 | 49 | // Example data value setting 50 | data.Id = types.StringValue("example-id") 51 | 52 | // Save data into Terraform state 53 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 54 | } 55 | -------------------------------------------------------------------------------- /internal/cmd/testdata/scaffold/provider/provider.go: -------------------------------------------------------------------------------- 1 | package scaffold 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/datasource" 7 | "github.com/hashicorp/terraform-plugin-framework/provider" 8 | "github.com/hashicorp/terraform-plugin-framework/resource" 9 | ) 10 | 11 | var _ provider.Provider = (*examplecloudProvider)(nil) 12 | 13 | func New() func() provider.Provider { 14 | return func() provider.Provider { 15 | return &examplecloudProvider{} 16 | } 17 | } 18 | 19 | type examplecloudProvider struct{} 20 | 21 | func (p *examplecloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { 22 | 23 | } 24 | 25 | func (p *examplecloudProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { 26 | 27 | } 28 | 29 | func (p *examplecloudProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { 30 | resp.TypeName = "examplecloud" 31 | } 32 | 33 | func (p *examplecloudProvider) DataSources(ctx context.Context) []func() datasource.DataSource { 34 | return []func() datasource.DataSource{} 35 | } 36 | 37 | func (p *examplecloudProvider) Resources(ctx context.Context) []func() resource.Resource { 38 | return []func() resource.Resource{} 39 | } 40 | -------------------------------------------------------------------------------- /internal/cmd/testdata/scaffold/resource/thing_resource.go: -------------------------------------------------------------------------------- 1 | package scaffold 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/resource" 7 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 8 | "github.com/hashicorp/terraform-plugin-framework/types" 9 | ) 10 | 11 | var _ resource.Resource = (*thingResource)(nil) 12 | 13 | func NewThingResource() resource.Resource { 14 | return &thingResource{} 15 | } 16 | 17 | type thingResource struct{} 18 | 19 | type thingResourceModel struct { 20 | Id types.String `tfsdk:"id"` 21 | } 22 | 23 | func (r *thingResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 24 | resp.TypeName = req.ProviderTypeName + "_thing" 25 | } 26 | 27 | func (r *thingResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 28 | resp.Schema = schema.Schema{ 29 | Attributes: map[string]schema.Attribute{ 30 | "id": schema.StringAttribute{ 31 | Computed: true, 32 | }, 33 | }, 34 | } 35 | } 36 | 37 | func (r *thingResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 38 | var data thingResourceModel 39 | 40 | // Read Terraform plan data into the model 41 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 42 | 43 | if resp.Diagnostics.HasError() { 44 | return 45 | } 46 | 47 | // Create API call logic 48 | 49 | // Example data value setting 50 | data.Id = types.StringValue("example-id") 51 | 52 | // Save data into Terraform state 53 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 54 | } 55 | 56 | func (r *thingResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 57 | var data thingResourceModel 58 | 59 | // Read Terraform prior state data into the model 60 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 61 | 62 | if resp.Diagnostics.HasError() { 63 | return 64 | } 65 | 66 | // Read API call logic 67 | 68 | // Save updated data into Terraform state 69 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 70 | } 71 | 72 | func (r *thingResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 73 | var data thingResourceModel 74 | 75 | // Read Terraform plan data into the model 76 | resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) 77 | 78 | if resp.Diagnostics.HasError() { 79 | return 80 | } 81 | 82 | // Update API call logic 83 | 84 | // Save updated data into Terraform state 85 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 86 | } 87 | 88 | func (r *thingResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 89 | var data thingResourceModel 90 | 91 | // Read Terraform prior state data into the model 92 | resp.Diagnostics.Append(req.State.Get(ctx, &data)...) 93 | 94 | if resp.Diagnostics.HasError() { 95 | return 96 | } 97 | 98 | // Delete API call logic 99 | } 100 | -------------------------------------------------------------------------------- /internal/convert/computed_optional_required.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | 9 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 10 | ) 11 | 12 | type ComputedOptionalRequired struct { 13 | computedOptionalRequired specschema.ComputedOptionalRequired 14 | } 15 | 16 | func NewComputedOptionalRequired(c specschema.ComputedOptionalRequired) ComputedOptionalRequired { 17 | return ComputedOptionalRequired{ 18 | computedOptionalRequired: c, 19 | } 20 | } 21 | 22 | func (c ComputedOptionalRequired) Equal(other ComputedOptionalRequired) bool { 23 | return c.computedOptionalRequired.Equal(other.computedOptionalRequired) 24 | } 25 | 26 | func (c ComputedOptionalRequired) IsComputed() bool { 27 | if c.computedOptionalRequired == specschema.Computed || c.computedOptionalRequired == specschema.ComputedOptional { 28 | return true 29 | } 30 | 31 | return false 32 | } 33 | 34 | func (c ComputedOptionalRequired) IsOptional() bool { 35 | if c.computedOptionalRequired == specschema.Optional || c.computedOptionalRequired == specschema.ComputedOptional { 36 | return true 37 | } 38 | 39 | return false 40 | } 41 | 42 | func (c ComputedOptionalRequired) IsRequired() bool { 43 | return c.computedOptionalRequired == specschema.Required 44 | } 45 | 46 | func (c ComputedOptionalRequired) Schema() []byte { 47 | var b bytes.Buffer 48 | 49 | if c.IsRequired() { 50 | b.WriteString("Required: true,\n") 51 | } 52 | 53 | if c.IsOptional() { 54 | b.WriteString("Optional: true,\n") 55 | } 56 | 57 | if c.IsComputed() { 58 | b.WriteString("Computed: true,\n") 59 | } 60 | 61 | return b.Bytes() 62 | } 63 | -------------------------------------------------------------------------------- /internal/convert/custom_type_nested_collection.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 13 | ) 14 | 15 | type CustomTypeNestedCollection struct { 16 | customType *specschema.CustomType 17 | } 18 | 19 | // NewCustomTypeNestedCollection constructs an CustomTypeNestedCollection which is used to determine whether a CustomType 20 | // should be assigned to a nested attribute in the schema. 21 | // 22 | // If a CustomType has been declared in the spec, then the CustomType.Type will be used as 23 | // the CustomType in the schema. 24 | // 25 | // If the spec CustomType is nil, the generator will create custom Type and Value types using the attribute 26 | // name, and the generated custom Type type will be used as the CustomType in the schema. 27 | func NewCustomTypeNestedCollection(c *specschema.CustomType) CustomTypeNestedCollection { 28 | return CustomTypeNestedCollection{ 29 | customType: c, 30 | } 31 | } 32 | 33 | func (c CustomTypeNestedCollection) Equal(other CustomTypeNestedCollection) bool { 34 | return c.customType.Equal(other.customType) 35 | } 36 | 37 | func (c CustomTypeNestedCollection) Imports() *schema.Imports { 38 | imports := schema.NewImports() 39 | 40 | if c.customType != nil { 41 | if c.customType.HasImport() { 42 | imports.Add(*c.customType.Import) 43 | } 44 | } else { 45 | imports.Add(code.Import{ 46 | Path: schema.TypesImport, 47 | }) 48 | } 49 | 50 | return imports 51 | } 52 | 53 | func (c CustomTypeNestedCollection) Schema() []byte { 54 | if c.customType != nil && c.customType.Type != "" { 55 | return []byte(fmt.Sprintf("CustomType: %s,\n", c.customType.Type)) 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func (c CustomTypeNestedCollection) ValueType() string { 62 | if c.customType != nil { 63 | return c.customType.ValueType 64 | } 65 | 66 | return "" 67 | } 68 | -------------------------------------------------------------------------------- /internal/convert/custom_type_nested_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/format" 13 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 14 | ) 15 | 16 | type CustomTypeNestedObject struct { 17 | customType *specschema.CustomType 18 | name string 19 | } 20 | 21 | // NewCustomTypeNestedObject constructs an CustomTypeNestedObject which is used to determine whether a CustomType 22 | // should be assigned to a nested attribute object in the schema. 23 | // 24 | // If a CustomType has been declared in the spec, then the CustomType.Type will be used as 25 | // the CustomType in the schema. 26 | // 27 | // If the spec CustomType is nil, the generator will create custom Type and Value types using the attribute 28 | // name, and the generated custom Type type will be used as the CustomType in the schema. 29 | func NewCustomTypeNestedObject(c *specschema.CustomType, name string) CustomTypeNestedObject { 30 | return CustomTypeNestedObject{ 31 | customType: c, 32 | name: name, 33 | } 34 | } 35 | 36 | func (c CustomTypeNestedObject) Equal(other CustomTypeNestedObject) bool { 37 | if !c.customType.Equal(other.customType) { 38 | return false 39 | } 40 | 41 | return c.name == other.name 42 | } 43 | 44 | func (c CustomTypeNestedObject) Imports() *schema.Imports { 45 | imports := schema.NewImports() 46 | 47 | if c.customType != nil { 48 | if c.customType.HasImport() { 49 | imports.Add(*c.customType.Import) 50 | } 51 | } else { 52 | imports.Add(code.Import{ 53 | Path: schema.TypesImport, 54 | }) 55 | } 56 | 57 | return imports 58 | } 59 | 60 | func (c CustomTypeNestedObject) Schema() []byte { 61 | var customTypeType string 62 | 63 | switch { 64 | case c.customType != nil: 65 | customTypeType = c.customType.Type 66 | default: 67 | customTypeType = fmt.Sprintf("%sType{\nObjectType: types.ObjectType{\nAttrTypes: %sValue{}.AttributeTypes(ctx),\n},\n}", format.ToPascalCase(c.name), format.ToPascalCase(c.name)) 68 | } 69 | 70 | if customTypeType != "" { 71 | return []byte(fmt.Sprintf("CustomType: %s,\n", customTypeType)) 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func (c CustomTypeNestedObject) ValueType() string { 78 | if c.customType != nil { 79 | return c.customType.ValueType 80 | } 81 | 82 | return "" 83 | } 84 | -------------------------------------------------------------------------------- /internal/convert/custom_type_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/format" 13 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 14 | ) 15 | 16 | type CustomTypeObject struct { 17 | associatedExternalType *specschema.AssociatedExternalType 18 | customType *specschema.CustomType 19 | name string 20 | } 21 | 22 | // NewCustomTypeObject constructs an CustomTypeObject which is used to determine whether a CustomType 23 | // should be assigned to an object attribute in the schema. 24 | // 25 | // If a CustomType has been declared in the spec, then the CustomType.Type will be used as 26 | // the CustomType in the schema. 27 | // 28 | // If the spec CustomType is nil, and the spec AssociatedExternalType is not nil, the generator 29 | // will create custom Type and Value types using the attribute name, and the generated custom 30 | // Type type will be used as the CustomType in the schema. 31 | func NewCustomTypeObject(c *specschema.CustomType, a *specschema.AssociatedExternalType, name string) CustomTypeObject { 32 | return CustomTypeObject{ 33 | associatedExternalType: a, 34 | customType: c, 35 | name: name, 36 | } 37 | } 38 | 39 | func (c CustomTypeObject) Equal(other CustomTypeObject) bool { 40 | if !c.associatedExternalType.Equal(other.associatedExternalType) { 41 | return false 42 | } 43 | 44 | if !c.customType.Equal(other.customType) { 45 | return false 46 | } 47 | 48 | return c.name == other.name 49 | } 50 | 51 | func (c CustomTypeObject) Imports() *schema.Imports { 52 | imports := schema.NewImports() 53 | 54 | if c.customType != nil { 55 | if c.customType.HasImport() { 56 | imports.Add(*c.customType.Import) 57 | } 58 | } else { 59 | imports.Add(code.Import{ 60 | Path: schema.TypesImport, 61 | }) 62 | } 63 | 64 | return imports 65 | } 66 | 67 | func (c CustomTypeObject) Schema() []byte { 68 | var customType string 69 | 70 | switch { 71 | case c.customType != nil: 72 | customType = c.customType.Type 73 | case c.associatedExternalType != nil: 74 | customType = fmt.Sprintf("%sType{\ntypes.ObjectType{\nAttrTypes: %sValue{}.AttributeTypes(ctx),\n},\n}", format.ToPascalCase(c.name), format.ToPascalCase(c.name)) 75 | } 76 | 77 | if customType != "" { 78 | return []byte(fmt.Sprintf("CustomType: %s,\n", customType)) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func (c CustomTypeObject) ValueType() string { 85 | switch { 86 | case c.customType != nil: 87 | return c.customType.ValueType 88 | case c.associatedExternalType != nil: 89 | return fmt.Sprintf("%sValue", format.ToPascalCase(c.name)) 90 | } 91 | 92 | return "" 93 | } 94 | -------------------------------------------------------------------------------- /internal/convert/custom_type_primitive.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/format" 13 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 14 | ) 15 | 16 | type CustomTypePrimitive struct { 17 | associatedExternalType *specschema.AssociatedExternalType 18 | customType *specschema.CustomType 19 | name string 20 | } 21 | 22 | // NewCustomTypePrimitive constructs an CustomTypePrimitive which is used to determine whether a CustomType 23 | // should be assigned to a primitive attribute in the schema. 24 | // 25 | // If a CustomType has been declared in the spec, then the CustomType.Type will be used as 26 | // the CustomType in the Schema. 27 | // 28 | // If the spec CustomType is nil, and the spec AssociatedExternalType is not nil, the generator 29 | // will create custom Type and Value types using the attribute name, and the generated custom 30 | // Type type will be used as the CustomType in the schema. 31 | func NewCustomTypePrimitive(c *specschema.CustomType, a *specschema.AssociatedExternalType, name string) CustomTypePrimitive { 32 | return CustomTypePrimitive{ 33 | associatedExternalType: a, 34 | customType: c, 35 | name: name, 36 | } 37 | } 38 | 39 | func (c CustomTypePrimitive) Equal(other CustomTypePrimitive) bool { 40 | if !c.associatedExternalType.Equal(other.associatedExternalType) { 41 | return false 42 | } 43 | 44 | if !c.customType.Equal(other.customType) { 45 | return false 46 | } 47 | 48 | return c.name == other.name 49 | } 50 | 51 | func (c CustomTypePrimitive) Imports() *schema.Imports { 52 | imports := schema.NewImports() 53 | 54 | if c.customType != nil { 55 | if c.customType.HasImport() { 56 | imports.Add(*c.customType.Import) 57 | } 58 | } else { 59 | imports.Add(code.Import{ 60 | Path: schema.TypesImport, 61 | }) 62 | } 63 | 64 | return imports 65 | } 66 | 67 | func (c CustomTypePrimitive) Schema() []byte { 68 | var customType string 69 | 70 | switch { 71 | case c.customType != nil: 72 | customType = c.customType.Type 73 | case c.associatedExternalType != nil: 74 | customType = fmt.Sprintf("%sType{}", format.ToPascalCase(c.name)) 75 | } 76 | 77 | if customType != "" { 78 | return []byte(fmt.Sprintf("CustomType: %s,\n", customType)) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func (c CustomTypePrimitive) ValueType() string { 85 | switch { 86 | case c.customType != nil: 87 | return c.customType.ValueType 88 | case c.associatedExternalType != nil: 89 | return fmt.Sprintf("%sValue", format.ToPascalCase(c.name)) 90 | } 91 | 92 | return "" 93 | } 94 | -------------------------------------------------------------------------------- /internal/convert/default_bool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 13 | ) 14 | 15 | const defaultBoolImport = "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" 16 | 17 | type DefaultBool struct { 18 | boolDefault *specschema.BoolDefault 19 | } 20 | 21 | func NewDefaultBool(b *specschema.BoolDefault) DefaultBool { 22 | return DefaultBool{ 23 | boolDefault: b, 24 | } 25 | } 26 | 27 | func (d DefaultBool) Equal(other DefaultBool) bool { 28 | return d.boolDefault.Equal(other.boolDefault) 29 | } 30 | 31 | func (d DefaultBool) Imports() *generatorschema.Imports { 32 | imports := generatorschema.NewImports() 33 | 34 | if d.boolDefault == nil { 35 | return imports 36 | } 37 | 38 | if d.boolDefault.Static != nil { 39 | imports.Add(code.Import{ 40 | Path: defaultBoolImport, 41 | }) 42 | } 43 | 44 | if d.boolDefault.Custom != nil { 45 | for _, i := range d.boolDefault.Custom.Imports { 46 | if len(i.Path) > 0 { 47 | imports.Add(i) 48 | } 49 | } 50 | } 51 | 52 | return imports 53 | } 54 | 55 | func (d DefaultBool) Schema() []byte { 56 | if d.boolDefault == nil { 57 | return nil 58 | } 59 | 60 | if d.boolDefault.Static != nil { 61 | return []byte(fmt.Sprintf("Default: booldefault.StaticBool(%t),\n", *d.boolDefault.Static)) 62 | } 63 | 64 | if d.boolDefault.Custom != nil && d.boolDefault.Custom.SchemaDefinition != "" { 65 | return []byte(fmt.Sprintf("Default: %s,\n", d.boolDefault.Custom.SchemaDefinition)) 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/convert/default_custom.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 10 | 11 | generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 12 | ) 13 | 14 | type DefaultCustom struct { 15 | custom *specschema.CustomDefault 16 | } 17 | 18 | func NewDefaultCustom(c *specschema.CustomDefault) DefaultCustom { 19 | return DefaultCustom{ 20 | custom: c, 21 | } 22 | } 23 | 24 | func (d DefaultCustom) Equal(other DefaultCustom) bool { 25 | return d.custom.Equal(other.custom) 26 | } 27 | 28 | func (d DefaultCustom) Imports() *generatorschema.Imports { 29 | imports := generatorschema.NewImports() 30 | 31 | if d.custom == nil { 32 | return imports 33 | } 34 | 35 | for _, i := range d.custom.Imports { 36 | if len(i.Path) > 0 { 37 | imports.Add(i) 38 | } 39 | } 40 | 41 | return imports 42 | } 43 | 44 | func (d DefaultCustom) Schema() []byte { 45 | if d.custom != nil && d.custom.SchemaDefinition != "" { 46 | return []byte(fmt.Sprintf("Default: %s,\n", d.custom.SchemaDefinition)) 47 | } 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /internal/convert/default_float64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 13 | ) 14 | 15 | const defaultFloat64Import = "github.com/hashicorp/terraform-plugin-framework/resource/schema/float64default" 16 | 17 | type DefaultFloat64 struct { 18 | float64Default *specschema.Float64Default 19 | } 20 | 21 | func NewDefaultFloat64(b *specschema.Float64Default) DefaultFloat64 { 22 | return DefaultFloat64{ 23 | float64Default: b, 24 | } 25 | } 26 | 27 | func (d DefaultFloat64) Equal(other DefaultFloat64) bool { 28 | return d.float64Default.Equal(other.float64Default) 29 | } 30 | 31 | func (d DefaultFloat64) Imports() *generatorschema.Imports { 32 | imports := generatorschema.NewImports() 33 | 34 | if d.float64Default == nil { 35 | return imports 36 | } 37 | 38 | if d.float64Default.Static != nil { 39 | imports.Add(code.Import{ 40 | Path: defaultFloat64Import, 41 | }) 42 | } 43 | 44 | if d.float64Default.Custom != nil { 45 | for _, i := range d.float64Default.Custom.Imports { 46 | if len(i.Path) > 0 { 47 | imports.Add(i) 48 | } 49 | } 50 | } 51 | 52 | return imports 53 | } 54 | 55 | func (d DefaultFloat64) Schema() []byte { 56 | if d.float64Default == nil { 57 | return nil 58 | } 59 | 60 | if d.float64Default.Static != nil { 61 | return []byte(fmt.Sprintf("Default: float64default.StaticFloat64(%g),\n", *d.float64Default.Static)) 62 | } 63 | 64 | if d.float64Default.Custom != nil && d.float64Default.Custom.SchemaDefinition != "" { 65 | return []byte(fmt.Sprintf("Default: %s,\n", d.float64Default.Custom.SchemaDefinition)) 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/convert/default_int64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 13 | ) 14 | 15 | const defaultInt64Import = "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" 16 | 17 | type DefaultInt64 struct { 18 | int64Default *specschema.Int64Default 19 | } 20 | 21 | func NewDefaultInt64(b *specschema.Int64Default) DefaultInt64 { 22 | return DefaultInt64{ 23 | int64Default: b, 24 | } 25 | } 26 | 27 | func (d DefaultInt64) Equal(other DefaultInt64) bool { 28 | return d.int64Default.Equal(other.int64Default) 29 | } 30 | 31 | func (d DefaultInt64) Imports() *generatorschema.Imports { 32 | imports := generatorschema.NewImports() 33 | 34 | if d.int64Default == nil { 35 | return imports 36 | } 37 | 38 | if d.int64Default.Static != nil { 39 | imports.Add(code.Import{ 40 | Path: defaultInt64Import, 41 | }) 42 | } 43 | 44 | if d.int64Default.Custom != nil { 45 | for _, i := range d.int64Default.Custom.Imports { 46 | if len(i.Path) > 0 { 47 | imports.Add(i) 48 | } 49 | } 50 | } 51 | 52 | return imports 53 | } 54 | 55 | func (d DefaultInt64) Schema() []byte { 56 | if d.int64Default == nil { 57 | return nil 58 | } 59 | 60 | if d.int64Default.Static != nil { 61 | return []byte(fmt.Sprintf("Default: int64default.StaticInt64(%d),\n", *d.int64Default.Static)) 62 | } 63 | 64 | if d.int64Default.Custom != nil && d.int64Default.Custom.SchemaDefinition != "" { 65 | return []byte(fmt.Sprintf("Default: %s,\n", d.int64Default.Custom.SchemaDefinition)) 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/convert/default_string.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 10 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 11 | 12 | generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 13 | ) 14 | 15 | const defaultStringImport = "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" 16 | 17 | type DefaultString struct { 18 | stringDefault *specschema.StringDefault 19 | } 20 | 21 | func NewDefaultString(b *specschema.StringDefault) DefaultString { 22 | return DefaultString{ 23 | stringDefault: b, 24 | } 25 | } 26 | 27 | func (d DefaultString) Equal(other DefaultString) bool { 28 | return d.stringDefault.Equal(other.stringDefault) 29 | } 30 | 31 | func (d DefaultString) Imports() *generatorschema.Imports { 32 | imports := generatorschema.NewImports() 33 | 34 | if d.stringDefault == nil { 35 | return imports 36 | } 37 | 38 | if d.stringDefault.Static != nil { 39 | imports.Add(code.Import{ 40 | Path: defaultStringImport, 41 | }) 42 | } 43 | 44 | if d.stringDefault.Custom != nil { 45 | for _, i := range d.stringDefault.Custom.Imports { 46 | if len(i.Path) > 0 { 47 | imports.Add(i) 48 | } 49 | } 50 | } 51 | 52 | return imports 53 | } 54 | 55 | func (d DefaultString) Schema() []byte { 56 | if d.stringDefault == nil { 57 | return nil 58 | } 59 | 60 | if d.stringDefault.Static != nil { 61 | return []byte(fmt.Sprintf("Default: stringdefault.StaticString(%q),\n", *d.stringDefault.Static)) 62 | } 63 | 64 | if d.stringDefault.Custom != nil && d.stringDefault.Custom.SchemaDefinition != "" { 65 | return []byte(fmt.Sprintf("Default: %s,\n", d.stringDefault.Custom.SchemaDefinition)) 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/convert/deprecation_message.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | ) 10 | 11 | type DeprecationMessage struct { 12 | deprecationMessage *string 13 | } 14 | 15 | func NewDeprecationMessage(d *string) DeprecationMessage { 16 | return DeprecationMessage{ 17 | deprecationMessage: d, 18 | } 19 | } 20 | 21 | func (d DeprecationMessage) DeprecationMessage() string { 22 | if d.deprecationMessage == nil { 23 | return "" 24 | } 25 | 26 | return *d.deprecationMessage 27 | } 28 | 29 | func (d DeprecationMessage) Equal(other DeprecationMessage) bool { 30 | if d.deprecationMessage == nil && other.deprecationMessage == nil { 31 | return true 32 | } 33 | 34 | if d.deprecationMessage == nil || other.deprecationMessage == nil { 35 | return false 36 | } 37 | 38 | return *d.deprecationMessage == *other.deprecationMessage 39 | } 40 | 41 | func (d DeprecationMessage) Schema() []byte { 42 | if d.deprecationMessage != nil { 43 | return []byte(fmt.Sprintf("DeprecationMessage: %s,\n", strconv.Quote(*d.deprecationMessage))) 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /internal/convert/description.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "strconv" 10 | ) 11 | 12 | type Description struct { 13 | description *string 14 | } 15 | 16 | func NewDescription(d *string) Description { 17 | return Description{ 18 | description: d, 19 | } 20 | } 21 | 22 | func (d Description) Description() string { 23 | if d.description == nil { 24 | return "" 25 | } 26 | 27 | return *d.description 28 | } 29 | 30 | func (d Description) Equal(other Description) bool { 31 | if d.description == nil && other.description == nil { 32 | return true 33 | } 34 | 35 | if d.description == nil || other.description == nil { 36 | return false 37 | } 38 | 39 | return *d.description == *other.description 40 | } 41 | 42 | func (d Description) Schema() []byte { 43 | var b bytes.Buffer 44 | 45 | if d.description != nil { 46 | quotedDescription := strconv.Quote(*d.description) 47 | 48 | b.WriteString(fmt.Sprintf("Description: %s,\n", quotedDescription)) 49 | b.WriteString(fmt.Sprintf("MarkdownDescription: %s,\n", quotedDescription)) 50 | } 51 | 52 | return b.Bytes() 53 | } 54 | -------------------------------------------------------------------------------- /internal/convert/nested_attribute_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | 9 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 10 | 11 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 12 | ) 13 | 14 | type NestedAttributeObject struct { 15 | attributes schema.GeneratorAttributes 16 | customType CustomTypeNestedObject 17 | validators Validators 18 | } 19 | 20 | // NewNestedAttributeObject constructs a NestedAttributeObject which is used to generate a 21 | // nested attribute object in the schema. 22 | func NewNestedAttributeObject(a schema.GeneratorAttributes, c *specschema.CustomType, v Validators, name string) NestedAttributeObject { 23 | return NestedAttributeObject{ 24 | attributes: a, 25 | customType: NewCustomTypeNestedObject(c, name), 26 | validators: v, 27 | } 28 | } 29 | 30 | func (n NestedAttributeObject) Equal(other NestedAttributeObject) bool { 31 | if !n.attributes.Equal(other.attributes) { 32 | return false 33 | } 34 | 35 | if !n.customType.Equal(other.customType) { 36 | return false 37 | } 38 | 39 | return n.validators.Equal(other.validators) 40 | } 41 | 42 | func (n NestedAttributeObject) Imports() *schema.Imports { 43 | imports := schema.NewImports() 44 | 45 | imports.Append(n.customType.Imports()) 46 | 47 | imports.Append(n.validators.Imports()) 48 | 49 | imports.Append(n.attributes.Imports()) 50 | 51 | return imports 52 | } 53 | 54 | func (n NestedAttributeObject) Schema() ([]byte, error) { 55 | var b bytes.Buffer 56 | 57 | attributesSchema, err := n.attributes.Schema() 58 | 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | b.WriteString("NestedObject: schema.NestedAttributeObject{\n") 64 | b.WriteString("Attributes: map[string]schema.Attribute{") 65 | b.WriteString(attributesSchema) 66 | b.WriteString("\n},\n") 67 | b.Write(n.customType.Schema()) 68 | b.Write(n.validators.Schema()) 69 | b.WriteString("},\n") 70 | 71 | return b.Bytes(), nil 72 | } 73 | -------------------------------------------------------------------------------- /internal/convert/nested_block_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | 9 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 10 | 11 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 12 | ) 13 | 14 | type NestedBlockObject struct { 15 | attributes schema.GeneratorAttributes 16 | blocks schema.GeneratorBlocks 17 | customType CustomTypeNestedObject 18 | validators Validators 19 | } 20 | 21 | // NewNestedBlockObject constructs a NestedBlockObject which is used to generate a 22 | // nested attribute block in the schema. 23 | func NewNestedBlockObject(a schema.GeneratorAttributes, b schema.GeneratorBlocks, c *specschema.CustomType, v Validators, name string) NestedBlockObject { 24 | return NestedBlockObject{ 25 | attributes: a, 26 | blocks: b, 27 | customType: NewCustomTypeNestedObject(c, name), 28 | validators: v, 29 | } 30 | } 31 | 32 | func (n NestedBlockObject) Equal(other NestedBlockObject) bool { 33 | if !n.attributes.Equal(other.attributes) { 34 | return false 35 | } 36 | 37 | if !n.blocks.Equal(other.blocks) { 38 | return false 39 | } 40 | 41 | if !n.customType.Equal(other.customType) { 42 | return false 43 | } 44 | 45 | return n.validators.Equal(other.validators) 46 | } 47 | 48 | func (n NestedBlockObject) Imports() *schema.Imports { 49 | imports := schema.NewImports() 50 | 51 | imports.Append(n.customType.Imports()) 52 | 53 | imports.Append(n.validators.Imports()) 54 | 55 | imports.Append(n.attributes.Imports()) 56 | 57 | imports.Append(n.blocks.Imports()) 58 | 59 | return imports 60 | } 61 | 62 | func (n NestedBlockObject) Schema() ([]byte, error) { 63 | var b bytes.Buffer 64 | 65 | attributesSchema, err := n.attributes.Schema() 66 | 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | blocksSchema, err := n.blocks.Schema() 72 | 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | b.WriteString("NestedObject: schema.NestedBlockObject{\n") 78 | if attributesSchema != "" { 79 | b.WriteString("Attributes: map[string]schema.Attribute{") 80 | b.WriteString(attributesSchema) 81 | b.WriteString("\n},\n") 82 | } 83 | if blocksSchema != "" { 84 | b.WriteString("Blocks: map[string]schema.Block{") 85 | b.WriteString(blocksSchema) 86 | b.WriteString("\n},\n") 87 | } 88 | b.Write(n.customType.Schema()) 89 | b.Write(n.validators.Schema()) 90 | b.WriteString("},\n") 91 | 92 | return b.Bytes(), nil 93 | } 94 | -------------------------------------------------------------------------------- /internal/convert/optional_required.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | 9 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 10 | ) 11 | 12 | type OptionalRequired struct { 13 | optionalRequired specschema.OptionalRequired 14 | } 15 | 16 | func NewOptionalRequired(c specschema.OptionalRequired) OptionalRequired { 17 | return OptionalRequired{ 18 | optionalRequired: c, 19 | } 20 | } 21 | 22 | func (o OptionalRequired) Equal(other OptionalRequired) bool { 23 | return o.optionalRequired.Equal(other.optionalRequired) 24 | } 25 | 26 | func (o OptionalRequired) IsRequired() bool { 27 | return o.optionalRequired == specschema.Required 28 | } 29 | 30 | func (o OptionalRequired) IsOptional() bool { 31 | return o.optionalRequired == specschema.Optional 32 | } 33 | 34 | func (o OptionalRequired) Schema() []byte { 35 | var b bytes.Buffer 36 | 37 | if o.IsRequired() { 38 | b.WriteString("Required: true,\n") 39 | } 40 | 41 | if o.IsOptional() { 42 | b.WriteString("Optional: true,\n") 43 | } 44 | 45 | return b.Bytes() 46 | } 47 | -------------------------------------------------------------------------------- /internal/convert/plan_modifiers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | 10 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 11 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 12 | 13 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 14 | ) 15 | 16 | const ( 17 | PlanModifierTypeBool PlanModifierType = "Bool" 18 | PlanModifierTypeFloat64 PlanModifierType = "Float64" 19 | PlanModifierTypeInt64 PlanModifierType = "Int64" 20 | PlanModifierTypeList PlanModifierType = "List" 21 | PlanModifierTypeMap PlanModifierType = "Map" 22 | PlanModifierTypeNumber PlanModifierType = "Number" 23 | PlanModifierTypeObject PlanModifierType = "Object" 24 | PlanModifierTypeSet PlanModifierType = "Set" 25 | PlanModifierTypeString PlanModifierType = "String" 26 | ) 27 | 28 | type PlanModifierType string 29 | 30 | type PlanModifiers struct { 31 | planModifierType PlanModifierType 32 | custom specschema.CustomPlanModifiers 33 | } 34 | 35 | func NewPlanModifiers(t PlanModifierType, c specschema.CustomPlanModifiers) PlanModifiers { 36 | return PlanModifiers{ 37 | planModifierType: t, 38 | custom: c, 39 | } 40 | } 41 | 42 | func (v PlanModifiers) Equal(other PlanModifiers) bool { 43 | if v.planModifierType != other.planModifierType { 44 | return false 45 | } 46 | 47 | if len(v.custom) == 0 && len(other.custom) == 0 { 48 | return true 49 | } 50 | 51 | if len(v.custom) != len(other.custom) { 52 | return false 53 | } 54 | 55 | v.custom.Sort() 56 | 57 | other.custom.Sort() 58 | 59 | for i := 0; i < len(v.custom); i++ { 60 | if !v.custom[i].Equal(other.custom[i]) { 61 | return false 62 | } 63 | } 64 | 65 | return true 66 | } 67 | 68 | func (v PlanModifiers) Imports() *schema.Imports { 69 | imports := schema.NewImports() 70 | 71 | if v.custom == nil { 72 | return imports 73 | } 74 | 75 | for _, c := range v.custom { 76 | for _, i := range c.Imports { 77 | if len(i.Path) > 0 { 78 | imports.Add(code.Import{ 79 | Path: schema.PlanModifierImport, 80 | }) 81 | 82 | imports.Add(i) 83 | } 84 | } 85 | } 86 | 87 | return imports 88 | } 89 | 90 | func (v PlanModifiers) Schema() []byte { 91 | var b, cb bytes.Buffer 92 | 93 | for _, c := range v.custom { 94 | if c == nil { 95 | continue 96 | } 97 | 98 | if c.SchemaDefinition == "" { 99 | continue 100 | } 101 | 102 | cb.WriteString(fmt.Sprintf("%s,\n", c.SchemaDefinition)) 103 | } 104 | 105 | if cb.Len() > 0 { 106 | b.WriteString(fmt.Sprintf("PlanModifiers: []planmodifier.%s{\n", v.planModifierType)) 107 | b.Write(cb.Bytes()) 108 | b.WriteString("},\n") 109 | } 110 | 111 | return b.Bytes() 112 | } 113 | -------------------------------------------------------------------------------- /internal/convert/sensitive.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | type Sensitive struct { 7 | sensitive *bool 8 | } 9 | 10 | func NewSensitive(s *bool) Sensitive { 11 | return Sensitive{ 12 | sensitive: s, 13 | } 14 | } 15 | 16 | func (s Sensitive) Equal(other Sensitive) bool { 17 | if s.sensitive == nil && other.sensitive == nil { 18 | return true 19 | } 20 | 21 | if s.sensitive == nil || other.sensitive == nil { 22 | return false 23 | } 24 | 25 | return *s.sensitive == *other.sensitive 26 | } 27 | 28 | func (s Sensitive) IsSensitive() bool { 29 | if s.sensitive == nil { 30 | return false 31 | } 32 | 33 | return *s.sensitive 34 | } 35 | 36 | func (s Sensitive) Schema() []byte { 37 | if s.IsSensitive() { 38 | return []byte("Sensitive: true,\n") 39 | } 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /internal/convert/validators.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package convert 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | 10 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 11 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 12 | 13 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 14 | ) 15 | 16 | const ( 17 | ValidatorTypeBool ValidatorType = "Bool" 18 | ValidatorTypeFloat64 ValidatorType = "Float64" 19 | ValidatorTypeInt64 ValidatorType = "Int64" 20 | ValidatorTypeList ValidatorType = "List" 21 | ValidatorTypeMap ValidatorType = "Map" 22 | ValidatorTypeNumber ValidatorType = "Number" 23 | ValidatorTypeObject ValidatorType = "Object" 24 | ValidatorTypeSet ValidatorType = "Set" 25 | ValidatorTypeString ValidatorType = "String" 26 | ) 27 | 28 | type ValidatorType string 29 | 30 | type Validators struct { 31 | validatorType ValidatorType 32 | custom specschema.CustomValidators 33 | } 34 | 35 | func NewValidators(t ValidatorType, c specschema.CustomValidators) Validators { 36 | return Validators{ 37 | validatorType: t, 38 | custom: c, 39 | } 40 | } 41 | 42 | func (v Validators) Equal(other Validators) bool { 43 | if v.validatorType != other.validatorType { 44 | return false 45 | } 46 | 47 | if len(v.custom) == 0 && len(other.custom) == 0 { 48 | return true 49 | } 50 | 51 | if len(v.custom) != len(other.custom) { 52 | return false 53 | } 54 | 55 | v.custom.Sort() 56 | 57 | other.custom.Sort() 58 | 59 | for i := 0; i < len(v.custom); i++ { 60 | if !v.custom[i].Equal(other.custom[i]) { 61 | return false 62 | } 63 | } 64 | 65 | return true 66 | } 67 | 68 | func (v Validators) Imports() *schema.Imports { 69 | imports := schema.NewImports() 70 | 71 | if v.custom == nil { 72 | return imports 73 | } 74 | 75 | for _, c := range v.custom { 76 | for _, i := range c.Imports { 77 | if len(i.Path) > 0 { 78 | imports.Add(code.Import{ 79 | Path: schema.ValidatorImport, 80 | }) 81 | 82 | imports.Add(i) 83 | } 84 | } 85 | } 86 | 87 | return imports 88 | } 89 | 90 | func (v Validators) Schema() []byte { 91 | var b, cb bytes.Buffer 92 | 93 | for _, c := range v.custom { 94 | if c == nil { 95 | continue 96 | } 97 | 98 | if c.SchemaDefinition == "" { 99 | continue 100 | } 101 | 102 | cb.WriteString(fmt.Sprintf("%s,\n", c.SchemaDefinition)) 103 | } 104 | 105 | if cb.Len() > 0 { 106 | b.WriteString(fmt.Sprintf("Validators: []validator.%s{\n", v.validatorType)) 107 | b.Write(cb.Bytes()) 108 | b.WriteString("},\n") 109 | } 110 | 111 | return b.Bytes() 112 | } 113 | -------------------------------------------------------------------------------- /internal/datasource/testdata/model.txt: -------------------------------------------------------------------------------- 1 | 2 | type ExampleModel struct { 3 | BoolAttribute types.Bool `tfsdk:"bool_attribute"` 4 | BoolAttributeCustom my_bool_value_type `tfsdk:"bool_attribute_custom"` 5 | Float64Attribute types.Float64 `tfsdk:"float64_attribute"` 6 | Float64AttributeCustom my_float64_value_type `tfsdk:"float64_attribute_custom"` 7 | Int64Attribute types.Int64 `tfsdk:"int64_attribute"` 8 | Int64AttributeCustom my_int64_value_type `tfsdk:"int64_attribute_custom"` 9 | ListAttribute types.List `tfsdk:"list_attribute"` 10 | ListAttributeCustom my_list_value_type `tfsdk:"list_attribute_custom"` 11 | ListNestedAttribute types.List `tfsdk:"list_nested_attribute"` 12 | ListNestedAttributeCustom my_list_nested_value_type `tfsdk:"list_nested_attribute_custom"` 13 | MapAttribute types.Map `tfsdk:"map_attribute"` 14 | MapAttributeCustom my_map_value_type `tfsdk:"map_attribute_custom"` 15 | MapNestedAttribute types.Map `tfsdk:"map_nested_attribute"` 16 | MapNestedAttributeCustom my_map_nested_value_type `tfsdk:"map_nested_attribute_custom"` 17 | NumberAttribute types.Number `tfsdk:"number_attribute"` 18 | NumberAttributeCustom my_number_value_type `tfsdk:"number_attribute_custom"` 19 | ObjectAttribute types.Object `tfsdk:"object_attribute"` 20 | ObjectAttributeCustom my_object_value_type `tfsdk:"object_attribute_custom"` 21 | SetAttribute types.Set `tfsdk:"set_attribute"` 22 | SetAttributeCustom my_set_value_type `tfsdk:"set_attribute_custom"` 23 | SetNestedAttribute types.Set `tfsdk:"set_nested_attribute"` 24 | SetNestedAttributeCustom my_set_nested_value_type `tfsdk:"set_nested_attribute_custom"` 25 | SingleNestedAttribute SingleNestedAttributeValue `tfsdk:"single_nested_attribute"` 26 | SingleNestedAttributeCustom my_single_nested_value_type `tfsdk:"single_nested_attribute_custom"` 27 | StringAttribute types.String `tfsdk:"string_attribute"` 28 | StringAttributeCustom my_string_value_type `tfsdk:"string_attribute_custom"` 29 | ListNestedBlock types.List `tfsdk:"list_nested_block"` 30 | ListNestedBlockCustom my_list_nested_value_type `tfsdk:"list_nested_block_custom"` 31 | SetNestedBlock types.Set `tfsdk:"set_nested_block"` 32 | SetNestedBlockCustom my_set_nested_value_type `tfsdk:"set_nested_block_custom"` 33 | SingleNestedBlock SingleNestedBlockValue `tfsdk:"single_nested_block"` 34 | SingleNestedBlockCustom my_single_nested_value_type `tfsdk:"single_nested_block_custom"` 35 | } 36 | -------------------------------------------------------------------------------- /internal/datasource/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package datasource 5 | 6 | import ( 7 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 10 | ) 11 | 12 | type GeneratorNestedAttributeObject struct { 13 | Attributes schema.GeneratorAttributes 14 | AssociatedExternalType *schema.AssocExtType 15 | CustomType *specschema.CustomType 16 | Validators specschema.ObjectValidators 17 | } 18 | 19 | func (g GeneratorNestedAttributeObject) Equal(other GeneratorNestedAttributeObject) bool { 20 | if !g.Attributes.Equal(other.Attributes) { 21 | return false 22 | } 23 | 24 | if !g.AssociatedExternalType.Equal(other.AssociatedExternalType) { 25 | return false 26 | } 27 | 28 | if !g.CustomType.Equal(other.CustomType) { 29 | return false 30 | } 31 | 32 | return g.Validators.Equal(other.Validators) 33 | } 34 | 35 | type GeneratorNestedBlockObject struct { 36 | Attributes schema.GeneratorAttributes 37 | Blocks schema.GeneratorBlocks 38 | 39 | AssociatedExternalType *schema.AssocExtType 40 | CustomType *specschema.CustomType 41 | Validators specschema.ObjectValidators 42 | } 43 | 44 | func (g GeneratorNestedBlockObject) Equal(other GeneratorNestedBlockObject) bool { 45 | for k := range g.Attributes { 46 | if _, ok := other.Attributes[k]; !ok { 47 | return false 48 | } 49 | 50 | if !g.Attributes[k].Equal(other.Attributes[k]) { 51 | return false 52 | } 53 | } 54 | 55 | for k := range g.Blocks { 56 | if _, ok := other.Blocks[k]; !ok { 57 | return false 58 | } 59 | 60 | if !g.Blocks[k].Equal(other.Blocks[k]) { 61 | return false 62 | } 63 | } 64 | 65 | if !g.AssociatedExternalType.Equal(other.AssociatedExternalType) { 66 | return false 67 | } 68 | 69 | if !g.CustomType.Equal(other.CustomType) { 70 | return false 71 | } 72 | 73 | return g.Validators.Equal(other.Validators) 74 | } 75 | -------------------------------------------------------------------------------- /internal/format/format.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package format 5 | 6 | import ( 7 | "go/format" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | func Format(schemas map[string][]byte) (map[string][]byte, error) { 13 | formattedSchemas := make(map[string][]byte, len(schemas)) 14 | 15 | for k, v := range schemas { 16 | formattedSchema, err := format.Source(v) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | formattedSchemas[k] = formattedSchema 22 | } 23 | 24 | return formattedSchemas, nil 25 | } 26 | 27 | func ToPascalCase(str string) string { 28 | return snakeLetters.ReplaceAllStringFunc(str, func(s string) string { 29 | return strings.ToUpper(strings.Replace(s, "_", "", -1)) 30 | }) 31 | } 32 | 33 | // snakeLetters will match to the first letter and an underscore followed by a letter 34 | var snakeLetters = regexp.MustCompile("(^[a-z])|_[a-z0-9]") 35 | -------------------------------------------------------------------------------- /internal/input/read.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package input 5 | 6 | import ( 7 | "io" 8 | "os" 9 | ) 10 | 11 | func Read(path string) ([]byte, error) { 12 | if path == "" { 13 | stdin, err := io.ReadAll(os.Stdin) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | return stdin, nil 19 | } 20 | 21 | src, err := os.ReadFile(path) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | return src, nil 27 | } 28 | -------------------------------------------------------------------------------- /internal/logging/context.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package logging 5 | 6 | import ( 7 | "context" 8 | "strings" 9 | ) 10 | 11 | type pathstring string 12 | 13 | const path pathstring = "path" 14 | 15 | type Path []string 16 | 17 | // SetPathInContext is used to maintain a path indicating the current 18 | // location within the schema that is being processed. 19 | func SetPathInContext(ctx context.Context, pathStep string) context.Context { 20 | if v, ok := ctx.Value(path).(Path); ok { 21 | return context.WithValue(ctx, path, append(v, pathStep)) 22 | } 23 | 24 | return context.WithValue(ctx, path, Path{pathStep}) 25 | } 26 | 27 | // GetPathFromContext returns a dot-separated path. 28 | func GetPathFromContext(ctx context.Context) string { 29 | if v, ok := ctx.Value(path).(Path); ok { 30 | return strings.Join(v, ".") 31 | } 32 | 33 | return "" 34 | } 35 | -------------------------------------------------------------------------------- /internal/model/model.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package model 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | BoolValueType = "types.Bool" 13 | Float64ValueType = "types.Float64" 14 | Int64ValueType = "types.Int64" 15 | ListValueType = "types.List" 16 | MapValueType = "types.Map" 17 | NumberValueType = "types.Number" 18 | ObjectValueType = "types.Object" 19 | SetValueType = "types.Set" 20 | StringValueType = "types.String" 21 | ) 22 | 23 | type Field struct { 24 | Name string 25 | TfsdkName string 26 | ValueType string 27 | } 28 | 29 | func (f Field) String() string { 30 | return fmt.Sprintf("%s %s `tfsdk:%q`", f.Name, f.ValueType, f.TfsdkName) 31 | } 32 | 33 | type Model struct { 34 | Name string 35 | Fields []Field 36 | } 37 | 38 | func (m Model) String() string { 39 | var fieldsStr string 40 | 41 | for _, field := range m.Fields { 42 | fieldsStr += field.String() + "\n" 43 | } 44 | 45 | fieldsStrTrim := strings.TrimSuffix(fieldsStr, "\n") 46 | 47 | return fmt.Sprintf("type %sModel struct {\n%s\n}", m.Name, fieldsStrTrim) 48 | } 49 | -------------------------------------------------------------------------------- /internal/provider/testdata/model.txt: -------------------------------------------------------------------------------- 1 | 2 | type ExampleModel struct { 3 | BoolAttribute types.Bool `tfsdk:"bool_attribute"` 4 | BoolAttributeCustom my_bool_value_type `tfsdk:"bool_attribute_custom"` 5 | Float64Attribute types.Float64 `tfsdk:"float64_attribute"` 6 | Float64AttributeCustom my_float64_value_type `tfsdk:"float64_attribute_custom"` 7 | Int64Attribute types.Int64 `tfsdk:"int64_attribute"` 8 | Int64AttributeCustom my_int64_value_type `tfsdk:"int64_attribute_custom"` 9 | ListAttribute types.List `tfsdk:"list_attribute"` 10 | ListAttributeCustom my_list_value_type `tfsdk:"list_attribute_custom"` 11 | ListNestedAttribute types.List `tfsdk:"list_nested_attribute"` 12 | ListNestedAttributeCustom my_list_nested_value_type `tfsdk:"list_nested_attribute_custom"` 13 | MapAttribute types.Map `tfsdk:"map_attribute"` 14 | MapAttributeCustom my_map_value_type `tfsdk:"map_attribute_custom"` 15 | MapNestedAttribute types.Map `tfsdk:"map_nested_attribute"` 16 | MapNestedAttributeCustom my_map_nested_value_type `tfsdk:"map_nested_attribute_custom"` 17 | NumberAttribute types.Number `tfsdk:"number_attribute"` 18 | NumberAttributeCustom my_number_value_type `tfsdk:"number_attribute_custom"` 19 | ObjectAttribute types.Object `tfsdk:"object_attribute"` 20 | ObjectAttributeCustom my_object_value_type `tfsdk:"object_attribute_custom"` 21 | SetAttribute types.Set `tfsdk:"set_attribute"` 22 | SetAttributeCustom my_set_value_type `tfsdk:"set_attribute_custom"` 23 | SetNestedAttribute types.Set `tfsdk:"set_nested_attribute"` 24 | SetNestedAttributeCustom my_set_nested_value_type `tfsdk:"set_nested_attribute_custom"` 25 | SingleNestedAttribute SingleNestedAttributeValue `tfsdk:"single_nested_attribute"` 26 | SingleNestedAttributeCustom my_single_nested_value_type `tfsdk:"single_nested_attribute_custom"` 27 | StringAttribute types.String `tfsdk:"string_attribute"` 28 | StringAttributeCustom my_string_value_type `tfsdk:"string_attribute_custom"` 29 | ListNestedBlock types.List `tfsdk:"list_nested_block"` 30 | ListNestedBlockCustom my_list_nested_value_type `tfsdk:"list_nested_block_custom"` 31 | SetNestedBlock types.Set `tfsdk:"set_nested_block"` 32 | SetNestedBlockCustom my_set_nested_value_type `tfsdk:"set_nested_block_custom"` 33 | SingleNestedBlock SingleNestedBlockValue `tfsdk:"single_nested_block"` 34 | SingleNestedBlockCustom my_single_nested_value_type `tfsdk:"single_nested_block_custom"` 35 | } 36 | -------------------------------------------------------------------------------- /internal/provider/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 10 | ) 11 | 12 | type GeneratorNestedAttributeObject struct { 13 | Attributes schema.GeneratorAttributes 14 | AssociatedExternalType *schema.AssocExtType 15 | CustomType *specschema.CustomType 16 | Validators specschema.ObjectValidators 17 | } 18 | 19 | func (g GeneratorNestedAttributeObject) Equal(other GeneratorNestedAttributeObject) bool { 20 | if !g.Attributes.Equal(other.Attributes) { 21 | return false 22 | } 23 | 24 | if !g.AssociatedExternalType.Equal(other.AssociatedExternalType) { 25 | return false 26 | } 27 | 28 | if !g.CustomType.Equal(other.CustomType) { 29 | return false 30 | } 31 | 32 | return g.Validators.Equal(other.Validators) 33 | } 34 | 35 | type GeneratorNestedBlockObject struct { 36 | Attributes schema.GeneratorAttributes 37 | Blocks schema.GeneratorBlocks 38 | 39 | AssociatedExternalType *schema.AssocExtType 40 | CustomType *specschema.CustomType 41 | Validators specschema.ObjectValidators 42 | } 43 | 44 | func (g GeneratorNestedBlockObject) Equal(other GeneratorNestedBlockObject) bool { 45 | for k := range g.Attributes { 46 | if _, ok := other.Attributes[k]; !ok { 47 | return false 48 | } 49 | 50 | if !g.Attributes[k].Equal(other.Attributes[k]) { 51 | return false 52 | } 53 | } 54 | 55 | for k := range g.Blocks { 56 | if _, ok := other.Blocks[k]; !ok { 57 | return false 58 | } 59 | 60 | if !g.Blocks[k].Equal(other.Blocks[k]) { 61 | return false 62 | } 63 | } 64 | 65 | if !g.AssociatedExternalType.Equal(other.AssociatedExternalType) { 66 | return false 67 | } 68 | 69 | if !g.CustomType.Equal(other.CustomType) { 70 | return false 71 | } 72 | 73 | return g.Validators.Equal(other.Validators) 74 | } 75 | -------------------------------------------------------------------------------- /internal/resource/nested_attribute_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | "bytes" 8 | 9 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 10 | 11 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/convert" 12 | generatorschema "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 13 | ) 14 | 15 | type NestedAttributeObject struct { 16 | attributes generatorschema.GeneratorAttributes 17 | customType convert.CustomTypeNestedObject 18 | planModifiers convert.PlanModifiers 19 | validators convert.Validators 20 | } 21 | 22 | // NewNestedAttributeObject constructs a NestedAttributeObject which is used to generate a 23 | // nested attribute object in the schema. 24 | func NewNestedAttributeObject(a generatorschema.GeneratorAttributes, c *specschema.CustomType, p convert.PlanModifiers, v convert.Validators, name string) NestedAttributeObject { 25 | return NestedAttributeObject{ 26 | attributes: a, 27 | customType: convert.NewCustomTypeNestedObject(c, name), 28 | planModifiers: p, 29 | validators: v, 30 | } 31 | } 32 | 33 | func (n NestedAttributeObject) Equal(other NestedAttributeObject) bool { 34 | if !n.attributes.Equal(other.attributes) { 35 | return false 36 | } 37 | 38 | if !n.customType.Equal(other.customType) { 39 | return false 40 | } 41 | 42 | if !n.planModifiers.Equal(other.planModifiers) { 43 | return false 44 | } 45 | 46 | return n.validators.Equal(other.validators) 47 | } 48 | 49 | func (n NestedAttributeObject) Imports() *generatorschema.Imports { 50 | imports := generatorschema.NewImports() 51 | 52 | imports.Append(n.customType.Imports()) 53 | 54 | imports.Append(n.planModifiers.Imports()) 55 | 56 | imports.Append(n.validators.Imports()) 57 | 58 | imports.Append(n.attributes.Imports()) 59 | 60 | return imports 61 | } 62 | 63 | func (n NestedAttributeObject) Schema() ([]byte, error) { 64 | var b bytes.Buffer 65 | 66 | attributesSchema, err := n.attributes.Schema() 67 | 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | b.WriteString("NestedObject: schema.NestedAttributeObject{\n") 73 | b.WriteString("Attributes: map[string]schema.Attribute{") 74 | b.WriteString(attributesSchema) 75 | b.WriteString("\n},\n") 76 | b.Write(n.customType.Schema()) 77 | b.Write(n.planModifiers.Schema()) 78 | b.Write(n.validators.Schema()) 79 | b.WriteString("},\n") 80 | 81 | return b.Bytes(), nil 82 | } 83 | -------------------------------------------------------------------------------- /internal/resource/testdata/model.txt: -------------------------------------------------------------------------------- 1 | 2 | type ExampleModel struct { 3 | BoolAttribute types.Bool `tfsdk:"bool_attribute"` 4 | BoolAttributeCustom my_bool_value_type `tfsdk:"bool_attribute_custom"` 5 | Float64Attribute types.Float64 `tfsdk:"float64_attribute"` 6 | Float64AttributeCustom my_float64_value_type `tfsdk:"float64_attribute_custom"` 7 | Int64Attribute types.Int64 `tfsdk:"int64_attribute"` 8 | Int64AttributeCustom my_int64_value_type `tfsdk:"int64_attribute_custom"` 9 | ListAttribute types.List `tfsdk:"list_attribute"` 10 | ListAttributeCustom my_list_value_type `tfsdk:"list_attribute_custom"` 11 | ListNestedAttribute types.List `tfsdk:"list_nested_attribute"` 12 | ListNestedAttributeCustom my_list_nested_value_type `tfsdk:"list_nested_attribute_custom"` 13 | MapAttribute types.Map `tfsdk:"map_attribute"` 14 | MapAttributeCustom my_map_value_type `tfsdk:"map_attribute_custom"` 15 | MapNestedAttribute types.Map `tfsdk:"map_nested_attribute"` 16 | MapNestedAttributeCustom my_map_nested_value_type `tfsdk:"map_nested_attribute_custom"` 17 | NumberAttribute types.Number `tfsdk:"number_attribute"` 18 | NumberAttributeCustom my_number_value_type `tfsdk:"number_attribute_custom"` 19 | ObjectAttribute types.Object `tfsdk:"object_attribute"` 20 | ObjectAttributeCustom my_object_value_type `tfsdk:"object_attribute_custom"` 21 | SetAttribute types.Set `tfsdk:"set_attribute"` 22 | SetAttributeCustom my_set_value_type `tfsdk:"set_attribute_custom"` 23 | SetNestedAttribute types.Set `tfsdk:"set_nested_attribute"` 24 | SetNestedAttributeCustom my_set_nested_value_type `tfsdk:"set_nested_attribute_custom"` 25 | SingleNestedAttribute SingleNestedAttributeValue `tfsdk:"single_nested_attribute"` 26 | SingleNestedAttributeCustom my_single_nested_value_type `tfsdk:"single_nested_attribute_custom"` 27 | StringAttribute types.String `tfsdk:"string_attribute"` 28 | StringAttributeCustom my_string_value_type `tfsdk:"string_attribute_custom"` 29 | ListNestedBlock types.List `tfsdk:"list_nested_block"` 30 | ListNestedBlockCustom my_list_nested_value_type `tfsdk:"list_nested_block_custom"` 31 | SetNestedBlock types.Set `tfsdk:"set_nested_block"` 32 | SetNestedBlockCustom my_set_nested_value_type `tfsdk:"set_nested_block_custom"` 33 | SingleNestedBlock SingleNestedBlockValue `tfsdk:"single_nested_block"` 34 | SingleNestedBlockCustom my_single_nested_value_type `tfsdk:"single_nested_block_custom"` 35 | } 36 | -------------------------------------------------------------------------------- /internal/resource/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package resource 5 | 6 | import ( 7 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 10 | ) 11 | 12 | type GeneratorNestedAttributeObject struct { 13 | Attributes schema.GeneratorAttributes 14 | 15 | AssociatedExternalType *schema.AssocExtType 16 | CustomType *specschema.CustomType 17 | PlanModifiers specschema.ObjectPlanModifiers 18 | Validators specschema.ObjectValidators 19 | } 20 | 21 | func (g GeneratorNestedAttributeObject) Equal(other GeneratorNestedAttributeObject) bool { 22 | for k := range g.Attributes { 23 | if _, ok := other.Attributes[k]; !ok { 24 | return false 25 | } 26 | 27 | if !g.Attributes[k].Equal(other.Attributes[k]) { 28 | return false 29 | } 30 | } 31 | 32 | if !g.AssociatedExternalType.Equal(other.AssociatedExternalType) { 33 | return false 34 | } 35 | 36 | if !g.CustomType.Equal(other.CustomType) { 37 | return false 38 | } 39 | 40 | if !g.PlanModifiers.Equal(other.PlanModifiers) { 41 | return false 42 | } 43 | 44 | return g.Validators.Equal(other.Validators) 45 | } 46 | 47 | type GeneratorNestedBlockObject struct { 48 | Attributes schema.GeneratorAttributes 49 | Blocks schema.GeneratorBlocks 50 | 51 | AssociatedExternalType *schema.AssocExtType 52 | CustomType *specschema.CustomType 53 | PlanModifiers specschema.ObjectPlanModifiers 54 | Validators specschema.ObjectValidators 55 | } 56 | 57 | func (g GeneratorNestedBlockObject) Equal(other GeneratorNestedBlockObject) bool { 58 | for k := range g.Attributes { 59 | if _, ok := other.Attributes[k]; !ok { 60 | return false 61 | } 62 | 63 | if !g.Attributes[k].Equal(other.Attributes[k]) { 64 | return false 65 | } 66 | } 67 | 68 | for k := range g.Blocks { 69 | if _, ok := other.Blocks[k]; !ok { 70 | return false 71 | } 72 | 73 | if !g.Blocks[k].Equal(other.Blocks[k]) { 74 | return false 75 | } 76 | } 77 | 78 | if !g.AssociatedExternalType.Equal(other.AssociatedExternalType) { 79 | return false 80 | } 81 | 82 | if !g.CustomType.Equal(other.CustomType) { 83 | return false 84 | } 85 | 86 | if !g.PlanModifiers.Equal(other.PlanModifiers) { 87 | return false 88 | } 89 | 90 | return g.Validators.Equal(other.Validators) 91 | } 92 | -------------------------------------------------------------------------------- /internal/scaffold/data_source.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package scaffold 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 11 | ) 12 | 13 | // DataSourceBytes will create scaffolding Go code bytes for a Terraform Plugin Framework data source 14 | func DataSourceBytes(dataSourceIdentifier schema.FrameworkIdentifier, packageName string) ([]byte, error) { 15 | t, err := template.New("data_source_scaffold").Parse(dataSourceScaffoldGoTemplate) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | var buf bytes.Buffer 21 | 22 | templateData := struct { 23 | PackageName string 24 | NameSnake string 25 | NameCamel string 26 | NamePascal string 27 | }{ 28 | PackageName: packageName, 29 | NameSnake: string(dataSourceIdentifier), 30 | NameCamel: dataSourceIdentifier.ToCamelCase(), 31 | NamePascal: dataSourceIdentifier.ToPascalCase(), 32 | } 33 | 34 | err = t.Execute(&buf, templateData) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return buf.Bytes(), nil 40 | } 41 | -------------------------------------------------------------------------------- /internal/scaffold/embed.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package scaffold 5 | 6 | import ( 7 | _ "embed" 8 | ) 9 | 10 | //go:embed templates/resource_scaffold.gotmpl 11 | var resourceScaffoldGoTemplate string 12 | 13 | //go:embed templates/data_source_scaffold.gotmpl 14 | var dataSourceScaffoldGoTemplate string 15 | 16 | //go:embed templates/provider_scaffold.gotmpl 17 | var providerScaffoldGoTemplate string 18 | -------------------------------------------------------------------------------- /internal/scaffold/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package scaffold 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 11 | ) 12 | 13 | // ProviderBytes will create scaffolding Go code bytes for a Terraform Plugin Framework provider 14 | func ProviderBytes(providerIdentifier schema.FrameworkIdentifier, packageName string) ([]byte, error) { 15 | t, err := template.New("provider_scaffold").Parse(providerScaffoldGoTemplate) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | var buf bytes.Buffer 21 | 22 | templateData := struct { 23 | PackageName string 24 | NameSnake string 25 | NameCamel string 26 | }{ 27 | PackageName: packageName, 28 | NameSnake: string(providerIdentifier), 29 | NameCamel: providerIdentifier.ToCamelCase(), 30 | } 31 | 32 | err = t.Execute(&buf, templateData) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return buf.Bytes(), nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/scaffold/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package scaffold 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | 10 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/schema" 11 | ) 12 | 13 | // ResourceBytes will create scaffolding Go code bytes for a Terraform Plugin Framework resource 14 | func ResourceBytes(resourceIdentifier schema.FrameworkIdentifier, packageName string) ([]byte, error) { 15 | t, err := template.New("resource_scaffold").Parse(resourceScaffoldGoTemplate) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | var buf bytes.Buffer 21 | 22 | templateData := struct { 23 | PackageName string 24 | NameSnake string 25 | NameCamel string 26 | NamePascal string 27 | }{ 28 | PackageName: packageName, 29 | NameSnake: string(resourceIdentifier), 30 | NameCamel: resourceIdentifier.ToCamelCase(), 31 | NamePascal: resourceIdentifier.ToPascalCase(), 32 | } 33 | 34 | err = t.Execute(&buf, templateData) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return buf.Bytes(), nil 40 | } 41 | -------------------------------------------------------------------------------- /internal/scaffold/templates/data_source_scaffold.gotmpl: -------------------------------------------------------------------------------- 1 | package {{.PackageName}} 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 | ) 10 | 11 | var _ datasource.DataSource = (*{{.NameCamel}}DataSource)(nil) 12 | 13 | func New{{.NamePascal}}DataSource() datasource.DataSource { 14 | return &{{.NameCamel}}DataSource{} 15 | } 16 | 17 | type {{.NameCamel}}DataSource struct{} 18 | 19 | type {{.NameCamel}}DataSourceModel struct { 20 | Id types.String `tfsdk:"id"` 21 | } 22 | 23 | func (d *{{.NameCamel}}DataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 24 | resp.TypeName = req.ProviderTypeName + "_{{.NameSnake}}" 25 | } 26 | 27 | func (d *{{.NameCamel}}DataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 28 | resp.Schema = schema.Schema{ 29 | Attributes: map[string]schema.Attribute{ 30 | "id": schema.StringAttribute{ 31 | Computed: true, 32 | }, 33 | }, 34 | } 35 | } 36 | 37 | func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 38 | var data {{.NameCamel}}DataSourceModel 39 | 40 | // Read Terraform configuration data into the model 41 | resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) 42 | 43 | if resp.Diagnostics.HasError() { 44 | return 45 | } 46 | 47 | // Read API call logic 48 | 49 | // Example data value setting 50 | data.Id = types.StringValue("example-id") 51 | 52 | // Save data into Terraform state 53 | resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) 54 | } 55 | -------------------------------------------------------------------------------- /internal/scaffold/templates/provider_scaffold.gotmpl: -------------------------------------------------------------------------------- 1 | package {{.PackageName}} 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/datasource" 7 | "github.com/hashicorp/terraform-plugin-framework/provider" 8 | "github.com/hashicorp/terraform-plugin-framework/resource" 9 | ) 10 | 11 | var _ provider.Provider = (*{{.NameCamel}}Provider)(nil) 12 | 13 | func New() func() provider.Provider { 14 | return func() provider.Provider { 15 | return &{{.NameCamel}}Provider{} 16 | } 17 | } 18 | 19 | type {{.NameCamel}}Provider struct{} 20 | 21 | func (p *{{.NameCamel}}Provider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { 22 | 23 | } 24 | 25 | func (p *{{.NameCamel}}Provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { 26 | 27 | } 28 | 29 | func (p *{{.NameCamel}}Provider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { 30 | resp.TypeName = "{{.NameSnake}}" 31 | } 32 | 33 | func (p *{{.NameCamel}}Provider) DataSources(ctx context.Context) []func() datasource.DataSource { 34 | return []func() datasource.DataSource{} 35 | } 36 | 37 | func (p *{{.NameCamel}}Provider) Resources(ctx context.Context) []func() resource.Resource { 38 | return []func() resource.Resource{} 39 | } 40 | -------------------------------------------------------------------------------- /internal/schema/associated_external_type.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "strings" 8 | "unicode" 9 | "unicode/utf8" 10 | 11 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 12 | "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 13 | ) 14 | 15 | type AssocExtType struct { 16 | *schema.AssociatedExternalType 17 | } 18 | 19 | func NewAssocExtType(assocExtType *schema.AssociatedExternalType) *AssocExtType { 20 | if assocExtType == nil { 21 | return nil 22 | } 23 | 24 | return &AssocExtType{ 25 | AssociatedExternalType: assocExtType, 26 | } 27 | } 28 | 29 | func (a *AssocExtType) Imports() *Imports { 30 | imports := NewImports() 31 | 32 | if a == nil { 33 | return imports 34 | } 35 | 36 | if a.AssociatedExternalType.Import == nil { 37 | return imports 38 | } 39 | 40 | if len(a.AssociatedExternalType.Import.Path) > 0 { 41 | imports.Add(*a.AssociatedExternalType.Import) 42 | 43 | imports.Add(code.Import{ 44 | Path: BaseTypesImport, 45 | }) 46 | } 47 | 48 | return imports 49 | } 50 | 51 | func (a *AssocExtType) Type() string { 52 | if a == nil { 53 | return "" 54 | } 55 | 56 | return a.AssociatedExternalType.Type 57 | } 58 | 59 | func (a *AssocExtType) TypeReference() string { 60 | if a == nil { 61 | return "" 62 | } 63 | 64 | tr, _ := strings.CutPrefix(a.AssociatedExternalType.Type, "*") 65 | 66 | return tr 67 | } 68 | 69 | func (a *AssocExtType) Equal(other *AssocExtType) bool { 70 | if a == nil && other == nil { 71 | return true 72 | } 73 | 74 | if a == nil || other == nil { 75 | return false 76 | } 77 | 78 | return a.AssociatedExternalType.Equal(other.AssociatedExternalType) 79 | } 80 | 81 | func (a *AssocExtType) ToPascalCase() string { 82 | inputSplit := strings.Split(a.TypeReference(), ".") 83 | 84 | var ucName string 85 | 86 | for _, v := range inputSplit { 87 | if len(v) < 1 { 88 | continue 89 | } 90 | 91 | firstChar := v[0:1] 92 | ucFirstChar := strings.ToUpper(firstChar) 93 | 94 | if len(v) < 2 { 95 | ucName += ucFirstChar 96 | continue 97 | } 98 | 99 | ucName += ucFirstChar + v[1:] 100 | } 101 | 102 | return ucName 103 | } 104 | 105 | func (a *AssocExtType) ToCamelCase() string { 106 | pascal := a.ToPascalCase() 107 | 108 | // Grab first rune and lower case it 109 | firstLetter, size := utf8.DecodeRuneInString(pascal) 110 | if firstLetter == utf8.RuneError && size <= 1 { 111 | return pascal 112 | } 113 | 114 | return string(unicode.ToLower(firstLetter)) + pascal[size:] 115 | } 116 | -------------------------------------------------------------------------------- /internal/schema/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import "strings" 7 | 8 | // UnimplementedError is used to indicate that the operation 9 | // being performed is not yet implemented. It is primarily used 10 | // to permit execution of code generation to continue whilst 11 | // logging any unimplemented operations. 12 | type UnimplementedError struct { 13 | err error 14 | path []string 15 | } 16 | 17 | // Error returns the underlying error string. 18 | func (e *UnimplementedError) Error() string { 19 | return e.err.Error() 20 | } 21 | 22 | // Path returns a dot-separated path. 23 | func (e *UnimplementedError) Path() string { 24 | return strings.Join(e.path, ".") 25 | } 26 | 27 | // NewUnimplementedError returns an UnimplementedError populated with the 28 | // supplied error and path. 29 | func NewUnimplementedError(err error, path ...string) *UnimplementedError { 30 | return &UnimplementedError{ 31 | err: err, 32 | path: path, 33 | } 34 | } 35 | 36 | // NestedUnimplementedError returns an UnimplementedError with a path that includes 37 | // the supplied parentPath. 38 | func (e *UnimplementedError) NestedUnimplementedError(parentPath string) *UnimplementedError { 39 | newErr := &UnimplementedError{ 40 | err: e.err, 41 | path: append([]string{parentPath}, e.path...), 42 | } 43 | 44 | return newErr 45 | } 46 | -------------------------------------------------------------------------------- /internal/schema/import.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-codegen-spec/code" 8 | ) 9 | 10 | const ( 11 | AttrImport = "github.com/hashicorp/terraform-plugin-framework/attr" 12 | BaseTypesImport = "github.com/hashicorp/terraform-plugin-framework/types/basetypes" 13 | ContextImport = "context" 14 | DiagImport = "github.com/hashicorp/terraform-plugin-framework/diag" 15 | FmtImport = "fmt" 16 | MathBigImport = "math/big" 17 | PlanModifierImport = "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 18 | StringsImport = "strings" 19 | TfTypesImport = "github.com/hashicorp/terraform-plugin-go/tftypes" 20 | TypesImport = "github.com/hashicorp/terraform-plugin-framework/types" 21 | ValidatorImport = "github.com/hashicorp/terraform-plugin-framework/schema/validator" 22 | ) 23 | 24 | type Imports struct { 25 | imports []code.Import 26 | paths map[string]struct{} 27 | } 28 | 29 | func NewImports() *Imports { 30 | return &Imports{ 31 | imports: []code.Import{}, 32 | paths: make(map[string]struct{}), 33 | } 34 | } 35 | 36 | func (i *Imports) Add(c ...code.Import) { 37 | for _, imp := range c { 38 | if _, ok := i.paths[imp.Path]; ok { 39 | continue 40 | } 41 | 42 | i.imports = append(i.imports, imp) 43 | i.paths[imp.Path] = struct{}{} 44 | } 45 | } 46 | 47 | func (i *Imports) All() []code.Import { 48 | return i.imports 49 | } 50 | 51 | func (i *Imports) Append(imps ...*Imports) { 52 | for _, imp := range imps { 53 | for _, c := range imp.imports { 54 | if _, ok := i.paths[c.Path]; ok { 55 | continue 56 | } 57 | 58 | i.imports = append(i.imports, c) 59 | i.paths[c.Path] = struct{}{} 60 | } 61 | } 62 | } 63 | 64 | func AttrImports() *Imports { 65 | imports := NewImports() 66 | 67 | imports.Add(code.Import{ 68 | Path: AttrImport, 69 | }) 70 | 71 | return imports 72 | } 73 | 74 | func AssociatedExternalTypeImports() *Imports { 75 | imports := NewImports() 76 | 77 | imports.Add([]code.Import{ 78 | { 79 | Path: FmtImport, 80 | }, 81 | { 82 | Path: DiagImport, 83 | }, 84 | { 85 | Path: AttrImport, 86 | }, 87 | { 88 | Path: TfTypesImport, 89 | }, 90 | { 91 | Path: BaseTypesImport, 92 | }, 93 | }...) 94 | 95 | return imports 96 | } 97 | -------------------------------------------------------------------------------- /internal/schema/schemas.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "fmt" 10 | "log/slog" 11 | "strings" 12 | 13 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/logging" 14 | ) 15 | 16 | // TODO: Field(s) could be added to handle end-user supplying their own templates to allow overriding. 17 | type GeneratorSchemas struct { 18 | schemas map[string]GeneratorSchema 19 | } 20 | 21 | func NewGeneratorSchemas(schemas map[string]GeneratorSchema) GeneratorSchemas { 22 | return GeneratorSchemas{ 23 | schemas: schemas, 24 | } 25 | } 26 | 27 | func (g GeneratorSchemas) Schemas(packageName, generatorType string) (map[string][]byte, error) { 28 | schemasBytes := make(map[string][]byte, len(g.schemas)) 29 | 30 | for k, s := range g.schemas { 31 | 32 | pkgName := packageName 33 | if pkgName == "" { 34 | pkgName = fmt.Sprintf("%s_%s", strings.ToLower(generatorType), k) 35 | } 36 | 37 | b, err := s.Schema(k, pkgName, generatorType) 38 | 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | schemasBytes[k] = b 44 | } 45 | 46 | return schemasBytes, nil 47 | } 48 | 49 | func (g GeneratorSchemas) Models() (map[string][]byte, error) { 50 | modelsBytes := make(map[string][]byte, len(g.schemas)) 51 | 52 | for name, schema := range g.schemas { 53 | var buf bytes.Buffer 54 | 55 | generatorSchema := GeneratorSchema{ 56 | Attributes: schema.Attributes, 57 | Blocks: schema.Blocks, 58 | Description: schema.Description, 59 | MarkdownDescription: schema.MarkdownDescription, 60 | DeprecationMessage: schema.DeprecationMessage, 61 | } 62 | 63 | models, err := generatorSchema.Models(name) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | for _, m := range models { 69 | buf.WriteString("\n" + m.String() + "\n") 70 | } 71 | 72 | modelsBytes[name] = buf.Bytes() 73 | } 74 | 75 | return modelsBytes, nil 76 | } 77 | 78 | func (g GeneratorSchemas) CustomTypeValue() (map[string][]byte, error) { 79 | customTypeValueBytes := make(map[string][]byte, len(g.schemas)) 80 | 81 | for name, s := range g.schemas { 82 | b, err := s.CustomTypeValueBytes() 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | customTypeValueBytes[name] = b 88 | } 89 | 90 | return customTypeValueBytes, nil 91 | } 92 | 93 | func (g GeneratorSchemas) ToFromFunctions(ctx context.Context, logger *slog.Logger) (map[string][]byte, error) { 94 | modelsExpandFlattenBytes := make(map[string][]byte, len(g.schemas)) 95 | 96 | for name, s := range g.schemas { 97 | ctxWithPath := logging.SetPathInContext(ctx, name) 98 | 99 | b, err := s.ToFromFunctions(ctxWithPath, logger) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | modelsExpandFlattenBytes[name] = b 105 | } 106 | 107 | return modelsExpandFlattenBytes, nil 108 | } 109 | -------------------------------------------------------------------------------- /internal/schema/templates/bool_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.BoolNull(), 8 | }, diags 9 | } 10 | 11 | return {{.Name}}Value{ 12 | types.BoolPointerValue(*apiObject), 13 | }, diags 14 | } 15 | -------------------------------------------------------------------------------- /internal/schema/templates/bool_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | a := {{.AssocExtType.TypeReference}}(v.ValueBoolPointer()) 18 | 19 | return &a, diags 20 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.BoolType.Equal(other.BoolType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.BoolTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.BoolType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_value_from_bool.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromBool(ctx context.Context, in basetypes.BoolValue) (basetypes.BoolValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | BoolValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.BoolType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | boolValue, ok := attrValue.(basetypes.BoolValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | boolValuable, diags := t.ValueFromBool(ctx, boolValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting BoolValue to BoolValuable: %v", diags) 19 | } 20 | 21 | return boolValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.BoolValue.Equal(other.BoolValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | } 5 | } -------------------------------------------------------------------------------- /internal/schema/templates/bool_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.BoolValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/bool_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.BoolValue 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.Float64Null(), 8 | }, diags 9 | } 10 | 11 | return {{.Name}}Value{ 12 | types.Float64PointerValue(*apiObject), 13 | }, diags 14 | } 15 | -------------------------------------------------------------------------------- /internal/schema/templates/float64_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | a := {{.AssocExtType.TypeReference}}(v.ValueFloat64Pointer()) 18 | 19 | return &a, diags 20 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.Float64Type.Equal(other.Float64Type) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.Float64Typable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.Float64Type 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_value_from_float64.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromFloat64(ctx context.Context, in basetypes.Float64Value) (basetypes.Float64Valuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | Float64Value: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.Float64Type.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | boolValue, ok := attrValue.(basetypes.Float64Value) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | boolValuable, diags := t.ValueFromFloat64(ctx, boolValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting Float64Value to Float64Valuable: %v", diags) 19 | } 20 | 21 | return boolValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.Float64Value.Equal(other.Float64Value) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | } 5 | } -------------------------------------------------------------------------------- /internal/schema/templates/float64_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.Float64Valuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/float64_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.Float64Value 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.Int64Null(), 8 | }, diags 9 | } 10 | 11 | return {{.Name}}Value{ 12 | types.Int64PointerValue(*apiObject), 13 | }, diags 14 | } 15 | -------------------------------------------------------------------------------- /internal/schema/templates/int64_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | a := {{.AssocExtType.TypeReference}}(v.ValueInt64Pointer()) 18 | 19 | return &a, diags 20 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.Int64Type.Equal(other.Int64Type) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.Int64Typable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.Int64Type 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_value_from_int64.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromInt64(ctx context.Context, in basetypes.Int64Value) (basetypes.Int64Valuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | Int64Value: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.Int64Type.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | boolValue, ok := attrValue.(basetypes.Int64Value) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | boolValuable, diags := t.ValueFromInt64(ctx, boolValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting Int64Value to Int64Valuable: %v", diags) 19 | } 20 | 21 | return boolValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.Int64Value.Equal(other.Int64Value) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | } 5 | } -------------------------------------------------------------------------------- /internal/schema/templates/int64_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.Int64Valuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/int64_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.Int64Value 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.ListNull({{.ElementTypeType}}), 8 | }, diags 9 | } 10 | 11 | var elems []{{.ElementTypeValue}} 12 | 13 | for _, e := range *apiObject { 14 | elems = append(elems, {{.ElementFrom}}(e)) 15 | } 16 | 17 | l, d := basetypes.NewListValueFrom(ctx, {{.ElementTypeType}}, elems) 18 | 19 | diags.Append(d...) 20 | 21 | if diags.HasError() { 22 | return {{.Name}}Value{ 23 | types.ListUnknown({{.ElementTypeType}}), 24 | }, diags 25 | } 26 | 27 | return {{.Name}}Value{ 28 | l, 29 | }, diags 30 | } 31 | -------------------------------------------------------------------------------- /internal/schema/templates/list_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} 18 | 19 | d := v.ElementsAs(ctx, &{{.AssocExtType.ToCamelCase}}, false) 20 | 21 | diags.Append(d...) 22 | 23 | if diags.HasError() { 24 | return nil, diags 25 | } 26 | 27 | return &{{.AssocExtType.ToCamelCase}}, diags 28 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.ListType.Equal(other.ListType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.ListTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/list_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.ListType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_type_value_from_list.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromList(ctx context.Context, in basetypes.ListValue) (basetypes.ListValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | ListValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.ListType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | listValue, ok := attrValue.(basetypes.ListValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | listValuable, diags := t.ValueFromList(ctx, listValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting ListValue to ListValuable: %v", diags) 19 | } 20 | 21 | return listValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.ListValue.Equal(other.ListValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | ListType: basetypes.ListType{ 5 | ElemType: {{.ElementType}}, 6 | }, 7 | } 8 | } -------------------------------------------------------------------------------- /internal/schema/templates/list_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.ListValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/list_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.ListValue 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.MapNull({{.ElementTypeType}}), 8 | }, diags 9 | } 10 | 11 | elems := make(map[string]{{.ElementTypeValue}}) 12 | 13 | for k, e := range *apiObject { 14 | elems[k] = {{.ElementFrom}}(e) 15 | } 16 | 17 | l, d := basetypes.NewMapValueFrom(ctx, {{.ElementTypeType}}, elems) 18 | 19 | diags.Append(d...) 20 | 21 | if diags.HasError() { 22 | return {{.Name}}Value{ 23 | types.MapUnknown({{.ElementTypeType}}), 24 | }, diags 25 | } 26 | 27 | return {{.Name}}Value{ 28 | l, 29 | }, diags 30 | } 31 | -------------------------------------------------------------------------------- /internal/schema/templates/map_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} 18 | 19 | d := v.ElementsAs(ctx, &{{.AssocExtType.ToCamelCase}}, false) 20 | 21 | diags.Append(d...) 22 | 23 | if diags.HasError() { 24 | return nil, diags 25 | } 26 | 27 | return &{{.AssocExtType.ToCamelCase}}, diags 28 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.MapType.Equal(other.MapType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.MapTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/map_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.MapType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_type_value_from_map.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromMap(ctx context.Context, in basetypes.MapValue) (basetypes.MapValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | MapValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.MapType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | mapValue, ok := attrValue.(basetypes.MapValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | mapValuable, diags := t.ValueFromMap(ctx, mapValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting MapValue to MapValuable: %v", diags) 19 | } 20 | 21 | return mapValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.MapValue.Equal(other.MapValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | MapType: basetypes.MapType{ 5 | ElemType: {{.ElementType}}, 6 | }, 7 | } 8 | } -------------------------------------------------------------------------------- /internal/schema/templates/map_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.MapValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/map_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.MapValue 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return New{{.Name}}ValueNull(), diags 7 | } 8 | {{- range $key, $value := .FromFuncs}} 9 | {{- if $value.AssocExtType}} 10 | 11 | {{$key.ToCamelCase}}Val, d := {{$key.ToPascalCase}}Value{}.From{{$value.AssocExtType.ToPascalCase}}(ctx, apiObject.{{$key.ToPascalCase}}) 12 | 13 | diags.Append(d...) 14 | 15 | if diags.HasError() { 16 | return New{{$.Name}}ValueUnknown(), diags 17 | } 18 | {{- else if $value.CollectionType.ElementType}} 19 | 20 | {{$key.ToCamelCase}}Val, d := {{$value.CollectionType.TypeValueFrom}}(ctx, {{$value.CollectionType.ElementType}}, apiObject.{{$key.ToPascalCase}}) 21 | 22 | diags.Append(d...) 23 | 24 | if diags.HasError() { 25 | return New{{$.Name}}ValueUnknown(), diags 26 | } 27 | {{- else if $value.ObjectType}} 28 | 29 | {{$key.ToCamelCase}}Val, d := basetypes.NewObjectValue( 30 | map[string]attr.Type{ 31 | {{- range $objectTypeKey, $objectTypeVal := $value.ObjectType}} 32 | "{{$objectTypeKey}}": {{$objectTypeVal.Type}}, 33 | {{- end}} 34 | }, map[string]attr.Value{ 35 | {{- range $objectTypeKey, $objectTypeVal := $value.ObjectType}} 36 | "{{$objectTypeKey}}": types.{{$objectTypeVal.FromFunc}}(apiObject.{{$key.ToPascalCase}}.{{$objectTypeKey.ToPascalCase}}), 37 | {{- end}} 38 | }) 39 | 40 | diags.Append(d...) 41 | 42 | if diags.HasError() { 43 | return New{{$.Name}}ValueUnknown(), diags 44 | } 45 | {{- end}} 46 | {{- end}} 47 | 48 | return {{.Name}}Value{ 49 | {{- range $key, $value := .FromFuncs}} 50 | {{- if $value.AssocExtType}} 51 | {{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, 52 | {{- else if $value.Default}} 53 | {{$key.ToPrefixPascalCase $.Name}}: types.{{$value.Default}}(apiObject.{{$key.ToPascalCase}}), 54 | {{- else if $value.CollectionType.ElementType}} 55 | {{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, 56 | {{- else if $value.ObjectType}} 57 | {{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, 58 | {{- end}} 59 | {{- end}} 60 | state: attr.ValueStateKnown, 61 | }, diags 62 | } 63 | -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | {{- range $key, $value := .ToFuncs}} 18 | {{- if $value.AssocExtType}} 19 | 20 | {{$value.AssocExtType.ToCamelCase}}, d := v.{{$key.ToPrefixPascalCase $.Name}}.To{{$value.AssocExtType.ToPascalCase}}(ctx) 21 | 22 | diags.Append(d...) 23 | 24 | if diags.HasError() { 25 | return nil, diags 26 | } 27 | {{- else if $value.CollectionType.GoType}} 28 | 29 | var {{$key.ToCamelCase}}Field {{$value.CollectionType.GoType}} 30 | 31 | d := v.{{$key.ToPrefixPascalCase $.Name}}.ElementsAs(ctx, &{{$key.ToCamelCase}}Field, false) 32 | 33 | diags.Append(d...) 34 | 35 | if diags.HasError() { 36 | return nil, diags 37 | } 38 | {{- else if $value.ObjectType}} 39 | 40 | attributes := v.{{$key.ToPrefixPascalCase $.Name}}.Attributes() 41 | 42 | {{- range $objectTypeKey, $objectTypeVal := $value.ObjectType}} 43 | 44 | {{$key.ToCamelCase}}Field{{$objectTypeKey.ToPascalCase}}, ok := attributes["{{$objectTypeKey}}"].({{$objectTypeVal.Type}}) 45 | 46 | if !ok { 47 | diags.Append(diag.NewErrorDiagnostic( 48 | "{{$key.ToPrefixPascalCase $.Name}} Field {{$objectTypeKey}} Is Wrong Type", 49 | fmt.Sprintf(`{{$key.ToPrefixPascalCase $.Name}} field {{$objectTypeKey}} expected to be {{$objectTypeVal.Type}}, was: %T`, attributes["bool"]), 50 | )) 51 | 52 | return nil, diags 53 | } 54 | {{- end}} 55 | {{- end}} 56 | {{- end}} 57 | 58 | return &{{.AssocExtType.TypeReference}}{ 59 | {{- range $key, $value := .ToFuncs}} 60 | {{- if $value.AssocExtType}} 61 | {{$key.ToPascalCase}}: {{$value.AssocExtType.ToCamelCase}}, 62 | {{- else if $value.Default}} 63 | {{$key.ToPascalCase}}: v.{{$key.ToPrefixPascalCase $.Name}}.{{$value.Default}}(), 64 | {{- else if $value.CollectionType.GoType}} 65 | {{$key.ToPascalCase}}: {{$key.ToCamelCase}}Field, 66 | {{- else if $value.ObjectType}} 67 | {{$key.ToPascalCase}}: struct { 68 | {{- range $objectTypeKey, $objectTypeVal := $value.ObjectType}} 69 | {{$objectTypeKey.ToPascalCase}} {{$objectTypeVal.GoType}} 70 | {{- end}} 71 | }{ 72 | {{- range $objectTypeKey, $objectTypeVal := $value.ObjectType}} 73 | {{$objectTypeKey.ToPascalCase}}: {{$key.ToCamelCase}}Field{{$objectTypeKey.ToPascalCase}}.{{$objectTypeVal.ToFunc}}(), 74 | {{- end}} 75 | }, 76 | {{- end}} 77 | {{- end}} 78 | }, diags 79 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.ObjectType.Equal(other.ObjectType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.ObjectTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.ObjectType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func New{{.Name}}Value(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 6 | ctx := context.Background() 7 | 8 | for name, attributeType := range attributeTypes { 9 | attribute, ok := attributes[name] 10 | 11 | if !ok { 12 | diags.AddError( 13 | "Missing {{.Name}}Value Attribute Value", 14 | "While creating a {{.Name}}Value value, a missing attribute value was detected. "+ 15 | "A {{.Name}}Value must contain values for all attributes, even if null or unknown. "+ 16 | "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ 17 | fmt.Sprintf("{{.Name}}Value Attribute Name (%s) Expected Type: %s", name, attributeType.String()), 18 | ) 19 | 20 | continue 21 | } 22 | 23 | if !attributeType.Equal(attribute.Type(ctx)) { 24 | diags.AddError( 25 | "Invalid {{.Name}}Value Attribute Type", 26 | "While creating a {{.Name}}Value value, an invalid attribute value was detected. "+ 27 | "A {{.Name}}Value must use a matching attribute type for the value. "+ 28 | "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ 29 | fmt.Sprintf("{{.Name}}Value Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ 30 | fmt.Sprintf("{{.Name}}Value Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), 31 | ) 32 | } 33 | } 34 | 35 | for name := range attributes { 36 | _, ok := attributeTypes[name] 37 | 38 | if !ok { 39 | diags.AddError( 40 | "Extra {{.Name}}Value Attribute Value", 41 | "While creating a {{.Name}}Value value, an extra attribute value was detected. "+ 42 | "A {{.Name}}Value must not contain values beyond the expected attribute types. "+ 43 | "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ 44 | fmt.Sprintf("Extra {{.Name}}Value Attribute Name: %s", name), 45 | ) 46 | } 47 | } 48 | 49 | if diags.HasError() { 50 | return New{{.Name}}ValueUnknown(), diags 51 | } 52 | 53 | {{range $key, $value := .AttrValues }} 54 | {{$key.ToCamelCase}}Attribute, ok := attributes["{{$key}}"] 55 | 56 | if !ok { 57 | diags.AddError( 58 | "Attribute Missing", 59 | `{{$key}} is missing from object`) 60 | 61 | return New{{$.Name}}ValueUnknown(), diags 62 | } 63 | 64 | {{$key.ToCamelCase}}Val, ok := {{$key.ToCamelCase}}Attribute.({{$value}}) 65 | 66 | if !ok { 67 | diags.AddError( 68 | "Attribute Wrong Type", 69 | fmt.Sprintf(`{{$key}} expected to be {{$value}}, was: %T`, {{$key.ToCamelCase}}Attribute)) 70 | } 71 | {{end}} 72 | 73 | if diags.HasError() { 74 | return New{{.Name}}ValueUnknown(), diags 75 | } 76 | 77 | return {{.Name}}Value{ 78 | {{- range $key, $value := .AttrValues }} 79 | {{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, 80 | {{- end}} 81 | state: attr.ValueStateKnown, 82 | }, diags 83 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value_from_object.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | {{- if .AttrValues}} 6 | 7 | attributes := in.Attributes() 8 | {{- end}} 9 | 10 | {{range $key, $value := .AttrValues }} 11 | {{$key.ToCamelCase}}Attribute, ok := attributes["{{$key}}"] 12 | 13 | if !ok { 14 | diags.AddError( 15 | "Attribute Missing", 16 | `{{$key}} is missing from object`) 17 | 18 | return nil, diags 19 | } 20 | 21 | {{$key.ToCamelCase}}Val, ok := {{$key.ToCamelCase}}Attribute.({{$value}}) 22 | 23 | if !ok { 24 | diags.AddError( 25 | "Attribute Wrong Type", 26 | fmt.Sprintf(`{{$key}} expected to be {{$value}}, was: %T`, {{$key.ToCamelCase}}Attribute)) 27 | } 28 | {{end}} 29 | 30 | if diags.HasError() { 31 | return nil, diags 32 | } 33 | 34 | return {{.Name}}Value{ 35 | {{- range $key, $value := .AttrValues }} 36 | {{$key.ToPrefixPascalCase $.Name}}: {{$key.ToCamelCase}}Val, 37 | {{- end}} 38 | state: attr.ValueStateKnown, 39 | }, diags 40 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | if in.Type() == nil { 4 | return New{{.Name}}ValueNull(), nil 5 | } 6 | 7 | if !in.Type().Equal(t.TerraformType(ctx)) { 8 | return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) 9 | } 10 | 11 | if !in.IsKnown() { 12 | return New{{.Name}}ValueUnknown(), nil 13 | } 14 | 15 | if in.IsNull() { 16 | return New{{.Name}}ValueNull(), nil 17 | } 18 | 19 | attributes := map[string]attr.Value{} 20 | 21 | val := map[string]tftypes.Value{} 22 | 23 | err := in.As(&val) 24 | 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | for k, v := range val { 30 | a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) 31 | 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | attributes[k] = a 37 | } 38 | 39 | return New{{.Name}}ValueMust({{.Name}}Value{}.AttributeTypes(ctx), attributes), nil 40 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value_must.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func New{{.Name}}ValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) {{.Name}}Value { 3 | object, diags := New{{.Name}}Value(attributeTypes, attributes) 4 | 5 | if diags.HasError() { 6 | // This could potentially be added to the diag package. 7 | diagsStrings := make([]string, 0, len(diags)) 8 | 9 | for _, diagnostic := range diags { 10 | diagsStrings = append(diagsStrings, fmt.Sprintf( 11 | "%s | %s | %s", 12 | diagnostic.Severity(), 13 | diagnostic.Summary(), 14 | diagnostic.Detail())) 15 | } 16 | 17 | panic("New{{.Name}}ValueMust received error(s): " + strings.Join(diagsStrings, "\n")) 18 | } 19 | 20 | return object 21 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value_null.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func New{{.Name}}ValueNull() {{.Name}}Value { 3 | return {{.Name}}Value{ 4 | state: attr.ValueStateNull, 5 | } 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_type_value_unknown.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func New{{.Name}}ValueUnknown() {{.Name}}Value { 3 | return {{.Name}}Value{ 4 | state: attr.ValueStateUnknown, 5 | } 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_attribute_types.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) AttributeTypes(ctx context.Context) map[string]attr.Type { 3 | return map[string]attr.Type{ 4 | {{- range $key, $value := .AttrTypes }} 5 | "{{$key}}": {{$value}}, 6 | {{- end}} 7 | } 8 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | if v.state != other.state { 10 | return false 11 | } 12 | 13 | if v.state != attr.ValueStateKnown { 14 | return true 15 | } 16 | 17 | {{range $key, $value := .AttrValues }} 18 | if !v.{{$key.ToPrefixPascalCase $.Name}}.Equal(other.{{$key.ToPrefixPascalCase $.Name}}) { 19 | return false 20 | } 21 | {{end}} 22 | 23 | return true 24 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_is_null.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) IsNull() bool { 3 | return v.state == attr.ValueStateNull 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_is_unknown.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) IsUnknown() bool { 3 | return v.state == attr.ValueStateUnknown 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) String() string { 3 | return "{{.Name}}Value" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_to_terraform_value.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { 2 | attrTypes := make(map[string]tftypes.Type, {{len .AttrTypes}}) 3 | 4 | {{- if .AttrTypes}} 5 | 6 | var val tftypes.Value 7 | var err error 8 | {{- end}} 9 | 10 | {{range $key, $value := .AttrTypes }} 11 | attrTypes["{{$key}}"] = {{$value}}.TerraformType(ctx) 12 | {{- end}} 13 | 14 | objectType := tftypes.Object{AttributeTypes: attrTypes} 15 | 16 | switch v.state { 17 | case attr.ValueStateKnown: 18 | vals := make(map[string]tftypes.Value, {{len .AttrTypes}}) 19 | 20 | {{range $key, $value := .AttrTypes }} 21 | val, err = v.{{$key.ToPrefixPascalCase $.Name}}.ToTerraformValue(ctx) 22 | 23 | if err != nil { 24 | return tftypes.NewValue(objectType, tftypes.UnknownValue), err 25 | } 26 | 27 | vals["{{$key}}"] = val 28 | 29 | {{end}} 30 | 31 | if err := tftypes.ValidateValue(objectType, vals); err != nil { 32 | return tftypes.NewValue(objectType, tftypes.UnknownValue), err 33 | } 34 | 35 | return tftypes.NewValue(objectType, vals), nil 36 | case attr.ValueStateNull: 37 | return tftypes.NewValue(objectType, nil), nil 38 | case attr.ValueStateUnknown: 39 | return tftypes.NewValue(objectType, tftypes.UnknownValue), nil 40 | default: 41 | panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) 42 | } 43 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | basetypes.ObjectType{ 5 | AttrTypes: v.AttributeTypes(ctx), 6 | }, 7 | } 8 | } -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.ObjectValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/nested_object_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | {{- range $key, $value := .AttrValues }} 3 | {{$key.ToPrefixPascalCase $.Name}} {{$value}} `tfsdk:"{{$key}}"` 4 | {{- end}} 5 | state attr.ValueState 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.NumberNull(), 8 | }, diags 9 | } 10 | 11 | return {{.Name}}Value{ 12 | types.NumberValue(*apiObject), 13 | }, diags 14 | } 15 | -------------------------------------------------------------------------------- /internal/schema/templates/number_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | a := {{.AssocExtType.TypeReference}}(v.ValueBigFloat()) 18 | 19 | return &a, diags 20 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.NumberType.Equal(other.NumberType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.NumberTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/number_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.NumberType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_type_value_from_number.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromNumber(ctx context.Context, in basetypes.NumberValue) (basetypes.NumberValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | NumberValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.NumberType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | boolValue, ok := attrValue.(basetypes.NumberValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | boolValuable, diags := t.ValueFromNumber(ctx, boolValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting NumberValue to NumberValuable: %v", diags) 19 | } 20 | 21 | return boolValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.NumberValue.Equal(other.NumberValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | } 5 | } -------------------------------------------------------------------------------- /internal/schema/templates/number_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.NumberValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/number_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.NumberValue 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.ObjectNull(v.AttributeTypes(ctx)), 8 | }, diags 9 | } 10 | 11 | o, d := basetypes.NewObjectValue(v.AttributeTypes(ctx), map[string]attr.Value{ 12 | {{- range $key, $value := .AttrTypesFromFuncs}} 13 | "{{$key}}": {{$value}}(apiObject.{{$key.ToPascalCase}}), 14 | {{- end}} 15 | }) 16 | 17 | diags.Append(d...) 18 | 19 | if diags.HasError() { 20 | return {{.Name}}Value{ 21 | types.ObjectUnknown(v.AttributeTypes(ctx)), 22 | }, diags 23 | } 24 | 25 | return {{.Name}}Value{ 26 | o, 27 | }, diags 28 | } 29 | -------------------------------------------------------------------------------- /internal/schema/templates/object_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | attributes := v.Attributes() 18 | 19 | {{- range $key, $value := .AttrTypesToFuncs}} 20 | 21 | {{$key}}Attribute, ok := attributes["{{$key}}"].({{$value.AttrValue}}) 22 | 23 | if !ok { 24 | diags.Append(diag.NewErrorDiagnostic( 25 | "{{$.Name}}Value {{$key}} is unexpected type", 26 | fmt.Sprintf(`"{{$.Name}}Value" {{$key}} is type of %T".`, attributes["{{$key}}"]), 27 | )) 28 | } 29 | {{- end}} 30 | 31 | if diags.HasError() { 32 | return nil, diags 33 | } 34 | 35 | {{.AssocExtType.ToCamelCase}} := {{.AssocExtType.TypeReference}} { 36 | {{- range $key, $value := .AttrTypesToFuncs}} 37 | {{$key.ToPascalCase}}: {{$key}}Attribute.{{$value.ToFunc}}(), 38 | {{- end}} 39 | } 40 | 41 | return &{{.AssocExtType.ToCamelCase}}, diags 42 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.ObjectType.Equal(other.ObjectType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.ObjectTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/object_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.ObjectType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_type_value_from_object.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | ObjectValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.ObjectType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | objectValue, ok := attrValue.(basetypes.ObjectValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | objectValuable, diags := t.ValueFromObject(ctx, objectValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting ObjectValue to ObjectValuable: %v", diags) 19 | } 20 | 21 | return objectValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_value_attribute_types.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) AttributeTypes(ctx context.Context) map[string]attr.Type { 3 | return map[string]attr.Type{ 4 | {{.AttrTypes }} 5 | } 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.ObjectValue.Equal(other.ObjectValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | ObjectType: basetypes.ObjectType{ 5 | AttrTypes: v.AttributeTypes(ctx), 6 | }, 7 | } 8 | } -------------------------------------------------------------------------------- /internal/schema/templates/object_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.ObjectValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/object_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.ObjectValue 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/schema.gotmpl: -------------------------------------------------------------------------------- 1 | // Code generated by terraform-plugin-framework-generator DO NOT EDIT. 2 | 3 | package {{.PackageName}} 4 | 5 | import ( 6 | {{.Imports}} 7 | {{- if eq .GeneratorType "DataSource"}} 8 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 9 | {{- else if eq .GeneratorType "Provider"}} 10 | "github.com/hashicorp/terraform-plugin-framework/provider/schema" 11 | {{- else if eq .GeneratorType "Resource"}} 12 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 13 | {{- end}} 14 | ) 15 | 16 | func {{.Name}}{{.GeneratorType}}Schema(ctx context.Context) schema.Schema { 17 | return schema.Schema{ 18 | {{- if .Attributes}} 19 | Attributes: map[string]schema.Attribute{ 20 | {{- .Attributes}} 21 | }, 22 | {{- end}} 23 | {{- if .Blocks }} 24 | Blocks: map[string]schema.Block{ 25 | {{- .Blocks}} 26 | }, 27 | {{- end}} 28 | {{- if .Description }} 29 | Description: {{printf "%q" .Description}}, 30 | {{- end}} 31 | {{- if .MarkdownDescription }} 32 | MarkdownDescription: {{printf "%q" .MarkdownDescription}}, 33 | {{- end}} 34 | {{- if .DeprecationMessage }} 35 | DeprecationMessage: {{printf "%q" .DeprecationMessage}}, 36 | {{- end}} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/schema/templates/set_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.SetNull({{.ElementTypeType}}), 8 | }, diags 9 | } 10 | 11 | var elems []{{.ElementTypeValue}} 12 | 13 | for _, e := range *apiObject { 14 | elems = append(elems, {{.ElementFrom}}(e)) 15 | } 16 | 17 | l, d := basetypes.NewSetValueFrom(ctx, {{.ElementTypeType}}, elems) 18 | 19 | diags.Append(d...) 20 | 21 | if diags.HasError() { 22 | return {{.Name}}Value{ 23 | types.SetUnknown({{.ElementTypeType}}), 24 | }, diags 25 | } 26 | 27 | return {{.Name}}Value{ 28 | l, 29 | }, diags 30 | } 31 | -------------------------------------------------------------------------------- /internal/schema/templates/set_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | var {{.AssocExtType.ToCamelCase}} {{.AssocExtType.TypeReference}} 18 | 19 | d := v.ElementsAs(ctx, &{{.AssocExtType.ToCamelCase}}, false) 20 | 21 | diags.Append(d...) 22 | 23 | if diags.HasError() { 24 | return nil, diags 25 | } 26 | 27 | return &{{.AssocExtType.ToCamelCase}}, diags 28 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.SetType.Equal(other.SetType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.SetTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/set_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.SetType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_type_value_from_set.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromSet(ctx context.Context, in basetypes.SetValue) (basetypes.SetValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | SetValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.SetType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | listValue, ok := attrValue.(basetypes.SetValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | listValuable, diags := t.ValueFromSet(ctx, listValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting SetValue to SetValuable: %v", diags) 19 | } 20 | 21 | return listValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.SetValue.Equal(other.SetValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | SetType: basetypes.SetType{ 5 | ElemType: {{.ElementType}}, 6 | }, 7 | } 8 | } -------------------------------------------------------------------------------- /internal/schema/templates/set_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.SetValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/set_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.SetValue 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_from.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) From{{.AssocExtType.ToPascalCase}}(ctx context.Context, apiObject {{.AssocExtType.Type}}) ({{.Name}}Value, diag.Diagnostics) { 3 | var diags diag.Diagnostics 4 | 5 | if apiObject == nil { 6 | return {{.Name}}Value{ 7 | types.StringNull(), 8 | }, diags 9 | } 10 | 11 | return {{.Name}}Value{ 12 | types.StringPointerValue(*apiObject), 13 | }, diags 14 | } 15 | -------------------------------------------------------------------------------- /internal/schema/templates/string_to.gotmpl: -------------------------------------------------------------------------------- 1 | func (v {{.Name}}Value) To{{.AssocExtType.ToPascalCase}}(ctx context.Context) ({{.AssocExtType.Type}}, diag.Diagnostics) { 2 | var diags diag.Diagnostics 3 | 4 | if v.IsNull() { 5 | return nil, diags 6 | } 7 | 8 | if v.IsUnknown() { 9 | diags.Append(diag.NewErrorDiagnostic( 10 | "{{.Name}}Value Value Is Unknown", 11 | `"{{.Name}}Value" is unknown.`, 12 | )) 13 | 14 | return nil, diags 15 | } 16 | 17 | a := {{.AssocExtType.TypeReference}}(v.ValueStringPointer()) 18 | 19 | return &a, diags 20 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_type_equal.gotmpl: -------------------------------------------------------------------------------- 1 | func (t {{.Name}}Type) Equal(o attr.Type) bool { 2 | other, ok := o.({{.Name}}Type) 3 | 4 | if !ok { 5 | return false 6 | } 7 | 8 | return t.StringType.Equal(other.StringType) 9 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_type_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) String() string { 3 | return "{{.Name}}Type" 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_type_typable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.StringTypable = {{.Name}}Type{} -------------------------------------------------------------------------------- /internal/schema/templates/string_type_type.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Type struct { 2 | basetypes.StringType 3 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_type_value_from_string.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromString(ctx context.Context, in basetypes.StringValue) (basetypes.StringValuable, diag.Diagnostics) { 3 | return {{.Name}}Value{ 4 | StringValue: in, 5 | }, nil 6 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_type_value_from_terraform.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { 3 | attrValue, err := t.StringType.ValueFromTerraform(ctx, in) 4 | 5 | if err != nil { 6 | return nil, err 7 | } 8 | 9 | boolValue, ok := attrValue.(basetypes.StringValue) 10 | 11 | if !ok { 12 | return nil, fmt.Errorf("unexpected value type of %T", attrValue) 13 | } 14 | 15 | boolValuable, diags := t.ValueFromString(ctx, boolValue) 16 | 17 | if diags.HasError() { 18 | return nil, fmt.Errorf("unexpected error converting StringValue to StringValuable: %v", diags) 19 | } 20 | 21 | return boolValuable, nil 22 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_type_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (t {{.Name}}Type) ValueType(ctx context.Context) attr.Value { 3 | return {{.Name}}Value{} 4 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_value_equal.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Equal(o attr.Value) bool { 3 | other, ok := o.({{.Name}}Value) 4 | 5 | if !ok { 6 | return false 7 | } 8 | 9 | return v.StringValue.Equal(other.StringValue) 10 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_value_type.gotmpl: -------------------------------------------------------------------------------- 1 | 2 | func (v {{.Name}}Value) Type(ctx context.Context) attr.Type { 3 | return {{.Name}}Type{ 4 | } 5 | } -------------------------------------------------------------------------------- /internal/schema/templates/string_value_valuable.gotmpl: -------------------------------------------------------------------------------- 1 | var _ basetypes.StringValuable = {{.Name}}Value{} -------------------------------------------------------------------------------- /internal/schema/templates/string_value_value.gotmpl: -------------------------------------------------------------------------------- 1 | type {{.Name}}Value struct { 2 | basetypes.StringValue 3 | } -------------------------------------------------------------------------------- /internal/schema/to_from_bool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromBool struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | templates map[string]string 15 | } 16 | 17 | func NewToFromBool(name string, assocExtType *AssocExtType) ToFromBool { 18 | t := map[string]string{ 19 | "from": BoolFromTemplate, 20 | "to": BoolToTemplate, 21 | } 22 | 23 | return ToFromBool{ 24 | Name: FrameworkIdentifier(name), 25 | AssocExtType: assocExtType, 26 | templates: t, 27 | } 28 | } 29 | 30 | func (o ToFromBool) Render() ([]byte, error) { 31 | var buf bytes.Buffer 32 | 33 | renderFuncs := []func() ([]byte, error){ 34 | o.renderTo, 35 | o.renderFrom, 36 | } 37 | 38 | for _, f := range renderFuncs { 39 | b, err := f() 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | buf.Write([]byte("\n")) 46 | 47 | buf.Write(b) 48 | } 49 | 50 | return buf.Bytes(), nil 51 | } 52 | 53 | func (o ToFromBool) renderTo() ([]byte, error) { 54 | var buf bytes.Buffer 55 | 56 | t, err := template.New("").Parse(o.templates["to"]) 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | err = t.Execute(&buf, struct { 63 | Name string 64 | AssocExtType *AssocExtType 65 | }{ 66 | Name: o.Name.ToPascalCase(), 67 | AssocExtType: o.AssocExtType, 68 | }) 69 | 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return buf.Bytes(), nil 75 | } 76 | 77 | func (o ToFromBool) renderFrom() ([]byte, error) { 78 | var buf bytes.Buffer 79 | 80 | t, err := template.New("").Parse(o.templates["from"]) 81 | 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | err = t.Execute(&buf, struct { 87 | Name string 88 | AssocExtType *AssocExtType 89 | }{ 90 | Name: o.Name.ToPascalCase(), 91 | AssocExtType: o.AssocExtType, 92 | }) 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return buf.Bytes(), nil 99 | } 100 | -------------------------------------------------------------------------------- /internal/schema/to_from_float64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromFloat64 struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | templates map[string]string 15 | } 16 | 17 | func NewToFromFloat64(name string, assocExtType *AssocExtType) ToFromFloat64 { 18 | t := map[string]string{ 19 | "from": Float64FromTemplate, 20 | "to": Float64ToTemplate, 21 | } 22 | 23 | return ToFromFloat64{ 24 | Name: FrameworkIdentifier(name), 25 | AssocExtType: assocExtType, 26 | templates: t, 27 | } 28 | } 29 | 30 | func (o ToFromFloat64) Render() ([]byte, error) { 31 | var buf bytes.Buffer 32 | 33 | renderFuncs := []func() ([]byte, error){ 34 | o.renderTo, 35 | o.renderFrom, 36 | } 37 | 38 | for _, f := range renderFuncs { 39 | b, err := f() 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | buf.Write([]byte("\n")) 46 | 47 | buf.Write(b) 48 | } 49 | 50 | return buf.Bytes(), nil 51 | } 52 | 53 | func (o ToFromFloat64) renderTo() ([]byte, error) { 54 | var buf bytes.Buffer 55 | 56 | t, err := template.New("").Parse(o.templates["to"]) 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | err = t.Execute(&buf, struct { 63 | Name string 64 | AssocExtType *AssocExtType 65 | }{ 66 | Name: o.Name.ToPascalCase(), 67 | AssocExtType: o.AssocExtType, 68 | }) 69 | 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return buf.Bytes(), nil 75 | } 76 | 77 | func (o ToFromFloat64) renderFrom() ([]byte, error) { 78 | var buf bytes.Buffer 79 | 80 | t, err := template.New("").Parse(o.templates["from"]) 81 | 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | err = t.Execute(&buf, struct { 87 | Name string 88 | AssocExtType *AssocExtType 89 | }{ 90 | Name: o.Name.ToPascalCase(), 91 | AssocExtType: o.AssocExtType, 92 | }) 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return buf.Bytes(), nil 99 | } 100 | -------------------------------------------------------------------------------- /internal/schema/to_from_int64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromInt64 struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | templates map[string]string 15 | } 16 | 17 | func NewToFromInt64(name string, assocExtType *AssocExtType) ToFromInt64 { 18 | t := map[string]string{ 19 | "from": Int64FromTemplate, 20 | "to": Int64ToTemplate, 21 | } 22 | 23 | return ToFromInt64{ 24 | Name: FrameworkIdentifier(name), 25 | AssocExtType: assocExtType, 26 | templates: t, 27 | } 28 | } 29 | 30 | func (o ToFromInt64) Render() ([]byte, error) { 31 | var buf bytes.Buffer 32 | 33 | renderFuncs := []func() ([]byte, error){ 34 | o.renderTo, 35 | o.renderFrom, 36 | } 37 | 38 | for _, f := range renderFuncs { 39 | b, err := f() 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | buf.Write([]byte("\n")) 46 | 47 | buf.Write(b) 48 | } 49 | 50 | return buf.Bytes(), nil 51 | } 52 | 53 | func (o ToFromInt64) renderTo() ([]byte, error) { 54 | var buf bytes.Buffer 55 | 56 | t, err := template.New("").Parse(o.templates["to"]) 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | err = t.Execute(&buf, struct { 63 | Name string 64 | AssocExtType *AssocExtType 65 | }{ 66 | Name: o.Name.ToPascalCase(), 67 | AssocExtType: o.AssocExtType, 68 | }) 69 | 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return buf.Bytes(), nil 75 | } 76 | 77 | func (o ToFromInt64) renderFrom() ([]byte, error) { 78 | var buf bytes.Buffer 79 | 80 | t, err := template.New("").Parse(o.templates["from"]) 81 | 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | err = t.Execute(&buf, struct { 87 | Name string 88 | AssocExtType *AssocExtType 89 | }{ 90 | Name: o.Name.ToPascalCase(), 91 | AssocExtType: o.AssocExtType, 92 | }) 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return buf.Bytes(), nil 99 | } 100 | -------------------------------------------------------------------------------- /internal/schema/to_from_list.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromList struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | ElementTypeType string 15 | ElementTypeValue string 16 | ElementFrom string 17 | templates map[string]string 18 | } 19 | 20 | func NewToFromList(name string, assocExtType *AssocExtType, elemTypeType, elemTypeValue, elemFrom string) ToFromList { 21 | t := map[string]string{ 22 | "from": ListFromTemplate, 23 | "to": ListToTemplate, 24 | } 25 | 26 | return ToFromList{ 27 | Name: FrameworkIdentifier(name), 28 | AssocExtType: assocExtType, 29 | ElementTypeType: elemTypeType, 30 | ElementTypeValue: elemTypeValue, 31 | ElementFrom: elemFrom, 32 | templates: t, 33 | } 34 | } 35 | 36 | func (o ToFromList) Render() ([]byte, error) { 37 | var buf bytes.Buffer 38 | 39 | renderFuncs := []func() ([]byte, error){ 40 | o.renderTo, 41 | o.renderFrom, 42 | } 43 | 44 | for _, f := range renderFuncs { 45 | b, err := f() 46 | 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | buf.Write([]byte("\n")) 52 | 53 | buf.Write(b) 54 | } 55 | 56 | return buf.Bytes(), nil 57 | } 58 | 59 | func (o ToFromList) renderTo() ([]byte, error) { 60 | var buf bytes.Buffer 61 | 62 | t, err := template.New("").Parse(o.templates["to"]) 63 | 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | err = t.Execute(&buf, struct { 69 | Name string 70 | AssocExtType *AssocExtType 71 | }{ 72 | Name: o.Name.ToPascalCase(), 73 | AssocExtType: o.AssocExtType, 74 | }) 75 | 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | return buf.Bytes(), nil 81 | } 82 | 83 | func (o ToFromList) renderFrom() ([]byte, error) { 84 | var buf bytes.Buffer 85 | 86 | t, err := template.New("").Parse(o.templates["from"]) 87 | 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | err = t.Execute(&buf, struct { 93 | Name string 94 | AssocExtType *AssocExtType 95 | ElementTypeType string 96 | ElementTypeValue string 97 | ElementFrom string 98 | }{ 99 | Name: o.Name.ToPascalCase(), 100 | AssocExtType: o.AssocExtType, 101 | ElementTypeType: o.ElementTypeType, 102 | ElementTypeValue: o.ElementTypeValue, 103 | ElementFrom: o.ElementFrom, 104 | }) 105 | 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | return buf.Bytes(), nil 111 | } 112 | -------------------------------------------------------------------------------- /internal/schema/to_from_map.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromMap struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | ElementTypeType string 15 | ElementTypeValue string 16 | ElementFrom string 17 | templates map[string]string 18 | } 19 | 20 | func NewToFromMap(name string, assocExtType *AssocExtType, elemTypeType, elemTypeValue, elemFrom string) ToFromMap { 21 | t := map[string]string{ 22 | "from": MapFromTemplate, 23 | "to": MapToTemplate, 24 | } 25 | 26 | return ToFromMap{ 27 | Name: FrameworkIdentifier(name), 28 | AssocExtType: assocExtType, 29 | ElementTypeType: elemTypeType, 30 | ElementTypeValue: elemTypeValue, 31 | ElementFrom: elemFrom, 32 | templates: t, 33 | } 34 | } 35 | 36 | func (o ToFromMap) Render() ([]byte, error) { 37 | var buf bytes.Buffer 38 | 39 | renderFuncs := []func() ([]byte, error){ 40 | o.renderTo, 41 | o.renderFrom, 42 | } 43 | 44 | for _, f := range renderFuncs { 45 | b, err := f() 46 | 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | buf.Write([]byte("\n")) 52 | 53 | buf.Write(b) 54 | } 55 | 56 | return buf.Bytes(), nil 57 | } 58 | 59 | func (o ToFromMap) renderTo() ([]byte, error) { 60 | var buf bytes.Buffer 61 | 62 | t, err := template.New("").Parse(o.templates["to"]) 63 | 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | err = t.Execute(&buf, struct { 69 | Name string 70 | AssocExtType *AssocExtType 71 | }{ 72 | Name: o.Name.ToPascalCase(), 73 | AssocExtType: o.AssocExtType, 74 | }) 75 | 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | return buf.Bytes(), nil 81 | } 82 | 83 | func (o ToFromMap) renderFrom() ([]byte, error) { 84 | var buf bytes.Buffer 85 | 86 | t, err := template.New("").Parse(o.templates["from"]) 87 | 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | err = t.Execute(&buf, struct { 93 | Name string 94 | AssocExtType *AssocExtType 95 | ElementTypeType string 96 | ElementTypeValue string 97 | ElementFrom string 98 | }{ 99 | Name: o.Name.ToPascalCase(), 100 | AssocExtType: o.AssocExtType, 101 | ElementTypeType: o.ElementTypeType, 102 | ElementTypeValue: o.ElementTypeValue, 103 | ElementFrom: o.ElementFrom, 104 | }) 105 | 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | return buf.Bytes(), nil 111 | } 112 | -------------------------------------------------------------------------------- /internal/schema/to_from_nested_object.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromNestedObject struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | ToFuncs map[FrameworkIdentifier]ToFromConversion 15 | FromFuncs map[FrameworkIdentifier]ToFromConversion 16 | templates map[string]string 17 | } 18 | 19 | func NewToFromNestedObject(name string, assocExtType *AssocExtType, toFuncs, fromFuncs map[string]ToFromConversion) ToFromNestedObject { 20 | t := map[string]string{ 21 | "from": NestedObjectFromTemplate, 22 | "to": NestedObjectToTemplate, 23 | } 24 | 25 | tf := make(map[FrameworkIdentifier]ToFromConversion, len(toFuncs)) 26 | 27 | for k, v := range toFuncs { 28 | tf[FrameworkIdentifier(k)] = v 29 | } 30 | 31 | ff := make(map[FrameworkIdentifier]ToFromConversion, len(fromFuncs)) 32 | 33 | for k, v := range fromFuncs { 34 | ff[FrameworkIdentifier(k)] = v 35 | } 36 | 37 | return ToFromNestedObject{ 38 | Name: FrameworkIdentifier(name), 39 | AssocExtType: assocExtType, 40 | FromFuncs: ff, 41 | ToFuncs: tf, 42 | templates: t, 43 | } 44 | } 45 | 46 | func (o ToFromNestedObject) Render() ([]byte, error) { 47 | var buf bytes.Buffer 48 | 49 | renderFuncs := []func() ([]byte, error){ 50 | o.renderTo, 51 | o.renderFrom, 52 | } 53 | 54 | for _, f := range renderFuncs { 55 | b, err := f() 56 | 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | buf.Write([]byte("\n")) 62 | 63 | buf.Write(b) 64 | } 65 | 66 | return buf.Bytes(), nil 67 | } 68 | 69 | func (o ToFromNestedObject) renderTo() ([]byte, error) { 70 | var buf bytes.Buffer 71 | 72 | t, err := template.New("").Parse(o.templates["to"]) 73 | 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | err = t.Execute(&buf, struct { 79 | Name string 80 | AssocExtType *AssocExtType 81 | ToFuncs map[FrameworkIdentifier]ToFromConversion 82 | }{ 83 | Name: o.Name.ToPascalCase(), 84 | AssocExtType: o.AssocExtType, 85 | ToFuncs: o.ToFuncs, 86 | }) 87 | 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | return buf.Bytes(), nil 93 | } 94 | 95 | func (o ToFromNestedObject) renderFrom() ([]byte, error) { 96 | var buf bytes.Buffer 97 | 98 | t, err := template.New("").Parse(o.templates["from"]) 99 | 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | err = t.Execute(&buf, struct { 105 | Name string 106 | AssocExtType *AssocExtType 107 | FromFuncs map[FrameworkIdentifier]ToFromConversion 108 | }{ 109 | Name: o.Name.ToPascalCase(), 110 | AssocExtType: o.AssocExtType, 111 | FromFuncs: o.FromFuncs, 112 | }) 113 | 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | return buf.Bytes(), nil 119 | } 120 | -------------------------------------------------------------------------------- /internal/schema/to_from_number.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromNumber struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | templates map[string]string 15 | } 16 | 17 | func NewToFromNumber(name string, assocExtType *AssocExtType) ToFromNumber { 18 | t := map[string]string{ 19 | "from": NumberFromTemplate, 20 | "to": NumberToTemplate, 21 | } 22 | 23 | return ToFromNumber{ 24 | Name: FrameworkIdentifier(name), 25 | AssocExtType: assocExtType, 26 | templates: t, 27 | } 28 | } 29 | 30 | func (o ToFromNumber) Render() ([]byte, error) { 31 | var buf bytes.Buffer 32 | 33 | renderFuncs := []func() ([]byte, error){ 34 | o.renderTo, 35 | o.renderFrom, 36 | } 37 | 38 | for _, f := range renderFuncs { 39 | b, err := f() 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | buf.Write([]byte("\n")) 46 | 47 | buf.Write(b) 48 | } 49 | 50 | return buf.Bytes(), nil 51 | } 52 | 53 | func (o ToFromNumber) renderTo() ([]byte, error) { 54 | var buf bytes.Buffer 55 | 56 | t, err := template.New("").Parse(o.templates["to"]) 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | err = t.Execute(&buf, struct { 63 | Name string 64 | AssocExtType *AssocExtType 65 | }{ 66 | Name: o.Name.ToPascalCase(), 67 | AssocExtType: o.AssocExtType, 68 | }) 69 | 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return buf.Bytes(), nil 75 | } 76 | 77 | func (o ToFromNumber) renderFrom() ([]byte, error) { 78 | var buf bytes.Buffer 79 | 80 | t, err := template.New("").Parse(o.templates["from"]) 81 | 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | err = t.Execute(&buf, struct { 87 | Name string 88 | AssocExtType *AssocExtType 89 | }{ 90 | Name: o.Name.ToPascalCase(), 91 | AssocExtType: o.AssocExtType, 92 | }) 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return buf.Bytes(), nil 99 | } 100 | -------------------------------------------------------------------------------- /internal/schema/to_from_set.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromSet struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | ElementTypeType string 15 | ElementTypeValue string 16 | ElementFrom string 17 | templates map[string]string 18 | } 19 | 20 | func NewToFromSet(name string, assocExtType *AssocExtType, elemTypeType, elemTypeValue, elemFrom string) ToFromSet { 21 | t := map[string]string{ 22 | "from": SetFromTemplate, 23 | "to": SetToTemplate, 24 | } 25 | 26 | return ToFromSet{ 27 | Name: FrameworkIdentifier(name), 28 | AssocExtType: assocExtType, 29 | ElementTypeType: elemTypeType, 30 | ElementTypeValue: elemTypeValue, 31 | ElementFrom: elemFrom, 32 | templates: t, 33 | } 34 | } 35 | 36 | func (o ToFromSet) Render() ([]byte, error) { 37 | var buf bytes.Buffer 38 | 39 | renderFuncs := []func() ([]byte, error){ 40 | o.renderTo, 41 | o.renderFrom, 42 | } 43 | 44 | for _, f := range renderFuncs { 45 | b, err := f() 46 | 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | buf.Write([]byte("\n")) 52 | 53 | buf.Write(b) 54 | } 55 | 56 | return buf.Bytes(), nil 57 | } 58 | 59 | func (o ToFromSet) renderTo() ([]byte, error) { 60 | var buf bytes.Buffer 61 | 62 | t, err := template.New("").Parse(o.templates["to"]) 63 | 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | err = t.Execute(&buf, struct { 69 | Name string 70 | AssocExtType *AssocExtType 71 | }{ 72 | Name: o.Name.ToPascalCase(), 73 | AssocExtType: o.AssocExtType, 74 | }) 75 | 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | return buf.Bytes(), nil 81 | } 82 | 83 | func (o ToFromSet) renderFrom() ([]byte, error) { 84 | var buf bytes.Buffer 85 | 86 | t, err := template.New("").Parse(o.templates["from"]) 87 | 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | err = t.Execute(&buf, struct { 93 | Name string 94 | AssocExtType *AssocExtType 95 | ElementTypeType string 96 | ElementTypeValue string 97 | ElementFrom string 98 | }{ 99 | Name: o.Name.ToPascalCase(), 100 | AssocExtType: o.AssocExtType, 101 | ElementTypeType: o.ElementTypeType, 102 | ElementTypeValue: o.ElementTypeValue, 103 | ElementFrom: o.ElementFrom, 104 | }) 105 | 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | return buf.Bytes(), nil 111 | } 112 | -------------------------------------------------------------------------------- /internal/schema/to_from_string.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | "bytes" 8 | "text/template" 9 | ) 10 | 11 | type ToFromString struct { 12 | Name FrameworkIdentifier 13 | AssocExtType *AssocExtType 14 | templates map[string]string 15 | } 16 | 17 | func NewToFromString(name string, assocExtType *AssocExtType) ToFromString { 18 | t := map[string]string{ 19 | "from": StringFromTemplate, 20 | "to": StringToTemplate, 21 | } 22 | 23 | return ToFromString{ 24 | Name: FrameworkIdentifier(name), 25 | AssocExtType: assocExtType, 26 | templates: t, 27 | } 28 | } 29 | 30 | func (o ToFromString) Render() ([]byte, error) { 31 | var buf bytes.Buffer 32 | 33 | renderFuncs := []func() ([]byte, error){ 34 | o.renderTo, 35 | o.renderFrom, 36 | } 37 | 38 | for _, f := range renderFuncs { 39 | b, err := f() 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | buf.Write([]byte("\n")) 46 | 47 | buf.Write(b) 48 | } 49 | 50 | return buf.Bytes(), nil 51 | } 52 | 53 | func (o ToFromString) renderTo() ([]byte, error) { 54 | var buf bytes.Buffer 55 | 56 | t, err := template.New("").Parse(o.templates["to"]) 57 | 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | err = t.Execute(&buf, struct { 63 | Name string 64 | AssocExtType *AssocExtType 65 | }{ 66 | Name: o.Name.ToPascalCase(), 67 | AssocExtType: o.AssocExtType, 68 | }) 69 | 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return buf.Bytes(), nil 75 | } 76 | 77 | func (o ToFromString) renderFrom() ([]byte, error) { 78 | var buf bytes.Buffer 79 | 80 | t, err := template.New("").Parse(o.templates["from"]) 81 | 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | err = t.Execute(&buf, struct { 87 | Name string 88 | AssocExtType *AssocExtType 89 | }{ 90 | Name: o.Name.ToPascalCase(), 91 | AssocExtType: o.AssocExtType, 92 | }) 93 | 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | return buf.Bytes(), nil 99 | } 100 | -------------------------------------------------------------------------------- /internal/schema/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package schema 5 | 6 | import ( 7 | specschema "github.com/hashicorp/terraform-plugin-codegen-spec/schema" 8 | 9 | "github.com/hashicorp/terraform-plugin-codegen-framework/internal/model" 10 | ) 11 | 12 | type Attributes interface { 13 | GetAttributes() GeneratorAttributes 14 | } 15 | 16 | type Attrs interface { 17 | AttrTypes() specschema.ObjectAttributeTypes 18 | } 19 | 20 | type Blocks interface { 21 | Attributes 22 | GetBlocks() GeneratorBlocks 23 | } 24 | 25 | type CustomTypeAndValue interface { 26 | CustomTypeAndValue(name string) ([]byte, error) 27 | } 28 | 29 | type Elements interface { 30 | ElemType() specschema.ElementType 31 | } 32 | 33 | type GeneratorAttribute interface { 34 | Equal(GeneratorAttribute) bool 35 | GeneratorSchemaType() Type 36 | Imports() *Imports 37 | ModelField(FrameworkIdentifier) (model.Field, error) 38 | Schema(FrameworkIdentifier) (string, error) 39 | } 40 | 41 | type AttrType interface { 42 | AttrType(FrameworkIdentifier) (string, error) 43 | } 44 | 45 | type AttrValue interface { 46 | AttrValue(FrameworkIdentifier) string 47 | } 48 | 49 | type CollectionType interface { 50 | CollectionType() (map[string]string, error) 51 | } 52 | 53 | type GeneratorBlock interface { 54 | Equal(GeneratorBlock) bool 55 | GeneratorSchemaType() Type 56 | Imports() *Imports 57 | ModelField(FrameworkIdentifier) (model.Field, error) 58 | Schema(FrameworkIdentifier) (string, error) 59 | } 60 | 61 | type ToFrom interface { 62 | ToFromFunctions(name string) ([]byte, error) 63 | } 64 | 65 | type ToFromConversion struct { 66 | Default string 67 | AssocExtType *AssocExtType 68 | CollectionType CollectionFields 69 | ObjectType map[FrameworkIdentifier]ObjectField 70 | } 71 | 72 | type CollectionFields struct { 73 | ElementType string 74 | GoType string 75 | TypeValueFrom string 76 | } 77 | 78 | type ObjectField struct { 79 | FromFunc string 80 | GoType string 81 | Type string 82 | ToFunc string 83 | } 84 | 85 | type To interface { 86 | To() (ToFromConversion, error) 87 | } 88 | 89 | type From interface { 90 | From() (ToFromConversion, error) 91 | } 92 | 93 | type Type int64 94 | 95 | const ( 96 | InvalidGeneratorSchemaType Type = iota 97 | GeneratorBoolAttribute 98 | GeneratorFloat64Attribute 99 | GeneratorInt64Attribute 100 | GeneratorListAttribute 101 | GeneratorListNestedAttribute 102 | GeneratorListNestedBlock 103 | GeneratorMapAttribute 104 | GeneratorMapNestedAttribute 105 | GeneratorNumberAttribute 106 | GeneratorObjectAttribute 107 | GeneratorSetAttribute 108 | GeneratorSetNestedAttribute 109 | GeneratorSetNestedBlock 110 | GeneratorSingleNestedAttribute 111 | GeneratorSingleNestedBlock 112 | GeneratorStringAttribute 113 | ) 114 | -------------------------------------------------------------------------------- /internal/validate/validate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package validate 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | ) 10 | 11 | func JSON(input []byte) error { 12 | if !json.Valid(input) { 13 | return errors.New("invalid JSON") 14 | } 15 | 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /questions.md: -------------------------------------------------------------------------------- 1 | # Questions 2 | 3 | ## Associated External Type and Primitives 4 | 5 | * Are there any instances in which a primitive type (i.e., bool, float, int, string) could have a 6 | type defined for _associated_external_type_, and if so, what would the accompanying expand and 7 | flatten functions look like? 8 | * At first glance, it seems as though primitives would just be pointers to Go types (e.g., *bool, 9 | *float, *int, *string). 10 | 11 | ```json 12 | { 13 | "datasources": [ 14 | { 15 | "name": "example", 16 | "schema": { 17 | "attributes": [ 18 | { 19 | "name": "bool_attribute", 20 | "bool": { 21 | "computed_optional_required": "computed", 22 | "associated_external_type": { 23 | "import": "example.com/apisdk", 24 | "type": "*apisdk.Bool" 25 | } 26 | } 27 | } 28 | ] 29 | } 30 | } 31 | ] 32 | } 33 | ``` 34 | 35 | ### Answer 36 | 37 | * Supporting custom primitive external types will require that a corresponding custom framework 38 | type also be defined. The custom framework type would be responsible for the mapping to/from 39 | the custom external type. This will require supporting basetype interfaces 40 | (e.g., [StringValuable](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types/basetypes#StringValuable)). 41 | * Without a corresponding custom framework type, primitive attributes will only be able to support 42 | pointers and values (e.g., `bool` and `*bool`). 43 | 44 | ```json 45 | { 46 | "name": "bool_attribute", 47 | "bool": { 48 | "computed_optional_required": "computed", 49 | "associated_external_type": { 50 | "type": "*bool" 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | ## Associated External Types and Collection Types - List, Map & Set 57 | 58 | * Are there any instances in which a collection type (i.e., list, map, set) could have a type 59 | defined for _associated_external_type_, and if so, what would the accompanying expand and 60 | flatten functions look like? 61 | * At first glance, it seems as though collection types would just be Go types (i.e., list => slice, 62 | map => map, set => slice). 63 | 64 | ```json 65 | { 66 | "datasources": [ 67 | { 68 | "name": "example", 69 | "schema": { 70 | "attributes": [ 71 | { 72 | "name": "list_attribute", 73 | "list": { 74 | "computed_optional_required": "computed", 75 | "associated_external_type": { 76 | "import": "example.com/apisdk", 77 | "type": "*apisdk.List" 78 | } 79 | } 80 | } 81 | ] 82 | } 83 | } 84 | ] 85 | } 86 | ``` -------------------------------------------------------------------------------- /tools/copywrite.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | //go:build generate 5 | 6 | package tools 7 | 8 | import ( 9 | // copywrite header generation 10 | _ "github.com/hashicorp/copywrite" 11 | ) 12 | 13 | //go:generate go run github.com/hashicorp/copywrite headers -d .. --config ../.copywrite.hcl 14 | -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module tools 2 | 3 | go 1.22.7 4 | toolchain go1.24.1 5 | 6 | require github.com/hashicorp/copywrite v0.22.0 7 | 8 | require ( 9 | github.com/AlecAivazis/survey/v2 v2.3.7 // indirect 10 | github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect 11 | github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect 12 | github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect 13 | github.com/bradleyfalzon/ghinstallation/v2 v2.5.0 // indirect 14 | github.com/cli/go-gh/v2 v2.11.2 // indirect 15 | github.com/cli/safeexec v1.0.0 // indirect 16 | github.com/cloudflare/circl v1.3.7 // indirect 17 | github.com/fatih/color v1.13.0 // indirect 18 | github.com/fsnotify/fsnotify v1.5.4 // indirect 19 | github.com/go-openapi/errors v0.20.2 // indirect 20 | github.com/go-openapi/strfmt v0.21.3 // indirect 21 | github.com/golang-jwt/jwt/v4 v4.5.2 // indirect 22 | github.com/golang/protobuf v1.5.2 // indirect 23 | github.com/google/go-github/v45 v45.2.0 // indirect 24 | github.com/google/go-github/v53 v53.0.0 // indirect 25 | github.com/google/go-querystring v1.1.0 // indirect 26 | github.com/hashicorp/go-hclog v1.5.0 // indirect 27 | github.com/hashicorp/hcl v1.0.0 // indirect 28 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 29 | github.com/jedib0t/go-pretty v4.3.0+incompatible // indirect 30 | github.com/jedib0t/go-pretty/v6 v6.4.6 // indirect 31 | github.com/joho/godotenv v1.3.0 // indirect 32 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 33 | github.com/knadh/koanf v1.5.0 // indirect 34 | github.com/mattn/go-colorable v0.1.13 // indirect 35 | github.com/mattn/go-isatty v0.0.20 // indirect 36 | github.com/mattn/go-runewidth v0.0.15 // indirect 37 | github.com/mergestat/timediff v0.0.3 // indirect 38 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 39 | github.com/mitchellh/copystructure v1.2.0 // indirect 40 | github.com/mitchellh/go-homedir v1.1.0 // indirect 41 | github.com/mitchellh/mapstructure v1.5.0 // indirect 42 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 43 | github.com/oklog/ulid v1.3.1 // indirect 44 | github.com/rivo/uniseg v0.4.7 // indirect 45 | github.com/rogpeppe/go-internal v1.10.0 // indirect 46 | github.com/samber/lo v1.37.0 // indirect 47 | github.com/spf13/cobra v1.6.1 // indirect 48 | github.com/spf13/pflag v1.0.5 // indirect 49 | github.com/thanhpk/randstr v1.0.4 // indirect 50 | go.mongodb.org/mongo-driver v1.10.0 // indirect 51 | golang.org/x/crypto v0.35.0 // indirect 52 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect 53 | golang.org/x/net v0.36.0 // indirect 54 | golang.org/x/oauth2 v0.8.0 // indirect 55 | golang.org/x/sync v0.11.0 // indirect 56 | golang.org/x/sys v0.30.0 // indirect 57 | golang.org/x/term v0.29.0 // indirect 58 | golang.org/x/text v0.22.0 // indirect 59 | google.golang.org/appengine v1.6.7 // indirect 60 | google.golang.org/protobuf v1.33.0 // indirect 61 | gopkg.in/yaml.v3 v3.0.1 // indirect 62 | ) 63 | --------------------------------------------------------------------------------