Repository: xanzy/go-gitlab
Pull request #1760 (2023-07-20 14:43:20)

Add group repository move
Add group repository storage move endpoints https://docs.gitlab.com/ee/api/group_repository_storage_moves.html


Patch:
diff --git a/gitlab.go b/gitlab.go
index c199188..4f30826 100644
--- a/gitlab.go
+++ b/gitlab.go
@@ -143,6 +143,7 @@ type Client struct {
 	GroupLabels                  *GroupLabelsService
 	GroupMembers                 *GroupMembersService
 	GroupMilestones              *GroupMilestonesService
+	GroupRepositoryStorageMove   *GroupRepositoryStorageMoveService
 	GroupVariables               *GroupVariablesService
 	GroupWikis                   *GroupWikisService
 	Groups                       *GroupsService
@@ -362,6 +363,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
 	c.GroupLabels = &GroupLabelsService{client: c}
 	c.GroupMembers = &GroupMembersService{client: c}
 	c.GroupMilestones = &GroupMilestonesService{client: c}
+	c.GroupRepositoryStorageMove = &GroupRepositoryStorageMoveService{client: c}
 	c.GroupVariables = &GroupVariablesService{client: c}
 	c.GroupWikis = &GroupWikisService{client: c}
 	c.Groups = &GroupsService{client: c}
diff --git a/group_repository_storage_move.go b/group_repository_storage_move.go
new file mode 100644
index 0000000..18951a1
--- /dev/null
+++ b/group_repository_storage_move.go
@@ -0,0 +1,195 @@
+//
+// Copyright 2023, Nick Westbury
+//
+// 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"
+)
+
+// GroupRepositoryStorageMoveService handles communication with the
+// group repositories related methods of the GitLab API.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html
+type GroupRepositoryStorageMoveService struct {
+	client *Client
+}
+
+// GroupRepositoryStorageMove represents the status of a repository move.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html
+type GroupRepositoryStorageMove struct {
+	ID                     int              `json:"id"`
+	CreatedAt              *time.Time       `json:"created_at"`
+	State                  string           `json:"state"`
+	SourceStorageName      string           `json:"source_storage_name"`
+	DestinationStorageName string           `json:"destination_storage_name"`
+	Group                  *RepositoryGroup `json:"group"`
+}
+
+type RepositoryGroup struct {
+	ID     int    `json:"id"`
+	Name   string `json:"name"`
+	WebURL string `json:"web_url"`
+}
+
+// RetrieveAllGroupStorageMovesOptions represents the available
+// RetrieveAllStorageMoves() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#retrieve-all-group-repository-storage-moves
+type RetrieveAllGroupStorageMovesOptions ListOptions
+
+// RetrieveAllStorageMoves retrieves all group repository storage moves
+// accessible by the authenticated user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#retrieve-all-group-repository-storage-moves
+func (g GroupRepositoryStorageMoveService) RetrieveAllStorageMoves(opts RetrieveAllGroupStorageMovesOptions, options ...RequestOptionFunc) ([]*GroupRepositoryStorageMove, *Response, error) {
+	req, err := g.client.NewRequest(http.MethodGet, "group_repository_storage_moves", opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var gsms []*GroupRepositoryStorageMove
+	resp, err := g.client.Do(req, &gsms)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gsms, resp, err
+}
+
+// RetrieveAllStorageMovesForGroup retrieves all repository storage moves for
+// a single group accessible by the authenticated user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-single-group
+func (g GroupRepositoryStorageMoveService) RetrieveAllStorageMovesForGroup(group int, opts RetrieveAllGroupStorageMovesOptions, options ...RequestOptionFunc) ([]*GroupRepositoryStorageMove, *Response, error) {
+	u := fmt.Sprintf("groups/%d/repository_storage_moves", group)
+
+	req, err := g.client.NewRequest(http.MethodGet, u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var gsms []*GroupRepositoryStorageMove
+	resp, err := g.client.Do(req, &gsms)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gsms, resp, err
+}
+
+// GetStorageMove gets a single group repository storage move.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#get-a-single-group-repository-storage-move
+func (g GroupRepositoryStorageMoveService) GetStorageMove(repositoryStorage int, options ...RequestOptionFunc) (*GroupRepositoryStorageMove, *Response, error) {
+	u := fmt.Sprintf("group_repository_storage_moves/%d", repositoryStorage)
+
+	req, err := g.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gsm := new(GroupRepositoryStorageMove)
+	resp, err := g.client.Do(req, gsm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gsm, resp, err
+}
+
+// GetStorageMoveForGroup gets a single repository storage move for a group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#get-a-single-repository-storage-move-for-a-group
+func (g GroupRepositoryStorageMoveService) GetStorageMoveForGroup(group int, repositoryStorage int, options ...RequestOptionFunc) (*GroupRepositoryStorageMove, *Response, error) {
+	u := fmt.Sprintf("groups/%d/repository_storage_moves/%d", group, repositoryStorage)
+
+	req, err := g.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gsm := new(GroupRepositoryStorageMove)
+	resp, err := g.client.Do(req, gsm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gsm, resp, err
+}
+
+// ScheduleStorageMoveForGroupOptions represents the available
+// ScheduleStorageMoveForGroup() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-group
+type ScheduleStorageMoveForGroupOptions struct {
+	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
+}
+
+// ScheduleStorageMoveForGroup schedule a repository to be moved for a group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-group
+func (g GroupRepositoryStorageMoveService) ScheduleStorageMoveForGroup(group int, opts ScheduleStorageMoveForGroupOptions, options ...RequestOptionFunc) (*GroupRepositoryStorageMove, *Response, error) {
+	u := fmt.Sprintf("groups/%d/repository_storage_moves", group)
+
+	req, err := g.client.NewRequest(http.MethodPost, u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gsm := new(GroupRepositoryStorageMove)
+	resp, err := g.client.Do(req, gsm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gsm, resp, err
+}
+
+// ScheduleAllGroupStorageMovesOptions represents the available
+// ScheduleAllStorageMoves() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-repository-storage-moves-for-all-groups-on-a-storage-shard
+type ScheduleAllGroupStorageMovesOptions struct {
+	SourceStorageName      *string `url:"source_storage_name,omitempty" json:"source_storage_name,omitempty"`
+	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
+}
+
+// ScheduleAllStorageMoves schedules all group repositories to be moved.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_repository_storage_moves.html#schedule-repository-storage-moves-for-all-groups-on-a-storage-shard
+func (g GroupRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllGroupStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) {
+	req, err := g.client.NewRequest(http.MethodPost, "group_repository_storage_moves", opts, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return g.client.Do(req, nil)
+}
diff --git a/project_repository_storage_move.go b/project_repository_storage_move.go
index 4911e61..3beecb1 100644
--- a/project_repository_storage_move.go
+++ b/project_repository_storage_move.go
@@ -58,7 +58,7 @@ type RepositoryProject struct {
 // RetrieveAllStorageMoves() options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/project_repository_storage_moves.html
+// https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#retrieve-all-project-repository-storage-moves
 type RetrieveAllProjectStorageMovesOptions ListOptions
 
 // RetrieveAllStorageMoves retrieves all project repository storage moves
@@ -145,14 +145,23 @@ func (p ProjectRepositoryStorageMoveService) GetStorageMoveForProject(project in
 	return psm, resp, err
 }
 
+// ScheduleStorageMoveForProjectOptions represents the available
+// ScheduleStorageMoveForProject() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-project
+type ScheduleStorageMoveForProjectOptions struct {
+	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
+}
+
 // ScheduleStorageMoveForProject schedule a repository to be moved for a project.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-project
-func (p ProjectRepositoryStorageMoveService) ScheduleStorageMoveForProject(project int, options ...RequestOptionFunc) (*ProjectRepositoryStorageMove, *Response, error) {
+func (p ProjectRepositoryStorageMoveService) ScheduleStorageMoveForProject(project int, opts ScheduleStorageMoveForProjectOptions, options ...RequestOptionFunc) (*ProjectRepositoryStorageMove, *Response, error) {
 	u := fmt.Sprintf("projects/%d/repository_storage_moves", project)
 
-	req, err := p.client.NewRequest(http.MethodPost, u, nil, options)
+	req, err := p.client.NewRequest(http.MethodPost, u, opts, options)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -166,12 +175,22 @@ func (p ProjectRepositoryStorageMoveService) ScheduleStorageMoveForProject(proje
 	return psm, resp, err
 }
 
+// ScheduleAllProjectStorageMovesOptions represents the available
+// ScheduleAllStorageMoves() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-repository-storage-moves-for-all-projects-on-a-storage-shard
+type ScheduleAllProjectStorageMovesOptions struct {
+	SourceStorageName      *string `url:"source_storage_name,omitempty" json:"source_storage_name,omitempty"`
+	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
+}
+
 // ScheduleAllStorageMoves schedules all repositories to be moved.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/project_repository_storage_moves.html#schedule-repository-storage-moves-for-all-projects-on-a-storage-shard
-func (p ProjectRepositoryStorageMoveService) ScheduleAllStorageMoves(options ...RequestOptionFunc) (*Response, error) {
-	req, err := p.client.NewRequest(http.MethodPost, "project_repository_storage_moves", nil, options)
+func (p ProjectRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllProjectStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) {
+	req, err := p.client.NewRequest(http.MethodPost, "project_repository_storage_moves", opts, options)
 	if err != nil {
 		return nil, err
 	}
diff --git a/snippet_repository_storage_move.go b/snippet_repository_storage_move.go
index 119051b..00761ec 100644
--- a/snippet_repository_storage_move.go
+++ b/snippet_repository_storage_move.go
@@ -62,7 +62,7 @@ type RepositorySnippet struct {
 // RetrieveAllStorageMoves() options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html
+// https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#retrieve-all-repository-storage-moves-for-a-snippet
 type RetrieveAllSnippetStorageMovesOptions ListOptions
 
 // RetrieveAllStorageMoves retrieves all snippet repository storage moves
@@ -153,7 +153,7 @@ func (s SnippetRepositoryStorageMoveService) GetStorageMoveForSnippet(snippet in
 // ScheduleStorageMoveForSnippet() options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html
+// https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-a-repository-storage-move-for-a-snippet
 type ScheduleStorageMoveForSnippetOptions struct {
 	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
 }
@@ -179,12 +179,12 @@ func (s SnippetRepositoryStorageMoveService) ScheduleStorageMoveForSnippet(snipp
 	return ssm, resp, err
 }
 
-// ScheduleAllStorageMovesOptions represents the available
+// ScheduleAllSnippetStorageMovesOptions represents the available
 // ScheduleAllStorageMoves() options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html
-type ScheduleAllStorageMovesOptions struct {
+// https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-repository-storage-moves-for-all-snippets-on-a-storage-shard
+type ScheduleAllSnippetStorageMovesOptions struct {
 	SourceStorageName      *string `url:"source_storage_name,omitempty" json:"source_storage_name,omitempty"`
 	DestinationStorageName *string `url:"destination_storage_name,omitempty" json:"destination_storage_name,omitempty"`
 }
@@ -193,7 +193,7 @@ type ScheduleAllStorageMovesOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/snippet_repository_storage_moves.html#schedule-repository-storage-moves-for-all-snippets-on-a-storage-shard
-func (s SnippetRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) {
+func (s SnippetRepositoryStorageMoveService) ScheduleAllStorageMoves(opts ScheduleAllSnippetStorageMovesOptions, options ...RequestOptionFunc) (*Response, error) {
 	req, err := s.client.NewRequest(http.MethodPost, "snippet_repository_storage_moves", opts, options)
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/group_repository_storage_move_test.go b/group_repository_storage_move_test.go
new file mode 100644
index 0000000..36fe7ae
--- /dev/null
+++ b/group_repository_storage_move_test.go
@@ -0,0 +1,229 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestGroupRepositoryStorageMove_RetrieveAllGroupStorageMoves(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/group_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `[
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "group":{
+			"id":283,
+			"Name":"Test Group",
+			"web_url": "https://gitlab.example.com/groups/test_group"
+		  }
+		},
+		{
+		  "id":122,
+		  "state":"finished",
+		  "group":{
+			"id":284,
+			"Name":"Test Group 2",
+			"web_url": "https://gitlab.example.com/groups/test_group_2"
+		  }
+		}]`)
+	})
+
+	opts := RetrieveAllGroupStorageMovesOptions{Page: 1, PerPage: 2}
+
+	gsms, _, err := client.GroupRepositoryStorageMove.RetrieveAllStorageMoves(opts)
+	require.NoError(t, err)
+
+	want := []*GroupRepositoryStorageMove{
+		{
+			ID:    123,
+			State: "scheduled",
+			Group: &RepositoryGroup{
+				ID:     283,
+				Name:   "Test Group",
+				WebURL: "https://gitlab.example.com/groups/test_group",
+			},
+		},
+		{
+			ID:    122,
+			State: "finished",
+			Group: &RepositoryGroup{
+				ID:     284,
+				Name:   "Test Group 2",
+				WebURL: "https://gitlab.example.com/groups/test_group_2",
+			},
+		},
+	}
+	require.Equal(t, want, gsms)
+}
+
+func TestGroupRepositoryStorageMove_RetrieveAllStorageMovesForGroup(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/groups/283/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `[
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "group":{
+			"id":283,
+			"Name":"Test Group",
+			"web_url": "https://gitlab.example.com/groups/test_group"
+		  }
+		},
+		{
+		  "id":122,
+		  "state":"finished",
+		  "group":{
+			"id":283,
+			"Name":"Test Group",
+			"web_url": "https://gitlab.example.com/groups/test_group"
+		  }
+		}]`)
+	})
+
+	opts := RetrieveAllGroupStorageMovesOptions{Page: 1, PerPage: 2}
+
+	gsms, _, err := client.GroupRepositoryStorageMove.RetrieveAllStorageMovesForGroup(283, opts)
+	require.NoError(t, err)
+
+	want := []*GroupRepositoryStorageMove{
+		{
+			ID:    123,
+			State: "scheduled",
+			Group: &RepositoryGroup{
+				ID:     283,
+				Name:   "Test Group",
+				WebURL: "https://gitlab.example.com/groups/test_group",
+			},
+		},
+		{
+			ID:    122,
+			State: "finished",
+			Group: &RepositoryGroup{
+				ID:     283,
+				Name:   "Test Group",
+				WebURL: "https://gitlab.example.com/groups/test_group",
+			},
+		},
+	}
+	require.Equal(t, want, gsms)
+}
+
+func TestGroupRepositoryStorageMove_GetStorageMove(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/group_repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "group":{
+			"id":283,
+			"Name":"Test Group",
+			"web_url": "https://gitlab.example.com/groups/test_group"
+		  }
+		}`)
+	})
+
+	gsm, _, err := client.GroupRepositoryStorageMove.GetStorageMove(123)
+	require.NoError(t, err)
+
+	want := &GroupRepositoryStorageMove{
+		ID:    123,
+		State: "scheduled",
+		Group: &RepositoryGroup{
+			ID:     283,
+			Name:   "Test Group",
+			WebURL: "https://gitlab.example.com/groups/test_group",
+		},
+	}
+	require.Equal(t, want, gsm)
+}
+
+func TestGroupRepositoryStorageMove_GetStorageMoveForGroup(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/groups/283/repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "group":{
+			"id":283,
+			"Name":"Test Group",
+			"web_url": "https://gitlab.example.com/groups/test_group"
+		  }
+		}`)
+	})
+
+	gsm, _, err := client.GroupRepositoryStorageMove.GetStorageMoveForGroup(283, 123)
+	require.NoError(t, err)
+
+	want := &GroupRepositoryStorageMove{
+		ID:    123,
+		State: "scheduled",
+		Group: &RepositoryGroup{
+			ID:     283,
+			Name:   "Test Group",
+			WebURL: "https://gitlab.example.com/groups/test_group",
+		},
+	}
+	require.Equal(t, want, gsm)
+}
+
+func TestGroupRepositoryStorageMove_ScheduleStorageMoveForGroup(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/groups/283/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		fmt.Fprint(w, `
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "group":{
+			"id":283,
+			"Name":"Test Group",
+			"web_url": "https://gitlab.example.com/groups/test_group"
+		  }
+		}`)
+	})
+
+	ssm, _, err := client.GroupRepositoryStorageMove.ScheduleStorageMoveForGroup(283, ScheduleStorageMoveForGroupOptions{})
+	require.NoError(t, err)
+
+	want := &GroupRepositoryStorageMove{
+		ID:    123,
+		State: "scheduled",
+		Group: &RepositoryGroup{
+			ID:     283,
+			Name:   "Test Group",
+			WebURL: "https://gitlab.example.com/groups/test_group",
+		},
+	}
+	require.Equal(t, want, ssm)
+}
+
+func TestGroupRepositoryStorageMove_ScheduleAllStorageMoves(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/group_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		fmt.Fprint(w, `{"message": "202 Accepted"}`)
+	})
+
+	_, err := client.GroupRepositoryStorageMove.ScheduleAllStorageMoves(
+		ScheduleAllGroupStorageMovesOptions{
+			SourceStorageName: String("default"),
+		},
+	)
+	require.NoError(t, err)
+}
diff --git a/project_repository_storage_move_test.go b/project_repository_storage_move_test.go
new file mode 100644
index 0000000..81f7edc
--- /dev/null
+++ b/project_repository_storage_move_test.go
@@ -0,0 +1,215 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestProjectRepositoryStorageMove_RetrieveAllProjectStorageMoves(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/project_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `[
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "project":{
+			"id":1,
+			"name":"Test Project"
+		  }
+		},
+		{
+		  "id":122,
+		  "state":"finished",
+		  "project":{
+			"id":2,
+			"name":"Test Project 2"
+		  }
+		}]`)
+	})
+
+	opts := RetrieveAllProjectStorageMovesOptions{Page: 1, PerPage: 2}
+
+	ssms, _, err := client.ProjectRepositoryStorageMove.RetrieveAllStorageMoves(opts)
+	require.NoError(t, err)
+
+	want := []*ProjectRepositoryStorageMove{
+		{
+			ID:    123,
+			State: "scheduled",
+			Project: &RepositoryProject{
+				ID:   1,
+				Name: "Test Project",
+			},
+		},
+		{
+			ID:    122,
+			State: "finished",
+			Project: &RepositoryProject{
+				ID:   2,
+				Name: "Test Project 2",
+			},
+		},
+	}
+	require.Equal(t, want, ssms)
+}
+
+func TestProjectRepositoryStorageMove_RetrieveAllStorageMovesForProject(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `[
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "project":{
+			"id":1,
+			"name":"Test Project"
+		  }
+		},
+		{
+		  "id":122,
+		  "state":"finished",
+		  "project":{
+			"id":1,
+			"name":"Test Project"
+		  }
+		}]`)
+	})
+
+	opts := RetrieveAllProjectStorageMovesOptions{Page: 1, PerPage: 2}
+
+	ssms, _, err := client.ProjectRepositoryStorageMove.RetrieveAllStorageMovesForProject(1, opts)
+	require.NoError(t, err)
+
+	want := []*ProjectRepositoryStorageMove{
+		{
+			ID:    123,
+			State: "scheduled",
+			Project: &RepositoryProject{
+				ID:   1,
+				Name: "Test Project",
+			},
+		},
+		{
+			ID:    122,
+			State: "finished",
+			Project: &RepositoryProject{
+				ID:   1,
+				Name: "Test Project",
+			},
+		},
+	}
+	require.Equal(t, want, ssms)
+}
+
+func TestProjectRepositoryStorageMove_GetStorageMove(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/project_repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "project":{
+			"id":1,
+			"name":"Test Project"
+		  }
+		}`)
+	})
+
+	ssm, _, err := client.ProjectRepositoryStorageMove.GetStorageMove(123)
+	require.NoError(t, err)
+
+	want := &ProjectRepositoryStorageMove{
+		ID:    123,
+		State: "scheduled",
+		Project: &RepositoryProject{
+			ID:   1,
+			Name: "Test Project",
+		},
+	}
+	require.Equal(t, want, ssm)
+}
+
+func TestProjectRepositoryStorageMove_GetStorageMoveForProject(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/repository_storage_moves/123", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `
+		{
+		  "id":123,
+		  "state":"scheduled",
+		  "project":{
+			"id":1,
+			"name":"Test Project"
+		  }
+		}`)
+	})
+
+	ssm, _, err := client.ProjectRepositoryStorageMove.GetStorageMoveForProject(1, 123)
+	require.NoError(t, err)
+
+	want := &ProjectRepositoryStorageMove{
+		ID:    123,
+		State: "scheduled",
+		Project: &RepositoryProject{
+			ID:   1,
+			Name: "Test Project",
+		},
+	}
+	require.Equal(t, want, ssm)
+}
+
+func TestProjectRepositoryStorageMove_ScheduleStorageMoveForProject(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		fmt.Fprint(w, `
+		{
+		  "id":124,
+		  "state":"scheduled",
+		  "project":{
+			"id":1,
+			"name":"Test Project"
+		  }
+		}`)
+	})
+
+	ssm, _, err := client.ProjectRepositoryStorageMove.ScheduleStorageMoveForProject(1, ScheduleStorageMoveForProjectOptions{})
+	require.NoError(t, err)
+
+	want := &ProjectRepositoryStorageMove{
+		ID:    124,
+		State: "scheduled",
+		Project: &RepositoryProject{
+			ID:   1,
+			Name: "Test Project",
+		},
+	}
+	require.Equal(t, want, ssm)
+}
+
+func TestProjectRepositoryStorageMove_ScheduleAllStorageMoves(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/project_repository_storage_moves", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		fmt.Fprint(w, `{"message": "202 Accepted"}`)
+	})
+
+	_, err := client.ProjectRepositoryStorageMove.ScheduleAllStorageMoves(
+		ScheduleAllProjectStorageMovesOptions{
+			SourceStorageName: String("default"),
+		},
+	)
+	require.NoError(t, err)
+}
diff --git a/snippet_repository_storage_move_test.go b/snippet_repository_storage_move_test.go
index 2d1a446..84e2f3d 100644
--- a/snippet_repository_storage_move_test.go
+++ b/snippet_repository_storage_move_test.go
@@ -207,7 +207,7 @@ func TestSnippetRepositoryStorageMove_ScheduleAllStorageMoves(t *testing.T) {
 	})
 
 	_, err := client.SnippetRepositoryStorageMove.ScheduleAllStorageMoves(
-		ScheduleAllStorageMovesOptions{
+		ScheduleAllSnippetStorageMovesOptions{
 			SourceStorageName: String("default"),
 		},
 	)