From 01eab583d0d16b44554a9790fb502f14ea84faf0 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Tue, 27 Nov 2012 11:43:39 -0800 Subject: [PATCH 1/7] API: list wall, snippet and issue notes --- lib/api.rb | 1 + lib/api/entities.rb | 4 ++- lib/api/notes.rb | 38 ++++++++++++++++++++++++ spec/requests/api/notes_spec.rb | 52 +++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 lib/api/notes.rb create mode 100644 spec/requests/api/notes_spec.rb diff --git a/lib/api.rb b/lib/api.rb index 7a1845443e7..99e2074f306 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -19,5 +19,6 @@ module Gitlab mount Milestones mount Session mount MergeRequests + mount Notes end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9e605a607a2..0c44f621b08 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -70,8 +70,10 @@ module Gitlab end class Note < Grape::Entity + expose :id + expose :note, as: :body expose :author, using: Entities::UserBasic - expose :note + expose :updated_at, :created_at end end end diff --git a/lib/api/notes.rb b/lib/api/notes.rb new file mode 100644 index 00000000000..d2857f3d2c7 --- /dev/null +++ b/lib/api/notes.rb @@ -0,0 +1,38 @@ +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/noteable/: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 + end + end + end +end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb new file mode 100644 index 00000000000..1d42921f00c --- /dev/null +++ b/spec/requests/api/notes_spec.rb @@ -0,0 +1,52 @@ +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 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 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 +end -- GitLab From 9a4974b7603c4df4b9b78970fb96e185ba97c250 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 29 Nov 2012 11:21:14 -0800 Subject: [PATCH 2/7] correct example --- lib/api/notes.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/api/notes.rb b/lib/api/notes.rb index d2857f3d2c7..9b39ff4c88f 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -27,7 +27,8 @@ module Gitlab # 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/noteable/:noteable_id/notes + # 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 -- GitLab From 1c5aa848ce6141b8679e167171096055dc7f4df3 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 29 Nov 2012 11:33:41 -0800 Subject: [PATCH 3/7] API: get a single note --- lib/api/notes.rb | 15 +++++++++++++++ spec/requests/api/notes_spec.rb | 22 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 9b39ff4c88f..84b6beb5c71 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -33,6 +33,21 @@ module Gitlab @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 end end end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 1d42921f00c..175d405848b 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -32,7 +32,7 @@ describe Gitlab::API do describe "GET /projects/:id/noteable/:noteable_id/notes" do context "when noteable is an Issue" do - it "should return an array of notes" 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 @@ -41,7 +41,7 @@ describe Gitlab::API do end context "when noteable is a Snippet" do - it "should return an array of notes" 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 @@ -49,4 +49,22 @@ describe Gitlab::API do 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 end -- GitLab From c946bf886c2c07491a4595e93861df02dbf809f4 Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 29 Nov 2012 12:06:24 -0800 Subject: [PATCH 4/7] API: create new notes --- lib/api/notes.rb | 22 ++++++++++++++++++++++ spec/requests/api/notes_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 84b6beb5c71..924eeaa3618 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -48,6 +48,28 @@ module Gitlab @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 diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 175d405848b..b7c8ffaf320 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -67,4 +67,24 @@ describe Gitlab::API do 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 -- GitLab From 961cb285b08aed0281bc9a6e4634388ecc8e914c Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 29 Nov 2012 12:10:07 -0800 Subject: [PATCH 5/7] API: restore broken merge request commenting --- lib/api/entities.rb | 5 +++++ lib/api/merge_requests.rb | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 0c44f621b08..b792d3fbf8d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -75,5 +75,10 @@ module Gitlab expose :author, using: Entities::UserBasic expose :updated_at, :created_at end + + class MRNote < Grape::Entity + expose :note + expose :author, using: Entities::UserBasic + end end end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index d8f2c51293a..1fa0c549b13 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -4,9 +4,9 @@ module Gitlab before { authenticate! } resource :projects do - + # List merge requests - # + # # Parameters: # id (required) - The ID or code name of a project # @@ -15,24 +15,24 @@ module Gitlab # get ":id/merge_requests" do authorize! :read_merge_request, user_project - + present paginate(user_project.merge_requests), with: Entities::MergeRequest end - + # Show MR - # + # # Parameters: # id (required) - The ID or code name of a project # merge_request_id (required) - The ID of MR - # + # # Example: # GET /projects/:id/merge_request/:merge_request_id # get ":id/merge_request/:merge_request_id" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) - + authorize! :read_merge_request, merge_request - + present merge_request, with: Entities::MergeRequest end @@ -45,17 +45,17 @@ module Gitlab # target_branch (required) - The target branch # assignee_id - Assignee user ID # title (required) - Title of MR - # + # # Example: # POST /projects/:id/merge_requests # post ":id/merge_requests" do authorize! :write_merge_request, user_project - + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] merge_request = user_project.merge_requests.new(attrs) merge_request.author = current_user - + if merge_request.save merge_request.reload_code present merge_request, with: Entities::MergeRequest @@ -80,9 +80,9 @@ module Gitlab put ":id/merge_request/:merge_request_id" do attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :closed] merge_request = user_project.merge_requests.find(params[:merge_request_id]) - + authorize! :modify_merge_request, merge_request - + if merge_request.update_attributes attrs merge_request.reload_code merge_request.mark_as_unchecked @@ -98,7 +98,7 @@ module Gitlab # id (required) - The ID or code name of a project # merge_request_id (required) - ID of MR # note (required) - Text of comment - # Examples: + # Examples: # POST /projects/:id/merge_request/:merge_request_id/comments # post ":id/merge_request/:merge_request_id/comments" do @@ -107,7 +107,7 @@ module Gitlab note.author = current_user if note.save - present note, with: Entities::Note + present note, with: Entities::MRNote else not_found! end -- GitLab From 658f260e9835800901462e5a5de350ee84fefe8d Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 29 Nov 2012 12:32:05 -0800 Subject: [PATCH 6/7] add docs for notes API --- app/views/help/api.html.haml | 11 ++++ doc/api/notes.md | 121 +++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 doc/api/notes.md diff --git a/app/views/help/api.html.haml b/app/views/help/api.html.haml index 00085166bcf..3f16637dd2e 100644 --- a/app/views/help/api.html.haml +++ b/app/views/help/api.html.haml @@ -21,6 +21,8 @@ = link_to "Issues", "#issues", 'data-toggle' => 'tab' %li = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' + %li + = link_to "Notes", "#notes", 'data-toggle' => 'tab' .tab-content .tab-pane.active#README @@ -94,3 +96,12 @@ .file_content.wiki = preserve do = 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")) diff --git a/doc/api/notes.md b/doc/api/notes.md new file mode 100644 index 00000000000..3d8309d3b9d --- /dev/null +++ b/doc/api/notes.md @@ -0,0 +1,121 @@ +## 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. -- GitLab From 24047e1e8dca4baf58e1533e3e7fcfa1e16d159e Mon Sep 17 00:00:00 2001 From: Nihad Abbasov Date: Thu, 29 Nov 2012 12:35:16 -0800 Subject: [PATCH 7/7] update CHANGELOG --- CHANGELOG | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c939e159a17..4b28da4682b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,7 @@ 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 - added username field to user - rake task to fill usernames based on emails create namespaces for users @@ -10,7 +13,7 @@ v 3.2.0 - Fixes commit patches getting escaped (see #2036) - Support diff and patch generation for commits and merge request - 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 - Updated gems @@ -48,7 +51,7 @@ v 3.0.0 - Fixed bug with gitolite keys - UI improved - 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 - Use Font Awsome for icons - Separate observing of Note and MergeRequestsa -- GitLab