162 lines
4.5 KiB
Go
162 lines
4.5 KiB
Go
package convert
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/hashicorp/go-cty/cty"
|
|
|
|
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
|
|
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
|
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
|
"github.com/hashicorp/terraform-plugin-sdk/v2/internal/logging"
|
|
)
|
|
|
|
// AppendProtoDiag appends a new diagnostic from a warning string or an error.
|
|
// This panics if d is not a string or error.
|
|
func AppendProtoDiag(ctx context.Context, diags []*tfprotov5.Diagnostic, d interface{}) []*tfprotov5.Diagnostic {
|
|
switch d := d.(type) {
|
|
case cty.PathError:
|
|
ap := PathToAttributePath(d.Path)
|
|
diagnostic := &tfprotov5.Diagnostic{
|
|
Severity: tfprotov5.DiagnosticSeverityError,
|
|
Summary: d.Error(),
|
|
Attribute: ap,
|
|
}
|
|
|
|
if diagnostic.Summary == "" {
|
|
logging.HelperSchemaWarn(ctx, "detected empty error string for diagnostic in AppendProtoDiag for cty.PathError type")
|
|
diagnostic.Summary = "Empty Error String"
|
|
diagnostic.Detail = "This is always a bug in the provider and should be reported to the provider developers."
|
|
}
|
|
|
|
diags = append(diags, diagnostic)
|
|
case diag.Diagnostics:
|
|
diags = append(diags, DiagsToProto(d)...)
|
|
case error:
|
|
if d == nil {
|
|
logging.HelperSchemaDebug(ctx, "skipping diagnostic for nil error in AppendProtoDiag")
|
|
return diags
|
|
}
|
|
|
|
diagnostic := &tfprotov5.Diagnostic{
|
|
Severity: tfprotov5.DiagnosticSeverityError,
|
|
Summary: d.Error(),
|
|
}
|
|
|
|
if diagnostic.Summary == "" {
|
|
logging.HelperSchemaWarn(ctx, "detected empty error string for diagnostic in AppendProtoDiag for error type")
|
|
diagnostic.Summary = "Error Missing Message"
|
|
diagnostic.Detail = "This is always a bug in the provider and should be reported to the provider developers."
|
|
}
|
|
|
|
diags = append(diags, diagnostic)
|
|
case string:
|
|
if d == "" {
|
|
logging.HelperSchemaDebug(ctx, "skipping diagnostic for empty string in AppendProtoDiag")
|
|
return diags
|
|
}
|
|
|
|
diags = append(diags, &tfprotov5.Diagnostic{
|
|
Severity: tfprotov5.DiagnosticSeverityWarning,
|
|
Summary: d,
|
|
})
|
|
case *tfprotov5.Diagnostic:
|
|
diags = append(diags, d)
|
|
case []*tfprotov5.Diagnostic:
|
|
diags = append(diags, d...)
|
|
}
|
|
return diags
|
|
}
|
|
|
|
// ProtoToDiags converts a list of tfprotov5.Diagnostics to a diag.Diagnostics.
|
|
func ProtoToDiags(ds []*tfprotov5.Diagnostic) diag.Diagnostics {
|
|
var diags diag.Diagnostics
|
|
for _, d := range ds {
|
|
var severity diag.Severity
|
|
|
|
switch d.Severity {
|
|
case tfprotov5.DiagnosticSeverityError:
|
|
severity = diag.Error
|
|
case tfprotov5.DiagnosticSeverityWarning:
|
|
severity = diag.Warning
|
|
}
|
|
|
|
diags = append(diags, diag.Diagnostic{
|
|
Severity: severity,
|
|
Summary: d.Summary,
|
|
Detail: d.Detail,
|
|
AttributePath: AttributePathToPath(d.Attribute),
|
|
})
|
|
}
|
|
|
|
return diags
|
|
}
|
|
|
|
func DiagsToProto(diags diag.Diagnostics) []*tfprotov5.Diagnostic {
|
|
var ds []*tfprotov5.Diagnostic
|
|
for _, d := range diags {
|
|
protoDiag := &tfprotov5.Diagnostic{
|
|
Severity: tfprotov5.DiagnosticSeverityError,
|
|
Summary: d.Summary,
|
|
Detail: d.Detail,
|
|
Attribute: PathToAttributePath(d.AttributePath),
|
|
}
|
|
if d.Severity == diag.Warning {
|
|
protoDiag.Severity = tfprotov5.DiagnosticSeverityWarning
|
|
}
|
|
if d.Summary == "" {
|
|
protoDiag.Summary = "Empty Summary: This is always a bug in the provider and should be reported to the provider developers."
|
|
}
|
|
ds = append(ds, protoDiag)
|
|
}
|
|
return ds
|
|
}
|
|
|
|
// AttributePathToPath takes the proto encoded path and converts it to a cty.Path
|
|
func AttributePathToPath(ap *tftypes.AttributePath) cty.Path {
|
|
var p cty.Path
|
|
if ap == nil {
|
|
return p
|
|
}
|
|
for _, step := range ap.Steps() {
|
|
switch step := step.(type) {
|
|
case tftypes.AttributeName:
|
|
p = p.GetAttr(string(step))
|
|
case tftypes.ElementKeyString:
|
|
p = p.Index(cty.StringVal(string(step)))
|
|
case tftypes.ElementKeyInt:
|
|
p = p.Index(cty.NumberIntVal(int64(step)))
|
|
}
|
|
}
|
|
return p
|
|
}
|
|
|
|
// PathToAttributePath takes a cty.Path and converts it to a proto-encoded path.
|
|
func PathToAttributePath(p cty.Path) *tftypes.AttributePath {
|
|
if p == nil || len(p) < 1 {
|
|
return nil
|
|
}
|
|
ap := tftypes.NewAttributePath()
|
|
for _, step := range p {
|
|
switch selector := step.(type) {
|
|
case cty.GetAttrStep:
|
|
ap = ap.WithAttributeName(selector.Name)
|
|
|
|
case cty.IndexStep:
|
|
key := selector.Key
|
|
switch key.Type() {
|
|
case cty.String:
|
|
ap = ap.WithElementKeyString(key.AsString())
|
|
case cty.Number:
|
|
v, _ := key.AsBigFloat().Int64()
|
|
ap = ap.WithElementKeyInt(int(v))
|
|
default:
|
|
// We'll bail early if we encounter anything else, and just
|
|
// return the valid prefix.
|
|
return ap
|
|
}
|
|
}
|
|
}
|
|
return ap
|
|
}
|