Skip to content

Commit 56c786e

Browse files
committed
feat: integrations client
1 parent 2f12f00 commit 56c786e

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

client/client.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type endpoints struct {
4545
LandingZones *url.URL `json:"meshlandingzones"`
4646
Platforms *url.URL `json:"meshplatforms"`
4747
PaymentMethods *url.URL `json:"meshpaymentmethods"`
48+
Integrations *url.URL `json:"meshintegrations"`
4849
}
4950

5051
type loginRequest struct {
@@ -82,6 +83,7 @@ func NewClient(rootUrl *url.URL, apiKey string, apiSecret string) (*MeshStackPro
8283
LandingZones: rootUrl.JoinPath(apiMeshObjectsRoot, "meshlandingzones"),
8384
Platforms: rootUrl.JoinPath(apiMeshObjectsRoot, "meshplatforms"),
8485
PaymentMethods: rootUrl.JoinPath(apiMeshObjectsRoot, "meshpaymentmethods"),
86+
Integrations: rootUrl.JoinPath(apiMeshObjectsRoot, "meshintegrations"),
8587
}
8688

8789
return client, nil
@@ -111,7 +113,7 @@ func (c *MeshStackProviderClient) login() error {
111113
if err != nil {
112114
return err
113115
} else if res.StatusCode != 200 {
114-
return errors.New(fmt.Sprintf("Status %d: %s", res.StatusCode, ERROR_AUTHENTICATION_FAILURE))
116+
return fmt.Errorf("Status %d: %s", res.StatusCode, ERROR_AUTHENTICATION_FAILURE)
115117
}
116118

117119
defer res.Body.Close()

client/integrations.go

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package client
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"net/url"
9+
)
10+
11+
const CONTENT_TYPE_INTEGRATION = "application/vnd.meshcloud.api.meshintegration.v1-preview.hal+json"
12+
13+
type MeshIntegration struct {
14+
ApiVersion string `json:"apiVersion" tfsdk:"api_version"`
15+
Kind string `json:"kind" tfsdk:"kind"`
16+
Metadata MeshIntegrationMetadata `json:"metadata" tfsdk:"metadata"`
17+
Spec MeshIntegrationSpec `json:"spec" tfsdk:"spec"`
18+
Status *MeshIntegrationStatus `json:"status,omitempty" tfsdk:"status"`
19+
}
20+
21+
type MeshIntegrationMetadata struct {
22+
Uuid *string `json:"uuid,omitempty" tfsdk:"uuid"`
23+
OwnedByWorkspace string `json:"ownedByWorkspace" tfsdk:"owned_by_workspace"`
24+
CreatedOn *string `json:"createdOn,omitempty" tfsdk:"created_on"`
25+
}
26+
27+
type MeshIntegrationSpec struct {
28+
DisplayName string `json:"displayName" tfsdk:"display_name"`
29+
Config MeshIntegrationConfig `json:"config" tfsdk:"config"`
30+
}
31+
32+
type MeshIntegrationStatus struct {
33+
IsBuiltIn bool `json:"isBuiltIn" tfsdk:"is_built_in"`
34+
WorkloadIdentityFederation *MeshWorkloadIdentityFederation `json:"workloadIdentityFederation,omitempty" tfsdk:"workload_identity_federation"`
35+
}
36+
37+
// Integration Config wrapper with type discrimination
38+
type MeshIntegrationConfig struct {
39+
Type string `json:"type" tfsdk:"type"`
40+
Github *MeshGithubIntegrationProperties `json:"github,omitempty" tfsdk:"github"`
41+
Gitlab *MeshGitlabIntegrationProperties `json:"gitlab,omitempty" tfsdk:"gitlab"`
42+
AzureDevops *MeshAzureDevopsIntegrationProperties `json:"azuredevops,omitempty" tfsdk:"azuredevops"`
43+
}
44+
45+
// GitHub Integration
46+
type MeshGithubIntegrationProperties struct {
47+
Owner string `json:"owner" tfsdk:"owner"`
48+
BaseUrl string `json:"baseUrl" tfsdk:"base_url"`
49+
AppId string `json:"appId" tfsdk:"app_id"`
50+
AppPrivateKey string `json:"appPrivateKey" tfsdk:"app_private_key"`
51+
RunnerRef BuildingBlockRunnerRef `json:"runnerRef" tfsdk:"runner_ref"`
52+
}
53+
54+
// GitLab Integration
55+
type MeshGitlabIntegrationProperties struct {
56+
BaseUrl string `json:"baseUrl" tfsdk:"base_url"`
57+
RunnerRef BuildingBlockRunnerRef `json:"runnerRef" tfsdk:"runner_ref"`
58+
}
59+
60+
// Azure DevOps Integration
61+
type MeshAzureDevopsIntegrationProperties struct {
62+
BaseUrl string `json:"baseUrl" tfsdk:"base_url"`
63+
Organization string `json:"organization" tfsdk:"organization"`
64+
PersonalAccessToken string `json:"personalAccessToken" tfsdk:"personal_access_token"`
65+
RunnerRef BuildingBlockRunnerRef `json:"runnerRef" tfsdk:"runner_ref"`
66+
}
67+
68+
// Building Block Runner Reference
69+
type BuildingBlockRunnerRef struct {
70+
Uuid string `json:"uuid" tfsdk:"uuid"`
71+
Kind string `json:"kind,omitempty" tfsdk:"kind"`
72+
}
73+
74+
// Workload Identity Federation
75+
type MeshWorkloadIdentityFederation struct {
76+
Issuer string `json:"issuer" tfsdk:"issuer"`
77+
Subject string `json:"subject" tfsdk:"subject"`
78+
Gcp *MeshWifProvider `json:"gcp,omitempty" tfsdk:"gcp"`
79+
Aws *MeshAwsWifProvider `json:"aws,omitempty" tfsdk:"aws"`
80+
Azure *MeshWifProvider `json:"azure,omitempty" tfsdk:"azure"`
81+
}
82+
83+
type MeshWifProvider struct {
84+
Audience string `json:"audience" tfsdk:"audience"`
85+
}
86+
87+
type MeshAwsWifProvider struct {
88+
Audience string `json:"audience" tfsdk:"audience"`
89+
Thumbprint string `json:"thumbprint" tfsdk:"thumbprint"`
90+
}
91+
92+
func (c *MeshStackProviderClient) urlForIntegration(workspace string, uuid string) *url.URL {
93+
return c.endpoints.Integrations.JoinPath(workspace, uuid)
94+
}
95+
96+
func (c *MeshStackProviderClient) ReadIntegration(workspace string, uuid string) (*MeshIntegration, error) {
97+
targetUrl := c.urlForIntegration(workspace, uuid)
98+
req, err := http.NewRequest("GET", targetUrl.String(), nil)
99+
if err != nil {
100+
return nil, err
101+
}
102+
req.Header.Set("Accept", CONTENT_TYPE_INTEGRATION)
103+
104+
res, err := c.doAuthenticatedRequest(req)
105+
if err != nil {
106+
return nil, err
107+
}
108+
109+
defer res.Body.Close()
110+
111+
data, err := io.ReadAll(res.Body)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
if res.StatusCode == http.StatusNotFound {
117+
return nil, nil
118+
}
119+
120+
if !isSuccessHTTPStatus(res) {
121+
return nil, fmt.Errorf("unexpected status code: %d, %s", res.StatusCode, data)
122+
}
123+
124+
var integration MeshIntegration
125+
err = json.Unmarshal(data, &integration)
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
return &integration, nil
131+
}
132+
133+
func (c *MeshStackProviderClient) ReadIntegrations(workspaceIdentifier string) (*[]MeshIntegration, error) {
134+
var allIntegrations []MeshIntegration
135+
136+
pageNumber := 0
137+
targetUrl := c.endpoints.Integrations
138+
query := targetUrl.Query()
139+
query.Set("workspaceIdentifier", workspaceIdentifier)
140+
141+
for {
142+
query.Set("page", fmt.Sprintf("%d", pageNumber))
143+
targetUrl.RawQuery = query.Encode()
144+
145+
req, err := http.NewRequest("GET", targetUrl.String(), nil)
146+
if err != nil {
147+
return nil, err
148+
}
149+
150+
req.Header.Set("Accept", CONTENT_TYPE_INTEGRATION)
151+
152+
res, err := c.doAuthenticatedRequest(req)
153+
if err != nil {
154+
return nil, err
155+
}
156+
157+
defer res.Body.Close()
158+
159+
data, err := io.ReadAll(res.Body)
160+
if err != nil {
161+
return nil, fmt.Errorf("failed to read response body: %w", err)
162+
}
163+
164+
if !isSuccessHTTPStatus(res) {
165+
return nil, fmt.Errorf("unexpected status code: %d, %s", res.StatusCode, data)
166+
}
167+
168+
var response struct {
169+
Embedded struct {
170+
MeshIntegrations []MeshIntegration `json:"meshIntegrations"`
171+
} `json:"_embedded"`
172+
Page struct {
173+
Size int `json:"size"`
174+
TotalElements int `json:"totalElements"`
175+
TotalPages int `json:"totalPages"`
176+
Number int `json:"number"`
177+
} `json:"page"`
178+
}
179+
180+
err = json.Unmarshal(data, &response)
181+
if err != nil {
182+
return nil, err
183+
}
184+
185+
allIntegrations = append(allIntegrations, response.Embedded.MeshIntegrations...)
186+
187+
// Check if there are more pages
188+
if response.Page.Number >= response.Page.TotalPages-1 {
189+
break
190+
}
191+
192+
pageNumber++
193+
}
194+
195+
return &allIntegrations, nil
196+
}

0 commit comments

Comments
 (0)