Skip to content

Commit e041cbb

Browse files
committed
Add RETL schedule config to destination subscription
1 parent e6e934f commit e041cbb

7 files changed

+517
-25
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/hashicorp/terraform-plugin-framework-validators v0.13.0
1212
github.com/hashicorp/terraform-plugin-go v0.24.0
1313
github.com/hashicorp/terraform-plugin-testing v1.10.0
14-
github.com/segmentio/public-api-sdk-go v0.0.0-20240909200753-311bb8d791a2
14+
github.com/segmentio/public-api-sdk-go v0.0.0-20241017001201-fbbdab459db8
1515
gotest.tools/gotestsum v1.12.0
1616
)
1717

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3V
187187
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
188188
github.com/segmentio/public-api-sdk-go v0.0.0-20240909200753-311bb8d791a2 h1:vlKTelJ32DPBuiiSx2PJaxN9jJd3OFK2avHU/XR/qB8=
189189
github.com/segmentio/public-api-sdk-go v0.0.0-20240909200753-311bb8d791a2/go.mod h1:yKkoPfcOkkYjiZQj4lRWxji0Qwc6ncNEf7wCfywochY=
190+
github.com/segmentio/public-api-sdk-go v0.0.0-20241017001201-fbbdab459db8 h1:pYJu97HA0FVdy+WCqQbS/baDzXxg2q0Vl+akD6SUBSI=
191+
github.com/segmentio/public-api-sdk-go v0.0.0-20241017001201-fbbdab459db8/go.mod h1:yKkoPfcOkkYjiZQj4lRWxji0Qwc6ncNEf7wCfywochY=
190192
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
191193
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
192194
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=

internal/provider/destination_resource_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ func TestAccDestinationResource(t *testing.T) {
138138
if req.Method == http.MethodPatch {
139139
updated++
140140
}
141+
// First update is in create to set the reverse etl model id
141142
if updated > 0 {
142143
payload = `{
143144
"data": {

internal/provider/destination_subscription_resource.go

Lines changed: 193 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@ package provider
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"strings"
78

89
"github.com/segmentio/terraform-provider-segment/internal/provider/docs"
910
"github.com/segmentio/terraform-provider-segment/internal/provider/models"
1011

1112
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
13+
"github.com/hashicorp/terraform-plugin-framework/diag"
1214
"github.com/hashicorp/terraform-plugin-framework/path"
1315
"github.com/hashicorp/terraform-plugin-framework/resource"
1416
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1517
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1618
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
19+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1720

1821
"github.com/segmentio/public-api-sdk-go/api"
1922
)
@@ -91,12 +94,27 @@ func (r *destinationSubscriptionResource) Schema(_ context.Context, _ resource.S
9194
Description: `The customer settings for action fields. Only settings included in the configuration will be managed by Terraform.`,
9295
CustomType: jsontypes.NormalizedType{},
9396
},
97+
"reverse_etl_schedule": schema.SingleNestedAttribute{
98+
Optional: true,
99+
Description: "(Reverse ETL only) The schedule for the subscription being attached to ReverseETL model.",
100+
Attributes: map[string]schema.Attribute{
101+
"strategy": schema.StringAttribute{
102+
Required: true,
103+
Description: "Strategy supports three modes: PERIODIC, SPECIFIC_DAYS, or MANUAL.",
104+
},
105+
"config": schema.StringAttribute{
106+
Optional: true,
107+
Description: "Configures the schedule for the subscription.",
108+
CustomType: jsontypes.NormalizedType{},
109+
},
110+
},
111+
},
94112
},
95113
}
96114
}
97115

98116
func (r *destinationSubscriptionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
99-
var plan models.DestinationSubscriptionState
117+
var plan models.DestinationSubscriptionPlan
100118
diags := req.Plan.Get(ctx, &plan)
101119
resp.Diagnostics.Append(diags...)
102120
if resp.Diagnostics.HasError() {
@@ -110,6 +128,15 @@ func (r *destinationSubscriptionResource) Create(ctx context.Context, req resour
110128
return
111129
}
112130

131+
if !plan.ModelID.IsNull() && !plan.ModelID.IsUnknown() && (plan.ReverseETLSchedule.IsNull() || plan.ReverseETLSchedule.IsUnknown()) {
132+
resp.Diagnostics.AddError(
133+
"Reverse ETL model ID provided without reverse ETL schedule",
134+
"Reverse ETL model ID must be provided with a reverse ETL schedule",
135+
)
136+
137+
return
138+
}
139+
113140
out, body, err := r.client.DestinationsAPI.CreateDestinationSubscription(r.authContext, plan.DestinationID.ValueString()).CreateDestinationSubscriptionAlphaInput(api.CreateDestinationSubscriptionAlphaInput{
114141
Name: plan.Name.ValueString(),
115142
ActionId: plan.ActionID.ValueString(),
@@ -130,9 +157,38 @@ func (r *destinationSubscriptionResource) Create(ctx context.Context, req resour
130157
return
131158
}
132159

133-
destinationSubscription := out.Data.GetDestinationSubscription()
160+
resp.State.SetAttribute(ctx, path.Root("id"), out.Data.DestinationSubscription.Id)
161+
resp.State.SetAttribute(ctx, path.Root("destination_id"), out.Data.DestinationSubscription.DestinationId)
162+
163+
reverseETLSchedule, diags := getSchedule(ctx, plan.ReverseETLSchedule)
164+
if diags.HasError() {
165+
resp.Diagnostics.Append(diags...)
166+
return
167+
}
168+
169+
updateOut, body, err := r.client.DestinationsAPI.UpdateSubscriptionForDestination(r.authContext, plan.DestinationID.ValueString(), out.Data.DestinationSubscription.Id).UpdateSubscriptionForDestinationAlphaInput(api.UpdateSubscriptionForDestinationAlphaInput{
170+
Input: api.DestinationSubscriptionUpdateInput{
171+
Name: plan.Name.ValueStringPointer(),
172+
Trigger: plan.Trigger.ValueStringPointer(),
173+
Enabled: plan.Enabled.ValueBoolPointer(),
174+
Settings: settings,
175+
ReverseETLModelId: plan.ModelID.ValueStringPointer(),
176+
ReverseETLSchedule: reverseETLSchedule,
177+
},
178+
}).Execute()
179+
if body != nil {
180+
defer body.Body.Close()
181+
}
182+
if err != nil {
183+
resp.Diagnostics.AddError(
184+
"Unable to update Destination subscription",
185+
getError(err, body),
186+
)
187+
188+
return
189+
}
134190

135-
resp.State.SetAttribute(ctx, path.Root("id"), destinationSubscription.Id)
191+
destinationSubscription := updateOut.Data.Subscription
136192

137193
var state models.DestinationSubscriptionState
138194
err = state.Fill(destinationSubscription)
@@ -203,7 +259,7 @@ func (r *destinationSubscriptionResource) Read(ctx context.Context, req resource
203259
}
204260

205261
func (r *destinationSubscriptionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
206-
var plan models.DestinationSubscriptionState
262+
var plan models.DestinationSubscriptionPlan
207263
diags := req.Plan.Get(ctx, &plan)
208264
resp.Diagnostics.Append(diags...)
209265
if resp.Diagnostics.HasError() {
@@ -224,12 +280,29 @@ func (r *destinationSubscriptionResource) Update(ctx context.Context, req resour
224280
return
225281
}
226282

283+
if !plan.ModelID.IsNull() && !plan.ModelID.IsUnknown() && (plan.ReverseETLSchedule.IsNull() || plan.ReverseETLSchedule.IsUnknown()) {
284+
resp.Diagnostics.AddError(
285+
"Reverse ETL model ID provided without reverse ETL schedule",
286+
"Reverse ETL model ID must be provided with a reverse ETL schedule",
287+
)
288+
289+
return
290+
}
291+
292+
reverseETLSchedule, diags := getSchedule(ctx, plan.ReverseETLSchedule)
293+
if diags.HasError() {
294+
resp.Diagnostics.Append(diags...)
295+
return
296+
}
297+
227298
out, body, err := r.client.DestinationsAPI.UpdateSubscriptionForDestination(r.authContext, state.DestinationID.ValueString(), state.ID.ValueString()).UpdateSubscriptionForDestinationAlphaInput(api.UpdateSubscriptionForDestinationAlphaInput{
228299
Input: api.DestinationSubscriptionUpdateInput{
229-
Name: plan.Name.ValueStringPointer(),
230-
Trigger: plan.Trigger.ValueStringPointer(),
231-
Enabled: plan.Enabled.ValueBoolPointer(),
232-
Settings: settings,
300+
Name: plan.Name.ValueStringPointer(),
301+
Trigger: plan.Trigger.ValueStringPointer(),
302+
Enabled: plan.Enabled.ValueBoolPointer(),
303+
Settings: settings,
304+
ReverseETLModelId: plan.ModelID.ValueStringPointer(),
305+
ReverseETLSchedule: reverseETLSchedule,
233306
},
234307
}).Execute()
235308
if body != nil {
@@ -320,3 +393,115 @@ func (r *destinationSubscriptionResource) Configure(_ context.Context, req resou
320393
r.client = config.client
321394
r.authContext = config.authContext
322395
}
396+
397+
func getSchedule(ctx context.Context, planSchedule basetypes.ObjectValue) (*api.ReverseEtlScheduleDefinition, diag.Diagnostics) {
398+
var reverseETLSchedule *api.ReverseEtlScheduleDefinition
399+
var diags diag.Diagnostics
400+
if !planSchedule.IsNull() && !planSchedule.IsUnknown() {
401+
reverseETLSchedule = &api.ReverseEtlScheduleDefinition{}
402+
403+
wrappedReverseETLModelScheduleStrategy, err := planSchedule.Attributes()["strategy"].ToTerraformValue(ctx)
404+
if err != nil {
405+
diags.AddError(
406+
"Unable to decode reverse ETL schedule strategy",
407+
err.Error(),
408+
)
409+
410+
return nil, diags
411+
}
412+
413+
var reverseETLModelScheduleStrategy string
414+
err = wrappedReverseETLModelScheduleStrategy.As(&reverseETLModelScheduleStrategy)
415+
if err != nil {
416+
diags.AddError(
417+
"Unable to decode reverse ETL schedule strategy",
418+
err.Error(),
419+
)
420+
421+
return nil, diags
422+
}
423+
424+
reverseETLSchedule.Strategy = reverseETLModelScheduleStrategy
425+
426+
wrappedReverseETLModelScheduleConfig, err := planSchedule.Attributes()["config"].ToTerraformValue(ctx)
427+
if err != nil {
428+
diags.AddError(
429+
"Unable to decode reverse ETL schedule config",
430+
err.Error(),
431+
)
432+
433+
return nil, diags
434+
}
435+
436+
if !wrappedReverseETLModelScheduleConfig.IsNull() && wrappedReverseETLModelScheduleConfig.IsKnown() {
437+
if reverseETLSchedule.Strategy == "PERIODIC" {
438+
reverseETLModelScheduleConfig := api.ReverseEtlPeriodicScheduleConfig{}
439+
var config string
440+
err = wrappedReverseETLModelScheduleConfig.As(&config)
441+
if err != nil {
442+
diags.AddError(
443+
"Unable to decode reverse ETL schedule config",
444+
err.Error(),
445+
)
446+
447+
return nil, diags
448+
}
449+
450+
err = json.Unmarshal([]byte(config), &reverseETLModelScheduleConfig)
451+
if err != nil {
452+
diags.AddError(
453+
"Unable to decode reverse ETL schedule config",
454+
err.Error(),
455+
)
456+
457+
return nil, diags
458+
}
459+
460+
reverseETLSchedule.Config = *api.NewNullableConfig(&api.Config{
461+
ReverseEtlPeriodicScheduleConfig: &reverseETLModelScheduleConfig,
462+
})
463+
} else if reverseETLSchedule.Strategy == "SPECIFIC_DAYS" {
464+
reverseETLModelScheduleConfig := api.ReverseEtlSpecificTimeScheduleConfig{}
465+
var config string
466+
err = wrappedReverseETLModelScheduleConfig.As(&config)
467+
if err != nil {
468+
diags.AddError(
469+
"Unable to decode reverse ETL schedule config",
470+
err.Error(),
471+
)
472+
473+
return nil, diags
474+
}
475+
476+
err = json.Unmarshal([]byte(config), &reverseETLModelScheduleConfig)
477+
if err != nil {
478+
diags.AddError(
479+
"Unable to decode reverse ETL schedule config",
480+
err.Error(),
481+
)
482+
483+
return nil, diags
484+
}
485+
486+
reverseETLSchedule.Config = *api.NewNullableConfig(&api.Config{
487+
ReverseEtlSpecificTimeScheduleConfig: &reverseETLModelScheduleConfig,
488+
})
489+
} else if reverseETLSchedule.Strategy == "MANUAL" {
490+
diags.AddError(
491+
"Manual reverse ETL schedule strategy does not require a config",
492+
"Manual reverse ETL schedule strategy does not require a config",
493+
)
494+
reverseETLSchedule.Config = *api.NewNullableConfig(nil)
495+
} else {
496+
diags.AddError(
497+
"Unsupported reverse ETL schedule strategy",
498+
fmt.Sprintf("Strategy %q is not supported", reverseETLSchedule.Strategy),
499+
)
500+
501+
return nil, diags
502+
}
503+
}
504+
}
505+
506+
return reverseETLSchedule, diags
507+
}

0 commit comments

Comments
 (0)