Commit 4317a2a3 authored by Sean McGivern's avatar Sean McGivern Committed by Rémy Coutable

Fix `updated_at` doesn't apply to `state_event` updates of issues via API

parent 425377f3
......@@ -13,6 +13,14 @@ module Noteable
end
end
# The timestamp of the note (e.g. the :updated_at attribute if provided via
# API call)
def system_note_timestamp
@system_note_timestamp || Time.now # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
attr_writer :system_note_timestamp
def base_class_name
self.class.base_class.name
end
......
......@@ -90,7 +90,7 @@ class Issue < ApplicationRecord
state :closed
before_transition any => :closed do |issue|
issue.closed_at = Time.zone.now
issue.closed_at = issue.system_note_timestamp
end
before_transition closed: :opened do |issue|
......
......@@ -5,7 +5,9 @@ class NoteSummary
attr_reader :metadata
def initialize(noteable, project, author, body, action: nil, commit_count: nil)
@note = { noteable: noteable, project: project, author: author, note: body }
@note = { noteable: noteable,
created_at: noteable.system_note_timestamp,
project: project, author: author, note: body }
@metadata = { action: action, commit_count: commit_count }.compact
set_commit_params if note[:noteable].is_a?(Commit)
......
......@@ -12,7 +12,7 @@ module ResourceEvents
label_hash = {
resource_column(resource) => resource.id,
user_id: user.id,
created_at: Time.now
created_at: resource.system_note_timestamp
}
labels = added_labels.map do |label|
......
......@@ -258,7 +258,7 @@ module SystemNoteService
body = "created #{issue.to_reference} to continue this discussion"
note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
note = Note.create(note_attributes.merge(system: true))
note = Note.create(note_attributes.merge(system: true, created_at: issue.system_note_timestamp))
note.system_note_metadata = SystemNoteMetadata.new(action: 'discussion')
note
......
---
title: "Respect updated_at attribute in notes produced by API calls"
merge_request: 27124
author: Ben Gamari
type: fixed
......@@ -230,10 +230,14 @@ module API
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
# Setting created_at time only allowed for admins and project/group owners
unless current_user.admin? || user_project.owner == current_user || current_user.owned_groups.include?(user_project.owner)
# Setting updated_at only allowed for admins and owners as well
if params[:updated_at].present?
if current_user.admin? || user_project.owner == current_user || current_user.owned_groups.include?(user_project.owner)
issue.system_note_timestamp = params[:updated_at]
else
params.delete(:updated_at)
end
end
update_params = declared_params(include_missing: false).merge(request: request, api: true)
......
......@@ -21,16 +21,20 @@ describe NoteSummary do
describe '#note' do
it 'returns note hash' do
expect(create_note_summary.note).to eq(noteable: noteable, project: project, author: user, note: 'note')
Timecop.freeze do
expect(create_note_summary.note).to eq(noteable: noteable, project: project, author: user, note: 'note',
created_at: Time.now)
end
end
context 'when noteable is a commit' do
let(:noteable) { build(:commit) }
let(:noteable) { build(:commit, system_note_timestamp: Time.at(43)) }
it 'returns note hash specific to commit' do
expect(create_note_summary.note).to eq(
noteable: nil, project: project, author: user, note: 'note',
noteable_type: 'Commit', commit_id: noteable.id
noteable_type: 'Commit', commit_id: noteable.id,
created_at: Time.at(43)
)
end
end
......
......@@ -11,6 +11,14 @@ describe SystemNoteService do
let(:noteable) { create(:issue, project: project) }
let(:issue) { noteable }
shared_examples_for 'a note with overridable created_at' do
let(:noteable) { create(:issue, project: project, system_note_timestamp: Time.at(42)) }
it 'the note has the correct time' do
expect(subject.created_at).to eq Time.at(42)
end
end
shared_examples_for 'a system note' do
let(:expected_noteable) { noteable }
let(:commit_count) { nil }
......@@ -137,6 +145,8 @@ describe SystemNoteService do
end
context 'when assignee added' do
it_behaves_like 'a note with overridable created_at'
it 'sets the note text' do
expect(subject.note).to eq "assigned to @#{assignee.username}"
end
......@@ -145,6 +155,8 @@ describe SystemNoteService do
context 'when assignee removed' do
let(:assignee) { nil }
it_behaves_like 'a note with overridable created_at'
it 'sets the note text' do
expect(subject.note).to eq 'removed assignee'
end
......@@ -168,6 +180,8 @@ describe SystemNoteService do
described_class.change_issue_assignees(issue, project, author, old_assignees).note
end
it_behaves_like 'a note with overridable created_at'
it 'builds a correct phrase when an assignee is added to a non-assigned issue' do
expect(build_note([], [assignee1])).to eq "assigned to @#{assignee1.username}"
end
......@@ -213,6 +227,8 @@ describe SystemNoteService do
expect(subject.note).to eq "changed milestone to #{reference}"
end
it_behaves_like 'a note with overridable created_at'
end
context 'when milestone removed' do
......@@ -221,6 +237,8 @@ describe SystemNoteService do
it 'sets the note text' do
expect(subject.note).to eq 'removed milestone'
end
it_behaves_like 'a note with overridable created_at'
end
end
......@@ -237,6 +255,8 @@ describe SystemNoteService do
it 'sets the note text to use the milestone name' do
expect(subject.note).to eq "changed milestone to #{milestone.to_reference(format: :name)}"
end
it_behaves_like 'a note with overridable created_at'
end
context 'when milestone removed' do
......@@ -245,6 +265,8 @@ describe SystemNoteService do
it 'sets the note text' do
expect(subject.note).to eq 'removed milestone'
end
it_behaves_like 'a note with overridable created_at'
end
end
end
......@@ -254,6 +276,8 @@ describe SystemNoteService do
let(:due_date) { Date.today }
it_behaves_like 'a note with overridable created_at'
it_behaves_like 'a system note' do
let(:action) { 'due_date' }
end
......@@ -280,6 +304,8 @@ describe SystemNoteService do
let(:status) { 'reopened' }
let(:source) { nil }
it_behaves_like 'a note with overridable created_at'
it_behaves_like 'a system note' do
let(:action) { 'opened' }
end
......@@ -289,6 +315,8 @@ describe SystemNoteService do
let(:status) { 'opened' }
let(:source) { double('commit', gfm_reference: 'commit 123456') }
it_behaves_like 'a note with overridable created_at'
it 'sets the note text' do
expect(subject.note).to eq "#{status} via commit 123456"
end
......@@ -338,6 +366,8 @@ describe SystemNoteService do
let(:action) { 'title' }
end
it_behaves_like 'a note with overridable created_at'
it 'sets the note text' do
expect(subject.note)
.to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**"
......@@ -353,6 +383,8 @@ describe SystemNoteService do
let(:action) { 'description' }
end
it_behaves_like 'a note with overridable created_at'
it 'sets the note text' do
expect(subject.note).to eq('changed the description')
end
......@@ -478,6 +510,8 @@ describe SystemNoteService do
let(:action) { 'cross_reference' }
end
it_behaves_like 'a note with overridable created_at'
describe 'note_body' do
context 'cross-project' do
let(:project2) { create(:project, :repository) }
......
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