diff --git a/docs/resources/zenduty_post_incident_tasks.md b/docs/resources/zenduty_post_incident_tasks.md new file mode 100644 index 0000000..80af4e4 --- /dev/null +++ b/docs/resources/zenduty_post_incident_tasks.md @@ -0,0 +1,67 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "Zenduty: PostIncidentTasks" +subcategory: "" +description: |- + Provides a Zenduty PostIncidentTasks Resource. This allows PostIncidentTasks to be created, updated, and deleted. + +--- + +# Resource : zenduty_post_incident_tasks +Provides a Zenduty PostIncidentTasks Resource. This allows PostIncidentTasks to be created, updated, and deleted. +## Example Usage +```hcl +resource "zenduty_teams" "exampleteam" { + name = "exmaple team" +} +``` + + +```hcl +resource "zenduty_post_incident_tasks" "demotask" { + title = "demo task template" + description = "this is a description of demo task" + team_id = zenduty_teams.exampleteam.id + due_in_time = "YYYY-MM-DD HH:MM" + status = 0 +} +``` + + +## Argument Reference + +* `team_id` - (Required) The unique_id of team. +* `title` - (Required) The title of the task. +* `description` - (Required) The description of the task. +* `status` - (Optional) the status of the task choices are `0` is To-Do,`1` is In-Progress, `2`- Done +* `due_in_time` - (Optional) The due time of the task in format YYYY-MM-DD HH:MM. +* `assigned_to` - (Optional) username of the user to assign the task +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Zenduty PostIncidentTasks. + +## Import + +Team PostIncidentTask can be imported using the `team_id`(ie. unique_id of the team) and `task_id`(ie. unique_id of the task template) + +```hcl +resource "zenduty_post_incident_tasks" "demotask" { + + +} +``` + +`$ terraform import zenduty_post_incident_tasks.demotask team_id/task_id` + +`$ terraform state show zenduty_post_incident_tasks.demotask` + +`* copy the output data and paste inside zenduty_post_incident_tasks.demotask resource block and remove the id attribute` + +`$ terraform plan` to verify the import + + + + + diff --git a/docs/resources/zenduty_sla.md b/docs/resources/zenduty_sla.md new file mode 100644 index 0000000..84a6c49 --- /dev/null +++ b/docs/resources/zenduty_sla.md @@ -0,0 +1,120 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "Zenduty: SLAs" +subcategory: "" +description: |- + Provides a Zenduty SLAs Resource. This allows SLAs to be created, updated, and deleted. + +--- + +# Resource : zenduty_sla +Provides a Zenduty SLAs Resource. This allows SLAs to be created, updated, and deleted. +## Example Usage +```hcl +resource "zenduty_teams" "exampleteam" { + name = "exmaple team" +} + +data "zenduty_user" "user1" { + email = "demouser@gmail.com" +} +``` + +```hcl + +resource "zenduty_sla" "example_sla" { + name = "demo sla" + description = "this is a demo sla" + team_id = zenduty_teams.exampleteam.id + acknowledge_time = 5 + resolve_time = 10 + escalations { + time = 30 + type = 1 + responders { + user = data.zenduty_user.user1.users[0].username + } + } + escalations { + time = -10 + type = 2 + responders { + user = data.zenduty_user.user1.users[0].username + } + } +} + +``` + + +## Argument Reference + +* `team_id` - (Required) The unique_id of team to create the sla in. +* `name` - (Required) The name of the sla. +* `description` - (Required) The description of the sla +* `acknowledge_time` - (Required) time in seconds to trigger SLA if not acknowledged. +* `resolve_time` - (Required) Time in seconds to trigger SLA if not resolved. +* `escalations` - (Required) Reminders when an SLA is breached or about to breach. (see [below for nested schema](#nestedblock--escalation)) + +```hcl + +escalations { + time = 30 + type = 1 + responders { + user = data.zenduty_user.user1.users[0].username + } + } + +``` + + + +* `type`: It is an integer field that determines the type of notification behavior. `1` signifies that notifications are sent when the SLA breach is acknowledged, while `2` indicates notifications for resolution SLA breaches. +* `time`: This field specifies the time duration in seconds when notifications should be sent. If time is positive, it means notifications will be sent x seconds after the SLA breach, and if it's negative, notifications will be sent x seconds before the breach. +* `responders` - users who need to be paged when an SLA is breached. (see [below for nested schema](#nestedblock--responders)) + + + + +```hcl +responders { + user = data.zenduty_user.user1.users[0].username +} +``` +* `user` - Username of the user who will be notified if sla is breached + + + + + + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Zenduty SlA. + +## Import + +Team SLAs can be imported using the `team_id`(ie. unique_id of the team) and `sla_id`(ie. unique_id of the sla), e.g. + +```hcl +resource "zenduty_sla" "sla1" { + + +} +``` + +`$ terraform import zenduty_sla.sla1 team_id/sla_id` + +`$ terraform state show zenduty_sla.sla1` + +`* copy the output data and paste inside zenduty_sla.sla1 resource block and remove the id attribute` + +`$ terraform plan` to verify the import + + + + + diff --git a/docs/resources/zenduty_task_template_tasks.md b/docs/resources/zenduty_task_template_tasks.md new file mode 100644 index 0000000..46abe9f --- /dev/null +++ b/docs/resources/zenduty_task_template_tasks.md @@ -0,0 +1,81 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "Zenduty: TaskTemplateTasks" +subcategory: "" +description: |- + Provides a Zenduty TaskTemplateTasks Resource. This allows TaskTemplateTasks to be created, updated, and deleted. + +--- + +# Resource : zenduty_task_template_tasks +Provides a Zenduty TaskTemplateTasks Resource. This allows TaskTemplateTasks to be created, updated, and deleted. +## Example Usage +```hcl +resource "zenduty_teams" "exampleteam" { + name = "exmaple team" +} + +resource "zenduty_task_template_tasks" "demotemplate" { + name = "example template" + summary = "this is an example template" + team_id = zenduty_teams.exampleteam.id +} + +resource "zenduty_roles" "example_role" { + team = zenduty_teams.exampleteam.id + title = "Example Role" + description = "Role Description" +} + +``` + + +```hcl +resource "zenduty_task_template_tasks" "demotask" { + team_id = "af0e6c8a-c895-434c-b667-2f44833e15b6" + task_template_id = zenduty_task_template_tasks.demotemplate.id + title = "demo task" + description = "this is a description of demo task" + role = zenduty_roles.example_role.id +} +``` + + +## Argument Reference + +* `team_id` - (Required) The unique_id of team. +* `task_template_id` - (Required) The unique_id of tasktemplate to create the tasktemplatetasks in. +* `title` - (Required) The title of the task. +* `description` - (Required) The description of the task. +* `role` - (Optional) The unique_id of incident role attached to the task +* `due_in` - (Optional) Time in minutes to complete the task `-1` => no due time + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Zenduty TaskTemplateTask. + +## Import + +Team TaskTemplate can be imported using the `team_id`(ie. unique_id of the team) and `task_template_id`(ie. unique_id of the task template), and `task_template_task_id` (ie. unique_id of the task template task id) . + +```hcl +resource "zenduty_task_template_tasks" "demotemplatetask" { + + +} +``` + +`$ terraform import zenduty_task_template_tasks.demotemplatetask team_id/task_template_id/task_template_task_id` + +`$ terraform state show zenduty_task_template_tasks.demotemplatetask` + +`* copy the output data and paste inside zenduty_task_template_tasks.demotemplatetask resource block and remove the id attribute` + +`$ terraform plan` to verify the import + + + + + diff --git a/docs/resources/zenduty_task_templates.md b/docs/resources/zenduty_task_templates.md new file mode 100644 index 0000000..67a06b6 --- /dev/null +++ b/docs/resources/zenduty_task_templates.md @@ -0,0 +1,62 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "Zenduty: TaskTemplates" +subcategory: "" +description: |- + Provides a Zenduty TaskTemplates Resource. This allows TaskTemplates to be created, updated, and deleted. + +--- + +# Resource : zenduty_task_templates +Provides a Zenduty TaskTemplates Resource. This allows TaskTemplates to be created, updated, and deleted. +## Example Usage +```hcl +resource "zenduty_teams" "exampleteam" { + name = "exmaple team" +} +``` + +```hcl +resource "zenduty_task_templates" "demotemplate" { + name = "example template" + summary = "this is an example template" + team_id = zenduty_teams.exampleteam.id +} +``` + + +## Argument Reference + +* `team_id` - (Required) The unique_id of team to create the tasktemplate in. +* `name` - (Required) The name of the tasktemplate. +* `summary` - (Required) The summary of the tasktemplate. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Zenduty TaskTemplate. + +## Import + +Team TaskTemplate can be imported using the `team_id`(ie. unique_id of the team) and `task_template_id`(ie. unique_id of the task template), e.g. + +```hcl +resource "zenduty_task_templates" "demotemplate" { + + +} +``` + +`$ terraform import zenduty_task_templates.demotemplate team_id/task_template_id` + +`$ terraform state show zenduty_task_templates.demotemplate` + +`* copy the output data and paste inside zenduty_task_templates.demotemplate resource block and remove the id attribute` + +`$ terraform plan` to verify the import + + + + + diff --git a/go.mod b/go.mod index 852d001..e1651c6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module terraform-provider-zenduty go 1.17 require ( - github.com/Zenduty/zenduty-go-sdk v0.1.7 + github.com/Zenduty/zenduty-go-sdk v0.1.8 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 ) @@ -16,6 +16,7 @@ require ( github.com/fatih/color v1.7.0 // indirect github.com/golang/protobuf v1.4.2 // indirect github.com/google/go-cmp v0.5.6 // indirect + github.com/google/uuid v1.3.1 github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/go.sum b/go.sum index 4b7ee55..c0591b5 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C6 github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/Zenduty/zenduty-go-sdk v0.1.7 h1:IojKvlQAKI6/5GnicYCFLtz7+J6vnG+PsHlUB5aZAGk= github.com/Zenduty/zenduty-go-sdk v0.1.7/go.mod h1:XnK1p2uyFpBUwbW+xecKSww0klZOSKKoywobqGDlFE8= +github.com/Zenduty/zenduty-go-sdk v0.1.8 h1:vREmE/XO+nVR7oZ0JLp2W18QTl8r3a6ymsYY7BwDAIg= +github.com/Zenduty/zenduty-go-sdk v0.1.8/go.mod h1:XnK1p2uyFpBUwbW+xecKSww0klZOSKKoywobqGDlFE8= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -150,6 +152,8 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= diff --git a/zenduty/helpers.go b/zenduty/helpers.go index 9969638..81e6dba 100644 --- a/zenduty/helpers.go +++ b/zenduty/helpers.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "github.com/google/uuid" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -113,3 +114,9 @@ func ValidateRequired() schema.SchemaValidateDiagFunc { } } + +func genrateUUID() string { + id := uuid.New() + uuidString := id.String() + return uuidString +} diff --git a/zenduty/provider.go b/zenduty/provider.go index f8812f3..46db6dd 100644 --- a/zenduty/provider.go +++ b/zenduty/provider.go @@ -42,6 +42,10 @@ func Provider() *schema.Provider { "zenduty_assign_account_role": resourceAssignAccountRole(), "zenduty_globalrouter": resourceGlobalRouter(), "zenduty_globalrouting_rule": resourceGlobalRoutingRules(), + "zenduty_sla": resourceSLA(), + "zenduty_post_incident_tasks": resourcePostIncidentTasks(), + "zenduty_task_templates": resourceTaskTemplates(), + "zenduty_task_template_tasks": resourceTaskTemplateTaskTasks(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/zenduty/resource_post_incident_tasks.go b/zenduty/resource_post_incident_tasks.go new file mode 100644 index 0000000..a720116 --- /dev/null +++ b/zenduty/resource_post_incident_tasks.go @@ -0,0 +1,217 @@ +package zenduty + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/Zenduty/zenduty-go-sdk/client" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourcePostIncidentTasks() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceCreatePostIncidentTasks, + UpdateContext: resourceUpdatePostIncidentTasks, + DeleteContext: resourceDeletePostIncidentTasks, + ReadContext: resourceReadPostIncidentTasks, + Importer: &schema.ResourceImporter{ + State: resourcePostIncidentTasksImporter, + }, + Schema: map[string]*schema.Schema{ + "team_id": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: ValidateUUID(), + }, + "unique_id": { + Type: schema.TypeString, + Computed: true, + }, + "title": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Required: true, + }, + "assigned_to": { + Type: schema.TypeString, + Optional: true, + }, + "status": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 2), + Default: 0, + }, + "due_in_time": { + Type: schema.TypeString, + Optional: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func parseDueInTime(timestamp string) string { + RFC3339local := "2006-01-02T15:04:05Z" + t, parseErr := time.Parse(RFC3339local, timestamp) + if parseErr != nil { + return timestamp + } + return t.Format("2006-01-02 15:04") + +} + +func CreatePostIncidentTask(Ctx context.Context, d *schema.ResourceData, m interface{}) (*client.PostIncidentTaskObj, diag.Diagnostics) { + + newpostincidenttask := &client.PostIncidentTaskObj{} + + if v, ok := d.GetOk("team_id"); ok { + newpostincidenttask.Team = v.(string) + + } + if v, ok := d.GetOk("description"); ok { + newpostincidenttask.Description = v.(string) + + } + if v, ok := d.GetOk("title"); ok { + newpostincidenttask.Title = v.(string) + } + if v, ok := d.GetOk("rank"); ok { + newpostincidenttask.Status = v.(int) + } + if v, ok := d.GetOk("assigned_to"); ok { + newpostincidenttask.AssignedTo = v.(string) + } + if v, ok := d.GetOk("status"); ok { + newpostincidenttask.Status = v.(int) + } + if v, ok := d.GetOk("due_in_time"); ok { + DueInTime := v.(string) + newpostincidenttask.DueInTime = &DueInTime + parsedTime, parsedErr := time.Parse("2006-01-02 15:04", v.(string)) + if parsedErr == nil { + formattedTime := parsedTime.In(time.UTC).Format(time.RFC3339) + newpostincidenttask.DueInTime = &formattedTime + } else { + return nil, diag.FromErr(parsedErr) + } + } + + return newpostincidenttask, nil + +} + +func resourceCreatePostIncidentTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + + task, createErr := CreatePostIncidentTask(Ctx, d, m) + if createErr != nil { + return createErr + } + + var diags diag.Diagnostics + incidenttask, err := apiclient.PostIncidentTask.CreatePostIncidentTask(teamID, task) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(incidenttask.UniqueID) + + return diags +} + +func resourceUpdatePostIncidentTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + task, createErr := CreatePostIncidentTask(Ctx, d, m) + if createErr != nil { + return createErr + + } + + _, err := apiclient.PostIncidentTask.UpdatePostIncidentTaskByID(teamID, id, task) + if err != nil { + return diag.FromErr(err) + } + return resourceReadPostIncidentTasks(Ctx, d, m) +} + +func resourceDeletePostIncidentTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + var diags diag.Diagnostics + err := apiclient.PostIncidentTask.DeletePostIncidentTaskByID(teamID, id) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceReadPostIncidentTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if emptyString(teamID) { + return diag.FromErr(errors.New("team_id is required")) + } + var diags diag.Diagnostics + postincidenttask, err := apiclient.PostIncidentTask.GetPostIncidentTaskByID(teamID, id) + if err != nil { + return diag.FromErr(err) + } + + d.Set("title", postincidenttask.Title) + d.Set("description", postincidenttask.Description) + d.Set("assigned_to", postincidenttask.AssignedTo) + d.Set("status", postincidenttask.Status) + d.Set("team_id", teamID) + if postincidenttask.DueInTime != nil { + d.Set("due_in_time", parseDueInTime(*postincidenttask.DueInTime)) + } + d.Set("creation_date", postincidenttask.CreationDate) + + return diags +} + +func resourcePostIncidentTasksImporter(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return nil, fmt.Errorf("unexpected format of id (%q), expected /", d.Id()) + } else if !IsValidUUID(parts[0]) { + return nil, fmt.Errorf("invalid team_id (%q)", parts[0]) + } else if !IsValidUUID(parts[1]) { + return nil, fmt.Errorf("invalid task_id (%q)", parts[1]) + } + d.Set("team_id", parts[0]) + d.SetId(parts[1]) + return []*schema.ResourceData{d}, nil +} diff --git a/zenduty/resource_sla.go b/zenduty/resource_sla.go new file mode 100644 index 0000000..7add0fc --- /dev/null +++ b/zenduty/resource_sla.go @@ -0,0 +1,302 @@ +package zenduty + +import ( + "context" + "errors" + "fmt" + "log" + "strings" + + "github.com/Zenduty/zenduty-go-sdk/client" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceSLA() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceCreateSLA, + UpdateContext: resourceUpdateSLA, + DeleteContext: resourceDeleteSLA, + ReadContext: resourceReadSLA, + Importer: &schema.ResourceImporter{ + State: resourceSLAImporter, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "team_id": { + Type: schema.TypeString, + Required: true, + }, + "escalations": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "unique_id": { + Type: schema.TypeString, + Computed: true, + }, + "time": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(-432000, 432000), + }, + "type": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 2), + }, + "responders": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "acknowledge_time": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 432000), + }, + "resolve_time": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 432000), + }, + "is_active": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + }, + } +} + +func CreateSLA(Ctx context.Context, d *schema.ResourceData, m interface{}) (*client.SLAObj, diag.Diagnostics) { + newSLA := &client.SLAObj{} + escalations := d.Get("escalations").([]interface{}) + if v, ok := d.GetOk("name"); ok { + newSLA.Name = v.(string) + } + if v, ok := d.GetOk("description"); ok { + newSLA.Description = v.(string) + if emptyString(newSLA.Description) { + return nil, diag.FromErr(errors.New("description is empty")) + } + } + if v, ok := d.GetOk("acknowledge_time"); ok { + newSLA.AcknowledgeTime = v.(int) + } + if v, ok := d.GetOk("resolve_time"); ok { + newSLA.ResolveTime = v.(int) + } + if v, ok := d.GetOk("is_active"); ok { + newSLA.IsActive = v.(bool) + } + + newSLA.Escalations = make([]client.SLAEscalations, len(escalations)) + + for i, escalation := range escalations { + escalationMap := escalation.(map[string]interface{}) + newEscalation := client.SLAEscalations{} + + if v, ok := escalationMap["time"]; ok { + newEscalation.Time = v.(int) + + } + if v, ok := escalationMap["type"]; ok { + newEscalation.Type = v.(int) + } + if v, ok := escalationMap["unique_id"]; ok { + newEscalation.UniqueID = v.(string) + if emptyString(newEscalation.UniqueID) { + newEscalation.UniqueID = genrateUUID() + } + } + + if v, ok := escalationMap["responders"]; ok { + responderusers := v.([]interface{}) + newEscalation.Responders = make([]client.ResponderUser, len(responderusers)) + for j, responder := range responderusers { + resonderMap := responder.(map[string]interface{}) + responderuser := client.ResponderUser{} + if v, ok := resonderMap["user"]; ok { + responderuser.User = v.(string) + } + newEscalation.Responders[j] = responderuser + } + } + newSLA.Escalations[i] = newEscalation + } + return newSLA, nil + +} + +func flattenEscalation(escalations []client.SLAEscalations) []map[string]interface{} { + result := make([]map[string]interface{}, len(escalations)) + for i, escalation := range escalations { + result[i] = map[string]interface{}{ + "time": escalation.Time, + "type": escalation.Type, + "unique_id": escalation.UniqueID, + "responders": flattenResponderUser(escalation.Responders), + } + } + return result +} + +func flattenResponderUser(responders []client.ResponderUser) []map[string]interface{} { + result := make([]map[string]interface{}, len(responders)) + for i, responder := range responders { + result[i] = map[string]interface{}{ + "user": responder.User, + } + } + return result +} + +func resourceCreateSLA(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + var teamID string + if v, ok := d.GetOk("team_id"); ok { + if emptyString(v.(string)) { + return diag.FromErr(errors.New("team_id must not be empty")) + } + teamID = v.(string) + } + + var diags diag.Diagnostics + newSLA, createErr := CreateSLA(Ctx, d, m) + if createErr != nil { + return createErr + } + + sla, err := apiclient.Sla.CreateSLA(teamID, newSLA) + + if err != nil { + return diag.FromErr(err) + } + + for i, escalation := range sla.Escalations { + log.Printf("SLAEscalation %d: %v", i, escalation) + } + log.Printf("%+v\n", sla.Escalations) + d.SetId(sla.UniqueID) + if err := d.Set("escalations", flattenEscalation(sla.Escalations)); err != nil { + return diag.FromErr(err) + } + + return diags + +} + +func resourceUpdateSLA(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + var teamID string + if v, ok := d.GetOk("team_id"); ok { + if emptyString(v.(string)) { + return diag.FromErr(errors.New("team_id must not be empty")) + } + teamID = v.(string) + } + + newSLA, createErr := CreateSLA(Ctx, d, m) + if createErr != nil { + return createErr + } + id := d.Id() + var diags diag.Diagnostics + + sla, err := apiclient.Sla.UpdateSLAByID(teamID, id, newSLA) + + if err != nil { + return diag.FromErr(err) + } + + if err := d.Set("escalations", flattenEscalation(sla.Escalations)); err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceDeleteSLA(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + var teamID string + if v, ok := d.GetOk("team_id"); ok { + if emptyString(v.(string)) { + return diag.FromErr(errors.New("team_id must not be empty")) + } + teamID = v.(string) + } + + id := d.Id() + + var diags diag.Diagnostics + err := apiclient.Sla.DeleteSLAByID(teamID, id) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceReadSLA(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + if v, ok := d.GetOk("team_id"); ok { + teamID = v.(string) + } + + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required ")) + } + var diags diag.Diagnostics + sla, err := apiclient.Sla.GetSLAByID(teamID, id) + if err != nil { + return diag.FromErr(err) + } + d.Set("name", sla.Name) + d.Set("description", sla.Description) + d.Set("acknowledge_time", sla.AcknowledgeTime) + d.Set("resolve_time", sla.ResolveTime) + d.Set("is_active", sla.IsActive) + if err := d.Set("escalations", flattenEscalation(sla.Escalations)); err != nil { + return diag.FromErr(err) + } + + return diags +} + +func resourceSLAImporter(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return nil, fmt.Errorf("unexpected format of id (%q), expected /", d.Id()) + } else if !IsValidUUID(parts[0]) { + return nil, fmt.Errorf("invalid team_id (%q)", parts[0]) + } else if !IsValidUUID(parts[1]) { + return nil, fmt.Errorf("invalid sla id (%q)", parts[1]) + } + d.Set("team_id", parts[0]) + d.SetId(parts[1]) + return []*schema.ResourceData{d}, nil +} diff --git a/zenduty/resource_task_template_tasks.go b/zenduty/resource_task_template_tasks.go new file mode 100644 index 0000000..73e357c --- /dev/null +++ b/zenduty/resource_task_template_tasks.go @@ -0,0 +1,203 @@ +package zenduty + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/Zenduty/zenduty-go-sdk/client" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceTaskTemplateTaskTasks() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceCreateTaskTemplateTaskTasks, + UpdateContext: resourceUpdateTaskTemplateTaskTasks, + DeleteContext: resourceDeleteTaskTemplateTaskTasks, + ReadContext: resourceReadTaskTemplateTaskTasks, + Importer: &schema.ResourceImporter{ + State: resourceTaskTemplateTaskTasksImporter, + }, + Schema: map[string]*schema.Schema{ + "team_id": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: ValidateUUID(), + }, + "task_template_id": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: ValidateUUID(), + }, + "unique_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: ValidateUUID(), + }, + "title": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Required: true, + }, + "due_in": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(-1, 10080), + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + "position": { + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func CreateTaskTemplateTask(Ctx context.Context, d *schema.ResourceData, m interface{}) (*client.TaskTemplateTaskObj, diag.Diagnostics) { + + newTaskTemplateTask := &client.TaskTemplateTaskObj{} + + if v, ok := d.GetOk("role"); ok { + newTaskTemplateTask.Role = v.(string) + + } + if v, ok := d.GetOk("description"); ok { + newTaskTemplateTask.Description = v.(string) + + } + if v, ok := d.GetOk("title"); ok { + newTaskTemplateTask.Title = v.(string) + } + if v, ok := d.GetOk("due_in"); ok { + newTaskTemplateTask.DueIn = v.(int) + } + if v, ok := d.GetOk("task_template_id"); ok { + newTaskTemplateTask.TaskTemplate = v.(string) + } + position := d.Get("position").(int) + newTaskTemplateTask.Positon = position + + return newTaskTemplateTask, nil + +} + +func resourceCreateTaskTemplateTaskTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + + task, createErr := CreateTaskTemplateTask(Ctx, d, m) + if createErr != nil { + return createErr + } + + var diags diag.Diagnostics + incidenttask, err := apiclient.TaskTemplate.CreateTaskTemplateTask(teamID, task.TaskTemplate, task) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(incidenttask.UniqueID) + + return diags +} + +func resourceUpdateTaskTemplateTaskTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + task, createErr := CreateTaskTemplateTask(Ctx, d, m) + if createErr != nil { + return createErr + + } + + _, err := apiclient.TaskTemplate.UpdateTaskTemplateTaskByID(teamID, task.TaskTemplate, id, task) + if err != nil { + return diag.FromErr(err) + } + return resourceReadTaskTemplateTaskTasks(Ctx, d, m) +} + +func resourceDeleteTaskTemplateTaskTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + task_id := d.Get("task_template_id").(string) + + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + + var diags diag.Diagnostics + err := apiclient.TaskTemplate.DeleteTaskTemplateTaskByID(teamID, task_id, id) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceReadTaskTemplateTaskTasks(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + task_id := d.Get("task_template_id").(string) + id := d.Id() + if emptyString(teamID) { + return diag.FromErr(errors.New("team_id is required")) + } + var diags diag.Diagnostics + tasktemplatetask, err := apiclient.TaskTemplate.GetTaskTemplateTaskByID(teamID, task_id, id) + if err != nil { + return diag.FromErr(err) + } + d.Set("title", tasktemplatetask.Title) + d.Set("description", tasktemplatetask.Description) + d.Set("creation_date", tasktemplatetask.CreationDate) + d.Set("team_id", teamID) + d.Set("role", tasktemplatetask.Role) + d.Set("task_template_id", tasktemplatetask.TaskTemplate) + d.Set("position", tasktemplatetask.Positon) + d.Set("due_in", tasktemplatetask.DueIn) + return diags +} + +func resourceTaskTemplateTaskTasksImporter(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 3 { + return nil, fmt.Errorf("unexpected format of id (%q), expected //", d.Id()) + } else if !IsValidUUID(parts[0]) { + return nil, fmt.Errorf("invalid team_id (%q)", parts[0]) + } else if !IsValidUUID(parts[1]) { + return nil, fmt.Errorf("invalid task_template_id (%q)", parts[1]) + } else if !IsValidUUID(parts[1]) { + return nil, fmt.Errorf("invalid task_id (%q)", parts[2]) + } + d.Set("team_id", parts[0]) + d.SetId(parts[1]) + return []*schema.ResourceData{d}, nil +} diff --git a/zenduty/resource_task_templates.go b/zenduty/resource_task_templates.go new file mode 100644 index 0000000..57af93d --- /dev/null +++ b/zenduty/resource_task_templates.go @@ -0,0 +1,166 @@ +package zenduty + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/Zenduty/zenduty-go-sdk/client" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceTaskTemplates() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceCreateTaskTemplates, + UpdateContext: resourceUpdateTaskTemplates, + DeleteContext: resourceDeleteTaskTemplates, + ReadContext: resourceReadTaskTemplates, + Importer: &schema.ResourceImporter{ + State: resourceTaskTemplatesImporter, + }, + Schema: map[string]*schema.Schema{ + "team_id": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: ValidateUUID(), + }, + "unique_id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "summary": { + Type: schema.TypeString, + Required: true, + }, + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func CreateTaskTemplate(Ctx context.Context, d *schema.ResourceData, m interface{}) (*client.TaskTemplateObj, diag.Diagnostics) { + + newtasktemplate := &client.TaskTemplateObj{} + + if v, ok := d.GetOk("team_id"); ok { + newtasktemplate.Team = v.(string) + + } + if v, ok := d.GetOk("summary"); ok { + newtasktemplate.Summary = v.(string) + + } + if v, ok := d.GetOk("name"); ok { + newtasktemplate.Name = v.(string) + + } + + return newtasktemplate, nil + +} + +func resourceCreateTaskTemplates(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + + task, createErr := CreateTaskTemplate(Ctx, d, m) + if createErr != nil { + return createErr + } + + var diags diag.Diagnostics + incidenttask, err := apiclient.TaskTemplate.CreateTaskTemplate(teamID, task) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(incidenttask.UniqueID) + + return diags +} + +func resourceUpdateTaskTemplates(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + task, createErr := CreateTaskTemplate(Ctx, d, m) + if createErr != nil { + return createErr + + } + + _, err := apiclient.TaskTemplate.UpdateTaskTemplateByID(teamID, id, task) + if err != nil { + return diag.FromErr(err) + } + return resourceReadTaskTemplates(Ctx, d, m) +} + +func resourceDeleteTaskTemplates(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if teamID == "" { + return diag.FromErr(errors.New("team_id is required")) + } + var diags diag.Diagnostics + err := apiclient.TaskTemplate.DeleteTaskTemplateByID(teamID, id) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceReadTaskTemplates(Ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + apiclient, _ := m.(*Config).Client() + + teamID := d.Get("team_id").(string) + id := d.Id() + if emptyString(teamID) { + return diag.FromErr(errors.New("team_id is required")) + } + var diags diag.Diagnostics + postincidenttask, err := apiclient.TaskTemplate.GetTaskTemplateByID(teamID, id) + if err != nil { + return diag.FromErr(err) + } + d.Set("name", postincidenttask.Name) + d.Set("summary", postincidenttask.Summary) + d.Set("creation_date", postincidenttask.CreationDate) + d.Set("team_id", teamID) + + return diags +} + +func resourceTaskTemplatesImporter(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return nil, fmt.Errorf("unexpected format of id (%q), expected /", d.Id()) + } else if !IsValidUUID(parts[0]) { + return nil, fmt.Errorf("invalid team_id (%q)", parts[0]) + } else if !IsValidUUID(parts[1]) { + return nil, fmt.Errorf("invalid task_template_id (%q)", parts[1]) + } + d.Set("team_id", parts[0]) + d.SetId(parts[1]) + return []*schema.ResourceData{d}, nil +}