Commit af68897a authored by Robert Speicher's avatar Robert Speicher

Merge branch 'api-project-upload' into 'master'

Add API project upload endpoint

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/4317

See merge request !2329
parents 6467448a 0614793b
...@@ -66,7 +66,7 @@ class @DropzoneInput ...@@ -66,7 +66,7 @@ class @DropzoneInput
success: (header, response) -> success: (header, response) ->
child = $(dropzone[0]).children("textarea") child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n" $(child).val $(child).val() + response.link.markdown + "\n"
return return
error: (temp, errorMessage) -> error: (temp, errorMessage) ->
...@@ -99,11 +99,6 @@ class @DropzoneInput ...@@ -99,11 +99,6 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea") child = $(dropzone[0]).children("textarea")
formatLink = (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
text
handlePaste = (event) -> handlePaste = (event) ->
pasteEvent = event.originalEvent pasteEvent = event.originalEvent
if pasteEvent.clipboardData and pasteEvent.clipboardData.items if pasteEvent.clipboardData and pasteEvent.clipboardData.items
...@@ -162,7 +157,7 @@ class @DropzoneInput ...@@ -162,7 +157,7 @@ class @DropzoneInput
closeAlertMessage() closeAlertMessage()
success: (e, textStatus, response) -> success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link)) insertToTextArea(filename, response.responseJSON.link.markdown)
error: (response) -> error: (response) ->
showError(response.responseJSON.message) showError(response.responseJSON.message)
...@@ -202,8 +197,3 @@ class @DropzoneInput ...@@ -202,8 +197,3 @@ class @DropzoneInput
e.preventDefault() e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click() $(@).closest('.gfm-form').find('.div-dropzone').click()
return return
formatLink: (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
text
...@@ -16,13 +16,7 @@ module Projects ...@@ -16,13 +16,7 @@ module Projects
uploader.download!(@url) uploader.download!(@url)
uploader.store! uploader.store!
filename = uploader.image? ? uploader.file.basename : uploader.file.filename uploader.to_h
{
'alt' => filename,
'url' => uploader.secure_url,
'is_image' => uploader.image?
}
end end
private private
......
...@@ -10,13 +10,7 @@ module Projects ...@@ -10,13 +10,7 @@ module Projects
uploader = FileUploader.new(@project) uploader = FileUploader.new(@project)
uploader.store!(@file) uploader.store!(@file)
filename = uploader.image? ? uploader.file.basename : uploader.file.filename uploader.to_h
{
alt: filename,
url: uploader.secure_url,
is_image: uploader.image?
}
end end
private private
......
...@@ -30,4 +30,19 @@ class FileUploader < CarrierWave::Uploader::Base ...@@ -30,4 +30,19 @@ class FileUploader < CarrierWave::Uploader::Base
def secure_url def secure_url
File.join("/uploads", @secret, file.filename) File.join("/uploads", @secret, file.filename)
end end
def to_h
filename = image? ? self.file.basename : self.file.filename
escaped_filename = filename.gsub("]", "\\]")
markdown = "[#{escaped_filename}](#{self.secure_url})"
markdown.prepend("!") if image?
{
alt: filename,
url: self.secure_url,
is_image: image?,
markdown: markdown
}
end
end end
...@@ -482,6 +482,34 @@ Parameters: ...@@ -482,6 +482,34 @@ Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
## Uploads
### Upload a file
Uploads a file to the specified project to be used in an issue or merge request description, or a comment.
```
POST /projects/:id/uploads
```
Parameters:
- `id` (required) - The ID of the project
- `file` (required) - The file to be uploaded
```json
{
"alt": "dk",
"url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
"is_image": true,
"markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
}
```
**Note**: The returned `url` is relative to the project path.
In Markdown contexts, the link is automatically expanded when the format in `markdown` is used.
## Team members ## Team members
### List project team members ### List project team members
......
...@@ -269,7 +269,7 @@ module API ...@@ -269,7 +269,7 @@ module API
# Remove a forked_from relationship # Remove a forked_from relationship
# #
# Parameters: # Parameters:
# id: (required) - The ID of the project being marked as a fork # id: (required) - The ID of the project being marked as a fork
# Example Request: # Example Request:
# DELETE /projects/:id/fork # DELETE /projects/:id/fork
delete ":id/fork" do delete ":id/fork" do
...@@ -278,6 +278,16 @@ module API ...@@ -278,6 +278,16 @@ module API
user_project.forked_project_link.destroy user_project.forked_project_link.destroy
end end
end end
# Upload a file
#
# Parameters:
# id: (required) - The ID of the project
# file: (required) - The file to be uploaded
post ":id/uploads" do
::Projects::UploadService.new(user_project, params[:file]).execute
end
# search for projects current_user has access to # search for projects current_user has access to
# #
# Parameters: # Parameters:
......
...@@ -74,7 +74,7 @@ module Gitlab ...@@ -74,7 +74,7 @@ module Gitlab
def sent_notification def sent_notification
return nil unless reply_key return nil unless reply_key
SentNotification.for(reply_key) SentNotification.for(reply_key)
end end
...@@ -82,10 +82,7 @@ module Gitlab ...@@ -82,10 +82,7 @@ module Gitlab
attachments = Email::AttachmentUploader.new(message).execute(sent_notification.project) attachments = Email::AttachmentUploader.new(message).execute(sent_notification.project)
attachments.each do |link| attachments.each do |link|
text = "[#{link[:alt]}](#{link[:url]})" reply << "\n\n#{link[:markdown]}"
text.prepend("!") if link[:is_image]
reply << "\n\n#{text}"
end end
reply reply
......
...@@ -232,9 +232,7 @@ module Gitlab ...@@ -232,9 +232,7 @@ module Gitlab
return nil if res.nil? return nil if res.nil?
text = "[#{res['alt']}](#{res['url']})" res[:markdown]
text = "!#{text}" if res['is_image']
text
end end
def build_attachment_url(rel_url) def build_attachment_url(rel_url)
......
...@@ -42,7 +42,7 @@ describe Gitlab::Email::Receiver, lib: true do ...@@ -42,7 +42,7 @@ describe Gitlab::Email::Receiver, lib: true do
context "when the email was auto generated" do context "when the email was auto generated" do
let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } let!(:reply_key) { '636ca428858779856c226bb145ef4fad' }
let!(:email_raw) { fixture_file("emails/auto_reply.eml") } let!(:email_raw) { fixture_file("emails/auto_reply.eml") }
it "raises an AutoGeneratedEmailError" do it "raises an AutoGeneratedEmailError" do
expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::AutoGeneratedEmailError) expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::AutoGeneratedEmailError)
end end
...@@ -90,7 +90,7 @@ describe Gitlab::Email::Receiver, lib: true do ...@@ -90,7 +90,7 @@ describe Gitlab::Email::Receiver, lib: true do
context "when the reply is blank" do context "when the reply is blank" do
let!(:email_raw) { fixture_file("emails/no_content_reply.eml") } let!(:email_raw) { fixture_file("emails/no_content_reply.eml") }
it "raises an EmptyEmailError" do it "raises an EmptyEmailError" do
expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::EmptyEmailError) expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::EmptyEmailError)
end end
...@@ -107,13 +107,16 @@ describe Gitlab::Email::Receiver, lib: true do ...@@ -107,13 +107,16 @@ describe Gitlab::Email::Receiver, lib: true do
end end
context "when everything is fine" do context "when everything is fine" do
let(:markdown) { "![image](uploads/image.png)" }
before do before do
allow_any_instance_of(Gitlab::Email::AttachmentUploader).to receive(:execute).and_return( allow_any_instance_of(Gitlab::Email::AttachmentUploader).to receive(:execute).and_return(
[ [
{ {
url: "uploads/image.png", url: "uploads/image.png",
is_image: true, is_image: true,
alt: "image" alt: "image",
markdown: markdown
} }
] ]
) )
...@@ -132,7 +135,7 @@ describe Gitlab::Email::Receiver, lib: true do ...@@ -132,7 +135,7 @@ describe Gitlab::Email::Receiver, lib: true do
note = noteable.notes.last note = noteable.notes.last
expect(note.note).to include("![image](uploads/image.png)") expect(note.note).to include(markdown)
end end
end end
end end
...@@ -353,6 +353,20 @@ describe API::API, api: true do ...@@ -353,6 +353,20 @@ describe API::API, api: true do
end end
end end
describe "POST /projects/:id/uploads" do
before { project }
it "uploads the file and returns its info" do
post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
expect(response.status).to be(201)
expect(json_response['alt']).to eq("dk")
expect(json_response['url']).to start_with("/uploads/")
expect(json_response['url']).to end_with("/dk.png")
expect(json_response['is_image']).to eq(true)
end
end
describe 'GET /projects/:id' do describe 'GET /projects/:id' do
before { project } before { project }
before { project_member } before { project_member }
......
...@@ -33,12 +33,12 @@ describe Projects::DownloadService, services: true do ...@@ -33,12 +33,12 @@ describe Projects::DownloadService, services: true do
@link_to_file = download_file(@project, url) @link_to_file = download_file(@project, url)
end end
it { expect(@link_to_file).to have_key('alt') } it { expect(@link_to_file).to have_key(:alt) }
it { expect(@link_to_file).to have_key('url') } it { expect(@link_to_file).to have_key(:url) }
it { expect(@link_to_file).to have_key('is_image') } it { expect(@link_to_file).to have_key(:is_image) }
it { expect(@link_to_file['is_image']).to be true } it { expect(@link_to_file[:is_image]).to be true }
it { expect(@link_to_file['url']).to match('rails_sample.jpg') } it { expect(@link_to_file[:url]).to match('rails_sample.jpg') }
it { expect(@link_to_file['alt']).to eq('rails_sample') } it { expect(@link_to_file[:alt]).to eq('rails_sample') }
end end
context 'a txt file' do context 'a txt file' do
...@@ -47,12 +47,12 @@ describe Projects::DownloadService, services: true do ...@@ -47,12 +47,12 @@ describe Projects::DownloadService, services: true do
@link_to_file = download_file(@project, url) @link_to_file = download_file(@project, url)
end end
it { expect(@link_to_file).to have_key('alt') } it { expect(@link_to_file).to have_key(:alt) }
it { expect(@link_to_file).to have_key('url') } it { expect(@link_to_file).to have_key(:url) }
it { expect(@link_to_file).to have_key('is_image') } it { expect(@link_to_file).to have_key(:is_image) }
it { expect(@link_to_file['is_image']).to be false } it { expect(@link_to_file[:is_image]).to be false }
it { expect(@link_to_file['url']).to match('doc_sample.txt') } it { expect(@link_to_file[:url]).to match('doc_sample.txt') }
it { expect(@link_to_file['alt']).to eq('doc_sample.txt') } it { expect(@link_to_file[:alt]).to eq('doc_sample.txt') }
end end
end 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