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

[Feature] Add data source databricks_notification_destinations #4087

Merged
Show file tree
Hide file tree
Changes from 9 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
60 changes: 60 additions & 0 deletions docs/data-sources/notification_destinations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
subcategory: "Workspace"
---
# databricks_notification_destination Data Source
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved

-> **NOTE** Ensure that the notification destinations you are attempting to retrieve have been created and are accessible within your workspace.
alexott marked this conversation as resolved.
Show resolved Hide resolved

This data source allows you to retrieve information about [Notification Destinations](https://docs.databricks.com/api/workspace/notificationdestinations). Notification Destinations are used to send notifications for query alerts and jobs to external systems such as email, Slack, Microsoft Teams, PagerDuty, or generic webhooks.

## Example Usage


```hcl
resource "databricks_notification_destination" "email" {
display_name = "Email Destination"
config {
email {
addresses = ["[email protected]"]
}
}
}

resource "databricks_notification_destination" "slack" {
display_name = "Slack Destination"
config {
slack {
url = "https://hooks.slack.com/services/..."
}
}
}

data "databricks_notification_destinations" "this" {} # Lists all notification desitnations

data "databricks_notification_destinations" "filtered_notification" {
display_name_contains = "Destination"
type = "EMAIL"
}

```


## Argument Reference

The following arguments are supported:

* `display_name_contains` - (Optional) A substring to filter Notification Destinations by their display name.
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
* `type` - (Optional) The type of the Notification Destination to filter by. Valid values are:
* `EMAIL` - Filters Notification Destinations of type Email.
* `MICROSOFT_TEAMS` - Filters Notification Destinations of type Microsoft Teams.
* `PAGERDUTY` - Filters Notification Destinations of type PagerDuty.
* `SLACK` - Filters Notification Destinations of type Slack.
* `WEBHOOK` - Filters Notification Destinations of type Webhook.

## Attribute Reference

In addition to all arguments above, the following attributes are exported:
* `notification_destinations` - A list of Notification Destinations matching the specified criteria. Each element contains the following attributes:
* `id` - The unique ID of the Notification Destination.
* `display_name` - The display name of the Notification Destination.
* `destination_type` - The type of the notification destination. Possible values are `EMAIL`, `MICROSOFT_TEAMS`, `PAGERDUTY`, `SLACK`, or `WEBHOOK`.
4 changes: 4 additions & 0 deletions internal/providers/pluginfw/pluginfw.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import (
providercommon "github.com/databricks/terraform-provider-databricks/internal/providers/common"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/cluster"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/library"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/notificationdestinations"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/qualitymonitor"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/registered_model"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/volume"

"github.com/hashicorp/terraform-plugin-framework/datasource"
Expand Down Expand Up @@ -52,6 +54,8 @@ func (p *DatabricksProviderPluginFramework) DataSources(ctx context.Context) []f
return []func() datasource.DataSource{
cluster.DataSourceCluster,
volume.DataSourceVolumes,
registered_model.DataSourceRegisteredModel,
notificationdestinations.DataSourceNotificationDestinations,
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package notificationdestinations

import (
"context"
"fmt"
"strings"

"github.com/databricks/databricks-sdk-go/service/settings"
"github.com/databricks/terraform-provider-databricks/common"
pluginfwcommon "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/common"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/converters"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/tfschema"
"github.com/databricks/terraform-provider-databricks/internal/service/settings_tf"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func DataSourceNotificationDestinations() datasource.DataSource {
return &NotificationDestinationsDataSource{}
}

var _ datasource.DataSourceWithConfigure = &NotificationDestinationsDataSource{}

type NotificationDestinationsDataSource struct {
Client *common.DatabricksClient
}

type NotificationDestinationsInfo struct {
Id types.String `tfsdk:"id" tf:"computed"`
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
DisplayNameContains types.String `tfsdk:"display_name_contains" tf:"optional"`
Type types.String `tfsdk:"type" tf:"optional"`
NotificationDestinations []settings_tf.ListNotificationDestinationsResult `tfsdk:"notification_destinations" tf:"computed"`
}

func (d *NotificationDestinationsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = "databricks_notification_destinations_pluginframework"
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
}

func (d *NotificationDestinationsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: tfschema.DataSourceStructToSchemaMap(NotificationDestinationsInfo{}, nil),
}
}

func (d *NotificationDestinationsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if d.Client == nil {
d.Client = pluginfwcommon.ConfigureDataSource(req, resp)
}
}

func validateType(notificationType string) diag.Diagnostics {
validTypes := map[string]struct{}{
alexott marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can just use a list together with slices.Contains function - we don't have huge performance impact to use a map for lookups

string(settings.DestinationTypeEmail): {},
string(settings.DestinationTypeMicrosoftTeams): {},
string(settings.DestinationTypePagerduty): {},
string(settings.DestinationTypeSlack): {},
string(settings.DestinationTypeWebhook): {},
}

if _, valid := validTypes[notificationType]; !valid {
return diag.Diagnostics{diag.NewErrorDiagnostic(fmt.Sprintf("Invalid type '%s'; valid types are EMAIL, MICROSOFT_TEAMS, PAGERDUTY, SLACK, WEBHOOK.", notificationType), "")}
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
}
return nil
}

func validateLength(destinations []settings_tf.ListNotificationDestinationsResult) diag.Diagnostics {
if len(destinations) == 0 {
return diag.Diagnostics{diag.NewErrorDiagnostic("Could not find any notification destinations with the specified criteria.", "")}
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
}
return nil
}

func appendToResponse(resp *datasource.ReadResponse, diags diag.Diagnostics) bool {
alexott marked this conversation as resolved.
Show resolved Hide resolved
resp.Diagnostics.Append(diags...)
return resp.Diagnostics.HasError()
}

func (d *NotificationDestinationsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
w, diags := d.Client.GetWorkspaceClient()
if appendToResponse(resp, diags) {
return
}

var notificationInfo NotificationDestinationsInfo
if appendToResponse(resp, req.Config.Get(ctx, &notificationInfo)) {
return
}

notificationType := notificationInfo.Type.ValueString()
notificationDisplayName := notificationInfo.DisplayNameContains.ValueString()

if notificationType != "" {
if appendToResponse(resp, validateType(notificationType)) {
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
return
}
}

notificationsGoSdk, err := w.NotificationDestinations.ListAll(ctx, settings.ListNotificationDestinationsRequest{})
if err != nil {
resp.Diagnostics.AddError("Failed to fetch Notification Destinations", err.Error())
return
}

var notificationsTfSdk []settings_tf.ListNotificationDestinationsResult
for _, notification := range notificationsGoSdk {
if (notification.DestinationType.String() != notificationType) || (notificationDisplayName != "" && !strings.Contains(notification.DisplayName, notificationDisplayName)) {
alexott marked this conversation as resolved.
Show resolved Hide resolved
continue
}

var notificationDestination settings_tf.ListNotificationDestinationsResult
if appendToResponse(resp, converters.GoSdkToTfSdkStruct(ctx, notification, &notificationDestination)) {
return
}
notificationsTfSdk = append(notificationsTfSdk, notificationDestination)
}

if appendToResponse(resp, validateLength(notificationsTfSdk)) {
return
}

notificationInfo.NotificationDestinations = notificationsTfSdk
resp.Diagnostics.Append(resp.State.Set(ctx, notificationInfo)...)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package notificationdestinations_test

import (
"testing"

"github.com/databricks/terraform-provider-databricks/internal/acceptance"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func CheckDataSourceNotificationsPopulated(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
ds, ok := s.Modules[0].Resources["data.databricks_notification_destinations.this"]
require.True(t, ok, "data.databricks_notification_destinations.this has to be there")

notificationCount := ds.Primary.Attributes["notification_destinations.#"]
require.Equal(t, "2", notificationCount, "expected two notifications")

notificationIds := []string{
ds.Primary.Attributes["notification_destinations.0.id"],
ds.Primary.Attributes["notification_destinations.1.id"],
}

expectedNotificationIds := []string{
s.Modules[0].Resources["databricks_notification_destination.email_notification"].Primary.ID,
s.Modules[0].Resources["databricks_notification_destination.slack_notification"].Primary.ID,
}

assert.ElementsMatch(t, expectedNotificationIds, notificationIds, "expected notification_destination ids to match")

return nil
}
}

func TestAccNotificationsCreation(t *testing.T) {
acceptance.WorkspaceLevel(t, acceptance.Step{
Template: `
resource "databricks_notification_destination" "email_notification" {
display_name = "email notification destination"
config {
email {
addresses = ["[email protected]"]
}
}
}

resource "databricks_notification_destination" "notification" {
display_name = "slack notification destination"
config {
slack {
url = "https://hooks.slack.com/services/..."
}
}
}

data "databricks_notification_destinations" "this" {}
`,
Check: CheckDataSourceNotificationsPopulated(t),
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package notificationdestinations

import (
"testing"

"github.com/databricks/terraform-provider-databricks/internal/service/settings_tf"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/stretchr/testify/assert"
)

func TestValidateType_InvalidType(t *testing.T) {
actualDiagnostics := validateType("INVALID")
expectedDiagnostics := diag.Diagnostics{diag.NewErrorDiagnostic("Invalid type 'INVALID'; valid types are EMAIL, MICROSOFT_TEAMS, PAGERDUTY, SLACK, WEBHOOK.", "")}
assert.True(t, actualDiagnostics.HasError())
assert.Equal(t, expectedDiagnostics, actualDiagnostics)
}

func TestValidateLength(t *testing.T) {
actualDiagnostics := validateLength([]settings_tf.ListNotificationDestinationsResult{})
expectedDiagnostics := diag.Diagnostics{diag.NewErrorDiagnostic("Could not find any notification destinations with the specified criteria.", "")}
assert.True(t, actualDiagnostics.HasError())
assert.Equal(t, expectedDiagnostics, actualDiagnostics)
}