diff --git a/betatemplates/data-sources/davinci_application.md.tmpl b/betatemplates/data-sources/davinci_application.md.tmpl new file mode 100644 index 000000000..997faa0c2 --- /dev/null +++ b/betatemplates/data-sources/davinci_application.md.tmpl @@ -0,0 +1,18 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}" +subcategory: "DaVinci" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/data-sources/" .Name "/data-source.tf") }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/betatemplates/data-sources/davinci_applications.md.tmpl b/betatemplates/data-sources/davinci_applications.md.tmpl new file mode 100644 index 000000000..6661a1323 --- /dev/null +++ b/betatemplates/data-sources/davinci_applications.md.tmpl @@ -0,0 +1,18 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}" +subcategory: "DaVinci" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/data-sources/" .Name "/data-source.tf") }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/betatemplates/data-sources/davinci_connector.md.tmpl b/betatemplates/data-sources/davinci_connector.md.tmpl new file mode 100644 index 000000000..997faa0c2 --- /dev/null +++ b/betatemplates/data-sources/davinci_connector.md.tmpl @@ -0,0 +1,18 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}" +subcategory: "DaVinci" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/data-sources/" .Name "/data-source.tf") }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/betatemplates/data-sources/davinci_connector_instance.md.tmpl b/betatemplates/data-sources/davinci_connector_instance.md.tmpl new file mode 100644 index 000000000..997faa0c2 --- /dev/null +++ b/betatemplates/data-sources/davinci_connector_instance.md.tmpl @@ -0,0 +1,18 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}" +subcategory: "DaVinci" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/data-sources/" .Name "/data-source.tf") }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/betatemplates/data-sources/davinci_connector_instances.md.tmpl b/betatemplates/data-sources/davinci_connector_instances.md.tmpl new file mode 100644 index 000000000..6661a1323 --- /dev/null +++ b/betatemplates/data-sources/davinci_connector_instances.md.tmpl @@ -0,0 +1,18 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}" +subcategory: "DaVinci" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/data-sources/" .Name "/data-source.tf") }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/betatemplates/data-sources/davinci_connectors.md.tmpl b/betatemplates/data-sources/davinci_connectors.md.tmpl new file mode 100644 index 000000000..6661a1323 --- /dev/null +++ b/betatemplates/data-sources/davinci_connectors.md.tmpl @@ -0,0 +1,18 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.RenderedProviderName}}" +subcategory: "DaVinci" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "%s%s%s" "examples/data-sources/" .Name "/data-source.tf") }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/examples/data-sources/pingone_davinci_application/data-source.tf b/examples/data-sources/pingone_davinci_application/data-source.tf new file mode 100644 index 000000000..96d23c6c1 --- /dev/null +++ b/examples/data-sources/pingone_davinci_application/data-source.tf @@ -0,0 +1,5 @@ +data "pingone_davinci_application" "example" { + environment_id = var.environment_id + application_id = var.application_id +} + diff --git a/examples/data-sources/pingone_davinci_applications/data-source.tf b/examples/data-sources/pingone_davinci_applications/data-source.tf new file mode 100644 index 000000000..adb281570 --- /dev/null +++ b/examples/data-sources/pingone_davinci_applications/data-source.tf @@ -0,0 +1,3 @@ +data "pingone_davinci_applications" "example" { + environment_id = var.environment_id +} \ No newline at end of file diff --git a/examples/data-sources/pingone_davinci_connector/data-source.tf b/examples/data-sources/pingone_davinci_connector/data-source.tf new file mode 100644 index 000000000..bbd6a162b --- /dev/null +++ b/examples/data-sources/pingone_davinci_connector/data-source.tf @@ -0,0 +1,5 @@ +data "pingone_davinci_connector" "example" { + environment_id = var.environment_id + connector_id = var.connector_id +} + diff --git a/examples/data-sources/pingone_davinci_connector_instance/data-source.tf b/examples/data-sources/pingone_davinci_connector_instance/data-source.tf new file mode 100644 index 000000000..d190cd5d8 --- /dev/null +++ b/examples/data-sources/pingone_davinci_connector_instance/data-source.tf @@ -0,0 +1,5 @@ +data "pingone_davinci_connector_instance" "example" { + environment_id = var.environment_id + instance_id = var.connector_instance_id +} + diff --git a/examples/data-sources/pingone_davinci_connector_instances/data-source.tf b/examples/data-sources/pingone_davinci_connector_instances/data-source.tf new file mode 100644 index 000000000..cf3622a0b --- /dev/null +++ b/examples/data-sources/pingone_davinci_connector_instances/data-source.tf @@ -0,0 +1,3 @@ +data "pingone_davinci_connector_instances" "example" { + environment_id = var.environment_id +} \ No newline at end of file diff --git a/examples/data-sources/pingone_davinci_connectors/data-source.tf b/examples/data-sources/pingone_davinci_connectors/data-source.tf new file mode 100644 index 000000000..0ad146870 --- /dev/null +++ b/examples/data-sources/pingone_davinci_connectors/data-source.tf @@ -0,0 +1,3 @@ +data "pingone_davinci_connectors" "example" { + environment_id = var.environment_id +} \ No newline at end of file diff --git a/go.mod b/go.mod index 9337b15b8..ab707e9ac 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/patrickcping/pingone-go-sdk-v2/mfa v0.23.2 github.com/patrickcping/pingone-go-sdk-v2/risk v0.21.0 github.com/patrickcping/pingone-go-sdk-v2/verify v0.10.0 - github.com/pingidentity/pingone-go-client v0.2.0 + github.com/pingidentity/pingone-go-client v0.3.1-0.20251117224159-0cf427981cb9 ) require ( @@ -286,7 +286,7 @@ require ( github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tdakkota/asciicheck v0.4.1 // indirect github.com/terraform-linters/tflint v0.57.0 // indirect diff --git a/go.sum b/go.sum index 3e0a7c9cc..d362a4eda 100644 --- a/go.sum +++ b/go.sum @@ -1496,8 +1496,8 @@ github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2 github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pingidentity/pingone-go-client v0.2.0 h1:B2rf89grJei47hw0wU7hA1LUzl2nIKQ99yeC87ECaaI= -github.com/pingidentity/pingone-go-client v0.2.0/go.mod h1:KgORZc7mikIKCClfmJXruf+uydMwDGDNPYo3179pYQo= +github.com/pingidentity/pingone-go-client v0.3.1-0.20251117224159-0cf427981cb9 h1:tjRKI8gRimaLZrUzXTyuKMqcqbJpzenG/+51B4QUc6Y= +github.com/pingidentity/pingone-go-client v0.3.1-0.20251117224159-0cf427981cb9/go.mod h1:cI4exDxqhSAEzazgVmiY7T0HZC4XF/lgTMy2hKjs5no= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -1664,8 +1664,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8= diff --git a/internal/service/davinci/data_source_davinci_application_gen.go b/internal/service/davinci/data_source_davinci_application_gen.go new file mode 100644 index 000000000..b80dd34ae --- /dev/null +++ b/internal/service/davinci/data_source_davinci_application_gen.go @@ -0,0 +1,255 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci + +import ( + "context" + "fmt" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/pingidentity/pingone-go-client/pingone" + "github.com/pingidentity/terraform-provider-pingone/internal/framework" +) + +var ( + _ datasource.DataSource = &davinciApplicationDataSource{} + _ datasource.DataSourceWithConfigure = &davinciApplicationDataSource{} +) + +func NewDavinciApplicationDataSource() datasource.DataSource { + return &davinciApplicationDataSource{} +} + +type davinciApplicationDataSource serviceClientType + +func (r *davinciApplicationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_davinci_application" +} + +func (r *davinciApplicationDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + resourceConfig, ok := req.ProviderData.(framework.ResourceType) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected the provider client, got: %T. Please report this issue to the provider maintainers.", req.ProviderData), + ) + + return + } + + r.Client = resourceConfig.Client + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialised", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.", + ) + return + } +} + +type davinciApplicationDataSourceModel struct { + ApiKey types.Object `tfsdk:"api_key"` + ApplicationId types.String `tfsdk:"application_id"` + EnvironmentId types.String `tfsdk:"environment_id"` + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Oauth types.Object `tfsdk:"oauth"` +} + +func (r *davinciApplicationDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data source to retrieve a DaVinci application.", + Attributes: map[string]schema.Attribute{ + "api_key": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Computed: true, + }, + "value": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "application_id": schema.StringAttribute{ + Required: true, + Description: "A string that specifies the ID of the application to retrieve configuration for.", + }, + "environment_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the environment that is configured with the application. Must be a valid PingOne resource ID.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this data source.", + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "oauth": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "client_secret": schema.StringAttribute{ + Computed: true, + }, + "enforce_signed_request_openid": schema.BoolAttribute{ + Computed: true, + }, + "grant_types": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "Options are \"authorizationCode\", \"clientCredentials\", \"implicit\".", + MarkdownDescription: "Options are `authorizationCode`, `clientCredentials`, `implicit`.", + }, + "logout_uris": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "redirect_uris": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "scopes": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "Options are \"flow_analytics\", \"offline_access\", \"openid\", \"profile\".", + MarkdownDescription: "Options are `flow_analytics`, `offline_access`, `openid`, `profile`.", + }, + "sp_jwks_openid": schema.StringAttribute{ + Computed: true, + }, + "sp_jwks_url": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + }, + } +} + +func (state *davinciApplicationDataSourceModel) readClientResponse(response *pingone.DaVinciApplicationResponse) diag.Diagnostics { + var respDiags, diags diag.Diagnostics + // api_key + apiKeyAttrTypes := map[string]attr.Type{ + "enabled": types.BoolType, + "value": types.StringType, + } + apiKeyValue, diags := types.ObjectValue(apiKeyAttrTypes, map[string]attr.Value{ + "enabled": types.BoolValue(response.ApiKey.Enabled), + "value": types.StringValue(response.ApiKey.Value), + }) + respDiags.Append(diags...) + state.ApiKey = apiKeyValue + // id + state.Id = types.StringValue(response.Id) + // name + state.Name = types.StringValue(response.Name) + // oauth + oauthAttrTypes := map[string]attr.Type{ + "client_secret": types.StringType, + "enforce_signed_request_openid": types.BoolType, + "grant_types": types.SetType{ElemType: types.StringType}, + "logout_uris": types.SetType{ElemType: types.StringType}, + "redirect_uris": types.SetType{ElemType: types.StringType}, + "scopes": types.SetType{ElemType: types.StringType}, + "sp_jwks_openid": types.StringType, + "sp_jwks_url": types.StringType, + } + oauthGrantTypesValue, diags := types.SetValueFrom(context.Background(), types.StringType, response.Oauth.GrantTypes) + respDiags.Append(diags...) + oauthLogoutUrisValue, diags := types.SetValueFrom(context.Background(), types.StringType, response.Oauth.LogoutUris) + respDiags.Append(diags...) + oauthRedirectUrisValue, diags := types.SetValueFrom(context.Background(), types.StringType, response.Oauth.RedirectUris) + respDiags.Append(diags...) + oauthScopesValue, diags := types.SetValueFrom(context.Background(), types.StringType, response.Oauth.Scopes) + respDiags.Append(diags...) + oauthValue, diags := types.ObjectValue(oauthAttrTypes, map[string]attr.Value{ + "client_secret": types.StringValue(response.Oauth.ClientSecret), + "enforce_signed_request_openid": types.BoolPointerValue(response.Oauth.EnforceSignedRequestOpenid), + "grant_types": oauthGrantTypesValue, + "logout_uris": oauthLogoutUrisValue, + "redirect_uris": oauthRedirectUrisValue, + "scopes": oauthScopesValue, + "sp_jwks_openid": types.StringPointerValue(response.Oauth.SpJwksOpenid), + "sp_jwks_url": types.StringPointerValue(response.Oauth.SpjwksUrl), + }) + respDiags.Append(diags...) + state.Oauth = oauthValue + return respDiags +} + +func (r *davinciApplicationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data davinciApplicationDataSourceModel + + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialized", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.") + return + } + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + environmentIdUuid, err := uuid.Parse(data.EnvironmentId.ValueString()) + if err != nil { + resp.Diagnostics.AddAttributeError( + path.Root("environment_id"), + "Attribute Validation Error", + fmt.Sprintf("The value '%s' for attribute '%s' is not a valid UUID: %s", data.EnvironmentId.ValueString(), "EnvironmentId", err.Error()), + ) + return + } + var responseData *pingone.DaVinciApplicationResponse + resp.Diagnostics.Append(framework.ParseResponse( + ctx, + + func() (any, *http.Response, error) { + fO, fR, fErr := r.Client.DaVinciApplicationsApi.GetDavinciApplicationById(ctx, environmentIdUuid, data.ApplicationId.ValueString()).Execute() + return framework.CheckEnvironmentExistsOnPermissionsError(ctx, r.Client, data.EnvironmentId.ValueString(), fO, fR, fErr) + }, + "GetDavinciApplicationById", + framework.DefaultCustomError, + framework.DefaultRetryable, + &responseData, + )...) + + if resp.Diagnostics.HasError() { + return + } + + // Read response into the model + resp.Diagnostics.Append(data.readClientResponse(responseData)...) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/service/davinci/data_source_davinci_application_gen_test.go b/internal/service/davinci/data_source_davinci_application_gen_test.go new file mode 100644 index 000000000..001fd5db6 --- /dev/null +++ b/internal/service/davinci/data_source_davinci_application_gen_test.go @@ -0,0 +1,215 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pingidentity/terraform-provider-pingone/internal/acctest" + "github.com/pingidentity/terraform-provider-pingone/internal/verify" +) + +func TestAccDavinciApplicationDataSource_ByIDFull_Clean(t *testing.T) { + testAccDavinciApplicationDataSource_ByIDFull(t, false) +} + +func TestAccDavinciApplicationDataSource_ByIDFull_WithBootstrap(t *testing.T) { + testAccDavinciApplicationDataSource_ByIDFull(t, true) +} + +func testAccDavinciApplicationDataSource_ByIDFull(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + CheckDestroy: davinciApplication_CheckDestroy, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciApplicationDataSourceConfig_ByIDFull(resourceName, withBootstrapConfig), + Check: davinciApplicationDataSource_CheckComputedValuesComplete(resourceName), + }, + }, + }) +} + +func TestAccDavinciApplicationDataSource_BootstrapApplicationByIDFull(t *testing.T) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciApplicationDataSourceConfig_BootstrapApplicationByID(resourceName), + Check: davinciApplicationDataSource_CheckComputedValuesBootstrapApplication(resourceName), + }, + }, + }) +} + +func TestAccDavinciApplicationDataSource_NotFound_Clean(t *testing.T) { + testAccDavinciApplicationDataSource_NotFound(t, false) +} + +func TestAccDavinciApplicationDataSource_NotFound_WithBootstrap(t *testing.T) { + testAccDavinciApplicationDataSource_NotFound(t, true) +} + +func testAccDavinciApplicationDataSource_NotFound(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + CheckDestroy: davinciApplication_CheckDestroy, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciApplicationDataSourceConfig_NotFoundByID(resourceName, withBootstrapConfig), + ExpectError: regexp.MustCompile("The requested resource was not found"), + }, + }, + }) +} + +func testAccDavinciApplicationDataSourceConfig_Full(resourceName string) string { + return fmt.Sprintf(` +resource "pingone_davinci_application" "%[1]s" { + environment_id = data.pingone_environment.general_test.id + name = "%[1]s" + api_key = { + enabled = false + } + oauth = { + enforce_signed_request_openid = true + grant_types = [ + "clientCredentials", + "authorizationCode", + "implicit", + ] + logout_uris = [ + "https://pingidentity.com/logout", + ] + redirect_uris = [ + "https://pingidentity.com/callback", + "https://pingidentity.com/redirect", + ] + scopes = [ + "profile", + "flow_analytics", + "openid", + ] + sp_jwks_url = "https://pingidentity.com/jwks" + } +} +`, resourceName) +} + +func testAccDavinciApplicationDataSourceConfig_ByIDFull(resourceName string, withBootstrapConfig bool) string { + return fmt.Sprintf(` + %[1]s + + %[3]s + +data "pingone_davinci_application" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + application_id = pingone_davinci_application.%[2]s.id +}`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName, testAccDavinciApplicationDataSourceConfig_Full(resourceName)) +} + +func testAccDavinciApplicationDataSourceConfig_BootstrapApplicationByID(resourceName string) (hcl string) { + return fmt.Sprintf(` +%[1]s + +data "pingone_davinci_applications" "%[2]s" { + environment_id = data.pingone_environment.general_test.id +} + +data "pingone_davinci_application" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + application_id = data.pingone_davinci_applications.%[2]s.davinci_applications.* [index(data.pingone_davinci_applications.%[2]s.davinci_applications[*].name, "PingOne SSO Connection")].id +} +`, acctest.DaVinciSandboxEnvironment(true), resourceName) +} + +func testAccDavinciApplicationDataSourceConfig_NotFoundByID(resourceName string, withBootstrapConfig bool) string { + return fmt.Sprintf(` + %[1]s + +data "pingone_davinci_application" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + application_id = "9c052a8a-14be-44e4-8f07-2662569994ce" // dummy ID that conforms to UUID v4 +} +`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName) +} + +// Validate any computed values when applying complete HCL +func davinciApplicationDataSource_CheckComputedValuesComplete(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "api_key.enabled", "false"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "api_key.value"), + resource.TestMatchResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "id", verify.P1DVResourceIDRegexp), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "name", resourceName), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.client_secret"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.enforce_signed_request_openid", "true"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.grant_types.#", "3"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.grant_types.*", "clientCredentials"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.grant_types.*", "authorizationCode"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.grant_types.*", "implicit"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.logout_uris.#", "1"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.logout_uris.*", "https://pingidentity.com/logout"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.redirect_uris.#", "2"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.redirect_uris.*", "https://pingidentity.com/callback"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.redirect_uris.*", "https://pingidentity.com/redirect"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.#", "3"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.*", "profile"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.*", "flow_analytics"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.*", "openid"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.sp_jwks_openid"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.sp_jwks_url", "https://pingidentity.com/jwks"), + ) +} + +func davinciApplicationDataSource_CheckComputedValuesBootstrapApplication(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "api_key.enabled", "true"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "api_key.value"), + resource.TestMatchResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "id", verify.P1DVResourceIDRegexp), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "name", "PingOne SSO Connection"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.client_secret"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.enforce_signed_request_openid"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.grant_types.#", "1"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.grant_types.*", "authorizationCode"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.logout_uris"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.redirect_uris.#", "1"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.#", "2"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.*", "profile"), + resource.TestCheckTypeSetElemAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.scopes.*", "openid"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.sp_jwks_openid"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_application.%s", resourceName), "oauth.sp_jwks_url"), + ) +} diff --git a/internal/service/davinci/data_source_davinci_applications_gen.go b/internal/service/davinci/data_source_davinci_applications_gen.go new file mode 100644 index 000000000..1436ce8f4 --- /dev/null +++ b/internal/service/davinci/data_source_davinci_applications_gen.go @@ -0,0 +1,277 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci + +import ( + "context" + "fmt" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/pingidentity/pingone-go-client/pingone" + "github.com/pingidentity/terraform-provider-pingone/internal/framework" +) + +var ( + _ datasource.DataSource = &davinciApplicationsDataSource{} + _ datasource.DataSourceWithConfigure = &davinciApplicationsDataSource{} +) + +func NewDavinciApplicationsDataSource() datasource.DataSource { + return &davinciApplicationsDataSource{} +} + +type davinciApplicationsDataSource serviceClientType + +func (r *davinciApplicationsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_davinci_applications" +} + +func (r *davinciApplicationsDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + resourceConfig, ok := req.ProviderData.(framework.ResourceType) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected the provider client, got: %T. Please report this issue to the provider maintainers.", req.ProviderData), + ) + + return + } + + r.Client = resourceConfig.Client + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialised", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.", + ) + return + } +} + +type davinciApplicationsDataSourceModel struct { + DavinciApplications types.Set `tfsdk:"davinci_applications"` + EnvironmentId types.String `tfsdk:"environment_id"` + Id types.String `tfsdk:"id"` +} + +func (r *davinciApplicationsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data source to retrieve all DaVinci applications.", + Attributes: map[string]schema.Attribute{ + "davinci_applications": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "api_key": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Computed: true, + }, + "value": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "id": schema.StringAttribute{ + Computed: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "oauth": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "client_secret": schema.StringAttribute{ + Computed: true, + }, + "enforce_signed_request_openid": schema.BoolAttribute{ + Computed: true, + }, + "grant_types": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "Options are \"authorizationCode\", \"clientCredentials\", \"implicit\".", + MarkdownDescription: "Options are `authorizationCode`, `clientCredentials`, `implicit`.", + }, + "logout_uris": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "redirect_uris": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "scopes": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "Options are \"flow_analytics\", \"offline_access\", \"openid\", \"profile\".", + MarkdownDescription: "Options are `flow_analytics`, `offline_access`, `openid`, `profile`.", + }, + "sp_jwks_openid": schema.StringAttribute{ + Computed: true, + }, + "sp_jwks_url": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + }, + }, + Computed: true, + }, + "environment_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the environment to read applications from. Must be a valid PingOne resource ID.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this data source.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + }, + } +} + +func (state *davinciApplicationsDataSourceModel) readClientResponse(response *pingone.DaVinciApplicationCollectionResponse) diag.Diagnostics { + var respDiags, diags diag.Diagnostics + // davinci_applications + davinciApplicationsApiKeyAttrTypes := map[string]attr.Type{ + "enabled": types.BoolType, + "value": types.StringType, + } + davinciApplicationsOauthAttrTypes := map[string]attr.Type{ + "client_secret": types.StringType, + "enforce_signed_request_openid": types.BoolType, + "grant_types": types.SetType{ElemType: types.StringType}, + "logout_uris": types.SetType{ElemType: types.StringType}, + "redirect_uris": types.SetType{ElemType: types.StringType}, + "scopes": types.SetType{ElemType: types.StringType}, + "sp_jwks_openid": types.StringType, + "sp_jwks_url": types.StringType, + } + davinciApplicationsAttrTypes := map[string]attr.Type{ + "api_key": types.ObjectType{AttrTypes: davinciApplicationsApiKeyAttrTypes}, + "id": types.StringType, + "name": types.StringType, + "oauth": types.ObjectType{AttrTypes: davinciApplicationsOauthAttrTypes}, + } + davinciApplicationsElementType := types.ObjectType{AttrTypes: davinciApplicationsAttrTypes} + var davinciApplicationsValues []attr.Value + for _, davinciApplicationsResponseValue := range response.Embedded.DavinciApplications { + davinciApplicationsApiKeyValue, diags := types.ObjectValue(davinciApplicationsApiKeyAttrTypes, map[string]attr.Value{ + "enabled": types.BoolValue(davinciApplicationsResponseValue.ApiKey.Enabled), + "value": types.StringValue(davinciApplicationsResponseValue.ApiKey.Value), + }) + respDiags.Append(diags...) + davinciApplicationsOauthGrantTypesValue, diags := types.SetValueFrom(context.Background(), types.StringType, davinciApplicationsResponseValue.Oauth.GrantTypes) + respDiags.Append(diags...) + davinciApplicationsOauthLogoutUrisValue, diags := types.SetValueFrom(context.Background(), types.StringType, davinciApplicationsResponseValue.Oauth.LogoutUris) + respDiags.Append(diags...) + davinciApplicationsOauthRedirectUrisValue, diags := types.SetValueFrom(context.Background(), types.StringType, davinciApplicationsResponseValue.Oauth.RedirectUris) + respDiags.Append(diags...) + davinciApplicationsOauthScopesValue, diags := types.SetValueFrom(context.Background(), types.StringType, davinciApplicationsResponseValue.Oauth.Scopes) + respDiags.Append(diags...) + davinciApplicationsOauthValue, diags := types.ObjectValue(davinciApplicationsOauthAttrTypes, map[string]attr.Value{ + "client_secret": types.StringValue(davinciApplicationsResponseValue.Oauth.ClientSecret), + "enforce_signed_request_openid": types.BoolPointerValue(davinciApplicationsResponseValue.Oauth.EnforceSignedRequestOpenid), + "grant_types": davinciApplicationsOauthGrantTypesValue, + "logout_uris": davinciApplicationsOauthLogoutUrisValue, + "redirect_uris": davinciApplicationsOauthRedirectUrisValue, + "scopes": davinciApplicationsOauthScopesValue, + "sp_jwks_openid": types.StringPointerValue(davinciApplicationsResponseValue.Oauth.SpJwksOpenid), + "sp_jwks_url": types.StringPointerValue(davinciApplicationsResponseValue.Oauth.SpjwksUrl), + }) + respDiags.Append(diags...) + davinciApplicationsValue, diags := types.ObjectValue(davinciApplicationsAttrTypes, map[string]attr.Value{ + "api_key": davinciApplicationsApiKeyValue, + "id": types.StringValue(davinciApplicationsResponseValue.Id), + "name": types.StringValue(davinciApplicationsResponseValue.Name), + "oauth": davinciApplicationsOauthValue, + }) + respDiags.Append(diags...) + davinciApplicationsValues = append(davinciApplicationsValues, davinciApplicationsValue) + } + davinciApplicationsValue, diags := types.SetValue(davinciApplicationsElementType, davinciApplicationsValues) + respDiags.Append(diags...) + state.DavinciApplications = davinciApplicationsValue + // id + state.Id = types.StringValue(uuid.New().String()) + return respDiags +} + +func (r *davinciApplicationsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data davinciApplicationsDataSourceModel + + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialized", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.") + return + } + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + environmentIdUuid, err := uuid.Parse(data.EnvironmentId.ValueString()) + if err != nil { + resp.Diagnostics.AddAttributeError( + path.Root("environment_id"), + "Attribute Validation Error", + fmt.Sprintf("The value '%s' for attribute '%s' is not a valid UUID: %s", data.EnvironmentId.ValueString(), "EnvironmentId", err.Error()), + ) + return + } + var responseData *pingone.DaVinciApplicationCollectionResponse + resp.Diagnostics.Append(framework.ParseResponse( + ctx, + + func() (any, *http.Response, error) { + fO, fR, fErr := r.Client.DaVinciApplicationsApi.GetDavinciApplications(ctx, environmentIdUuid).Execute() + return framework.CheckEnvironmentExistsOnPermissionsError(ctx, r.Client, data.EnvironmentId.ValueString(), fO, fR, fErr) + }, + "GetDavinciApplications", + framework.DefaultCustomError, + framework.DefaultRetryable, + &responseData, + )...) + + if resp.Diagnostics.HasError() { + return + } + + // Read response into the model + resp.Diagnostics.Append(data.readClientResponse(responseData)...) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/service/davinci/data_source_davinci_applications_gen_test.go b/internal/service/davinci/data_source_davinci_applications_gen_test.go new file mode 100644 index 000000000..4c59d0c16 --- /dev/null +++ b/internal/service/davinci/data_source_davinci_applications_gen_test.go @@ -0,0 +1,136 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pingidentity/terraform-provider-pingone/internal/acctest" +) + +func TestAccDavinciApplicationsDataSource_Get_Clean(t *testing.T) { + testAccDavinciApplicationsDataSource_Get(t, false) +} + +func TestAccDavinciApplicationsDataSource_Get_WithBootstrap(t *testing.T) { + testAccDavinciApplicationsDataSource_Get(t, true) +} + +func testAccDavinciApplicationsDataSource_Get(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + check := davinciApplicationsDataSource_CheckComputedValuesComplete(resourceName) + if withBootstrapConfig { + check = resource.ComposeTestCheckFunc( + check, + davinciApplicationsDataSource_CheckComputedValuesBootstrapApplication(resourceName), + ) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + CheckDestroy: davinciApplication_CheckDestroy, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciApplicationsDataSourceConfig_Get(resourceName, withBootstrapConfig), + Check: check, + }, + }, + }) +} + +func testAccDavinciApplicationsDataSourceConfig_Get(resourceName string, withBootstrapConfig bool) string { + return fmt.Sprintf(` + %[1]s + +resource "pingone_davinci_application" "%[2]s-simple" { + environment_id = data.pingone_environment.general_test.id + name = "%[2]s-simple" +} + +resource "pingone_davinci_application" "%[2]s-full" { + environment_id = data.pingone_environment.general_test.id + name = "%[2]s-full" + api_key = { + enabled = false + } + oauth = { + enforce_signed_request_openid = true + grant_types = [ + "clientCredentials", + "authorizationCode", + "implicit", + ] + logout_uris = [ + "https://pingidentity.com/logout", + ] + redirect_uris = [ + "https://pingidentity.com/callback", + "https://pingidentity.com/redirect", + ] + scopes = [ + "profile", + "flow_analytics", + "openid", + ] + sp_jwks_url = "https://pingidentity.com/jwks" + } +} + +data "pingone_davinci_applications" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + + depends_on = [ + pingone_davinci_application.%[2]s-simple, + pingone_davinci_application.%[2]s-full, + ] +} +`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName) +} + +// Validate any computed values when applying complete HCL +func davinciApplicationsDataSource_CheckComputedValuesComplete(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_applications.%s", resourceName), "id"), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_applications.%s", resourceName), "davinci_applications.*", map[string]string{ + "api_key.enabled": "true", + "name": fmt.Sprintf("%s-simple", resourceName), + "oauth.grant_types.#": "1", + "oauth.scopes.#": "2", + }), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_applications.%s", resourceName), "davinci_applications.*", map[string]string{ + "api_key.enabled": "false", + "name": fmt.Sprintf("%s-full", resourceName), + "oauth.enforce_signed_request_openid": "true", + "oauth.grant_types.#": "3", + "oauth.logout_uris.#": "1", + "oauth.redirect_uris.#": "2", + "oauth.scopes.#": "3", + "oauth.sp_jwks_url": "https://pingidentity.com/jwks", + }), + ) + +} + +func davinciApplicationsDataSource_CheckComputedValuesBootstrapApplication(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_applications.%s", resourceName), "davinci_applications.*", map[string]string{ + "api_key.enabled": "true", + "name": "PingOne SSO Connection", + "oauth.grant_types.#": "1", + "oauth.scopes.#": "2", + }), + ) +} diff --git a/internal/service/davinci/data_source_davinci_connector_gen.go b/internal/service/davinci/data_source_davinci_connector_gen.go new file mode 100644 index 000000000..fab5e81dd --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connector_gen.go @@ -0,0 +1,277 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci + +import ( + "context" + "fmt" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/pingidentity/pingone-go-client/pingone" + "github.com/pingidentity/terraform-provider-pingone/internal/framework" +) + +var ( + _ datasource.DataSource = &davinciConnectorDataSource{} + _ datasource.DataSourceWithConfigure = &davinciConnectorDataSource{} +) + +func NewDavinciConnectorDataSource() datasource.DataSource { + return &davinciConnectorDataSource{} +} + +type davinciConnectorDataSource serviceClientType + +func (r *davinciConnectorDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_davinci_connector" +} + +func (r *davinciConnectorDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + resourceConfig, ok := req.ProviderData.(framework.ResourceType) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected the provider client, got: %T. Please report this issue to the provider maintainers.", req.ProviderData), + ) + + return + } + + r.Client = resourceConfig.Client + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialised", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.", + ) + return + } +} + +type davinciConnectorDataSourceModel struct { + ConnectorId types.String `tfsdk:"connector_id"` + Description types.String `tfsdk:"description"` + EnvironmentId types.String `tfsdk:"environment_id"` + Id types.String `tfsdk:"id"` + Metadata types.Object `tfsdk:"metadata"` + Name types.String `tfsdk:"name"` + Version types.String `tfsdk:"version"` +} + +func (r *davinciConnectorDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data source to retrieve a DaVinci connector.", + Attributes: map[string]schema.Attribute{ + "connector_id": schema.StringAttribute{ + Required: true, + Description: "A string that specifies the ID of the connector to retrieve configuration for.", + }, + "description": schema.StringAttribute{ + Computed: true, + }, + "environment_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the environment that is configured with the connector. Must be a valid PingOne resource ID.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this data source.", + }, + "metadata": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "colors": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "canvas": schema.StringAttribute{ + Computed: true, + }, + "canvas_text": schema.StringAttribute{ + Computed: true, + }, + "dark": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "logos": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "canvas": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "image_file_name": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, + "type": schema.StringAttribute{ + Computed: true, + Description: "Options are \"core\", \"ping\", \"service\".", + MarkdownDescription: "Options are `core`, `ping`, `service`.", + }, + "vendor": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "version": schema.StringAttribute{ + Computed: true, + }, + }, + } +} + +func (state *davinciConnectorDataSourceModel) readClientResponse(response *pingone.DaVinciConnectorMinimalResponse) diag.Diagnostics { + var respDiags, diags diag.Diagnostics + // description + state.Description = types.StringPointerValue(response.Description) + // id + state.Id = types.StringValue(response.Id) + // metadata + metadataColorsAttrTypes := map[string]attr.Type{ + "canvas": types.StringType, + "canvas_text": types.StringType, + "dark": types.StringType, + } + metadataLogosCanvasAttrTypes := map[string]attr.Type{ + "image_file_name": types.StringType, + } + metadataLogosAttrTypes := map[string]attr.Type{ + "canvas": types.ObjectType{AttrTypes: metadataLogosCanvasAttrTypes}, + } + metadataAttrTypes := map[string]attr.Type{ + "colors": types.ObjectType{AttrTypes: metadataColorsAttrTypes}, + "logos": types.ObjectType{AttrTypes: metadataLogosAttrTypes}, + "type": types.StringType, + "vendor": types.StringType, + } + var metadataColorsValue types.Object + if response.Metadata.Colors == nil { + metadataColorsValue = types.ObjectNull(metadataColorsAttrTypes) + } else { + metadataColorsValue, diags = types.ObjectValue(metadataColorsAttrTypes, map[string]attr.Value{ + "canvas": types.StringPointerValue(response.Metadata.Colors.Canvas), + "canvas_text": types.StringPointerValue(response.Metadata.Colors.CanvasText), + "dark": types.StringPointerValue(response.Metadata.Colors.Dark), + }) + respDiags.Append(diags...) + } + var metadataLogosValue types.Object + if response.Metadata.Logos == nil { + metadataLogosValue = types.ObjectNull(metadataLogosAttrTypes) + } else { + var metadataLogosCanvasValue types.Object + if response.Metadata.Logos.Canvas == nil { + metadataLogosCanvasValue = types.ObjectNull(metadataLogosCanvasAttrTypes) + } else { + metadataLogosCanvasValue, diags = types.ObjectValue(metadataLogosCanvasAttrTypes, map[string]attr.Value{ + "image_file_name": types.StringPointerValue(response.Metadata.Logos.Canvas.ImageFileName), + }) + respDiags.Append(diags...) + } + metadataLogosValue, diags = types.ObjectValue(metadataLogosAttrTypes, map[string]attr.Value{ + "canvas": metadataLogosCanvasValue, + }) + respDiags.Append(diags...) + } + var metadataTypePtrValue *string + if response.Metadata.Type != nil { + metadataTypeStringValue := string(*response.Metadata.Type) + metadataTypePtrValue = &metadataTypeStringValue + } + metadataTypeValue := types.StringPointerValue(metadataTypePtrValue) + metadataValue, diags := types.ObjectValue(metadataAttrTypes, map[string]attr.Value{ + "colors": metadataColorsValue, + "logos": metadataLogosValue, + "type": metadataTypeValue, + "vendor": types.StringPointerValue(response.Metadata.Vendor), + }) + respDiags.Append(diags...) + state.Metadata = metadataValue + // name + state.Name = types.StringValue(response.Name) + // version + state.Version = types.StringValue(response.Version) + return respDiags +} + +func (r *davinciConnectorDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data davinciConnectorDataSourceModel + + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialized", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.") + return + } + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + environmentIdUuid, err := uuid.Parse(data.EnvironmentId.ValueString()) + if err != nil { + resp.Diagnostics.AddAttributeError( + path.Root("environment_id"), + "Attribute Validation Error", + fmt.Sprintf("The value '%s' for attribute '%s' is not a valid UUID: %s", data.EnvironmentId.ValueString(), "EnvironmentId", err.Error()), + ) + return + } + var responseData *pingone.DaVinciConnectorMinimalResponse + resp.Diagnostics.Append(framework.ParseResponse( + ctx, + + func() (any, *http.Response, error) { + fO, fR, fErr := r.Client.DaVinciConnectorsApi.GetConnectorById(ctx, environmentIdUuid, data.ConnectorId.ValueString()).Execute() + return framework.CheckEnvironmentExistsOnPermissionsError(ctx, r.Client, data.EnvironmentId.ValueString(), fO, fR, fErr) + }, + "GetConnectorById", + framework.DefaultCustomError, + framework.DefaultRetryable, + &responseData, + )...) + + if resp.Diagnostics.HasError() { + return + } + + // Read response into the model + resp.Diagnostics.Append(data.readClientResponse(responseData)...) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/service/davinci/data_source_davinci_connector_gen_test.go b/internal/service/davinci/data_source_davinci_connector_gen_test.go new file mode 100644 index 000000000..c4f53b3ca --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connector_gen_test.go @@ -0,0 +1,134 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pingidentity/terraform-provider-pingone/internal/acctest" +) + +func TestAccDavinciConnectorDataSource_ByIDFull_Clean(t *testing.T) { + testAccDavinciConnectorDataSource_ByIDFull(t, false) +} + +func TestAccDavinciConnectorDataSource_ByIDFull_WithBootstrap(t *testing.T) { + testAccDavinciConnectorDataSource_ByIDFull(t, true) +} + +func testAccDavinciConnectorDataSource_ByIDFull(t *testing.T, withBootstrap bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciConnectorDataSourceConfig_ByIDFull(resourceName, withBootstrap), + Check: davinciConnectorDataSource_CheckComputedValuesComplete(resourceName), + }, + }, + }) +} + +func TestAccDavinciConnectorDataSource_NotFound(t *testing.T) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciConnectorDataSourceConfig_NotFoundByID(resourceName), + ExpectError: regexp.MustCompile("The requested resource was not found"), + }, + }, + }) +} + +func testAccDavinciConnectorDataSourceConfig_ByIDFull(resourceName string, withBootstrap bool) string { + return fmt.Sprintf(` + %[1]s + +data "pingone_davinci_connector" "%[2]s-zendesk" { + environment_id = data.pingone_environment.general_test.id + connector_id = "connectorZendesk" +} + +data "pingone_davinci_connector" "%[2]s-pingauthadapter" { + environment_id = data.pingone_environment.general_test.id + connector_id = "pingauthadapter" +} +data "pingone_davinci_connector" "%[2]s-pingoneforms" { + environment_id = data.pingone_environment.general_test.id + connector_id = "pingOneFormsConnector" +}`, acctest.DaVinciSandboxEnvironment(withBootstrap), resourceName) +} + +func testAccDavinciConnectorDataSourceConfig_NotFoundByID(resourceName string) string { + return fmt.Sprintf(` + %[1]s + +data "pingone_davinci_connector" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + connector_id = "9c052a8a14be44e48f072662569994ce" // dummy generic ID +} +`, acctest.GenericSandboxEnvironment(), resourceName) +} + +// Validate any computed values when applying complete HCL +func davinciConnectorDataSource_CheckComputedValuesComplete(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + // Zendesk Connector + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "description", "Create, read, update and delete Zendesk users and tickets"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "id", "connectorZendesk"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "metadata.colors.canvas", "#BDD9D7"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "metadata.colors.canvas_text", "#212529"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "metadata.colors.dark", "#03363D"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "metadata.logos.canvas.image_file_name", "zendesk.svg"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "metadata.type", "service"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "metadata.vendor", "zendesk"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "name", "Zendesk"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_connector.%s-zendesk", resourceName), "version"), + // PingOne Auth Adapter Connector + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "description", "Demonstrate how Ping can authorize and optionally filter an HTTP request and response. This connector showcases PingOne Authorize API Access Management without needing to configure an API Gateway and Ping Identity integration kit.\nWARNING: You should only use this connector in a Sandbox environment."), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "id", "pingauthadapter"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "metadata.colors.canvas", "#6AC15C"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "metadata.colors.canvas_text", "#171D21"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "metadata.colors.dark", "#171D21"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "metadata.logos.canvas.image_file_name", "pingIdentity.svg"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "metadata.type", "ping"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "metadata.vendor"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "name", "PingOne Authorize - API Access Management"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_connector.%s-pingauthadapter", resourceName), "version"), + // PingOne Forms Connector + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "description", "Show an interactive form or basic message that uses your branding and selected theme."), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "id", "pingOneFormsConnector"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "metadata.colors.canvas", "#E3F0FF"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "metadata.colors.canvas_text", "#212529"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "metadata.colors.dark", "#2B90C9"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "metadata.logos.canvas.image_file_name", "pingone-forms.svg"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "metadata.type", "core"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "metadata.vendor"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "name", "Form"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_connector.%s-pingoneforms", resourceName), "version"), + ) +} diff --git a/internal/service/davinci/data_source_davinci_connector_instance_gen.go b/internal/service/davinci/data_source_davinci_connector_instance_gen.go new file mode 100644 index 000000000..f34e534ff --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connector_instance_gen.go @@ -0,0 +1,202 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/pingidentity/pingone-go-client/pingone" + "github.com/pingidentity/terraform-provider-pingone/internal/framework" +) + +var ( + _ datasource.DataSource = &davinciConnectorInstanceDataSource{} + _ datasource.DataSourceWithConfigure = &davinciConnectorInstanceDataSource{} +) + +func NewDavinciConnectorInstanceDataSource() datasource.DataSource { + return &davinciConnectorInstanceDataSource{} +} + +type davinciConnectorInstanceDataSource serviceClientType + +func (r *davinciConnectorInstanceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_davinci_connector_instance" +} + +func (r *davinciConnectorInstanceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + resourceConfig, ok := req.ProviderData.(framework.ResourceType) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected the provider client, got: %T. Please report this issue to the provider maintainers.", req.ProviderData), + ) + + return + } + + r.Client = resourceConfig.Client + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialised", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.", + ) + return + } +} + +type davinciConnectorInstanceDataSourceModel struct { + Connector types.Object `tfsdk:"connector"` + EnvironmentId types.String `tfsdk:"environment_id"` + Id types.String `tfsdk:"id"` + InstanceId types.String `tfsdk:"instance_id"` + Name types.String `tfsdk:"name"` + Properties jsontypes.Normalized `tfsdk:"properties"` +} + +func (r *davinciConnectorInstanceDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data source to retrieve a DaVinci connector instance.", + Attributes: map[string]schema.Attribute{ + "connector": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "environment_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the environment that is configured with the instance. Must be a valid PingOne resource ID.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this data source.", + }, + "instance_id": schema.StringAttribute{ + Required: true, + Description: "A string that specifies the ID of the instance to retrieve configuration for.", + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "properties": schema.StringAttribute{ + CustomType: jsontypes.NormalizedType{}, + Computed: true, + }, + }, + } +} + +func (state *davinciConnectorInstanceDataSourceModel) readClientResponse(response *pingone.DaVinciConnectorInstanceResponse) diag.Diagnostics { + var respDiags, diags diag.Diagnostics + // connector + connectorAttrTypes := map[string]attr.Type{ + "id": types.StringType, + } + connectorValue, diags := types.ObjectValue(connectorAttrTypes, map[string]attr.Value{ + "id": types.StringValue(response.Connector.Id), + }) + respDiags.Append(diags...) + state.Connector = connectorValue + // id + state.Id = types.StringValue(response.Id) + // name + state.Name = types.StringValue(response.Name) + // properties + state.Properties = jsontypes.NewNormalizedNull() + if response.Properties != nil { + propertiesBytes, err := json.Marshal(response.Properties) + if err != nil { + respDiags.AddAttributeError( + path.Root("properties"), + "Error Marshaling Properties", + fmt.Sprintf("An error occurred while marshaling the properties: %s", err.Error()), + ) + } else { + state.Properties = jsontypes.NewNormalizedValue(string(propertiesBytes)) + } + } + return respDiags +} + +func (r *davinciConnectorInstanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data davinciConnectorInstanceDataSourceModel + + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialized", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.") + return + } + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + environmentIdUuid, err := uuid.Parse(data.EnvironmentId.ValueString()) + if err != nil { + resp.Diagnostics.AddAttributeError( + path.Root("environment_id"), + "Attribute Validation Error", + fmt.Sprintf("The value '%s' for attribute '%s' is not a valid UUID: %s", data.EnvironmentId.ValueString(), "EnvironmentId", err.Error()), + ) + return + } + var responseData *pingone.DaVinciConnectorInstanceResponse + resp.Diagnostics.Append(framework.ParseResponse( + ctx, + + func() (any, *http.Response, error) { + fO, fR, fErr := r.Client.DaVinciConnectorsApi.GetConnectorInstanceById(ctx, environmentIdUuid, data.InstanceId.ValueString()).Execute() + return framework.CheckEnvironmentExistsOnPermissionsError(ctx, r.Client, data.EnvironmentId.ValueString(), fO, fR, fErr) + }, + "GetConnectorInstanceById", + framework.DefaultCustomError, + framework.DefaultRetryable, + &responseData, + )...) + + if resp.Diagnostics.HasError() { + return + } + + // Read response into the model + resp.Diagnostics.Append(data.readClientResponse(responseData)...) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/service/davinci/data_source_davinci_connector_instance_gen_test.go b/internal/service/davinci/data_source_davinci_connector_instance_gen_test.go new file mode 100644 index 000000000..0d3a11c2c --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connector_instance_gen_test.go @@ -0,0 +1,196 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pingidentity/terraform-provider-pingone/internal/acctest" + "github.com/pingidentity/terraform-provider-pingone/internal/verify" +) + +func TestAccDavinciConnectorInstanceDataSource_ByIDFull_Clean(t *testing.T) { + testAccDavinciConnectorInstanceDataSource_ByIDFull(t, false) +} + +func TestAccDavinciConnectorInstanceDataSource_ByIDFull_WithBootstrap(t *testing.T) { + testAccDavinciConnectorInstanceDataSource_ByIDFull(t, true) +} + +func testAccDavinciConnectorInstanceDataSource_ByIDFull(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + CheckDestroy: davinciConnectorInstance_CheckDestroy, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciConnectorInstanceDataSourceConfig_ByIDFull(resourceName, withBootstrapConfig), + Check: davinciConnectorInstanceDataSource_CheckComputedValuesComplete(resourceName), + }, + }, + }) +} + +func TestAccDavinciConnectorInstanceDataSource_NotFound_Clean(t *testing.T) { + testAccDavinciConnectorInstanceDataSource_NotFound(t, false) +} + +func TestAccDavinciConnectorInstanceDataSource_NotFound_WithBootstrap(t *testing.T) { + testAccDavinciConnectorInstanceDataSource_NotFound(t, true) +} + +func testAccDavinciConnectorInstanceDataSource_NotFound(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + CheckDestroy: davinciConnectorInstance_CheckDestroy, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciConnectorInstanceDataSourceConfig_NotFoundByID(resourceName, withBootstrapConfig), + ExpectError: regexp.MustCompile("The requested resource was not found"), + }, + }, + }) +} + +func TestAccDavinciConnectorInstanceDataSource_BootstrapConnectionByID_Clean(t *testing.T) { + testAccDavinciConnectorInstanceDataSource_BootstrapConnectionByID(t, false) +} + +func TestAccDavinciConnectorInstanceDataSource_BootstrapConnectionByID_WithBootstrap(t *testing.T) { + testAccDavinciConnectorInstanceDataSource_BootstrapConnectionByID(t, true) +} + +func testAccDavinciConnectorInstanceDataSource_BootstrapConnectionByID(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + testStep := resource.TestStep{ + Config: testAccDavinciConnectorInstanceDataSourceConfig_BootstrapConnection_ByID_Hcl(resourceName, withBootstrapConfig), + } + if withBootstrapConfig { + testStep.Check = davinciConnectorInstanceDataSource_CheckComputedValuesBootstrapConnection(resourceName) + } else { + testStep.ExpectError = regexp.MustCompile(`The requested resource was not found`) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + testStep, + }, + }) +} + +func testAccDavinciConnectorInstanceDataSourceConfig_Full(resourceName string) string { + return fmt.Sprintf(` +resource "pingone_davinci_connector_instance" "%[1]s" { + environment_id = data.pingone_environment.general_test.id + connector = { + id = "webhookConnector" + } + name = "%[1]s" + properties = jsonencode({ + "urls" : { + "type" : "string", + "displayName" : "Register URLs", + "createdDate" : 12345, + "customerId" : "12345", + "companyId" : "singularkey", + "preferredControlType" : "urlsTableView", + "info" : "POST requests will be made to these registered url as selected later.", + "required" : true, + "value" : [ + { + "name" : "example", + "url" : "https://pingidentity.com", + "token" : "mytoken", + "value" : "https://pingidentity.com" + } + ] + } + }) +} +`, resourceName) +} + +func testAccDavinciConnectorInstanceDataSourceConfig_ByIDFull(resourceName string, withBootstrapConfig bool) string { + return fmt.Sprintf(` + %[1]s + + %[3]s + +data "pingone_davinci_connector_instance" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + instance_id = pingone_davinci_connector_instance.%[2]s.id +}`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName, testAccDavinciConnectorInstanceDataSourceConfig_Full(resourceName)) +} + +func testAccDavinciConnectorInstanceDataSourceConfig_NotFoundByID(resourceName string, withBootstrapConfig bool) string { + return fmt.Sprintf(` + %[1]s + +data "pingone_davinci_connector_instance" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + instance_id = "9c052a8a14be44e48f072662569994ce" // dummy generic ID +} +`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName) +} + +func testAccDavinciConnectorInstanceDataSourceConfig_BootstrapConnection_ByID_Hcl(resourceName string, withBootstrapConfig bool) (hcl string) { + return fmt.Sprintf(` +%[1]s + +data "pingone_davinci_connector_instance" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + instance_id = "94141bf2f1b9b59a5f5365ff135e02bb" // the PingOne connector +} +`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName) +} + +// Validate any computed values when applying complete HCL +func davinciConnectorInstanceDataSource_CheckComputedValuesComplete(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "connector.id", "webhookConnector"), + resource.TestMatchResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "id", verify.P1DVResourceIDRegexp), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "name", resourceName), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "properties", + "{\"urls\":{\"companyId\":\"singularkey\",\"createdDate\":12345,\"customerId\":\"12345\",\"displayName\":\"Register URLs\",\"info\":\"POST requests will be made to these registered url as selected later.\",\"preferredControlType\":\"urlsTableView\",\"required\":true,\"type\":\"string\",\"value\":[{\"name\":\"example\",\"token\":\"mytoken\",\"url\":\"https://pingidentity.com\",\"value\":\"https://pingidentity.com\"}]}}"), + ) +} +func davinciConnectorInstanceDataSource_CheckComputedValuesBootstrapConnection(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "connector.id", "pingOneSSOConnector"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "id", "94141bf2f1b9b59a5f5365ff135e02bb"), + resource.TestCheckResourceAttr(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "name", "PingOne"), + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_connector_instance.%s", resourceName), "properties"), + ) +} diff --git a/internal/service/davinci/data_source_davinci_connector_instances_gen.go b/internal/service/davinci/data_source_davinci_connector_instances_gen.go new file mode 100644 index 000000000..9dfb1bfcf --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connector_instances_gen.go @@ -0,0 +1,224 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/pingidentity/pingone-go-client/pingone" + "github.com/pingidentity/terraform-provider-pingone/internal/framework" +) + +var ( + _ datasource.DataSource = &davinciConnectorInstancesDataSource{} + _ datasource.DataSourceWithConfigure = &davinciConnectorInstancesDataSource{} +) + +func NewDavinciConnectorInstancesDataSource() datasource.DataSource { + return &davinciConnectorInstancesDataSource{} +} + +type davinciConnectorInstancesDataSource serviceClientType + +func (r *davinciConnectorInstancesDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_davinci_connector_instances" +} + +func (r *davinciConnectorInstancesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + resourceConfig, ok := req.ProviderData.(framework.ResourceType) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected the provider client, got: %T. Please report this issue to the provider maintainers.", req.ProviderData), + ) + + return + } + + r.Client = resourceConfig.Client + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialised", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.", + ) + return + } +} + +type davinciConnectorInstancesDataSourceModel struct { + ConnectorInstances types.Set `tfsdk:"connector_instances"` + EnvironmentId types.String `tfsdk:"environment_id"` + Id types.String `tfsdk:"id"` +} + +func (r *davinciConnectorInstancesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data source to retrieve all DaVinci connector instances.", + Attributes: map[string]schema.Attribute{ + "connector_instances": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "connector": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "id": schema.StringAttribute{ + Computed: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "properties": schema.StringAttribute{ + CustomType: jsontypes.NormalizedType{}, + Computed: true, + }, + }, + }, + Computed: true, + }, + "environment_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the environment to read instances from. Must be a valid PingOne resource ID.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this data source.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + }, + } +} + +func (state *davinciConnectorInstancesDataSourceModel) readClientResponse(response *pingone.DaVinciConnectorInstanceCollectionResponse) diag.Diagnostics { + var respDiags, diags diag.Diagnostics + // connector_instances + connectorInstancesConnectorAttrTypes := map[string]attr.Type{ + "id": types.StringType, + } + connectorInstancesAttrTypes := map[string]attr.Type{ + "connector": types.ObjectType{AttrTypes: connectorInstancesConnectorAttrTypes}, + "id": types.StringType, + "name": types.StringType, + "properties": jsontypes.NormalizedType{}, + } + connectorInstancesElementType := types.ObjectType{AttrTypes: connectorInstancesAttrTypes} + var connectorInstancesValues []attr.Value + for _, connectorInstancesResponseValue := range response.Embedded.ConnectorInstances { + connectorInstancesConnectorValue, diags := types.ObjectValue(connectorInstancesConnectorAttrTypes, map[string]attr.Value{ + "id": types.StringValue(connectorInstancesResponseValue.Connector.Id), + }) + respDiags.Append(diags...) + connectorInstancesPropertiesValue := jsontypes.NewNormalizedNull() + if connectorInstancesResponseValue.Properties != nil { + propertiesBytes, err := json.Marshal(connectorInstancesResponseValue.Properties) + if err != nil { + respDiags.AddError( + "Error Marshaling Properties", + fmt.Sprintf("An error occurred while marshaling the connector instance properties: %s", err.Error()), + ) + } else { + connectorInstancesPropertiesValue = jsontypes.NewNormalizedValue(string(propertiesBytes)) + } + } + connectorInstancesValue, diags := types.ObjectValue(connectorInstancesAttrTypes, map[string]attr.Value{ + "connector": connectorInstancesConnectorValue, + "id": types.StringValue(connectorInstancesResponseValue.Id), + "name": types.StringValue(connectorInstancesResponseValue.Name), + "properties": connectorInstancesPropertiesValue, + }) + respDiags.Append(diags...) + connectorInstancesValues = append(connectorInstancesValues, connectorInstancesValue) + } + connectorInstancesValue, diags := types.SetValue(connectorInstancesElementType, connectorInstancesValues) + respDiags.Append(diags...) + state.ConnectorInstances = connectorInstancesValue + // id + state.Id = types.StringValue(uuid.New().String()) + return respDiags +} + +func (r *davinciConnectorInstancesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data davinciConnectorInstancesDataSourceModel + + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialized", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.") + return + } + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + environmentIdUuid, err := uuid.Parse(data.EnvironmentId.ValueString()) + if err != nil { + resp.Diagnostics.AddAttributeError( + path.Root("environment_id"), + "Attribute Validation Error", + fmt.Sprintf("The value '%s' for attribute '%s' is not a valid UUID: %s", data.EnvironmentId.ValueString(), "EnvironmentId", err.Error()), + ) + return + } + var responseData *pingone.DaVinciConnectorInstanceCollectionResponse + resp.Diagnostics.Append(framework.ParseResponse( + ctx, + + func() (any, *http.Response, error) { + fO, fR, fErr := r.Client.DaVinciConnectorsApi.GetConnectorInstances(ctx, environmentIdUuid).Execute() + return framework.CheckEnvironmentExistsOnPermissionsError(ctx, r.Client, data.EnvironmentId.ValueString(), fO, fR, fErr) + }, + "GetConnectorInstances", + framework.DefaultCustomError, + framework.DefaultRetryable, + &responseData, + )...) + + if resp.Diagnostics.HasError() { + return + } + + // Read response into the model + resp.Diagnostics.Append(data.readClientResponse(responseData)...) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/service/davinci/data_source_davinci_connector_instances_gen_test.go b/internal/service/davinci/data_source_davinci_connector_instances_gen_test.go new file mode 100644 index 000000000..efc972788 --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connector_instances_gen_test.go @@ -0,0 +1,152 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pingidentity/terraform-provider-pingone/internal/acctest" +) + +func TestAccDavinciConnectorInstancesDataSource_Get_Clean(t *testing.T) { + testAccDavinciConnectorInstancesDataSource_Get(t, false) +} + +func TestAccDavinciConnectorInstancesDataSource_Get_WithBootstrap(t *testing.T) { + testAccDavinciConnectorInstancesDataSource_Get(t, true) +} + +func testAccDavinciConnectorInstancesDataSource_Get(t *testing.T, withBootstrapConfig bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + var testCheck resource.TestCheckFunc + if withBootstrapConfig { + testCheck = resource.ComposeTestCheckFunc( + davinciConnectorInstancesDataSource_CheckComputedValuesComplete(resourceName), + davinciConnectorInstancesDataSource_CheckComputedValuesWithBootstrap(resourceName), + ) + } else { + testCheck = davinciConnectorInstancesDataSource_CheckComputedValuesComplete(resourceName) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + CheckDestroy: davinciConnectorInstance_CheckDestroy, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciConnectorInstancesDataSourceConfig_Get(resourceName, withBootstrapConfig), + Check: testCheck, + }, + }, + }) +} + +func testAccDavinciConnectorInstancesDataSourceConfig_Get(resourceName string, withBootstrapConfig bool) string { + return fmt.Sprintf(` + %[1]s + +resource "pingone_davinci_connector_instance" "%[2]s-first" { + environment_id = data.pingone_environment.general_test.id + connector = { + id = "webhookConnector" + } + name = "%[2]s-first" + properties = jsonencode({ + "urls" : { + "type" : "string", + "displayName" : "Register URLs", + "createdDate" : 12345, + "customerId" : "12345", + "companyId" : "singularkey", + "preferredControlType" : "urlsTableView", + "info" : "POST requests will be made to these registered url as selected later.", + "required" : true, + "value" : [ + { + "name" : "example", + "url" : "https://pingidentity.com", + "token" : "mytoken", + "value" : "https://pingidentity.com" + } + ] + } + }) +} + +resource "pingone_davinci_connector_instance" "%[2]s-second" { + environment_id = data.pingone_environment.general_test.id + connector = { + id = "webhookConnector" + } + name = "%[2]s-second" + properties = jsonencode({ + "urls" : { + "type" : "string", + "displayName" : "Register URLs", + "createdDate" : 12345, + "customerId" : "12345", + "companyId" : "singularkey", + "preferredControlType" : "urlsTableView", + "info" : "POST requests will be made to these registered url as selected later.", + "required" : true, + "value" : [ + { + "name" : "example", + "url" : "https://pingidentity.com", + "token" : "mytoken", + "value" : "https://pingidentity.com" + } + ] + } + }) +} + +data "pingone_davinci_connector_instances" "%[2]s" { + environment_id = data.pingone_environment.general_test.id + + depends_on = [ + pingone_davinci_connector_instance.%[2]s-first, + pingone_davinci_connector_instance.%[2]s-second, + ] +} +`, acctest.DaVinciSandboxEnvironment(withBootstrapConfig), resourceName) +} + +// Validate any computed values when applying complete HCL +func davinciConnectorInstancesDataSource_CheckComputedValuesComplete(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(fmt.Sprintf("data.pingone_davinci_connector_instances.%s", resourceName), "id"), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_connector_instances.%s", resourceName), "connector_instances.*", map[string]string{ + "connector.id": "webhookConnector", + "name": fmt.Sprintf("%s-first", resourceName), + "properties": "{\"urls\":{\"companyId\":\"singularkey\",\"createdDate\":12345,\"customerId\":\"12345\",\"displayName\":\"Register URLs\",\"info\":\"POST requests will be made to these registered url as selected later.\",\"preferredControlType\":\"urlsTableView\",\"required\":true,\"type\":\"string\",\"value\":[{\"name\":\"example\",\"token\":\"mytoken\",\"url\":\"https://pingidentity.com\",\"value\":\"https://pingidentity.com\"}]}}", + }), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_connector_instances.%s", resourceName), "connector_instances.*", map[string]string{ + "connector.id": "webhookConnector", + "name": fmt.Sprintf("%s-second", resourceName), + "properties": "{\"urls\":{\"companyId\":\"singularkey\",\"createdDate\":12345,\"customerId\":\"12345\",\"displayName\":\"Register URLs\",\"info\":\"POST requests will be made to these registered url as selected later.\",\"preferredControlType\":\"urlsTableView\",\"required\":true,\"type\":\"string\",\"value\":[{\"name\":\"example\",\"token\":\"mytoken\",\"url\":\"https://pingidentity.com\",\"value\":\"https://pingidentity.com\"}]}}", + }), + ) +} + +func davinciConnectorInstancesDataSource_CheckComputedValuesWithBootstrap(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_connector_instances.%s", resourceName), "connector_instances.*", map[string]string{ + "connector.id": "pingOneSSOConnector", + "name": "PingOne", + "id": "94141bf2f1b9b59a5f5365ff135e02bb", + }), + ) +} diff --git a/internal/service/davinci/data_source_davinci_connectors_gen.go b/internal/service/davinci/data_source_davinci_connectors_gen.go new file mode 100644 index 000000000..1cd17080d --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connectors_gen.go @@ -0,0 +1,298 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci + +import ( + "context" + "fmt" + "net/http" + "regexp" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/pingidentity/pingone-go-client/pingone" + "github.com/pingidentity/terraform-provider-pingone/internal/framework" +) + +var ( + _ datasource.DataSource = &davinciConnectorsDataSource{} + _ datasource.DataSourceWithConfigure = &davinciConnectorsDataSource{} +) + +func NewDavinciConnectorsDataSource() datasource.DataSource { + return &davinciConnectorsDataSource{} +} + +type davinciConnectorsDataSource serviceClientType + +func (r *davinciConnectorsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_davinci_connectors" +} + +func (r *davinciConnectorsDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + resourceConfig, ok := req.ProviderData.(framework.ResourceType) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected the provider client, got: %T. Please report this issue to the provider maintainers.", req.ProviderData), + ) + + return + } + + r.Client = resourceConfig.Client + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialised", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.", + ) + return + } +} + +type davinciConnectorsDataSourceModel struct { + Connectors types.Set `tfsdk:"connectors"` + EnvironmentId types.String `tfsdk:"environment_id"` + Id types.String `tfsdk:"id"` +} + +func (r *davinciConnectorsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Data source to retrieve all DaVinci connectors.", + Attributes: map[string]schema.Attribute{ + "connectors": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "description": schema.StringAttribute{ + Computed: true, + }, + "id": schema.StringAttribute{ + Computed: true, + }, + "metadata": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "colors": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "canvas": schema.StringAttribute{ + Computed: true, + }, + "canvas_text": schema.StringAttribute{ + Computed: true, + }, + "dark": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "logos": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "canvas": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "image_file_name": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, + "type": schema.StringAttribute{ + Computed: true, + Description: "Options are \"core\", \"ping\", \"service\".", + MarkdownDescription: "Options are `core`, `ping`, `service`.", + }, + "vendor": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + }, + "name": schema.StringAttribute{ + Computed: true, + }, + "version": schema.StringAttribute{ + Computed: true, + }, + }, + }, + Computed: true, + }, + "environment_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the environment to read connectors from. Must be a valid PingOne resource ID.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this data source.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), "Must be a valid UUID"), + }, + }, + }, + } +} + +func (state *davinciConnectorsDataSourceModel) readClientResponse(response *pingone.DaVinciConnectorCollectionMinimalResponse) diag.Diagnostics { + var respDiags, diags diag.Diagnostics + // connectors + connectorsMetadataColorsAttrTypes := map[string]attr.Type{ + "canvas": types.StringType, + "canvas_text": types.StringType, + "dark": types.StringType, + } + connectorsMetadataLogosCanvasAttrTypes := map[string]attr.Type{ + "image_file_name": types.StringType, + } + connectorsMetadataLogosAttrTypes := map[string]attr.Type{ + "canvas": types.ObjectType{AttrTypes: connectorsMetadataLogosCanvasAttrTypes}, + } + connectorsMetadataAttrTypes := map[string]attr.Type{ + "colors": types.ObjectType{AttrTypes: connectorsMetadataColorsAttrTypes}, + "logos": types.ObjectType{AttrTypes: connectorsMetadataLogosAttrTypes}, + "type": types.StringType, + "vendor": types.StringType, + } + connectorsAttrTypes := map[string]attr.Type{ + "description": types.StringType, + "id": types.StringType, + "metadata": types.ObjectType{AttrTypes: connectorsMetadataAttrTypes}, + "name": types.StringType, + "version": types.StringType, + } + connectorsElementType := types.ObjectType{AttrTypes: connectorsAttrTypes} + var connectorsValues []attr.Value + for _, connectorsResponseValue := range response.Embedded.Connectors { + var connectorsMetadataColorsValue types.Object + if connectorsResponseValue.Metadata.Colors == nil { + connectorsMetadataColorsValue = types.ObjectNull(connectorsMetadataColorsAttrTypes) + } else { + connectorsMetadataColorsValue, diags = types.ObjectValue(connectorsMetadataColorsAttrTypes, map[string]attr.Value{ + "canvas": types.StringPointerValue(connectorsResponseValue.Metadata.Colors.Canvas), + "canvas_text": types.StringPointerValue(connectorsResponseValue.Metadata.Colors.CanvasText), + "dark": types.StringPointerValue(connectorsResponseValue.Metadata.Colors.Dark), + }) + respDiags.Append(diags...) + } + var connectorsMetadataLogosValue types.Object + if connectorsResponseValue.Metadata.Logos == nil { + connectorsMetadataLogosValue = types.ObjectNull(connectorsMetadataLogosAttrTypes) + } else { + var connectorsMetadataLogosCanvasValue types.Object + if connectorsResponseValue.Metadata.Logos.Canvas == nil { + connectorsMetadataLogosCanvasValue = types.ObjectNull(connectorsMetadataLogosCanvasAttrTypes) + } else { + connectorsMetadataLogosCanvasValue, diags = types.ObjectValue(connectorsMetadataLogosCanvasAttrTypes, map[string]attr.Value{ + "image_file_name": types.StringPointerValue(connectorsResponseValue.Metadata.Logos.Canvas.ImageFileName), + }) + respDiags.Append(diags...) + } + connectorsMetadataLogosValue, diags = types.ObjectValue(connectorsMetadataLogosAttrTypes, map[string]attr.Value{ + "canvas": connectorsMetadataLogosCanvasValue, + }) + respDiags.Append(diags...) + } + var connectorsMetadataTypePtrValue *string + if connectorsResponseValue.Metadata.Type != nil { + connectorsMetadataTypeStringValue := string(*connectorsResponseValue.Metadata.Type) + connectorsMetadataTypePtrValue = &connectorsMetadataTypeStringValue + } + connectorsMetadataTypeValue := types.StringPointerValue(connectorsMetadataTypePtrValue) + connectorsMetadataValue, diags := types.ObjectValue(connectorsMetadataAttrTypes, map[string]attr.Value{ + "colors": connectorsMetadataColorsValue, + "logos": connectorsMetadataLogosValue, + "type": connectorsMetadataTypeValue, + "vendor": types.StringPointerValue(connectorsResponseValue.Metadata.Vendor), + }) + respDiags.Append(diags...) + connectorsValue, diags := types.ObjectValue(connectorsAttrTypes, map[string]attr.Value{ + "description": types.StringPointerValue(connectorsResponseValue.Description), + "id": types.StringValue(connectorsResponseValue.Id), + "metadata": connectorsMetadataValue, + "name": types.StringValue(connectorsResponseValue.Name), + "version": types.StringValue(connectorsResponseValue.Version), + }) + respDiags.Append(diags...) + connectorsValues = append(connectorsValues, connectorsValue) + } + connectorsValue, diags := types.SetValue(connectorsElementType, connectorsValues) + respDiags.Append(diags...) + state.Connectors = connectorsValue + // id + state.Id = types.StringValue(uuid.New().String()) + return respDiags +} + +func (r *davinciConnectorsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data davinciConnectorsDataSourceModel + + if r.Client == nil { + resp.Diagnostics.AddError( + "Client not initialized", + "Expected the PingOne client, got nil. Please report this issue to the provider maintainers.") + return + } + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + environmentIdUuid, err := uuid.Parse(data.EnvironmentId.ValueString()) + if err != nil { + resp.Diagnostics.AddAttributeError( + path.Root("environment_id"), + "Attribute Validation Error", + fmt.Sprintf("The value '%s' for attribute '%s' is not a valid UUID: %s", data.EnvironmentId.ValueString(), "EnvironmentId", err.Error()), + ) + return + } + var responseData *pingone.DaVinciConnectorCollectionMinimalResponse + resp.Diagnostics.Append(framework.ParseResponse( + ctx, + + func() (any, *http.Response, error) { + fO, fR, fErr := r.Client.DaVinciConnectorsApi.GetConnectors(ctx, environmentIdUuid).Execute() + return framework.CheckEnvironmentExistsOnPermissionsError(ctx, r.Client, data.EnvironmentId.ValueString(), fO, fR, fErr) + }, + "GetConnectors", + framework.DefaultCustomError, + framework.DefaultRetryable, + &responseData, + )...) + + if resp.Diagnostics.HasError() { + return + } + + // Read response into the model + resp.Diagnostics.Append(data.readClientResponse(responseData)...) + + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/service/davinci/data_source_davinci_connectors_gen_test.go b/internal/service/davinci/data_source_davinci_connectors_gen_test.go new file mode 100644 index 000000000..a3383980e --- /dev/null +++ b/internal/service/davinci/data_source_davinci_connectors_gen_test.go @@ -0,0 +1,93 @@ +// Copyright © 2025 Ping Identity Corporation +// Code generated by ping-terraform-plugin-framework-generator + +//go:build beta + +package davinci_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pingidentity/terraform-provider-pingone/internal/acctest" + "github.com/pingidentity/terraform-provider-pingone/internal/verify" +) + +func TestAccDavinciConnectorsDataSource_Get_Clean(t *testing.T) { + testAccDavinciConnectorsDataSource_Get(t, false) +} + +func TestAccDavinciConnectorsDataSource_Get_WithBootstrap(t *testing.T) { + testAccDavinciConnectorsDataSource_Get(t, true) +} + +func testAccDavinciConnectorsDataSource_Get(t *testing.T, withBootstrap bool) { + t.Parallel() + + resourceName := acctest.ResourceNameGen() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckBeta(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ErrorCheck: acctest.ErrorCheck(t), + Steps: []resource.TestStep{ + { + Config: testAccDavinciConnectorsDataSourceConfig_Get(resourceName, withBootstrap), + Check: davinciConnectorsDataSource_CheckComputedValuesComplete(resourceName), + }, + }, + }) +} + +func testAccDavinciConnectorsDataSourceConfig_Get(resourceName string, withBootstrap bool) string { + return fmt.Sprintf(` + %[1]s + +data "pingone_davinci_connectors" "%[2]s" { + environment_id = data.pingone_environment.general_test.id +} +`, acctest.DaVinciSandboxEnvironment(withBootstrap), resourceName) +} + +// Validate any computed values when applying complete HCL +func davinciConnectorsDataSource_CheckComputedValuesComplete(resourceName string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr(fmt.Sprintf("data.pingone_davinci_connectors.%s", resourceName), "id", verify.P1ResourceIDRegexp), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_connectors.%s", resourceName), "connectors.*", map[string]string{ + "id": "httpConnector", + "description": "Create forms and custom HTML pages, or make REST API calls.", + "metadata.colors.canvas": "#AFD5FF", + "metadata.colors.canvas_text": "#253746", + "metadata.colors.dark": "#2E5EA6", + "metadata.logos.canvas.image_file_name": "http.svg", + "metadata.type": "core", + "name": "HTTP", + }), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_connectors.%s", resourceName), "connectors.*", map[string]string{ + "id": "connector-oai-github", + "description": "Create integrations, retrieve data, and automate your workflows with the GitHub REST API.", + "metadata.colors.canvas": "#dde0de", + "metadata.colors.canvas_text": "#000000", + "metadata.colors.dark": "#50C3D5", + "metadata.logos.canvas.image_file_name": "github.svg", + "metadata.type": "service", + "metadata.vendor": "github", + "name": "GitHub API", + }), + resource.TestCheckTypeSetElemNestedAttrs(fmt.Sprintf("data.pingone_davinci_connectors.%s", resourceName), "connectors.*", map[string]string{ + "id": "connectorJiraServiceDesk", + "description": "Create, manage, and resolve service requests submitted by your customers.", + "metadata.colors.canvas": "#deebff", + "metadata.colors.canvas_text": "#000000", + "metadata.colors.dark": "#858A96", + "metadata.logos.canvas.image_file_name": "jira.svg", + "metadata.type": "service", + "metadata.vendor": "atlassian", + "name": "Jira Service Desk", + }), + ) +} diff --git a/internal/service/davinci/resource_davinci_application_key_test.go b/internal/service/davinci/resource_davinci_application_key_test.go index 790b84524..85de2218e 100644 --- a/internal/service/davinci/resource_davinci_application_key_test.go +++ b/internal/service/davinci/resource_davinci_application_key_test.go @@ -103,7 +103,7 @@ func TestAccDavinciApplicationKey_Rotate(t *testing.T) { // Initial rotation on create Config: davinciApplicationKey_FirstRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationKey_checkExpectedKey(t, resourceName, true), + davinciApplicationKey_checkExpectedKey(resourceName, true), davinciApplicationKey_CheckComputedValues(resourceName), davinciApplicationKey_GetApplicationApiKey(resourceFullName, ¤tApiKey), ), @@ -112,7 +112,7 @@ func TestAccDavinciApplicationKey_Rotate(t *testing.T) { // Expect no additional rotation Config: davinciApplicationKey_FirstNoRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationKey_checkExpectedKey(t, resourceName, false), + davinciApplicationKey_checkExpectedKey(resourceName, false), davinciApplicationKey_CheckComputedValues(resourceName), davinciApplicationKey_GetApplicationApiKey(resourceFullName, ¤tApiKey), ), @@ -121,7 +121,7 @@ func TestAccDavinciApplicationKey_Rotate(t *testing.T) { // Expect rotation Config: davinciApplicationKey_SecondRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationKey_checkExpectedKey(t, resourceName, true), + davinciApplicationKey_checkExpectedKey(resourceName, true), davinciApplicationKey_CheckComputedValues(resourceName), davinciApplicationKey_GetApplicationApiKey(resourceFullName, ¤tApiKey), ), @@ -130,7 +130,7 @@ func TestAccDavinciApplicationKey_Rotate(t *testing.T) { // Expect no additional rotation Config: davinciApplicationKey_SecondNoRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationKey_checkExpectedKey(t, resourceName, false), + davinciApplicationKey_checkExpectedKey(resourceName, false), davinciApplicationKey_CheckComputedValues(resourceName), davinciApplicationKey_GetApplicationApiKey(resourceFullName, ¤tApiKey), ), @@ -267,7 +267,7 @@ func davinciApplicationKey_CheckComputedValues(resourceName string) resource.Tes ) } -func davinciApplicationKey_checkExpectedKey(_ *testing.T, resourceName string, expectRotation bool) resource.TestCheckFunc { +func davinciApplicationKey_checkExpectedKey(resourceName string, expectRotation bool) resource.TestCheckFunc { return func(s *terraform.State) error { err := resource.TestCheckResourceAttr(fmt.Sprintf("pingone_davinci_application_key.%s", resourceName), "api_key.value", currentApiKey)(s) if err != nil && !expectRotation { diff --git a/internal/service/davinci/resource_davinci_application_secret_test.go b/internal/service/davinci/resource_davinci_application_secret_test.go index 4ca95c2af..c6721e10c 100644 --- a/internal/service/davinci/resource_davinci_application_secret_test.go +++ b/internal/service/davinci/resource_davinci_application_secret_test.go @@ -103,7 +103,7 @@ func TestAccDavinciApplicationSecret_Rotate(t *testing.T) { // Initial rotation on create Config: davinciApplicationSecret_FirstRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationSecret_checkExpectedSecret(t, resourceName, true), + davinciApplicationSecret_checkExpectedSecret(resourceName, true), davinciApplicationSecret_CheckComputedValues(resourceName), davinciApplicationSecret_GetApplicationSecret(resourceFullName, ¤tClientSecret), ), @@ -112,7 +112,7 @@ func TestAccDavinciApplicationSecret_Rotate(t *testing.T) { // Expect no additional rotation Config: davinciApplicationSecret_FirstNoRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationSecret_checkExpectedSecret(t, resourceName, false), + davinciApplicationSecret_checkExpectedSecret(resourceName, false), davinciApplicationSecret_CheckComputedValues(resourceName), davinciApplicationSecret_GetApplicationSecret(resourceFullName, ¤tClientSecret), ), @@ -121,7 +121,7 @@ func TestAccDavinciApplicationSecret_Rotate(t *testing.T) { // Expect rotation Config: davinciApplicationSecret_SecondRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationSecret_checkExpectedSecret(t, resourceName, true), + davinciApplicationSecret_checkExpectedSecret(resourceName, true), davinciApplicationSecret_CheckComputedValues(resourceName), davinciApplicationSecret_GetApplicationSecret(resourceFullName, ¤tClientSecret), ), @@ -130,7 +130,7 @@ func TestAccDavinciApplicationSecret_Rotate(t *testing.T) { // Expect no additional rotation Config: davinciApplicationSecret_SecondNoRotateHCL(resourceName), Check: resource.ComposeTestCheckFunc( - davinciApplicationSecret_checkExpectedSecret(t, resourceName, false), + davinciApplicationSecret_checkExpectedSecret(resourceName, false), davinciApplicationSecret_CheckComputedValues(resourceName), davinciApplicationSecret_GetApplicationSecret(resourceFullName, ¤tClientSecret), ), @@ -266,7 +266,7 @@ func davinciApplicationSecret_CheckComputedValues(resourceName string) resource. ) } -func davinciApplicationSecret_checkExpectedSecret(_ *testing.T, resourceName string, expectRotation bool) resource.TestCheckFunc { +func davinciApplicationSecret_checkExpectedSecret(resourceName string, expectRotation bool) resource.TestCheckFunc { return func(s *terraform.State) error { err := resource.TestCheckResourceAttr(fmt.Sprintf("pingone_davinci_application_secret.%s", resourceName), "oauth.client_secret", currentClientSecret)(s) if err != nil && !expectRotation { diff --git a/internal/service/davinci/resource_davinci_variable_gen.go b/internal/service/davinci/resource_davinci_variable_gen.go index ac9d293fb..e6e2ac416 100644 --- a/internal/service/davinci/resource_davinci_variable_gen.go +++ b/internal/service/davinci/resource_davinci_variable_gen.go @@ -313,7 +313,7 @@ func (model *davinciVariableResourceModel) buildClientStructPost() (*pingone.DaV fmt.Sprintf("The value provided for json_object could not be parsed as json: %s", err.Error()), ) } - valueValue.MapmapOfStringAny = &jsonValueMap + valueValue.Object = &jsonValueMap } if !valueAttrs["string"].IsNull() && !valueAttrs["string"].IsUnknown() { valueValue.String = valueAttrs["string"].(types.String).ValueStringPointer() @@ -393,7 +393,7 @@ func (model *davinciVariableResourceModel) buildClientStructPut() (*pingone.DaVi fmt.Sprintf("The value provided for json_object could not be parsed as json: %s", err.Error()), ) } - valueValue.MapmapOfStringAny = &jsonValueMap + valueValue.Object = &jsonValueMap } if !valueAttrs["string"].IsNull() && !valueAttrs["string"].IsUnknown() { valueValue.String = valueAttrs["string"].(types.String).ValueStringPointer() @@ -470,8 +470,8 @@ func (state *davinciVariableResourceModel) readClientResponse(response *pingone. valueValue = types.ObjectNull(valueAttrTypes) } else { jsonObjectValue := jsontypes.NewNormalizedNull() - if response.Value.MapmapOfStringAny != nil { - jsonObjectBytes, err := json.Marshal(response.Value.MapmapOfStringAny) + if response.Value.Object != nil { + jsonObjectBytes, err := json.Marshal(response.Value.Object) if err != nil { respDiags.AddAttributeError( path.Root("value").AtName("json_object"), diff --git a/internal/service/davinci/service_beta.go b/internal/service/davinci/service_beta.go index 93db7f531..815c1980c 100644 --- a/internal/service/davinci/service_beta.go +++ b/internal/service/davinci/service_beta.go @@ -21,5 +21,12 @@ func BetaResources() []func() resource.Resource { } func BetaDataSources() []func() datasource.DataSource { - return []func() datasource.DataSource{} + return []func() datasource.DataSource{ + NewDavinciApplicationDataSource, + NewDavinciApplicationsDataSource, + NewDavinciConnectorDataSource, + NewDavinciConnectorsDataSource, + NewDavinciConnectorInstanceDataSource, + NewDavinciConnectorInstancesDataSource, + } }