Skip to content

Commit 40b1dce

Browse files
committed
Merged in HEIMDALL-11524-add-api-based-integrations-resource-support (pull request #40)
HEIMDALL-11524 add api based integrations resource support * Implement API Based Integration Resource Support * Added tests for Api Integration Resource. Added example configuration for Api Integration Resource. Updated variable name in Api Integration Resource * Reused existing model and DTO definitions which are common between email and api resources Approved-by: Bekir Kağan Yıldız
1 parent 31f3e66 commit 40b1dce

File tree

12 files changed

+901
-38
lines changed

12 files changed

+901
-38
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
terraform {
2+
required_providers {
3+
jsm-ops = {
4+
source = "registry.terraform.io/atlassian/jsm-ops"
5+
}
6+
}
7+
}
8+
9+
resource "jsm-ops_api_integration" "example" {
10+
name = "api integration"
11+
enabled = true
12+
type = "AmazonSecurityHub"
13+
team_id = "81577279-b6ed-4dae-8bf4-e3119fbdf046"
14+
type_specific_properties = jsonencode({
15+
suppressNotifications: false
16+
securityHubIamRoleArn: "arn:aws:iam::416306766477:role/jsmSecurityHubRole"
17+
region: "AP_SOUTH_1"
18+
})
19+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/hashicorp/go-retryablehttp v0.7.7
88
github.com/hashicorp/terraform-plugin-framework v1.13.0
99
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0
10+
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0
1011
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0
1112
github.com/hashicorp/terraform-plugin-go v0.25.0
1213
github.com/hashicorp/terraform-plugin-log v0.9.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7
7878
github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A=
7979
github.com/hashicorp/terraform-plugin-framework v1.13.0 h1:8OTG4+oZUfKgnfTdPTJwZ532Bh2BobF4H+yBiYJ/scw=
8080
github.com/hashicorp/terraform-plugin-framework v1.13.0/go.mod h1:j64rwMGpgM3NYXTKuxrCnyubQb/4VKldEKlcG8cvmjU=
81+
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0 h1:SJXL5FfJJm17554Kpt9jFXngdM6fXbnUnZ6iT2IeiYA=
82+
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0/go.mod h1:p0phD0IYhsu9bR4+6OetVvvH59I6LwjXGnTVEr8ox6E=
8183
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0 h1:v3DapR8gsp3EM8fKMh6up9cJUFQ2iRaFsYLP8UJnCco=
8284
github.com/hashicorp/terraform-plugin-framework-timetypes v0.5.0/go.mod h1:c3PnGE9pHBDfdEVG9t1S1C9ia5LW+gkFR0CygXlM8ak=
8385
github.com/hashicorp/terraform-plugin-framework-validators v0.15.0 h1:RXMmu7JgpFjnI1a5QjMCBb11usrW2OtAG+iOTIj5c9Y=

internal/dto/api_integration.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dto
2+
3+
type (
4+
ApiIntegration struct {
5+
Id string `json:"id"`
6+
Name string `json:"name"`
7+
Type string `json:"type"`
8+
Enabled bool `json:"enabled"`
9+
TeamId string `json:"teamId"`
10+
Advanced bool `json:"advanced,omitempty"`
11+
MaintenanceSources []MaintenanceSource `json:"maintenanceSources,omitempty"`
12+
Directions []string `json:"directions,omitempty"`
13+
Domains []string `json:"domains,omitempty"`
14+
TypeSpecificProperties map[string]interface{} `json:"typeSpecificProperties"`
15+
}
16+
)
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package provider
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"github.com/atlassian/terraform-provider-jsm-ops/internal/dto"
10+
"github.com/atlassian/terraform-provider-jsm-ops/internal/httpClient"
11+
"github.com/atlassian/terraform-provider-jsm-ops/internal/provider/dataModels"
12+
"github.com/atlassian/terraform-provider-jsm-ops/internal/provider/schemaAttributes"
13+
"github.com/hashicorp/terraform-plugin-framework/path"
14+
"github.com/hashicorp/terraform-plugin-framework/resource"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
16+
"github.com/hashicorp/terraform-plugin-log/tflog"
17+
)
18+
19+
// Ensure provider defined types fully satisfy framework interfaces.
20+
var _ resource.Resource = &ApiIntegrationResource{}
21+
var _ resource.ResourceWithImportState = &ApiIntegrationResource{}
22+
23+
func NewApiIntegrationResource() resource.Resource {
24+
return &ApiIntegrationResource{}
25+
}
26+
27+
// ApiIntegrationResource defines the resource implementation.
28+
type ApiIntegrationResource struct {
29+
client *httpClient.HttpClient
30+
}
31+
32+
func (r *ApiIntegrationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
33+
resp.TypeName = req.ProviderTypeName + "_api_integration"
34+
}
35+
36+
func (r *ApiIntegrationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
37+
resp.Schema = schema.Schema{
38+
Attributes: schemaAttributes.ApiIntegrationResourceAttributes,
39+
}
40+
}
41+
42+
func (r *ApiIntegrationResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
43+
tflog.Trace(ctx, "Configuring ApiIntegrationResource")
44+
45+
// Prevent panic if the provider has not been configured.
46+
if req.ProviderData == nil {
47+
return
48+
}
49+
50+
client, ok := req.ProviderData.(*JsmOpsClient)
51+
52+
if !ok {
53+
tflog.Error(ctx, "Unexpected Resource Configure Type")
54+
resp.Diagnostics.AddError(
55+
"Unexpected Resource Configure Type",
56+
fmt.Sprintf("Expected *JsmOpsClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
57+
)
58+
return
59+
}
60+
61+
r.client = client.OpsClient
62+
63+
tflog.Trace(ctx, "Configured ApiIntegrationResource")
64+
}
65+
66+
func (r *ApiIntegrationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
67+
tflog.Trace(ctx, "Creating the ApiIntegrationResource")
68+
69+
var data dataModels.ApiIntegrationModel
70+
71+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
72+
73+
dtoObj := ApiIntegrationModelToDto(ctx, data)
74+
75+
errorMap := httpClient.NewOpsClientErrorMap()
76+
httpResp, err := r.client.NewRequest().
77+
JoinBaseUrl("v1/integrations").
78+
Method(httpClient.POST).
79+
SetBody(dtoObj).
80+
SetBodyParseObject(&dtoObj).
81+
SetErrorParseMap(&errorMap).
82+
Send()
83+
84+
if httpResp == nil {
85+
tflog.Error(ctx, "Client Error. Unable to create api integration, got nil response")
86+
resp.Diagnostics.AddError("Client Error", "Unable to create api integration, got nil response")
87+
} else if httpResp.IsError() {
88+
statusCode := httpResp.GetStatusCode()
89+
errorResponse := errorMap[statusCode]
90+
if errorResponse != nil {
91+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to create api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
92+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
93+
} else {
94+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to create api integration, got http response: %d", statusCode))
95+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create api integration, got http response: %d", statusCode))
96+
}
97+
}
98+
if err != nil {
99+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to create api integration, got error: %s", err))
100+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create api integration, got error: %s", err))
101+
}
102+
103+
if resp.Diagnostics.HasError() {
104+
return
105+
}
106+
107+
data = ApiIntegrationDtoToModel(dtoObj)
108+
109+
tflog.Trace(ctx, "Created the ApiIntegrationResource")
110+
111+
// Save data into Terraform state
112+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
113+
tflog.Trace(ctx, "Saved the ApiIntegrationResource into Terraform state")
114+
}
115+
116+
func (r *ApiIntegrationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
117+
var data dataModels.ApiIntegrationModel
118+
119+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
120+
121+
tflog.Trace(ctx, "Reading the ApiIntegrationResource")
122+
123+
ApiIntegration := dto.ApiIntegration{}
124+
errorMap := httpClient.NewOpsClientErrorMap()
125+
126+
httpResp, err := r.client.NewRequest().
127+
JoinBaseUrl(fmt.Sprintf("v1/integrations/%s", data.Id.ValueString())).
128+
Method(httpClient.GET).
129+
SetBodyParseObject(&ApiIntegration).
130+
SetErrorParseMap(&errorMap).
131+
Send()
132+
133+
if httpResp == nil {
134+
tflog.Error(ctx, "Client Error. Unable to read api integration, got nil response")
135+
resp.Diagnostics.AddError("Client Error", "Unable to read api integration, got nil response")
136+
} else if httpResp.IsError() {
137+
statusCode := httpResp.GetStatusCode()
138+
errorResponse := errorMap[statusCode]
139+
if errorResponse != nil {
140+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to read api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
141+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
142+
} else {
143+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to read api integration, got http response: %d", statusCode))
144+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read api integration, got http response: %d", statusCode))
145+
}
146+
}
147+
if err != nil {
148+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to read api integration, got error: %s", err))
149+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read api integration or to parse received data, got error: %s", err))
150+
}
151+
152+
if resp.Diagnostics.HasError() {
153+
return
154+
}
155+
156+
data = ApiIntegrationDtoToModel(ApiIntegration)
157+
158+
tflog.Trace(ctx, "Read the ApiIntegrationResource")
159+
160+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
161+
tflog.Trace(ctx, "Saved the ApiIntegrationResource into Terraform state")
162+
}
163+
164+
func (r *ApiIntegrationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
165+
var data dataModels.ApiIntegrationModel
166+
167+
// Read Terraform plan data into the model
168+
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
169+
170+
tflog.Trace(ctx, "Updating the ApiIntegrationResource")
171+
172+
dtoObj := ApiIntegrationModelToDto(ctx, data)
173+
errorMap := httpClient.NewOpsClientErrorMap()
174+
175+
httpResp, err := r.client.NewRequest().
176+
JoinBaseUrl(fmt.Sprintf("v1/integrations/%s", data.Id.ValueString())).
177+
Method(httpClient.PATCH).
178+
SetBody(dtoObj).
179+
SetBodyParseObject(&dtoObj).
180+
SetErrorParseMap(&errorMap).
181+
Send()
182+
183+
if httpResp == nil {
184+
tflog.Error(ctx, "Client Error. Unable to update api integration, got nil response")
185+
resp.Diagnostics.AddError("Client Error", "Unable to update api integration, got nil response")
186+
} else if httpResp.IsError() {
187+
statusCode := httpResp.GetStatusCode()
188+
errorResponse := errorMap[statusCode]
189+
if errorResponse != nil {
190+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to update api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
191+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
192+
} else {
193+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to update api integration, got http response: %d", statusCode))
194+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update api integration, got http response: %d", statusCode))
195+
}
196+
}
197+
if err != nil {
198+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to update api integration, got error: %s", err))
199+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update api integration, got error: %s", err))
200+
}
201+
202+
if resp.Diagnostics.HasError() {
203+
return
204+
}
205+
206+
data = ApiIntegrationDtoToModel(dtoObj)
207+
208+
tflog.Trace(ctx, "Updated the ApiIntegrationResource")
209+
210+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
211+
tflog.Trace(ctx, "Saved the ApiIntegrationResource into Terraform state")
212+
}
213+
214+
func (r *ApiIntegrationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
215+
var data dataModels.ApiIntegrationModel
216+
217+
// Read Terraform prior state data into the model
218+
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
219+
220+
tflog.Trace(ctx, "Deleting the ApiIntegrationResource")
221+
errorMap := httpClient.NewOpsClientErrorMap()
222+
223+
httpResp, err := r.client.NewRequest().
224+
JoinBaseUrl(fmt.Sprintf("v1/integrations/%s", data.Id.ValueString())).
225+
Method(httpClient.DELETE).
226+
SetErrorParseMap(&errorMap).
227+
Send()
228+
229+
if httpResp == nil {
230+
tflog.Error(ctx, "Client Error. Unable to delete api integration, got nil response")
231+
resp.Diagnostics.AddError("Client Error", "Unable to delete api integration, got nil response")
232+
} else if httpResp.IsError() {
233+
statusCode := httpResp.GetStatusCode()
234+
errorResponse := errorMap[statusCode]
235+
if errorResponse != nil {
236+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to delete api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
237+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete api integration, status code: %d. Got response: %s", statusCode, errorResponse.Error()))
238+
} else {
239+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to delete api integration, got http response: %d", statusCode))
240+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete api integration, got http response: %d", statusCode))
241+
}
242+
} else if err != nil {
243+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to delete api integration, got http response: %d", httpResp.GetStatusCode()))
244+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete api integration, got error: %s", err))
245+
}
246+
247+
if resp.Diagnostics.HasError() {
248+
return
249+
}
250+
251+
tflog.Trace(ctx, "Deleted the ApiIntegrationResource")
252+
}
253+
254+
func (r *ApiIntegrationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
255+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
256+
}

0 commit comments

Comments
 (0)