Project

General

Profile

Feature #44288 ยป 0001-Add-reactions-to-REST-API-responses-for-issues-and-n.patch

Go MAEDA, 2026-07-26 06:14

View differences:

app/helpers/reactions_helper.rb
47 47
    dom_id(object, :reaction)
48 48
  end
49 49

  
50
  def render_api_reactions(object, api)
51
    return unless Redmine::Reaction.visible?(object, User.current)
52

  
53
    api.array :reactions do
54
      object.reaction_detail.visible_users.each do |user|
55
        api.reaction do
56
          api.user :id => user.id, :name => user.name
57
        end
58
      end
59
    end
60
  end
61

  
50 62
  private
51 63

  
52 64
  def reaction_button_reacted(object, reaction, count, tooltip)
app/views/issues/show.api.rsb
29 29
  api.updated_on @issue.updated_on
30 30
  api.closed_on @issue.closed_on
31 31

  
32
  render_api_reactions(@issue, api) if include_in_api_response?('reactions')
33

  
32 34
  render_api_issue_children(@issue, api) if include_in_api_response?('children')
33 35

  
34 36
  api.array :attachments do
......
62 64
        api.updated_on journal.updated_on
63 65
        api.updated_by(:id => journal.updated_by.id, :name => journal.updated_by.name) unless journal.updated_by.nil?
64 66
        api.private_notes journal.private_notes
67
        render_api_reactions(journal, api) if include_in_api_response?('reactions')
65 68
        api.array :details do
66 69
          journal.visible_details.each do |detail|
67 70
            api.detail :property => detail.property, :name => detail.prop_key do
app/views/news/show.api.rsb
7 7
  api.description @news.description
8 8
  api.created_on @news.created_on
9 9

  
10
  render_api_reactions(@news, api) if include_in_api_response?('reactions')
11

  
10 12
  api.array :attachments do
11 13
    @news.attachments.each do |attachment|
12 14
      render_api_attachment(attachment, api)
......
18 20
      api.comment :id => comment.id do
19 21
        api.author(:id => comment.author_id, :name => comment.author.name) unless comment.author.nil?
20 22
        api.content comment.content
23
        render_api_reactions(comment, api) if include_in_api_response?('reactions')
21 24
      end
22 25
    end
23 26
  end if include_in_api_response?('comments')
test/integration/api_test/issues_test.rb
379 379
    assert_select 'issue>status[is_closed=false]'
380 380
  end
381 381

  
382
  test "GET /issues/:id.xml should not include reactions by default" do
383
    get '/issues/1.xml', :headers => credentials('admin')
384

  
385
    assert_response :ok
386
    assert_select 'issue>reactions', 0
387
  end
388

  
389
  test "GET /issues/:id.xml?include=reactions should include reactions" do
390
    get '/issues/1.xml?include=reactions', :headers => credentials('admin')
391

  
392
    assert_response :ok
393
    assert_equal 'application/xml', response.media_type
394
    assert_select 'issue>reactions[type=array]' do
395
      assert_select 'reaction', 3
396
      assert_select 'reaction>user[id="1"][name="Redmine Admin"]'
397
      assert_select 'reaction>user[id="2"][name="John Smith"]'
398
      assert_select 'reaction>user[id="3"][name="Dave Lopper"]'
399
    end
400
  end
401

  
402
  test "GET /issues/:id.json?include=reactions should include reactions" do
403
    get '/issues/1.json?include=reactions', :headers => credentials('admin')
404

  
405
    assert_response :ok
406
    assert_equal 'application/json', response.media_type
407
    json = ActiveSupport::JSON.decode(response.body)
408
    reactions = json['issue']['reactions'].sort_by {|reaction| reaction['user']['id']}
409

  
410
    assert_equal(
411
      [
412
        {'user' => {'id' => 1, 'name' => 'Redmine Admin'}},
413
        {'user' => {'id' => 2, 'name' => 'John Smith'}},
414
        {'user' => {'id' => 3, 'name' => 'Dave Lopper'}}
415
      ],
416
      reactions
417
    )
418
  end
419

  
420
  test "GET /issues/:id.json?include=reactions should return an empty array when there are no reactions" do
421
    get '/issues/2.json?include=reactions', :headers => credentials('admin')
422

  
423
    assert_response :ok
424
    json = ActiveSupport::JSON.decode(response.body)
425

  
426
    assert_equal [], json['issue']['reactions']
427
  end
428

  
429
  test "GET /issues/:id.xml?include=journals,reactions should include journal reactions" do
430
    get '/issues/1.xml?include=journals,reactions', :headers => credentials('admin')
431

  
432
    assert_response :ok
433
    assert_select 'issue journals[type=array]' do
434
      assert_select 'journal[id="1"]>reactions[type=array]' do
435
        assert_select 'reaction', 1
436
        assert_select 'reaction>user[id="2"][name="John Smith"]'
437
      end
438
    end
439
  end
440

  
441
  test "GET /issues/:id.xml?include=reactions should not include reactions when reactions are disabled" do
442
    with_settings :reactions_enabled => '0' do
443
      get '/issues/1.xml?include=reactions', :headers => credentials('admin')
444
    end
445

  
446
    assert_response :ok
447
    assert_select 'issue>reactions', 0
448
  end
449

  
450
  test "GET /issues/:id.xml?include=reactions should not disclose hidden reaction users" do
451
    user = User.generate! do |u|
452
      u.password = 'secret123'
453
      u.password_confirmation = 'secret123'
454
    end
455
    role = Role.generate!(
456
      :users_visibility => 'members_of_visible_projects',
457
      :permissions => [:view_issues]
458
    )
459
    User.add_to_project(user, projects(:projects_001), role)
460

  
461
    get '/issues/1.xml?include=reactions', :headers => credentials(user.login, 'secret123')
462

  
463
    assert_response :ok
464
    assert_select 'issue>reactions[type=array]' do
465
      assert_select 'reaction', 2
466
      assert_select 'user[id="1"]', 0
467
      assert_select 'reaction>user[id="2"][name="John Smith"]'
468
      assert_select 'reaction>user[id="3"][name="Dave Lopper"]'
469
    end
470
  end
471

  
382 472
  test "GET /issues/:id.xml?include=watchers should include watchers" do
383 473
    Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
384 474

  
test/integration/api_test/news_test.rb
75 75
    assert_equal 1, json['news']['id']
76 76
  end
77 77

  
78
  test "GET /news/:id.xml?include=reactions should include reactions" do
79
    get "/news/1.xml?include=reactions", :headers => credentials('admin')
80

  
81
    assert_response :success
82
    assert_equal 'application/xml', response.media_type
83
    assert_select 'news>reactions[type=array]' do
84
      assert_select 'reaction', 1
85
      assert_select 'reaction>user[id="1"][name="Redmine Admin"]'
86
    end
87
  end
88

  
78 89
  test "GET /news/:id.xml with attachments" do
79 90
    news = News.find(1)
80 91
    attachment = Attachment.first
......
105 116
    end
106 117
  end
107 118

  
119
  test "GET /news/:id.xml?include=comments,reactions should include comment reactions" do
120
    get "/news/1.xml?include=comments,reactions", :headers => credentials('admin')
121

  
122
    assert_response :success
123
    assert_equal 'application/xml', response.media_type
124
    assert_select 'news comments[type=array]' do
125
      assert_select 'comment[id="1"]>reactions[type=array]' do
126
        assert_select 'reaction', 1
127
        assert_select 'reaction>user[id="2"][name="John Smith"]'
128
      end
129
    end
130
  end
131

  
108 132
  test "POST /project/:project_id/news.xml should create a news with the attributes" do
109 133
    payload = <<~XML
110 134
      <?xml version="1.0" encoding="UTF-8" ?>
    (1-1/1)