From 7977ad832bfa466bbd0147ac8c5fb17857d88760 Mon Sep 17 00:00:00 2001 From: Cameron Thornton Date: Mon, 28 Jul 2025 11:24:00 -0500 Subject: [PATCH] FW aigee keystores aliases key cert file --- mmv1/products/datafusion/Instance.yaml | 2 +- .../services/apigee/fw_apigee_utils.go | 117 ++++ ..._apigee_keystores_aliases_key_cert_file.go | 575 ++++++++++++++++++ 3 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 mmv1/third_party/terraform/services/apigee/fw_apigee_utils.go create mode 100644 mmv1/third_party/terraform/services/apigee/fw_resource_apigee_keystores_aliases_key_cert_file.go diff --git a/mmv1/products/datafusion/Instance.yaml b/mmv1/products/datafusion/Instance.yaml index f7a2f5d7cfed..2809e987d1ed 100644 --- a/mmv1/products/datafusion/Instance.yaml +++ b/mmv1/products/datafusion/Instance.yaml @@ -13,7 +13,7 @@ --- name: 'Instance' -plugin_framework: true +# plugin_framework: true description: | Represents a Data Fusion instance. references: diff --git a/mmv1/third_party/terraform/services/apigee/fw_apigee_utils.go b/mmv1/third_party/terraform/services/apigee/fw_apigee_utils.go new file mode 100644 index 000000000000..065a3862bda8 --- /dev/null +++ b/mmv1/third_party/terraform/services/apigee/fw_apigee_utils.go @@ -0,0 +1,117 @@ +package apigee + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-log/tflog" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + "google.golang.org/api/googleapi" +) + +func RetryWithContext(ctx context.Context, opt transport_tpg.RetryOptions) error { + doneCh := make(chan error, 1) + + go func() { + doneCh <- transport_tpg.Retry(opt) + }() + + select { + case err := <-doneCh: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + +func sendRequestRawBodyFramework(ctx context.Context, opts SendRequestRawBodyOptions) (map[string]interface{}, diag.Diagnostics) { + var diags diag.Diagnostics + + tflog.Trace(ctx, "Executing raw body request", map[string]interface{}{ + "url": opts.RawURL, + "method": opts.Method, + }) + + reqHeaders := make(http.Header) + reqHeaders.Set("User-Agent", opts.UserAgent) + reqHeaders.Set("Content-Type", opts.ContentType) + + if opts.Config.UserProjectOverride && opts.Project != "" { + reqHeaders.Set("X-Goog-User-Project", opts.Project) + } + + timeout := opts.Timeout + if timeout == 0 { + timeout = 2 * time.Minute + } + + var httpResp *http.Response + + err := RetryWithContext(ctx, transport_tpg.RetryOptions{ + RetryFunc: func() error { + client := opts.Config.Client + + if bodySeeker, ok := opts.Body.(io.ReadSeeker); ok { + bodySeeker.Seek(0, io.SeekStart) + } + + httpReq, err := http.NewRequestWithContext(ctx, opts.Method, opts.RawURL, opts.Body) + if err != nil { + return fmt.Errorf("error creating HTTP request: %w", err) + } + httpReq.Header = reqHeaders + + httpResp, err = client.Do(httpReq) + if err != nil { + return fmt.Errorf("error sending request: %w", err) + } + + if err := googleapi.CheckResponse(httpResp); err != nil { + return err + } + + return nil + }, + Timeout: timeout, + }) + + if err != nil { + diags.AddError("API Request Failed", fmt.Sprintf("request to %s failed with retries: %s", opts.RawURL, err.Error())) + return nil, diags + } + + if httpResp == nil { + diags.AddError("API Response Error", "Request was successful, but the HTTP response was nil.") + return nil, diags + } + defer googleapi.CloseBody(httpResp) + + if httpResp.StatusCode == http.StatusNoContent { + return nil, diags + } + + result := make(map[string]interface{}) + if err := json.NewDecoder(httpResp.Body).Decode(&result); err != nil { + diags.AddError("API Response Decode Error", fmt.Sprintf("failed to decode JSON response body: %s", err.Error())) + return nil, diags + } + + tflog.Trace(ctx, "Raw body request successful") + return result, diags +} + +type SendRequestRawBodyOptions struct { + Config *transport_tpg.Config + Method string + Project string + RawURL string + UserAgent string + Body io.Reader + ContentType string + Timeout time.Duration +} diff --git a/mmv1/third_party/terraform/services/apigee/fw_resource_apigee_keystores_aliases_key_cert_file.go b/mmv1/third_party/terraform/services/apigee/fw_resource_apigee_keystores_aliases_key_cert_file.go new file mode 100644 index 000000000000..4151fd7f1929 --- /dev/null +++ b/mmv1/third_party/terraform/services/apigee/fw_resource_apigee_keystores_aliases_key_cert_file.go @@ -0,0 +1,575 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: Handwritten *** +// +// ---------------------------------------------------------------------------- +// +// This code is generated by Magic Modules using the following: +// +// Source file: https://github.com/GoogleCloudPlatform/magic-modules/tree/main/mmv1/third_party/terraform/services/apigee/fw_resource_apigee_keystores_aliases_key_cert_file.go +// +// DO NOT EDIT this file directly. Any changes made to this file will be +// overwritten during the next generation cycle. +// +// ---------------------------------------------------------------------------- +package apigee + +import ( + "bytes" + "context" + "fmt" + "mime/multipart" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-provider-google/google/fwmodels" + "github.com/hashicorp/terraform-provider-google/google/fwtransport" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +var ( + _ resource.Resource = &ApigeeKeystoresAliasesKeyCertFileResource{} + _ resource.ResourceWithConfigure = &ApigeeKeystoresAliasesKeyCertFileResource{} +) + +func NewApigeeKeystoresAliasesKeyCertFileResource() resource.Resource { + return &ApigeeKeystoresAliasesKeyCertFileResource{} +} + +type ApigeeKeystoresAliasesKeyCertFileResource struct { + providerConfig *transport_tpg.Config +} + +type ApigeeKeystoresAliasesKeyCertFileResourceModel struct { + Id types.String `tfsdk:"id"` + OrgId types.String `tfsdk:"org_id"` + Environment types.String `tfsdk:"environment"` + Keystore types.String `tfsdk:"keystore"` + Alias types.String `tfsdk:"alias"` + Cert types.String `tfsdk:"cert"` + Key types.String `tfsdk:"key"` + Password types.String `tfsdk:"password"` + Type types.String `tfsdk:"type"` + CertsInfo *CertsInfoModel `tfsdk:"certs_info"` +} + +type CertsInfoModel struct { + CertInfo []CertInfoDetailModel `tfsdk:"cert_info"` +} + +type CertInfoDetailModel struct { + BasicConstraints types.String `tfsdk:"basic_constraints"` + ExpiryDate types.String `tfsdk:"expiry_date"` + IsValid types.String `tfsdk:"is_valid"` + Issuer types.String `tfsdk:"issuer"` + PublicKey types.String `tfsdk:"public_key"` + SerialNumber types.String `tfsdk:"serial_number"` + SigAlgName types.String `tfsdk:"sig_alg_name"` + Subject types.String `tfsdk:"subject"` + SubjectAlternativeNames types.List `tfsdk:"subject_alternative_names"` + ValidFrom types.String `tfsdk:"valid_from"` + Version types.Int64 `tfsdk:"version"` +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_fw_apigee_keystores_aliases_key_cert_file" +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + p, ok := req.ProviderData.(*transport_tpg.Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *transport_tpg.Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + r.providerConfig = p +} + +type useComputedBlockAsState struct{} + +func (m useComputedBlockAsState) Description(ctx context.Context) string { + return "For a computed-only block, this plan modifier preserves the state's value in the plan." +} + +func (m useComputedBlockAsState) MarkdownDescription(ctx context.Context) string { + return m.Description(ctx) +} + +func (m useComputedBlockAsState) PlanModifyObject(ctx context.Context, req planmodifier.ObjectRequest, resp *planmodifier.ObjectResponse) { + // Do nothing if the resource is being created. + if req.State.Raw.IsNull() { + return + } + + // Do nothing if the config is not null. This means the user has configured the block, + // so we should not interfere. + if !req.ConfigValue.IsNull() { + return + } + + // Do nothing if the plan is already not null. This can happen if another plan modifier + // has already set the value. + if !req.PlanValue.IsNull() { + return + } + + // If the config is null and the state has a value, we want to use the state's value in the plan. + // This is the primary purpose of this plan modifier. + if !req.StateValue.IsNull() { + resp.PlanValue = req.StateValue + } +} + +func UseComputedBlockAsState() planmodifier.Object { + return useComputedBlockAsState{} +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "An alias from a key/cert file.", + Attributes: map[string]schema.Attribute{ + "org_id": schema.StringAttribute{ + Description: "Organization ID associated with the alias.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "environment": schema.StringAttribute{ + Description: "Environment associated with the alias.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "keystore": schema.StringAttribute{ + Description: "Keystore Name.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "alias": schema.StringAttribute{ + Description: "Alias Name.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "cert": schema.StringAttribute{ + Description: "Cert content.", + Required: true, + }, + "key": schema.StringAttribute{ + Description: "Private Key content, omit if uploading to truststore.", + Optional: true, + Sensitive: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "password": schema.StringAttribute{ + Description: "Password for the Private Key if it's encrypted.", + Optional: true, + Sensitive: true, + }, + "type": schema.StringAttribute{ + Description: "Optional. Type of Alias.", + Computed: true, + }, + "id": schema.StringAttribute{ + Description: "Project identifier", + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + "certs_info": schema.SingleNestedBlock{ + Description: "Chain of certificates under this alias.", + PlanModifiers: []planmodifier.Object{ + UseComputedBlockAsState(), + }, + Blocks: map[string]schema.Block{ + "cert_info": schema.ListNestedBlock{ + Description: "List of all properties in the object.", + NestedObject: schema.NestedBlockObject{ + PlanModifiers: []planmodifier.Object{ + UseComputedBlockAsState(), + }, + Attributes: map[string]schema.Attribute{ + "basic_constraints": schema.StringAttribute{ + Description: "X.509 basic constraints extension.", + Optional: true, + Computed: true, + }, + "expiry_date": schema.StringAttribute{ + Description: "X.509 notAfter validity period in milliseconds since epoch.", + Optional: true, + Computed: true, + }, + "is_valid": schema.StringAttribute{ + Description: "Flag that specifies whether the certificate is valid. Flag is set to Yes if the certificate is valid, No if expired, or Not yet if not yet valid.", + Optional: true, + Computed: true, + }, + "issuer": schema.StringAttribute{ + Description: "X.509 issuer.", + Optional: true, + Computed: true, + }, + "public_key": schema.StringAttribute{ + Description: "Public key component of the X.509 subject public key info.", + Optional: true, + Computed: true, + }, + "serial_number": schema.StringAttribute{ + Description: "X.509 serial number.", + Optional: true, + Computed: true, + }, + "sig_alg_name": schema.StringAttribute{ + Description: "X.509 signatureAlgorithm.", + Optional: true, + Computed: true, + }, + "subject": schema.StringAttribute{ + Description: "X.509 subject.", + Optional: true, + Computed: true, + }, + "subject_alternative_names": schema.ListAttribute{ + Description: "X.509 subject alternative names (SANs) extension.", + Optional: true, + Computed: true, + ElementType: types.StringType, + }, + "valid_from": schema.StringAttribute{ + Description: "X.509 notBefore validity period in milliseconds since epoch.", + Optional: true, + Computed: true, + }, + "version": schema.Int64Attribute{ + Description: "X.509 version.", + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan ApigeeKeystoresAliasesKeyCertFileResourceModel + var metaData *fwmodels.ProviderMetaModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.ProviderMeta.Get(ctx, &metaData)...) + if resp.Diagnostics.HasError() { + return + } + + buf := new(bytes.Buffer) + bw := multipart.NewWriter(buf) + if !plan.Key.IsNull() && !plan.Key.IsUnknown() { + keyFilePartWriter, _ := bw.CreateFormField("keyFile") + keyFilePartWriter.Write([]byte(plan.Key.ValueString())) + } + if !plan.Password.IsNull() && !plan.Password.IsUnknown() { + keyFilePartWriter, _ := bw.CreateFormField("password") + keyFilePartWriter.Write([]byte(plan.Password.ValueString())) + } + certFilePartWriter, _ := bw.CreateFormField("certFile") + certFilePartWriter.Write([]byte(plan.Cert.ValueString())) + bw.Close() + + billingProject := types.StringValue(r.providerConfig.BillingProject) + + var schemaDefaultVals fwtransport.DefaultVars + + userAgent := fwtransport.GenerateFrameworkUserAgentString(metaData, r.providerConfig.UserAgent) + url := fwtransport.ReplaceVars(ctx, req, &resp.Diagnostics, schemaDefaultVals, r.providerConfig, "{{ApigeeBasePath}}organizations/{{org_id}}/environments/{{environment}}/keystores/{{keystore}}/aliases?format=keycertfile&alias={{alias}}&ignoreExpiryValidation=true") + if resp.Diagnostics.HasError() { + return + } + res, diags := sendRequestRawBodyFramework(ctx, SendRequestRawBodyOptions{ + Config: r.providerConfig, + Method: "POST", + Project: billingProject.ValueString(), + RawURL: url, + UserAgent: userAgent, + Body: buf, + ContentType: bw.FormDataContentType(), + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Trace(ctx, "Successfully created Apigee Keystore Alias", map[string]interface{}{"response": res}) + + id := fmt.Sprintf("organizations/%s/environments/%s/keystores/%s/aliases/%s", + plan.OrgId.ValueString(), + plan.Environment.ValueString(), + plan.Keystore.ValueString(), + plan.Alias.ValueString(), + ) + plan.Id = types.StringValue(id) + + r.refresh(ctx, req, &plan, &resp.State, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ApigeeKeystoresAliasesKeyCertFileResourceModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + r.refresh(ctx, req, &state, &resp.State, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ApigeeKeystoresAliasesKeyCertFileResourceModel + var state ApigeeKeystoresAliasesKeyCertFileResourceModel + var metaData *fwmodels.ProviderMetaModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(req.ProviderMeta.Get(ctx, &metaData)...) + if resp.Diagnostics.HasError() { + return + } + + buf := new(bytes.Buffer) + bw := multipart.NewWriter(buf) + certFilePartWriter, err := bw.CreateFormField("certFile") + if err != nil { + resp.Diagnostics.AddError("Unable to create form field for certificate", err.Error()) + return + } + certFilePartWriter.Write([]byte(plan.Cert.ValueString())) + bw.Close() + + billingProject := types.StringValue(r.providerConfig.BillingProject) + userAgent := fwtransport.GenerateFrameworkUserAgentString(metaData, r.providerConfig.UserAgent) + + var schemaDefaultVals fwtransport.DefaultVars + url := fwtransport.ReplaceVars(ctx, &req, &resp.Diagnostics, schemaDefaultVals, r.providerConfig, "{{ApigeeBasePath}}organizations/{{org_id}}/environments/{{environment}}/keystores/{{keystore}}/aliases/{{alias}}?ignoreExpiryValidation=true") + if resp.Diagnostics.HasError() { + return + } + + tflog.Trace(ctx, "Updating Apigee Keystore Alias", map[string]interface{}{"url": url}) + + res, diags := sendRequestRawBodyFramework(ctx, SendRequestRawBodyOptions{ + Config: r.providerConfig, + Method: "PUT", + Project: billingProject.ValueString(), + RawURL: url, + UserAgent: userAgent, + Body: buf, + ContentType: bw.FormDataContentType(), + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Trace(ctx, "Successfully sent update request for Apigee Keystore Alias", map[string]interface{}{"response": res}) + + r.refresh(ctx, &req, &plan, &resp.State, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data ApigeeKeystoresAliasesKeyCertFileResourceModel + var metaData *fwmodels.ProviderMetaModel + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(req.ProviderMeta.Get(ctx, &metaData)...) + if resp.Diagnostics.HasError() { + return + } + + userAgent := fwtransport.GenerateFrameworkUserAgentString(metaData, r.providerConfig.UserAgent) + + var schemaDefaultVals fwtransport.DefaultVars + url := fwtransport.ReplaceVars(ctx, req, &resp.Diagnostics, schemaDefaultVals, r.providerConfig, "{{ApigeeBasePath}}organizations/{{org_id}}/environments/{{environment}}/keystores/{{keystore}}/aliases/{{alias}}") + if resp.Diagnostics.HasError() { + return + } + + tflog.Trace(ctx, "Deleting Apigee Keystore Alias", map[string]interface{}{"url": url}) + + _ = fwtransport.SendRequest(fwtransport.SendRequestOptions{ + Config: r.providerConfig, + Method: "DELETE", + Project: data.OrgId.ValueString(), + RawURL: url, + UserAgent: userAgent, + }, &resp.Diagnostics) + + tflog.Trace(ctx, "Successfully deleted Apigee Keystore Alias.") +} + +func (r *ApigeeKeystoresAliasesKeyCertFileResource) refresh(ctx context.Context, req interface{}, data *ApigeeKeystoresAliasesKeyCertFileResourceModel, state *tfsdk.State, diags *diag.Diagnostics) { + var metaData *fwmodels.ProviderMetaModel + + userAgent := fwtransport.GenerateFrameworkUserAgentString(metaData, r.providerConfig.UserAgent) + + var schemaDefaultVals fwtransport.DefaultVars + url := fwtransport.ReplaceVars(ctx, req, diags, schemaDefaultVals, r.providerConfig, "{{ApigeeBasePath}}organizations/{{org_id}}/environments/{{environment}}/keystores/{{keystore}}/aliases/{{alias}}") + if diags.HasError() { + return + } + + tflog.Trace(ctx, "Refreshing Apigee Keystore Alias", map[string]interface{}{"url": url}) + + res := fwtransport.SendRequest(fwtransport.SendRequestOptions{ + Config: r.providerConfig, + Method: "GET", + Project: data.OrgId.ValueString(), + RawURL: url, + UserAgent: userAgent, + }, diags) + + if diags.HasError() { + return + } + + tflog.Trace(ctx, "Successfully refreshed Apigee Keystore Alias", map[string]interface{}{"response": res}) + + data.Type = types.StringValue(res["type"].(string)) + + // flattenedCertsInfo, certDiags := flattenCertsInfo(res["certsInfo"]) + // diags.Append(certDiags...) + // if diags.HasError() { + // return + // } + // data.CertsInfo = flattenedCertsInfo +} + +func flattenCertsInfo(v interface{}) (*CertsInfoModel, diag.Diagnostics) { + if v == nil { + return nil, nil + } + + var diags diag.Diagnostics + + certsInfoMap, ok := v.(map[string]interface{}) + if !ok { + diags.AddError("Invalid Type", "Cannot flatten certs_info: input is not a map.") + return nil, diags + } + if len(certsInfoMap) == 0 { + return nil, nil + } + + certInfoListRaw, ok := certsInfoMap["certInfo"].([]interface{}) + if !ok || len(certInfoListRaw) == 0 { + return nil, nil + } + + var certInfoDetails []CertInfoDetailModel + for _, rawCertInfo := range certInfoListRaw { + certInfo, ok := rawCertInfo.(map[string]interface{}) + if !ok || len(certInfo) == 0 { + continue + } + getStringValue := func(key string) types.String { + if val, ok := certInfo[key].(string); ok { + return types.StringValue(val) + } + return types.StringNull() + } + var sansValue types.List + if sansRaw, ok := certInfo["subjectAlternativeNames"].([]interface{}); ok { + sans := make([]string, 0, len(sansRaw)) + for _, san := range sansRaw { + if s, ok := san.(string); ok { + sans = append(sans, s) + } + } + var listDiags diag.Diagnostics + sansValue, listDiags = types.ListValueFrom(context.Background(), types.StringType, sans) + diags.Append(listDiags...) + } else { + sansValue = types.ListNull(types.StringType) + } + var versionValue types.Int64 + if versionRaw, ok := certInfo["version"]; ok { + switch v := versionRaw.(type) { + case float64: + versionValue = types.Int64Value(int64(v)) + case string: + versionValue = types.Int64Null() + default: + versionValue = types.Int64Null() + } + } else { + versionValue = types.Int64Null() + } + detail := CertInfoDetailModel{ + BasicConstraints: getStringValue("basicConstraints"), + ExpiryDate: getStringValue("expiryDate"), + IsValid: getStringValue("isValid"), + Issuer: getStringValue("issuer"), + PublicKey: getStringValue("publicKey"), + SerialNumber: getStringValue("serialNumber"), + SigAlgName: getStringValue("sigAlgName"), + Subject: getStringValue("subject"), + ValidFrom: getStringValue("validFrom"), + SubjectAlternativeNames: sansValue, + Version: versionValue, + } + certInfoDetails = append(certInfoDetails, detail) + } + + if diags.HasError() { + return nil, diags + } + + return &CertsInfoModel{ + CertInfo: certInfoDetails, + }, nil +}