Commit 1ab1347b authored by Huijie Shi's avatar Huijie Shi Committed by Patrick Rice
Browse files

Add missing fields to emoji and milestone event types

Changelog: Improvements
parent e942b763
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -361,7 +361,7 @@ func TestParseWikiPageHook(t *testing.T) {

func TestParseEmojiHook(t *testing.T) {
	t.Parallel()
	raw := loadFixture(t, "testdata/webhooks/emoji.json")
	raw := loadFixture(t, "testdata/webhooks/emoji_issue.json")

	parsedEvent, err := ParseWebhook("Emoji Hook", raw)
	assert.NoError(t, err)
@@ -383,7 +383,7 @@ func TestParseEmojiHook(t *testing.T) {

func TestParseMilestoneHook(t *testing.T) {
	t.Parallel()
	raw := loadFixture(t, "testdata/webhooks/milestone.json")
	raw := loadFixture(t, "testdata/webhooks/milestone_project.json")

	parsedEvent, err := ParseWebhook("Milestone Hook", raw)
	assert.NoError(t, err)
+119 −25
Original line number Diff line number Diff line
@@ -234,10 +234,13 @@ type GroupResourceAccessTokenEvent struct {
	ObjectAttributes GroupResourceAccessTokenEventObjectAttributes `json:"object_attributes"`
}

// GroupResourceAccessTokenEventGroup represents a group in a resource access
// token event.
type GroupResourceAccessTokenEventGroup struct {
	GroupID   int64  `json:"group_id"`
	GroupName string `json:"group_name"`
	GroupPath string `json:"group_path"`
	FullPath  string `json:"full_path"`
}

type GroupResourceAccessTokenEventObjectAttributes struct {
@@ -839,7 +842,7 @@ type MergeEventChangesTargetProjectID struct {
	Current  int64 `json:"current"`
}

// EventUser represents a user record in an event and is used as an even
// EventUser represents a user record in an event and is used as an event
// initiator or a merge assignee.
type EventUser struct {
	ID        int64  `json:"id"`
@@ -1364,8 +1367,12 @@ type EmojiEvent struct {
	ObjectAttributes EmojiEventObjectAttributes `json:"object_attributes"`
	Note             *EmojiEventNote            `json:"note,omitempty"`
	Issue            *EmojiEventIssue           `json:"issue,omitempty"`
	MergeRequest     *EmojiEventMergeRequest    `json:"merge_request,omitempty"`
	ProjectSnippet   *EmojiEventSnippet         `json:"project_snippet,omitempty"`
	Commit           *EmojiEventCommit          `json:"commit,omitempty"`
}

// EmojiEventProject represents a project in an emoji event.
type EmojiEventProject struct {
	ID                int64  `json:"id"`
	Name              string `json:"name"`
@@ -1461,6 +1468,82 @@ type EmojiEventIssue struct {
	Severity            string        `json:"severity"`
}

// EmojiEventMergeRequest represents a merge request in an emoji event.
type EmojiEventMergeRequest struct {
	ID                        int64                       `json:"id"`
	TargetBranch              string                      `json:"target_branch"`
	SourceBranch              string                      `json:"source_branch"`
	SourceProjectID           int64                       `json:"source_project_id"`
	AuthorID                  int64                       `json:"author_id"`
	AssigneeID                int64                       `json:"assignee_id"`
	AssigneeIDs               []int64                     `json:"assignee_ids"`
	ReviewerIDs               []int64                     `json:"reviewer_ids"`
	Title                     string                      `json:"title"`
	CreatedAt                 string                      `json:"created_at"`
	UpdatedAt                 string                      `json:"updated_at"`
	MilestoneID               int64                       `json:"milestone_id"`
	State                     string                      `json:"state"`
	MergeStatus               string                      `json:"merge_status"`
	TargetProjectID           int64                       `json:"target_project_id"`
	IID                       int64                       `json:"iid"`
	Description               string                      `json:"description"`
	Position                  int64                       `json:"position"`
	Labels                    []*EventLabel               `json:"labels"`
	LockedAt                  string                      `json:"locked_at"`
	UpdatedByID               int64                       `json:"updated_by_id"`
	MergeError                string                      `json:"merge_error"`
	MergeParams               *MergeParams                `json:"merge_params"`
	MergeWhenPipelineSucceeds bool                        `json:"merge_when_pipeline_succeeds"`
	MergeUserID               int64                       `json:"merge_user_id"`
	MergeCommitSHA            string                      `json:"merge_commit_sha"`
	DeletedAt                 string                      `json:"deleted_at"`
	InProgressMergeCommitSHA  string                      `json:"in_progress_merge_commit_sha"`
	LockVersion               int64                       `json:"lock_version"`
	ApprovalsBeforeMerge      string                      `json:"approvals_before_merge"`
	RebaseCommitSHA           string                      `json:"rebase_commit_sha"`
	TimeEstimate              int64                       `json:"time_estimate"`
	Squash                    bool                        `json:"squash"`
	LastEditedAt              string                      `json:"last_edited_at"`
	LastEditedByID            int64                       `json:"last_edited_by_id"`
	Source                    *Repository                 `json:"source"`
	Target                    *Repository                 `json:"target"`
	LastCommit                EventMergeRequestLastCommit `json:"last_commit"`
	WorkInProgress            bool                        `json:"work_in_progress"`
	TotalTimeSpent            int64                       `json:"total_time_spent"`
	HeadPipelineID            int64                       `json:"head_pipeline_id"`
	Assignee                  *EventUser                  `json:"assignee"`
	DetailedMergeStatus       string                      `json:"detailed_merge_status"`
	URL                       string                      `json:"url"`
}

// EmojiEventSnippet represents a snippet in an emoji event.
type EmojiEventSnippet struct {
	ID                 int64  `json:"id"`
	Title              string `json:"title"`
	Content            string `json:"content"`
	AuthorID           int64  `json:"author_id"`
	ProjectID          int64  `json:"project_id"`
	CreatedAt          string `json:"created_at"`
	UpdatedAt          string `json:"updated_at"`
	Filename           string `json:"file_name"`
	ExpiresAt          string `json:"expires_at"`
	Type               string `json:"type"`
	VisibilityLevel    int64  `json:"visibility_level"`
	Description        string `json:"description"`
	Secret             bool   `json:"secret"`
	RepositoryReadOnly bool   `json:"repository_read_only"`
}

// EmojiEventCommit represents a commit in an emoji event.
type EmojiEventCommit struct {
	ID        string            `json:"id"`
	Title     string            `json:"title"`
	Message   string            `json:"message"`
	Timestamp *time.Time        `json:"timestamp"`
	URL       string            `json:"url"`
	Author    EventCommitAuthor `json:"author"`
}

// MilestoneWebhookEvent represents a milestone webhook event.
//
// GitLab API docs:
@@ -1469,10 +1552,34 @@ type MilestoneWebhookEvent struct {
	ObjectKind       string                         `json:"object_kind"`
	EventType        string                         `json:"event_type"`
	Project          MilestoneEventProject          `json:"project"`
	Group            *MilestoneEventGroup           `json:"group,omitempty"`
	ObjectAttributes MilestoneEventObjectAttributes `json:"object_attributes"`
	Action           string                         `json:"action"`
}

type MilestoneEventObjectAttributes struct {
	ID          int64    `json:"id"`
	IID         int64    `json:"iid"`
	Title       string   `json:"title"`
	Description string   `json:"description"`
	State       string   `json:"state"`
	CreatedAt   string   `json:"created_at"`
	UpdatedAt   string   `json:"updated_at"`
	DueDate     *ISOTime `json:"due_date"`
	StartDate   *ISOTime `json:"start_date"`
	GroupID     *int64   `json:"group_id"`
	ProjectID   int64    `json:"project_id"`
}

// MilestoneEventGroup represents a group in a milestone event.
type MilestoneEventGroup struct {
	GroupID   int64  `json:"group_id"`
	GroupName string `json:"group_name"`
	GroupPath string `json:"group_path"`
	FullPath  string `json:"full_path"`
}

// MilestoneEventProject represents a project in a milestone event.
type MilestoneEventProject struct {
	ID                int64  `json:"id"`
	Name              string `json:"name"`
@@ -1492,20 +1599,6 @@ type MilestoneEventProject struct {
	HTTPURL           string `json:"http_url"`
}

type MilestoneEventObjectAttributes struct {
	ID          int64    `json:"id"`
	IID         int64    `json:"iid"`
	Title       string   `json:"title"`
	Description string   `json:"description"`
	State       string   `json:"state"`
	CreatedAt   string   `json:"created_at"`
	UpdatedAt   string   `json:"updated_at"`
	DueDate     *ISOTime `json:"due_date"`
	StartDate   *ISOTime `json:"start_date"`
	GroupID     *int64   `json:"group_id"`
	ProjectID   int64    `json:"project_id"`
}

// ProjectWebhookEvent represents a project webhook event for group webhooks.
//
// GitLab API docs:
@@ -1521,6 +1614,7 @@ type ProjectWebhookEvent struct {
	ProjectNamespaceID   int64               `json:"project_namespace_id"`
	Owners               []ProjectEventOwner `json:"owners"`
	ProjectVisibility    string              `json:"project_visibility"`
	OldPathWithNamespace string              `json:"old_path_with_namespace,omitempty"`
}

type ProjectEventOwner struct {
+254 −3
Original line number Diff line number Diff line
@@ -327,6 +327,7 @@ func TestGroupResourceAccessTokenEventUnmarshal(t *testing.T) {
	expected.Group.GroupID = 35
	expected.Group.GroupName = "Twitter"
	expected.Group.GroupPath = "twitter"
	expected.Group.FullPath = "twitter"

	expected.ObjectAttributes.ID = 25
	expected.ObjectAttributes.UserID = 90
@@ -1421,7 +1422,7 @@ func TestEmojiEventUnmarshal(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload
	jsonObject := loadFixture(t, "testdata/webhooks/emoji.json")
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_issue.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
@@ -1442,6 +1443,7 @@ func TestEmojiEventUnmarshal(t *testing.T) {
	assert.Equal(t, "admin@example.com", event.User.Email)

	// Project assertions
	assert.NotNil(t, event.Project)
	assert.Equal(t, int64(7), event.Project.ID)
	assert.Equal(t, "Flight", event.Project.Name)
	assert.Equal(t, "flightjs/Flight", event.Project.PathWithNamespace)
@@ -1469,11 +1471,210 @@ func TestEmojiEventUnmarshal(t *testing.T) {
	assert.False(t, event.Issue.Confidential)
}

func TestEmojiEventUnmarshalMergeRequest(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload on a merge request
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_merge_request.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "emoji", event.ObjectKind)
	assert.Equal(t, "award", event.EventType)
	assert.Equal(t, "MergeRequest", event.ObjectAttributes.AwardableType)

	// MergeRequest assertions
	assert.NotNil(t, event.MergeRequest)
	assert.Equal(t, int64(123), event.MergeRequest.ID)
	assert.Equal(t, int64(1), event.MergeRequest.IID)
	assert.Equal(t, "Test Merge Request", event.MergeRequest.Title)
	assert.Equal(t, "opened", event.MergeRequest.State)

	// Issue and Snippet should be nil
	assert.Nil(t, event.Issue)
	assert.Nil(t, event.ProjectSnippet)
	assert.Nil(t, event.Note)
	assert.Nil(t, event.Commit)
}

func TestEmojiEventUnmarshalSnippet(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload on a snippet
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_snippet.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "emoji", event.ObjectKind)
	assert.Equal(t, "award", event.EventType)
	assert.Equal(t, "Snippet", event.ObjectAttributes.AwardableType)

	// Snippet assertions
	assert.NotNil(t, event.ProjectSnippet)
	assert.Equal(t, int64(456), event.ProjectSnippet.ID)
	assert.Equal(t, "Test Snippet", event.ProjectSnippet.Title)
	assert.Equal(t, "ProjectSnippet", event.ProjectSnippet.Type)

	// Issue and MergeRequest should be nil
	assert.Nil(t, event.Issue)
	assert.Nil(t, event.MergeRequest)
	assert.Nil(t, event.Note)
	assert.Nil(t, event.Commit)
}

func TestEmojiEventUnmarshalNoteIssue(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload on a note on an issue
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_note_issue.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "emoji", event.ObjectKind)
	assert.Equal(t, "award", event.EventType)
	assert.Equal(t, "Note", event.ObjectAttributes.AwardableType)

	// Note assertions
	assert.NotNil(t, event.Note)
	assert.Equal(t, int64(789), event.Note.ID)
	assert.Equal(t, "Issue", event.Note.NoteableType)
	assert.Equal(t, int64(123), event.Note.NoteableID)

	// Issue assertions
	assert.NotNil(t, event.Issue)
	assert.Equal(t, int64(123), event.Issue.ID)

	// MergeRequest and Snippet should be nil
	assert.Nil(t, event.MergeRequest)
	assert.Nil(t, event.ProjectSnippet)
	assert.Nil(t, event.Commit)
}

func TestEmojiEventUnmarshalNoteMergeRequest(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload on a note on a merge request
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_note_merge_request.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "emoji", event.ObjectKind)
	assert.Equal(t, "award", event.EventType)
	assert.Equal(t, "Note", event.ObjectAttributes.AwardableType)

	// Note assertions
	assert.NotNil(t, event.Note)
	assert.Equal(t, int64(790), event.Note.ID)
	assert.Equal(t, "MergeRequest", event.Note.NoteableType)

	// MergeRequest assertions
	assert.NotNil(t, event.MergeRequest)
	assert.Equal(t, int64(123), event.MergeRequest.ID)

	// Issue and Snippet should be nil
	assert.Nil(t, event.Issue)
	assert.Nil(t, event.ProjectSnippet)
	assert.Nil(t, event.Commit)
}

func TestEmojiEventUnmarshalNoteSnippet(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload on a note on a snippet
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_note_snippet.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "emoji", event.ObjectKind)
	assert.Equal(t, "award", event.EventType)
	assert.Equal(t, "Note", event.ObjectAttributes.AwardableType)

	// Note assertions
	assert.NotNil(t, event.Note)
	assert.Equal(t, int64(791), event.Note.ID)
	assert.Equal(t, "Snippet", event.Note.NoteableType)

	// Snippet assertions
	assert.NotNil(t, event.ProjectSnippet)
	assert.Equal(t, int64(456), event.ProjectSnippet.ID)

	// Issue and MergeRequest should be nil
	assert.Nil(t, event.Issue)
	assert.Nil(t, event.MergeRequest)
	assert.Nil(t, event.Commit)
}

func TestEmojiEventUnmarshalNoteCommit(t *testing.T) {
	t.Parallel()

	// GIVEN an emoji event JSON payload on a note on a commit
	jsonObject := loadFixture(t, "testdata/webhooks/emoji_note_commit.json")

	// WHEN the JSON is unmarshaled
	var event *EmojiEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "emoji", event.ObjectKind)
	assert.Equal(t, "award", event.EventType)
	assert.Equal(t, "Note", event.ObjectAttributes.AwardableType)

	// Note assertions
	assert.NotNil(t, event.Note)
	assert.Equal(t, int64(792), event.Note.ID)
	assert.Equal(t, "Commit", event.Note.NoteableType)
	assert.NotNil(t, event.Note.CommitID)
	assert.Equal(t, "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", *event.Note.CommitID)

	// Commit assertions
	assert.NotNil(t, event.Commit)
	assert.Equal(t, "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", event.Commit.ID)
	assert.Equal(t, "Add submodule", event.Commit.Title)

	// Issue, MergeRequest and Snippet should be nil
	assert.Nil(t, event.Issue)
	assert.Nil(t, event.MergeRequest)
	assert.Nil(t, event.ProjectSnippet)
}

func TestMilestoneWebhookEventUnmarshal(t *testing.T) {
	t.Parallel()

	// GIVEN a milestone webhook event JSON payload
	jsonObject := loadFixture(t, "testdata/webhooks/milestone.json")
	jsonObject := loadFixture(t, "testdata/webhooks/milestone_project.json")

	// WHEN the JSON is unmarshaled
	var event *MilestoneWebhookEvent
@@ -1487,12 +1688,16 @@ func TestMilestoneWebhookEventUnmarshal(t *testing.T) {
	assert.Equal(t, "milestone", event.EventType)
	assert.Equal(t, "create", event.Action)

	// Project assertions
	// Project assertions (project milestone)
	assert.NotNil(t, event.Project)
	assert.Equal(t, int64(7), event.Project.ID)
	assert.Equal(t, "Flight", event.Project.Name)
	assert.Equal(t, "flightjs/Flight", event.Project.PathWithNamespace)
	assert.Equal(t, int64(0), event.Project.VisibilityLevel)

	// Group should be nil for project milestone
	assert.Nil(t, event.Group)

	// Object attributes assertions
	assert.Equal(t, int64(42), event.ObjectAttributes.ID)
	assert.Equal(t, int64(1), event.ObjectAttributes.IID)
@@ -1512,6 +1717,51 @@ func TestMilestoneWebhookEventUnmarshal(t *testing.T) {
	assert.Equal(t, &startDate, event.ObjectAttributes.StartDate)
}

func TestMilestoneWebhookEventUnmarshalGroup(t *testing.T) {
	t.Parallel()

	// GIVEN a group milestone webhook event JSON payload
	jsonObject := loadFixture(t, "testdata/webhooks/milestone_group.json")

	// WHEN the JSON is unmarshaled
	var event *MilestoneWebhookEvent
	err := json.Unmarshal(jsonObject, &event)

	// THEN no error should occur and the event should be populated correctly
	assert.NoError(t, err)
	assert.NotNil(t, event)

	assert.Equal(t, "milestone", event.ObjectKind)
	assert.Equal(t, "milestone", event.EventType)
	assert.Equal(t, "create", event.Action)

	// Project should be empty for group milestone
	assert.Empty(t, event.Project)

	// Group assertions (group milestone)
	assert.NotNil(t, event.Group)
	assert.Equal(t, int64(35), event.Group.GroupID)
	assert.Equal(t, "Flightjs", event.Group.GroupName)
	assert.Equal(t, "flightjs", event.Group.GroupPath)
	assert.Equal(t, "flightjs", event.Group.FullPath)

	// Object attributes assertions
	assert.Equal(t, int64(42), event.ObjectAttributes.ID)
	assert.Equal(t, int64(1), event.ObjectAttributes.IID)
	assert.Equal(t, "v1.0.0", event.ObjectAttributes.Title)
	assert.Equal(t, int64(35), *event.ObjectAttributes.GroupID)
	assert.Equal(t, int64(0), event.ObjectAttributes.ProjectID)

	// Date assertions
	dueDate, err := ParseISOTime("2024-03-01")
	assert.NoError(t, err)
	assert.Equal(t, &dueDate, event.ObjectAttributes.DueDate)

	startDate, err := ParseISOTime("2024-01-01")
	assert.NoError(t, err)
	assert.Equal(t, &startDate, event.ObjectAttributes.StartDate)
}

func TestProjectWebhookEventUnmarshal(t *testing.T) {
	t.Parallel()

@@ -1535,6 +1785,7 @@ func TestProjectWebhookEventUnmarshal(t *testing.T) {
	assert.Equal(t, int64(7), event.ProjectID)
	assert.Equal(t, int64(35), event.ProjectNamespaceID)
	assert.Equal(t, "private", event.ProjectVisibility)
	assert.Empty(t, event.OldPathWithNamespace)

	// Owners assertions
	assert.Len(t, event.Owners, 1)
+63 −0
Original line number Diff line number Diff line
{
  "object_kind": "emoji",
  "event_type": "award",
  "user": {
    "id": 1,
    "name": "Administrator",
    "username": "root",
    "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
    "email": "admin@example.com"
  },
  "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": "",
    "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": 1,
    "created_at": "2024-01-24 16:27:40 UTC",
    "id": 42,
    "name": "thumbsup",
    "awardable_type": "MergeRequest",
    "awardable_id": 123,
    "updated_at": "2024-01-24 16:27:40 UTC",
    "action": "award",
    "awarded_on_url": "https://example.com/flightjs/Flight/-/merge_requests/1"
  },
  "merge_request": {
    "id": 123,
    "iid": 1,
    "project_id": 7,
    "source_project_id": 7,
    "target_project_id": 7,
    "author_id": 1,
    "assignee_id": 0,
    "assignee_ids": [],
    "reviewer_ids": [],
    "title": "Test Merge Request",
    "description": "This is a test merge request",
    "state": "opened",
    "merge_status": "can_be_merged",
    "target_branch": "master",
    "source_branch": "feature/test",
    "created_at": "2024-01-20 10:00:00 UTC",
    "updated_at": "2024-01-24 16:27:40 UTC",
    "milestone_id": null,
    "labels": [],
    "url": "https://example.com/flightjs/Flight/-/merge_requests/1"
  }
}
Loading