Skip to content
13 changes: 9 additions & 4 deletions internal/provider/function_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package provider
import (
"context"
"fmt"

"net/http"
"regexp"

Expand All @@ -16,7 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"

tftypes "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/segmentio/public-api-sdk-go/api"
)

Expand All @@ -35,6 +36,10 @@ type functionResource struct {
authContext context.Context
}

func hasValue(v tftypes.String) bool {
return !(v.IsNull() || v.IsUnknown())
}

func (r *functionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_function"
}
Expand Down Expand Up @@ -163,7 +168,7 @@ func (r *functionResource) Create(ctx context.Context, req resource.CreateReques
state.Fill(function)

// Destination functions append workspace name to display name causing inconsistency
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" {
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" || state.ResourceType.ValueString() == "INSERT_SOURCE" {
state.DisplayName = plan.DisplayName
}

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

// Destination functions append workspace name to display name causing inconsistency
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" {
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" || state.ResourceType.ValueString() == "INSERT_SOURCE" && hasValue(previousState.DisplayName) {
state.DisplayName = previousState.DisplayName
}

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

// Destination functions append workspace name to display name causing inconsistency
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" {
if state.ResourceType.ValueString() == "DESTINATION" || state.ResourceType.ValueString() == "INSERT_DESTINATION" || state.ResourceType.ValueString() == "INSERT_SOURCE" {
state.DisplayName = plan.DisplayName
}

Expand Down
216 changes: 216 additions & 0 deletions internal/provider/function_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,219 @@ func TestAccFunctionResource(t *testing.T) {
},
})
}

func TestAccFunctionResource_InsertSource(t *testing.T) {
t.Parallel()

// keeps track of whether we’ve already updated the Function
updated := 0

// ────────────────────────── Fake Segment API ──────────────────────────
fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("content-type", "application/json")

switch {
// ── CREATE ─────────────────────────────────────────────────────────
case req.URL.Path == "/functions" && req.Method == http.MethodPost:
_, _ = w.Write([]byte(`
{
"data": {
"function": {
"id": "my-function-id",
"workspaceId": "my-workspace-id",
"displayName": "My test function",
"description": "My function description",
"logoUrl": "https://segment.com/cool-logo.png",
"code": "// My test code!",
"createdAt": "2023-10-11T18:52:07.087Z",
"createdBy": "my-user-id",
"previewWebhookUrl": "",
"settings": [{
"name": "mySettingName",
"label": "My setting label",
"description": "My setting description",
"type": "STRING",
"required": false,
"sensitive": false
}],
"buildpack": "boreal",
"catalogId": "my-catalog-id",
"batchMaxCount": 0,
"resourceType": "INSERT_SOURCE"
}
}
}`))
// ── UPDATE ─────────────────────────────────────────────────────────
case req.URL.Path == "/functions/my-function-id" && req.Method == http.MethodPatch:
updated++
_, _ = w.Write([]byte(`
{
"data": {
"function": {
"id": "my-function-id",
"workspaceId": "my-workspace-id",
"displayName": "My new test function",
"description": "My new function description",
"logoUrl": "https://segment.com/cool-other-logo.png",
"code": "// My new test code!",
"createdAt": "2023-10-11T18:52:07.087Z",
"createdBy": "my-user-id",
"previewWebhookUrl": "",
"settings": [{
"name": "myNewSettingName",
"label": "My new setting label",
"description": "My new setting description",
"type": "STRING",
"required": true,
"sensitive": true
}],
"buildpack": "boreal",
"catalogId": "my-catalog-id",
"batchMaxCount": 0,
"resourceType": "INSERT_SOURCE"
}
}
}`))

case req.URL.Path == "/functions/my-function-id" && req.Method == http.MethodGet:
if updated == 0 {
_, _ = w.Write([]byte(`
{
"data": {
"function": {
"id": "my-function-id",
"workspaceId": "my-workspace-id",
"displayName": "My test function",
"description": "My function description",
"logoUrl": "https://segment.com/cool-logo.png",
"code": "// My test code!",
"createdAt": "2023-10-11T18:52:07.087Z",
"createdBy": "my-user-id",
"previewWebhookUrl": "",
"settings": [{
"name": "mySettingName",
"label": "My setting label",
"description": "My setting description",
"type": "STRING",
"required": false,
"sensitive": false
}],
"buildpack": "boreal",
"catalogId": "my-catalog-id",
"batchMaxCount": 0,
"resourceType": "INSERT_SOURCE"
}
}
}`))
} else {
_, _ = w.Write([]byte(`
{
"data": {
"function": {
"id": "my-function-id",
"workspaceId": "my-workspace-id",
"displayName": "My new test function",
"description": "My new function description",
"logoUrl": "https://segment.com/cool-other-logo.png",
"code": "// My new test code!",
"createdAt": "2023-10-11T18:52:07.087Z",
"createdBy": "my-user-id",
"previewWebhookUrl": "",
"settings": [{
"name": "myNewSettingName",
"label": "My new setting label",
"description": "My new setting description",
"type": "STRING",
"required": true,
"sensitive": true
}],
"buildpack": "boreal",
"catalogId": "my-catalog-id",
"batchMaxCount": 0,
"resourceType": "INSERT_SOURCE"
}
}
}`))
}
}
}))
defer fakeServer.Close()

providerConfig := `
provider "segment" {
url = "` + fakeServer.URL + `"
token = "abc123"
}
`

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: providerConfig + `
resource "segment_function" "test" {
code = "// My test code!"
display_name = "My test function"
logo_url = "https://segment.com/cool-logo.png"
resource_type = "INSERT_SOURCE"
description = "My function description"
settings = [{
name = "mySettingName"
label = "My setting label"
type = "STRING"
description = "My setting description"
required = false
sensitive = false
}]
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("segment_function.test", "id", "my-function-id"),
resource.TestCheckResourceAttr("segment_function.test", "resource_type", "INSERT_SOURCE"),
resource.TestCheckResourceAttr("segment_function.test", "display_name", "My test function"),
resource.TestCheckResourceAttr("segment_function.test", "logo_url", "https://segment.com/cool-logo.png"),
resource.TestCheckResourceAttr("segment_function.test", "description", "My function description"),
resource.TestCheckResourceAttr("segment_function.test", "settings.#", "1"),
resource.TestCheckResourceAttr("segment_function.test", "settings.0.name", "mySettingName"),
resource.TestCheckResourceAttr("segment_function.test", "settings.0.required", "false"),
resource.TestCheckResourceAttr("segment_function.test", "settings.0.sensitive", "false"),
),
},

{
ResourceName: "segment_function.test",
ImportState: true,
ImportStateVerify: true,
},

{
Config: providerConfig + `
resource "segment_function" "test" {
code = "// My new test code!"
display_name = "My new test function"
logo_url = "https://segment.com/cool-other-logo.png"
resource_type = "INSERT_SOURCE"
description = "My new function description"
settings = [{
name = "myNewSettingName"
label = "My new setting label"
type = "STRING"
description = "My new setting description"
required = true
sensitive = true
}]
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("segment_function.test", "resource_type", "INSERT_SOURCE"),
resource.TestCheckResourceAttr("segment_function.test", "display_name", "My new test function"),
resource.TestCheckResourceAttr("segment_function.test", "logo_url", "https://segment.com/cool-other-logo.png"),
resource.TestCheckResourceAttr("segment_function.test", "description", "My new function description"),
resource.TestCheckResourceAttr("segment_function.test", "settings.0.name", "myNewSettingName"),
resource.TestCheckResourceAttr("segment_function.test", "settings.0.required", "true"),
resource.TestCheckResourceAttr("segment_function.test", "settings.0.sensitive", "true"),
),
},
},
})
}
Loading