diff --git a/pkg/framework/objects/azure_dev_ops_project/data_source.go b/pkg/framework/objects/azure_dev_ops_project/data_source.go new file mode 100644 index 0000000..5fe0e5b --- /dev/null +++ b/pkg/framework/objects/azure_dev_ops_project/data_source.go @@ -0,0 +1,79 @@ +package azure_dev_ops_project + +import ( + "context" + "fmt" + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/dbt_cloud" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var ( + _ datasource.DataSource = &azureDevOpsProjectDataSource{} + _ datasource.DataSourceWithConfigure = &azureDevOpsProjectDataSource{} +) + +func AzureDevOpsProjectDataSource() datasource.DataSource { + return &azureDevOpsProjectDataSource{} +} + +type azureDevOpsProjectDataSource struct { + client *dbt_cloud.Client +} + +func (d *azureDevOpsProjectDataSource) Metadata( + _ context.Context, + req datasource.MetadataRequest, + resp *datasource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_azure_dev_ops_project" +} + +func (d *azureDevOpsProjectDataSource) Read( + ctx context.Context, + req datasource.ReadRequest, + resp *datasource.ReadResponse, +) { + var state AzureDevOpsProjectDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &state)...) + + projectName := state.Name.ValueString() + + azureDevOpsProject, err := d.client.GetAzureDevOpsProject(projectName) + + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Did not find Azure DevOps Project with name: %s", state.Name), + err.Error(), + ) + return + } + + state.Name = types.StringValue(azureDevOpsProject.Name) + state.ID = types.StringValue(azureDevOpsProject.ID) + state.URL = types.StringValue(azureDevOpsProject.URL) + + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (d *azureDevOpsProjectDataSource) Configure( + _ context.Context, + req datasource.ConfigureRequest, + resp *datasource.ConfigureResponse, +) { + switch c := req.ProviderData.(type) { + case nil: // do nothing + case *dbt_cloud.Client: + d.client = c + default: + resp.Diagnostics.AddError( + "Missing client", + "A client is required to configure the Azure DevOps Project data source", + ) + } +} diff --git a/pkg/framework/objects/azure_dev_ops_project/data_source_acceptance_test.go b/pkg/framework/objects/azure_dev_ops_project/data_source_acceptance_test.go new file mode 100644 index 0000000..6e9b931 --- /dev/null +++ b/pkg/framework/objects/azure_dev_ops_project/data_source_acceptance_test.go @@ -0,0 +1,70 @@ +package azure_dev_ops_project_test + +import ( + "os" + "testing" + + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/acctest_helper" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDbtCloudAzureDevOpsProject(t *testing.T) { + //TODO: Remove both env var checks when this gets configurted in CI and the variables are parameterized + if os.Getenv("CI") != "" { + t.Skip("Skipping Azure DevOps Project datasource test in CI " + + "until Azure integration and a personal access token are available") + } + + if os.Getenv("DBT_CLOUD_PERSONAL_ACCESS_TOKEN") == "" { + t.Skip("Skipping Azure DevOps Project datasource because no personal access token is available") + } + + //TODO: Parameterize these values when a standard method is available for parameterization + adoProjectName := "dbt-cloud-ado-project" + personalAccessToken := os.Getenv("DBT_CLOUD_PERSONAL_ACCESS_TOKEN") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest_helper.TestAccPreCheck(t) }, + + ProtoV6ProviderFactories: acctest_helper.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigVariables: config.Variables{ + "dbt_token": config.StringVariable(personalAccessToken), + "ado_project_name": config.StringVariable(adoProjectName), + }, + Config: ` + variable "dbt_token" { + type = string + sensitive = true + } + + provider "dbtcloud" { + token = var.dbt_token + } + + variable "ado_project_name" { + type = string + } + + data dbtcloud_azure_dev_ops_project test { + name = var.ado_project_name + } + `, + // we check the computed values, for the other ones the test suite already checks that the plan and state are the same + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.dbtcloud_azure_dev_ops_project.test", + "id", + ), + resource.TestCheckResourceAttrSet( + "data.dbtcloud_azure_dev_ops_project.test", + "url", + ), + ), + }, + }, + }) + +} diff --git a/pkg/framework/objects/azure_dev_ops_project/model.go b/pkg/framework/objects/azure_dev_ops_project/model.go new file mode 100644 index 0000000..975eb90 --- /dev/null +++ b/pkg/framework/objects/azure_dev_ops_project/model.go @@ -0,0 +1,9 @@ +package azure_dev_ops_project + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type AzureDevOpsProjectDataSourceModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + URL types.String `tfsdk:"url"` +} diff --git a/pkg/framework/objects/azure_dev_ops_project/schema.go b/pkg/framework/objects/azure_dev_ops_project/schema.go new file mode 100644 index 0000000..ea2996c --- /dev/null +++ b/pkg/framework/objects/azure_dev_ops_project/schema.go @@ -0,0 +1,35 @@ +package azure_dev_ops_project + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func (r *azureDevOpsProjectDataSource) Schema( + _ context.Context, + _ datasource.SchemaRequest, + resp *datasource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: `Use this data source to retrieve the ID of an Azure Dev Ops project +based on its name. + +This data source requires connecting with a user token and doesn't work with a service token.`, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "The internal Azure Dev Ops ID of the ADO Project", + }, + "name": schema.StringAttribute{ + Required: true, + Description: "The name of the ADO project", + }, + "url": schema.StringAttribute{ + Computed: true, + Description: "The URL of the ADO project", + }, + }, + } +} diff --git a/pkg/provider/framework_provider.go b/pkg/provider/framework_provider.go index 4b1f72a..57fbcce 100644 --- a/pkg/provider/framework_provider.go +++ b/pkg/provider/framework_provider.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/azure_dev_ops_project" "os" "strconv" @@ -182,7 +183,8 @@ func (p *dbtCloudProvider) Configure( func (p *dbtCloudProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ - azure_dev_ops_repository.AzureDevOpsRepositoryDataSource, + azure_dev_ops_project.AzureDevOpsProjectDataSource, + azure_dev_ops_repository.AzureDevOpsRepositoryDataSource, user.UserDataSource, user.UsersDataSource, notification.NotificationDataSource, diff --git a/pkg/provider/sdk_provider.go b/pkg/provider/sdk_provider.go index c55df13..15add5b 100644 --- a/pkg/provider/sdk_provider.go +++ b/pkg/provider/sdk_provider.go @@ -51,7 +51,6 @@ func SDKProvider(version string) func() *schema.Provider { "dbtcloud_user_groups": data_sources.DatasourceUserGroups(), "dbtcloud_extended_attributes": data_sources.DatasourceExtendedAttributes(), "dbtcloud_group_users": data_sources.DatasourceGroupUsers(), - "dbtcloud_azure_dev_ops_project": data_sources.DatasourceAzureDevOpsProject(), }, ResourcesMap: map[string]*schema.Resource{ "dbtcloud_job": resources.ResourceJob(), diff --git a/pkg/sdkv2/data_sources/azure_dev_ops_project.go b/pkg/sdkv2/data_sources/azure_dev_ops_project.go deleted file mode 100644 index 015ec2b..0000000 --- a/pkg/sdkv2/data_sources/azure_dev_ops_project.go +++ /dev/null @@ -1,69 +0,0 @@ -package data_sources - -import ( - "context" - - "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/dbt_cloud" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -var azureDevOpsProjectSchema = map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The internal Azure Dev Ops ID of the ADO Project", - }, - "name": { - Type: schema.TypeString, - Required: true, - Description: "The name of the ADO project", - }, - "url": { - Type: schema.TypeString, - Computed: true, - Description: "The URL of the ADO project", - }, -} - -func DatasourceAzureDevOpsProject() *schema.Resource { - return &schema.Resource{ - ReadContext: datasourceAzureDevOpsProjectRead, - Schema: azureDevOpsProjectSchema, - Description: `Use this data source to retrieve the ID of an Azure Dev Ops project -based on its name. - -This data source requires connecting with a user token and doesn't work with a service token.`, - } -} - -func datasourceAzureDevOpsProjectRead( - ctx context.Context, - d *schema.ResourceData, - m interface{}, -) diag.Diagnostics { - c := m.(*dbt_cloud.Client) - - var diags diag.Diagnostics - - projectName := d.Get("name").(string) - - azureDevOpsProject, err := c.GetAzureDevOpsProject(projectName) - if err != nil { - return diag.FromErr(err) - } - - if err := d.Set("id", azureDevOpsProject.ID); err != nil { - return diag.FromErr(err) - } - if err := d.Set("name", azureDevOpsProject.Name); err != nil { - return diag.FromErr(err) - } - if err := d.Set("url", azureDevOpsProject.URL); err != nil { - return diag.FromErr(err) - } - - d.SetId(azureDevOpsProject.ID) - - return diags -}