Commit 2d17d6f1 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge pull request #2123 from NARKOZ/notes-api

Notes API
parents aaa1c942 24047e1e
v 3.2.0 v 3.2.0
- [API] create notes for snippets and issues
- [API] list notes for snippets and issues
- [API] list project wall notes
- Remove project code - use path instead - Remove project code - use path instead
- added username field to user - added username field to user
- rake task to fill usernames based on emails create namespaces for users - rake task to fill usernames based on emails create namespaces for users
...@@ -10,7 +13,7 @@ v 3.2.0 ...@@ -10,7 +13,7 @@ v 3.2.0
- Fixes commit patches getting escaped (see #2036) - Fixes commit patches getting escaped (see #2036)
- Support diff and patch generation for commits and merge request - Support diff and patch generation for commits and merge request
- MergeReqest doesn't generate a temporary file for the patch any more - MergeReqest doesn't generate a temporary file for the patch any more
- Update the UI to allow downloading Patch or Diff - Update the UI to allow downloading Patch or Diff
v 3.1.0 v 3.1.0
- Updated gems - Updated gems
...@@ -48,7 +51,7 @@ v 3.0.0 ...@@ -48,7 +51,7 @@ v 3.0.0
- Fixed bug with gitolite keys - Fixed bug with gitolite keys
- UI improved - UI improved
- Increased perfomance of application - Increased perfomance of application
- Show user avatar in last commit when browsing Files - Show user avatar in last commit when browsing Files
- Refactored Gitlab::Merge - Refactored Gitlab::Merge
- Use Font Awsome for icons - Use Font Awsome for icons
- Separate observing of Note and MergeRequestsa - Separate observing of Note and MergeRequestsa
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
= link_to "Issues", "#issues", 'data-toggle' => 'tab' = link_to "Issues", "#issues", 'data-toggle' => 'tab'
%li %li
= link_to "Milestones", "#milestones", 'data-toggle' => 'tab' = link_to "Milestones", "#milestones", 'data-toggle' => 'tab'
%li
= link_to "Notes", "#notes", 'data-toggle' => 'tab'
.tab-content .tab-content
.tab-pane.active#README .tab-pane.active#README
...@@ -94,3 +96,12 @@ ...@@ -94,3 +96,12 @@
.file_content.wiki .file_content.wiki
= preserve do = preserve do
= markdown File.read(Rails.root.join("doc", "api", "milestones.md")) = markdown File.read(Rails.root.join("doc", "api", "milestones.md"))
.tab-pane#notes
.file_holder
.file_title
%i.icon-file
Notes
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "notes.md"))
## List notes
### List project wall notes
Get a list of project wall notes.
```
GET /projects/:id/notes
```
```json
[
{
"id": 522,
"body": "The solution is rather tricky",
"author": {
"id": 1,
"email": "john@example.com",
"name": "John Smith",
"blocked": false,
"created_at": "2012-05-23T08:00:58Z"
},
"updated_at":"2012-11-27T19:16:44Z",
"created_at":"2012-11-27T19:16:44Z"
}
]
```
Parameters:
+ `id` (required) - The ID or code name of a project
### List issue notes
Get a list of issue notes.
```
GET /projects/:id/issues/:issue_id/notes
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `issue_id` (required) - The ID of an issue
### List snippet notes
Get a list of snippet notes.
```
GET /projects/:id/snippets/:snippet_id/notes
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `snippet_id` (required) - The ID of a snippet
## Single note
### Single issue note
Get an issue note.
```
GET /projects/:id/issues/:issue_id/:notes/:note_id
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `issue_id` (required) - The ID of a project issue
+ `note_id` (required) - The ID of an issue note
### Single snippet note
Get a snippet note.
```
GET /projects/:id/issues/:snippet_id/:notes/:note_id
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `snippet_id` (required) - The ID of a project snippet
+ `note_id` (required) - The ID of an snippet note
## New note
### New issue note
Create a new issue note.
```
POST /projects/:id/issues/:issue_id/notes
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `issue_id` (required) - The ID of an issue
+ `body` (required) - The content of a note
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
### New snippet note
Create a new snippet note.
```
POST /projects/:id/snippets/:snippet_id/notes
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `snippet_id` (required) - The ID of an snippet
+ `body` (required) - The content of a note
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
...@@ -19,5 +19,6 @@ module Gitlab ...@@ -19,5 +19,6 @@ module Gitlab
mount Milestones mount Milestones
mount Session mount Session
mount MergeRequests mount MergeRequests
mount Notes
end end
end end
...@@ -70,8 +70,15 @@ module Gitlab ...@@ -70,8 +70,15 @@ module Gitlab
end end
class Note < Grape::Entity class Note < Grape::Entity
expose :id
expose :note, as: :body
expose :author, using: Entities::UserBasic expose :author, using: Entities::UserBasic
expose :updated_at, :created_at
end
class MRNote < Grape::Entity
expose :note expose :note
expose :author, using: Entities::UserBasic
end end
end end
end end
...@@ -4,9 +4,9 @@ module Gitlab ...@@ -4,9 +4,9 @@ module Gitlab
before { authenticate! } before { authenticate! }
resource :projects do resource :projects do
# List merge requests # List merge requests
# #
# Parameters: # Parameters:
# id (required) - The ID or code name of a project # id (required) - The ID or code name of a project
# #
...@@ -15,24 +15,24 @@ module Gitlab ...@@ -15,24 +15,24 @@ module Gitlab
# #
get ":id/merge_requests" do get ":id/merge_requests" do
authorize! :read_merge_request, user_project authorize! :read_merge_request, user_project
present paginate(user_project.merge_requests), with: Entities::MergeRequest present paginate(user_project.merge_requests), with: Entities::MergeRequest
end end
# Show MR # Show MR
# #
# Parameters: # Parameters:
# id (required) - The ID or code name of a project # id (required) - The ID or code name of a project
# merge_request_id (required) - The ID of MR # merge_request_id (required) - The ID of MR
# #
# Example: # Example:
# GET /projects/:id/merge_request/:merge_request_id # GET /projects/:id/merge_request/:merge_request_id
# #
get ":id/merge_request/:merge_request_id" do get ":id/merge_request/:merge_request_id" do
merge_request = user_project.merge_requests.find(params[:merge_request_id]) merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequest present merge_request, with: Entities::MergeRequest
end end
...@@ -45,17 +45,17 @@ module Gitlab ...@@ -45,17 +45,17 @@ module Gitlab
# target_branch (required) - The target branch # target_branch (required) - The target branch
# assignee_id - Assignee user ID # assignee_id - Assignee user ID
# title (required) - Title of MR # title (required) - Title of MR
# #
# Example: # Example:
# POST /projects/:id/merge_requests # POST /projects/:id/merge_requests
# #
post ":id/merge_requests" do post ":id/merge_requests" do
authorize! :write_merge_request, user_project authorize! :write_merge_request, user_project
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title]
merge_request = user_project.merge_requests.new(attrs) merge_request = user_project.merge_requests.new(attrs)
merge_request.author = current_user merge_request.author = current_user
if merge_request.save if merge_request.save
merge_request.reload_code merge_request.reload_code
present merge_request, with: Entities::MergeRequest present merge_request, with: Entities::MergeRequest
...@@ -80,9 +80,9 @@ module Gitlab ...@@ -80,9 +80,9 @@ module Gitlab
put ":id/merge_request/:merge_request_id" do put ":id/merge_request/:merge_request_id" do
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :closed] attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :closed]
merge_request = user_project.merge_requests.find(params[:merge_request_id]) merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :modify_merge_request, merge_request authorize! :modify_merge_request, merge_request
if merge_request.update_attributes attrs if merge_request.update_attributes attrs
merge_request.reload_code merge_request.reload_code
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
...@@ -98,7 +98,7 @@ module Gitlab ...@@ -98,7 +98,7 @@ module Gitlab
# id (required) - The ID or code name of a project # id (required) - The ID or code name of a project
# merge_request_id (required) - ID of MR # merge_request_id (required) - ID of MR
# note (required) - Text of comment # note (required) - Text of comment
# Examples: # Examples:
# POST /projects/:id/merge_request/:merge_request_id/comments # POST /projects/:id/merge_request/:merge_request_id/comments
# #
post ":id/merge_request/:merge_request_id/comments" do post ":id/merge_request/:merge_request_id/comments" do
...@@ -107,7 +107,7 @@ module Gitlab ...@@ -107,7 +107,7 @@ module Gitlab
note.author = current_user note.author = current_user
if note.save if note.save
present note, with: Entities::Note present note, with: Entities::MRNote
else else
not_found! not_found!
end end
......
module Gitlab
# Notes API
class Notes < Grape::API
before { authenticate! }
NOTEABLE_TYPES = [Issue, Snippet]
resource :projects do
# Get a list of project wall notes
#
# Parameters:
# id (required) - The ID or code name of a project
# Example Request:
# GET /projects/:id/notes
get ":id/notes" do
@notes = user_project.common_notes
present paginate(@notes), with: Entities::Note
end
NOTEABLE_TYPES.each do |noteable_type|
noteables_str = noteable_type.to_s.underscore.pluralize
noteable_id_str = "#{noteable_type.to_s.underscore}_id"
# Get a list of project +noteable+ notes
#
# Parameters:
# id (required) - The ID or code name of a project
# noteable_id (required) - The ID of an issue or snippet
# Example Request:
# GET /projects/:id/issues/:noteable_id/notes
# GET /projects/:id/snippets/:noteable_id/notes
get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
present paginate(@noteable.notes), with: Entities::Note
end
# Get a single +noteable+ note
#
# Parameters:
# id (required) - The ID or code name of a project
# noteable_id (required) - The ID of an issue or snippet
# note_id (required) - The ID of a note
# Example Request:
# GET /projects/:id/issues/:noteable_id/notes/:note_id
# GET /projects/:id/snippets/:noteable_id/notes/:note_id
get ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
@note = @noteable.notes.find(params[:note_id])
present @note, with: Entities::Note
end
# Create a new +noteable+ note
#
# Parameters:
# id (required) - The ID or code name of a project
# noteable_id (required) - The ID of an issue or snippet
# body (required) - The content of a note
# Example Request:
# POST /projects/:id/issues/:noteable_id/notes
# POST /projects/:id/snippets/:noteable_id/notes
post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
@note = @noteable.notes.new(note: params[:body])
@note.author = current_user
@note.project = user_project
if @note.save
present @note, with: Entities::Note
else
not_found!
end
end
end
end
end
end
require 'spec_helper'
describe Gitlab::API do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:project, owner: user) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:snippet) { create(:snippet, project: project, author: user) }
let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) }
let!(:wall_note) { create(:note, project: project, author: user) }
before { project.add_access(user, :read) }
describe "GET /projects/:id/notes" do
context "when unauthenticated" do
it "should return authentication error" do
get api("/projects/#{project.id}/notes")
response.status.should == 401
end
end
context "when authenticated" do
it "should return project wall notes" do
get api("/projects/#{project.id}/notes", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['body'].should == wall_note.note
end
end
end
describe "GET /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
it "should return an array of issue notes" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['body'].should == issue_note.note
end
end
context "when noteable is a Snippet" do
it "should return an array of snippet notes" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['body'].should == snippet_note.note
end
end
end
describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do
context "when noteable is an Issue" do
it "should return an issue note by id" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user)
response.status.should == 200
json_response['body'].should == issue_note.note
end
end
context "when noteable is a Snippet" do
it "should return a snippet note by id" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user)
response.status.should == 200
json_response['body'].should == snippet_note.note
end
end
end
describe "POST /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
it "should create a new issue note" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
response.status.should == 201
json_response['body'].should == 'hi!'
json_response['author']['email'].should == user.email
end
end
context "when noteable is a Snippet" do
it "should create a new snippet note" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!'
response.status.should == 201
json_response['body'].should == 'hi!'
json_response['author']['email'].should == user.email
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