package provider import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" ) // requiredWhen declares that `attribute` must hold a non-empty value whenever // `discriminator` equals `value`, and must be absent otherwise. // // Terraform's schema language cannot express "required, but only for this // variant", and Dokploy's Zod schemas accept every combination -- so without a // provider-side check a nonsensical resource is created without complaint. The // `dokploy_mount` case is the reason this exists: see mountConfigValidators. type requiredWhen struct { discriminator path.Path value string attribute path.Path // rationale explains the consequence of getting it wrong, so the error // tells the practitioner why rather than only what. rationale string } var _ resource.ConfigValidator = &requiredWhen{} func (v *requiredWhen) Description(ctx context.Context) string { return v.MarkdownDescription(ctx) } func (v *requiredWhen) MarkdownDescription(_ context.Context) string { return fmt.Sprintf("`%s` is required when `%s` is `%s`, and must not be set otherwise.", v.attribute, v.discriminator, v.value) } func (v *requiredWhen) ValidateResource( ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, ) { var discriminator types.String resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.discriminator, &discriminator)...) if resp.Diagnostics.HasError() || discriminator.IsNull() || discriminator.IsUnknown() { return } var attribute types.String resp.Diagnostics.Append(req.Config.GetAttribute(ctx, v.attribute, &attribute)...) if resp.Diagnostics.HasError() || attribute.IsUnknown() { // An unknown value cannot be checked at plan time; it is resolved // during apply and Dokploy validates it there. return } matches := discriminator.ValueString() == v.value empty := attribute.IsNull() || attribute.ValueString() == "" switch { case matches && empty: detail := fmt.Sprintf("`%s` must be set to a non-empty value when `%s` is `%s`.", v.attribute, v.discriminator, v.value) if v.rationale != "" { detail += "\n\n" + v.rationale } resp.Diagnostics.AddAttributeError(v.attribute, fmt.Sprintf("Missing %s", v.attribute), detail) case !matches && !empty: resp.Diagnostics.AddAttributeError(v.attribute, fmt.Sprintf("Unexpected %s", v.attribute), fmt.Sprintf("`%s` only applies when `%s` is `%s`, but it is `%s`. "+ "Dokploy ignores the value, so leaving it set hides a mistake.", v.attribute, v.discriminator, v.value, discriminator.ValueString())) } }