Commit 9e387616 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'storage_move_api' into 'master'

Readonly Storage Move API

Closes #210041

See merge request gitlab-org/gitlab!31285
parents 7ffdba13 a5caae33
......@@ -52,4 +52,7 @@ class ProjectRepositoryStorageMove < ApplicationRecord
state :finished, value: 4
state :failed, value: 5
end
scope :order_created_at_desc, -> { order(created_at: :desc) }
scope :with_projects, -> { includes(project: :route) }
end
---
title: Read only storage move API
merge_request: 31285
author:
type: added
......@@ -139,6 +139,7 @@ The following API resources are available outside of project and group contexts
| [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) |
| [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) |
| [Projects](projects.md) | `/users/:id/projects` (also available for projects) |
| [Project Repository Storage Moves](project_repository_storage_moves.md) | `/project_repository_storage_moves` |
| [Runners](runners.md) | `/runners` (also available for projects) |
| [Search](search.md) | `/search` (also available for groups and projects) |
| [Settings](settings.md) **(CORE ONLY)** | `/application/settings` |
......
# Project repository storage move API
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31285) in GitLab 13.0.
Project repository storage can be moved. To retrieve project repository storage moves using the API, you must [authenticate yourself](README.md#authentication) as an administrator.
## Retrieve all project repository storage moves
```text
GET /project_repository_storage_moves
```
By default, `GET` requests return 20 results at a time because the API results
are [paginated](README.md#pagination).
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://primary.example.com/api/v4/project_repository_storage_moves'
```
Example response:
```json
[
{
"id": 1,
"created_at": "2020-05-07T04:27:17.234Z",
"state": "scheduled",
"source_storage_name": "default",
"destination_storage_name": "storage2",
"project": {
"id": 1,
"description": null,
"name": "project1",
"name_with_namespace": "John Doe2 / project1",
"path": "project1",
"path_with_namespace": "namespace1/project1",
"created_at": "2020-05-07T04:27:17.016Z"
}
]
```
## Get a single project repository storage move
```text
GET /project_repository_storage_moves/:id
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of the project repository storage move |
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://primary.example.com/api/v4/project_repository_storage_moves/1'
```
Example response:
```json
{
"id": 1,
"created_at": "2020-05-07T04:27:17.234Z",
"state": "scheduled",
"source_storage_name": "default",
"destination_storage_name": "storage2",
"project": {
"id": 1,
"description": null,
"name": "project1",
"name_with_namespace": "John Doe2 / project1",
"path": "project1",
"path_with_namespace": "namespace1/project1",
"created_at": "2020-05-07T04:27:17.016Z"
}
```
......@@ -180,6 +180,7 @@ module API
mount ::API::ProjectImport
mount ::API::ProjectHooks
mount ::API::ProjectMilestones
mount ::API::ProjectRepositoryStorageMoves
mount ::API::Projects
mount ::API::ProjectSnapshots
mount ::API::ProjectSnippets
......
# frozen_string_literal: true
module API
module Entities
class ProjectRepositoryStorageMove < Grape::Entity
expose :id
expose :created_at
expose :human_state_name, as: :state
expose :source_storage_name
expose :destination_storage_name
expose :project, using: Entities::ProjectIdentity
end
end
end
# frozen_string_literal: true
module API
class ProjectRepositoryStorageMoves < Grape::API
include PaginationParams
before { authenticated_as_admin! }
resource :project_repository_storage_moves do
desc 'Get a list of all project repository storage moves' do
detail 'This feature was introduced in GitLab 13.0.'
success Entities::ProjectRepositoryStorageMove
end
params do
use :pagination
end
get do
storage_moves = ProjectRepositoryStorageMove.with_projects.order_created_at_desc
present paginate(storage_moves), with: Entities::ProjectRepositoryStorageMove, current_user: current_user
end
desc 'Get a project repository storage move' do
detail 'This feature was introduced in GitLab 13.0.'
success Entities::ProjectRepositoryStorageMove
end
get ':id' do
storage_move = ProjectRepositoryStorageMove.find(params[:id])
present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
end
end
end
end
{
"type": "object",
"required": [
"id",
"created_at",
"state",
"source_storage_name",
"destination_storage_name",
"project"
],
"properties" : {
"id": { "type": "integer" },
"created_at": { "type": "date" },
"state": { "type": "string" },
"source_storage_name": { "type": "string" },
"destination_storage_name": { "type": "string" },
"project": { "type": "object" }
},
"additionalProperties": false
}
{
"type": "array",
"items": {
"$ref": "./project_repository_storage_move.json"
}
}
# frozen_string_literal: true
require 'spec_helper'
describe API::Entities::ProjectRepositoryStorageMove do
describe '#as_json' do
subject { entity.as_json }
let(:storage_move) { build(:project_repository_storage_move, :scheduled, destination_storage_name: 'test_second_storage') }
let(:entity) { described_class.new(storage_move) }
it 'includes basic fields' do
is_expected.to include(
state: 'scheduled',
source_storage_name: 'default',
destination_storage_name: 'test_second_storage',
project: a_kind_of(Hash)
)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe API::ProjectRepositoryStorageMoves do
include AccessMatchersForRequest
let(:user) { create(:admin) }
let!(:storage_move) { create(:project_repository_storage_move, :scheduled) }
describe 'GET /project_repository_storage_moves' do
def get_project_repository_storage_moves
get api('/project_repository_storage_moves', user)
end
it 'returns project repository storage moves' do
get_project_repository_storage_moves
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(response).to match_response_schema('public_api/v4/project_repository_storage_moves')
expect(json_response.size).to eq(1)
expect(json_response.first['id']).to eq(storage_move.id)
expect(json_response.first['state']).to eq(storage_move.human_state_name)
end
it 'avoids N+1 queries', :request_store do
# prevent `let` from polluting the control
get_project_repository_storage_moves
control = ActiveRecord::QueryRecorder.new { get_project_repository_storage_moves }
create(:project_repository_storage_move, :scheduled)
expect { get_project_repository_storage_moves }.not_to exceed_query_limit(control)
end
it 'returns the most recently created first' do
storage_move_oldest = create(:project_repository_storage_move, :scheduled, created_at: 2.days.ago)
storage_move_middle = create(:project_repository_storage_move, :scheduled, created_at: 1.day.ago)
get api('/project_repository_storage_moves', user)
json_ids = json_response.map {|storage_move| storage_move['id'] }
expect(json_ids).to eq([
storage_move.id,
storage_move_middle.id,
storage_move_oldest.id
])
end
describe 'permissions' do
it { expect { get_project_repository_storage_moves }.to be_allowed_for(:admin) }
it { expect { get_project_repository_storage_moves }.to be_denied_for(:user) }
end
end
describe 'GET /project_repository_storage_moves/:id' do
let(:project_repository_storage_move_id) { storage_move.id }
def get_project_repository_storage_move
get api("/project_repository_storage_moves/#{project_repository_storage_move_id}", user)
end
it 'returns a project repository storage move' do
get_project_repository_storage_move
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/project_repository_storage_move')
expect(json_response['id']).to eq(storage_move.id)
expect(json_response['state']).to eq(storage_move.human_state_name)
end
context 'non-existent project repository storage move' do
let(:project_repository_storage_move_id) { non_existing_record_id }
it 'returns not found' do
get_project_repository_storage_move
expect(response).to have_gitlab_http_status(:not_found)
end
end
describe 'permissions' do
it { expect { get_project_repository_storage_move }.to be_allowed_for(:admin) }
it { expect { get_project_repository_storage_move }.to be_denied_for(:user) }
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment