internal/utils/utils.go (188 lines of code) (raw):

package utils import ( "context" "crypto/sha1" "encoding/json" "fmt" "reflect" "strings" "time" providerSchema "github.com/elastic/terraform-provider-elasticstack/internal/schema" fwdiag "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-log/tflog" sdkdiag "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // Compares the JSON in two byte slices func JSONBytesEqual(a, b []byte) (bool, error) { var j, j2 interface{} if err := json.Unmarshal(a, &j); err != nil { return false, err } if err := json.Unmarshal(b, &j2); err != nil { return false, err } return MapsEqual(j, j2), nil } func MapsEqual(m1, m2 interface{}) bool { return reflect.DeepEqual(m2, m1) } // Flattens the multilevel map, and concatenates keys together with dot "." // # Examples // map of form: // // map := map[string]interface{}{ // "index": map[string]interface{}{ // "key": 1 // } // } // // becomes: // // map := map[string]interface{}{ // "index.key": 1 // } func FlattenMap(m map[string]interface{}) map[string]interface{} { out := make(map[string]interface{}) var flattener func(string, map[string]interface{}, map[string]interface{}) flattener = func(k string, src, dst map[string]interface{}) { if len(k) > 0 { k += "." } for key, v := range src { switch inner := v.(type) { case map[string]interface{}: flattener(k+key, inner, dst) default: dst[k+key] = v } } } flattener("", m, out) return out } func MergeSchemaMaps(maps ...map[string]*schema.Schema) map[string]*schema.Schema { result := make(map[string]*schema.Schema) for _, m := range maps { for k, v := range m { result[k] = v } } return result } func IsEmpty(v interface{}) bool { switch t := v.(type) { case int, int8, int16, int32, int64, float32, float64: if t == 0 { return true } case string: if strings.TrimSpace(t) == "" { return true } case []interface{}: if len(t) == 0 { return true } case map[interface{}]interface{}: if len(t) == 0 { return true } case nil: return true } return false } const connectionKeyName = "elasticsearch_connection" // Returns the common connection schema for all the Elasticsearch resources, // which defines the fields which can be used to configure the API access func AddConnectionSchema(providedSchema map[string]*schema.Schema) { providedSchema[connectionKeyName] = providerSchema.GetEsConnectionSchema(connectionKeyName, false) } func StringToHash(s string) (*string, error) { h := sha1.New() _, err := h.Write([]byte(s)) if err != nil { return nil, err } bs := h.Sum(nil) hash := fmt.Sprintf("%x", bs) return &hash, nil } func FormatStrictDateTime(t time.Time) string { strictDateTime := t.Format("2006-01-02T15:04:05.000Z") return strictDateTime } func ExpandIndividuallyDefinedSettings(ctx context.Context, d *schema.ResourceData, settingsKeys map[string]schema.ValueType) map[string]interface{} { settings := make(map[string]interface{}) for key := range settingsKeys { tfFieldKey := ConvertSettingsKeyToTFFieldKey(key) if raw, ok := d.GetOk(tfFieldKey); ok { switch field := raw.(type) { case *schema.Set: settings[key] = field.List() default: settings[key] = raw } tflog.Trace(ctx, fmt.Sprintf("expandIndividuallyDefinedSettings: settingsKey:%+v tfFieldKey:%+v value:%+v, %+v", key, tfFieldKey, raw, settings)) } } return settings } func ConvertSettingsKeyToTFFieldKey(settingKey string) string { return strings.ReplaceAll(settingKey, ".", "_") } func Pointer[T any](value T) *T { return &value } // MapRef is similar to Pointer, in that it takes the reference of // the given value, however if the value is already nil then it returns // nil rather than a pointer to nil. func MapRef[T any, M ~map[string]T](value M) *M { if value == nil { return nil } return &value } // SliceRef is similar to Pointer, in that it takes the reference of // the given value, however if the value is already nil then it returns // nil rather than a pointer to nil. func SliceRef[T any, S ~[]T](value S) *S { if value == nil { return nil } return &value } // Deref returns the value referenced by the given pointer. If the value is // nil, a zero value is returned. func Deref[T any](value *T) T { if value == nil { var zero T return zero } else { return *value } } // Itol converts *int to *in64. func Itol(value *int) *int64 { if value == nil { return nil } return Pointer(int64(*value)) } // Ltoi converts *int64 to *int. func Ltoi(value *int64) *int { if value == nil { return nil } return Pointer(int(*value)) } func FlipMap[K comparable, V comparable](m map[K]V) map[V]K { inv := make(map[V]K) for k, v := range m { inv[v] = k } return inv } func SdkDiagsAsError(diags sdkdiag.Diagnostics) error { for _, diag := range diags { if diag.Severity == sdkdiag.Error { return fmt.Errorf("%s: %s", diag.Summary, diag.Detail) } } return nil } func FwDiagsAsError(diags fwdiag.Diagnostics) error { for _, diag := range diags { if diag.Severity() == fwdiag.SeverityError { return fmt.Errorf("%s: %s", diag.Summary(), diag.Detail()) } } return nil } // ConvertToAttrDiags wraps an existing collection of diagnostics with an attribute path. func ConvertToAttrDiags(diags fwdiag.Diagnostics, path path.Path) fwdiag.Diagnostics { var nd fwdiag.Diagnostics for _, d := range diags { if d.Severity() == fwdiag.SeverityError { nd.AddAttributeError(path, d.Summary(), d.Detail()) } else if d.Severity() == fwdiag.SeverityWarning { nd.AddAttributeWarning(path, d.Summary(), d.Detail()) } else { nd.Append(d) } } return nd }