diff --git a/docs/data-sources/jobs.md b/docs/data-sources/jobs.md new file mode 100644 index 00000000..5c49c295 --- /dev/null +++ b/docs/data-sources/jobs.md @@ -0,0 +1,133 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "dbtcloud_jobs Data Source - dbtcloud" +subcategory: "" +description: |- + Retrieve all the jobs for a given dbt Cloud project or environment along with the environment details for the jobs. This will return both the jobs created from Terraform but also the jobs created in the dbt Cloud UI. +--- + +# dbtcloud_jobs (Data Source) + +Retrieve all the jobs for a given dbt Cloud project or environment along with the environment details for the jobs. This will return both the jobs created from Terraform but also the jobs created in the dbt Cloud UI. + +## Example Usage + +```terraform +// we can search all jobs by project +data dbtcloud_jobs test_all_jobs_in_project { + project_id = 1234 +} + +// or by environment +data dbtcloud_jobs test_all_jobs_in_environment { + environment_id = 1234 +} + +// we can then retrieve all the jobs from the environment flagged as production +// this would include the jobs created by Terraform and the jobs created from the dbt Cloud UI +locals { + my_jobs_prod = [for job in data.dbtcloud_jobs.test_all_jobs_in_project.jobs : job if job.environment.deployment_type == "production"] +} +``` + + +## Schema + +### Optional + +- `environment_id` (Number) The ID of the environment for which we want to retrieve the jobs (one of `project_id` or `environment_id` must be set) +- `project_id` (Number) The ID of the project for which we want to retrieve the jobs (one of `project_id` or `environment_id` must be set) + +### Read-Only + +- `jobs` (Attributes Set) Set of jobs with their details (see [below for nested schema](#nestedatt--jobs)) + + +### Nested Schema for `jobs` + +Read-Only: + +- `dbt_version` (String) The version of dbt used for the job. If not set, the environment version will be used. +- `deferring_environment_id` (Number) The ID of the environment this job defers to +- `deferring_job_definition_id` (Number) [Deprecated - deferral is now set at the environment level] The ID of the job definition this job defers to +- `description` (String) The description of the job +- `environment` (Attributes) Details of the environment the job is running in (see [below for nested schema](#nestedatt--jobs--environment)) +- `environment_id` (Number) The ID of environment +- `execute_steps` (List of String) The list of steps to run in the job +- `execution` (Attributes) (see [below for nested schema](#nestedatt--jobs--execution)) +- `generate_docs` (Boolean) Whether the job generate docs +- `id` (Number) The ID of the job +- `job_completion_trigger_condition` (Attributes) Whether the job is triggered by the completion of another job (see [below for nested schema](#nestedatt--jobs--job_completion_trigger_condition)) +- `job_type` (String) The type of job (e.g. CI, scheduled) +- `name` (String) The name of the job +- `project_id` (Number) The ID of the project +- `run_generate_sources` (Boolean) Whether the job test source freshness +- `schedule` (Attributes) (see [below for nested schema](#nestedatt--jobs--schedule)) +- `settings` (Attributes) (see [below for nested schema](#nestedatt--jobs--settings)) +- `triggers` (Attributes) (see [below for nested schema](#nestedatt--jobs--triggers)) +- `triggers_on_draft_pr` (Boolean) Whether the CI job should be automatically triggered on draft PRs + + +### Nested Schema for `jobs.environment` + +Read-Only: + +- `deployment_type` (String) Type of deployment environment: staging, production +- `id` (Number) ID of the environment +- `name` (String) Name of the environment +- `project_id` (Number) +- `type` (String) Environment type: development or deployment + + + +### Nested Schema for `jobs.execution` + +Read-Only: + +- `timeout_seconds` (Number) The number of seconds before the job times out + + + +### Nested Schema for `jobs.job_completion_trigger_condition` + +Read-Only: + +- `condition` (Attributes) (see [below for nested schema](#nestedatt--jobs--job_completion_trigger_condition--condition)) + + +### Nested Schema for `jobs.job_completion_trigger_condition.condition` + +Read-Only: + +- `job_id` (Number) +- `project_id` (Number) +- `statuses` (Set of String) + + + + +### Nested Schema for `jobs.schedule` + +Read-Only: + +- `cron` (String) The cron schedule for the job. Only used if triggers.schedule is true + + + +### Nested Schema for `jobs.settings` + +Read-Only: + +- `target_name` (String) Value for `target.name` in the Jinja context +- `threads` (Number) Number of threads to run dbt with + + + +### Nested Schema for `jobs.triggers` + +Read-Only: + +- `git_provider_webhook` (Boolean) Whether the job runs automatically on PR creation +- `github_webhook` (Boolean) Whether the job runs automatically on PR creation +- `on_merge` (Boolean) Whether the job runs automatically once a PR is merged +- `schedule` (Boolean) Whether the job runs on a schedule diff --git a/examples/data-sources/dbtcloud_jobs/data-source.tf b/examples/data-sources/dbtcloud_jobs/data-source.tf new file mode 100644 index 00000000..47426d79 --- /dev/null +++ b/examples/data-sources/dbtcloud_jobs/data-source.tf @@ -0,0 +1,15 @@ +// we can search all jobs by project +data dbtcloud_jobs test_all_jobs_in_project { + project_id = 1234 +} + +// or by environment +data dbtcloud_jobs test_all_jobs_in_environment { + environment_id = 1234 +} + +// we can then retrieve all the jobs from the environment flagged as production +// this would include the jobs created by Terraform and the jobs created from the dbt Cloud UI +locals { + my_jobs_prod = [for job in data.dbtcloud_jobs.test_all_jobs_in_project.jobs : job if job.environment.deployment_type == "production"] +} diff --git a/pkg/dbt_cloud/job.go b/pkg/dbt_cloud/job.go index 96fa15af..53724f3c 100644 --- a/pkg/dbt_cloud/job.go +++ b/pkg/dbt_cloud/job.go @@ -80,6 +80,11 @@ type Job struct { JobCompletionTrigger *JobCompletionTrigger `json:"job_completion_trigger_condition"` } +type JobWithEnvironment struct { + Job + Environment Environment `json:"environment"` +} + func (c *Client) GetJob(jobID string) (*Job, error) { req, err := http.NewRequest( "GET", diff --git a/pkg/dbt_cloud/paginate.go b/pkg/dbt_cloud/paginate.go index 25239687..e0fed97f 100644 --- a/pkg/dbt_cloud/paginate.go +++ b/pkg/dbt_cloud/paginate.go @@ -185,3 +185,48 @@ func (c *Client) GetAllLicenseMaps() ([]LicenseMap, error) { } return allLicenseMaps, nil } + +func (c *Client) GetAllJobs(projectID int, environmentID int) ([]JobWithEnvironment, error) { + var url string + + if projectID != 0 && environmentID != 0 { + return nil, fmt.Errorf("you can't filter by both project and environment") + } + + if projectID == 0 && environmentID == 0 { + return nil, fmt.Errorf("you must filter by either project or environment") + } + + if projectID != 0 { + url = fmt.Sprintf( + "%s/v2/accounts/%d/jobs?project_id=%d&include_related=[environment]", + c.HostURL, + c.AccountID, + projectID, + ) + } + + if environmentID != 0 { + url = fmt.Sprintf( + "%s/v2/accounts/%d/jobs?environment_id=%d&include_related=[environment]", + c.HostURL, + c.AccountID, + environmentID, + ) + } + + allJobsRaw := c.GetData(url) + + allJobs := []JobWithEnvironment{} + for _, job := range allJobsRaw { + + data, _ := json.Marshal(job) + currentJob := JobWithEnvironment{} + err := json.Unmarshal(data, ¤tJob) + if err != nil { + return nil, err + } + allJobs = append(allJobs, currentJob) + } + return allJobs, nil +} diff --git a/pkg/framework/objects/environment/data_source_all.go b/pkg/framework/objects/environment/data_source_all.go index 221ede48..f11c1517 100644 --- a/pkg/framework/objects/environment/data_source_all.go +++ b/pkg/framework/objects/environment/data_source_all.go @@ -14,7 +14,7 @@ var ( _ datasource.DataSourceWithConfigure = &environmentsDataSources{} ) -func EnvironmentsDataSources() datasource.DataSource { +func EnvironmentsDataSource() datasource.DataSource { return &environmentsDataSources{} } diff --git a/pkg/framework/objects/job/data_source_all.go b/pkg/framework/objects/job/data_source_all.go new file mode 100644 index 00000000..149e4fd4 --- /dev/null +++ b/pkg/framework/objects/job/data_source_all.go @@ -0,0 +1,165 @@ +package job + +import ( + "context" + + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/dbt_cloud" + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/helper" + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/utils" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/samber/lo" +) + +var ( + _ datasource.DataSource = &jobsDataSource{} + _ datasource.DataSourceWithConfigure = &jobsDataSource{} + _ datasource.DataSourceWithValidateConfig = &jobsDataSource{} +) + +func JobsDataSource() datasource.DataSource { + return &jobsDataSource{} +} + +type jobsDataSource struct { + client *dbt_cloud.Client +} + +func (d *jobsDataSource) Metadata( + _ context.Context, + req datasource.MetadataRequest, + resp *datasource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_jobs" +} + +func (d *jobsDataSource) Read( + ctx context.Context, + req datasource.ReadRequest, + resp *datasource.ReadResponse, +) { + var config JobsDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + var projectID int + if config.ProjectID.IsNull() { + projectID = 0 + } else { + projectID = int(config.ProjectID.ValueInt64()) + } + var environmentID int + if config.EnvironmentID.IsNull() { + environmentID = 0 + } else { + environmentID = int(config.EnvironmentID.ValueInt64()) + } + + apiJobs, err := d.client.GetAllJobs(projectID, environmentID) + + if err != nil { + resp.Diagnostics.AddError( + "Issue when retrieving jobs", + err.Error(), + ) + return + } + + state := config + + allJobs := []JobDataSourceModel{} + for _, job := range apiJobs { + + // we need to handle the case the condition is nil + var jobCompletionTriggerCondition *JobCompletionTrigger + if job.JobCompletionTrigger != nil { + jobCompletionTriggerCondition = &JobCompletionTrigger{ + Condition: JobCompletionTriggerCondition{ + JobID: types.Int64Value( + int64(job.JobCompletionTrigger.Condition.JobID), + ), + ProjectID: types.Int64Value( + int64(job.JobCompletionTrigger.Condition.ProjectID), + ), + Statuses: lo.Map( + job.JobCompletionTrigger.Condition.Statuses, + func(status int, _ int) types.String { + return types.StringValue( + utils.JobCompletionTriggerConditionsMappingCodeHuman[status].(string), + ) + }, + ), + }, + } + } + + currentJob := JobDataSourceModel{ + Execution: JobExecution{ + TimeoutSeconds: types.Int64Value(int64(job.Execution.Timeout_Seconds)), + }, + GenerateDocs: types.BoolValue(job.Generate_Docs), + RunGenerateSources: types.BoolValue(job.Run_Generate_Sources), + ID: types.Int64PointerValue( + helper.IntPointerToInt64Pointer(job.ID), + ), + ProjectID: types.Int64Value(int64(job.Project_Id)), + EnvironmentID: types.Int64Value(int64(job.Environment_Id)), + Name: types.StringValue(job.Name), + Description: types.StringValue(job.Description), + DbtVersion: types.StringPointerValue( + job.Dbt_Version, + ), + ExecuteSteps: helper.SliceStringToSliceTypesString(job.Execute_Steps), + DeferringJobDefinitionID: types.Int64PointerValue(helper.IntPointerToInt64Pointer( + job.Deferring_Job_Id), + ), + DeferringEnvironmentID: types.Int64PointerValue(helper.IntPointerToInt64Pointer( + job.DeferringEnvironmentId), + ), + Triggers: JobTriggers{ + GithubWebhook: types.BoolValue(job.Triggers.Github_Webhook), + GitProviderWebhook: types.BoolValue(job.Triggers.GitProviderWebhook), + Schedule: types.BoolValue(job.Triggers.Schedule), + OnMerge: types.BoolValue(job.Triggers.OnMerge), + }, + Settings: JobSettings{ + Threads: types.Int64Value(int64(job.Settings.Threads)), + TargetName: types.StringValue(job.Settings.Target_Name), + }, + Schedule: JobSchedule{ + Cron: types.StringValue(job.Schedule.Cron), + }, + JobType: types.StringValue(job.JobType), + TriggersOnDraftPr: types.BoolValue(job.TriggersOnDraftPR), + Environment: JobEnvironment{ + ProjectID: types.Int64Value(int64(job.Environment.Project_Id)), + ID: types.Int64Value(int64(*job.Environment.ID)), + Name: types.StringValue(job.Environment.Name), + DeploymentType: types.StringPointerValue(job.Environment.DeploymentType), + Type: types.StringValue(job.Environment.Type), + }, + JobCompletionTriggerCondition: jobCompletionTriggerCondition, + } + + allJobs = append(allJobs, currentJob) + } + state.Jobs = allJobs + + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (d *jobsDataSource) Configure( + _ context.Context, + req datasource.ConfigureRequest, + _ *datasource.ConfigureResponse, +) { + if req.ProviderData == nil { + return + } + + d.client = req.ProviderData.(*dbt_cloud.Client) +} diff --git a/pkg/framework/objects/job/data_source_all_acceptance_test.go b/pkg/framework/objects/job/data_source_all_acceptance_test.go new file mode 100644 index 00000000..28d38834 --- /dev/null +++ b/pkg/framework/objects/job/data_source_all_acceptance_test.go @@ -0,0 +1,141 @@ +package job_test + +import ( + "fmt" + "testing" + + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/acctest_helper" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestDbtCloudJobsDataSource(t *testing.T) { + + randomJobName := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + randomJobName2 := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := jobs(randomJobName, randomJobName2) + + check := resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test", "project_id"), + resource.TestCheckResourceAttr("data.dbtcloud_jobs.test", "jobs.#", "2"), + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test", "jobs.0.id"), + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test", "jobs.0.name"), + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test", "jobs.1.id"), + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test", "jobs.1.name"), + + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test_env", "environment_id"), + resource.TestCheckResourceAttr("data.dbtcloud_jobs.test_env", "jobs.#", "1"), + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test_env", "jobs.0.id"), + resource.TestCheckResourceAttrSet( + "data.dbtcloud_jobs.test_env", + "jobs.0.project_id", + ), + resource.TestCheckResourceAttrSet("data.dbtcloud_jobs.test_env", "environment_id"), + resource.TestCheckResourceAttr("data.dbtcloud_jobs.test_env", "jobs.0.name", randomJobName), + resource.TestCheckResourceAttr( + "data.dbtcloud_jobs.test_env", + "jobs.0.environment.deployment_type", + "production", + ), + resource.TestCheckResourceAttr( + "data.dbtcloud_jobs.test_env", + "jobs.0.execution.timeout_seconds", + "180", + ), + resource.TestCheckResourceAttr( + "data.dbtcloud_jobs.test_env", + "jobs.0.triggers_on_draft_pr", + "false", + ), + resource.TestCheckResourceAttr( + "data.dbtcloud_jobs.test_env", + "jobs.0.job_completion_trigger_condition.condition.statuses.0", + "success", + ), + ) + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: acctest_helper.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) +} + +func jobs(jobName string, jobName2 string) string { + return fmt.Sprintf(` + resource "dbtcloud_project" "test_project" { + name = "jobs_test_project" + } + + resource "dbtcloud_environment" "test_environment" { + project_id = dbtcloud_project.test_project.id + name = "job_test_env" + dbt_version = "%s" + type = "deployment" + deployment_type = "production" + } + + resource "dbtcloud_environment" "test_environment2" { + project_id = dbtcloud_project.test_project.id + name = "job_test_env2" + dbt_version = "%s" + type = "deployment" + } + + resource "dbtcloud_job" "test_job" { + name = "%s" + project_id = dbtcloud_project.test_project.id + environment_id = dbtcloud_environment.test_environment.environment_id + execute_steps = [ + "dbt run" + ] + triggers = { + "github_webhook" : false, + "schedule" : false, + "git_provider_webhook": false + } + timeout_seconds = 180 + job_completion_trigger_condition { + job_id = dbtcloud_job.test_job2.id + project_id = dbtcloud_project.test_project.id + statuses = ["success"] + } + } + + resource "dbtcloud_job" "test_job2" { + name = "%s" + project_id = dbtcloud_project.test_project.id + environment_id = dbtcloud_environment.test_environment2.environment_id + execute_steps = [ + "dbt run" + ] + triggers = { + "github_webhook" : false, + "schedule" : false, + "git_provider_webhook": false + } + timeout_seconds = 1800 + } + + data "dbtcloud_jobs" "test" { + project_id = dbtcloud_project.test_project.id + depends_on = [ + dbtcloud_job.test_job, + dbtcloud_job.test_job2, + ] + } + + data "dbtcloud_jobs" "test_env" { + environment_id = dbtcloud_environment.test_environment.environment_id + depends_on = [ + dbtcloud_job.test_job, + dbtcloud_job.test_job2, + ] + } + `, acctest_helper.DBT_CLOUD_VERSION, acctest_helper.DBT_CLOUD_VERSION, jobName, jobName2) +} diff --git a/pkg/framework/objects/job/model.go b/pkg/framework/objects/job/model.go new file mode 100644 index 00000000..817e29ed --- /dev/null +++ b/pkg/framework/objects/job/model.go @@ -0,0 +1,69 @@ +package job + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type JobsDataSourceModel struct { + ProjectID types.Int64 `tfsdk:"project_id"` + EnvironmentID types.Int64 `tfsdk:"environment_id"` + Jobs []JobDataSourceModel `tfsdk:"jobs"` +} + +type JobExecution struct { + TimeoutSeconds types.Int64 `tfsdk:"timeout_seconds"` +} + +type JobTriggers struct { + GithubWebhook types.Bool `tfsdk:"github_webhook"` + GitProviderWebhook types.Bool `tfsdk:"git_provider_webhook"` + Schedule types.Bool `tfsdk:"schedule"` + OnMerge types.Bool `tfsdk:"on_merge"` +} + +type JobSettings struct { + Threads types.Int64 `tfsdk:"threads"` + TargetName types.String `tfsdk:"target_name"` +} + +type JobEnvironment struct { + ProjectID types.Int64 `tfsdk:"project_id"` + ID types.Int64 `tfsdk:"id"` + Name types.String `tfsdk:"name"` + DeploymentType types.String `tfsdk:"deployment_type"` + Type types.String `tfsdk:"type"` +} + +type JobCompletionTrigger struct { + Condition JobCompletionTriggerCondition `tfsdk:"condition"` +} + +type JobCompletionTriggerCondition struct { + JobID types.Int64 `tfsdk:"job_id"` + ProjectID types.Int64 `tfsdk:"project_id"` + Statuses []types.String `tfsdk:"statuses"` +} + +type JobSchedule struct { + Cron types.String `tfsdk:"cron"` +} + +type JobDataSourceModel struct { + Execution JobExecution `tfsdk:"execution"` + GenerateDocs types.Bool `tfsdk:"generate_docs"` + RunGenerateSources types.Bool `tfsdk:"run_generate_sources"` + ID types.Int64 `tfsdk:"id"` + ProjectID types.Int64 `tfsdk:"project_id"` + EnvironmentID types.Int64 `tfsdk:"environment_id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + DbtVersion types.String `tfsdk:"dbt_version"` + ExecuteSteps []types.String `tfsdk:"execute_steps"` + DeferringJobDefinitionID types.Int64 `tfsdk:"deferring_job_definition_id"` + DeferringEnvironmentID types.Int64 `tfsdk:"deferring_environment_id"` + Triggers JobTriggers `tfsdk:"triggers"` + Settings JobSettings `tfsdk:"settings"` + Schedule JobSchedule `tfsdk:"schedule"` + JobType types.String `tfsdk:"job_type"` + TriggersOnDraftPr types.Bool `tfsdk:"triggers_on_draft_pr"` + Environment JobEnvironment `tfsdk:"environment"` + JobCompletionTriggerCondition *JobCompletionTrigger `tfsdk:"job_completion_trigger_condition"` +} diff --git a/pkg/framework/objects/job/schema.go b/pkg/framework/objects/job/schema.go new file mode 100644 index 00000000..0a58318a --- /dev/null +++ b/pkg/framework/objects/job/schema.go @@ -0,0 +1,219 @@ +package job + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func (d jobsDataSource) ValidateConfig( + ctx context.Context, + req datasource.ValidateConfigRequest, + resp *datasource.ValidateConfigResponse, +) { + var data JobsDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + if data.ProjectID.IsNull() && data.EnvironmentID.IsNull() { + resp.Diagnostics.AddAttributeError( + path.Root("project_id"), + "Missing Attribute Configuration", + "project_id or environment_id must be configured.", + ) + } + + if !(data.ProjectID.IsNull() || data.EnvironmentID.IsNull()) { + resp.Diagnostics.AddAttributeError( + path.Root("project_id"), + "Invalid Attribute Configuration", + "Only one of project_id or environment_id can be configured.", + ) + } +} + +func (d *jobsDataSource) Schema( + ctx context.Context, + req datasource.SchemaRequest, + resp *datasource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: "Retrieve all the jobs for a given dbt Cloud project or environment along with the environment details for the jobs. This will return both the jobs created from Terraform but also the jobs created in the dbt Cloud UI.", + Attributes: map[string]schema.Attribute{ + "project_id": schema.Int64Attribute{ + Optional: true, + Description: "The ID of the project for which we want to retrieve the jobs (one of `project_id` or `environment_id` must be set)", + }, + "environment_id": schema.Int64Attribute{ + Optional: true, + Description: "The ID of the environment for which we want to retrieve the jobs (one of `project_id` or `environment_id` must be set)", + }, + "jobs": schema.SetNestedAttribute{ + Computed: true, + Description: "Set of jobs with their details", + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "execution": schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "timeout_seconds": schema.Int64Attribute{ + Computed: true, + Description: "The number of seconds before the job times out", + }, + }, + }, + "generate_docs": schema.BoolAttribute{ + Computed: true, + Description: "Whether the job generate docs", + }, + "run_generate_sources": schema.BoolAttribute{ + Computed: true, + Description: "Whether the job test source freshness", + }, + "id": schema.Int64Attribute{ + Computed: true, + Description: "The ID of the job", + }, + "project_id": schema.Int64Attribute{ + Computed: true, + Description: "The ID of the project", + }, + "environment_id": schema.Int64Attribute{ + Computed: true, + Description: "The ID of environment", + }, + "name": schema.StringAttribute{ + Computed: true, + Description: "The name of the job", + }, + "description": schema.StringAttribute{ + Computed: true, + Description: "The description of the job", + }, + "dbt_version": schema.StringAttribute{ + Computed: true, + Description: "The version of dbt used for the job. If not set, the environment version will be used.", + }, + "execute_steps": schema.ListAttribute{ + Computed: true, + ElementType: types.StringType, + Description: "The list of steps to run in the job", + }, + "deferring_job_definition_id": schema.Int64Attribute{ + Computed: true, + Description: "[Deprecated - deferral is now set at the environment level] The ID of the job definition this job defers to", + }, + "deferring_environment_id": schema.Int64Attribute{ + Computed: true, + Description: "The ID of the environment this job defers to", + }, + "triggers": schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "github_webhook": schema.BoolAttribute{ + Computed: true, + Description: "Whether the job runs automatically on PR creation", + }, + "git_provider_webhook": schema.BoolAttribute{ + Computed: true, + Description: "Whether the job runs automatically on PR creation", + }, + "schedule": schema.BoolAttribute{ + Computed: true, + Description: "Whether the job runs on a schedule", + }, + "on_merge": schema.BoolAttribute{ + Computed: true, + Description: "Whether the job runs automatically once a PR is merged", + }, + }, + }, + "settings": schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "threads": schema.Int64Attribute{ + Computed: true, + Description: "Number of threads to run dbt with", + }, + "target_name": schema.StringAttribute{ + Computed: true, + Description: "Value for `target.name` in the Jinja context", + }, + }, + }, + "schedule": schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "cron": schema.StringAttribute{ + Computed: true, + Description: "The cron schedule for the job. Only used if triggers.schedule is true", + }, + }, + }, + "job_type": schema.StringAttribute{ + Computed: true, + Description: "The type of job (e.g. CI, scheduled)", + }, + "triggers_on_draft_pr": schema.BoolAttribute{ + Computed: true, + Description: "Whether the CI job should be automatically triggered on draft PRs", + }, + "environment": schema.SingleNestedAttribute{ + Computed: true, + Description: "Details of the environment the job is running in", + Attributes: map[string]schema.Attribute{ + "project_id": schema.Int64Attribute{ + Computed: true, + }, + "id": schema.Int64Attribute{ + Computed: true, + Description: "ID of the environment", + }, + "name": schema.StringAttribute{ + Computed: true, + Description: "Name of the environment", + }, + "deployment_type": schema.StringAttribute{ + Computed: true, + Description: "Type of deployment environment: staging, production", + }, + "type": schema.StringAttribute{ + Computed: true, + Description: "Environment type: development or deployment", + }, + }, + }, + "job_completion_trigger_condition": schema.SingleNestedAttribute{ + Computed: true, + Description: "Whether the job is triggered by the completion of another job", + Attributes: map[string]schema.Attribute{ + "condition": schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "job_id": schema.Int64Attribute{ + Computed: true, + }, + "project_id": schema.Int64Attribute{ + Computed: true, + }, + "statuses": schema.SetAttribute{ + Computed: true, + ElementType: types.StringType, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} diff --git a/pkg/framework/objects/notification/resource.go b/pkg/framework/objects/notification/resource.go index 59e6efdd..fa7b6e89 100644 --- a/pkg/framework/objects/notification/resource.go +++ b/pkg/framework/objects/notification/resource.go @@ -12,9 +12,10 @@ import ( ) var ( - _ resource.Resource = ¬ificationResource{} - _ resource.ResourceWithConfigure = ¬ificationResource{} - _ resource.ResourceWithImportState = ¬ificationResource{} + _ resource.Resource = ¬ificationResource{} + _ resource.ResourceWithConfigure = ¬ificationResource{} + _ resource.ResourceWithImportState = ¬ificationResource{} + _ resource.ResourceWithValidateConfig = ¬ificationResource{} ) func NotificationResource() resource.Resource { diff --git a/pkg/helper/helper.go b/pkg/helper/helper.go index 435801d2..792738c9 100644 --- a/pkg/helper/helper.go +++ b/pkg/helper/helper.go @@ -19,6 +19,15 @@ func EmptySetDefault(elemType attr.Type) defaults.Set { ) } +func IntPointerToInt64Pointer(value *int) *int64 { + if value == nil { + return nil + } + ret := int64(*value) + return &ret +} + +// API data types to TF types func SetIntToInt64OrNull(value int) types.Int64 { if value == 0 { return types.Int64Null() @@ -26,6 +35,23 @@ func SetIntToInt64OrNull(value int) types.Int64 { return types.Int64Value(int64(value)) } +func SliceStringToSliceTypesString(input []string) []types.String { + result := make([]types.String, len(input)) + for i, v := range input { + result[i] = types.StringValue(v) + } + return result +} + +func SliceStringToSliceTypesInt64(input []int) []types.Int64 { + result := make([]types.Int64, len(input)) + for i, v := range input { + result[i] = types.Int64Value(int64(v)) + } + return result +} + +// TF types to API data types func Int64SetToIntSlice(set types.Set) []int { elements := set.Elements() result := make([]int, len(elements)) @@ -44,15 +70,8 @@ func StringSetToStringSlice(set types.Set) []string { return result } +// useful for docs func DocString(inp string) string { newString := strings.ReplaceAll(inp, "~~~", "`") return regexp.MustCompile(`(?m)^\t+`).ReplaceAllString(newString, "") } - -func IntPointerToInt64Pointer(value *int) *int64 { - if value == nil { - return nil - } - ret := int64(*value) - return &ret -} diff --git a/pkg/provider/framework_provider.go b/pkg/provider/framework_provider.go index 5f3908b2..95189f22 100644 --- a/pkg/provider/framework_provider.go +++ b/pkg/provider/framework_provider.go @@ -9,6 +9,7 @@ import ( "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/environment" "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/group" "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/group_partial_permissions" + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/job" "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/notification" "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/partial_license_map" "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/framework/objects/partial_notification" @@ -177,8 +178,9 @@ func (p *dbtCloudProvider) DataSources(_ context.Context) []func() datasource.Da user.UserDataSource, notification.NotificationDataSource, environment.EnvironmentDataSource, - environment.EnvironmentsDataSources, + environment.EnvironmentsDataSource, group.GroupDataSource, + job.JobsDataSource, service_token.ServiceTokenDataSource, } } diff --git a/terraform_resources.d2 b/terraform_resources.d2 index 11a75bd6..92f1ee12 100644 --- a/terraform_resources.d2 +++ b/terraform_resources.d2 @@ -49,7 +49,7 @@ user_groups -- group user_groups -- group_partial_permissions project -- environment job -- environment -job -- environemnt_variable_job_override +job -- environment_variable_job_override notification -- job partial_notification -- job project_artefacts -- job diff --git a/terraform_resources.png b/terraform_resources.png index 26dba453..c124d427 100644 Binary files a/terraform_resources.png and b/terraform_resources.png differ