From 089038455215efc3a48828e7b29108f21d0983e0 Mon Sep 17 00:00:00 2001 From: max-voitcov Date: Wed, 26 Aug 2026 00:39:51 +0300 Subject: [PATCH] Add ConfigValidators and CreateDefaults hooks to the generic resource Two things ResourceSpec could not express: plan-time checks that span several attributes, and create bodies that need a key Dokploy insists on receiving but the model cannot produce. Both are used by the commits that follow. --- internal/provider/generic.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/internal/provider/generic.go b/internal/provider/generic.go index 7a821d3..bc3010e 100644 --- a/internal/provider/generic.go +++ b/internal/provider/generic.go @@ -67,6 +67,16 @@ type ResourceSpec struct { // discover the new ID. ListIDs func(ctx context.Context, api *client.Client, model any) (map[string]struct{}, error) + // CreateDefaults supplies values for API fields that Dokploy's create + // schema insists on receiving but that the model cannot produce -- most + // often a name the server declines to generate. Only keys missing from + // the assembled body are added, so an explicit configuration always wins. + CreateDefaults func(model any) map[string]any + + // ConfigValidators are plan-time checks that span several attributes, for + // invariants Terraform's schema cannot express on its own. + ConfigValidators []resource.ConfigValidator + // PostRead derives extra model fields from the raw read response, for // values that are not plain top-level columns (for example a project's // default environment, which arrives nested under `environments`). @@ -80,9 +90,10 @@ type genericResource struct { } var ( - _ resource.Resource = &genericResource{} - _ resource.ResourceWithConfigure = &genericResource{} - _ resource.ResourceWithImportState = &genericResource{} + _ resource.Resource = &genericResource{} + _ resource.ResourceWithConfigure = &genericResource{} + _ resource.ResourceWithImportState = &genericResource{} + _ resource.ResourceWithConfigValidators = &genericResource{} ) func newGenericResource(spec ResourceSpec) func() resource.Resource { @@ -97,6 +108,10 @@ func (r *genericResource) Schema(_ context.Context, _ resource.SchemaRequest, re resp.Schema = r.spec.Schema } +func (r *genericResource) ConfigValidators(_ context.Context) []resource.ConfigValidator { + return r.spec.ConfigValidators +} + func (r *genericResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return @@ -125,6 +140,14 @@ func (r *genericResource) Create(ctx context.Context, req resource.CreateRequest return } + if r.spec.CreateDefaults != nil { + for key, value := range r.spec.CreateDefaults(model) { + if _, present := body[key]; !present { + body[key] = value + } + } + } + if r.spec.NeedsOrganizationID { orgID, err := r.api.OrganizationID(ctx) if err != nil {