Commit f34f9a9ef364a5d67abece6dc235b015aa0fb380
Exists in
spb-stable
and in
2 other branches
Merge branch 'notes_finder_updated_at' into 'master'
NotesFinder filters by updated_at
Showing
7 changed files
with
62 additions
and
5 deletions
Show diff stats
app/assets/javascripts/notes.js.coffee
| 1 | class Notes | 1 | class Notes |
| 2 | @interval: null | 2 | @interval: null |
| 3 | 3 | ||
| 4 | - constructor: (notes_url, note_ids) -> | 4 | + constructor: (notes_url, note_ids, last_fetched_at) -> |
| 5 | @notes_url = notes_url | 5 | @notes_url = notes_url |
| 6 | @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root? | 6 | @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root? |
| 7 | @note_ids = note_ids | 7 | @note_ids = note_ids |
| 8 | + @last_fetched_at = last_fetched_at | ||
| 8 | @initRefresh() | 9 | @initRefresh() |
| 9 | @setupMainTargetNoteForm() | 10 | @setupMainTargetNoteForm() |
| 10 | @cleanBinding() | 11 | @cleanBinding() |
| @@ -76,9 +77,11 @@ class Notes | @@ -76,9 +77,11 @@ class Notes | ||
| 76 | getContent: -> | 77 | getContent: -> |
| 77 | $.ajax | 78 | $.ajax |
| 78 | url: @notes_url | 79 | url: @notes_url |
| 80 | + data: "last_fetched_at=" + @last_fetched_at | ||
| 79 | dataType: "json" | 81 | dataType: "json" |
| 80 | success: (data) => | 82 | success: (data) => |
| 81 | notes = data.notes | 83 | notes = data.notes |
| 84 | + @last_fetched_at = data.last_fetched_at | ||
| 82 | $.each notes, (i, note) => | 85 | $.each notes, (i, note) => |
| 83 | @renderNote(note) | 86 | @renderNote(note) |
| 84 | 87 |
app/controllers/projects/notes_controller.rb
| @@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController | @@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController | ||
| 5 | before_filter :authorize_admin_note!, only: [:update, :destroy] | 5 | before_filter :authorize_admin_note!, only: [:update, :destroy] |
| 6 | 6 | ||
| 7 | def index | 7 | def index |
| 8 | + current_fetched_at = Time.now.to_i | ||
| 8 | @notes = NotesFinder.new.execute(project, current_user, params) | 9 | @notes = NotesFinder.new.execute(project, current_user, params) |
| 9 | 10 | ||
| 10 | - notes_json = { notes: [] } | 11 | + notes_json = { notes: [], last_fetched_at: current_fetched_at } |
| 11 | 12 | ||
| 12 | @notes.each do |note| | 13 | @notes.each do |note| |
| 13 | notes_json[:notes] << { | 14 | notes_json[:notes] << { |
app/finders/notes_finder.rb
| 1 | class NotesFinder | 1 | class NotesFinder |
| 2 | + FETCH_OVERLAP = 5.seconds | ||
| 3 | + | ||
| 2 | def execute(project, current_user, params) | 4 | def execute(project, current_user, params) |
| 3 | target_type = params[:target_type] | 5 | target_type = params[:target_type] |
| 4 | target_id = params[:target_id] | 6 | target_id = params[:target_id] |
| 7 | + # Default to 0 to remain compatible with old clients | ||
| 8 | + last_fetched_at = Time.at(params.fetch(:last_fetched_at, 0).to_i) | ||
| 5 | 9 | ||
| 6 | - case target_type | 10 | + notes = case target_type |
| 7 | when "commit" | 11 | when "commit" |
| 8 | project.notes.for_commit_id(target_id).not_inline.fresh | 12 | project.notes.for_commit_id(target_id).not_inline.fresh |
| 9 | when "issue" | 13 | when "issue" |
| @@ -12,6 +16,11 @@ class NotesFinder | @@ -12,6 +16,11 @@ class NotesFinder | ||
| 12 | project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh | 16 | project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh |
| 13 | when "snippet" | 17 | when "snippet" |
| 14 | project.snippets.find(target_id).notes.fresh | 18 | project.snippets.find(target_id).notes.fresh |
| 19 | + else | ||
| 20 | + raise 'invalid target_type' | ||
| 15 | end | 21 | end |
| 22 | + | ||
| 23 | + # Use overlapping intervals to avoid worrying about race conditions | ||
| 24 | + notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP) | ||
| 16 | end | 25 | end |
| 17 | end | 26 | end |
app/views/projects/notes/_notes_with_form.html.haml
| @@ -7,4 +7,4 @@ | @@ -7,4 +7,4 @@ | ||
| 7 | = render "projects/notes/form" | 7 | = render "projects/notes/form" |
| 8 | 8 | ||
| 9 | :javascript | 9 | :javascript |
| 10 | - new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}) | 10 | + new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}) |
db/schema.rb
| @@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
| 11 | # | 11 | # |
| 12 | # It's strongly recommended that you check this file into your version control system. | 12 | # It's strongly recommended that you check this file into your version control system. |
| 13 | 13 | ||
| 14 | -ActiveRecord::Schema.define(version: 20140416185734) do | 14 | +ActiveRecord::Schema.define(version: 20140428105831) do |
| 15 | 15 | ||
| 16 | # These are extensions that must be enabled in order to support this database | 16 | # These are extensions that must be enabled in order to support this database |
| 17 | enable_extension "plpgsql" | 17 | enable_extension "plpgsql" |
| @@ -200,6 +200,7 @@ ActiveRecord::Schema.define(version: 20140416185734) do | @@ -200,6 +200,7 @@ ActiveRecord::Schema.define(version: 20140416185734) do | ||
| 200 | add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree | 200 | add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree |
| 201 | add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree | 201 | add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree |
| 202 | add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree | 202 | add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree |
| 203 | + add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree | ||
| 203 | 204 | ||
| 204 | create_table "projects", force: true do |t| | 205 | create_table "projects", force: true do |t| |
| 205 | t.string "name" | 206 | t.string "name" |
| @@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe NotesFinder do | ||
| 4 | + let(:user) { create :user } | ||
| 5 | + let(:project) { create :project } | ||
| 6 | + let(:note1) { create :note_on_commit, project: project } | ||
| 7 | + let(:note2) { create :note_on_commit, project: project } | ||
| 8 | + let(:commit) { note1.noteable } | ||
| 9 | + | ||
| 10 | + before do | ||
| 11 | + project.team << [user, :master] | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + describe :execute do | ||
| 15 | + let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } } | ||
| 16 | + | ||
| 17 | + before do | ||
| 18 | + note1 | ||
| 19 | + note2 | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + it 'should find all notes' do | ||
| 23 | + notes = NotesFinder.new.execute(project, user, params) | ||
| 24 | + notes.size.should eq(2) | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + it 'should raise an exception for an invalid target_type' do | ||
| 28 | + params.merge!(target_type: 'invalid') | ||
| 29 | + expect { NotesFinder.new.execute(project, user, params) }.to raise_error('invalid target_type') | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + it 'filters out old notes' do | ||
| 33 | + note2.update_attribute(:updated_at, 2.hours.ago) | ||
| 34 | + notes = NotesFinder.new.execute(project, user, params) | ||
| 35 | + notes.should eq([note1]) | ||
| 36 | + end | ||
| 37 | + end | ||
| 38 | +end |