Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_web_pubsub_hub - add event_listener property #21145

Merged
merged 9 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions internal/services/signalr/web_pubsub_hub_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/webpubsub/2023-02-01/webpubsub"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
eventhubValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/signalr/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/signalr/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
Expand Down Expand Up @@ -105,6 +106,46 @@ func resourceWebPubSubHub() *pluginsdk.Resource {
},
},

"event_listener": {
Type: pluginsdk.TypeList,
Optional: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"user_event_name_filter": {
Type: pluginsdk.TypeList,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"system_event_name_filter": {
Type: pluginsdk.TypeList,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateFunc: validation.StringInSlice([]string{
"connected",
"disconnected",
}, false),
},
},

"eventhub_namespace_name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: eventhubValidate.ValidateEventHubNamespaceName(),
},

"eventhub_name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: eventhubValidate.ValidateEventHubName(),
},
xiaxyi marked this conversation as resolved.
Show resolved Hide resolved
},
},
},

"anonymous_connections_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Expand Down Expand Up @@ -151,6 +192,13 @@ func resourceWebPubSubHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{
},
}

eventListener, err := expandEventListener(d.Get("event_listener").([]interface{}))
if err != nil {
return fmt.Errorf("expanding event listener for web pubsub %s: %+v", id, err)
}

parameters.Properties.EventListeners = eventListener

if err := client.HubsCreateOrUpdateThenPoll(ctx, id, parameters); err != nil {
return fmt.Errorf("creating/updating %s: %+v", id, err)
}
Expand Down Expand Up @@ -188,6 +236,9 @@ func resourceWebPubSubHubRead(d *pluginsdk.ResourceData, meta interface{}) error
return fmt.Errorf("setting `event_handler`: %+v", err)
}
d.Set("anonymous_connections_enabled", strings.EqualFold(*model.Properties.AnonymousConnectPolicy, "Allow"))
if err := d.Set("event_listener", flattenEventListener(model.Properties.EventListeners)); err != nil {
return fmt.Errorf("setting `event_listener`: %+v", err)
}
}

return nil
Expand Down Expand Up @@ -278,6 +329,85 @@ func flattenEventHandler(input *[]webpubsub.EventHandler) []interface{} {
return eventHandlerBlock
}

func expandEventListener(input []interface{}) (*[]webpubsub.EventListener, error) {
result := make([]webpubsub.EventListener, 0)
if len(input) == 0 {
return &result, nil
}

for _, eventListenerItem := range input {
block := eventListenerItem.(map[string]interface{})
systemEvents := make([]string, 0)
userEventPattern := ""
if v, ok := block["user_event_name_filter"]; ok && len(v.([]interface{})) > 0 {
userEventPatternList := utils.ExpandStringSlice(v.([]interface{}))
userEventPattern = strings.Join(*userEventPatternList, ",")
}

if v, ok := block["system_event_name_filter"]; ok {
for _, item := range v.([]interface{}) {
systemEvents = append(systemEvents, item.(string))
}
}
filter := webpubsub.EventNameFilter{
SystemEvents: &systemEvents,
UserEventPattern: utils.String(userEventPattern),
}

endpointName := block["eventhub_namespace_name"].(string)
fullQualifiedName := endpointName + ".servicebus.windows.net"
if _, ok := block["eventhub_name"]; !ok {
return nil, fmt.Errorf("no event hub is specified")
}
ehName := block["eventhub_name"].(string)
endpoint := webpubsub.EventHubEndpoint{
FullyQualifiedNamespace: fullQualifiedName,
EventHubName: ehName,
}

result = append(result, webpubsub.EventListener{
Filter: filter,
Endpoint: endpoint,
})
}
return &result, nil
}

func flattenEventListener(listener *[]webpubsub.EventListener) []interface{} {
eventListenerBlocks := make([]interface{}, 0)
if listener == nil {
return eventListenerBlocks
}

for _, item := range *listener {
listenerBlock := make(map[string]interface{}, 0)
// todo use the type Assertion or Type field in sdk to get the different sub-class
if eventFilter := item.Filter; eventFilter != nil {
eventNameFilter := item.Filter.(webpubsub.EventNameFilter)
userNameFilterList := make([]interface{}, 0)
if eventNameFilter.SystemEvents != nil {
listenerBlock["system_event_name_filter"] = utils.FlattenStringSlice(eventNameFilter.SystemEvents)
}
if eventNameFilter.UserEventPattern != nil && *eventNameFilter.UserEventPattern != "" {
v := strings.Split(*eventNameFilter.UserEventPattern, ",")
for _, s := range v {
userNameFilterList = append(userNameFilterList, s)
}
listenerBlock["user_event_name_filter"] = userNameFilterList
}
}

if eventEndpoint := item.Endpoint; eventEndpoint != nil {
eventhubEndpoint := item.Endpoint.(webpubsub.EventHubEndpoint)
listenerBlock["eventhub_namespace_name"] = strings.TrimSuffix(eventhubEndpoint.FullyQualifiedNamespace, ".servicebus.windows.net")
listenerBlock["eventhub_name"] = eventhubEndpoint.EventHubName
}
eventListenerBlocks = append(eventListenerBlocks, listenerBlock)
}

return eventListenerBlocks
}

func expandAuth(input []interface{}) *webpubsub.UpstreamAuthSettings {
if len(input) == 0 || input[0] == nil {
authType := webpubsub.UpstreamAuthTypeNone
Expand Down
84 changes: 81 additions & 3 deletions internal/services/signalr/web_pubsub_hub_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func TestAccWebPubsubHub_withPropertyUpdate(t *testing.T) {
})
}

func TestAccWebPubsubHub_withMultipleEventhandlerSettingsUpdate(t *testing.T) {
func TestAccWebPubsubHub_withMultipleEventHandlerSettingsUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_web_pubsub_hub", "test")
r := WebPubsubHubResource{}

Expand All @@ -129,7 +129,33 @@ func TestAccWebPubsubHub_withMultipleEventhandlerSettingsUpdate(t *testing.T) {
},
data.ImportStep(),
{
Config: r.withMultipleEventhandlerSettings(data),
Config: r.withMultipleEventHandlerSettings(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r)),
},
data.ImportStep(),
})
}

func TestAccWebPubsubHub_withMultipleEventListenerSettingsUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_web_pubsub_hub", "test")
r := WebPubsubHubResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.withMultipleEventListenerSettings(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r)),
},
data.ImportStep(),
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r)),
},
data.ImportStep(),
{
Config: r.withMultipleEventListenerSettings(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r)),
},
Expand Down Expand Up @@ -234,7 +260,7 @@ resource "azurerm_web_pubsub_hub" "import" {
`, r.basic(data))
}

func (r WebPubsubHubResource) withMultipleEventhandlerSettings(data acceptance.TestData) string {
func (r WebPubsubHubResource) withMultipleEventHandlerSettings(data acceptance.TestData) string {
return fmt.Sprintf(`
%s

Expand Down Expand Up @@ -271,6 +297,54 @@ resource "azurerm_web_pubsub_hub" "test" {
`, r.template(data), data.RandomInteger, data.RandomInteger, data.RandomInteger)
}

func (r WebPubsubHubResource) withMultipleEventListenerSettings(data acceptance.TestData) string {
return fmt.Sprintf(`
%s

resource "azurerm_eventhub_namespace" "test" {
name = "acctesteventhubnamespace-%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
sku = "Standard"
}

resource "azurerm_eventhub" "test" {
name = "acctesteventhub-%d"
namespace_name = azurerm_eventhub_namespace.test.name
resource_group_name = azurerm_resource_group.test.name
partition_count = 1
message_retention = 1
}

resource "azurerm_eventhub" "test1" {
name = "acctesteventhub-%d"
namespace_name = azurerm_eventhub_namespace.test.name
resource_group_name = azurerm_resource_group.test.name
partition_count = 1
message_retention = 1
}

resource "azurerm_web_pubsub_hub" "test" {
name = "acctestwpsh%d"
web_pubsub_id = azurerm_web_pubsub.test.id

event_listener {
system_event_name_filter = ["disconnected", "connected"]
user_event_name_filter = ["event1"]
eventhub_namespace_name = azurerm_eventhub_namespace.test.name
eventhub_name = azurerm_eventhub.test.name
}

event_listener {
system_event_name_filter = ["connected"]
user_event_name_filter = ["event1", "event2"]
eventhub_namespace_name = azurerm_eventhub_namespace.test.name
eventhub_name = azurerm_eventhub.test1.name
}
}
`, r.template(data), data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger)
}

func (r WebPubsubHubResource) withMultipleEventhandlerSettingsAndNoAuth(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down Expand Up @@ -342,6 +416,10 @@ resource "azurerm_web_pubsub" "test" {
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Standard_S1"

identity {
type = "SystemAssigned"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}
44 changes: 41 additions & 3 deletions website/docs/r/web_pubsub_hub.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@ resource "azurerm_web_pubsub_hub" "example" {
managed_identity_id = azurerm_user_assigned_identity.example.id
}
}

event_listener {
system_event_name_filter = ["connected"]
user_event_name_filter = ["event1", "event2"]
eventhub_namespace_name = azurerm_eventhub_namespace.test.name
eventhub_name = azurerm_eventhub.test1.name
}

event_listener {
system_event_name_filter = ["connected"]
user_event_name_filter = ["*"]
eventhub_namespace_name = azurerm_eventhub_namespace.test.name
eventhub_name = azurerm_eventhub.test1.name
}

event_listener {
system_event_name_filter = ["connected"]
user_event_name_filter = ["event1"]
eventhub_namespace_name = azurerm_eventhub_namespace.test.name
eventhub_name = azurerm_eventhub.test1.name
}

anonymous_connections_enabled = true

depends_on = [
Expand All @@ -64,7 +86,7 @@ The following arguments are supported:

* `name` - (Required) The name of the Web Pubsub hub service. Changing this forces a new resource to be created.

* `web_pubsub_id` - (Required) Specify the id of the Web Pubsub. Changing this forces a new resource to be created.
* `web_pubsub_id` - (Required) Specifies the id of the Web Pubsub. Changing this forces a new resource to be created.

* `anonymous_connections_enabled` - (Optional) Is anonymous connections are allowed for this hub? Defaults to `false`.
Possible values are `true`, `false`.
Expand All @@ -73,20 +95,36 @@ The following arguments are supported:

-> **NOTE:** User can change the order of `event_handler` to change the priority accordingly.

* `event_listener` - (Optional) An `event_listener` block as defined below.

-> **NOTE:** The managed identity of Web PubSub service must be enabled and the identity must have the "Azure Event Hubs Data sender" role to access the Event Hub.

---

An `event_handler` block supports the following:

* `url_template` - (Required) The Event Handler URL Template. Two predefined parameters `{hub}` and `{event}` are available to use in the template. The value of the EventHandler URL is dynamically calculated when the client request comes in. Example: `http://example.com/api/{hub}/{event}`.

* `user_event_pattern` - (Optional) Specify the matching event names. There are 3 kind of patterns supported: * `*` matches any event name * `,` Combine multiple events with `,` for example `event1,event2`, it matches event `event1` and `event2` * The single event name, for example `event1`, it matches `event1`.
* `user_event_pattern` - (Optional) Specifies the matching event names. There are 3 kind of patterns supported: * `*` matches any event name * `,` Combine multiple events with `,` for example `event1,event2`, it matches event `event1` and `event2` * The single event name, for example `event1`, it matches `event1`.

* `system_events` - (Optional) Specify the list of system events. Supported values are `connect`, `connected` and `disconnected`.
* `system_events` - (Optional) Specifies the list of system events. Supported values are `connect`, `connected` and `disconnected`.

* `auth` - (Optional) An `auth` block as defined below.

---

An `event_listener` block supports the following:

* `system_event_name_filter` - (Optional) Specifies the list of system events. Supported values are `connected` and `disconnected`.

* `user_event_name_filter` - (Optional) Specifies the list of matching user event names. `["*"]` can be used to match all events.

* `eventhub_namespace_name` - (Required) Specifies the event hub namespace name to receive the events.

* `eventhub_name` - (Required) Specifies the event hub name to receive the events.

---

An `auth` block supports the following:

* `managed_identity_id` - (Required) Specify the identity ID of the target resource.
Expand Down