Skip to content

Commit

Permalink
improved schema validations
Browse files Browse the repository at this point in the history
  • Loading branch information
ramonsnir committed May 11, 2024
1 parent b9d67da commit 5d88c68
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 17 deletions.
11 changes: 6 additions & 5 deletions internal/provider/account_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/netlify/terraform-provider-netlify/internal/models"
"github.com/netlify/terraform-provider-netlify/internal/plumbing/operations"
Expand Down Expand Up @@ -57,6 +60,9 @@ func (d *accountDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
"id": schema.StringAttribute{
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.AtLeastOneOf(path.MatchRoot("slug")),
},
},
"slug": schema.StringAttribute{
Optional: true,
Expand All @@ -76,11 +82,6 @@ func (d *accountDataSource) Read(ctx context.Context, req datasource.ReadRequest
return
}

if (config.ID.IsUnknown() || config.ID.IsNull()) && (config.Slug.IsUnknown() || config.Slug.IsNull()) {
resp.Diagnostics.AddError("Error reading Netlify account", "Either id or slug must be specified for an account search")
return
}

var account *models.AccountMembership
if !config.ID.IsUnknown() && !config.ID.IsNull() {
accountOk, err := d.data.client.Operations.GetAccount(
Expand Down
11 changes: 6 additions & 5 deletions internal/provider/dns_zone_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/netlify/terraform-provider-netlify/internal/models"
"github.com/netlify/terraform-provider-netlify/internal/plumbing/operations"
Expand Down Expand Up @@ -73,6 +76,9 @@ func (d *dnsZoneDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
"id": schema.StringAttribute{
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.AtLeastOneOf(path.MatchRoot("name")),
},
},
"name": schema.StringAttribute{
Optional: true,
Expand Down Expand Up @@ -157,11 +163,6 @@ func (d *dnsZoneDataSource) Read(ctx context.Context, req datasource.ReadRequest
return
}

if (config.ID.IsUnknown() || config.ID.IsNull()) && (config.Name.IsUnknown() || config.Name.IsNull()) {
resp.Diagnostics.AddError("Error reading Netlify DNS zone", "Either id or name must be specified for a DNS zone search")
return
}

var zone *models.DNSZone
if !config.ID.IsUnknown() && !config.ID.IsNull() {
zoneOk, err := d.data.client.Operations.GetDNSZone(
Expand Down
7 changes: 6 additions & 1 deletion internal/provider/environment_variable_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/netlify/terraform-provider-netlify/internal/models"
"github.com/netlify/terraform-provider-netlify/internal/plumbing/operations"
"github.com/netlify/terraform-provider-netlify/internal/provider/netlify_validators"
)

var (
Expand Down Expand Up @@ -141,9 +142,13 @@ func (r *environmentVariableResource) Schema(_ context.Context, _ resource.Schem
Optional: true,
Computed: true,
Default: stringdefault.StaticString(""),
Validators: []validator.String{
netlify_validators.EnvironmentVariableContextParameterValidator{
ContextPathExpression: path.MatchRelative().AtParent().AtName("context"),
},
},
},
},
// TODO: validate that context_parameter is not empty iff context is "branch"
},
// TODO: validate that values don't overlap
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package netlify_validators

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

var (
_ validator.String = EnvironmentVariableContextParameterValidator{}
)

type EnvironmentVariableContextParameterValidator struct {
ContextPathExpression path.Expression
}

type EnvironmentVariableContextParameterValidatorRequest struct {
Config tfsdk.Config
ConfigValue attr.Value
Path path.Path
PathExpression path.Expression
}

type EnvironmentVariableContextParameterValidatorResponse struct {
Diagnostics diag.Diagnostics
}

func (av EnvironmentVariableContextParameterValidator) Description(ctx context.Context) string {
return av.MarkdownDescription(ctx)
}

func (av EnvironmentVariableContextParameterValidator) MarkdownDescription(_ context.Context) string {
return fmt.Sprintf("Ensure that an attribute is a non-empty string iff %q is set to \"branch\"", av.ContextPathExpression)
}

func (av EnvironmentVariableContextParameterValidator) Validate(ctx context.Context, req EnvironmentVariableContextParameterValidatorRequest, res *EnvironmentVariableContextParameterValidatorResponse) {
// Delay validation until all involved attributes have a known value
if req.ConfigValue.IsUnknown() {
return
}

isNonEmpty := !req.ConfigValue.IsNull() && !req.ConfigValue.Equal(types.StringValue(""))

matchedPaths, diags := req.Config.PathMatches(ctx, req.PathExpression.Merge(av.ContextPathExpression))

res.Diagnostics.Append(diags...)
if diags.HasError() {
return
}

for _, mp := range matchedPaths {
var mpVal attr.Value
diags := req.Config.GetAttribute(ctx, mp, &mpVal)
res.Diagnostics.Append(diags...)

// Collect all errors
if diags.HasError() {
continue
}

// Delay validation until all involved attributes have a known value
if mpVal.IsUnknown() {
return
}

var listValue basetypes.ListValue
listValue, diags = types.ListValue(types.StringType, []attr.Value{types.StringValue("branch")})
res.Diagnostics.Append(diags...)

// Collect all errors
if diags.HasError() {
continue
}

isBranch := !mpVal.IsNull() && mpVal.Equal(listValue)

if isNonEmpty != isBranch {
res.Diagnostics.Append(validatordiag.InvalidAttributeCombinationDiagnostic(
req.Path,
fmt.Sprintf("Attribute %q must be a non-empty string iff %q is specified", req.Path, mp),
))
}
}
}

func (av EnvironmentVariableContextParameterValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
validateReq := EnvironmentVariableContextParameterValidatorRequest{
Config: req.Config,
ConfigValue: req.ConfigValue,
Path: req.Path,
PathExpression: req.PathExpression,
}
validateResp := &EnvironmentVariableContextParameterValidatorResponse{}

av.Validate(ctx, validateReq, validateResp)

resp.Diagnostics.Append(validateResp.Diagnostics...)
}
15 changes: 9 additions & 6 deletions internal/provider/site_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/netlify/terraform-provider-netlify/internal/models"
"github.com/netlify/terraform-provider-netlify/internal/plumbing/operations"
Expand Down Expand Up @@ -51,6 +54,9 @@ func (d *siteDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
"id": schema.StringAttribute{
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.AtLeastOneOf(path.MatchRoot("name")),
},
},
"account_slug": schema.StringAttribute{
Optional: true,
Expand All @@ -59,6 +65,9 @@ func (d *siteDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
"name": schema.StringAttribute{
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.AlsoRequires(path.MatchRoot("account_slug")),
},
},
"custom_domain": schema.StringAttribute{
Computed: true,
Expand All @@ -78,12 +87,6 @@ func (d *siteDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
return
}

if (config.ID.IsUnknown() || config.ID.IsNull()) &&
(config.AccountSlug.IsUnknown() || config.AccountSlug.IsNull() || config.Name.IsUnknown() || config.Name.IsNull()) {
resp.Diagnostics.AddError("Error reading Netlify site", "Either id, or account slug and site name, must be specified for a site search")
return
}

var site *models.Site
if !config.ID.IsUnknown() && !config.ID.IsNull() {
siteOk, err := d.data.client.Operations.GetSite(
Expand Down

0 comments on commit 5d88c68

Please sign in to comment.