diff --git a/gitlab.go b/gitlab.go index 5bc1eb511..82d97ba7d 100644 --- a/gitlab.go +++ b/gitlab.go @@ -101,107 +101,108 @@ type Client struct { UserAgent string // Services used for talking to different parts of the GitLab API. - AccessRequests *AccessRequestsService - Applications *ApplicationsService - AuditEvents *AuditEventsService - Avatar *AvatarRequestsService - AwardEmoji *AwardEmojiService - Boards *IssueBoardsService - Branches *BranchesService - BroadcastMessage *BroadcastMessagesService - ClusterAgents *ClusterAgentsService - CIYMLTemplate *CIYMLTemplatesService - Commits *CommitsService - ContainerRegistry *ContainerRegistryService - CustomAttribute *CustomAttributesService - DeployKeys *DeployKeysService - DeployTokens *DeployTokensService - Deployments *DeploymentsService - Discussions *DiscussionsService - Environments *EnvironmentsService - EpicIssues *EpicIssuesService - Epics *EpicsService - Events *EventsService - ExternalStatusChecks *ExternalStatusChecksService - Features *FeaturesService - FreezePeriods *FreezePeriodsService - GenericPackages *GenericPackagesService - GeoNodes *GeoNodesService - GitIgnoreTemplates *GitIgnoreTemplatesService - GroupAccessTokens *GroupAccessTokensService - GroupBadges *GroupBadgesService - GroupCluster *GroupClustersService - GroupImportExport *GroupImportExportService - GroupIssueBoards *GroupIssueBoardsService - GroupIterations *GroupIterationsService - GroupLabels *GroupLabelsService - GroupMembers *GroupMembersService - GroupMilestones *GroupMilestonesService - GroupVariables *GroupVariablesService - GroupWikis *GroupWikisService - Groups *GroupsService - InstanceCluster *InstanceClustersService - InstanceVariables *InstanceVariablesService - Invites *InvitesService - IssueLinks *IssueLinksService - Issues *IssuesService - IssuesStatistics *IssuesStatisticsService - Jobs *JobsService - Keys *KeysService - Labels *LabelsService - License *LicenseService - LicenseTemplates *LicenseTemplatesService - ManagedLicenses *ManagedLicensesService - Markdown *MarkdownService - MergeRequestApprovals *MergeRequestApprovalsService - MergeRequests *MergeRequestsService - Milestones *MilestonesService - Namespaces *NamespacesService - Notes *NotesService - NotificationSettings *NotificationSettingsService - Packages *PackagesService - Pages *PagesService - PagesDomains *PagesDomainsService - PersonalAccessTokens *PersonalAccessTokensService - PipelineSchedules *PipelineSchedulesService - PipelineTriggers *PipelineTriggersService - Pipelines *PipelinesService - PlanLimits *PlanLimitsService - ProjectBadges *ProjectBadgesService - ProjectAccessTokens *ProjectAccessTokensService - ProjectCluster *ProjectClustersService - ProjectImportExport *ProjectImportExportService - ProjectIterations *ProjectIterationsService - ProjectMembers *ProjectMembersService - ProjectMirrors *ProjectMirrorService - ProjectSnippets *ProjectSnippetsService - ProjectVariables *ProjectVariablesService - ProjectVulnerabilities *ProjectVulnerabilitiesService - Projects *ProjectsService - ProtectedBranches *ProtectedBranchesService - ProtectedEnvironments *ProtectedEnvironmentsService - ProtectedTags *ProtectedTagsService - ReleaseLinks *ReleaseLinksService - Releases *ReleasesService - Repositories *RepositoriesService - RepositoryFiles *RepositoryFilesService - RepositorySubmodules *RepositorySubmodulesService - ResourceLabelEvents *ResourceLabelEventsService - ResourceStateEvents *ResourceStateEventsService - Runners *RunnersService - Search *SearchService - Services *ServicesService - Settings *SettingsService - Sidekiq *SidekiqService - Snippets *SnippetsService - SystemHooks *SystemHooksService - Tags *TagsService - Todos *TodosService - Topics *TopicsService - Users *UsersService - Validate *ValidateService - Version *VersionService - Wikis *WikisService + AccessRequests *AccessRequestsService + Applications *ApplicationsService + AuditEvents *AuditEventsService + Avatar *AvatarRequestsService + AwardEmoji *AwardEmojiService + Boards *IssueBoardsService + Branches *BranchesService + BroadcastMessage *BroadcastMessagesService + CIYMLTemplate *CIYMLTemplatesService + ClusterAgents *ClusterAgentsService + Commits *CommitsService + ContainerRegistry *ContainerRegistryService + CustomAttribute *CustomAttributesService + DeployKeys *DeployKeysService + DeployTokens *DeployTokensService + Deployments *DeploymentsService + Discussions *DiscussionsService + Environments *EnvironmentsService + EpicIssues *EpicIssuesService + Epics *EpicsService + Events *EventsService + ExternalStatusChecks *ExternalStatusChecksService + Features *FeaturesService + FreezePeriods *FreezePeriodsService + GenericPackages *GenericPackagesService + GeoNodes *GeoNodesService + GitIgnoreTemplates *GitIgnoreTemplatesService + GroupAccessTokens *GroupAccessTokensService + GroupBadges *GroupBadgesService + GroupCluster *GroupClustersService + GroupImportExport *GroupImportExportService + GroupIssueBoards *GroupIssueBoardsService + GroupIterations *GroupIterationsService + GroupLabels *GroupLabelsService + GroupMembers *GroupMembersService + GroupMilestones *GroupMilestonesService + GroupVariables *GroupVariablesService + GroupWikis *GroupWikisService + Groups *GroupsService + InstanceCluster *InstanceClustersService + InstanceVariables *InstanceVariablesService + Invites *InvitesService + IssueLinks *IssueLinksService + Issues *IssuesService + IssuesStatistics *IssuesStatisticsService + Jobs *JobsService + Keys *KeysService + Labels *LabelsService + License *LicenseService + LicenseTemplates *LicenseTemplatesService + ManagedLicenses *ManagedLicensesService + Markdown *MarkdownService + MergeRequestApprovals *MergeRequestApprovalsService + MergeRequests *MergeRequestsService + Milestones *MilestonesService + Namespaces *NamespacesService + Notes *NotesService + NotificationSettings *NotificationSettingsService + Packages *PackagesService + Pages *PagesService + PagesDomains *PagesDomainsService + PersonalAccessTokens *PersonalAccessTokensService + PipelineSchedules *PipelineSchedulesService + PipelineTriggers *PipelineTriggersService + Pipelines *PipelinesService + PlanLimits *PlanLimitsService + ProjectAccessTokens *ProjectAccessTokensService + ProjectBadges *ProjectBadgesService + ProjectCluster *ProjectClustersService + ProjectImportExport *ProjectImportExportService + ProjectIterations *ProjectIterationsService + ProjectMembers *ProjectMembersService + ProjectMirrors *ProjectMirrorService + ProjectSnippets *ProjectSnippetsService + ProjectVariables *ProjectVariablesService + ProjectVulnerabilities *ProjectVulnerabilitiesService + Projects *ProjectsService + ProtectedBranches *ProtectedBranchesService + ProtectedEnvironments *ProtectedEnvironmentsService + ProtectedTags *ProtectedTagsService + ReleaseLinks *ReleaseLinksService + Releases *ReleasesService + Repositories *RepositoriesService + RepositoryFiles *RepositoryFilesService + RepositorySubmodules *RepositorySubmodulesService + ResourceLabelEvents *ResourceLabelEventsService + ResourceMilestoneEvents *ResourceMilestoneEventsService + ResourceStateEvents *ResourceStateEventsService + Runners *RunnersService + Search *SearchService + Services *ServicesService + Settings *SettingsService + Sidekiq *SidekiqService + Snippets *SnippetsService + SystemHooks *SystemHooksService + Tags *TagsService + Todos *TodosService + Topics *TopicsService + Users *UsersService + Validate *ValidateService + Version *VersionService + Wikis *WikisService } // ListOptions specifies the optional parameters to various List methods that @@ -387,6 +388,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.RepositoryFiles = &RepositoryFilesService{client: c} c.RepositorySubmodules = &RepositorySubmodulesService{client: c} c.ResourceLabelEvents = &ResourceLabelEventsService{client: c} + c.ResourceMilestoneEvents = &ResourceMilestoneEventsService{client: c} c.ResourceStateEvents = &ResourceStateEventsService{client: c} c.Runners = &RunnersService{client: c} c.Search = &SearchService{client: c} diff --git a/resource_milestone_events.go b/resource_milestone_events.go new file mode 100644 index 000000000..0301e8fa6 --- /dev/null +++ b/resource_milestone_events.go @@ -0,0 +1,155 @@ +// +// Copyright 2022, Mai Lapyst +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" + "time" +) + +// ResourceMilestoneEventsService handles communication with the event related +// methods of the GitLab API. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/resource_milestone_events.html +type ResourceMilestoneEventsService struct { + client *Client +} + +// MilestoneEvent represents a resource milestone event. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/resource_milestone_events.html +type MilestoneEvent struct { + ID int `json:"id"` + User *BasicUser `json:"user"` + CreatedAt *time.Time `json:"created_at"` + ResourceType string `json:"resource_type"` + ResourceID int `json:"resource_id"` + Milestone *Milestone `json:"milestone"` + Action string `json:"action"` +} + +// ListMilestoneEventsOptions represents the options for all resource state events +// list methods. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/resource_milestone_events.html#list-project-issue-milestone-events +type ListMilestoneEventsOptions struct { + ListOptions +} + +// ListIssueMilestoneEvents retrieves resource milestone events for the specified +// project and issue. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/resource_milestone_events.html#list-project-issue-milestone-events +func (s *ResourceMilestoneEventsService) ListIssueMilestoneEvents(pid interface{}, issue int, opt *ListMilestoneEventsOptions, options ...RequestOptionFunc) ([]*MilestoneEvent, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/issues/%d/resource_milestone_events", PathEscape(project), issue) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var mes []*MilestoneEvent + resp, err := s.client.Do(req, &mes) + if err != nil { + return nil, resp, err + } + + return mes, resp, err +} + +// GetIssueMilestoneEvent gets a single issue-milestone-event. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/resource_milestone_events.html#get-single-issue-milestone-event +func (s *ResourceMilestoneEventsService) GetIssueMilestoneEvent(pid interface{}, issue int, event int, options ...RequestOptionFunc) (*MilestoneEvent, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/issues/%d/resource_milestone_events/%d", PathEscape(project), issue, event) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + me := new(MilestoneEvent) + resp, err := s.client.Do(req, me) + if err != nil { + return nil, resp, err + } + + return me, resp, err +} + +// ListMergeMilestoneEvents retrieves resource milestone events for the specified +// project and merge request. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/resource_milestone_events.html#list-project-merge-request-milestone-events +func (s *ResourceMilestoneEventsService) ListMergeMilestoneEvents(pid interface{}, request int, opt *ListMilestoneEventsOptions, options ...RequestOptionFunc) ([]*MilestoneEvent, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_milestone_events", PathEscape(project), request) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var mes []*MilestoneEvent + resp, err := s.client.Do(req, &mes) + if err != nil { + return nil, resp, err + } + + return mes, resp, err +} + +// GetMergeRequestMilestoneEvent gets a single merge request milestone event. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/resource_milestone_events.html#get-single-merge-request-milestone-event +func (s *ResourceMilestoneEventsService) GetMergeRequestMilestoneEvent(pid interface{}, request int, event int, options ...RequestOptionFunc) (*MilestoneEvent, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_milestone_events/%d", PathEscape(project), request, event) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + me := new(MilestoneEvent) + resp, err := s.client.Do(req, me) + if err != nil { + return nil, resp, err + } + + return me, resp, err +} diff --git a/resource_milestone_events_test.go b/resource_milestone_events_test.go new file mode 100644 index 000000000..46177dc07 --- /dev/null +++ b/resource_milestone_events_test.go @@ -0,0 +1,259 @@ +package gitlab + +import ( + "fmt" + "net/http" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestResourceMilestoneEventsService_ListIssueMilestoneEvents(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/5/issues/11/resource_milestone_events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `[ + { + "id": 142, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "resource_type": "Issue", + "resource_id": 11, + "milestone": { + "id": 61, + "iid": 9, + "project_id": 7, + "title": "v1.2", + "description": "Ipsum Lorem", + "state": "active", + "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" + }, + "action": "add" + } + ]`) + }) + + opt := &ListMilestoneEventsOptions{ListOptions{Page: 1, PerPage: 10}} + + mes, _, err := client.ResourceMilestoneEvents.ListIssueMilestoneEvents(5, 11, opt) + require.NoError(t, err) + + want := []*MilestoneEvent{{ + ID: 142, + User: &BasicUser{ + ID: 1, + Username: "root", + Name: "Administrator", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + WebURL: "http://gitlab.example.com/root", + }, + ResourceType: "Issue", + ResourceID: 11, + Milestone: &Milestone{ + ID: 61, + IID: 9, + ProjectID: 7, + Title: "v1.2", + Description: "Ipsum Lorem", + State: "active", + WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", + }, + Action: "add", + }} + require.Equal(t, want, mes) +} + +func TestResourceMilestoneEventsService_GetIssueMilestoneEvent(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/5/issues/11/resource_milestone_events/143", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, ` + { + "id": 143, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "resource_type": "Issue", + "resource_id": 11, + "milestone": { + "id": 61, + "iid": 9, + "project_id": 7, + "title": "v1.2", + "description": "Ipsum Lorem", + "state": "active", + "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" + }, + "action": "remove" + }`, + ) + }) + + me, _, err := client.ResourceMilestoneEvents.GetIssueMilestoneEvent(5, 11, 143) + require.NoError(t, err) + + want := &MilestoneEvent{ + ID: 143, + User: &BasicUser{ + ID: 1, + Username: "root", + Name: "Administrator", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + WebURL: "http://gitlab.example.com/root", + }, + ResourceType: "Issue", + ResourceID: 11, + Milestone: &Milestone{ + ID: 61, + IID: 9, + ProjectID: 7, + Title: "v1.2", + Description: "Ipsum Lorem", + State: "active", + WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", + }, + Action: "remove", + } + require.Equal(t, want, me) +} + +func TestResourceMilestoneEventsService_ListMergeMilestoneEvents(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_milestone_events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `[ + { + "id": 142, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "resource_type": "MergeRequest", + "resource_id": 11, + "milestone": { + "id": 61, + "iid": 9, + "project_id": 7, + "title": "v1.2", + "description": "Ipsum Lorem", + "state": "active", + "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" + }, + "action": "add" + }]`, + ) + }) + + opt := &ListMilestoneEventsOptions{ListOptions{Page: 1, PerPage: 10}} + + ses, _, err := client.ResourceMilestoneEvents.ListMergeMilestoneEvents(5, 11, opt) + require.NoError(t, err) + + want := []*MilestoneEvent{{ + ID: 142, + User: &BasicUser{ + ID: 1, + Username: "root", + Name: "Administrator", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + WebURL: "http://gitlab.example.com/root", + }, + ResourceType: "MergeRequest", + ResourceID: 11, + Milestone: &Milestone{ + ID: 61, + IID: 9, + ProjectID: 7, + Title: "v1.2", + Description: "Ipsum Lorem", + State: "active", + WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", + }, + Action: "add", + }} + require.Equal(t, want, ses) +} + +func TestResourceMilestoneEventsService_GetMergeRequestMilestoneEvent(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_milestone_events/120", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `{ + "id": 120, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "resource_type": "MergeRequest", + "resource_id": 11, + "milestone": { + "id": 61, + "iid": 9, + "project_id": 7, + "title": "v1.2", + "description": "Ipsum Lorem", + "state": "active", + "web_url": "http://gitlab.example.com:3000/group/project/-/milestones/9" + }, + "action": "remove" + }`) + }) + + me, _, err := client.ResourceMilestoneEvents.GetMergeRequestMilestoneEvent(5, 11, 120) + require.NoError(t, err) + + want := &MilestoneEvent{ + ID: 120, + User: &BasicUser{ + ID: 1, + Username: "root", + Name: "Administrator", + State: "active", + AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + WebURL: "http://gitlab.example.com/root", + }, + ResourceType: "MergeRequest", + ResourceID: 11, + Milestone: &Milestone{ + ID: 61, + IID: 9, + ProjectID: 7, + Title: "v1.2", + Description: "Ipsum Lorem", + State: "active", + WebURL: "http://gitlab.example.com:3000/group/project/-/milestones/9", + }, + Action: "remove", + } + require.Equal(t, want, me) +}