activity_notification 2.0.0 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +22 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  4. data/.github/pull_request_template.md +13 -0
  5. data/.gitignore +10 -3
  6. data/.travis.yml +6 -5
  7. data/CHANGELOG.md +60 -0
  8. data/Gemfile +8 -3
  9. data/Procfile +1 -1
  10. data/README.md +153 -1510
  11. data/activity_notification.gemspec +4 -1
  12. data/app/channels/activity_notification/notification_api_channel.rb +12 -0
  13. data/app/channels/activity_notification/notification_api_with_devise_channel.rb +46 -0
  14. data/app/channels/activity_notification/notification_channel.rb +2 -2
  15. data/app/channels/activity_notification/notification_with_devise_channel.rb +2 -2
  16. data/app/controllers/activity_notification/apidocs_controller.rb +75 -0
  17. data/app/controllers/activity_notification/notifications_api_controller.rb +143 -0
  18. data/app/controllers/activity_notification/notifications_api_with_devise_controller.rb +7 -0
  19. data/app/controllers/activity_notification/notifications_controller.rb +79 -53
  20. data/app/controllers/activity_notification/subscriptions_api_controller.rb +197 -0
  21. data/app/controllers/activity_notification/subscriptions_api_with_devise_controller.rb +7 -0
  22. data/app/controllers/activity_notification/subscriptions_controller.rb +78 -69
  23. data/app/views/activity_notification/notifications/default/_default.html.erb +18 -18
  24. data/app/views/activity_notification/notifications/default/_default_without_grouping.html.erb +14 -14
  25. data/app/views/activity_notification/notifications/default/index.html.erb +6 -6
  26. data/app/views/activity_notification/optional_targets/default/action_cable_channel/_default.html.erb +176 -0
  27. data/app/views/activity_notification/subscriptions/default/_form.html.erb +1 -1
  28. data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +3 -31
  29. data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +7 -7
  30. data/app/views/activity_notification/subscriptions/default/index.html.erb +11 -7
  31. data/bin/deploy_on_heroku.sh +3 -1
  32. data/docs/CODE_OF_CONDUCT.md +76 -0
  33. data/docs/CONTRIBUTING.md +36 -0
  34. data/docs/Functions.md +1130 -0
  35. data/docs/Setup.md +801 -0
  36. data/docs/Testing.md +148 -0
  37. data/gemfiles/Gemfile.rails-4.2 +5 -1
  38. data/gemfiles/Gemfile.rails-5.0 +6 -1
  39. data/gemfiles/Gemfile.rails-5.1 +6 -1
  40. data/gemfiles/Gemfile.rails-5.2 +6 -1
  41. data/gemfiles/{Gemfile.rails-6.0.rc → Gemfile.rails-6.0} +6 -5
  42. data/lib/activity_notification.rb +13 -0
  43. data/lib/activity_notification/apis/notification_api.rb +37 -93
  44. data/lib/activity_notification/apis/subscription_api.rb +20 -8
  45. data/lib/activity_notification/apis/swagger.rb +6 -0
  46. data/lib/activity_notification/common.rb +4 -1
  47. data/lib/activity_notification/config.rb +41 -21
  48. data/lib/activity_notification/controllers/common_api_controller.rb +30 -0
  49. data/lib/activity_notification/controllers/common_controller.rb +45 -21
  50. data/lib/activity_notification/controllers/concerns/swagger/error_responses.rb +55 -0
  51. data/lib/activity_notification/controllers/concerns/swagger/notifications_api.rb +273 -0
  52. data/lib/activity_notification/controllers/concerns/swagger/notifications_parameters.rb +92 -0
  53. data/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb +405 -0
  54. data/lib/activity_notification/controllers/concerns/swagger/subscriptions_parameters.rb +50 -0
  55. data/lib/activity_notification/controllers/devise_authentication_controller.rb +7 -6
  56. data/lib/activity_notification/gem_version.rb +14 -0
  57. data/lib/activity_notification/helpers/errors.rb +2 -0
  58. data/lib/activity_notification/helpers/view_helpers.rb +4 -0
  59. data/lib/activity_notification/mailers/helpers.rb +17 -10
  60. data/lib/activity_notification/models/concerns/notifiable.rb +31 -15
  61. data/lib/activity_notification/models/concerns/subscriber.rb +12 -1
  62. data/lib/activity_notification/models/concerns/swagger/error_schema.rb +36 -0
  63. data/lib/activity_notification/models/concerns/swagger/notification_schema.rb +209 -0
  64. data/lib/activity_notification/models/concerns/swagger/subscription_schema.rb +162 -0
  65. data/lib/activity_notification/models/concerns/target.rb +36 -10
  66. data/lib/activity_notification/models/notification.rb +1 -0
  67. data/lib/activity_notification/models/subscription.rb +1 -0
  68. data/lib/activity_notification/optional_targets/action_cable_api_channel.rb +69 -0
  69. data/lib/activity_notification/optional_targets/action_cable_channel.rb +68 -0
  70. data/lib/activity_notification/optional_targets/base.rb +7 -13
  71. data/lib/activity_notification/orm/active_record/notification.rb +17 -1
  72. data/lib/activity_notification/orm/active_record/subscription.rb +1 -1
  73. data/lib/activity_notification/orm/dynamoid.rb +38 -3
  74. data/lib/activity_notification/orm/dynamoid/extension.rb +79 -1
  75. data/lib/activity_notification/orm/dynamoid/notification.rb +49 -14
  76. data/lib/activity_notification/orm/dynamoid/subscription.rb +2 -2
  77. data/lib/activity_notification/orm/mongoid.rb +32 -3
  78. data/lib/activity_notification/orm/mongoid/notification.rb +24 -6
  79. data/lib/activity_notification/orm/mongoid/subscription.rb +1 -1
  80. data/lib/activity_notification/rails/routes.rb +132 -48
  81. data/lib/activity_notification/renderable.rb +13 -2
  82. data/lib/activity_notification/roles/acts_as_notifiable.rb +39 -20
  83. data/lib/activity_notification/version.rb +1 -1
  84. data/lib/generators/activity_notification/controllers_generator.rb +2 -1
  85. data/lib/generators/templates/activity_notification.rb +8 -0
  86. data/lib/generators/templates/controllers/notifications_api_controller.rb +31 -0
  87. data/lib/generators/templates/controllers/notifications_api_with_devise_controller.rb +31 -0
  88. data/lib/generators/templates/controllers/notifications_controller.rb +1 -37
  89. data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +1 -45
  90. data/lib/generators/templates/controllers/subscriptions_api_controller.rb +61 -0
  91. data/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb +61 -0
  92. data/lib/generators/templates/controllers/subscriptions_controller.rb +14 -37
  93. data/lib/generators/templates/controllers/subscriptions_with_devise_controller.rb +14 -45
  94. data/lib/generators/templates/models/README +8 -4
  95. data/lib/generators/templates/models/notification.rb +1 -1
  96. data/lib/generators/templates/models/subscription.rb +1 -1
  97. data/package.json +8 -0
  98. data/spec/channels/notification_api_channel_shared_examples.rb +59 -0
  99. data/spec/channels/notification_api_channel_spec.rb +51 -0
  100. data/spec/channels/notification_api_with_devise_channel_spec.rb +78 -0
  101. data/spec/concerns/apis/notification_api_spec.rb +38 -3
  102. data/spec/concerns/models/notifiable_spec.rb +82 -18
  103. data/spec/concerns/models/subscriber_spec.rb +13 -16
  104. data/spec/concerns/models/target_spec.rb +32 -0
  105. data/spec/concerns/renderable_spec.rb +2 -2
  106. data/spec/config_spec.rb +26 -15
  107. data/spec/controllers/controller_spec_utility.rb +136 -0
  108. data/spec/controllers/notifications_api_controller_shared_examples.rb +506 -0
  109. data/spec/controllers/notifications_api_controller_spec.rb +19 -0
  110. data/spec/controllers/notifications_api_with_devise_controller_spec.rb +60 -0
  111. data/spec/controllers/notifications_controller_shared_examples.rb +54 -79
  112. data/spec/controllers/notifications_controller_spec.rb +1 -2
  113. data/spec/controllers/notifications_with_devise_controller_spec.rb +3 -12
  114. data/spec/controllers/subscriptions_api_controller_shared_examples.rb +750 -0
  115. data/spec/controllers/subscriptions_api_controller_spec.rb +19 -0
  116. data/spec/controllers/subscriptions_api_with_devise_controller_spec.rb +60 -0
  117. data/spec/controllers/subscriptions_controller_shared_examples.rb +94 -121
  118. data/spec/controllers/subscriptions_controller_spec.rb +1 -2
  119. data/spec/controllers/subscriptions_with_devise_controller_spec.rb +3 -12
  120. data/spec/helpers/view_helpers_spec.rb +4 -11
  121. data/spec/mailers/mailer_spec.rb +41 -0
  122. data/spec/models/notification_spec.rb +17 -0
  123. data/spec/models/subscription_spec.rb +8 -13
  124. data/spec/optional_targets/action_cable_api_channel_spec.rb +37 -0
  125. data/spec/optional_targets/action_cable_channel_spec.rb +44 -0
  126. data/spec/optional_targets/amazon_sns_spec.rb +0 -2
  127. data/spec/optional_targets/slack_spec.rb +0 -2
  128. data/spec/rails_app/Rakefile +9 -0
  129. data/spec/rails_app/app/assets/config/manifest.js +3 -0
  130. data/spec/rails_app/app/assets/images/.keep +0 -0
  131. data/spec/rails_app/app/controllers/admins_controller.rb +21 -0
  132. data/spec/rails_app/app/controllers/application_controller.rb +1 -1
  133. data/spec/rails_app/app/controllers/articles_controller.rb +6 -3
  134. data/spec/rails_app/app/controllers/spa_controller.rb +7 -0
  135. data/spec/rails_app/app/controllers/users/notifications_controller.rb +0 -65
  136. data/spec/rails_app/app/controllers/users/notifications_with_devise_controller.rb +0 -73
  137. data/spec/rails_app/app/controllers/users/subscriptions_controller.rb +0 -77
  138. data/spec/rails_app/app/controllers/users/subscriptions_with_devise_controller.rb +0 -85
  139. data/spec/rails_app/app/controllers/users_controller.rb +26 -0
  140. data/spec/rails_app/app/javascript/App.vue +40 -0
  141. data/spec/rails_app/app/javascript/components/DeviseTokenAuth.vue +82 -0
  142. data/spec/rails_app/app/javascript/components/Top.vue +98 -0
  143. data/spec/rails_app/app/javascript/components/notifications/Index.vue +200 -0
  144. data/spec/rails_app/app/javascript/components/notifications/Notification.vue +133 -0
  145. data/spec/rails_app/app/javascript/components/notifications/NotificationContent.vue +122 -0
  146. data/spec/rails_app/app/javascript/components/subscriptions/Index.vue +279 -0
  147. data/spec/rails_app/app/javascript/components/subscriptions/NewSubscription.vue +112 -0
  148. data/spec/rails_app/app/javascript/components/subscriptions/NotificationKey.vue +141 -0
  149. data/spec/rails_app/app/javascript/components/subscriptions/Subscription.vue +226 -0
  150. data/spec/rails_app/app/javascript/config/development.js +5 -0
  151. data/spec/rails_app/app/javascript/config/environment.js +7 -0
  152. data/spec/rails_app/app/javascript/config/production.js +5 -0
  153. data/spec/rails_app/app/javascript/config/test.js +5 -0
  154. data/spec/rails_app/app/javascript/packs/application.js +18 -0
  155. data/spec/rails_app/app/javascript/packs/spa.js +14 -0
  156. data/spec/rails_app/app/javascript/router/index.js +73 -0
  157. data/spec/rails_app/app/javascript/store/index.js +37 -0
  158. data/spec/rails_app/app/models/admin.rb +16 -15
  159. data/spec/rails_app/app/models/article.rb +26 -21
  160. data/spec/rails_app/app/models/comment.rb +24 -71
  161. data/spec/rails_app/app/models/dummy/dummy_group.rb +8 -0
  162. data/spec/rails_app/app/models/dummy/dummy_notifiable_target.rb +8 -0
  163. data/spec/rails_app/app/models/user.rb +44 -20
  164. data/spec/rails_app/app/views/activity_notification/notifications/default/article/_update.html.erb +146 -0
  165. data/spec/rails_app/app/views/articles/index.html.erb +51 -7
  166. data/spec/rails_app/app/views/articles/show.html.erb +1 -1
  167. data/spec/rails_app/app/views/layouts/_header.html.erb +8 -10
  168. data/spec/rails_app/app/views/spa/index.html.erb +2 -0
  169. data/spec/rails_app/babel.config.js +72 -0
  170. data/spec/rails_app/bin/webpack +18 -0
  171. data/spec/rails_app/bin/webpack-dev-server +18 -0
  172. data/spec/rails_app/config/application.rb +18 -2
  173. data/spec/rails_app/config/dynamoid.rb +11 -3
  174. data/spec/rails_app/config/environment.rb +2 -1
  175. data/spec/rails_app/config/environments/development.rb +5 -0
  176. data/spec/rails_app/config/environments/production.rb +6 -0
  177. data/spec/rails_app/config/environments/test.rb +5 -0
  178. data/spec/rails_app/config/initializers/activity_notification.rb +11 -3
  179. data/spec/rails_app/config/initializers/copy_it.aws.rb.template +6 -0
  180. data/spec/rails_app/config/initializers/devise_token_auth.rb +55 -0
  181. data/spec/rails_app/config/initializers/mysql.rb +9 -0
  182. data/spec/rails_app/config/locales/activity_notification.en.yml +2 -2
  183. data/spec/rails_app/config/routes.rb +37 -1
  184. data/spec/rails_app/config/webpack/development.js +5 -0
  185. data/spec/rails_app/config/webpack/environment.js +7 -0
  186. data/spec/rails_app/config/webpack/loaders/vue.js +6 -0
  187. data/spec/rails_app/config/webpack/production.js +5 -0
  188. data/spec/rails_app/config/webpack/test.js +5 -0
  189. data/spec/rails_app/config/webpacker.yml +97 -0
  190. data/spec/rails_app/db/migrate/20191201000000_add_tokens_to_users.rb +10 -0
  191. data/spec/rails_app/db/schema.rb +4 -1
  192. data/spec/rails_app/db/seeds.rb +10 -2
  193. data/spec/rails_app/lib/custom_optional_targets/raise_error.rb +14 -0
  194. data/spec/rails_app/package.json +23 -0
  195. data/spec/rails_app/postcss.config.js +12 -0
  196. data/spec/roles/acts_as_group_spec.rb +0 -2
  197. data/spec/roles/acts_as_notifiable_spec.rb +6 -8
  198. data/spec/roles/acts_as_notifier_spec.rb +0 -2
  199. data/spec/roles/acts_as_target_spec.rb +0 -4
  200. data/spec/spec_helper.rb +7 -15
  201. data/spec/version_spec.rb +31 -0
  202. metadata +191 -13
@@ -74,10 +74,9 @@ shared_examples_for :subscriber do
74
74
  end
75
75
 
76
76
  context "without params" do
77
- it "does not create a new subscription since it is invalid" do
78
- new_subscription = test_instance.create_subscription
79
- expect(new_subscription).to be_nil
80
- expect(test_instance.subscriptions.reload.to_a).to be_empty
77
+ it "raises ActivityNotification::RecordInvalidError it is invalid" do
78
+ expect { test_instance.create_subscription }
79
+ .to raise_error(ActivityNotification::RecordInvalidError)
81
80
  end
82
81
  end
83
82
 
@@ -122,16 +121,14 @@ shared_examples_for :subscriber do
122
121
  end
123
122
 
124
123
  context "with false as subscribing and true as subscribing_to_email params" do
125
- it "does not create a new subscription since it is invalid" do
126
- params = { key: 'key_1', subscribing: false, subscribing_to_email: true }
127
- new_subscription = test_instance.create_subscription(params)
128
- expect(new_subscription).to be_nil
129
- expect(test_instance.subscriptions.reload.to_a).to be_empty
124
+ it "raises ActivityNotification::RecordInvalidError it is invalid" do
125
+ expect {
126
+ params = { key: 'key_1', subscribing: false, subscribing_to_email: true }
127
+ test_instance.create_subscription(params)
128
+ }.to raise_error(ActivityNotification::RecordInvalidError)
130
129
  end
131
130
  end
132
131
 
133
-
134
-
135
132
  context "with true as optional_targets params" do
136
133
  it "creates a new subscription" do
137
134
  params = { key: 'key_1', optional_targets: { subscribing_to_console_output: true } }
@@ -163,11 +160,11 @@ shared_examples_for :subscriber do
163
160
  end
164
161
 
165
162
  context "with false as subscribing and true as optional_targets params" do
166
- it "does not create a new subscription since it is invalid" do
167
- params = { key: 'key_1', subscribing: false, optional_targets: { subscribing_to_console_output: true } }
168
- new_subscription = test_instance.create_subscription(params)
169
- expect(new_subscription).to be_nil
170
- expect(test_instance.subscriptions.reload.to_a).to be_empty
163
+ it "raises ActivityNotification::RecordInvalidError it is invalid" do
164
+ expect {
165
+ params = { key: 'key_1', subscribing: false, optional_targets: { subscribing_to_console_output: true } }
166
+ test_instance.create_subscription(params)
167
+ }.to raise_error(ActivityNotification::RecordInvalidError)
171
168
  end
172
169
  end
173
170
  end
@@ -729,6 +729,22 @@ shared_examples_for :target do
729
729
  end
730
730
  end
731
731
 
732
+ context 'with later_than options' do
733
+ it "returns filtered notifications only" do
734
+ options = { later_than: (@notification1.created_at.in_time_zone + 0.001).iso8601(3) }
735
+ expect(test_instance.notification_index(options)[0]).to eq(@notification3)
736
+ expect(test_instance.notification_index(options).size).to eq(1)
737
+ end
738
+ end
739
+
740
+ context 'with earlier_than options' do
741
+ it "returns filtered notifications only" do
742
+ options = { earlier_than: @notification1.created_at.iso8601(3) }
743
+ expect(test_instance.notification_index(options)[0]).to eq(@notification2)
744
+ expect(test_instance.notification_index(options).size).to eq(1)
745
+ end
746
+ end
747
+
732
748
  context 'with custom_filter options' do
733
749
  it "returns filtered notifications only" do
734
750
  options = { custom_filter: { key: @key } }
@@ -1009,6 +1025,22 @@ shared_examples_for :target do
1009
1025
  expect(test_instance.notification_index_with_attributes(options).size).to eq(1)
1010
1026
  end
1011
1027
  end
1028
+
1029
+ context 'with later_than options' do
1030
+ it "returns filtered notifications only" do
1031
+ options = { later_than: (@notification1.created_at.in_time_zone + 0.001).iso8601(3) }
1032
+ expect(test_instance.notification_index_with_attributes(options)[0]).to eq(@notification3)
1033
+ expect(test_instance.notification_index_with_attributes(options).size).to eq(1)
1034
+ end
1035
+ end
1036
+
1037
+ context 'with earlier_than options' do
1038
+ it "returns filtered notifications only" do
1039
+ options = { earlier_than: @notification1.created_at.iso8601(3) }
1040
+ expect(test_instance.notification_index_with_attributes(options)[0]).to eq(@notification2)
1041
+ expect(test_instance.notification_index_with_attributes(options).size).to eq(1)
1042
+ end
1043
+ end
1012
1044
  end
1013
1045
 
1014
1046
  context "when the target has no unopened notifications" do
@@ -13,11 +13,11 @@ shared_examples_for :renderable do
13
13
  let(:group_text_key) { 'comment.reply' }
14
14
  let(:plural_text_key) { 'comment.post' }
15
15
  let(:simple_text_original) { 'Article has been created' }
16
- let(:params_text_original) { "Article %{article_title} has been updated" }
16
+ let(:params_text_original) { 'Article "%{article_title}" has been updated' }
17
17
  let(:plural_text_original_one) { "<p>%{notifier_name} posted a comment on your article %{article_title}</p>" }
18
18
  let(:plural_text_original_other) { "<p>%{notifier_name} posted %{count} comments on your article %{article_title}</p>" }
19
19
  let(:group_text_original) { "<p>%{notifier_name} and %{group_member_count} other people replied %{group_notification_count} times to your comment</p>" }
20
- let(:params_text_embedded) { "Article bar has been updated" }
20
+ let(:params_text_embedded) { 'Article "bar" has been updated' }
21
21
  let(:group_text_embedded) { "<p>foo and 3 other people replied 4 times to your comment</p>" }
22
22
  let(:plural_text_embedded_one) { "<p>foo posted a comment on your article bar</p>" }
23
23
  let(:plural_text_embedded_other) { "<p>foo posted 4 comments on your article bar</p>" }
@@ -35,38 +35,49 @@ describe ActivityNotification::Config do
35
35
  describe "config.store_with_associated_records" do
36
36
  let(:target) { create(:confirmed_user) }
37
37
 
38
- context "false as default" do
39
- before do
40
- @notification = create(:notification, target: target)
41
- end
42
-
43
- it "stores notification without associated records" do
44
- expect(@notification.target).to eq(target)
45
- expect { @notification.target_record }.to raise_error(NoMethodError)
46
- end
47
- end
48
-
49
38
  context "when it is configured as true" do
50
39
  if ActivityNotification.config.orm == :active_record
51
40
  it "raises ActivityNotification::ConfigError when you use active_record ORM" do
52
41
  expect { ActivityNotification.config.store_with_associated_records = true }.to raise_error(ActivityNotification::ConfigError)
53
42
  end
54
-
55
43
  else
56
44
  before do
45
+ @original = ActivityNotification.config.store_with_associated_records
57
46
  ActivityNotification.config.store_with_associated_records = true
58
47
  load Rails.root.join("../../lib/activity_notification/orm/#{ActivityNotification.config.orm}/notification.rb").to_s
59
48
  @notification = create(:notification, target: target)
60
49
  end
61
50
 
62
51
  after do
63
- ActivityNotification.config.store_with_associated_records = false
52
+ ActivityNotification.config.store_with_associated_records = @original
64
53
  load Rails.root.join("../../lib/activity_notification/orm/#{ActivityNotification.config.orm}/notification.rb").to_s
65
54
  end
66
55
 
67
- it "stores notification without associated records" do
56
+ it "stores notification with associated records" do
68
57
  expect(@notification.target).to eq(target)
69
- expect(@notification.target_record).to eq(target.to_json)
58
+ expect(@notification.stored_target["id"].to_s).to eq(target.id.to_s)
59
+ end
60
+ end
61
+ end
62
+
63
+ context "when it is configured as false" do
64
+ before do
65
+ @original = ActivityNotification.config.store_with_associated_records
66
+ ActivityNotification.config.store_with_associated_records = false
67
+ load Rails.root.join("../../lib/activity_notification/orm/#{ActivityNotification.config.orm}/notification.rb").to_s
68
+ @notification = create(:notification, target: target)
69
+ end
70
+
71
+ after do
72
+ ActivityNotification.config.store_with_associated_records = @original
73
+ load Rails.root.join("../../lib/activity_notification/orm/#{ActivityNotification.config.orm}/notification.rb").to_s
74
+ end
75
+
76
+ it "does not store notification with associated records" do
77
+ expect(@notification.target).to eq(target)
78
+ begin
79
+ expect(@notification.stored_target).to be_nil
80
+ rescue NoMethodError
70
81
  end
71
82
  end
72
83
  end
@@ -0,0 +1,136 @@
1
+ module ActivityNotification
2
+ module ControllerSpec
3
+ module RequestUtility
4
+ def get_with_compatibility action, params, session
5
+ if Rails::VERSION::MAJOR <= 4
6
+ get action, params, session
7
+ else
8
+ get action, params: params, session: session
9
+ end
10
+ end
11
+
12
+ def post_with_compatibility action, params, session
13
+ if Rails::VERSION::MAJOR <= 4
14
+ post action, params, session
15
+ else
16
+ post action, params: params, session: session
17
+ end
18
+ end
19
+
20
+ def put_with_compatibility action, params, session
21
+ if Rails::VERSION::MAJOR <= 4
22
+ put action, params, session
23
+ else
24
+ put action, params: params, session: session
25
+ end
26
+ end
27
+
28
+ def delete_with_compatibility action, params, session
29
+ if Rails::VERSION::MAJOR <= 4
30
+ delete action, params, session
31
+ else
32
+ delete action, params: params, session: session
33
+ end
34
+ end
35
+
36
+ def xhr_with_compatibility method, action, params, session
37
+ if Rails::VERSION::MAJOR <= 4
38
+ xhr method, action, params, session
39
+ else
40
+ send method.to_s, action, xhr: true, params: params, session: session
41
+ end
42
+ end
43
+ end
44
+
45
+ module ApiResponseUtility
46
+ def response_json
47
+ JSON.parse(response.body)
48
+ end
49
+
50
+ def assert_json_with_array_size(json_array, size)
51
+ expect(json_array.size).to eq(size)
52
+ end
53
+
54
+ def assert_json_with_object(json_object, object)
55
+ expect(json_object['id'].to_s).to eq(object.id.to_s)
56
+ end
57
+
58
+ def assert_json_with_object_array(json_array, expected_object_array)
59
+ assert_json_with_array_size(json_array, expected_object_array.size)
60
+ expected_object_array.each_with_index do |json_object, index|
61
+ assert_json_with_object(json_object, expected_object_array[index])
62
+ end
63
+ end
64
+
65
+ def assert_error_response(code)
66
+ expect(response_json['gem']).to eq('activity_notification')
67
+ expect(response_json['error']['code']).to eq(code)
68
+ end
69
+ end
70
+
71
+ module CommitteeUtility
72
+ extend ActiveSupport::Concern
73
+ included do
74
+ include Committee::Rails::Test::Methods
75
+
76
+ def api_path
77
+ "/#{root_path}/#{target_type}/#{test_target.id}"
78
+ end
79
+
80
+ def schema_path
81
+ Rails.root.join('..', 'openapi.json')
82
+ end
83
+
84
+ def write_schema_file(schema_json)
85
+ File.open(schema_path, "w") { |file| file.write(schema_json) }
86
+ end
87
+
88
+ def read_schema_file
89
+ JSON.parse(File.read(schema_path))
90
+ end
91
+
92
+ def committee_options
93
+ @committee_options ||= { schema: Committee::Drivers::load_from_file(schema_path), prefix: root_path, validate_success_only: true }
94
+ end
95
+
96
+ def get_with_compatibility path, options = {}
97
+ if Rails::VERSION::MAJOR <= 4
98
+ get path, options[:params], options[:headers]
99
+ else
100
+ get path, options
101
+ end
102
+ end
103
+
104
+ def post_with_compatibility path, options = {}
105
+ if Rails::VERSION::MAJOR <= 4
106
+ post path, options[:params], options[:headers]
107
+ else
108
+ post path, options
109
+ end
110
+ end
111
+
112
+ def put_with_compatibility path, options = {}
113
+ if Rails::VERSION::MAJOR <= 4
114
+ put path, options[:params], options[:headers]
115
+ else
116
+ put path, options
117
+ end
118
+ end
119
+
120
+ def delete_with_compatibility path, options = {}
121
+ if Rails::VERSION::MAJOR <= 4
122
+ delete path, options[:params], options[:headers]
123
+ else
124
+ delete path, options
125
+ end
126
+ end
127
+
128
+ def assert_all_schema_confirm(response, status)
129
+ expect(response).to have_http_status(status)
130
+ assert_request_schema_confirm
131
+ assert_response_schema_confirm
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,506 @@
1
+ require_relative 'controller_spec_utility'
2
+
3
+ shared_examples_for :notifications_api_controller do
4
+ include ActivityNotification::ControllerSpec::RequestUtility
5
+ include ActivityNotification::ControllerSpec::ApiResponseUtility
6
+
7
+ let(:target_params) { { target_type: target_type }.merge(extra_params || {}) }
8
+
9
+ describe "GET #index" do
10
+ context "with target_type and target_id parameters" do
11
+ before do
12
+ @notification = create(:notification, target: test_target)
13
+ get_with_compatibility :index, target_params.merge({ target_id: test_target, typed_target_param => 'dummy' }), valid_session
14
+ end
15
+
16
+ it "returns 200 as http status code" do
17
+ expect(response.status).to eq(200)
18
+ end
19
+
20
+ it "returns JSON response" do
21
+ expect(response_json["count"]).to eq(1)
22
+ assert_json_with_object_array(response_json["notifications"], [@notification])
23
+ end
24
+ end
25
+
26
+ context "with target_type and (typed_target)_id parameters" do
27
+ before do
28
+ @notification = create(:notification, target: test_target)
29
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target }), valid_session
30
+ end
31
+
32
+ it "returns 200 as http status code" do
33
+ expect(response.status).to eq(200)
34
+ end
35
+ end
36
+
37
+ context "without target_type parameters" do
38
+ before do
39
+ @notification = create(:notification, target: test_target)
40
+ get_with_compatibility :index, { typed_target_param => test_target }, valid_session
41
+ end
42
+
43
+ it "returns 400 as http status code" do
44
+ expect(response.status).to eq(400)
45
+ end
46
+
47
+ it "returns error JSON response" do
48
+ assert_error_response(400)
49
+ end
50
+ end
51
+
52
+ context "with not found (typed_target)_id parameter" do
53
+ before do
54
+ @notification = create(:notification, target: test_target)
55
+ get_with_compatibility :index, target_params.merge({ typed_target_param => 0 }), valid_session
56
+ end
57
+
58
+ it "returns 404 as http status code" do
59
+ expect(response.status).to eq(404)
60
+ end
61
+
62
+ it "returns error JSON response" do
63
+ assert_error_response(404)
64
+ end
65
+ end
66
+
67
+ context "with filter parameter" do
68
+ context "with unopened as filter" do
69
+ before do
70
+ @notification = create(:notification, target: test_target)
71
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, filter: 'unopened' }), valid_session
72
+ end
73
+
74
+ it "returns unopened notification index as JSON" do
75
+ assert_json_with_object_array(response_json["notifications"], [@notification])
76
+ end
77
+ end
78
+
79
+ context "with opened as filter" do
80
+ before do
81
+ @notification = create(:notification, target: test_target)
82
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, filter: 'opened' }), valid_session
83
+ end
84
+
85
+ it "returns unopened notification index as JSON" do
86
+ assert_json_with_object_array(response_json["notifications"], [])
87
+ end
88
+ end
89
+ end
90
+
91
+ context "with limit parameter" do
92
+ before do
93
+ create(:notification, target: test_target)
94
+ create(:notification, target: test_target)
95
+ end
96
+ context "with 2 as limit" do
97
+ before do
98
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, limit: 2 }), valid_session
99
+ end
100
+
101
+ it "returns notification index of size 2 as JSON" do
102
+ assert_json_with_array_size(response_json["notifications"], 2)
103
+ end
104
+ end
105
+
106
+ context "with 1 as limit" do
107
+ before do
108
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, limit: 1 }), valid_session
109
+ end
110
+
111
+ it "returns notification index of size 1 as JSON" do
112
+ assert_json_with_array_size(response_json["notifications"], 1)
113
+ end
114
+ end
115
+ end
116
+
117
+ context "with reverse parameter" do
118
+ before do
119
+ @notifiable = create(:article)
120
+ @group = create(:article)
121
+ @key = 'test.key.1'
122
+ notification = create(:notification, target: test_target, notifiable: @notifiable)
123
+ create(:notification, target: test_target, notifiable: create(:comment), group: @group, created_at: notification.created_at + 10.second)
124
+ create(:notification, target: test_target, notifiable: create(:article), key: @key, created_at: notification.created_at + 20.second).open!
125
+ @notification1 = test_target.notification_index[0]
126
+ @notification2 = test_target.notification_index[1]
127
+ @notification3 = test_target.notification_index[2]
128
+ end
129
+
130
+ context "as default" do
131
+ before do
132
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target }), valid_session
133
+ end
134
+
135
+ it "returns the latest order" do
136
+ assert_json_with_object_array(response_json["notifications"], [@notification1, @notification2, @notification3])
137
+ end
138
+ end
139
+
140
+ context "with true as reverse" do
141
+ before do
142
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, reverse: true }), valid_session
143
+ end
144
+
145
+ it "returns the earliest order" do
146
+ assert_json_with_object_array(response_json["notifications"], [@notification2, @notification1, @notification3])
147
+ end
148
+ end
149
+ end
150
+
151
+ context "with options filter parameters" do
152
+ before do
153
+ @notifiable = create(:article)
154
+ @group = create(:article)
155
+ @key = 'test.key.1'
156
+ @notification2 = create(:notification, target: test_target, notifiable: @notifiable)
157
+ @notification1 = create(:notification, target: test_target, notifiable: create(:comment), group: @group, created_at: @notification2.created_at + 10.second)
158
+ @notification3 = create(:notification, target: test_target, notifiable: create(:article), key: @key, created_at: @notification2.created_at + 20.second)
159
+ @notification3.open!
160
+ end
161
+
162
+ context 'with filtered_by_type parameter' do
163
+ it "returns filtered notifications only" do
164
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, filtered_by_type: 'Article' }), valid_session
165
+ assert_json_with_object_array(response_json["notifications"], [@notification2, @notification3])
166
+ end
167
+ end
168
+
169
+ context 'with filtered_by_group_type and filtered_by_group_id parameters' do
170
+ it "returns filtered notifications only" do
171
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, filtered_by_group_type: 'Article', filtered_by_group_id: @group.id.to_s }), valid_session
172
+ assert_json_with_object_array(response_json["notifications"], [@notification1])
173
+ end
174
+ end
175
+
176
+ context 'with filtered_by_key parameter' do
177
+ it "returns filtered notifications only" do
178
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, filtered_by_key: @key }), valid_session
179
+ assert_json_with_object_array(response_json["notifications"], [@notification3])
180
+ end
181
+ end
182
+
183
+ context 'with later_than parameter' do
184
+ it "returns filtered notifications only" do
185
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, later_than: (@notification1.created_at.in_time_zone + 0.001).iso8601(3) }), valid_session
186
+ assert_json_with_object_array(response_json["notifications"], [@notification3])
187
+ end
188
+ end
189
+
190
+ context 'with earlier_than parameter' do
191
+ it "returns filtered notifications only" do
192
+ get_with_compatibility :index, target_params.merge({ typed_target_param => test_target, earlier_than: @notification1.created_at.iso8601(3) }), valid_session
193
+ assert_json_with_object_array(response_json["notifications"], [@notification2])
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "POST #open_all" do
200
+ context "http POST request" do
201
+ before do
202
+ @notification = create(:notification, target: test_target)
203
+ expect(@notification.opened?).to be_falsey
204
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target }), valid_session
205
+ end
206
+
207
+ it "returns 200 as http status code" do
208
+ expect(response.status).to eq(200)
209
+ end
210
+
211
+ it "opens all notifications of the target" do
212
+ expect(@notification.reload.opened?).to be_truthy
213
+ end
214
+
215
+ it "returns JSON response" do
216
+ expect(response_json["count"]).to eq(1)
217
+ assert_json_with_object_array(response_json["notifications"], [@notification])
218
+ end
219
+ end
220
+
221
+ context "with filter request parameters" do
222
+ before do
223
+ @target_1, @notifiable_1, @group_1, @key_1 = create(:confirmed_user), create(:article), nil, "key.1"
224
+ @target_2, @notifiable_2, @group_2, @key_2 = create(:confirmed_user), create(:comment), @notifiable_1, "key.2"
225
+ @notification_1 = create(:notification, target: test_target, notifiable: @notifiable_1, group: @group_1, key: @key_1)
226
+ @notification_2 = create(:notification, target: test_target, notifiable: @notifiable_2, group: @group_2, key: @key_2, created_at: @notification_1.created_at + 10.second)
227
+ expect(@notification_1.opened?).to be_falsey
228
+ expect(@notification_2.opened?).to be_falsey
229
+ end
230
+
231
+ context "with filtered_by_type request parameters" do
232
+ it "opens filtered notifications only" do
233
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target, 'filtered_by_type' => @notifiable_2.to_class_name }), valid_session
234
+ expect(@notification_1.reload.opened?).to be_falsey
235
+ expect(@notification_2.reload.opened?).to be_truthy
236
+ end
237
+ end
238
+
239
+ context 'with filtered_by_group_type and :filtered_by_group_id request parameters' do
240
+ it "opens filtered notifications only" do
241
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target, 'filtered_by_group_type' => 'Article', 'filtered_by_group_id' => @group_2.id.to_s }), valid_session
242
+ expect(@notification_1.reload.opened?).to be_falsey
243
+ expect(@notification_2.reload.opened?).to be_truthy
244
+ end
245
+ end
246
+
247
+ context 'with filtered_by_key request parameters' do
248
+ it "opens filtered notifications only" do
249
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target, 'filtered_by_key' => 'key.2' }), valid_session
250
+ expect(@notification_1.reload.opened?).to be_falsey
251
+ expect(@notification_2.reload.opened?).to be_truthy
252
+ end
253
+ end
254
+
255
+ context 'with later_than parameter' do
256
+ it "opens filtered notifications only" do
257
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target, later_than: (@notification_1.created_at.in_time_zone + 0.001).iso8601(3) }), valid_session
258
+ expect(@notification_1.reload.opened?).to be_falsey
259
+ expect(@notification_2.reload.opened?).to be_truthy
260
+ end
261
+ end
262
+
263
+ context 'with earlier_than parameter' do
264
+ it "opens filtered notifications only" do
265
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target, earlier_than: @notification_2.created_at.iso8601(3) }), valid_session
266
+ expect(@notification_1.reload.opened?).to be_truthy
267
+ expect(@notification_2.reload.opened?).to be_falsey
268
+ end
269
+ end
270
+
271
+ context "with no filter request parameters" do
272
+ it "opens all notifications of the target" do
273
+ post_with_compatibility :open_all, target_params.merge({ typed_target_param => test_target}), valid_session
274
+ expect(@notification_1.reload.opened?).to be_truthy
275
+ expect(@notification_2.reload.opened?).to be_truthy
276
+ end
277
+ end
278
+ end
279
+ end
280
+
281
+ describe "GET #show" do
282
+ context "with id, target_type and (typed_target)_id parameters" do
283
+ before do
284
+ @notification = create(:notification, target: test_target)
285
+ get_with_compatibility :show, target_params.merge({ id: @notification, typed_target_param => test_target }), valid_session
286
+ end
287
+
288
+ it "returns 200 as http status code" do
289
+ expect(response.status).to eq(200)
290
+ end
291
+
292
+ it "returns the requested notification as JSON" do
293
+ assert_json_with_object(response_json, @notification)
294
+ end
295
+ end
296
+
297
+ context "with wrong id and (typed_target)_id parameters" do
298
+ before do
299
+ @notification = create(:notification, target: create(:user))
300
+ get_with_compatibility :show, target_params.merge({ id: @notification, typed_target_param => test_target }), valid_session
301
+ end
302
+
303
+ it "returns 403 as http status code" do
304
+ expect(response.status).to eq(403)
305
+ end
306
+
307
+ it "returns error JSON response" do
308
+ assert_error_response(403)
309
+ end
310
+ end
311
+
312
+ context "when associated notifiable record was not found" do
313
+ before do
314
+ @notification = create(:notification, target: test_target)
315
+ @notification.notifiable.delete
316
+ get_with_compatibility :show, target_params.merge({ id: @notification, typed_target_param => test_target }), valid_session
317
+ end
318
+
319
+ it "returns 500 as http status code" do
320
+ expect(response.status).to eq(500)
321
+ end
322
+
323
+ it "returns error JSON response" do
324
+ assert_error_response(500)
325
+ end
326
+ end
327
+ end
328
+
329
+ describe "DELETE #destroy" do
330
+ context "http DELETE request" do
331
+ before do
332
+ @notification = create(:notification, target: test_target)
333
+ delete_with_compatibility :destroy, target_params.merge({ id: @notification, typed_target_param => test_target }), valid_session
334
+ end
335
+
336
+ it "returns 204 as http status code" do
337
+ expect(response.status).to eq(204)
338
+ end
339
+
340
+ it "deletes the notification" do
341
+ expect(test_target.notifications.where(id: @notification.id).exists?).to be_falsey
342
+ end
343
+ end
344
+ end
345
+
346
+ describe "PUT #open" do
347
+ context "without move parameter" do
348
+ context "http PUT request" do
349
+ before do
350
+ @notification = create(:notification, target: test_target)
351
+ expect(@notification.opened?).to be_falsey
352
+ put_with_compatibility :open, target_params.merge({ id: @notification, typed_target_param => test_target }), valid_session
353
+ end
354
+
355
+ it "returns 200 as http status code" do
356
+ expect(response.status).to eq(200)
357
+ end
358
+
359
+ it "opens the notification" do
360
+ expect(@notification.reload.opened?).to be_truthy
361
+ end
362
+
363
+ it "returns JSON response" do
364
+ expect(response_json["count"]).to eq(1)
365
+ assert_json_with_object(response_json["notification"], @notification)
366
+ end
367
+ end
368
+ end
369
+
370
+ context "with true as move parameter" do
371
+ context "http PUT request" do
372
+ before do
373
+ @notification = create(:notification, target: test_target)
374
+ expect(@notification.opened?).to be_falsey
375
+ put_with_compatibility :open, target_params.merge({ id: @notification, typed_target_param => test_target, move: true }), valid_session
376
+ end
377
+
378
+ it "returns 302 as http status code" do
379
+ expect(response.status).to eq(302)
380
+ end
381
+
382
+ it "opens the notification" do
383
+ expect(@notification.reload.opened?).to be_truthy
384
+ end
385
+
386
+ it "redirects to notifiable_path" do
387
+ expect(response).to redirect_to @notification.notifiable_path
388
+ end
389
+ end
390
+ end
391
+ end
392
+
393
+ describe "GET #move" do
394
+ context "without open parameter" do
395
+ context "http GET request" do
396
+ before do
397
+ @notification = create(:notification, target: test_target)
398
+ get_with_compatibility :move, target_params.merge({ id: @notification, typed_target_param => test_target }), valid_session
399
+ end
400
+
401
+ it "returns 302 as http status code" do
402
+ expect(response.status).to eq(302)
403
+ end
404
+
405
+ it "redirects to notifiable_path" do
406
+ expect(response).to redirect_to @notification.notifiable_path
407
+ end
408
+ end
409
+ end
410
+
411
+ context "with true as open parameter" do
412
+ context "http GET request" do
413
+ before do
414
+ @notification = create(:notification, target: test_target)
415
+ expect(@notification.opened?).to be_falsey
416
+ get_with_compatibility :move, target_params.merge({ id: @notification, typed_target_param => test_target, open: true }), valid_session
417
+ end
418
+
419
+ it "returns 302 as http status code" do
420
+ expect(response.status).to eq(302)
421
+ end
422
+
423
+ it "opens the notification" do
424
+ expect(@notification.reload.opened?).to be_truthy
425
+ end
426
+
427
+ it "redirects to notifiable_path" do
428
+ expect(response).to redirect_to @notification.notifiable_path
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end
434
+
435
+ shared_examples_for :notifications_api_request do
436
+ include ActivityNotification::ControllerSpec::CommitteeUtility
437
+
438
+ before do
439
+ group = create(:article)
440
+ notifier = create(:user)
441
+ create(:notification, target: test_target)
442
+ group_owner = create(:notification, target: test_target, group: group, notifier: notifier, parameters: { "test_default_param": "1" })
443
+ @notification = create(:notification, target: test_target, group: group, group_owner: group_owner, notifier: notifier, parameters: { "test_default_param": "1" })
444
+ group_owner.open!
445
+ end
446
+
447
+ describe "GET /apidocs" do
448
+ it "returns API references as OpenAPI Specification JSON schema" do
449
+ get "#{root_path}/apidocs"
450
+ write_schema_file(response.body)
451
+ expect(read_schema_file["openapi"]).to eq("3.0.0")
452
+ end
453
+ end
454
+
455
+ describe "GET /{target_type}/{target_id}/notifications", type: :request do
456
+ it "returns response as API references" do
457
+ get_with_compatibility "#{api_path}/notifications", headers: @headers
458
+ assert_all_schema_confirm(response, 200)
459
+ end
460
+ end
461
+
462
+ describe "POST /{target_type}/{target_id}/notifications/open_all", type: :request do
463
+ it "returns response as API references" do
464
+ post_with_compatibility "#{api_path}/notifications/open_all", headers: @headers
465
+ assert_all_schema_confirm(response, 200)
466
+ end
467
+ end
468
+
469
+ describe "GET /{target_type}/{target_id}/notifications/{id}", type: :request do
470
+ it "returns response as API references" do
471
+ get_with_compatibility "#{api_path}/notifications/#{@notification.id}", headers: @headers
472
+ assert_all_schema_confirm(response, 200)
473
+ end
474
+
475
+ it "returns error response as API references" do
476
+ get_with_compatibility "#{api_path}/notifications/0", headers: @headers
477
+ assert_all_schema_confirm(response, 404)
478
+ end
479
+ end
480
+
481
+ describe "DELETE /{target_type}/{target_id}/notifications/{id}", type: :request do
482
+ it "returns response as API references" do
483
+ delete_with_compatibility "#{api_path}/notifications/#{@notification.id}", headers: @headers
484
+ assert_all_schema_confirm(response, 204)
485
+ end
486
+ end
487
+
488
+ describe "PUT /{target_type}/{target_id}/notifications/{id}/open", type: :request do
489
+ it "returns response as API references" do
490
+ put_with_compatibility "#{api_path}/notifications/#{@notification.id}/open", headers: @headers
491
+ assert_all_schema_confirm(response, 200)
492
+ end
493
+
494
+ it "returns response as API references when request parameters have move=true" do
495
+ put_with_compatibility "#{api_path}/notifications/#{@notification.id}/open?move=true", headers: @headers
496
+ assert_all_schema_confirm(response, 302)
497
+ end
498
+ end
499
+
500
+ describe "GET /{target_type}/{target_id}/notifications/{id}/move", type: :request do
501
+ it "returns response as API references" do
502
+ get_with_compatibility "#{api_path}/notifications/#{@notification.id}/move", headers: @headers
503
+ assert_all_schema_confirm(response, 302)
504
+ end
505
+ end
506
+ end