Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 73 additions & 2 deletions stackit/internal/services/mariadb/credential/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ var (
_ datasource.DataSource = &credentialDataSource{}
)

type DataSourceModel struct {
Id types.String `tfsdk:"id"` // needed by TF
CredentialId types.String `tfsdk:"credential_id"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Host types.String `tfsdk:"host"`
Hosts types.List `tfsdk:"hosts"`
Name types.String `tfsdk:"name"`
Password types.String `tfsdk:"password"`
Port types.Int32 `tfsdk:"port"`
Uri types.String `tfsdk:"uri"`
Username types.String `tfsdk:"username"`
}

// NewCredentialDataSource is a helper function to simplify the provider implementation.
func NewCredentialDataSource() datasource.DataSource {
return &credentialDataSource{}
Expand Down Expand Up @@ -127,7 +141,7 @@ func (r *credentialDataSource) Schema(_ context.Context, _ datasource.SchemaRequ

// Read refreshes the Terraform state with the latest data.
func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
var model Model
var model DataSourceModel
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
Expand Down Expand Up @@ -162,7 +176,7 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
ctx = core.LogResponse(ctx)

// Map response body to schema
err = mapFields(ctx, recordSetResp, &model)
err = mapDataSourceFields(ctx, recordSetResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Processing API payload: %v", err))
return
Expand All @@ -176,3 +190,60 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
}
tflog.Info(ctx, "mariadb credential read")
}

func mapDataSourceFields(ctx context.Context, credentialsResp *mariadb.CredentialsResponse, model *DataSourceModel) error {
if credentialsResp == nil {
return fmt.Errorf("response input is nil")
}
if credentialsResp.Raw == nil {
return fmt.Errorf("response credentials raw is nil")
}
if model == nil {
return fmt.Errorf("model input is nil")
}
credentials := credentialsResp.Raw.Credentials

var credentialId string
if model.CredentialId.ValueString() != "" {
credentialId = model.CredentialId.ValueString()
} else if credentialsResp.Id != "" {
credentialId = credentialsResp.Id
} else {
return fmt.Errorf("credentials id not present")
}

model.Id = utils.BuildInternalTerraformId(
model.ProjectId.ValueString(),
model.InstanceId.ValueString(),
credentialId,
)

modelHosts, err := utils.ListValueToStringSlice(model.Hosts)
if err != nil {
return err
}

model.Hosts = types.ListNull(types.StringType)
model.CredentialId = types.StringValue(credentialId)

if credentials.Hosts != nil {
respHosts := credentials.Hosts

reconciledHosts := utils.ReconcileStringSlices(modelHosts, respHosts)

hostsTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledHosts)
if diags.HasError() {
return fmt.Errorf("failed to map hosts: %w", core.DiagsToError(diags))
}

model.Hosts = hostsTF
}
model.Host = types.StringValue(credentials.Host)
model.Name = types.StringPointerValue(credentials.Name)
model.Password = types.StringValue(credentials.Password)
model.Port = types.Int32PointerValue(credentials.Port)
model.Uri = types.StringPointerValue(credentials.Uri)
model.Username = types.StringValue(credentials.Username)

return nil
}
220 changes: 220 additions & 0 deletions stackit/internal/services/mariadb/credential/datasource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package mariadb

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
mariadb "github.com/stackitcloud/stackit-sdk-go/services/mariadb/v1api"
)

func TestMapDataSourceFields(t *testing.T) {
tests := []struct {
description string
state DataSourceModel
input *mariadb.CredentialsResponse
expected DataSourceModel
isValid bool
}{
{
"default_values",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
&mariadb.CredentialsResponse{
Id: "cid",
Raw: &mariadb.RawCredentials{},
},
DataSourceModel{
Id: types.StringValue("pid,iid,cid"),
CredentialId: types.StringValue("cid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Host: types.StringValue(""),
Hosts: types.ListNull(types.StringType),
Name: types.StringNull(),
Password: types.StringValue(""),
Port: types.Int32Null(),
Uri: types.StringNull(),
Username: types.StringValue(""),
},
true,
},
{
"simple_values",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
&mariadb.CredentialsResponse{
Id: "cid",
Raw: &mariadb.RawCredentials{
Credentials: mariadb.Credentials{
Host: "host",
Hosts: []string{
"host_1",
"",
},
Name: new("name"),
Password: "password",
Port: new(int32(1234)),
Uri: new("uri"),
Username: "username",
},
},
},
DataSourceModel{
Id: types.StringValue("pid,iid,cid"),
CredentialId: types.StringValue("cid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Host: types.StringValue("host"),
Hosts: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("host_1"),
types.StringValue(""),
}),
Name: types.StringValue("name"),
Password: types.StringValue("password"),
Port: types.Int32Value(1234),
Uri: types.StringValue("uri"),
Username: types.StringValue("username"),
},
true,
},
{
"hosts_unordered",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Hosts: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("host_2"),
types.StringValue(""),
types.StringValue("host_1"),
}),
},
&mariadb.CredentialsResponse{
Id: "cid",
Raw: &mariadb.RawCredentials{
Credentials: mariadb.Credentials{
Host: "host",
Hosts: []string{
"",
"host_1",
"host_2",
},
Name: new("name"),
Password: "password",
Port: new(int32(1234)),
Uri: new("uri"),
Username: "username",
},
},
},
DataSourceModel{
Id: types.StringValue("pid,iid,cid"),
CredentialId: types.StringValue("cid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Host: types.StringValue("host"),
Hosts: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("host_2"),
types.StringValue(""),
types.StringValue("host_1"),
}),
Name: types.StringValue("name"),
Password: types.StringValue("password"),
Port: types.Int32Value(1234),
Uri: types.StringValue("uri"),
Username: types.StringValue("username"),
},
true,
},
{
"null_fields_and_int_conversions",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
&mariadb.CredentialsResponse{
Id: "cid",
Raw: &mariadb.RawCredentials{
Credentials: mariadb.Credentials{
Host: "",
Hosts: []string{},
Name: nil,
Password: "",
Port: new(int32(2123456789)),
Uri: nil,
Username: "",
},
},
},
DataSourceModel{
Id: types.StringValue("pid,iid,cid"),
CredentialId: types.StringValue("cid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Host: types.StringValue(""),
Hosts: types.ListValueMust(types.StringType, []attr.Value{}),
Name: types.StringNull(),
Password: types.StringValue(""),
Port: types.Int32Value(2123456789),
Uri: types.StringNull(),
Username: types.StringValue(""),
},
true,
},
{
"nil_response",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
nil,
DataSourceModel{},
false,
},
{
"no_resource_id",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
&mariadb.CredentialsResponse{},
DataSourceModel{},
false,
},
{
"nil_raw_credential",
DataSourceModel{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
&mariadb.CredentialsResponse{
Id: "cid",
},
DataSourceModel{},
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
err := mapDataSourceFields(context.Background(), tt.input, &tt.state)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(tt.state, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
Loading
Loading