Skip to content

Commit

Permalink
Merge pull request #1916 from vntw/access-token-webhook-event
Browse files Browse the repository at this point in the history
Add new project and group access token events to webhook event types
  • Loading branch information
svanharmelen committed Apr 18, 2024
2 parents 5cf9a2e + c187a2d commit a29d319
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 23 deletions.
55 changes: 37 additions & 18 deletions event_parsing.go
Expand Up @@ -27,24 +27,25 @@ type EventType string

// List of available event types.
const (
EventConfidentialIssue EventType = "Confidential Issue Hook"
EventConfidentialNote EventType = "Confidential Note Hook"
EventTypeBuild EventType = "Build Hook"
EventTypeDeployment EventType = "Deployment Hook"
EventTypeFeatureFlag EventType = "Feature Flag Hook"
EventTypeIssue EventType = "Issue Hook"
EventTypeJob EventType = "Job Hook"
EventTypeMember EventType = "Member Hook"
EventTypeMergeRequest EventType = "Merge Request Hook"
EventTypeNote EventType = "Note Hook"
EventTypePipeline EventType = "Pipeline Hook"
EventTypePush EventType = "Push Hook"
EventTypeRelease EventType = "Release Hook"
EventTypeServiceHook EventType = "Service Hook"
EventTypeSubGroup EventType = "Subgroup Hook"
EventTypeSystemHook EventType = "System Hook"
EventTypeTagPush EventType = "Tag Push Hook"
EventTypeWikiPage EventType = "Wiki Page Hook"
EventConfidentialIssue EventType = "Confidential Issue Hook"
EventConfidentialNote EventType = "Confidential Note Hook"
EventTypeBuild EventType = "Build Hook"
EventTypeDeployment EventType = "Deployment Hook"
EventTypeFeatureFlag EventType = "Feature Flag Hook"
EventTypeIssue EventType = "Issue Hook"
EventTypeJob EventType = "Job Hook"
EventTypeMember EventType = "Member Hook"
EventTypeMergeRequest EventType = "Merge Request Hook"
EventTypeNote EventType = "Note Hook"
EventTypePipeline EventType = "Pipeline Hook"
EventTypePush EventType = "Push Hook"
EventTypeRelease EventType = "Release Hook"
EventTypeResourceAccessToken EventType = "Resource Access Token Hook"
EventTypeServiceHook EventType = "Service Hook"
EventTypeSubGroup EventType = "Subgroup Hook"
EventTypeSystemHook EventType = "System Hook"
EventTypeTagPush EventType = "Tag Push Hook"
EventTypeWikiPage EventType = "Wiki Page Hook"
)

const (
Expand Down Expand Up @@ -252,6 +253,24 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e
event = &PushEvent{}
case EventTypeRelease:
event = &ReleaseEvent{}
case EventTypeResourceAccessToken:
data := map[string]interface{}{}
err := json.Unmarshal(payload, &data)
if err != nil {
return nil, err
}

_, groupEvent := data["group"]
_, projectEvent := data["project"]

switch {
case groupEvent:
event = &GroupResourceAccessTokenEvent{}
case projectEvent:
event = &ProjectResourceAccessTokenEvent{}
default:
return nil, fmt.Errorf("unexpected resource access token payload")
}
case EventTypeServiceHook:
service := &serviceEvent{}
err := json.Unmarshal(payload, service)
Expand Down
38 changes: 37 additions & 1 deletion event_parsing_webhook_test.go
Expand Up @@ -101,7 +101,7 @@ func TestParseCommitCommentHook(t *testing.T) {
}
}

func TestParseFeatureFLagHook(t *testing.T) {
func TestParseFeatureFlagHook(t *testing.T) {
raw := loadFixture("testdata/webhooks/feature_flag.json")

parsedEvent, err := ParseWebhook("Feature Flag Hook", raw)
Expand Down Expand Up @@ -147,6 +147,24 @@ func TestParseFeatureFLagHook(t *testing.T) {
}
}

func TestParseGroupResourceAccessTokenHook(t *testing.T) {
raw := loadFixture("testdata/webhooks/resource_access_token_group.json")

parsedEvent, err := ParseWebhook("Resource Access Token Hook", raw)
if err != nil {
t.Errorf("Error parsing group resource access token hook: %s", err)
}

event, ok := parsedEvent.(*GroupResourceAccessTokenEvent)
if !ok {
t.Errorf("Expected GroupResourceAccessTokenEvent, but parsing produced %T", parsedEvent)
}

if event.GroupID != 35 {
t.Errorf("GroupID is %v, want %v", event.GroupID, 35)
}
}

func TestParseHookWebHook(t *testing.T) {
parsedEvent1, err := ParseHook("Merge Request Hook", loadFixture("testdata/webhooks/merge_request.json"))
if err != nil {
Expand Down Expand Up @@ -351,6 +369,24 @@ func TestParsePipelineHook(t *testing.T) {
}
}

func TestParseProjectResourceAccessTokenHook(t *testing.T) {
raw := loadFixture("testdata/webhooks/resource_access_token_project.json")

parsedEvent, err := ParseWebhook("Resource Access Token Hook", raw)
if err != nil {
t.Errorf("Error parsing project resource access token hook: %s", err)
}

event, ok := parsedEvent.(*ProjectResourceAccessTokenEvent)
if !ok {
t.Errorf("Expected ProjectResourceAccessTokenEvent, but parsing produced %T", parsedEvent)
}

if event.ProjectID != 7 {
t.Errorf("ProjectID is %v, want %v", event.ProjectID, 7)
}
}

func TestParsePushHook(t *testing.T) {
raw := loadFixture("testdata/webhooks/push.json")

Expand Down
70 changes: 66 additions & 4 deletions event_webhook_types.go
Expand Up @@ -131,7 +131,7 @@ type CommitCommentEvent struct {
} `json:"commit"`
}

// DeploymentEvent represents a deployment event
// DeploymentEvent represents a deployment event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#deployment-events
Expand Down Expand Up @@ -171,7 +171,7 @@ type DeploymentEvent struct {
CommitTitle string `json:"commit_title"`
}

// FeatureFlagEvent represents a feature flag event
// FeatureFlagEvent represents a feature flag event.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#feature-flag-events
Expand Down Expand Up @@ -205,6 +205,30 @@ type FeatureFlagEvent struct {
} `json:"object_attributes"`
}

// GroupResourceAccessTokenEvent represents a resource access token event for a
// group.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#project-and-group-access-token-events
type GroupResourceAccessTokenEvent struct {
EventName string `json:"event_name"`
ObjectKind string `json:"object_kind"`
GroupID int `json:"group_id"`
Group struct {
GroupID int `json:"group_id"`
GroupName string `json:"group_name"`
GroupPath string `json:"group_path"`
FullPath string `json:"full_path"`
} `json:"group"`
ObjectAttributes struct {
ID int `json:"id"`
UserID int `json:"user_id"`
Name string `json:"name"`
CreatedAt *time.Time `json:"created_at"`
ExpiresAt *ISOTime `json:"expires_at"`
} `json:"object_attributes"`
}

// IssueCommentEvent represents a comment on an issue event.
//
// GitLab API docs:
Expand Down Expand Up @@ -737,7 +761,8 @@ type MergeEvent struct {
Reviewers []*EventUser `json:"reviewers"`
}

// EventUser represents a user record in an event and is used as an even initiator or a merge assignee.
// EventUser represents a user record in an event and is used as an even
// initiator or a merge assignee.
type EventUser struct {
ID int `json:"id"`
Name string `json:"name"`
Expand All @@ -753,7 +778,8 @@ type MergeParams struct {

// UnmarshalJSON decodes the merge parameters
//
// This allows support of ForceRemoveSourceBranch for both type bool (>11.9) and string (<11.9)
// This allows support of ForceRemoveSourceBranch for both type
// bool (>11.9) and string (<11.9)
func (p *MergeParams) UnmarshalJSON(b []byte) error {
type Alias MergeParams
raw := struct {
Expand Down Expand Up @@ -899,6 +925,42 @@ type PipelineEvent struct {
} `json:"builds"`
}

// ProjectResourceAccessTokenEvent represents a resource access token event for
// a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#project-and-group-access-token-events
type ProjectResourceAccessTokenEvent struct {
EventName string `json:"event_name"`
ObjectKind string `json:"object_kind"`
ProjectID int `json:"project_id"`
Project struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
WebURL string `json:"web_url"`
AvatarURL string `json:"avatar_url"`
GitSSHURL string `json:"git_ssh_url"`
GitHTTPURL string `json:"git_http_url"`
Namespace string `json:"namespace"`
VisibilityLevel int `json:"visibility_level"`
PathWithNamespace string `json:"path_with_namespace"`
DefaultBranch string `json:"default_branch"`
CIConfigPath string `json:"ci_config_path"`
Homepage string `json:"homepage"`
URL string `json:"url"`
SSHURL string `json:"ssh_url"`
HTTPURL string `json:"http_url"`
} `json:"project"`
ObjectAttributes struct {
ID int `json:"id"`
UserID int `json:"user_id"`
Name string `json:"name"`
CreatedAt *time.Time `json:"created_at"`
ExpiresAt *ISOTime `json:"expires_at"`
} `json:"object_attributes"`
}

// PushEvent represents a push event.
//
// GitLab API docs:
Expand Down
96 changes: 96 additions & 0 deletions event_webhook_types_test.go
Expand Up @@ -286,6 +286,48 @@ func TestFeatureFlagEventUnmarshal(t *testing.T) {
}
}

func TestGroupResourceAccessTokenEventUnmarshal(t *testing.T) {
jsonObject := loadFixture("testdata/webhooks/resource_access_token_group.json")
var event *GroupResourceAccessTokenEvent
err := json.Unmarshal(jsonObject, &event)
if err != nil {
t.Errorf("could not unmarshal event: %v\n ", err.Error())
}

if event == nil {
t.Errorf("event is null")
}

createdAt, err := time.Parse(time.RFC3339, "2024-02-05T03:13:44.855Z")
if err != nil {
t.Fatalf("could not parse time: %v", err)
}

expiresAt, err := ParseISOTime("2024-01-26")
if err != nil {
t.Fatalf("could not parse ISO time: %v", err)
}

expected := &GroupResourceAccessTokenEvent{
GroupID: 35,
ObjectKind: "access_token",
EventName: "expiring_access_token",
}

expected.Group.GroupID = 35
expected.Group.GroupName = "Twitter"
expected.Group.GroupPath = "twitter"
expected.Group.FullPath = "twitter"

expected.ObjectAttributes.ID = 25
expected.ObjectAttributes.UserID = 90
expected.ObjectAttributes.Name = "acd"
expected.ObjectAttributes.CreatedAt = &createdAt
expected.ObjectAttributes.ExpiresAt = &expiresAt

assert.Equal(t, expected, event)
}

func TestIssueCommentEventUnmarshal(t *testing.T) {
jsonObject := loadFixture("testdata/webhooks/note_issue.json")

Expand Down Expand Up @@ -1014,6 +1056,60 @@ func TestPipelineEventUnmarshal(t *testing.T) {
}
}

func TestProjectResourceAccessTokenEventUnmarshal(t *testing.T) {
jsonObject := loadFixture("testdata/webhooks/resource_access_token_project.json")
var event *ProjectResourceAccessTokenEvent
err := json.Unmarshal(jsonObject, &event)
if err != nil {
t.Errorf("could not unmarshal event: %v\n ", err.Error())
}

if event == nil {
t.Errorf("event is null")
}

createdAt, err := time.Parse(time.RFC3339, "2024-02-05T03:13:44.855Z")
if err != nil {
t.Fatalf("could not parse time: %v", err)
}

expiresAt, err := ParseISOTime("2024-01-26")
if err != nil {
t.Fatalf("could not parse ISO time: %v", err)
}

expected := &ProjectResourceAccessTokenEvent{
ProjectID: 7,
ObjectKind: "access_token",
EventName: "expiring_access_token",
}

expected.ObjectAttributes.ID = 25
expected.ObjectAttributes.UserID = 90
expected.ObjectAttributes.Name = "acd"
expected.ObjectAttributes.CreatedAt = &createdAt
expected.ObjectAttributes.ExpiresAt = &expiresAt

expected.Project.ID = 7
expected.Project.Name = "Flight"
expected.Project.Description = "Eum dolore maxime atque reprehenderit voluptatem."
expected.Project.WebURL = "https://example.com/flightjs/Flight"
expected.Project.AvatarURL = ""
expected.Project.GitSSHURL = "ssh://git@example.com/flightjs/Flight.git"
expected.Project.GitHTTPURL = "https://example.com/flightjs/Flight.git"
expected.Project.Namespace = "Flightjs"
expected.Project.VisibilityLevel = 0
expected.Project.PathWithNamespace = "flightjs/Flight"
expected.Project.DefaultBranch = "master"
expected.Project.CIConfigPath = ""
expected.Project.Homepage = "https://example.com/flightjs/Flight"
expected.Project.URL = "ssh://git@example.com/flightjs/Flight.git"
expected.Project.SSHURL = "ssh://git@example.com/flightjs/Flight.git"
expected.Project.HTTPURL = "https://example.com/flightjs/Flight.git"

assert.Equal(t, expected, event)
}

func TestPushEventUnmarshal(t *testing.T) {
jsonObject := loadFixture("testdata/webhooks/push.json")
var event *PushEvent
Expand Down
18 changes: 18 additions & 0 deletions testdata/webhooks/resource_access_token_group.json
@@ -0,0 +1,18 @@
{
"object_kind": "access_token",
"group_id": 35,
"group": {
"group_name": "Twitter",
"group_path": "twitter",
"full_path": "twitter",
"group_id": 35
},
"object_attributes": {
"user_id": 90,
"created_at": "2024-02-05T03:13:44.855Z",
"id": 25,
"name": "acd",
"expires_at": "2024-01-26"
},
"event_name": "expiring_access_token"
}
30 changes: 30 additions & 0 deletions testdata/webhooks/resource_access_token_project.json
@@ -0,0 +1,30 @@
{
"object_kind": "access_token",
"project_id": 7,
"project": {
"id": 7,
"name": "Flight",
"description": "Eum dolore maxime atque reprehenderit voluptatem.",
"web_url": "https://example.com/flightjs/Flight",
"avatar_url": null,
"git_ssh_url": "ssh://git@example.com/flightjs/Flight.git",
"git_http_url": "https://example.com/flightjs/Flight.git",
"namespace": "Flightjs",
"visibility_level": 0,
"path_with_namespace": "flightjs/Flight",
"default_branch": "master",
"ci_config_path": null,
"homepage": "https://example.com/flightjs/Flight",
"url": "ssh://git@example.com/flightjs/Flight.git",
"ssh_url": "ssh://git@example.com/flightjs/Flight.git",
"http_url": "https://example.com/flightjs/Flight.git"
},
"object_attributes": {
"user_id": 90,
"created_at": "2024-02-05T03:13:44.855Z",
"id": 25,
"name": "acd",
"expires_at": "2024-01-26"
},
"event_name": "expiring_access_token"
}

0 comments on commit a29d319

Please sign in to comment.