Support descriptions for snippets
This commit is contained in:
parent
8039b9c3c6
commit
4464c22d6d
|
|
@ -218,6 +218,16 @@ import ShortcutsBlob from './shortcuts_blob';
|
||||||
new gl.GLForm($('.tag-form'));
|
new gl.GLForm($('.tag-form'));
|
||||||
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
|
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
|
||||||
break;
|
break;
|
||||||
|
case 'projects:snippets:new':
|
||||||
|
case 'projects:snippets:edit':
|
||||||
|
case 'projects:snippets:create':
|
||||||
|
case 'projects:snippets:update':
|
||||||
|
case 'snippets:new':
|
||||||
|
case 'snippets:edit':
|
||||||
|
case 'snippets:create':
|
||||||
|
case 'snippets:update':
|
||||||
|
new gl.GLForm($('.snippet-form'));
|
||||||
|
break;
|
||||||
case 'projects:releases:edit':
|
case 'projects:releases:edit':
|
||||||
new ZenMode();
|
new ZenMode();
|
||||||
new gl.GLForm($('.release-form'));
|
new gl.GLForm($('.release-form'));
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import './preview_markdown';
|
||||||
|
|
||||||
window.DropzoneInput = (function() {
|
window.DropzoneInput = (function() {
|
||||||
function DropzoneInput(form) {
|
function DropzoneInput(form) {
|
||||||
var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile;
|
var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile, addFileToForm;
|
||||||
Dropzone.autoDiscover = false;
|
Dropzone.autoDiscover = false;
|
||||||
divHover = '<div class="div-dropzone-hover"></div>';
|
divHover = '<div class="div-dropzone-hover"></div>';
|
||||||
iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
|
iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
|
||||||
|
|
@ -71,6 +71,7 @@ window.DropzoneInput = (function() {
|
||||||
pasteText(response.link.markdown, shouldPad);
|
pasteText(response.link.markdown, shouldPad);
|
||||||
// Show 'Attach a file' link only when all files have been uploaded.
|
// Show 'Attach a file' link only when all files have been uploaded.
|
||||||
if (!processingFileCount) $attachButton.removeClass('hide');
|
if (!processingFileCount) $attachButton.removeClass('hide');
|
||||||
|
addFileToForm(response.link.url);
|
||||||
},
|
},
|
||||||
error: function(file, errorMessage = 'Attaching the file failed.', xhr) {
|
error: function(file, errorMessage = 'Attaching the file failed.', xhr) {
|
||||||
// If 'error' event is fired by dropzone, the second parameter is error message.
|
// If 'error' event is fired by dropzone, the second parameter is error message.
|
||||||
|
|
@ -197,6 +198,10 @@ window.DropzoneInput = (function() {
|
||||||
return formTextarea.trigger('input');
|
return formTextarea.trigger('input');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addFileToForm = function(path) {
|
||||||
|
$(form).append('<input type="hidden" name="files[]" value="' + path + '">');
|
||||||
|
};
|
||||||
|
|
||||||
getFilename = function(e) {
|
getFilename = function(e) {
|
||||||
var value;
|
var value;
|
||||||
if (window.clipboardData && window.clipboardData.getData) {
|
if (window.clipboardData && window.clipboardData.getData) {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,6 @@ class Projects::SnippetsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def snippet_params
|
def snippet_params
|
||||||
params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
|
params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ class SnippetsController < ApplicationController
|
||||||
|
|
||||||
@snippet = CreateSnippetService.new(nil, current_user, create_params).execute
|
@snippet = CreateSnippetService.new(nil, current_user, create_params).execute
|
||||||
|
|
||||||
|
move_temporary_files if params[:files]
|
||||||
|
|
||||||
recaptcha_check_with_fallback { render :new }
|
recaptcha_check_with_fallback { render :new }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -124,6 +126,12 @@ class SnippetsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def snippet_params
|
def snippet_params
|
||||||
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
|
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_temporary_files
|
||||||
|
params[:files].each do |file|
|
||||||
|
FileMover.new(file, @snippet).execute
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ class UploadsController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_model
|
def find_model
|
||||||
|
return nil unless params[:id]
|
||||||
|
|
||||||
return render_404 unless upload_model && upload_mount
|
return render_404 unless upload_model && upload_mount
|
||||||
|
|
||||||
@model = upload_model.find(params[:id])
|
@model = upload_model.find(params[:id])
|
||||||
|
|
@ -33,6 +35,8 @@ class UploadsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_create_access!
|
def authorize_create_access!
|
||||||
|
return unless model
|
||||||
|
|
||||||
# for now we support only personal snippets comments
|
# for now we support only personal snippets comments
|
||||||
authorized = can?(current_user, :comment_personal_snippet, model)
|
authorized = can?(current_user, :comment_personal_snippet, model)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ module GitlabRoutingHelper
|
||||||
|
|
||||||
def preview_markdown_path(project, *args)
|
def preview_markdown_path(project, *args)
|
||||||
if @snippet.is_a?(PersonalSnippet)
|
if @snippet.is_a?(PersonalSnippet)
|
||||||
preview_markdown_snippet_path(@snippet)
|
preview_markdown_snippets_path
|
||||||
else
|
else
|
||||||
preview_markdown_namespace_project_path(project.namespace, project, *args)
|
preview_markdown_namespace_project_path(project.namespace, project, *args)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ class Snippet < ActiveRecord::Base
|
||||||
include Spammable
|
include Spammable
|
||||||
|
|
||||||
cache_markdown_field :title, pipeline: :single_line
|
cache_markdown_field :title, pipeline: :single_line
|
||||||
|
cache_markdown_field :description
|
||||||
cache_markdown_field :content
|
cache_markdown_field :content
|
||||||
|
|
||||||
# Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with snippets.
|
# Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with snippets.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
class FileMover
|
||||||
|
attr_reader :secret, :file_name, :model
|
||||||
|
|
||||||
|
def initialize(file_path, model, update_field = :description)
|
||||||
|
@secret = File.split(File.dirname(file_path)).last
|
||||||
|
@file_name = File.basename(file_path)
|
||||||
|
@model = model
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
move
|
||||||
|
update_markdown
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def move
|
||||||
|
FileUtils.mkdir_p(file_path)
|
||||||
|
FileUtils.move(temp_file_path, file_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_markdown(field = :description)
|
||||||
|
updated_text = model.send(field).sub(temp_file_uploader.to_markdown, uploader.to_markdown)
|
||||||
|
model.update_attribute(field, updated_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
def temp_file_path
|
||||||
|
temp_file_uploader.retrieve_from_store!(file_name)
|
||||||
|
|
||||||
|
temp_file_uploader.file.path
|
||||||
|
end
|
||||||
|
|
||||||
|
def file_path
|
||||||
|
return @file_path if @file_path
|
||||||
|
|
||||||
|
uploader.retrieve_from_store!(file_name)
|
||||||
|
|
||||||
|
@file_path = uploader.file.path
|
||||||
|
end
|
||||||
|
|
||||||
|
def uploader
|
||||||
|
@uploader ||= PersonalFileUploader.new(model, secret)
|
||||||
|
end
|
||||||
|
|
||||||
|
def temp_file_uploader
|
||||||
|
@temp_file_uploader ||= PersonalFileUploader.new(nil, secret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -10,6 +10,10 @@ class PersonalFileUploader < FileUploader
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.model_path(model)
|
def self.model_path(model)
|
||||||
File.join("/#{base_dir}", model.class.to_s.underscore, model.id.to_s)
|
if model
|
||||||
|
File.join("/#{base_dir}", model.class.to_s.underscore, model.id.to_s)
|
||||||
|
else
|
||||||
|
File.join("/#{base_dir}", 'temp')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
- header_title "Snippets", snippets_path
|
- header_title "Snippets", snippets_path
|
||||||
|
|
||||||
- content_for :page_specific_javascripts do
|
- content_for :page_specific_javascripts do
|
||||||
- if @snippet&.persisted? && current_user
|
- if @snippet && current_user
|
||||||
:javascript
|
:javascript
|
||||||
window.uploads_path = "#{upload_path('personal_snippet', @snippet)}";
|
window.uploads_path = "#{upload_path('personal_snippet', id: @snippet.id)}";
|
||||||
window.preview_markdown_path = "#{preview_markdown_snippet_path(@snippet)}";
|
|
||||||
|
|
||||||
= render template: "layouts/application"
|
= render template: "layouts/application"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
- project = local_assigns.fetch(:project)
|
- project = local_assigns.fetch(:project)
|
||||||
- issuable = local_assigns.fetch(:issuable)
|
- model = local_assigns.fetch(:model)
|
||||||
|
|
||||||
- form = local_assigns.fetch(:form)
|
- form = local_assigns.fetch(:form)
|
||||||
- supports_slash_commands = issuable.new_record?
|
- supports_slash_commands = !model.persisted?
|
||||||
|
|
||||||
- if supports_slash_commands
|
- if supports_slash_commands
|
||||||
- preview_url = preview_markdown_path(project, slash_commands_target_type: issuable.class.name)
|
- preview_url = preview_markdown_path(project, slash_commands_target_type: model.class.name)
|
||||||
- else
|
- else
|
||||||
- preview_url = preview_markdown_path(project)
|
- preview_url = preview_markdown_path(project)
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
= render 'shared/issuable/form/template_selector', issuable: issuable
|
= render 'shared/issuable/form/template_selector', issuable: issuable
|
||||||
= render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?)
|
= render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?)
|
||||||
|
|
||||||
= render 'shared/issuable/form/description', issuable: issuable, form: form, project: project
|
= render 'shared/form_elements/description', model: issuable, form: form, project: project
|
||||||
|
|
||||||
- if issuable.respond_to?(:confidential)
|
- if issuable.respond_to?(:confidential)
|
||||||
.form-group
|
.form-group
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
= page_specific_javascript_bundle_tag('snippet')
|
= page_specific_javascript_bundle_tag('snippet')
|
||||||
|
|
||||||
.snippet-form-holder
|
.snippet-form-holder
|
||||||
= form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit" } do |f|
|
= form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f|
|
||||||
= form_errors(@snippet)
|
= form_errors(@snippet)
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.text_field :title, class: 'form-control', required: true, autofocus: true
|
= f.text_field :title, class: 'form-control', required: true, autofocus: true
|
||||||
|
|
||||||
|
= render 'shared/form_elements/description', model: @snippet, project: @project, form: f
|
||||||
|
|
||||||
= render 'shared/visibility_level', f: f, visibility_level: @snippet.visibility_level, can_change_visibility_level: true, form_model: @snippet
|
= render 'shared/visibility_level', f: f, visibility_level: @snippet.visibility_level, can_change_visibility_level: true, form_model: @snippet
|
||||||
|
|
||||||
.file-editor
|
.file-editor
|
||||||
|
|
@ -23,6 +25,9 @@
|
||||||
.file-content.code
|
.file-content.code
|
||||||
%pre#editor= @snippet.content
|
%pre#editor= @snippet.content
|
||||||
= f.hidden_field :content, class: 'snippet-file-content'
|
= f.hidden_field :content, class: 'snippet-file-content'
|
||||||
|
- if params[:files]
|
||||||
|
- params[:files].each_with_index do |file, index|
|
||||||
|
= hidden_field_tag "files[]", file, id: "files_#{index}"
|
||||||
|
|
||||||
.form-actions
|
.form-actions
|
||||||
- if @snippet.new_record?
|
- if @snippet.new_record?
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,10 @@
|
||||||
|
|
||||||
- if @snippet.updated_at != @snippet.created_at
|
- if @snippet.updated_at != @snippet.created_at
|
||||||
= edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true)
|
= edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true)
|
||||||
|
%div
|
||||||
|
- if @snippet.description.present?
|
||||||
|
.description
|
||||||
|
.wiki
|
||||||
|
= markdown_field(@snippet, :description)
|
||||||
|
%textarea.hidden.js-task-list-field
|
||||||
|
= @snippet.description
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Support descriptions for snippets
|
||||||
|
merge_request:
|
||||||
|
author:
|
||||||
|
|
@ -2,6 +2,9 @@ resources :snippets, concerns: :awardable do
|
||||||
member do
|
member do
|
||||||
get :raw
|
get :raw
|
||||||
post :mark_as_spam
|
post :mark_as_spam
|
||||||
|
end
|
||||||
|
|
||||||
|
collection do
|
||||||
post :preview_markdown
|
post :preview_markdown
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ scope path: :uploads do
|
||||||
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: /[^\/]+/ }
|
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: /[^\/]+/ }
|
||||||
|
|
||||||
# create uploads for models, snippets (notes) available for now
|
# create uploads for models, snippets (notes) available for now
|
||||||
post ':model/:id/',
|
post ':model',
|
||||||
to: 'uploads#create',
|
to: 'uploads#create',
|
||||||
constraints: { model: /personal_snippet/, id: /\d+/ },
|
constraints: { model: /personal_snippet/, id: /\d+/ },
|
||||||
as: 'upload'
|
as: 'upload'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
class AddDescriptionToSnippets < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column :snippets, :description, :text
|
||||||
|
add_column :snippets, :description_html, :text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1153,6 +1153,8 @@ ActiveRecord::Schema.define(version: 20170523091700) do
|
||||||
t.text "title_html"
|
t.text "title_html"
|
||||||
t.text "content_html"
|
t.text "content_html"
|
||||||
t.integer "cached_markdown_version"
|
t.integer "cached_markdown_version"
|
||||||
|
t.text "description"
|
||||||
|
t.text "description_html"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
|
add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ Parameters:
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title": "test",
|
"title": "test",
|
||||||
"file_name": "add.rb",
|
"file_name": "add.rb",
|
||||||
|
"description": "Ruby test snippet",
|
||||||
"author": {
|
"author": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"username": "john_smith",
|
"username": "john_smith",
|
||||||
|
|
@ -70,8 +71,9 @@ Parameters:
|
||||||
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
|
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
|
||||||
- `title` (required) - The title of a snippet
|
- `title` (required) - The title of a snippet
|
||||||
- `file_name` (required) - The name of a snippet file
|
- `file_name` (required) - The name of a snippet file
|
||||||
|
- `description` (optional) - The description of a snippet
|
||||||
- `code` (required) - The content of a snippet
|
- `code` (required) - The content of a snippet
|
||||||
- `visibility` (required) - The snippet's visibility
|
- `visibility` (optional) - The snippet's visibility
|
||||||
|
|
||||||
## Update snippet
|
## Update snippet
|
||||||
|
|
||||||
|
|
@ -87,6 +89,7 @@ Parameters:
|
||||||
- `snippet_id` (required) - The ID of a project's snippet
|
- `snippet_id` (required) - The ID of a project's snippet
|
||||||
- `title` (optional) - The title of a snippet
|
- `title` (optional) - The title of a snippet
|
||||||
- `file_name` (optional) - The name of a snippet file
|
- `file_name` (optional) - The name of a snippet file
|
||||||
|
- `description` (optional) - The description of a snippet
|
||||||
- `code` (optional) - The content of a snippet
|
- `code` (optional) - The content of a snippet
|
||||||
- `visibility` (optional) - The snippet's visibility
|
- `visibility` (optional) - The snippet's visibility
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ Example response:
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title": "test",
|
"title": "test",
|
||||||
"file_name": "add.rb",
|
"file_name": "add.rb",
|
||||||
|
"description": "Ruby test snippet",
|
||||||
"author": {
|
"author": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"username": "john_smith",
|
"username": "john_smith",
|
||||||
|
|
@ -73,16 +74,17 @@ POST /snippets
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `title` | String | yes | The title of a snippet |
|
| `title` | String | yes | The title of a snippet |
|
||||||
| `file_name` | String | yes | The name of a snippet file |
|
| `file_name` | String | yes | The name of a snippet file |
|
||||||
| `content` | String | yes | The content of a snippet |
|
| `content` | String | yes | The content of a snippet |
|
||||||
| `visibility` | String | yes | The snippet's visibility |
|
| `description` | String | no | The description of a snippet |
|
||||||
|
| `visibility` | String | no | The snippet's visibility |
|
||||||
|
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "file_name": "test.txt", "visibility": "internal" }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets
|
curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "description": "Hello World snippet", "file_name": "test.txt", "visibility": "internal" }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets
|
||||||
```
|
```
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
|
@ -92,6 +94,7 @@ Example response:
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title": "This is a snippet",
|
"title": "This is a snippet",
|
||||||
"file_name": "test.txt",
|
"file_name": "test.txt",
|
||||||
|
"description": "Hello World snippet",
|
||||||
"author": {
|
"author": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"username": "john_smith",
|
"username": "john_smith",
|
||||||
|
|
@ -117,13 +120,14 @@ PUT /snippets/:id
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | Integer | yes | The ID of a snippet |
|
| `id` | Integer | yes | The ID of a snippet |
|
||||||
| `title` | String | no | The title of a snippet |
|
| `title` | String | no | The title of a snippet |
|
||||||
| `file_name` | String | no | The name of a snippet file |
|
| `file_name` | String | no | The name of a snippet file |
|
||||||
| `content` | String | no | The content of a snippet |
|
| `description` | String | no | The description of a snippet |
|
||||||
| `visibility` | String | no | The snippet's visibility |
|
| `content` | String | no | The content of a snippet |
|
||||||
|
| `visibility` | String | no | The snippet's visibility |
|
||||||
|
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
|
|
@ -137,6 +141,7 @@ Example response:
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title": "test",
|
"title": "test",
|
||||||
"file_name": "add.rb",
|
"file_name": "add.rb",
|
||||||
|
"description": "description of snippet",
|
||||||
"author": {
|
"author": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"username": "john_smith",
|
"username": "john_smith",
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
class ProjectSnippet < Grape::Entity
|
class ProjectSnippet < Grape::Entity
|
||||||
expose :id, :title, :file_name
|
expose :id, :title, :file_name, :description
|
||||||
expose :author, using: Entities::UserBasic
|
expose :author, using: Entities::UserBasic
|
||||||
expose :updated_at, :created_at
|
expose :updated_at, :created_at
|
||||||
|
|
||||||
|
|
@ -234,7 +234,7 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
class PersonalSnippet < Grape::Entity
|
class PersonalSnippet < Grape::Entity
|
||||||
expose :id, :title, :file_name
|
expose :id, :title, :file_name, :description
|
||||||
expose :author, using: Entities::UserBasic
|
expose :author, using: Entities::UserBasic
|
||||||
expose :updated_at, :created_at
|
expose :updated_at, :created_at
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ module API
|
||||||
requires :title, type: String, desc: 'The title of the snippet'
|
requires :title, type: String, desc: 'The title of the snippet'
|
||||||
requires :file_name, type: String, desc: 'The file name of the snippet'
|
requires :file_name, type: String, desc: 'The file name of the snippet'
|
||||||
requires :code, type: String, desc: 'The content of the snippet'
|
requires :code, type: String, desc: 'The content of the snippet'
|
||||||
|
optional :description, type: String, desc: 'The description of a snippet'
|
||||||
requires :visibility, type: String,
|
requires :visibility, type: String,
|
||||||
values: Gitlab::VisibilityLevel.string_values,
|
values: Gitlab::VisibilityLevel.string_values,
|
||||||
desc: 'The visibility of the snippet'
|
desc: 'The visibility of the snippet'
|
||||||
|
|
@ -77,6 +78,7 @@ module API
|
||||||
optional :title, type: String, desc: 'The title of the snippet'
|
optional :title, type: String, desc: 'The title of the snippet'
|
||||||
optional :file_name, type: String, desc: 'The file name of the snippet'
|
optional :file_name, type: String, desc: 'The file name of the snippet'
|
||||||
optional :code, type: String, desc: 'The content of the snippet'
|
optional :code, type: String, desc: 'The content of the snippet'
|
||||||
|
optional :description, type: String, desc: 'The description of a snippet'
|
||||||
optional :visibility, type: String,
|
optional :visibility, type: String,
|
||||||
values: Gitlab::VisibilityLevel.string_values,
|
values: Gitlab::VisibilityLevel.string_values,
|
||||||
desc: 'The visibility of the snippet'
|
desc: 'The visibility of the snippet'
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ module API
|
||||||
requires :title, type: String, desc: 'The title of a snippet'
|
requires :title, type: String, desc: 'The title of a snippet'
|
||||||
requires :file_name, type: String, desc: 'The name of a snippet file'
|
requires :file_name, type: String, desc: 'The name of a snippet file'
|
||||||
requires :content, type: String, desc: 'The content of a snippet'
|
requires :content, type: String, desc: 'The content of a snippet'
|
||||||
|
optional :description, type: String, desc: 'The description of a snippet'
|
||||||
optional :visibility, type: String,
|
optional :visibility, type: String,
|
||||||
values: Gitlab::VisibilityLevel.string_values,
|
values: Gitlab::VisibilityLevel.string_values,
|
||||||
default: 'internal',
|
default: 'internal',
|
||||||
|
|
@ -85,6 +86,7 @@ module API
|
||||||
optional :title, type: String, desc: 'The title of a snippet'
|
optional :title, type: String, desc: 'The title of a snippet'
|
||||||
optional :file_name, type: String, desc: 'The name of a snippet file'
|
optional :file_name, type: String, desc: 'The name of a snippet file'
|
||||||
optional :content, type: String, desc: 'The content of a snippet'
|
optional :content, type: String, desc: 'The content of a snippet'
|
||||||
|
optional :description, type: String, desc: 'The description of a snippet'
|
||||||
optional :visibility, type: String,
|
optional :visibility, type: String,
|
||||||
values: Gitlab::VisibilityLevel.string_values,
|
values: Gitlab::VisibilityLevel.string_values,
|
||||||
desc: 'The visibility of the snippet'
|
desc: 'The visibility of the snippet'
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,18 @@ describe Projects::SnippetsController do
|
||||||
post :create, {
|
post :create, {
|
||||||
namespace_id: project.namespace.to_param,
|
namespace_id: project.namespace.to_param,
|
||||||
project_id: project,
|
project_id: project,
|
||||||
project_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params)
|
project_snippet: { title: 'Title', content: 'Content', description: 'Description' }.merge(snippet_params)
|
||||||
}.merge(additional_params)
|
}.merge(additional_params)
|
||||||
|
|
||||||
|
Snippet.last
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the snippet correctly' do
|
||||||
|
snippet = create_snippet(project, visibility_level: Snippet::PRIVATE)
|
||||||
|
|
||||||
|
expect(snippet.title).to eq('Title')
|
||||||
|
expect(snippet.content).to eq('Content')
|
||||||
|
expect(snippet.description).to eq('Description')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the snippet is spam' do
|
context 'when the snippet is spam' do
|
||||||
|
|
|
||||||
|
|
@ -171,12 +171,50 @@ describe SnippetsController do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
post :create, {
|
post :create, {
|
||||||
personal_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params)
|
personal_snippet: { title: 'Title', content: 'Content', description: 'Description' }.merge(snippet_params)
|
||||||
}.merge(additional_params)
|
}.merge(additional_params)
|
||||||
|
|
||||||
Snippet.last
|
Snippet.last
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates the snippet correctly' do
|
||||||
|
snippet = create_snippet(visibility_level: Snippet::PRIVATE)
|
||||||
|
|
||||||
|
expect(snippet.title).to eq('Title')
|
||||||
|
expect(snippet.content).to eq('Content')
|
||||||
|
expect(snippet.description).to eq('Description')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the snippet description contains a file' do
|
||||||
|
let(:picture_file) { '/temp/secret56/picture.jpg' }
|
||||||
|
let(:text_file) { '/temp/secret78/text.txt' }
|
||||||
|
let(:description) do
|
||||||
|
"Description with picture:  and "\
|
||||||
|
"text: [text.txt](/uploads#{text_file})"
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(FileUtils).to receive(:mkdir_p)
|
||||||
|
allow(FileUtils).to receive(:move)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { create_snippet({ description: description }, { files: [picture_file, text_file] }) }
|
||||||
|
|
||||||
|
it 'creates the snippet' do
|
||||||
|
expect { subject }.to change { Snippet.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'stores the snippet description correctly' do
|
||||||
|
snippet = subject
|
||||||
|
|
||||||
|
expected_description = "Description with picture: "\
|
||||||
|
" and "\
|
||||||
|
"text: [text.txt](/uploads/personal_snippet/#{snippet.id}/secret78/text.txt)"
|
||||||
|
|
||||||
|
expect(snippet.description).to eq(expected_description)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when the snippet is spam' do
|
context 'when the snippet is spam' do
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
|
allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ FactoryGirl.define do
|
||||||
author
|
author
|
||||||
title { generate(:title) }
|
title { generate(:title) }
|
||||||
content { generate(:title) }
|
content { generate(:title) }
|
||||||
|
description { generate(:title) }
|
||||||
file_name { generate(:filename) }
|
file_name { generate(:filename) }
|
||||||
|
|
||||||
trait :public do
|
trait :public do
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
feature 'Create Snippet', :js, feature: true do
|
||||||
|
include DropzoneHelper
|
||||||
|
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:project) { create(:project, :repository, :public) }
|
||||||
|
|
||||||
|
def fill_form
|
||||||
|
fill_in 'project_snippet_title', with: 'My Snippet Title'
|
||||||
|
fill_in 'project_snippet_description', with: 'My Snippet **Description**'
|
||||||
|
page.within('.file-editor') do
|
||||||
|
find('.ace_editor').native.send_keys('Hello World!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user is authenticated' do
|
||||||
|
before do
|
||||||
|
project.team << [user, :master]
|
||||||
|
login_as(user)
|
||||||
|
|
||||||
|
visit namespace_project_snippets_path(project.namespace, project)
|
||||||
|
|
||||||
|
click_on('New snippet')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new snippet' do
|
||||||
|
fill_form
|
||||||
|
click_button('Create snippet')
|
||||||
|
wait_for_ajax
|
||||||
|
|
||||||
|
expect(page).to have_content('My Snippet Title')
|
||||||
|
expect(page).to have_content('Hello World!')
|
||||||
|
page.within('.snippet-header .description') do
|
||||||
|
expect(page).to have_content('My Snippet Description')
|
||||||
|
expect(page).to have_selector('strong')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uploads a file when dragging into textarea' do
|
||||||
|
fill_form
|
||||||
|
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
|
||||||
|
|
||||||
|
expect(page.find_field("project_snippet_description").value).to have_content('banana_sample')
|
||||||
|
|
||||||
|
click_button('Create snippet')
|
||||||
|
wait_for_ajax
|
||||||
|
|
||||||
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
|
expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a snippet when all reuiqred fields are filled in after validation failing' do
|
||||||
|
fill_in 'project_snippet_title', with: 'My Snippet Title'
|
||||||
|
click_button('Create snippet')
|
||||||
|
|
||||||
|
expect(page).to have_selector('#error_explanation')
|
||||||
|
|
||||||
|
fill_form
|
||||||
|
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
|
||||||
|
|
||||||
|
click_button('Create snippet')
|
||||||
|
wait_for_ajax
|
||||||
|
|
||||||
|
expect(page).to have_content('My Snippet Title')
|
||||||
|
expect(page).to have_content('Hello World!')
|
||||||
|
page.within('.snippet-header .description') do
|
||||||
|
expect(page).to have_content('My Snippet Description')
|
||||||
|
expect(page).to have_selector('strong')
|
||||||
|
end
|
||||||
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
|
expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user is not authenticated' do
|
||||||
|
it 'shows a public snippet on the index page but not the New snippet button' do
|
||||||
|
snippet = create(:project_snippet, :public, project: project)
|
||||||
|
|
||||||
|
visit namespace_project_snippets_path(project.namespace, project)
|
||||||
|
|
||||||
|
expect(page).to have_content(snippet.title)
|
||||||
|
expect(page).not_to have_content('New snippet')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,24 +1,71 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
feature 'Create Snippet', :js, feature: true do
|
feature 'Create Snippet', :js, feature: true do
|
||||||
|
include DropzoneHelper
|
||||||
|
|
||||||
before do
|
before do
|
||||||
login_as :user
|
login_as :user
|
||||||
visit new_snippet_path
|
visit new_snippet_path
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'Authenticated user creates a snippet' do
|
def fill_form
|
||||||
fill_in 'personal_snippet_title', with: 'My Snippet Title'
|
fill_in 'personal_snippet_title', with: 'My Snippet Title'
|
||||||
|
fill_in 'personal_snippet_description', with: 'My Snippet **Description**'
|
||||||
page.within('.file-editor') do
|
page.within('.file-editor') do
|
||||||
find('.ace_editor').native.send_keys 'Hello World!'
|
find('.ace_editor').native.send_keys 'Hello World!'
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
click_button 'Create snippet'
|
scenario 'Authenticated user creates a snippet' do
|
||||||
|
fill_form
|
||||||
|
|
||||||
|
click_button('Create snippet')
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
|
||||||
expect(page).to have_content('My Snippet Title')
|
expect(page).to have_content('My Snippet Title')
|
||||||
|
page.within('.snippet-header .description') do
|
||||||
|
expect(page).to have_content('My Snippet Description')
|
||||||
|
expect(page).to have_selector('strong')
|
||||||
|
end
|
||||||
expect(page).to have_content('Hello World!')
|
expect(page).to have_content('Hello World!')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario 'uploads a file when dragging into textarea' do
|
||||||
|
fill_form
|
||||||
|
|
||||||
|
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
|
||||||
|
|
||||||
|
expect(page.find_field("personal_snippet_description").value).to have_content('banana_sample')
|
||||||
|
|
||||||
|
click_button('Create snippet')
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
|
expect(link).to match(%r{/uploads/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'validation fails for the first time' do
|
||||||
|
fill_in 'personal_snippet_title', with: 'My Snippet Title'
|
||||||
|
click_button('Create snippet')
|
||||||
|
|
||||||
|
expect(page).to have_selector('#error_explanation')
|
||||||
|
|
||||||
|
fill_form
|
||||||
|
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
|
||||||
|
|
||||||
|
click_button('Create snippet')
|
||||||
|
wait_for_requests
|
||||||
|
|
||||||
|
expect(page).to have_content('My Snippet Title')
|
||||||
|
page.within('.snippet-header .description') do
|
||||||
|
expect(page).to have_content('My Snippet Description')
|
||||||
|
expect(page).to have_selector('strong')
|
||||||
|
end
|
||||||
|
expect(page).to have_content('Hello World!')
|
||||||
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
|
expect(link).to match(%r{/uploads/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
|
||||||
|
end
|
||||||
|
|
||||||
scenario 'Authenticated user creates a snippet with + in filename' do
|
scenario 'Authenticated user creates a snippet with + in filename' do
|
||||||
fill_in 'personal_snippet_title', with: 'My Snippet Title'
|
fill_in 'personal_snippet_title', with: 'My Snippet Title'
|
||||||
page.within('.file-editor') do
|
page.within('.file-editor') do
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
feature 'Edit Snippet', :js, feature: true do
|
||||||
|
include DropzoneHelper
|
||||||
|
|
||||||
|
let(:file_name) { 'test.rb' }
|
||||||
|
let(:content) { 'puts "test"' }
|
||||||
|
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
login_as(user)
|
||||||
|
|
||||||
|
visit edit_snippet_path(snippet)
|
||||||
|
wait_for_ajax
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the snippet' do
|
||||||
|
fill_in 'personal_snippet_title', with: 'New Snippet Title'
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
wait_for_ajax
|
||||||
|
|
||||||
|
expect(page).to have_content('New Snippet Title')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the snippet with files attached' do
|
||||||
|
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
|
||||||
|
expect(page.find_field("personal_snippet_description").value).to have_content('banana_sample')
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
wait_for_ajax
|
||||||
|
|
||||||
|
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
|
||||||
|
expect(link).to match(%r{/uploads/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -92,6 +92,7 @@ Milestone:
|
||||||
ProjectSnippet:
|
ProjectSnippet:
|
||||||
- id
|
- id
|
||||||
- title
|
- title
|
||||||
|
- description
|
||||||
- content
|
- content
|
||||||
- author_id
|
- author_id
|
||||||
- project_id
|
- project_id
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,34 @@ describe API::ProjectSnippets do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /projects/:project_id/snippets/:id' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:snippet) { create(:project_snippet, :public, project: project) }
|
||||||
|
|
||||||
|
it 'returns snippet json' do
|
||||||
|
get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
|
||||||
|
expect(json_response['title']).to eq(snippet.title)
|
||||||
|
expect(json_response['description']).to eq(snippet.description)
|
||||||
|
expect(json_response['file_name']).to eq(snippet.file_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404 for invalid snippet id' do
|
||||||
|
get api("/projects/#{project.id}/snippets/1234", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
expect(json_response['message']).to eq('404 Not found')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'POST /projects/:project_id/snippets/' do
|
describe 'POST /projects/:project_id/snippets/' do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
title: 'Test Title',
|
title: 'Test Title',
|
||||||
file_name: 'test.rb',
|
file_name: 'test.rb',
|
||||||
|
description: 'test description',
|
||||||
code: 'puts "hello world"',
|
code: 'puts "hello world"',
|
||||||
visibility: 'public'
|
visibility: 'public'
|
||||||
}
|
}
|
||||||
|
|
@ -52,6 +75,7 @@ describe API::ProjectSnippets do
|
||||||
expect(response).to have_http_status(201)
|
expect(response).to have_http_status(201)
|
||||||
snippet = ProjectSnippet.find(json_response['id'])
|
snippet = ProjectSnippet.find(json_response['id'])
|
||||||
expect(snippet.content).to eq(params[:code])
|
expect(snippet.content).to eq(params[:code])
|
||||||
|
expect(snippet.description).to eq(params[:description])
|
||||||
expect(snippet.title).to eq(params[:title])
|
expect(snippet.title).to eq(params[:title])
|
||||||
expect(snippet.file_name).to eq(params[:file_name])
|
expect(snippet.file_name).to eq(params[:file_name])
|
||||||
expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
|
expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
|
||||||
|
|
@ -106,12 +130,14 @@ describe API::ProjectSnippets do
|
||||||
|
|
||||||
it 'updates snippet' do
|
it 'updates snippet' do
|
||||||
new_content = 'New content'
|
new_content = 'New content'
|
||||||
|
new_description = 'New description'
|
||||||
|
|
||||||
put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content
|
put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content, description: new_description
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
expect(response).to have_http_status(200)
|
||||||
snippet.reload
|
snippet.reload
|
||||||
expect(snippet.content).to eq(new_content)
|
expect(snippet.content).to eq(new_content)
|
||||||
|
expect(snippet.description).to eq(new_description)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 404 for invalid snippet id' do
|
it 'returns 404 for invalid snippet id' do
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,33 @@ describe API::Snippets do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /snippets/:id' do
|
||||||
|
let(:snippet) { create(:personal_snippet, author: user) }
|
||||||
|
|
||||||
|
it 'returns snippet json' do
|
||||||
|
get api("/snippets/#{snippet.id}", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
|
||||||
|
expect(json_response['title']).to eq(snippet.title)
|
||||||
|
expect(json_response['description']).to eq(snippet.description)
|
||||||
|
expect(json_response['file_name']).to eq(snippet.file_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404 for invalid snippet id' do
|
||||||
|
get api("/snippets/1234", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
expect(json_response['message']).to eq('404 Not found')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'POST /snippets/' do
|
describe 'POST /snippets/' do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
title: 'Test Title',
|
title: 'Test Title',
|
||||||
file_name: 'test.rb',
|
file_name: 'test.rb',
|
||||||
|
description: 'test description',
|
||||||
content: 'puts "hello world"',
|
content: 'puts "hello world"',
|
||||||
visibility: 'public'
|
visibility: 'public'
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +119,7 @@ describe API::Snippets do
|
||||||
|
|
||||||
expect(response).to have_http_status(201)
|
expect(response).to have_http_status(201)
|
||||||
expect(json_response['title']).to eq(params[:title])
|
expect(json_response['title']).to eq(params[:title])
|
||||||
|
expect(json_response['description']).to eq(params[:description])
|
||||||
expect(json_response['file_name']).to eq(params[:file_name])
|
expect(json_response['file_name']).to eq(params[:file_name])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -150,12 +173,14 @@ describe API::Snippets do
|
||||||
|
|
||||||
it 'updates snippet' do
|
it 'updates snippet' do
|
||||||
new_content = 'New content'
|
new_content = 'New content'
|
||||||
|
new_description = 'New description'
|
||||||
|
|
||||||
put api("/snippets/#{snippet.id}", user), content: new_content
|
put api("/snippets/#{snippet.id}", user), content: new_content, description: new_description
|
||||||
|
|
||||||
expect(response).to have_http_status(200)
|
expect(response).to have_http_status(200)
|
||||||
snippet.reload
|
snippet.reload
|
||||||
expect(snippet.content).to eq(new_content)
|
expect(snippet.content).to eq(new_content)
|
||||||
|
expect(snippet.description).to eq(new_description)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 404 for invalid snippet id' do
|
it 'returns 404 for invalid snippet id' do
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe FileMover do
|
||||||
|
let(:filename) { 'banana_sample.gif' }
|
||||||
|
let(:file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) }
|
||||||
|
let(:temp_description) { 'test ' }
|
||||||
|
let(:temp_file_path) { File.join('secret55', filename).to_s }
|
||||||
|
let(:file_path) { File.join('uploads', 'personal_snippet', snippet.id.to_s, 'secret55', filename).to_s }
|
||||||
|
|
||||||
|
let(:snippet) { create(:personal_snippet, description: temp_description) }
|
||||||
|
|
||||||
|
subject { described_class.new(file_path, snippet).execute }
|
||||||
|
|
||||||
|
describe '#execute' do
|
||||||
|
it 'updates the description correctly' do
|
||||||
|
expect(FileUtils).to receive(:mkdir_p).with(a_string_including(file_path))
|
||||||
|
expect(FileUtils).to receive(:move).with(a_string_including(temp_file_path), a_string_including(file_path))
|
||||||
|
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(snippet.reload.description)
|
||||||
|
.to eq("test ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue