Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

function: Replace usage of diagnostics with function errors during execution of provider-defined functions #925

Merged
merged 27 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
03600f8
Replacing function.RunResponse diagnostics with error
bendbennett Feb 8, 2024
4eff01d
Adding custom FunctionError
bendbennett Feb 9, 2024
ad94609
Adding custom FunctionErrors
bendbennett Feb 12, 2024
6df74dc
Removing unneeded equateErrors gocmp option
bendbennett Feb 12, 2024
f32bdb1
Switching to using convenience functions for adding errors to Functio…
bendbennett Feb 12, 2024
06bd2e2
Add copyright headers
bendbennett Feb 12, 2024
3bf7f79
Refactor to use Error() method on function errors when converting to …
bendbennett Feb 13, 2024
8957a63
Adding documentation and testing for fwerrors types
bendbennett Feb 19, 2024
c14bf43
Formatting errors during conversion to tfprotov<5|6>.FunctionError
bendbennett Feb 20, 2024
b608411
Removing argument error and argument warning diagnostics
bendbennett Feb 20, 2024
e3ab1d2
Renaming field name for FunctionErrors from Error to Errors
bendbennett Feb 20, 2024
7f0c6ee
Modifying documentation to reflect that executing the Run() method of…
bendbennett Feb 20, 2024
f82289c
Remove references to AddArgumentError and AddArgumentWarning from dia…
bendbennett Feb 20, 2024
a488bb8
Removing fwerror package and moving FunctionError to function package
bendbennett Feb 21, 2024
f0d758b
Refactoring to replace FunctionErrors slice with single FunctionError
bendbennett Feb 22, 2024
1419cd2
Bumping terraform-plugin-go to v0.22.0
bendbennett Feb 23, 2024
9bb896b
Removing unneeded DiagnosticWithFunctionArgument interface and implem…
bendbennett Feb 23, 2024
419f3e1
Merge remote-tracking branch 'origin/main' into pdf-err-handling
bendbennett Feb 23, 2024
cb45ef3
Altering function signature of ConcatFuncErrors
bendbennett Feb 26, 2024
18c5015
Removing HasError method
bendbennett Feb 26, 2024
aa853c3
Updating docs
bendbennett Feb 27, 2024
605ae15
Updates following code review
bendbennett Feb 27, 2024
9ea959e
Adding changelog entries
bendbennett Feb 27, 2024
9fb8b29
Fix naming
bendbennett Feb 27, 2024
5ace625
Update website/docs/plugin/framework/functions/errors.mdx
bendbennett Feb 27, 2024
bba24ad
Formatting
bendbennett Feb 27, 2024
c705415
Updates following code review
bendbennett Feb 28, 2024
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
13 changes: 0 additions & 13 deletions diag/argument_error_diagnostic.go

This file was deleted.

13 changes: 0 additions & 13 deletions diag/argument_warning_diagnostic.go

This file was deleted.

12 changes: 0 additions & 12 deletions diag/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,6 @@ import (
// or consistent.
type Diagnostics []Diagnostic

// AddArgumentError adds a generic function argument error diagnostic to the
// collection.
func (diags *Diagnostics) AddArgumentError(position int, summary string, detail string) {
diags.Append(NewArgumentErrorDiagnostic(position, summary, detail))
}

// AddArgumentWarning adds a function argument warning diagnostic to the
// collection.
func (diags *Diagnostics) AddArgumentWarning(position int, summary string, detail string) {
diags.Append(NewArgumentWarningDiagnostic(position, summary, detail))
}

// AddAttributeError adds a generic attribute error diagnostic to the collection.
func (diags *Diagnostics) AddAttributeError(path path.Path, summary string, detail string) {
diags.Append(NewAttributeErrorDiagnostic(path, summary, detail))
Expand Down
124 changes: 0 additions & 124 deletions diag/diagnostics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,130 +12,6 @@ import (
"github.com/hashicorp/terraform-plugin-framework/path"
)

func TestDiagnosticsAddArgumentError(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
diags diag.Diagnostics
position int
summary string
detail string
expected diag.Diagnostics
}{
"nil-add": {
diags: nil,
position: 0,
summary: "one summary",
detail: "one detail",
expected: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
},
},
"add": {
diags: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
},
position: 0,
summary: "three summary",
detail: "three detail",
expected: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
diag.NewArgumentErrorDiagnostic(0, "three summary", "three detail"),
},
},
"duplicate": {
diags: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
},
position: 0,
summary: "one summary",
detail: "one detail",
expected: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
},
},
}

for name, tc := range testCases {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()

tc.diags.AddArgumentError(tc.position, tc.summary, tc.detail)

if diff := cmp.Diff(tc.diags, tc.expected); diff != "" {
t.Errorf("Unexpected response (+wanted, -got): %s", diff)
}
})
}
}

func TestDiagnosticsAddArgumentWarning(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
diags diag.Diagnostics
position int
summary string
detail string
expected diag.Diagnostics
}{
"nil-add": {
diags: nil,
position: 0,
summary: "one summary",
detail: "one detail",
expected: diag.Diagnostics{
diag.NewArgumentWarningDiagnostic(0, "one summary", "one detail"),
},
},
"add": {
diags: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
},
position: 0,
summary: "three summary",
detail: "three detail",
expected: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
diag.NewArgumentWarningDiagnostic(0, "three summary", "three detail"),
},
},
"duplicate": {
diags: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
},
position: 0,
summary: "two summary",
detail: "two detail",
expected: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(0, "one summary", "one detail"),
diag.NewArgumentWarningDiagnostic(0, "two summary", "two detail"),
},
},
}

for name, tc := range testCases {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()

tc.diags.AddArgumentWarning(tc.position, tc.summary, tc.detail)

if diff := cmp.Diff(tc.diags, tc.expected); diff != "" {
t.Errorf("Unexpected response (+wanted, -got): %s", diff)
}
})
}
}

func TestDiagnosticsAddAttributeError(t *testing.T) {
t.Parallel()

Expand Down
111 changes: 53 additions & 58 deletions function/arguments_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"fmt"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/fwerror"
fwreflect "github.com/hashicorp/terraform-plugin-framework/internal/reflect"
"github.com/hashicorp/terraform-plugin-framework/path"
)
Expand Down Expand Up @@ -49,30 +49,28 @@ func (d ArgumentsData) Equal(o ArgumentsData) bool {
// type with an element type appropriate for the parameter definition ([]T). The
// framework automatically populates this list with elements matching the zero,
// one, or more arguments passed.
func (d ArgumentsData) Get(ctx context.Context, targets ...any) diag.Diagnostics {
var diags diag.Diagnostics
func (d ArgumentsData) Get(ctx context.Context, targets ...any) fwerror.FunctionErrors {
var fe fwerror.FunctionErrors

if len(d.values) == 0 {
diags.AddError(
"Invalid Argument Data Usage",
"When attempting to fetch argument data during the function call, the provider code incorrectly attempted to read argument data. "+
"This is always an issue in the provider code and should be reported to the provider developers.\n\n"+
"Function does not have argument data.",
)

return diags
msg := "Invalid Argument Data Usage: When attempting to fetch argument data during the function call, the provider code incorrectly attempted to read argument data. " +
"This is always an issue in the provider code and should be reported to the provider developers.\n\n" +
"Function does not have argument data."

fe.AddError(msg)

return fe
}

if len(targets) != len(d.values) {
diags.AddError(
"Invalid Argument Data Usage",
"When attempting to fetch argument data during the function call, the provider code incorrectly attempted to read argument data. "+
"The Get call requires all parameters and the final variadic parameter, if implemented, to be in the targets. "+
"This is always an error in the provider code and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Given targets count: %d, expected targets count: %d", len(targets), len(d.values)),
)
msg := "Invalid Argument Data Usage: When attempting to fetch argument data during the function call, the provider code incorrectly attempted to read argument data. " +
"The Get call requires all parameters and the final variadic parameter, if implemented, to be in the targets. " +
"This is always an error in the provider code and should be reported to the provider developers.\n\n" +
fmt.Sprintf("Given targets count: %d, expected targets count: %d", len(targets), len(d.values))

fe.AddError(msg)

return diags
return fe
}

for position, attrValue := range d.values {
Expand All @@ -85,27 +83,26 @@ func (d ArgumentsData) Get(ctx context.Context, targets ...any) diag.Diagnostics
continue
}

tfValue, err := attrValue.ToTerraformValue(ctx)
tfValue, tfValueErr := attrValue.ToTerraformValue(ctx)

if err != nil {
diags.AddError(
"Argument Value Conversion Error",
fmt.Sprintf("An unexpected error was encountered converting a %T to its equivalent Terraform representation. "+
"This is always an error in the provider code and should be reported to the provider developers.\n\n"+
"Position: %d\n"+
"Error: %s",
attrValue, position, err),
)
if tfValueErr != nil {
msg := fmt.Sprintf("Argument Value Conversion Error: An unexpected error was encountered converting a %T to its equivalent Terraform representation. "+
"This is always an error in the provider code and should be reported to the provider developers.\n\n"+
"Position: %d\n"+
"Error: %s",
attrValue, position, tfValueErr)

fe.AddArgumentError(position, msg)

continue
}

reflectDiags := fwreflect.Into(ctx, attrValue.Type(ctx), tfValue, target, fwreflect.Options{}, path.Empty())

diags.Append(reflectDiags...)
fe.Append(fwerror.FunctionErrorsFromDiags(ctx, reflectDiags)...)
}

return diags
return fe
}

// GetArgument retrieves the argument data found at the given zero-based
Expand All @@ -116,30 +113,28 @@ func (d ArgumentsData) Get(ctx context.Context, targets ...any) diag.Diagnostics
// type with an element type appropriate for the parameter definition ([]T) at
// the position after all parameters. The framework automatically populates this
// list with elements matching the zero, one, or more arguments passed.
func (d ArgumentsData) GetArgument(ctx context.Context, position int, target any) diag.Diagnostics {
var diags diag.Diagnostics
func (d ArgumentsData) GetArgument(ctx context.Context, position int, target any) fwerror.FunctionErrors {
var fe fwerror.FunctionErrors

if len(d.values) == 0 {
diags.AddError(
"Invalid Argument Data Usage",
"When attempting to fetch argument data during the function call, the provider code incorrectly attempted to read argument data. "+
"This is always an issue in the provider code and should be reported to the provider developers.\n\n"+
"Function does not have argument data.",
)

return diags
msg := "Invalid Argument Data Usage: When attempting to fetch argument data during the function call, the provider code incorrectly attempted to read argument data. " +
"This is always an issue in the provider code and should be reported to the provider developers.\n\n" +
"Function does not have argument data."

fe.AddArgumentError(position, msg)

return fe
}

if position >= len(d.values) {
diags.AddError(
"Invalid Argument Data Position",
"When attempting to fetch argument data during the function call, the provider code attempted to read a non-existent argument position. "+
"Function argument positions are 0-based and any final variadic parameter is represented as one argument position with an ordered list of the parameter data type. "+
"This is always an error in the provider code and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Given argument position: %d, last argument position: %d", position, len(d.values)-1),
)
msg := "Invalid Argument Data Position: When attempting to fetch argument data during the function call, the provider code attempted to read a non-existent argument position. " +
"Function argument positions are 0-based and any final variadic parameter is represented as one argument position with an ordered list of the parameter data type. " +
"This is always an error in the provider code and should be reported to the provider developers.\n\n" +
fmt.Sprintf("Given argument position: %d, last argument position: %d", position, len(d.values)-1)

fe.AddArgumentError(position, msg)

return diags
return fe
}

attrValue := d.values[position]
Expand All @@ -154,20 +149,20 @@ func (d ArgumentsData) GetArgument(ctx context.Context, position int, target any
tfValue, err := attrValue.ToTerraformValue(ctx)

if err != nil {
diags.AddError(
"Argument Value Conversion Error",
fmt.Sprintf("An unexpected error was encountered converting a %T to its equivalent Terraform representation. "+
"This is always an error in the provider code and should be reported to the provider developers.\n\n"+
"Error: %s", attrValue, err),
)
return diags
msg := fmt.Sprintf("Argument Value Conversion Error: An unexpected error was encountered converting a %T to its equivalent Terraform representation. "+
"This is always an error in the provider code and should be reported to the provider developers.\n\n"+
"Error: %s", attrValue, err)

fe.AddArgumentError(position, msg)

return fe
}

reflectDiags := fwreflect.Into(ctx, attrValue.Type(ctx), tfValue, target, fwreflect.Options{}, path.Empty())

diags.Append(reflectDiags...)
fe.Append(fwerror.FunctionErrorsFromDiags(ctx, reflectDiags)...)

return diags
return fe
}

// NewArgumentsData creates an ArgumentsData. This is only necessary for unit
Expand Down
Loading
Loading