diff --git a/docs/resources/namespace_search_attribute.md b/docs/resources/namespace_search_attribute.md index 9be2cba..170b33c 100644 --- a/docs/resources/namespace_search_attribute.md +++ b/docs/resources/namespace_search_attribute.md @@ -58,7 +58,7 @@ resource "temporalcloud_namespace_search_attribute" "custom_search_attribute3" { - `name` (String) The name of the search attribute. - `namespace_id` (String) The ID of the namespace to which this search attribute belongs. -- `type` (String) The type of the search attribute. Must be one of `bool`, `datetime`, `double`, `int`, `keyword`, or `text`. (case-insensitive) +- `type` (String) The type of the search attribute. Must be one of `bool`, `datetime`, `double`, `int`, `keyword`, `keyword_list` or `text`. (case-insensitive) ### Read-Only diff --git a/internal/provider/enums/namespace.go b/internal/provider/enums/namespace.go index 924a074..1db4e79 100644 --- a/internal/provider/enums/namespace.go +++ b/internal/provider/enums/namespace.go @@ -26,12 +26,11 @@ func ToNamespaceSearchAttribute(s string) (namespace.NamespaceSpec_SearchAttribu return namespace.NamespaceSpec_SEARCH_ATTRIBUTE_TYPE_BOOL, nil case "datetime": return namespace.NamespaceSpec_SEARCH_ATTRIBUTE_TYPE_DATETIME, nil - case "keyword_list", "keywordlist": + case "keyword_list", "keywordlist", "keyword-list": return namespace.NamespaceSpec_SEARCH_ATTRIBUTE_TYPE_KEYWORD_LIST, nil default: return namespace.NamespaceSpec_SEARCH_ATTRIBUTE_TYPE_UNSPECIFIED, fmt.Errorf("%w: %s", ErrInvalidNamespaceSearchAttribute, s) } - } func FromNamespaceSearchAttribute(r namespace.NamespaceSpec_SearchAttributeType) (string, error) { diff --git a/internal/provider/namespace_search_attribute_resource.go b/internal/provider/namespace_search_attribute_resource.go index 714c7f4..f479b8f 100644 --- a/internal/provider/namespace_search_attribute_resource.go +++ b/internal/provider/namespace_search_attribute_resource.go @@ -92,9 +92,10 @@ func (r *namespaceSearchAttributeResource) Schema(ctx context.Context, _ resourc Required: true, }, "type": schema.StringAttribute{ - CustomType: internaltypes.CaseInsensitiveStringType{}, - Description: "The type of the search attribute. Must be one of `bool`, `datetime`, `double`, `int`, `keyword`, or `text`. (case-insensitive)", - Required: true, + CustomType: internaltypes.CaseInsensitiveStringType{}, + Description: "The type of the search attribute. Must be one of `bool`, `datetime`, `double`, `int`, `keyword`, `keyword_list` or `text`. (case-insensitive)", + Required: true, + PlanModifiers: []planmodifier.String{newSearchAttrTypePlanModifier()}, }, }, } @@ -330,3 +331,52 @@ func withNamespaceLock(ns string, f func()) { }() f() } + +func newSearchAttrTypePlanModifier() planmodifier.String { + return &searchAttrTypePlanModifier{} +} + +type searchAttrTypePlanModifier struct { +} + +// Description returns a human-readable description of the plan modifier. +func (m searchAttrTypePlanModifier) Description(_ context.Context) string { + return "If the value of the search attribute changes (case-insensitive), update the resource accordingly." +} + +// MarkdownDescription returns a markdown description of the plan modifier. +func (m searchAttrTypePlanModifier) MarkdownDescription(ctx context.Context) string { + return m.Description(ctx) +} + +// PlanModifyString implements the plan modification logic. +func (m searchAttrTypePlanModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { + if req.State.Raw.IsNull() { + // Its a create operation, no need to update the plan. + return + } + if req.Plan.Raw.IsNull() { + // Its a delete operation, no need to update the plan. + return + } + + saTypePlan, err := enums.ToNamespaceSearchAttribute(req.PlanValue.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Failed to parse search attribute type in plan", err.Error()) + return + } + saTypeState, err := enums.ToNamespaceSearchAttribute(req.StateValue.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Failed to parse search attribute type in state", err.Error()) + return + } + + if saTypePlan == saTypeState { + // The state and the plan values are equal. + // No need to update the resource, update the response to the same as the one in the state to avoid an update. + resp.PlanValue = req.StateValue + return + } + // Its a change in the value, update the response accordingly. + resp.PlanValue = types.StringValue(req.PlanValue.ValueString()) +}