diff --git a/doc/api/projects.md b/doc/api/projects.md index d53c515bfbe1a9aac016afd94162c84d18075c6f..d20b359582b65afca43043fe03ecdcb04a05649c 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -2215,7 +2215,9 @@ POST /projects/:id/restore |-----------|----------------|------------------------|-------------| | `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). | -## Upload a file +## File uploads + +### Upload a file Uploads a file to the specified project to be used in an issue or merge request description, or a comment. GitLab versions 14.0 and later @@ -2255,7 +2257,7 @@ The returned `url` is relative to the project path. The returned `full_path` is the absolute path to the file. In Markdown contexts, the link is expanded when the format in `markdown` is used. -### Max attachment size enforcement +#### Max attachment size enforcement > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57250) in GitLab 13.11. @@ -2291,6 +2293,43 @@ To disable this enforcement: Feature.disable(:enforce_max_attachment_size_upload_api) ``` +### List uploaded files + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111444) in GitLab 15.9 + +Lists files in the specified project that may be used in an issue or merge request description, or a comment. + +```plaintext +GET /projects/:id/uploads +``` + +| Attribute | Type | Required | Description | +|-----------|----------------|------------------------|-------------| +| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). | + +Example using curl: + +```shell +curl --request GET --header "PRIVATE-TOKEN: " \ + "https://gitlab.example.com/api/v4/projects/5/uploads" +``` + +Returned object: + +```json +[ + { + "alt": "dk", + "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", + "full_path": "/namespace1/project1/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", + "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" + } +] +``` + +The returned `url` is relative to the project path. The returned `full_path` is the absolute path to the file. +In Markdown contexts, the link is expanded when the format in `markdown` is used. + ## Upload a project avatar Uploads an avatar to the specified project. diff --git a/lib/api/entities/upload.rb b/lib/api/entities/upload.rb new file mode 100644 index 0000000000000000000000000000000000000000..1513aa6488853edd7e4cc1edb33809e930bdebf2 --- /dev/null +++ b/lib/api/entities/upload.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module API + module Entities + class Upload < Grape::Entity + expose :path + end + end +end + diff --git a/lib/api/projects.rb b/lib/api/projects.rb index e1e756d8e8c24900492182debb7fd778f494826c..e1ffa63637423e45bf34c5d455e1261985bb2283 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -808,6 +808,19 @@ def add_import_params(params) present upload, with: Entities::ProjectUpload end + desc 'List uploaded files' do + success code: 200, model: Entities::ProjectUpload + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[projects] + end + get ":id/uploads", feature_category: :not_owned do # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned + upload = user_project.uploads + + present paginate(upload), with: Entities::Upload + end + desc 'Get the users list of a project' do success code: 200, model: Entities::UserBasic failure [ diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 1c8aec878eaa9cd192e093d25e2a0887738cfe7c..6b0c826ce1fd91b05353102df8cd8532f8a7fdef 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -2227,6 +2227,32 @@ end end + describe "GET /projects/:id/uploads" do + + context 'when unauthenticated' do + it 'does not return uploads for private projects' do + private_project = create(:project, :private) + get api("/projects/#{private_project.id}/uploads") + + expect(response).to have_gitlab_http_status(:not_found) + end + + context 'for public projects' do + it 'successful upload list response' do + public_project = create(:project, :public) + create(:upload, :attachment_upload, :with_file, model: public_project) + get api("/projects/#{public_project.id}/uploads") + + binding.pry + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + end + end + end + end + describe 'GET /project/:id/share_locations' do let_it_be(:root_group) { create(:group, :public, name: 'root group') } let_it_be(:project_group1) { create(:group, :public, parent: root_group, name: 'group1') }