5
5
"fmt"
6
6
"net/http"
7
7
"regexp"
8
+ "strings"
8
9
9
10
"github.com/segmentio/terraform-provider-segment/internal/provider/docs"
10
11
"github.com/segmentio/terraform-provider-segment/internal/provider/models"
@@ -16,10 +17,25 @@ import (
16
17
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
17
18
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
18
19
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
20
+ "github.com/hashicorp/terraform-plugin-framework/types"
19
21
20
22
"github.com/segmentio/public-api-sdk-go/api"
21
23
)
22
24
25
+ /*──────────────────────────── helper ────────────────────────────*/
26
+
27
+ // Matches "<name> (workspace-slug)" and returns "<name>"
28
+ var wsSuffix = regexp .MustCompile (`^(.*) [^)]+$` )
29
+
30
+ func stripWorkspaceSuffix (name string ) string {
31
+ if m := wsSuffix .FindStringSubmatch (name ); m != nil {
32
+ return strings .TrimSpace (m [1 ])
33
+ }
34
+ return name
35
+ }
36
+
37
+ /*──────────────────────────── resource boilerplate ──────────────*/
38
+
23
39
var (
24
40
_ resource.Resource = & functionResource {}
25
41
_ resource.ResourceWithConfigure = & functionResource {}
@@ -39,6 +55,8 @@ func (r *functionResource) Metadata(_ context.Context, req resource.MetadataRequ
39
55
resp .TypeName = req .ProviderTypeName + "_function"
40
56
}
41
57
58
+ /*──────────────────────────── schema ────────────────────────────*/
59
+
42
60
func (r * functionResource ) Schema (_ context.Context , _ resource.SchemaRequest , resp * resource.SchemaResponse ) {
43
61
resp .Schema = schema.Schema {
44
62
Description : "Configures a Function. For more information, visit the [Segment docs](https://segment.com/docs/connections/functions/).\n \n " +
@@ -57,7 +75,7 @@ func (r *functionResource) Schema(_ context.Context, _ resource.SchemaRequest, r
57
75
},
58
76
"display_name" : schema.StringAttribute {
59
77
Optional : true ,
60
- Description : "A display name for this Function. Destination Functions append the Workspace to the display name, but this is omitted from the Terraform output for consistency purposes ." ,
78
+ Description : "A display name for this Function (alphanumeric + spaces) ." ,
61
79
},
62
80
"logo_url" : schema.StringAttribute {
63
81
Optional : true ,
@@ -84,7 +102,7 @@ func (r *functionResource) Schema(_ context.Context, _ resource.SchemaRequest, r
84
102
},
85
103
"settings" : schema.SetNestedAttribute {
86
104
Optional : true ,
87
- Description : "The settings associated with this Function. Common settings are connection-related configuration used to connect to it, for example host, username, and port ." ,
105
+ Description : "Settings associated with this Function." ,
88
106
NestedObject : schema.NestedAttributeObject {
89
107
Attributes : map [string ]schema.Attribute {
90
108
"name" : schema.StringAttribute {
@@ -93,26 +111,26 @@ func (r *functionResource) Schema(_ context.Context, _ resource.SchemaRequest, r
93
111
},
94
112
"label" : schema.StringAttribute {
95
113
Required : true ,
96
- Description : "The label for this Function Setting ." ,
114
+ Description : "The label for this Function setting ." ,
97
115
},
98
116
"description" : schema.StringAttribute {
99
117
Required : true ,
100
- Description : "A description of this Function Setting ." ,
118
+ Description : "A description of this Function setting ." ,
101
119
},
102
120
"type" : schema.StringAttribute {
103
121
Required : true ,
104
- Description : "The type of this Function Setting ." ,
122
+ Description : "The type of this Function setting ." ,
105
123
Validators : []validator.String {
106
124
stringvalidator .RegexMatches (regexp .MustCompile ("^[A-Z_]+$" ), "'type' must be in all uppercase" ),
107
125
},
108
126
},
109
127
"required" : schema.BoolAttribute {
110
128
Required : true ,
111
- Description : "Whether this Function Setting is required." ,
129
+ Description : "Whether this Function setting is required." ,
112
130
},
113
131
"sensitive" : schema.BoolAttribute {
114
132
Required : true ,
115
- Description : "Whether this Function Setting contains sensitive information." ,
133
+ Description : "Whether this Function setting contains sensitive information." ,
116
134
},
117
135
},
118
136
},
@@ -121,6 +139,8 @@ func (r *functionResource) Schema(_ context.Context, _ resource.SchemaRequest, r
121
139
}
122
140
}
123
141
142
+ /*──────────────────────────── CREATE ────────────────────────────*/
143
+
124
144
func (r * functionResource ) Create (ctx context.Context , req resource.CreateRequest , resp * resource.CreateResponse ) {
125
145
var plan models.FunctionPlan
126
146
diags := req .Plan .Get (ctx , & plan )
@@ -147,11 +167,7 @@ func (r *functionResource) Create(ctx context.Context, req resource.CreateReques
147
167
defer body .Body .Close ()
148
168
}
149
169
if err != nil {
150
- resp .Diagnostics .AddError (
151
- "Unable to create Function" ,
152
- getError (err , body ),
153
- )
154
-
170
+ resp .Diagnostics .AddError ("Unable to create Function" , getError (err , body ))
155
171
return
156
172
}
157
173
@@ -162,24 +178,18 @@ func (r *functionResource) Create(ctx context.Context, req resource.CreateReques
162
178
var state models.FunctionState
163
179
state .Fill (function )
164
180
165
- // Destination functions append workspace name to display name causing inconsistency
166
- if state .ResourceType .ValueString () == "DESTINATION" || state .ResourceType .ValueString () == "INSERT_DESTINATION" || state .ResourceType .ValueString () == "INSERT_SOURCE" {
167
- state .DisplayName = plan .DisplayName
168
- }
181
+ // Always normalise the name for workspace-scoped types
182
+ state .DisplayName = types .StringValue (stripWorkspaceSuffix (state .DisplayName .ValueString ()))
169
183
170
- // Set state to fully populated data
171
- diags = resp .State .Set (ctx , state )
184
+ diags = resp .State .Set (ctx , & state )
172
185
resp .Diagnostics .Append (diags ... )
173
- if resp .Diagnostics .HasError () {
174
- return
175
- }
176
186
}
177
187
188
+ /*──────────────────────────── READ ───────────────────────────────*/
189
+
178
190
func (r * functionResource ) Read (ctx context.Context , req resource.ReadRequest , resp * resource.ReadResponse ) {
179
191
var previousState models.FunctionState
180
-
181
192
diags := req .State .Get (ctx , & previousState )
182
-
183
193
resp .Diagnostics .Append (diags ... )
184
194
if resp .Diagnostics .HasError () {
185
195
return
@@ -192,35 +202,26 @@ func (r *functionResource) Read(ctx context.Context, req resource.ReadRequest, r
192
202
if err != nil {
193
203
if body .StatusCode == http .StatusNotFound {
194
204
resp .State .RemoveResource (ctx )
195
-
196
205
return
197
206
}
198
-
199
- resp .Diagnostics .AddError (
200
- fmt .Sprintf ("Unable to read Function (ID: %s)" , previousState .ID .ValueString ()),
201
- getError (err , body ),
202
- )
203
-
207
+ resp .Diagnostics .AddError (fmt .Sprintf ("Unable to read Function (ID: %s)" , previousState .ID .ValueString ()), getError (err , body ))
204
208
return
205
209
}
206
210
207
211
var state models.FunctionState
212
+ state .Fill (response .Data .GetFunction ())
208
213
209
- function := response .Data .GetFunction ()
210
- state .Fill (function )
211
-
212
- // Destination functions append workspace name to display name causing inconsistency
213
- if state .ResourceType .ValueString () == "DESTINATION" || state .ResourceType .ValueString () == "INSERT_DESTINATION" || state .ResourceType .ValueString () == "INSERT_SOURCE" {
214
- state .DisplayName = previousState .DisplayName
214
+ // normalise if necessary
215
+ if rt := state .ResourceType .ValueString (); rt == "DESTINATION" || rt == "INSERT_DESTINATION" || rt == "INSERT_SOURCE" {
216
+ state .DisplayName = types .StringValue (stripWorkspaceSuffix (state .DisplayName .ValueString ()))
215
217
}
216
218
217
219
diags = resp .State .Set (ctx , & state )
218
220
resp .Diagnostics .Append (diags ... )
219
- if resp .Diagnostics .HasError () {
220
- return
221
- }
222
221
}
223
222
223
+ /*──────────────────────────── UPDATE ────────────────────────────*/
224
+
224
225
func (r * functionResource ) Update (ctx context.Context , req resource.UpdateRequest , resp * resource.UpdateResponse ) {
225
226
var plan models.FunctionPlan
226
227
diags := req .Plan .Get (ctx , & plan )
@@ -253,30 +254,22 @@ func (r *functionResource) Update(ctx context.Context, req resource.UpdateReques
253
254
defer body .Body .Close ()
254
255
}
255
256
if err != nil {
256
- resp .Diagnostics .AddError (
257
- fmt .Sprintf ("Unable to update Function (ID: %s)" , plan .ID .ValueString ()),
258
- getError (err , body ),
259
- )
260
-
257
+ resp .Diagnostics .AddError (fmt .Sprintf ("Unable to update Function (ID: %s)" , plan .ID .ValueString ()), getError (err , body ))
261
258
return
262
259
}
263
260
264
- function := out .Data .GetFunction ()
265
-
266
- state .Fill (function )
261
+ state .Fill (out .Data .GetFunction ())
267
262
268
- // Destination functions append workspace name to display name causing inconsistency
269
- if state .ResourceType .ValueString () == "DESTINATION" || state .ResourceType .ValueString () == "INSERT_DESTINATION" || state .ResourceType .ValueString () == "INSERT_SOURCE" {
270
- state .DisplayName = plan .DisplayName
263
+ if rt := state .ResourceType .ValueString (); rt == "DESTINATION" || rt == "INSERT_DESTINATION" || rt == "INSERT_SOURCE" {
264
+ state .DisplayName = types .StringValue (stripWorkspaceSuffix (state .DisplayName .ValueString ()))
271
265
}
272
266
273
267
diags = resp .State .Set (ctx , & state )
274
268
resp .Diagnostics .Append (diags ... )
275
- if resp .Diagnostics .HasError () {
276
- return
277
- }
278
269
}
279
270
271
+ /*──────────────────────────── DELETE ────────────────────────────*/
272
+
280
273
func (r * functionResource ) Delete (ctx context.Context , req resource.DeleteRequest , resp * resource.DeleteResponse ) {
281
274
var config models.FunctionState
282
275
diags := req .State .Get (ctx , & config )
@@ -290,35 +283,30 @@ func (r *functionResource) Delete(ctx context.Context, req resource.DeleteReques
290
283
defer body .Body .Close ()
291
284
}
292
285
if err != nil {
293
- resp .Diagnostics .AddError (
294
- fmt .Sprintf ("Unable to delete Function (ID: %s)" , config .ID .ValueString ()),
295
- getError (err , body ),
296
- )
297
-
298
- return
286
+ resp .Diagnostics .AddError (fmt .Sprintf ("Unable to delete Function (ID: %s)" , config .ID .ValueString ()), getError (err , body ))
299
287
}
300
288
}
301
289
290
+ /*──────────────────────────── IMPORT ────────────────────────────*/
291
+
302
292
func (r * functionResource ) ImportState (ctx context.Context , req resource.ImportStateRequest , resp * resource.ImportStateResponse ) {
303
- // Retrieve import ID and save to id attribute
304
293
resource .ImportStatePassthroughID (ctx , path .Root ("id" ), req , resp )
305
294
}
306
295
296
+ /*──────────────────────────── CONFIGURE ─────────────────────────*/
297
+
307
298
func (r * functionResource ) Configure (_ context.Context , req resource.ConfigureRequest , resp * resource.ConfigureResponse ) {
308
299
if req .ProviderData == nil {
309
300
return
310
301
}
311
-
312
302
config , ok := req .ProviderData .(* ClientInfo )
313
303
if ! ok {
314
304
resp .Diagnostics .AddError (
315
305
"Unexpected Resource Configure Type" ,
316
306
fmt .Sprintf ("Expected ClientInfo, got: %T. Please report this issue to the provider developers." , req .ProviderData ),
317
307
)
318
-
319
308
return
320
309
}
321
-
322
310
r .client = config .client
323
311
r .authContext = config .authContext
324
312
}
0 commit comments