Skip to content

Commit b461236

Browse files
authored
[FUNK-1866] - Source insert function handling with terraform (#192)
1 parent 0c243af commit b461236

File tree

2 files changed

+227
-6
lines changed

2 files changed

+227
-6
lines changed

internal/provider/function_resource.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ import (
66
"net/http"
77
"regexp"
88

9-
"github.com/segmentio/terraform-provider-segment/internal/provider/docs"
10-
"github.com/segmentio/terraform-provider-segment/internal/provider/models"
11-
129
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
1310
"github.com/hashicorp/terraform-plugin-framework/path"
1411
"github.com/hashicorp/terraform-plugin-framework/resource"
1512
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1613
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1714
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
1815
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
16+
tftypes "github.com/hashicorp/terraform-plugin-framework/types"
1917

2018
"github.com/segmentio/public-api-sdk-go/api"
19+
20+
"github.com/segmentio/terraform-provider-segment/internal/provider/docs"
21+
"github.com/segmentio/terraform-provider-segment/internal/provider/models"
2122
)
2223

2324
var (
@@ -35,6 +36,10 @@ type functionResource struct {
3536
authContext context.Context
3637
}
3738

39+
func hasValue(v tftypes.String) bool {
40+
return !(v.IsNull() || v.IsUnknown())
41+
}
42+
3843
func (r *functionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
3944
resp.TypeName = req.ProviderTypeName + "_function"
4045
}
@@ -163,7 +168,7 @@ func (r *functionResource) Create(ctx context.Context, req resource.CreateReques
163168
state.Fill(function)
164169

165170
// Destination functions append workspace name to display name causing inconsistency
166-
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" {
171+
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" || state.ResourceType.ValueString() == "INSERT_SOURCE" {
167172
state.DisplayName = plan.DisplayName
168173
}
169174

@@ -210,7 +215,7 @@ func (r *functionResource) Read(ctx context.Context, req resource.ReadRequest, r
210215
state.Fill(function)
211216

212217
// Destination functions append workspace name to display name causing inconsistency
213-
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" {
218+
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" || state.ResourceType.ValueString() == "INSERT_SOURCE" && hasValue(previousState.DisplayName) {
214219
state.DisplayName = previousState.DisplayName
215220
}
216221

@@ -266,7 +271,7 @@ func (r *functionResource) Update(ctx context.Context, req resource.UpdateReques
266271
state.Fill(function)
267272

268273
// Destination functions append workspace name to display name causing inconsistency
269-
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" {
274+
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" || state.ResourceType.ValueString() == "INSERT_SOURCE" {
270275
state.DisplayName = plan.DisplayName
271276
}
272277

internal/provider/function_resource_test.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,219 @@ func TestAccFunctionResource(t *testing.T) {
269269
},
270270
})
271271
}
272+
273+
func TestAccFunctionResource_InsertSource(t *testing.T) {
274+
t.Parallel()
275+
276+
// keeps track of whether we’ve already updated the Function
277+
updated := 0
278+
279+
// ────────────────────────── Fake Segment API ──────────────────────────
280+
fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
281+
w.Header().Set("content-type", "application/json")
282+
283+
switch {
284+
// ── CREATE ─────────────────────────────────────────────────────────
285+
case req.URL.Path == "/functions" && req.Method == http.MethodPost:
286+
_, _ = w.Write([]byte(`
287+
{
288+
"data": {
289+
"function": {
290+
"id": "my-function-id",
291+
"workspaceId": "my-workspace-id",
292+
"displayName": "My test function",
293+
"description": "My function description",
294+
"logoUrl": "https://segment.com/cool-logo.png",
295+
"code": "// My test code!",
296+
"createdAt": "2023-10-11T18:52:07.087Z",
297+
"createdBy": "my-user-id",
298+
"previewWebhookUrl": "",
299+
"settings": [{
300+
"name": "mySettingName",
301+
"label": "My setting label",
302+
"description": "My setting description",
303+
"type": "STRING",
304+
"required": false,
305+
"sensitive": false
306+
}],
307+
"buildpack": "boreal",
308+
"catalogId": "my-catalog-id",
309+
"batchMaxCount": 0,
310+
"resourceType": "INSERT_SOURCE"
311+
}
312+
}
313+
}`))
314+
// ── UPDATE ─────────────────────────────────────────────────────────
315+
case req.URL.Path == "/functions/my-function-id" && req.Method == http.MethodPatch:
316+
updated++
317+
_, _ = w.Write([]byte(`
318+
{
319+
"data": {
320+
"function": {
321+
"id": "my-function-id",
322+
"workspaceId": "my-workspace-id",
323+
"displayName": "My new test function",
324+
"description": "My new function description",
325+
"logoUrl": "https://segment.com/cool-other-logo.png",
326+
"code": "// My new test code!",
327+
"createdAt": "2023-10-11T18:52:07.087Z",
328+
"createdBy": "my-user-id",
329+
"previewWebhookUrl": "",
330+
"settings": [{
331+
"name": "myNewSettingName",
332+
"label": "My new setting label",
333+
"description": "My new setting description",
334+
"type": "STRING",
335+
"required": true,
336+
"sensitive": true
337+
}],
338+
"buildpack": "boreal",
339+
"catalogId": "my-catalog-id",
340+
"batchMaxCount": 0,
341+
"resourceType": "INSERT_SOURCE"
342+
}
343+
}
344+
}`))
345+
346+
case req.URL.Path == "/functions/my-function-id" && req.Method == http.MethodGet:
347+
if updated == 0 {
348+
_, _ = w.Write([]byte(`
349+
{
350+
"data": {
351+
"function": {
352+
"id": "my-function-id",
353+
"workspaceId": "my-workspace-id",
354+
"displayName": "My test function",
355+
"description": "My function description",
356+
"logoUrl": "https://segment.com/cool-logo.png",
357+
"code": "// My test code!",
358+
"createdAt": "2023-10-11T18:52:07.087Z",
359+
"createdBy": "my-user-id",
360+
"previewWebhookUrl": "",
361+
"settings": [{
362+
"name": "mySettingName",
363+
"label": "My setting label",
364+
"description": "My setting description",
365+
"type": "STRING",
366+
"required": false,
367+
"sensitive": false
368+
}],
369+
"buildpack": "boreal",
370+
"catalogId": "my-catalog-id",
371+
"batchMaxCount": 0,
372+
"resourceType": "INSERT_SOURCE"
373+
}
374+
}
375+
}`))
376+
} else {
377+
_, _ = w.Write([]byte(`
378+
{
379+
"data": {
380+
"function": {
381+
"id": "my-function-id",
382+
"workspaceId": "my-workspace-id",
383+
"displayName": "My new test function",
384+
"description": "My new function description",
385+
"logoUrl": "https://segment.com/cool-other-logo.png",
386+
"code": "// My new test code!",
387+
"createdAt": "2023-10-11T18:52:07.087Z",
388+
"createdBy": "my-user-id",
389+
"previewWebhookUrl": "",
390+
"settings": [{
391+
"name": "myNewSettingName",
392+
"label": "My new setting label",
393+
"description": "My new setting description",
394+
"type": "STRING",
395+
"required": true,
396+
"sensitive": true
397+
}],
398+
"buildpack": "boreal",
399+
"catalogId": "my-catalog-id",
400+
"batchMaxCount": 0,
401+
"resourceType": "INSERT_SOURCE"
402+
}
403+
}
404+
}`))
405+
}
406+
}
407+
}))
408+
defer fakeServer.Close()
409+
410+
providerConfig := `
411+
provider "segment" {
412+
url = "` + fakeServer.URL + `"
413+
token = "abc123"
414+
}
415+
`
416+
417+
resource.Test(t, resource.TestCase{
418+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
419+
Steps: []resource.TestStep{
420+
{
421+
Config: providerConfig + `
422+
resource "segment_function" "test" {
423+
code = "// My test code!"
424+
display_name = "My test function"
425+
logo_url = "https://segment.com/cool-logo.png"
426+
resource_type = "INSERT_SOURCE"
427+
description = "My function description"
428+
settings = [{
429+
name = "mySettingName"
430+
label = "My setting label"
431+
type = "STRING"
432+
description = "My setting description"
433+
required = false
434+
sensitive = false
435+
}]
436+
}
437+
`,
438+
Check: resource.ComposeAggregateTestCheckFunc(
439+
resource.TestCheckResourceAttr("segment_function.test", "id", "my-function-id"),
440+
resource.TestCheckResourceAttr("segment_function.test", "resource_type", "INSERT_SOURCE"),
441+
resource.TestCheckResourceAttr("segment_function.test", "display_name", "My test function"),
442+
resource.TestCheckResourceAttr("segment_function.test", "logo_url", "https://segment.com/cool-logo.png"),
443+
resource.TestCheckResourceAttr("segment_function.test", "description", "My function description"),
444+
resource.TestCheckResourceAttr("segment_function.test", "settings.#", "1"),
445+
resource.TestCheckResourceAttr("segment_function.test", "settings.0.name", "mySettingName"),
446+
resource.TestCheckResourceAttr("segment_function.test", "settings.0.required", "false"),
447+
resource.TestCheckResourceAttr("segment_function.test", "settings.0.sensitive", "false"),
448+
),
449+
},
450+
451+
{
452+
ResourceName: "segment_function.test",
453+
ImportState: true,
454+
ImportStateVerify: true,
455+
},
456+
457+
{
458+
Config: providerConfig + `
459+
resource "segment_function" "test" {
460+
code = "// My new test code!"
461+
display_name = "My new test function"
462+
logo_url = "https://segment.com/cool-other-logo.png"
463+
resource_type = "INSERT_SOURCE"
464+
description = "My new function description"
465+
settings = [{
466+
name = "myNewSettingName"
467+
label = "My new setting label"
468+
type = "STRING"
469+
description = "My new setting description"
470+
required = true
471+
sensitive = true
472+
}]
473+
}
474+
`,
475+
Check: resource.ComposeAggregateTestCheckFunc(
476+
resource.TestCheckResourceAttr("segment_function.test", "resource_type", "INSERT_SOURCE"),
477+
resource.TestCheckResourceAttr("segment_function.test", "display_name", "My new test function"),
478+
resource.TestCheckResourceAttr("segment_function.test", "logo_url", "https://segment.com/cool-other-logo.png"),
479+
resource.TestCheckResourceAttr("segment_function.test", "description", "My new function description"),
480+
resource.TestCheckResourceAttr("segment_function.test", "settings.0.name", "myNewSettingName"),
481+
resource.TestCheckResourceAttr("segment_function.test", "settings.0.required", "true"),
482+
resource.TestCheckResourceAttr("segment_function.test", "settings.0.sensitive", "true"),
483+
),
484+
},
485+
},
486+
})
487+
}

0 commit comments

Comments
 (0)