From 84cda970648b1f613bbaf3246659cb3ae8791ba9 Mon Sep 17 00:00:00 2001 From: Graham Russell Date: Thu, 12 Dec 2024 13:22:24 +0000 Subject: [PATCH] [INF-5307] - Add the MongoDB Ably Ingress Rule This adds the new Ably Ingress rule for MongoDB. --- docs/resources/ingress_rule_mongodb.md | 44 +++ docs/resources/rule_azure_function.md | 2 +- examples/playground/ingress_rule_mongodb.tf | 19 ++ .../playground/rule_http_azure_function.tf | 2 +- .../resources/rule_http_azure_function.tf | 2 +- go.mod | 2 +- go.sum | 2 + internal/provider/ingress_rules.go | 297 ++++++++++++++++++ internal/provider/models.go | 29 ++ internal/provider/provider.go | 1 + .../resource_ably_ingress_rule_mongo.go | 94 ++++++ .../resource_ably_ingress_rule_mongo_test.go | 101 ++++++ .../provider/resource_ably_namespace_test.go | 4 +- .../resource_ably_rule_azure_function_test.go | 4 +- templates/index.md.tmpl | 6 +- 15 files changed, 598 insertions(+), 11 deletions(-) create mode 100644 docs/resources/ingress_rule_mongodb.md create mode 100644 examples/playground/ingress_rule_mongodb.tf create mode 100644 internal/provider/ingress_rules.go create mode 100644 internal/provider/resource_ably_ingress_rule_mongo.go create mode 100644 internal/provider/resource_ably_ingress_rule_mongo_test.go diff --git a/docs/resources/ingress_rule_mongodb.md b/docs/resources/ingress_rule_mongodb.md new file mode 100644 index 0000000..0633df2 --- /dev/null +++ b/docs/resources/ingress_rule_mongodb.md @@ -0,0 +1,44 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "ably_ingress_rule_mongodb Resource - terraform-provider-ably" +subcategory: "" +description: |- + The ably_ingress_rule_mongodb resource sets up a MongoDB Integration Rule to stream document changes from a database collection over Ably. +--- + +# ably_ingress_rule_mongodb (Resource) + +The `ably_ingress_rule_mongodb` resource sets up a MongoDB Integration Rule to stream document changes from a database collection over Ably. + + + + +## Schema + +### Required + +- `app_id` (String) The Ably application ID. +- `target` (Attributes) object (rule_source) (see [below for nested schema](#nestedatt--target)) + +### Optional + +- `status` (String) The status of the rule. Rules can be enabled or disabled. + +### Read-Only + +- `id` (String) The rule ID. + + +### Nested Schema for `target` + +Required: + +- `collection` (String) What the connector should watch within the database. The connector only supports watching collections. +- `database` (String) The MongoDB Database Name +- `full_document` (String) Controls whether the full document should be included in the published change events. Full Document is not available by default in all types of change event. Possible values are updateLookup, whenAvailable, off. The default is off. +- `full_document_before_change` (String) Controls wheter the full document before the change should be included in the change event. Full Document before change is not available on all types of change event. Possible values are whenAvailable or off. The default is off. +- `pipeline` (String) A MongoDB pipeline to pass to the Change Stream API. This field allows you to control which types of change events are published, and which channel the change event should be published to. The pipeline must set the _ablyChannel field on the root of the change event. It must also be a valid JSON array of pipeline operations. +- `primary_site` (String) The primary site that the connector will run in. You should choose a site that is close to your database. +- `url` (String) The connection string of your MongoDB instance. (e.g. mongodb://user:pass@myhost.com) + + diff --git a/docs/resources/rule_azure_function.md b/docs/resources/rule_azure_function.md index 3a97553..afe138f 100644 --- a/docs/resources/rule_azure_function.md +++ b/docs/resources/rule_azure_function.md @@ -22,7 +22,7 @@ resource "ably_rule_azure_function" "rule0" { } request_mode = "batch" target = { - azure_app_id = "coms", + azure_app_id = "demo", function_name = "function0" headers = [ { diff --git a/examples/playground/ingress_rule_mongodb.tf b/examples/playground/ingress_rule_mongodb.tf new file mode 100644 index 0000000..2fd24a8 --- /dev/null +++ b/examples/playground/ingress_rule_mongodb.tf @@ -0,0 +1,19 @@ +resource "ably_ingress_rule_mongodb" "rule0" { + app_id = ably_app.app0.id + status = "enabled" + target = { + url = "mongodb://${var.mongodb_user}:${var.mongodb_password}@${var.mongodb_host}:27017" + database = "coconut" + collection = "coconut" + pipeline = jsonencode([ + { + "$set" = { + "_ablyChannel" = "myChannel" + } + } + ]) + full_document = "off" + full_document_before_change = "off" + primary_site = "us-east-1-A" + } +} diff --git a/examples/playground/rule_http_azure_function.tf b/examples/playground/rule_http_azure_function.tf index 557447c..549a3f3 100644 --- a/examples/playground/rule_http_azure_function.tf +++ b/examples/playground/rule_http_azure_function.tf @@ -7,7 +7,7 @@ resource "ably_rule_azure_function" "rule0" { } request_mode = "batch" target = { - azure_app_id = "coms", + azure_app_id = "demo", function_name = "function0" headers = [ { diff --git a/examples/resources/rule_http_azure_function.tf b/examples/resources/rule_http_azure_function.tf index 557447c..549a3f3 100644 --- a/examples/resources/rule_http_azure_function.tf +++ b/examples/resources/rule_http_azure_function.tf @@ -7,7 +7,7 @@ resource "ably_rule_azure_function" "rule0" { } request_mode = "batch" target = { - azure_app_id = "coms", + azure_app_id = "demo", function_name = "function0" headers = [ { diff --git a/go.mod b/go.mod index cb59b0f..18b3dd5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/ably/terraform-provider-ably go 1.19 require ( - github.com/ably/ably-control-go v0.4.0 + github.com/ably/ably-control-go v0.4.1-0.20241211161828-60786a2a414b github.com/hashicorp/terraform-plugin-docs v0.13.0 github.com/hashicorp/terraform-plugin-framework v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.21.0 diff --git a/go.sum b/go.sum index 3a068c6..7883f89 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/ably/ably-control-go v0.3.0 h1:m5Y2SHE69Mwg8iDASZxqmlHFSwpejN4s2TgZi3 github.com/ably/ably-control-go v0.3.0/go.mod h1:TP7gWAy+ga++gX6OZ0DtjwH8oVKKdiaIGQvZvxDKNdk= github.com/ably/ably-control-go v0.4.0 h1:JouYcHKT2TvvAGPpEPQJcFo5p9k1KfLUr/k9bfy+tYI= github.com/ably/ably-control-go v0.4.0/go.mod h1:TP7gWAy+ga++gX6OZ0DtjwH8oVKKdiaIGQvZvxDKNdk= +github.com/ably/ably-control-go v0.4.1-0.20241211161828-60786a2a414b h1:yHdqAi4zYDFIGmzv7WUPwA8JnLwaL5ExFys85Mf0Ih0= +github.com/ably/ably-control-go v0.4.1-0.20241211161828-60786a2a414b/go.mod h1:D5cHDJdYVHXdxfRwypsLaTySGE/bT3H0wlgxwBWCbxQ= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= diff --git a/internal/provider/ingress_rules.go b/internal/provider/ingress_rules.go new file mode 100644 index 0000000..e163c03 --- /dev/null +++ b/internal/provider/ingress_rules.go @@ -0,0 +1,297 @@ +package ably_control + +import ( + "context" + "fmt" + "strings" + + ably_control_go "github.com/ably/ably-control-go" + "github.com/hashicorp/terraform-plugin-framework/path" + tfsdk_resource "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// converts ingress rule from terraform format to control sdk format +func GetPlanIngressRule(plan AblyIngressRule) ably_control_go.NewIngressRule { + var target ably_control_go.IngressTarget + + switch t := plan.Target.(type) { + case *AblyIngressRuleTargetMongo: + target = &ably_control_go.IngressMongoTarget{ + Url: t.Url, + Database: t.Database, + Collection: t.Collection, + Pipeline: t.Pipeline, + FullDocument: t.FullDocument, + FullDocumentBeforeChange: t.FullDocumentBeforeChange, + PrimarySite: t.PrimarySite, + } + } + + rule_values := ably_control_go.NewIngressRule{ + Status: plan.Status.ValueString(), + Target: target, + } + + return rule_values +} + +// Maps response body to resource schema attributes. +// Using plan to fill in values that the api does not return. +func GetIngressRuleResponse(ably_ingress_rule *ably_control_go.IngressRule, plan *AblyIngressRule) AblyIngressRule { + var resp_target interface{} + + switch v := ably_ingress_rule.Target.(type) { + case *ably_control_go.IngressMongoTarget: + + resp_target = &AblyIngressRuleTargetMongo{ + Url: v.Url, + Database: v.Database, + Collection: v.Collection, + Pipeline: v.Pipeline, + FullDocument: v.FullDocument, + FullDocumentBeforeChange: v.FullDocumentBeforeChange, + PrimarySite: v.PrimarySite, + } + } + + resp_rule := AblyIngressRule{ + ID: types.StringValue(ably_ingress_rule.ID), + AppID: types.StringValue(ably_ingress_rule.AppID), + Status: types.StringValue(ably_ingress_rule.Status), + Target: resp_target, + } + + return resp_rule +} + +func GetIngressRuleSchema(target map[string]tfsdk.Attribute, markdown_description string) tfsdk.Schema { + return tfsdk.Schema{ + MarkdownDescription: markdown_description, + Attributes: map[string]tfsdk.Attribute{ + "id": { + Type: types.StringType, + Computed: true, + Description: "The rule ID.", + PlanModifiers: []tfsdk.AttributePlanModifier{ + tfsdk_resource.UseStateForUnknown(), + }, + }, + "app_id": { + Type: types.StringType, + Required: true, + Description: "The Ably application ID.", + PlanModifiers: []tfsdk.AttributePlanModifier{ + tfsdk_resource.RequiresReplace(), + }, + }, + "status": { + Type: types.StringType, + Optional: true, + Description: "The status of the rule. Rules can be enabled or disabled.", + }, + "target": { + Required: true, + Description: "object (rule_source)", + Attributes: tfsdk.SingleNestedAttributes(target), + }, + }, + } +} + +type IngressRule interface { + Provider() *provider + Name() string +} + +// Create a new resource +func CreateIngressRule[T any](r IngressRule, ctx context.Context, req tfsdk_resource.CreateRequest, resp *tfsdk_resource.CreateResponse) { + // Checks whether the provider and API Client are configured. If they are not, the provider responds with an error. + if !r.Provider().configured { + resp.Diagnostics.AddError( + "Provider not configured", + "The provider hasn't been configured before apply", + ) + return + } + + // Gets plan values + var p AblyIngressRuleDecoder[*T] + diags := req.Plan.Get(ctx, &p) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + plan := p.IngressRule() + plan_values := GetPlanIngressRule(plan) + + // Creates a new Ably Ingress Rule by invoking the CreateRule function from the Client Library + ingress_rule, err := r.Provider().client.CreateIngressRule(plan.AppID.ValueString(), &plan_values) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating Resource '%s'", r.Name()), + fmt.Sprintf("Could not create resource '%s', unexpected error: %s", r.Name(), err.Error()), + ) + + return + } + + response_values := GetIngressRuleResponse(&ingress_rule, &plan) + + // Sets state for the new Ably Ingress Rule. + diags = resp.State.Set(ctx, response_values) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read resource +func ReadIngressRule[T any](r IngressRule, ctx context.Context, req tfsdk_resource.ReadRequest, resp *tfsdk_resource.ReadResponse) { + // Gets the current state. If it is unable to, the provider responds with an error. + var s AblyIngressRuleDecoder[*T] + diags := req.State.Get(ctx, &s) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + state := s.IngressRule() + + // Gets the Ably App ID and Ably Ingress Rule ID value for the resource + app_id := s.AppID.ValueString() + ingress_rule_id := s.ID.ValueString() + + // Get Ingress Rule data + ingress_rule, err := r.Provider().client.IngressRule(app_id, ingress_rule_id) + + if err != nil { + if is_404(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting Resource %s", r.Name()), + fmt.Sprintf("Could not delete resource %s, unexpected error: %s", r.Name(), err.Error()), + ) + return + } + + response_values := GetIngressRuleResponse(&ingress_rule, &state) + + // Sets state to app values. + diags = resp.State.Set(ctx, &response_values) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } +} + +// // Update resource +func UpdateIngressRule[T any](r IngressRule, ctx context.Context, req tfsdk_resource.UpdateRequest, resp *tfsdk_resource.UpdateResponse) { + // Gets plan values + var p AblyIngressRuleDecoder[*T] + diags := req.Plan.Get(ctx, &p) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + plan := p.IngressRule() + + rule_values := GetPlanIngressRule(plan) + + // Gets the Ably App ID and Ably Ingress Rule ID value for the resource + app_id := plan.AppID.ValueString() + rule_id := plan.ID.ValueString() + + // Update Ably Ingress Rule + ingress_rule, err := r.Provider().client.UpdateIngressRule(app_id, rule_id, &rule_values) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error updading Resource %s", r.Name()), + fmt.Sprintf("Could not update resource %s, unexpected error: %s", r.Name(), err.Error()), + ) + return + } + + response_values := GetIngressRuleResponse(&ingress_rule, &plan) + + // Sets state to app values. + diags = resp.State.Set(ctx, &response_values) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } +} + +// Delete resource +func DeleteIngressRule[T any](r IngressRule, ctx context.Context, req tfsdk_resource.DeleteRequest, resp *tfsdk_resource.DeleteResponse) { + // Gets the current state. If it is unable to, the provider responds with an error. + var s AblyIngressRuleDecoder[*T] + diags := req.State.Get(ctx, &s) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + state := s.IngressRule() + + // Gets the Ably App ID and Ably Rule ID value for the resource + app_id := state.AppID.ValueString() + ingress_rule_id := state.ID.ValueString() + + err := r.Provider().client.DeleteIngressRule(app_id, ingress_rule_id) + if err != nil { + if is_404(err) { + resp.Diagnostics.AddWarning( + fmt.Sprintf("Resource does %s not exist", r.Name()), + fmt.Sprintf("Resource does %s not exist, it may have already been deleted: %s", r.Name(), err.Error()), + ) + } else { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting Resource %s'", r.Name()), + fmt.Sprintf("Could not delete resource '%s', unexpected error: %s", r.Name(), err.Error()), + ) + return + } + } + + // Remove resource from state + resp.State.RemoveResource(ctx) +} + +// // Import resource +func ImportIngressRuleResource(ctx context.Context, req tfsdk_resource.ImportStateRequest, resp *tfsdk_resource.ImportStateResponse, fields ...string) { + // Save the import identifier in the id attribute + // identifier should be in the format app_id,key_id + idParts := strings.Split(req.ID, ",") + anyEmpty := false + + for _, v := range idParts { + if v == "" { + anyEmpty = true + } + } + + if len(idParts) != len(fields) || anyEmpty { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: '%s'. Got: %q", strings.Join(fields, ","), req.ID), + ) + return + } + // Recent PR in TF Plugin Framework for paths but Hashicorp examples not updated - https://github.com/hashicorp/terraform-plugin-framework/pull/390 + for i, v := range fields { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root(v), idParts[i])...) + } +} diff --git a/internal/provider/models.go b/internal/provider/models.go index df19ed3..7c6c898 100644 --- a/internal/provider/models.go +++ b/internal/provider/models.go @@ -77,6 +77,35 @@ func emptyStringToNull(v *types.String) { } } +// Ably Ingress Rule +func (r *AblyIngressRuleDecoder[_]) IngressRule() AblyIngressRule { + return AblyIngressRule{ + ID: r.ID, + AppID: r.AppID, + Status: r.Status, + Target: r.Target, + } +} + +type AblyIngressRuleDecoder[T any] struct { + ID types.String `tfsdk:"id"` + AppID types.String `tfsdk:"app_id"` + Status types.String `tfsdk:"status"` + Target T `tfsdk:"target"` +} + +type AblyIngressRule AblyIngressRuleDecoder[any] + +type AblyIngressRuleTargetMongo struct { + Url string `tfsdk:"url"` + Database string `tfsdk:"database"` + Collection string `tfsdk:"collection"` + Pipeline string `tfsdk:"pipeline"` + FullDocument string `tfsdk:"full_document"` + FullDocumentBeforeChange string `tfsdk:"full_document_before_change"` + PrimarySite string `tfsdk:"primary_site"` +} + // Ably Rule type AblyRuleSource struct { ChannelFilter types.String `tfsdk:"channel_filter"` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index dd8fb5d..7e85e03 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -142,6 +142,7 @@ func (p *provider) Resources(context.Context) []func() tfsdk_resource.Resource { func() tfsdk_resource.Resource { return resourceRuleKafka{p} }, func() tfsdk_resource.Resource { return resourceRuleAmqp{p} }, func() tfsdk_resource.Resource { return resourceRuleAmqpExternal{p} }, + func() tfsdk_resource.Resource { return resourceIngressRuleMongo{p} }, } } diff --git a/internal/provider/resource_ably_ingress_rule_mongo.go b/internal/provider/resource_ably_ingress_rule_mongo.go new file mode 100644 index 0000000..b92ac1d --- /dev/null +++ b/internal/provider/resource_ably_ingress_rule_mongo.go @@ -0,0 +1,94 @@ +package ably_control + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + tfsdk_resource "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type resourceIngressRuleMongo struct { + p *provider +} + +// Get Rule Resource schema +func (r resourceIngressRuleMongo) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return GetIngressRuleSchema( + map[string]tfsdk.Attribute{ + "url": { + Type: types.StringType, + Required: true, + Description: "The connection string of your MongoDB instance. (e.g. mongodb://user:pass@myhost.com)", + }, + "database": { + Type: types.StringType, + Required: true, + Description: "The MongoDB Database Name", + }, + "collection": { + Type: types.StringType, + Required: true, + Description: "What the connector should watch within the database. The connector only supports watching collections.", + }, + "pipeline": { + Type: types.StringType, + Required: true, + Description: "A MongoDB pipeline to pass to the Change Stream API. This field allows you to control which types of change events are published, and which channel the change event should be published to. The pipeline must set the _ablyChannel field on the root of the change event. It must also be a valid JSON array of pipeline operations.", + }, + "full_document": { + Type: types.StringType, + Required: true, + Description: "Controls whether the full document should be included in the published change events. Full Document is not available by default in all types of change event. Possible values are updateLookup, whenAvailable, off. The default is off.", + }, + "full_document_before_change": { + Type: types.StringType, + Required: true, + Description: "Controls wheter the full document before the change should be included in the change event. Full Document before change is not available on all types of change event. Possible values are whenAvailable or off. The default is off.", + }, + "primary_site": { + Type: types.StringType, + Required: true, + Description: "The primary site that the connector will run in. You should choose a site that is close to your database.", + }, + }, + "The `ably_ingress_rule_mongodb` resource sets up a MongoDB Integration Rule to stream document changes from a database collection over Ably."), nil +} + +func (r resourceIngressRuleMongo) Metadata(ctx context.Context, req tfsdk_resource.MetadataRequest, resp *tfsdk_resource.MetadataResponse) { + resp.TypeName = "ably_ingress_rule_mongodb" +} + +func (r *resourceIngressRuleMongo) Provider() *provider { + return r.p +} + +func (r *resourceIngressRuleMongo) Name() string { + return "MongoDB" +} + +// Create a new resource +func (r resourceIngressRuleMongo) Create(ctx context.Context, req tfsdk_resource.CreateRequest, resp *tfsdk_resource.CreateResponse) { + CreateIngressRule[AblyIngressRuleTargetMongo](&r, ctx, req, resp) +} + +// Read resource +func (r resourceIngressRuleMongo) Read(ctx context.Context, req tfsdk_resource.ReadRequest, resp *tfsdk_resource.ReadResponse) { + ReadIngressRule[AblyIngressRuleTargetMongo](&r, ctx, req, resp) +} + +// Update resource +func (r resourceIngressRuleMongo) Update(ctx context.Context, req tfsdk_resource.UpdateRequest, resp *tfsdk_resource.UpdateResponse) { + UpdateIngressRule[AblyIngressRuleTargetMongo](&r, ctx, req, resp) +} + +// Delete resource +func (r resourceIngressRuleMongo) Delete(ctx context.Context, req tfsdk_resource.DeleteRequest, resp *tfsdk_resource.DeleteResponse) { + DeleteIngressRule[AblyIngressRuleTargetMongo](&r, ctx, req, resp) +} + +// Import resource +func (r resourceIngressRuleMongo) ImportState(ctx context.Context, req tfsdk_resource.ImportStateRequest, resp *tfsdk_resource.ImportStateResponse) { + ImportResource(ctx, req, resp, "app_id", "id") +} diff --git a/internal/provider/resource_ably_ingress_rule_mongo_test.go b/internal/provider/resource_ably_ingress_rule_mongo_test.go new file mode 100644 index 0000000..0812ac4 --- /dev/null +++ b/internal/provider/resource_ably_ingress_rule_mongo_test.go @@ -0,0 +1,101 @@ +package ably_control + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "testing" +) + +func TestAccAblyIngressRuleMongo(t *testing.T) { + app_name := acctest.RandStringFromCharSet(15, acctest.CharSetAlphaNum) + update_app_name := "acc-test-" + app_name + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create and Read testing of ably_app.app0 + { + Config: testAccAblyIngressRuleMongoConfig( + app_name, + "enabled", + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ably_app.app0", "name", app_name), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "status", "enabled"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule", "target.url", "mongodb://coco:nut@coco.io:27017"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule", "target.collection", "coconut"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.database", "coconut"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.full_document", "off"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.full_document_before_change", "off"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.primary_site", "us-east-1-A"), + ), + }, + // Update and Read testing of ably_app.app0 + { + Config: testAccAblyIngressRuleMongoConfig( + update_app_name, + "enabled", + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ably_app.app0", "name", update_app_name), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "status", "enabled"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.url", "mongodb://coco:nut@coco.io:27017"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.collection", "coconut"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.database", "coconut"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.full_document", "off"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.full_document_before_change", "off"), + resource.TestCheckResourceAttr("ably_ingress_rule_mongodb.rule0", "target.primary_site", "us-east-1-A"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +// Function with inline HCL to provision an ably_app resource +func testAccAblyIngressRuleMongoConfig( + appName string, + ruleStatus string, +) string { + return fmt.Sprintf(` +terraform { + required_providers { + ably = { + source = "github.com/ably/ably" + } + } +} + +# You can provide your Ably Token & URL inline or use environment variables ABLY_ACCOUNT_TOKEN & ABLY_URL +provider "ably" {} + +resource "ably_app" "app0" { + name = %[1]q + status = "enabled" + tls_only = true +} + +resource "ably_ingress_rule_mongodb" "rule0" { + app_id = ably_app.app0.id + status = %[2]q + + target = { + url = "mongodb://coco:nut@coco.io:27017" + database = "coconut" + collection = "coconut" + pipeline = jsonencode([ + { + "$set" = { + "_ablyChannel" = "myChannel" + } + } + ]) + full_document = "off" + full_document_before_change = "off" + primary_site = "us-east-1-A" + + } + } +`, appName, ruleStatus) +} diff --git a/internal/provider/resource_ably_namespace_test.go b/internal/provider/resource_ably_namespace_test.go index 362585f..f2fbcff 100644 --- a/internal/provider/resource_ably_namespace_test.go +++ b/internal/provider/resource_ably_namespace_test.go @@ -91,7 +91,7 @@ func TestAccAblyNamespace(t *testing.T) { TlsOnly: false, ExposeTimeserial: false, BatchingEnabled: true, - BatchingPolicy: "some-policy", + BatchingPolicy: "simple", BatchingInterval: ably_control_go.BatchingInterval(100), }), Check: resource.ComposeAggregateTestCheckFunc( @@ -104,7 +104,7 @@ func TestAccAblyNamespace(t *testing.T) { resource.TestCheckResourceAttr("ably_namespace.namespace0", "tls_only", "false"), resource.TestCheckResourceAttr("ably_namespace.namespace0", "expose_timeserial", "false"), resource.TestCheckResourceAttr("ably_namespace.namespace0", "batching_enabled", "true"), - resource.TestCheckResourceAttr("ably_namespace.namespace0", "batching_policy", "some-policy"), + resource.TestCheckResourceAttr("ably_namespace.namespace0", "batching_policy", "simple"), resource.TestCheckResourceAttr("ably_namespace.namespace0", "batching_interval", "100"), ), }, diff --git a/internal/provider/resource_ably_rule_azure_function_test.go b/internal/provider/resource_ably_rule_azure_function_test.go index c15c668..7bc0482 100644 --- a/internal/provider/resource_ably_rule_azure_function_test.go +++ b/internal/provider/resource_ably_rule_azure_function_test.go @@ -38,7 +38,7 @@ func TestAccAblyRuleAzureFunction(t *testing.T) { "enabled", "channel.message", "batch", - "coms", + "demo", "function0", original_headers_block, "ably_api_key.api_key_0.id", @@ -61,7 +61,7 @@ func TestAccAblyRuleAzureFunction(t *testing.T) { "disabled", "channel.presence", "batch", - "coms", + "demo", "function1", update_headers_block, "ably_api_key.api_key_1.id", diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index 4ee89ba..52b8157 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -42,17 +42,17 @@ To start using the Ably Terraform Provider you need to authenticate with Ably. Y In order to import a resource, you need to add the resource to your Terraform configuration file, and then follow https://www.terraform.io/cli/import. -For example, in order to import an app named `ably-tf-provider-app-0000`, add something similar to the following: +For example, in order to import an app with id `{APP_ID}` (you can retrieve app id from Ably dashboard or via [Control API](https://ably.com/docs/api/control-api#tag/apps/paths/~1accounts~1%7Baccount_id%7D~1apps/get)), add something similar to the following: {{ tffile "examples/resources/app.tf" }} Then run the following command: ```bash -terraform import ably_app.app0 ably-tf-provider-app-0000 +terraform import ably_app.app0 {APP_ID} ``` This will add the app to your Terraform state file. You can then run `terraform plan` to see what changes will be made to the app. -{{ .SchemaMarkdown | trimspace }} \ No newline at end of file +{{ .SchemaMarkdown | trimspace }}