Skip to content

Commit

Permalink
Merge pull request #389 from Juniper/feat/388
Browse files Browse the repository at this point in the history
Datasource and Resource for Dashboards
  • Loading branch information
rajagopalans authored Oct 9, 2023
2 parents 326c67c + 13858ef commit a995094
Show file tree
Hide file tree
Showing 16 changed files with 1,097 additions and 10 deletions.
96 changes: 96 additions & 0 deletions apstra/data_source_iba_dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package tfapstra

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/iba"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
)

var _ datasource.DataSourceWithConfigure = &dataSourceIbaDashboard{}

type dataSourceIbaDashboard struct {
client *apstra.Client
}

func (o *dataSourceIbaDashboard) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_iba_dashboard"
}

func (o *dataSourceIbaDashboard) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
o.client = DataSourceGetClient(ctx, req, resp)
}

func (o *dataSourceIbaDashboard) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This data source provides details of a specific IBA Dashboard in a Blueprint." +
"\n\n" +
"At least one optional attribute is required.",
Attributes: iba.IbaDashboard{}.DataSourceAttributes(),
}
}

func (o *dataSourceIbaDashboard) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config iba.IbaDashboard
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

bpClient, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(config.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found",
config.BlueprintId), err.Error())
return
}
resp.Diagnostics.AddError("failed to create blueprint client", err.Error())
return
}

var api *apstra.IbaDashboard
switch {
case !config.Name.IsNull():
api, err = bpClient.GetIbaDashboardByLabel(ctx, config.Name.ValueString())
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("name"),
"IBA dashboard not found",
fmt.Sprintf("IBA Dashboard with name %s not found: Error : %q", config.Name, err))
return
}
resp.Diagnostics.AddAttributeError(
path.Root("name"), "Failed reading IBA Dashboard", err.Error(),
)
return
}
case !config.Id.IsNull():
api, err = bpClient.GetIbaDashboard(ctx, apstra.ObjectId(config.Id.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddAttributeError(
path.Root("id"),
"IbaDashboard not found",
fmt.Sprintf("IbaDashboard with ID %s not found", config.Id))
return
}
resp.Diagnostics.AddAttributeError(
path.Root("name"), "Failed reading IBA Dashboard", err.Error(),
)
return
}
}

config.LoadApiData(ctx, api, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

// Set state
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...)
}
96 changes: 96 additions & 0 deletions apstra/data_source_iba_dashboard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package tfapstra

import (
"context"
"errors"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
testutils "github.com/Juniper/terraform-provider-apstra/apstra/test_utils"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"testing"
)

const (
dataSourceIbaDashboardTemplateByNameHCL = `
data "apstra_iba_dashboard" "test" {
blueprint_id = "%s"
name = "%s"
}
`

dataSourceIbaDashboardTemplateByIdHCL = `
data "apstra_iba_dashboard" "test" {
blueprint_id = "%s"
id = "%s"
}
`
)

func TestAccDataSourceIbaDashboard(t *testing.T) {
ctx := context.Background()

bpClient, bpDelete, err := testutils.MakeOrFindBlueprint(ctx, "BPA", testutils.BlueprintA)

if err != nil {
t.Fatal(errors.Join(err, bpDelete(ctx)))
}
defer func() {
err = bpDelete(ctx)
if err != nil {
t.Error(err)
}
}()

// Set up Widgets
widgetIdA, _, widgetIdB, _, cleanup := testutils.TestWidgetsAB(ctx, t, bpClient)
if err != nil {
t.Fatal(err)
}
defer func() {
err = cleanup()
if err != nil {
t.Error(err)
}
}()

data := apstra.IbaDashboardData{
Description: "Test Dashboard",
Default: false,
Label: "Test Dash",
IbaWidgetGrid: [][]apstra.ObjectId{{widgetIdA, widgetIdB}},
}
dId, err := bpClient.CreateIbaDashboard(ctx, &data)
if err != nil {
t.Fatal(err)
}
defer bpClient.DeleteIbaDashboard(ctx, dId)

resource.Test(t, resource.TestCase{
// PreCheck: setup,
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Read by ID
{
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceIbaDashboardTemplateByIdHCL,
bpClient.Id().String(), dId.String()),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "id", dId.String()),
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "name", data.Label),
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "widget_grid.0.0", widgetIdA.String()),
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "widget_grid.0.1", widgetIdB.String()),
),
},
// Read by Name
{
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceIbaDashboardTemplateByNameHCL,
bpClient.Id().String(), data.Label),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "id", dId.String()),
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "name", data.Label),
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "widget_grid.0.0", widgetIdA.String()),
resource.TestCheckResourceAttr("data.apstra_iba_dashboard.test", "widget_grid.0.1", widgetIdB.String()),
),
},
},
})
}
95 changes: 95 additions & 0 deletions apstra/data_source_iba_dashboards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package tfapstra

import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var _ datasource.DataSourceWithConfigure = &dataSourceIbaDashboards{}

type dataSourceIbaDashboards struct {
client *apstra.Client
}

func (o *dataSourceIbaDashboards) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_iba_dashboards"
}

func (o *dataSourceIbaDashboards) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
o.client = DataSourceGetClient(ctx, req, resp)
}

func (o *dataSourceIbaDashboards) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "This data source returns the ID numbers of all IBA Dashboards in a Blueprint.",
Attributes: map[string]schema.Attribute{
"blueprint_id": schema.StringAttribute{
MarkdownDescription: "Apstra Blueprint ID. " +
"Used to identify the Blueprint that the IBA Dashboards belongs to.",
Required: true,
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"ids": schema.SetAttribute{
MarkdownDescription: "A set of Apstra object ID numbers of the IBA Dashboards in the Blueprint.",
Computed: true,
ElementType: types.StringType,
},
},
}
}

func (o *dataSourceIbaDashboards) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
Ids types.Set `tfsdk:"ids"`
}

// get the configuration
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}

bpClient, err := o.client.NewTwoStageL3ClosClient(ctx, apstra.ObjectId(config.BlueprintId.ValueString()))
if err != nil {
if utils.IsApstra404(err) {
resp.Diagnostics.AddError(fmt.Sprintf("blueprint %s not found",
config.BlueprintId), err.Error())
return
}
resp.Diagnostics.AddError("failed to create blueprint client", err.Error())
return
}

ds, err := bpClient.GetAllIbaDashboards(ctx)
if err != nil {
resp.Diagnostics.AddError("error retrieving IBA Dashboards", err.Error())
return
}

ids := make([]attr.Value, len(ds))
for i, j := range ds {
ids[i] = types.StringValue(j.Id.String())
}
idSet := types.SetValueMust(types.StringType, ids)

// create new state object
state := struct {
BlueprintId types.String `tfsdk:"blueprint_id"`
Ids types.Set `tfsdk:"ids"`
}{
BlueprintId: config.BlueprintId,
Ids: idSet,
}

// set state
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
10 changes: 5 additions & 5 deletions apstra/data_source_iba_widget_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import (
)

const (
dataSourceDatacenterIbaWidgetTemplateByNameHCL = `
dataSourceIbaWidgetTemplateByNameHCL = `
data "apstra_iba_widget" "test" {
blueprint_id = "%s"
name = "%s"
}
`

dataSourceDatacenterIbaWidgetTemplateByIdHCL = `
dataSourceIbaWidgetTemplateByIdHCL = `
data "apstra_iba_widget" "test" {
blueprint_id = "%s"
id = "%s"
}
`
)

func TestAccDataSourceDatacenterIbaWidget(t *testing.T) {
func TestAccDataSourceIbaWidget(t *testing.T) {
ctx := context.Background()

bpClient, bpDelete, err := testutils.MakeOrFindBlueprint(ctx, "BPA", testutils.BlueprintA)
Expand Down Expand Up @@ -58,7 +58,7 @@ func TestAccDataSourceDatacenterIbaWidget(t *testing.T) {
Steps: []resource.TestStep{
// Read by ID
{
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceDatacenterIbaWidgetTemplateByIdHCL,
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceIbaWidgetTemplateByIdHCL,
string(bpClient.Id()), string(widgetIdA)),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.apstra_iba_widget.test", "id", widgetIdA.String()),
Expand All @@ -68,7 +68,7 @@ func TestAccDataSourceDatacenterIbaWidget(t *testing.T) {
},
// Read by Name
{
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceDatacenterIbaWidgetTemplateByNameHCL,
Config: insecureProviderConfigHCL + fmt.Sprintf(dataSourceIbaWidgetTemplateByNameHCL,
string(bpClient.Id()), widgetDataA.Label),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.apstra_iba_widget.test", "id", widgetIdA.String()),
Expand Down
Loading

0 comments on commit a995094

Please sign in to comment.