Commit f34f9a9ef364a5d67abece6dc235b015aa0fb380

Authored by Dmitriy Zaporozhets
2 parents 27f31a7e bd8b2b7f

Merge branch 'notes_finder_updated_at' into 'master'

NotesFinder filters by updated_at
app/assets/javascripts/notes.js.coffee
1 1 class Notes
2 2 @interval: null
3 3  
4   - constructor: (notes_url, note_ids) ->
  4 + constructor: (notes_url, note_ids, last_fetched_at) ->
5 5 @notes_url = notes_url
6 6 @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
7 7 @note_ids = note_ids
  8 + @last_fetched_at = last_fetched_at
8 9 @initRefresh()
9 10 @setupMainTargetNoteForm()
10 11 @cleanBinding()
... ... @@ -76,9 +77,11 @@ class Notes
76 77 getContent: ->
77 78 $.ajax
78 79 url: @notes_url
  80 + data: "last_fetched_at=" + @last_fetched_at
79 81 dataType: "json"
80 82 success: (data) =>
81 83 notes = data.notes
  84 + @last_fetched_at = data.last_fetched_at
82 85 $.each notes, (i, note) =>
83 86 @renderNote(note)
84 87  
... ...
app/controllers/projects/notes_controller.rb
... ... @@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController
5 5 before_filter :authorize_admin_note!, only: [:update, :destroy]
6 6  
7 7 def index
  8 + current_fetched_at = Time.now.to_i
8 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 13 @notes.each do |note|
13 14 notes_json[:notes] << {
... ...
app/finders/notes_finder.rb
1 1 class NotesFinder
  2 + FETCH_OVERLAP = 5.seconds
  3 +
2 4 def execute(project, current_user, params)
3 5 target_type = params[:target_type]
4 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 11 when "commit"
8 12 project.notes.for_commit_id(target_id).not_inline.fresh
9 13 when "issue"
... ... @@ -12,6 +16,11 @@ class NotesFinder
12 16 project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
13 17 when "snippet"
14 18 project.snippets.find(target_id).notes.fresh
  19 + else
  20 + raise 'invalid target_type'
15 21 end
  22 +
  23 + # Use overlapping intervals to avoid worrying about race conditions
  24 + notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP)
16 25 end
17 26 end
... ...
app/views/projects/notes/_notes_with_form.html.haml
... ... @@ -7,4 +7,4 @@
7 7 = render "projects/notes/form"
8 8  
9 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/migrate/20140428105831_add_notes_index_updated_at.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class AddNotesIndexUpdatedAt < ActiveRecord::Migration
  2 + def change
  3 + add_index :notes, :updated_at
  4 + end
  5 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 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 16 # These are extensions that must be enabled in order to support this database
17 17 enable_extension "plpgsql"
... ... @@ -200,6 +200,7 @@ ActiveRecord::Schema.define(version: 20140416185734) do
200 200 add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
201 201 add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
202 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 205 create_table "projects", force: true do |t|
205 206 t.string "name"
... ...
spec/finders/notes_finder_spec.rb 0 → 100644
... ... @@ -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
... ...