Skip to content

Commit a12efe6

Browse files
authored
Merge pull request #43 from opengovern/feat_add_new_resource
Feat add new resource
2 parents 4e462d9 + cb953f5 commit a12efe6

File tree

15 files changed

+1090
-576
lines changed

15 files changed

+1090
-576
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ build/
22
og-describer-*
33
.idea
44
.env
5-
.vscode
5+
.vscode
6+
output.json

cloudql/github/plugin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
6060
"github_npm_package": tableGitHubNPMPackage(),
6161
"github_nuget_package": tableGitHubNugetPackage(),
6262
"github_artifact_dockerfile": tableGitHubArtifactDockerFile(),
63+
"github_repository_webhook": tableGithubRepositoryWebhook(),
6364
},
6465
}
6566
for key, table := range p.TableMap {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package github
2+
3+
import (
4+
opengovernance "github.com/opengovern/og-describer-github/discovery/pkg/es"
5+
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
6+
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
7+
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
8+
)
9+
10+
func tableGithubRepositoryWebhook() *plugin.Table {
11+
return &plugin.Table{
12+
Name: "github_repository_webhook",
13+
Description: "Webhooks configured in the system.",
14+
List: &plugin.ListConfig{
15+
Hydrate: opengovernance.ListWebhook,
16+
},
17+
Get: &plugin.GetConfig{
18+
KeyColumns: plugin.SingleColumn("id"),
19+
Hydrate: opengovernance.GetWebhook,
20+
},
21+
Columns: commonColumns([]*plugin.Column{
22+
{
23+
Name: "id",
24+
Type: proto.ColumnType_INT,
25+
Transform: transform.FromField("Description.ID"),
26+
Description: "The unique identifier of the webhook.",
27+
},
28+
{
29+
Name: "type",
30+
Type: proto.ColumnType_STRING,
31+
Transform: transform.FromField("Description.Type"),
32+
Description: "The type of the webhook.",
33+
},
34+
{
35+
Name: "name",
36+
Type: proto.ColumnType_STRING,
37+
Transform: transform.FromField("Description.Name"),
38+
Description: "The name of the webhook.",
39+
},
40+
{
41+
Name: "active",
42+
Type: proto.ColumnType_BOOL,
43+
Transform: transform.FromField("Description.Active"),
44+
Description: "Indicates whether the webhook is active.",
45+
},
46+
{
47+
Name: "events",
48+
Type: proto.ColumnType_JSON,
49+
Transform: transform.FromField("Description.Events"),
50+
Description: "List of events that trigger this webhook.",
51+
},
52+
{
53+
Name: "config",
54+
Type: proto.ColumnType_JSON,
55+
Transform: transform.FromField("Description.Config"),
56+
Description: "Configuration details for the webhook.",
57+
},
58+
{
59+
Name: "updated_at",
60+
Type: proto.ColumnType_TIMESTAMP,
61+
Transform: transform.FromField("Description.UpdatedAt"),
62+
Description: "The last update timestamp of the webhook.",
63+
},
64+
{
65+
Name: "created_at",
66+
Type: proto.ColumnType_TIMESTAMP,
67+
Transform: transform.FromField("Description.CreatedAt"),
68+
Description: "The creation timestamp of the webhook.",
69+
},
70+
{
71+
Name: "url",
72+
Type: proto.ColumnType_STRING,
73+
Transform: transform.FromField("Description.URL"),
74+
Description: "The primary URL of the webhook.",
75+
},
76+
{
77+
Name: "test_url",
78+
Type: proto.ColumnType_STRING,
79+
Transform: transform.FromField("Description.TestURL"),
80+
Description: "The test URL for the webhook.",
81+
},
82+
{
83+
Name: "ping_url",
84+
Type: proto.ColumnType_STRING,
85+
Transform: transform.FromField("Description.PingURL"),
86+
Description: "The ping URL for testing the webhook.",
87+
},
88+
{
89+
Name: "deliveries_url",
90+
Type: proto.ColumnType_STRING,
91+
Transform: transform.FromField("Description.DeliveriesURL"),
92+
Description: "The URL where deliveries for this webhook are logged.",
93+
},
94+
{
95+
Name: "last_response",
96+
Type: proto.ColumnType_JSON,
97+
Transform: transform.FromField("Description.LastResponse"),
98+
Description: "The last response received from the webhook endpoint.",
99+
},
100+
}),
101+
}
102+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package describers
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/opengovern/og-describer-github/discovery/pkg/models"
8+
model "github.com/opengovern/og-describer-github/discovery/provider"
9+
resilientbridge "github.com/opengovern/resilient-bridge"
10+
"github.com/opengovern/resilient-bridge/adapters"
11+
"net/url"
12+
"strconv"
13+
"time"
14+
)
15+
16+
func ListRepositoryWebhooks(ctx context.Context, githubClient model.GitHubClient, organizationName string, stream *models.StreamSender) ([]models.Resource, error) {
17+
handler := resilientbridge.NewResilientBridge()
18+
handler.SetDebug(false)
19+
handler.RegisterProvider("github", adapters.NewGitHubAdapter(githubClient.Token), &resilientbridge.ProviderConfig{
20+
UseProviderLimits: true,
21+
MaxRetries: 3,
22+
BaseBackoff: time.Second,
23+
})
24+
25+
repositories, err := getRepositories(ctx, githubClient.RestClient, organizationName)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
var values []models.Resource
31+
for _, repo := range repositories {
32+
webhooks, err := processRepositoryWebhooks(ctx, handler, organizationName, repo.GetName())
33+
if err != nil {
34+
return nil, err
35+
}
36+
for _, webhook := range webhooks {
37+
config := model.WebhookConfig{
38+
URL: webhook.Config.URL,
39+
ContentType: webhook.Config.ContentType,
40+
Secret: webhook.Config.Secret,
41+
InsecureSSL: webhook.Config.InsecureSSL,
42+
}
43+
lastResponse := model.HookResponse{
44+
Code: webhook.LastResponse.Code,
45+
Status: webhook.LastResponse.Status,
46+
Message: webhook.LastResponse.Message,
47+
}
48+
value := models.Resource{
49+
ID: strconv.Itoa(int(webhook.ID)),
50+
Name: webhook.Name,
51+
Description: model.WebhookDescription{
52+
Type: webhook.Type,
53+
ID: webhook.ID,
54+
Name: webhook.Name,
55+
Active: webhook.Active,
56+
Events: webhook.Events,
57+
Config: config,
58+
UpdatedAt: webhook.UpdatedAt,
59+
CreatedAt: webhook.CreatedAt,
60+
URL: webhook.URL,
61+
TestURL: webhook.TestURL,
62+
PingURL: webhook.PingURL,
63+
DeliveriesURL: webhook.DeliveriesURL,
64+
LastResponse: lastResponse,
65+
},
66+
}
67+
if stream != nil {
68+
if err := (*stream)(value); err != nil {
69+
return nil, err
70+
}
71+
} else {
72+
values = append(values, value)
73+
}
74+
}
75+
}
76+
77+
return values, nil
78+
}
79+
80+
func GetRepositoryWebhook(ctx context.Context, githubClient model.GitHubClient, organizationName string, repositoryName string, resourceID string, stream *models.StreamSender) (*models.Resource, error) {
81+
handler := resilientbridge.NewResilientBridge()
82+
handler.SetDebug(false)
83+
handler.RegisterProvider("github", adapters.NewGitHubAdapter(githubClient.Token), &resilientbridge.ProviderConfig{
84+
UseProviderLimits: true,
85+
MaxRetries: 3,
86+
BaseBackoff: time.Second,
87+
})
88+
89+
webhook, err := processRepositoryWebhook(ctx, handler, organizationName, repositoryName, resourceID)
90+
if err != nil {
91+
return nil, err
92+
}
93+
config := model.WebhookConfig{
94+
URL: webhook.Config.URL,
95+
ContentType: webhook.Config.ContentType,
96+
Secret: webhook.Config.Secret,
97+
InsecureSSL: webhook.Config.InsecureSSL,
98+
}
99+
lastResponse := model.HookResponse{
100+
Code: webhook.LastResponse.Code,
101+
Status: webhook.LastResponse.Status,
102+
Message: webhook.LastResponse.Message,
103+
}
104+
value := models.Resource{
105+
ID: strconv.Itoa(int(webhook.ID)),
106+
Name: webhook.Name,
107+
Description: model.WebhookDescription{
108+
Type: webhook.Type,
109+
ID: webhook.ID,
110+
Name: webhook.Name,
111+
Active: webhook.Active,
112+
Events: webhook.Events,
113+
Config: config,
114+
UpdatedAt: webhook.UpdatedAt,
115+
CreatedAt: webhook.CreatedAt,
116+
URL: webhook.URL,
117+
TestURL: webhook.TestURL,
118+
PingURL: webhook.PingURL,
119+
DeliveriesURL: webhook.DeliveriesURL,
120+
LastResponse: lastResponse,
121+
},
122+
}
123+
124+
return &value, nil
125+
}
126+
127+
func processRepositoryWebhooks(ctx context.Context, handler *resilientbridge.ResilientBridge, organization, repo string) ([]model.WebhookJSON, error) {
128+
var webhooks []model.WebhookJSON
129+
var responseWebhooks []model.WebhookJSON
130+
baseURL := "/repos/"
131+
page := 1
132+
133+
for {
134+
params := url.Values{}
135+
params.Set("per_page", "100")
136+
params.Set("page", strconv.Itoa(page))
137+
finalURL := fmt.Sprintf("%s%s/%s/hooks?%s", baseURL, organization, repo, params.Encode())
138+
139+
req := &resilientbridge.NormalizedRequest{
140+
Method: "GET",
141+
Endpoint: finalURL,
142+
Headers: map[string]string{"accept": "application/vnd.github+json"},
143+
}
144+
145+
resp, err := handler.Request("github", req)
146+
if err != nil {
147+
return nil, fmt.Errorf("request execution failed: %w", err)
148+
}
149+
150+
if resp.StatusCode >= 400 {
151+
return nil, fmt.Errorf("error %d: %s", resp.StatusCode, string(resp.Data))
152+
}
153+
154+
if err = json.Unmarshal(resp.Data, &responseWebhooks); err != nil {
155+
return nil, fmt.Errorf("error parsing response: %w", err)
156+
}
157+
158+
webhooks = append(webhooks, responseWebhooks...)
159+
if len(responseWebhooks) < 100 {
160+
break
161+
}
162+
page++
163+
}
164+
165+
return webhooks, nil
166+
}
167+
168+
func processRepositoryWebhook(ctx context.Context, handler *resilientbridge.ResilientBridge, organization, repo, resourceID string) (*model.WebhookJSON, error) {
169+
var webhook model.WebhookJSON
170+
baseURL := "/repos/"
171+
172+
finalURL := fmt.Sprintf("%s%s/%s/hooks/%s", baseURL, organization, repo, resourceID)
173+
174+
req := &resilientbridge.NormalizedRequest{
175+
Method: "GET",
176+
Endpoint: finalURL,
177+
Headers: map[string]string{"accept": "application/vnd.github+json"},
178+
}
179+
180+
resp, err := handler.Request("github", req)
181+
if err != nil {
182+
return nil, fmt.Errorf("request execution failed: %w", err)
183+
}
184+
185+
if resp.StatusCode >= 400 {
186+
return nil, fmt.Errorf("error %d: %s", resp.StatusCode, string(resp.Data))
187+
}
188+
189+
if err = json.Unmarshal(resp.Data, &webhook); err != nil {
190+
return nil, fmt.Errorf("error parsing response: %w", err)
191+
}
192+
193+
return &webhook, nil
194+
}

discovery/describers/utils.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ func getRepositories(ctx context.Context, client *github.Client, owner string) (
13571357
for {
13581358
repos, resp, err := client.Repositories.ListByOrg(ctx, owner, opt)
13591359
if err != nil {
1360+
fmt.Printf("failed: %v\n", err)
13601361
return nil, err
13611362
}
13621363

discovery/local/cmd/describer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import (
1212
"time"
1313

1414
"github.com/google/uuid"
15-
"github.com/opengovern/og-describer-github/discovery/pkg/orchestrator"
1615
model "github.com/opengovern/og-describer-github/discovery/pkg/models"
16+
"github.com/opengovern/og-describer-github/discovery/pkg/orchestrator"
1717
"github.com/opengovern/og-describer-github/discovery/provider"
1818
"github.com/opengovern/og-util/pkg/describe"
1919
"github.com/opengovern/og-util/pkg/es"

discovery/local/cmd/getDescriber.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"encoding/json"
55
"fmt"
66
"github.com/google/uuid"
7-
"github.com/opengovern/og-describer-github/discovery/pkg/orchestrator"
87
model "github.com/opengovern/og-describer-github/discovery/pkg/models"
8+
"github.com/opengovern/og-describer-github/discovery/pkg/orchestrator"
99
"github.com/opengovern/og-describer-github/discovery/provider"
1010
"github.com/opengovern/og-describer-github/global"
1111
"github.com/opengovern/og-util/pkg/describe"

0 commit comments

Comments
 (0)