From 48f7312e64f82062d30cb3746c0c0d13f16d5711 Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 14:01:14 +0300 Subject: [PATCH 1/8] Fix GitLab GetPullRequestByID --- vcsclient/gitlab.go | 35 ++++- vcsclient/gitlab_test.go | 21 ++- .../testdata/gitlab/get_project_response.json | 147 ++++++++++++++++++ 3 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 vcsclient/testdata/gitlab/get_project_response.json diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index 6df287ab..1a671269 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -275,13 +275,25 @@ func (client *GitLabClient) getOpenPullRequests(ctx context.Context, owner, repo // GetPullRequestInfoById on GitLab func (client *GitLabClient) GetPullRequestByID(_ context.Context, owner, repository string, pullRequestId int) (pullRequestInfo PullRequestInfo, err error) { client.logger.Debug("fetching merge requests by ID in", repository) - mergeRequest, response, err := client.glClient.MergeRequests.GetMergeRequest(getProjectID(owner, repository), pullRequestId, nil) - if err != nil || response.StatusCode != http.StatusOK { + mergeRequest, glResponse, err := client.glClient.MergeRequests.GetMergeRequest(getProjectID(owner, repository), pullRequestId, nil) + if err != nil { return PullRequestInfo{}, err } + if glResponse != nil { + if err = vcsutils.CheckResponseStatusWithBody(glResponse.Response, http.StatusOK); err != nil { + return PullRequestInfo{}, err + } + } + sourceOwner := owner + if mergeRequest.SourceProjectID != mergeRequest.TargetProjectID { + client.logger.Debug("source project owner is different from the target project owner, fetching its name by the project id...") + if sourceOwner, err = getProjectOwnerByID(mergeRequest.SourceProjectID, client); err != nil { + return PullRequestInfo{}, err + } + } pullRequestInfo = PullRequestInfo{ ID: int64(mergeRequest.ID), - Source: BranchInfo{Name: mergeRequest.SourceBranch, Repository: repository, Owner: owner}, + Source: BranchInfo{Name: mergeRequest.SourceBranch, Repository: repository, Owner: sourceOwner}, Target: BranchInfo{Name: mergeRequest.TargetBranch, Repository: repository, Owner: owner}, } return @@ -603,3 +615,20 @@ func mapGitLabMergeRequestToPullRequestInfoList(mergeRequests []*gitlab.MergeReq } return } + +func getProjectOwnerByID(sourceProjectID int, client *GitLabClient) (string, error) { + var sourceProject *gitlab.Project + sourceProject, glResponse, err := client.glClient.Projects.GetProject(sourceProjectID, &gitlab.GetProjectOptions{}) + if err != nil { + return "", err + } + if glResponse != nil { + if err = vcsutils.CheckResponseStatusWithBody(glResponse.Response, http.StatusOK); err != nil { + return "", err + } + } + if sourceProject.Namespace == nil { + return "", errors.New("could not fetch the name of the source project owner") + } + return sourceProject.Namespace.Name, nil +} diff --git a/vcsclient/gitlab_test.go b/vcsclient/gitlab_test.go index 09c923b7..7ffe1e1e 100644 --- a/vcsclient/gitlab_test.go +++ b/vcsclient/gitlab_test.go @@ -596,7 +596,6 @@ func createGitLabHandler(t *testing.T, expectedURI string, response []byte, expe } } func createGitLabWithPaginationHandler(t *testing.T, _ string, response []byte, _ []byte, expectedStatusCode int, expectedHttpMethod string) http.HandlerFunc { - var repos []gitlab.Project err := json.Unmarshal(response, &repos) assert.NoError(t, err) @@ -723,3 +722,23 @@ func TestGitLabClient_TestGetCommitStatus(t *testing.T) { assert.Error(t, err) }) } + +func TestGitLabClient_getProjectOwnerByID(t *testing.T) { + projectID := 47457684 + + // Successful response + response, err := os.ReadFile(filepath.Join("testdata", "gitlab", "get_project_response.json")) + assert.NoError(t, err) + client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, response, + fmt.Sprintf("/api/v4/projects/%d", projectID), createGitLabHandler) + defer cleanUp() + projectOwner, err := getProjectOwnerByID(projectID, client.(*GitLabClient)) + assert.NoError(t, err) + assert.Equal(t, projectOwner, "test") + badClient, badClientCleanUp := createServerAndClient(t, vcsutils.GitLab, false, nil, + fmt.Sprintf("/api/v4/projects/%d", projectID), createGitLabHandler) + defer badClientCleanUp() + + projectOwner, err = getProjectOwnerByID(projectID, badClient.(*GitLabClient)) + assert.Error(t, err) +} diff --git a/vcsclient/testdata/gitlab/get_project_response.json b/vcsclient/testdata/gitlab/get_project_response.json new file mode 100644 index 00000000..262b6a9c --- /dev/null +++ b/vcsclient/testdata/gitlab/get_project_response.json @@ -0,0 +1,147 @@ +{ + "id": 47457684, + "description": null, + "name": "frogbot-demo", + "name_with_namespace": "test / frogbot-demo", + "path": "frogbot-demo", + "path_with_namespace": "test/frogbot-demo", + "created_at": "2023-07-06T06:55:07.521Z", + "default_branch": "main", + "tag_list": [], + "topics": [], + "ssh_url_to_repo": "git@gitlab.com:test/frogbot-demo.git", + "http_url_to_repo": "https://gitlab.com/test/frogbot-demo.git", + "web_url": "https://gitlab.com/test/frogbot-demo", + "readme_url": "https://gitlab.com/test/frogbot-demo/-/blob/main/README.md", + "forks_count": 0, + "avatar_url": null, + "star_count": 0, + "last_activity_at": "2023-07-06T08:23:22.766Z", + "namespace": { + "id": 62039166, + "name": "test", + "path": "test", + "kind": "group", + "full_path": "test", + "parent_id": null, + "avatar_url": null, + "web_url": "https://gitlab.com/groups/test" + }, + "container_registry_image_prefix": "registry.gitlab.com/test/frogbot-demo", + "_links": { + "self": "https://gitlab.com/api/v4/projects/47457684", + "issues": "https://gitlab.com/api/v4/projects/47457684/issues", + "merge_requests": "https://gitlab.com/api/v4/projects/47457684/merge_requests", + "repo_branches": "https://gitlab.com/api/v4/projects/47457684/repository/branches", + "labels": "https://gitlab.com/api/v4/projects/47457684/labels", + "events": "https://gitlab.com/api/v4/projects/47457684/events", + "members": "https://gitlab.com/api/v4/projects/47457684/members", + "cluster_agents": "https://gitlab.com/api/v4/projects/47457684/cluster_agents" + }, + "packages_enabled": true, + "empty_repo": false, + "archived": false, + "visibility": "private", + "resolve_outdated_diff_discussions": false, + "container_expiration_policy": { + "cadence": "1d", + "enabled": false, + "keep_n": 10, + "older_than": "90d", + "name_regex": ".*", + "name_regex_keep": null, + "next_run_at": "2023-07-07T06:55:07.560Z" + }, + "issues_enabled": true, + "merge_requests_enabled": true, + "wiki_enabled": true, + "jobs_enabled": true, + "snippets_enabled": true, + "container_registry_enabled": true, + "service_desk_enabled": true, + "service_desk_address": "contact-project+test-frogbot-demo-47457684-issue-@incoming.gitlab.com", + "can_create_merge_request_in": true, + "issues_access_level": "enabled", + "repository_access_level": "enabled", + "merge_requests_access_level": "enabled", + "forking_access_level": "enabled", + "wiki_access_level": "enabled", + "builds_access_level": "enabled", + "snippets_access_level": "enabled", + "pages_access_level": "private", + "analytics_access_level": "enabled", + "container_registry_access_level": "enabled", + "security_and_compliance_access_level": "private", + "releases_access_level": "enabled", + "environments_access_level": "enabled", + "feature_flags_access_level": "enabled", + "infrastructure_access_level": "enabled", + "monitor_access_level": "enabled", + "emails_disabled": false, + "emails_enabled": true, + "shared_runners_enabled": true, + "lfs_enabled": true, + "creator_id": 13377328, + "import_url": null, + "import_type": null, + "import_status": "none", + "import_error": null, + "open_issues_count": 0, + "description_html": "", + "updated_at": "2023-07-06T08:53:22.050Z", + "ci_default_git_depth": 20, + "ci_forward_deployment_enabled": true, + "ci_forward_deployment_rollback_allowed": true, + "ci_job_token_scope_enabled": false, + "ci_separated_caches": true, + "ci_allow_fork_pipelines_to_run_in_parent_project": true, + "build_git_strategy": "fetch", + "keep_latest_artifact": true, + "restrict_user_defined_variables": false, + "runners_token": "GR1348941eo_AAkjv4niyY222E4nz", + "runner_token_expiration_interval": null, + "group_runners_enabled": true, + "auto_cancel_pending_pipelines": "enabled", + "build_timeout": 3600, + "auto_devops_enabled": false, + "auto_devops_deploy_strategy": "continuous", + "ci_config_path": "", + "public_jobs": true, + "shared_with_groups": [], + "only_allow_merge_if_pipeline_succeeds": false, + "allow_merge_on_skipped_pipeline": null, + "request_access_enabled": true, + "only_allow_merge_if_all_discussions_are_resolved": false, + "remove_source_branch_after_merge": true, + "printing_merge_request_link_enabled": true, + "merge_method": "merge", + "squash_option": "default_off", + "enforce_auth_checks_on_uploads": true, + "suggestion_commit_message": null, + "merge_commit_template": null, + "squash_commit_template": null, + "issue_branch_template": null, + "autoclose_referenced_issues": true, + "approvals_before_merge": 0, + "mirror": false, + "external_authorization_classification_label": "", + "marked_for_deletion_at": null, + "marked_for_deletion_on": null, + "requirements_enabled": true, + "requirements_access_level": "enabled", + "security_and_compliance_enabled": true, + "compliance_frameworks": [], + "issues_template": null, + "merge_requests_template": null, + "merge_pipelines_enabled": false, + "merge_trains_enabled": false, + "only_allow_merge_if_all_status_checks_passed": false, + "allow_pipeline_trigger_approve_deployment": false, + "permissions": { + "project_access": null, + "group_access": { + "access_level": 50, + "notification_level": 3 + } + } +} \ No newline at end of file From 82758cc22c5bc3210c29e0958e304ad6ee4abb26 Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 14:03:41 +0300 Subject: [PATCH 2/8] Fix GitLab GetPullRequestByID --- vcsclient/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index 1a671269..2f46a2a8 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -286,7 +286,7 @@ func (client *GitLabClient) GetPullRequestByID(_ context.Context, owner, reposit } sourceOwner := owner if mergeRequest.SourceProjectID != mergeRequest.TargetProjectID { - client.logger.Debug("source project owner is different from the target project owner, fetching its name by the project id...") + client.logger.Debug("Source project owner is different from the target project owner, fetching its name by the project id...") if sourceOwner, err = getProjectOwnerByID(mergeRequest.SourceProjectID, client); err != nil { return PullRequestInfo{}, err } From 8690ca2dd38b6c75bb58d8c8bbc529192ac81208 Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 14:03:41 +0300 Subject: [PATCH 3/8] Fix GitLab GetPullRequestByID --- vcsclient/gitlab.go | 2 +- vcsutils/utils.go | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index 1a671269..2f46a2a8 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -286,7 +286,7 @@ func (client *GitLabClient) GetPullRequestByID(_ context.Context, owner, reposit } sourceOwner := owner if mergeRequest.SourceProjectID != mergeRequest.TargetProjectID { - client.logger.Debug("source project owner is different from the target project owner, fetching its name by the project id...") + client.logger.Debug("Source project owner is different from the target project owner, fetching its name by the project id...") if sourceOwner, err = getProjectOwnerByID(mergeRequest.SourceProjectID, client); err != nil { return PullRequestInfo{}, err } diff --git a/vcsutils/utils.go b/vcsutils/utils.go index 6df78fbb..7a8c82aa 100644 --- a/vcsutils/utils.go +++ b/vcsutils/utils.go @@ -12,6 +12,7 @@ import ( "github.com/go-git/go-git/v5/config" "github.com/google/uuid" "github.com/mitchellh/mapstructure" + "golang.org/x/exp/slices" "io" "net/http" "os" @@ -241,10 +242,12 @@ func unzipFile(f *zip.File, destination string) (err error) { } func CheckResponseStatusWithBody(resp *http.Response, expectedStatusCodes ...int) error { - for _, statusCode := range expectedStatusCodes { - if statusCode == resp.StatusCode { - return nil - } + if resp == nil { + return errors.New("received an empty response") + } + + if slices.Contains(expectedStatusCodes, resp.StatusCode) { + return nil } body, err := io.ReadAll(resp.Body) From 2f2b6ae2d441552e56f9e68673197e28e5fc254e Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 14:16:13 +0300 Subject: [PATCH 4/8] Fix GitLab GetPullRequestByID --- vcsclient/gitlab_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vcsclient/gitlab_test.go b/vcsclient/gitlab_test.go index 7ffe1e1e..90c8b327 100644 --- a/vcsclient/gitlab_test.go +++ b/vcsclient/gitlab_test.go @@ -734,11 +734,12 @@ func TestGitLabClient_getProjectOwnerByID(t *testing.T) { defer cleanUp() projectOwner, err := getProjectOwnerByID(projectID, client.(*GitLabClient)) assert.NoError(t, err) - assert.Equal(t, projectOwner, "test") + assert.Equal(t, "test", projectOwner) badClient, badClientCleanUp := createServerAndClient(t, vcsutils.GitLab, false, nil, fmt.Sprintf("/api/v4/projects/%d", projectID), createGitLabHandler) defer badClientCleanUp() projectOwner, err = getProjectOwnerByID(projectID, badClient.(*GitLabClient)) assert.Error(t, err) + assert.NotEqual(t, "test", projectOwner) } From cd3306cd48792bc26e4dfd1ea31b6c170aac8a8c Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 16:25:30 +0300 Subject: [PATCH 5/8] CR Changes --- vcsclient/bitbucketserver.go | 9 +++++++-- vcsclient/github.go | 9 +++++++-- vcsclient/gitlab.go | 24 ++++++++++++++---------- vcsclient/gitlab_test.go | 6 +++--- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/vcsclient/bitbucketserver.go b/vcsclient/bitbucketserver.go index 495bb9e2..425654e7 100644 --- a/vcsclient/bitbucketserver.go +++ b/vcsclient/bitbucketserver.go @@ -380,8 +380,13 @@ func (client *BitbucketServerClient) GetPullRequestByID(ctx context.Context, own client.logger.Debug("fetching pull request by ID in ", repository) bitbucketClient := client.buildBitbucketClient(ctx) apiResponse, err := bitbucketClient.GetPullRequest(owner, repository, pullRequestId) - if err != nil || apiResponse.StatusCode != http.StatusOK { - return + if err != nil { + return PullRequestInfo{}, err + } + if apiResponse != nil { + if err = vcsutils.CheckResponseStatusWithBody(apiResponse.Response, http.StatusOK); err != nil { + return PullRequestInfo{}, err + } } pullRequest, err := bitbucketv1.GetPullRequestResponse(apiResponse) if err != nil { diff --git a/vcsclient/github.go b/vcsclient/github.go index 08efd2ff..1d2ec3ae 100644 --- a/vcsclient/github.go +++ b/vcsclient/github.go @@ -326,10 +326,15 @@ func (client *GitHubClient) GetPullRequestByID(ctx context.Context, owner, repos return PullRequestInfo{}, err } client.logger.Debug(fetchingPullRequestById, repository) - pullRequest, response, err := ghClient.PullRequests.Get(ctx, owner, repository, pullRequestId) - if err != nil || response.StatusCode != http.StatusOK { + pullRequest, ghResponse, err := ghClient.PullRequests.Get(ctx, owner, repository, pullRequestId) + if err != nil { return PullRequestInfo{}, err } + if ghResponse != nil { + if err = vcsutils.CheckResponseStatusWithBody(ghResponse.Response, http.StatusOK); err != nil { + return PullRequestInfo{}, err + } + } sourceBranch, err1 := extractBranchFromLabel(vcsutils.DefaultIfNotNil(pullRequest.Head.Label)) targetBranch, err2 := extractBranchFromLabel(vcsutils.DefaultIfNotNil(pullRequest.Base.Label)) diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index ec7c219d..98828937 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -269,7 +269,7 @@ func (client *GitLabClient) getOpenPullRequests(ctx context.Context, owner, repo if err != nil { return []PullRequestInfo{}, err } - return mapGitLabMergeRequestToPullRequestInfoList(mergeRequests, owner, repository, withBody), nil + return mapGitLabMergeRequestToPullRequestInfoList(mergeRequests, client, owner, repository, withBody) } // GetPullRequestInfoById on GitLab @@ -286,7 +286,6 @@ func (client *GitLabClient) GetPullRequestByID(_ context.Context, owner, reposit } sourceOwner := owner if mergeRequest.SourceProjectID != mergeRequest.TargetProjectID { - client.logger.Debug("Source project owner is different from the target project owner, fetching its name by the project id...") if sourceOwner, err = getProjectOwnerByID(mergeRequest.SourceProjectID, client); err != nil { return PullRequestInfo{}, err } @@ -600,19 +599,25 @@ func mapGitLabNotesToCommentInfoList(notes []*gitlab.Note) (res []CommentInfo) { return } -func mapGitLabMergeRequestToPullRequestInfoList(mergeRequests []*gitlab.MergeRequest, owner, repository string, withBody bool) (res []PullRequestInfo) { +func mapGitLabMergeRequestToPullRequestInfoList(mergeRequests []*gitlab.MergeRequest, client *GitLabClient, owner, repository string, withBody bool) (res []PullRequestInfo, err error) { for _, mergeRequest := range mergeRequests { var body string if withBody { body = mergeRequest.Description } + sourceOwner := owner + if mergeRequest.SourceProjectID != mergeRequest.TargetProjectID { + if sourceOwner, err = getProjectOwnerByID(mergeRequest.SourceProjectID, client); err != nil { + return + } + } res = append(res, PullRequestInfo{ ID: int64(mergeRequest.IID), Body: body, Source: BranchInfo{ Name: mergeRequest.SourceBranch, Repository: repository, - Owner: owner, + Owner: sourceOwner, }, Target: BranchInfo{ Name: mergeRequest.TargetBranch, @@ -624,9 +629,8 @@ func mapGitLabMergeRequestToPullRequestInfoList(mergeRequests []*gitlab.MergeReq return } -func getProjectOwnerByID(sourceProjectID int, client *GitLabClient) (string, error) { - var sourceProject *gitlab.Project - sourceProject, glResponse, err := client.glClient.Projects.GetProject(sourceProjectID, &gitlab.GetProjectOptions{}) +func getProjectOwnerByID(projectID int, client *GitLabClient) (string, error) { + project, glResponse, err := client.glClient.Projects.GetProject(projectID, &gitlab.GetProjectOptions{}) if err != nil { return "", err } @@ -635,8 +639,8 @@ func getProjectOwnerByID(sourceProjectID int, client *GitLabClient) (string, err return "", err } } - if sourceProject.Namespace == nil { - return "", errors.New("could not fetch the name of the source project owner") + if project.Namespace == nil { + return "", errors.New("could not fetch the name of the project owner. projectID: " + string(projectID)) } - return sourceProject.Namespace.Name, nil + return project.Namespace.Name, nil } diff --git a/vcsclient/gitlab_test.go b/vcsclient/gitlab_test.go index fef63fb8..ce21f846 100644 --- a/vcsclient/gitlab_test.go +++ b/vcsclient/gitlab_test.go @@ -735,10 +735,10 @@ func TestGitLabClient_getProjectOwnerByID(t *testing.T) { projectOwner, err := getProjectOwnerByID(projectID, client.(*GitLabClient)) assert.NoError(t, err) assert.Equal(t, "test", projectOwner) - badClient, badClientCleanUp := createServerAndClient(t, vcsutils.GitLab, false, nil, - fmt.Sprintf("/api/v4/projects/%d", projectID), createGitLabHandler) - defer badClientCleanUp() + badClient, badClientCleanUp := + createServerAndClient(t, vcsutils.GitLab, false, nil, fmt.Sprintf("/api/v4/projects/%d", projectID), createGitLabHandler) + defer badClientCleanUp() projectOwner, err = getProjectOwnerByID(projectID, badClient.(*GitLabClient)) assert.Error(t, err) assert.NotEqual(t, "test", projectOwner) From 0d60fb3d52c83baaf5cca329b60ccf2b6a6a2e9c Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 16:27:52 +0300 Subject: [PATCH 6/8] CR Changes --- vcsclient/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index 98828937..1124f8f6 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -640,7 +640,7 @@ func getProjectOwnerByID(projectID int, client *GitLabClient) (string, error) { } } if project.Namespace == nil { - return "", errors.New("could not fetch the name of the project owner. projectID: " + string(projectID)) + return "", fmt.Errorf("could not fetch the name of the project owner. projectID: %d", projectID) } return project.Namespace.Name, nil } From cf83778145ed07af51caa245b57df7cf6f80c955 Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 16:28:10 +0300 Subject: [PATCH 7/8] CR Changes --- vcsclient/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index 1124f8f6..da1e4ad9 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -640,7 +640,7 @@ func getProjectOwnerByID(projectID int, client *GitLabClient) (string, error) { } } if project.Namespace == nil { - return "", fmt.Errorf("could not fetch the name of the project owner. projectID: %d", projectID) + return "", fmt.Errorf("could not fetch the name of the project owner. Project ID: %d", projectID) } return project.Namespace.Name, nil } From ab817fbf461a8338acdf594bc12bbede283fe29f Mon Sep 17 00:00:00 2001 From: Omer Zidkoni Date: Sun, 30 Jul 2023 16:31:59 +0300 Subject: [PATCH 8/8] Fix TestGitLabClient_ListOpenPullRequests --- vcsclient/testdata/gitlab/pull_requests_list_response.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcsclient/testdata/gitlab/pull_requests_list_response.json b/vcsclient/testdata/gitlab/pull_requests_list_response.json index 64d34ba5..14e767f3 100644 --- a/vcsclient/testdata/gitlab/pull_requests_list_response.json +++ b/vcsclient/testdata/gitlab/pull_requests_list_response.json @@ -56,7 +56,7 @@ "web_url": "https://gitlab.example.com//kenyatta_oconnell" }], "source_project_id": 2, - "target_project_id": 3, + "target_project_id": 2, "labels": [ "Community contribution", "Manage"