Skip to content

Commit 6777bf6

Browse files
committed
feat: Add workspace_name field in stream_connection resource and datasource
Signed-off-by: Kush Patel <[email protected]>
1 parent 423efb6 commit 6777bf6

9 files changed

+261
-33
lines changed

docs/data-sources/stream_connection.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,25 @@ data "mongodbatlas_stream_connection" "example" {
1212
}
1313
```
1414

15+
### Example using workspace_name
16+
17+
```terraform
18+
data "mongodbatlas_stream_connection" "example" {
19+
project_id = "<PROJECT_ID>"
20+
workspace_name = "<WORKSPACE_NAME>"
21+
connection_name = "<CONNECTION_NAME>"
22+
}
23+
```
24+
1525
## Argument Reference
1626

1727
* `project_id` - (Required) Unique 24-hexadecimal digit string that identifies your project.
18-
* `instance_name` - (Required) Human-readable label that identifies the stream instance.
28+
* `instance_name` - (Optional) Human-readable label that identifies the stream instance. Conflicts with `workspace_name`.
29+
* `workspace_name` - (Optional) Human-readable label that identifies the stream instance. This is an alias for `instance_name`. Conflicts with `instance_name`.
1930
* `connection_name` - (Required) Human-readable label that identifies the stream connection. In the case of the Sample type, this is the name of the sample source.
2031

32+
~> **NOTE:** Either `instance_name` or `workspace_name` must be provided, but not both. These fields are functionally identical and `workspace_name` is provided as an alias for `instance_name`.
33+
2134
## Attributes Reference
2235

2336
* `type` - Type of connection. Can be `AWSLambda`, `Cluster`, `Https`, `Kafka` or `Sample`.

docs/data-sources/stream_connections.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ data "mongodbatlas_stream_connections" "test" {
1414
## Argument Reference
1515

1616
* `project_id` - (Required) Unique 24-hexadecimal digit string that identifies your project.
17-
* `instance_name` - (Required) Human-readable label that identifies the stream instance.
17+
* `instance_name` - (Optional) Human-readable label that identifies the stream instance. Conflicts with `workspace_name`.
18+
* `workspace_name` - (Optional) Human-readable label that identifies the stream instance. This is an alias for `instance_name`. Conflicts with `instance_name`.
19+
20+
~> **NOTE:** Either `instance_name` or `workspace_name` must be provided, but not both. These fields are functionally identical and `workspace_name` is provided as an alias for `instance_name`.
1821

1922
* `page_num` - (Optional) Number of the page that displays the current set of the total objects that the response returns. Defaults to `1`.
2023
* `items_per_page` - (Optional) Number of items that the response returns per page, up to a maximum of `500`. Defaults to `100`.

docs/resources/stream_connection.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ resource "mongodbatlas_stream_connection" "test" {
1919
}
2020
```
2121

22+
### Example Cluster Connection using workspace_name
23+
24+
```terraform
25+
resource "mongodbatlas_stream_connection" "test" {
26+
project_id = var.project_id
27+
workspace_name = "WorkspaceName"
28+
connection_name = "ConnectionName"
29+
type = "Cluster"
30+
cluster_name = "Cluster0"
31+
}
32+
```
33+
2234
### Example Cross Project Cluster Connection
2335

2436
```terraform
@@ -113,10 +125,13 @@ resource "mongodbatlas_stream_connection" "example-https" {
113125
## Argument Reference
114126

115127
* `project_id` - (Required) Unique 24-hexadecimal digit string that identifies your project.
116-
* `instance_name` - (Required) Human-readable label that identifies the stream instance.
128+
* `instance_name` - (Optional) Human-readable label that identifies the stream instance. Conflicts with `workspace_name`.
129+
* `workspace_name` - (Optional) Human-readable label that identifies the stream instance. This is an alias for `instance_name`. Conflicts with `instance_name`.
117130
* `connection_name` - (Required) Human-readable label that identifies the stream connection. In the case of the Sample type, this is the name of the sample source.
118131
* `type` - (Required) Type of connection. Can be `AWSLambda`, `Cluster`, `Https`, `Kafka` or `Sample`.
119132

133+
~> **NOTE:** Either `instance_name` or `workspace_name` must be provided, but not both. These fields are functionally identical and `workspace_name` is provided as an alias for `instance_name`.
134+
120135
If `type` is of value `Cluster` the following additional arguments are defined:
121136
* `cluster_name` - Name of the cluster configured for this connection.
122137
* `db_role_to_execute` - The name of a Built in or Custom DB Role to connect to an Atlas Cluster. See [DBRoleToExecute](#DBRoleToExecute).

internal/service/streamconnection/data_source_stream_connection.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type streamConnectionDS struct {
2525

2626
func (d *streamConnectionDS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
2727
resp.Schema = conversion.DataSourceSchemaFromResource(ResourceSchema(ctx), &conversion.DataSourceSchemaRequest{
28-
RequiredFields: []string{"project_id", "instance_name", "connection_name"},
28+
RequiredFields: []string{"project_id", "connection_name"},
2929
})
3030
}
3131

@@ -38,15 +38,19 @@ func (d *streamConnectionDS) Read(ctx context.Context, req datasource.ReadReques
3838

3939
connV2 := d.Client.AtlasV2
4040
projectID := streamConnectionConfig.ProjectID.ValueString()
41-
instanceName := streamConnectionConfig.InstanceName.ValueString()
41+
instanceName := getEffectiveInstanceName(&streamConnectionConfig)
42+
if instanceName == "" {
43+
resp.Diagnostics.AddError("validation error", "either instance_name or workspace_name must be provided")
44+
return
45+
}
4246
connectionName := streamConnectionConfig.ConnectionName.ValueString()
4347
apiResp, _, err := connV2.StreamsApi.GetStreamConnection(ctx, projectID, instanceName, connectionName).Execute()
4448
if err != nil {
4549
resp.Diagnostics.AddError("error fetching resource", err.Error())
4650
return
4751
}
4852

49-
newStreamConnectionModel, diags := NewTFStreamConnection(ctx, projectID, instanceName, nil, apiResp)
53+
newStreamConnectionModel, diags := NewTFStreamConnectionWithOriginal(ctx, projectID, instanceName, nil, apiResp, &streamConnectionConfig)
5054
if diags.HasError() {
5155
resp.Diagnostics.Append(diags...)
5256
return

internal/service/streamconnection/data_source_stream_connections.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,22 @@ type streamConnectionsDS struct {
2828

2929
func (d *streamConnectionsDS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
3030
resp.Schema = conversion.PluralDataSourceSchemaFromResource(ResourceSchema(ctx), &conversion.PluralDataSourceSchemaRequest{
31-
RequiredFields: []string{"project_id", "instance_name"},
31+
RequiredFields: []string{"project_id"},
3232
HasLegacyFields: true,
3333
})
3434
}
3535

36+
// getEffectiveInstanceNameForDS returns the instance name from either instance_name or workspace_name field for datasource model
37+
func getEffectiveInstanceNameForDS(model *TFStreamConnectionsDSModel) string {
38+
if !model.InstanceName.IsNull() && !model.InstanceName.IsUnknown() {
39+
return model.InstanceName.ValueString()
40+
}
41+
if !model.WorkspaceName.IsNull() && !model.WorkspaceName.IsUnknown() {
42+
return model.WorkspaceName.ValueString()
43+
}
44+
return ""
45+
}
46+
3647
func (d *streamConnectionsDS) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
3748
var streamConnectionsConfig TFStreamConnectionsDSModel
3849
resp.Diagnostics.Append(req.Config.Get(ctx, &streamConnectionsConfig)...)
@@ -42,7 +53,11 @@ func (d *streamConnectionsDS) Read(ctx context.Context, req datasource.ReadReque
4253

4354
connV2 := d.Client.AtlasV2
4455
projectID := streamConnectionsConfig.ProjectID.ValueString()
45-
instanceName := streamConnectionsConfig.InstanceName.ValueString()
56+
instanceName := getEffectiveInstanceNameForDS(&streamConnectionsConfig)
57+
if instanceName == "" {
58+
resp.Diagnostics.AddError("validation error", "either instance_name or workspace_name must be provided")
59+
return
60+
}
4661
itemsPerPage := streamConnectionsConfig.ItemsPerPage.ValueInt64Pointer()
4762
pageNum := streamConnectionsConfig.PageNum.ValueInt64Pointer()
4863

@@ -58,7 +73,7 @@ func (d *streamConnectionsDS) Read(ctx context.Context, req datasource.ReadReque
5873
return
5974
}
6075

61-
newStreamConnectionsModel, diags := NewTFStreamConnections(ctx, &streamConnectionsConfig, apiResp)
76+
newStreamConnectionsModel, diags := NewTFStreamConnectionsWithOriginal(ctx, &streamConnectionsConfig, apiResp)
6277
if diags.HasError() {
6378
resp.Diagnostics.Append(diags...)
6479
return
@@ -67,11 +82,12 @@ func (d *streamConnectionsDS) Read(ctx context.Context, req datasource.ReadReque
6782
}
6883

6984
type TFStreamConnectionsDSModel struct {
70-
ID types.String `tfsdk:"id"`
71-
ProjectID types.String `tfsdk:"project_id"`
72-
InstanceName types.String `tfsdk:"instance_name"`
73-
Results []TFStreamConnectionModel `tfsdk:"results"`
74-
PageNum types.Int64 `tfsdk:"page_num"`
75-
ItemsPerPage types.Int64 `tfsdk:"items_per_page"`
76-
TotalCount types.Int64 `tfsdk:"total_count"`
85+
ID types.String `tfsdk:"id"`
86+
ProjectID types.String `tfsdk:"project_id"`
87+
InstanceName types.String `tfsdk:"instance_name"`
88+
WorkspaceName types.String `tfsdk:"workspace_name"`
89+
Results []TFStreamConnectionModel `tfsdk:"results"`
90+
PageNum types.Int64 `tfsdk:"page_num"`
91+
ItemsPerPage types.Int64 `tfsdk:"items_per_page"`
92+
TotalCount types.Int64 `tfsdk:"total_count"`
7793
}

internal/service/streamconnection/model_stream_connection.go

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,14 @@ func NewStreamConnectionUpdateReq(ctx context.Context, plan *TFStreamConnectionM
118118
}
119119

120120
func NewTFStreamConnection(ctx context.Context, projID, instanceName string, currAuthConfig *types.Object, apiResp *admin.StreamsConnection) (*TFStreamConnectionModel, diag.Diagnostics) {
121+
return NewTFStreamConnectionWithOriginal(ctx, projID, instanceName, currAuthConfig, apiResp, nil)
122+
}
123+
124+
func NewTFStreamConnectionWithOriginal(ctx context.Context, projID, instanceName string, currAuthConfig *types.Object, apiResp *admin.StreamsConnection, originalModel *TFStreamConnectionModel) (*TFStreamConnectionModel, diag.Diagnostics) {
121125
rID := fmt.Sprintf("%s-%s-%s", instanceName, projID, conversion.SafeString(apiResp.Name))
122126
connectionModel := TFStreamConnectionModel{
123127
ID: types.StringValue(rID),
124128
ProjectID: types.StringValue(projID),
125-
InstanceName: types.StringValue(instanceName),
126129
ConnectionName: types.StringPointerValue(apiResp.Name),
127130
Type: types.StringPointerValue(apiResp.Type),
128131
ClusterName: types.StringPointerValue(apiResp.ClusterName),
@@ -131,6 +134,21 @@ func NewTFStreamConnection(ctx context.Context, projID, instanceName string, cur
131134
URL: types.StringPointerValue(apiResp.Url),
132135
}
133136

137+
// Set the appropriate field based on the original model
138+
if originalModel != nil {
139+
if !originalModel.WorkspaceName.IsNull() && !originalModel.WorkspaceName.IsUnknown() {
140+
connectionModel.WorkspaceName = types.StringValue(instanceName)
141+
connectionModel.InstanceName = types.StringNull()
142+
} else {
143+
connectionModel.InstanceName = types.StringValue(instanceName)
144+
connectionModel.WorkspaceName = types.StringNull()
145+
}
146+
} else {
147+
// Default to instance_name for backward compatibility
148+
connectionModel.InstanceName = types.StringValue(instanceName)
149+
connectionModel.WorkspaceName = types.StringNull()
150+
}
151+
134152
authModel, diags := newTFConnectionAuthenticationModel(ctx, currAuthConfig, apiResp.Authentication)
135153
if diags.HasError() {
136154
return nil, diags
@@ -238,26 +256,42 @@ func newTFConnectionAuthenticationModel(ctx context.Context, currAuthConfig *typ
238256
}
239257

240258
func NewTFStreamConnections(ctx context.Context,
259+
streamConnectionsConfig *TFStreamConnectionsDSModel,
260+
paginatedResult *admin.PaginatedApiStreamsConnection) (*TFStreamConnectionsDSModel, diag.Diagnostics) {
261+
return NewTFStreamConnectionsWithOriginal(ctx, streamConnectionsConfig, paginatedResult)
262+
}
263+
264+
func NewTFStreamConnectionsWithOriginal(ctx context.Context,
241265
streamConnectionsConfig *TFStreamConnectionsDSModel,
242266
paginatedResult *admin.PaginatedApiStreamsConnection) (*TFStreamConnectionsDSModel, diag.Diagnostics) {
243267
input := paginatedResult.GetResults()
244268
results := make([]TFStreamConnectionModel, len(input))
269+
270+
// Determine the effective instance name
271+
var instanceName string
272+
if !streamConnectionsConfig.InstanceName.IsNull() && !streamConnectionsConfig.InstanceName.IsUnknown() {
273+
instanceName = streamConnectionsConfig.InstanceName.ValueString()
274+
} else if !streamConnectionsConfig.WorkspaceName.IsNull() && !streamConnectionsConfig.WorkspaceName.IsUnknown() {
275+
instanceName = streamConnectionsConfig.WorkspaceName.ValueString()
276+
}
277+
245278
for i := range input {
246279
projectID := streamConnectionsConfig.ProjectID.ValueString()
247-
instanceName := streamConnectionsConfig.InstanceName.ValueString()
248280
connectionModel, diags := NewTFStreamConnection(ctx, projectID, instanceName, nil, &input[i])
249281
if diags.HasError() {
250282
return nil, diags
251283
}
252284
results[i] = *connectionModel
253285
}
286+
254287
return &TFStreamConnectionsDSModel{
255-
ID: types.StringValue(id.UniqueId()),
256-
ProjectID: streamConnectionsConfig.ProjectID,
257-
InstanceName: streamConnectionsConfig.InstanceName,
258-
Results: results,
259-
PageNum: streamConnectionsConfig.PageNum,
260-
ItemsPerPage: streamConnectionsConfig.ItemsPerPage,
261-
TotalCount: types.Int64PointerValue(conversion.IntPtrToInt64Ptr(paginatedResult.TotalCount)),
288+
ID: types.StringValue(id.UniqueId()),
289+
ProjectID: streamConnectionsConfig.ProjectID,
290+
InstanceName: streamConnectionsConfig.InstanceName,
291+
WorkspaceName: streamConnectionsConfig.WorkspaceName,
292+
Results: results,
293+
PageNum: streamConnectionsConfig.PageNum,
294+
ItemsPerPage: streamConnectionsConfig.ItemsPerPage,
295+
TotalCount: types.Int64PointerValue(conversion.IntPtrToInt64Ptr(paginatedResult.TotalCount)),
262296
}, nil
263297
}

internal/service/streamconnection/resource_schema.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55

66
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
7+
"github.com/hashicorp/terraform-plugin-framework/path"
78
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
89
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
910
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -28,10 +29,26 @@ func ResourceSchema(ctx context.Context) schema.Schema {
2829
},
2930
},
3031
"instance_name": schema.StringAttribute{
31-
Required: true,
32+
Optional: true,
3233
PlanModifiers: []planmodifier.String{
3334
stringplanmodifier.RequiresReplace(),
3435
},
36+
Validators: []validator.String{
37+
stringvalidator.ConflictsWith(path.Expressions{
38+
path.MatchRelative().AtParent().AtName("workspace_name"),
39+
}...),
40+
},
41+
},
42+
"workspace_name": schema.StringAttribute{
43+
Optional: true,
44+
PlanModifiers: []planmodifier.String{
45+
stringplanmodifier.RequiresReplace(),
46+
},
47+
Validators: []validator.String{
48+
stringvalidator.ConflictsWith(path.Expressions{
49+
path.MatchRelative().AtParent().AtName("instance_name"),
50+
}...),
51+
},
3552
},
3653
"connection_name": schema.StringAttribute{
3754
Required: true,

internal/service/streamconnection/resource_stream_connection.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type TFStreamConnectionModel struct {
3737
ID types.String `tfsdk:"id"`
3838
ProjectID types.String `tfsdk:"project_id"`
3939
InstanceName types.String `tfsdk:"instance_name"`
40+
WorkspaceName types.String `tfsdk:"workspace_name"`
4041
ConnectionName types.String `tfsdk:"connection_name"`
4142
Type types.String `tfsdk:"type"`
4243
ClusterName types.String `tfsdk:"cluster_name"`
@@ -116,6 +117,17 @@ func (r *streamConnectionRS) Schema(ctx context.Context, req resource.SchemaRequ
116117
conversion.UpdateSchemaDescription(&resp.Schema)
117118
}
118119

120+
// getEffectiveInstanceName returns the instance name from either instance_name or workspace_name field
121+
func getEffectiveInstanceName(model *TFStreamConnectionModel) string {
122+
if !model.InstanceName.IsNull() && !model.InstanceName.IsUnknown() {
123+
return model.InstanceName.ValueString()
124+
}
125+
if !model.WorkspaceName.IsNull() && !model.WorkspaceName.IsUnknown() {
126+
return model.WorkspaceName.ValueString()
127+
}
128+
return ""
129+
}
130+
119131
func (r *streamConnectionRS) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
120132
var streamConnectionPlan TFStreamConnectionModel
121133
resp.Diagnostics.Append(req.Plan.Get(ctx, &streamConnectionPlan)...)
@@ -125,7 +137,11 @@ func (r *streamConnectionRS) Create(ctx context.Context, req resource.CreateRequ
125137

126138
connV2 := r.Client.AtlasV2
127139
projectID := streamConnectionPlan.ProjectID.ValueString()
128-
instanceName := streamConnectionPlan.InstanceName.ValueString()
140+
instanceName := getEffectiveInstanceName(&streamConnectionPlan)
141+
if instanceName == "" {
142+
resp.Diagnostics.AddError("validation error", "either instance_name or workspace_name must be provided")
143+
return
144+
}
129145
streamConnectionReq, diags := NewStreamConnectionReq(ctx, &streamConnectionPlan)
130146
if diags.HasError() {
131147
resp.Diagnostics.Append(diags...)
@@ -137,7 +153,7 @@ func (r *streamConnectionRS) Create(ctx context.Context, req resource.CreateRequ
137153
return
138154
}
139155

140-
newStreamConnectionModel, diags := NewTFStreamConnection(ctx, projectID, instanceName, &streamConnectionPlan.Authentication, apiResp)
156+
newStreamConnectionModel, diags := NewTFStreamConnectionWithOriginal(ctx, projectID, instanceName, &streamConnectionPlan.Authentication, apiResp, &streamConnectionPlan)
141157
if diags.HasError() {
142158
resp.Diagnostics.Append(diags...)
143159
return
@@ -154,7 +170,11 @@ func (r *streamConnectionRS) Read(ctx context.Context, req resource.ReadRequest,
154170

155171
connV2 := r.Client.AtlasV2
156172
projectID := streamConnectionState.ProjectID.ValueString()
157-
instanceName := streamConnectionState.InstanceName.ValueString()
173+
instanceName := getEffectiveInstanceName(&streamConnectionState)
174+
if instanceName == "" {
175+
resp.Diagnostics.AddError("validation error", "either instance_name or workspace_name must be provided")
176+
return
177+
}
158178
connectionName := streamConnectionState.ConnectionName.ValueString()
159179
apiResp, getResp, err := connV2.StreamsApi.GetStreamConnection(ctx, projectID, instanceName, connectionName).Execute()
160180
if err != nil {
@@ -166,7 +186,7 @@ func (r *streamConnectionRS) Read(ctx context.Context, req resource.ReadRequest,
166186
return
167187
}
168188

169-
newStreamConnectionModel, diags := NewTFStreamConnection(ctx, projectID, instanceName, &streamConnectionState.Authentication, apiResp)
189+
newStreamConnectionModel, diags := NewTFStreamConnectionWithOriginal(ctx, projectID, instanceName, &streamConnectionState.Authentication, apiResp, &streamConnectionState)
170190
if diags.HasError() {
171191
resp.Diagnostics.Append(diags...)
172192
return
@@ -183,7 +203,11 @@ func (r *streamConnectionRS) Update(ctx context.Context, req resource.UpdateRequ
183203

184204
connV2 := r.Client.AtlasV2
185205
projectID := streamConnectionPlan.ProjectID.ValueString()
186-
instanceName := streamConnectionPlan.InstanceName.ValueString()
206+
instanceName := getEffectiveInstanceName(&streamConnectionPlan)
207+
if instanceName == "" {
208+
resp.Diagnostics.AddError("validation error", "either instance_name or workspace_name must be provided")
209+
return
210+
}
187211
connectionName := streamConnectionPlan.ConnectionName.ValueString()
188212
streamConnectionReq, diags := NewStreamConnectionUpdateReq(ctx, &streamConnectionPlan)
189213
if diags.HasError() {
@@ -196,7 +220,7 @@ func (r *streamConnectionRS) Update(ctx context.Context, req resource.UpdateRequ
196220
return
197221
}
198222

199-
newStreamConnectionModel, diags := NewTFStreamConnection(ctx, projectID, instanceName, &streamConnectionPlan.Authentication, apiResp)
223+
newStreamConnectionModel, diags := NewTFStreamConnectionWithOriginal(ctx, projectID, instanceName, &streamConnectionPlan.Authentication, apiResp, &streamConnectionPlan)
200224
if diags.HasError() {
201225
resp.Diagnostics.Append(diags...)
202226
return
@@ -213,7 +237,11 @@ func (r *streamConnectionRS) Delete(ctx context.Context, req resource.DeleteRequ
213237

214238
connV2 := r.Client.AtlasV2
215239
projectID := streamConnectionState.ProjectID.ValueString()
216-
instanceName := streamConnectionState.InstanceName.ValueString()
240+
instanceName := getEffectiveInstanceName(streamConnectionState)
241+
if instanceName == "" {
242+
resp.Diagnostics.AddError("validation error", "either instance_name or workspace_name must be provided")
243+
return
244+
}
217245
connectionName := streamConnectionState.ConnectionName.ValueString()
218246
if err := DeleteStreamConnection(ctx, connV2.StreamsApi, projectID, instanceName, connectionName, 10*time.Minute); err != nil {
219247
resp.Diagnostics.AddError("error deleting resource", err.Error())

0 commit comments

Comments
 (0)