loco-rails 4.1.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6efc2ac92aece905588e66392e372f7fba0414d542b45a659cf6637e25932b4
4
- data.tar.gz: e53dbee6931104ceb192dcb677b2a927528629c620fef5a68c30afb53ff5397a
3
+ metadata.gz: 3b3b768bf72d198a530c47e9aab5060b783c647f0ff97eb54c3ab5549499bf7c
4
+ data.tar.gz: 2f10baeb21193dc8a3bf0abb1038847224fe4aef5663c21dadf74a1dfb821486
5
5
  SHA512:
6
- metadata.gz: a196c6b188cd04c205862ee75d1dbe35ec14607a2a49ffa659013c116789d406faaf6521861dad623cbe6ac5356155b35a91fa73967f999142007b553e3a9a77
7
- data.tar.gz: '0692ad504f2455e620352adefdf38e9327c9212601578ed9dde443c23d40a0addcfb7431950c8ad04cf47c4c82f18a472363209fc7e64c3a4c5e860933e08398'
6
+ metadata.gz: 54a025a862ecb19b028a8e86cc14832aa8565656250914431cc0cffc8f0e7eb361d9ea606fa922f3f7fcc655fc47524055f79547e2aeb37874be011e25883912
7
+ data.tar.gz: 175abf6fda5e84783a2c38138cd08a62a3c7a48e0ff7b04c1e5b80d4053d52f3ce6dac0e532e8fb4b0e809e540c62975d846e68ccab3f835329e7c5a37528daf
@@ -5,61 +5,37 @@ module Loco
5
5
  def subscribed
6
6
  return unless loco_permissions.is_a?(Array)
7
7
 
8
- stream_for_resources
9
- return if loco_permissions.compact.size > 1
8
+ @uuid, *signed_in_resources = PermissionsPresenter.signed_in(loco_permissions)
9
+ return unless @uuid.is_a?(String)
10
10
 
11
- SenderJob.perform_later @uuid, loco: { start_ajax_polling: true }
11
+ stream_from("loco:notification_center:#{@uuid}")
12
+ broadcast_to(@uuid, loco: { uuid: @uuid })
13
+ signed_in_resources.each { |resource| WsConnectionManager.new(resource).add(@uuid) }
12
14
  end
13
15
 
14
16
  def unsubscribed
15
- loco_permissions.each do |resource|
16
- next if resource.nil? || resource.is_a?(String)
17
-
18
- UuidJob.perform_later serialize_resource(resource), @uuid, 'del'
17
+ PermissionsPresenter.signed_in(loco_permissions, except: :uuid).each do |resource|
18
+ WsConnectionManager.new(resource).del(@uuid)
19
19
  end
20
20
  end
21
21
 
22
- def receive(data)
23
- update_connections if data['loco'] && data['loco']['connection_check']
24
- NotificationCenter.new.received_message permissions, data
25
- end
26
-
27
- protected
28
-
29
- def stream_for_resources
30
- loco_permissions.compact.each do |resource|
31
- if resource.is_a? String
32
- @uuid = resource
33
- stream_for_resource resource
34
- SenderJob.perform_later @uuid, loco: { uuid: @uuid }
35
- else
36
- UuidJob.perform_later serialize_resource(resource), @uuid, 'add'
37
- stream_for_resource resource
38
- end
22
+ def receive(payload)
23
+ if payload.dig('loco', 'pong')
24
+ update_connections
25
+ broadcast_to(@uuid, loco: { ping: true })
39
26
  end
40
- end
27
+ return if payload.keys == ['loco']
41
28
 
42
- def stream_for_resource(resource)
43
- identifier = WsConnectionManager.new(resource).identifier
44
- stream_from "loco:notification_center:#{identifier}"
29
+ indexed_permissions = PermissionsPresenter.indexed(loco_permissions)
30
+ NotificationCenter.new.received_message(indexed_permissions, payload)
45
31
  end
46
32
 
47
- def permissions
48
- loco_permissions.compact.index_by do |o|
49
- o.class.name.downcase.to_sym
50
- end
51
- end
33
+ protected
52
34
 
53
35
  def update_connections
54
- permissions.each do |key, val|
55
- next if key == :string
56
-
57
- UuidJob.perform_later serialize_resource(val), @uuid, 'update'
36
+ PermissionsPresenter.indexed(loco_permissions, except: :uuid).each do |_, resource|
37
+ WsConnectionManager.new(resource).update(@uuid)
58
38
  end
59
39
  end
60
-
61
- def serialize_resource(resource)
62
- { 'class' => resource.class.name, 'id' => resource.id }
63
- end
64
40
  end
65
41
  end
@@ -21,41 +21,16 @@ module Loco
21
21
  render json: [[], Time.current.iso8601(6)]
22
22
  return
23
23
  end
24
- fetcher = Notification::Fetcher.new notif_fetcher_args
25
- render json: [
26
- fetcher.formatted_notifications,
27
- fetcher.next_sync_time.iso8601(6)
28
- ]
29
- end
30
-
31
- def notif_fetcher_args
32
- {
33
- synced_at: params[:synced_at],
34
- permissions: permissions,
35
- recipient_token: params[:token]
36
- }
24
+ fetcher = Notification::Fetcher.new({ synced_at: params[:synced_at],
25
+ permissions: permissions,
26
+ recipient_token: params[:token] })
27
+ render json: [fetcher.formatted_notifications, fetcher.next_sync_time.iso8601(6)]
37
28
  end
38
29
 
39
30
  def permissions
40
- return [] unless defined? loco_permissions
41
- return loco_permissions if params[:uuid].blank?
42
-
43
- process_loco_permissions
44
- end
31
+ return [] unless defined?(loco_permissions)
45
32
 
46
- def process_loco_permissions
47
- resources_to_del = []
48
- resources_to_add = []
49
- loco_permissions.each do |resource|
50
- next if resource.nil?
51
-
52
- ws_conn_manager = WsConnectionManager.new resource
53
- next unless ws_conn_manager.connected?(params[:uuid])
54
-
55
- resources_to_del << resource
56
- resources_to_add << resource.class
57
- end
58
- loco_permissions - resources_to_del + resources_to_add.uniq
33
+ loco_permissions
59
34
  end
60
35
  end
61
36
  end
@@ -5,7 +5,7 @@ module Loco
5
5
  queue_as :loco
6
6
 
7
7
  def perform(recipient, data)
8
- Sender.new(recipient, data).emit
8
+ Sender.(recipient, data)
9
9
  end
10
10
  end
11
11
  end
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Loco
4
4
  class Notification < ApplicationRecord
5
+ FOR_OBJ_SQL_TMPL = 'recipient_class = ? AND recipient_id = ?'
6
+ FOR_CLASS_SQL_TMPL = 'recipient_class = ? AND recipient_id IS NULL'
7
+
5
8
  attr_reader :obj
6
9
 
7
10
  serialize :data, JSON
@@ -31,9 +34,9 @@ module Loco
31
34
  return if val.nil?
32
35
  return if val == :all
33
36
 
34
- if val.is_a? String
37
+ if val.is_a?(String)
35
38
  self.recipient_token = val
36
- elsif val.instance_of? Class
39
+ elsif val.instance_of?(Class)
37
40
  self.recipient_class = val.to_s
38
41
  else
39
42
  self.recipient_class = val.class.name
@@ -41,17 +44,14 @@ module Loco
41
44
  end
42
45
  end
43
46
 
44
- def recipient(opts = {})
45
- return recipient_token if recipient_token
46
- return unless regular_recipient?
47
- return class_recipient unless recipient_id
48
-
49
- obj_recipient opts[:shallow]
50
- end
51
-
52
- def prepare
53
- set_event
54
- set_data
47
+ def recipient(shallow: false)
48
+ if !recipient_token.nil?
49
+ recipient_token
50
+ elsif regular_recipient?
51
+ init_recipient(shallow)
52
+ elsif !recipient_class.nil?
53
+ recipient_class.constantize
54
+ end
55
55
  end
56
56
 
57
57
  def compact
@@ -61,41 +61,42 @@ module Loco
61
61
  private
62
62
 
63
63
  def regular_recipient?
64
- recipient_class && recipient_id
64
+ !recipient_class.nil? && !recipient_id.nil?
65
65
  end
66
66
 
67
- def class_recipient
68
- recipient_class.constantize
69
- end
70
-
71
- def obj_recipient(shallow = false)
67
+ def init_recipient(shallow)
72
68
  if shallow
73
- recipient_class.constantize.new id: recipient_id
69
+ recipient_class.constantize.new(id: recipient_id)
74
70
  else
75
- recipient_class.constantize.find recipient_id
71
+ recipient_class.constantize.find(recipient_id)
76
72
  end
77
73
  end
78
74
 
75
+ def prepare
76
+ set_event
77
+ set_data
78
+ end
79
+
79
80
  def set_event
80
81
  return if event.present?
81
- return if obj.instance_of? Class
82
+ return if obj.instance_of?(Class)
82
83
 
83
- if obj.new_record?
84
- self.event = 'creating'
85
- else
86
- set_event_for_persisted_obj
87
- end
84
+ self.event = if obj.new_record?
85
+ 'creating'
86
+ else
87
+ event_for_persisted_obj
88
+ end
88
89
  end
89
90
 
90
- def set_event_for_persisted_obj
91
- self.event = obj.created_at == obj.updated_at ? 'created' : 'updated'
91
+ def event_for_persisted_obj
92
+ obj.created_at == obj.updated_at ? 'created' : 'updated'
92
93
  end
93
94
 
94
95
  def set_data
95
96
  self.data ||= {}
96
97
  return if obj.nil?
97
98
 
98
- self.data.merge!(id: obj.id)
99
+ self.data = data.merge(id: obj.id)
99
100
  end
100
101
  end
101
102
  end
@@ -5,17 +5,12 @@ module Loco
5
5
  class Fetcher
6
6
  attr_accessor :max_size
7
7
 
8
- def initialize(
9
- synced_at:,
10
- permissions: [],
11
- recipient_token: nil,
12
- max_size: nil
13
- )
14
- @synced_at = synced_at
15
- @permissions = permissions
16
- @recipient_token = recipient_token
8
+ def initialize(opts)
9
+ @synced_at = opts[:synced_at]
10
+ @permissions = (opts[:permissions] || []).compact
11
+ @recipient_token = opts[:recipient_token]
17
12
  @notifications = nil
18
- @max_size = max_size || Loco::Config.notifications_size
13
+ @max_size = opts[:max_size] || Loco::Config.notifications_size
19
14
  end
20
15
 
21
16
  def formatted_notifications
@@ -56,17 +51,11 @@ module Loco
56
51
  recipient_class: nil,
57
52
  recipient_id: nil,
58
53
  recipient_token: nil
59
- ).first max_size
54
+ ).first(max_size)
60
55
  end
61
56
 
62
57
  def notifications_behind_permissions
63
- notifications = []
64
- @permissions.each do |resource|
65
- next unless resource
66
-
67
- notifications += notification_for_resource resource
68
- end
69
- notifications
58
+ @permissions.inject([]) { |arr, resource| arr + notification_for_resource(resource) }
70
59
  end
71
60
 
72
61
  def notifications_behind_token
@@ -74,15 +63,13 @@ module Loco
74
63
  end
75
64
 
76
65
  def notification_for_resource(resource)
77
- if resource.instance_of? Class
78
- sql = 'recipient_class = ? AND recipient_id IS NULL'
79
- return default_scope.where(sql, resource.to_s).first max_size
66
+ if resource.instance_of?(Class)
67
+ return default_scope.where(Notification::FOR_CLASS_SQL_TMPL, resource.to_s)
68
+ .first(max_size)
80
69
  end
81
70
  klass = resource.class.name
82
- cond1 = '(recipient_class = ? AND recipient_id = ?)'
83
- cond2 = '(recipient_class = ? AND recipient_id IS NULL)'
84
- sql = "#{cond1} OR #{cond2}"
85
- default_scope.where(sql, klass, resource.id, klass).first max_size
71
+ sql = "(#{Notification::FOR_OBJ_SQL_TMPL}) OR (#{Notification::FOR_CLASS_SQL_TMPL})"
72
+ default_scope.where(sql, klass, resource.id, klass).first(max_size)
86
73
  end
87
74
  end
88
75
  end
@@ -1,6 +1,7 @@
1
1
  identified_by :loco_permissions
2
2
 
3
3
  def connect
4
+ reject_unauthorized_connection unless current_user || current_admin
4
5
  # loco_permissions should be the same as in application_controller.rb
5
6
  # + SecureRandom.uuid is mandatory at 1st position
6
7
  self.loco_permissions = [SecureRandom.uuid, current_user, current_admin]
@@ -8,9 +8,12 @@ require 'loco/emitter'
8
8
  require 'loco/engine'
9
9
  require 'loco/helpers'
10
10
  require 'loco/hub'
11
+ require 'loco/permissions_presenter'
11
12
  require 'loco/sender'
13
+ require 'loco/ws_connection_checker'
12
14
  require 'loco/ws_connection_manager'
13
- require 'loco/ws_connected_resources_manager'
15
+ require 'loco/ws_connection_finder'
16
+ require 'loco/ws_connection_identifier'
14
17
  require 'loco/ws_connection_storage'
15
18
 
16
19
  module Loco
@@ -2,99 +2,51 @@
2
2
 
3
3
  module Loco
4
4
  class Broadcaster
5
- attr_reader :obj, :event, :recipients, :data, :notifications
6
-
7
- def initialize(obj, event = nil, opts = {})
8
- recipient_key = opts[:for] ? :for : :to
9
- @obj = obj
10
- @event = event
11
- @recipients = opts[recipient_key] ? [*opts[recipient_key]] : [nil]
12
- @data = opts[:data]
13
- @notifications = []
14
- @sent_via_ws = 0
15
- @conn_res_manager = WsConnectedResourcesManager.new @recipients.compact
16
- end
17
-
18
- def emit
19
- init_notifications if notifications.empty?
20
- send_notifications
21
- if notify_about_xhr_notifications?
22
- notify_about_xhr_notifications
23
- else
24
- set_sync_time_via_ws
5
+ class << self
6
+ def call(obj, event, recipients: nil, payload: nil)
7
+ payload ||= {}
8
+ payload[:loco] = { idempotency_key: SecureRandom.hex }
9
+ send_notifications(obj, event, process_recipients(recipients), payload)
25
10
  end
26
- end
27
11
 
28
- private
12
+ private
29
13
 
30
- def init_notifications
31
- recipients.each do |recipient|
32
- @notifications << Notification.new(
33
- obj: obj,
34
- event: event,
35
- recipient: recipient,
36
- data: data
37
- )
14
+ def process_recipients(recipients)
15
+ recipients = [:all] if recipients.nil?
16
+ recipients = [recipients] unless recipients.is_a?(Array)
17
+ recipients = recipients.map { |e| e.nil? ? :all : e }
18
+ recipients = [:all] if recipients.include?(:all)
19
+ recipients
38
20
  end
39
- end
40
-
41
- def send_notifications
42
- notifications.each do |notification|
43
- notification.save!
44
- next if notification.recipient_id.nil?
45
21
 
46
- shallow_recipient = notification.recipient shallow: true
47
- next unless @conn_res_manager.connected? shallow_recipient
48
-
49
- send_via_ws notification
50
- end
51
- end
52
-
53
- def send_via_ws(notification)
54
- recipient = notification.recipient shallow: true
55
- data = { loco: { notification: notification.compact } }
56
- SenderJob.perform_later recipient, data
57
- @sent_via_ws += 1
58
- end
59
-
60
- def notify_about_xhr_notifications?
61
- @sent_via_ws < notifications.size
62
- end
63
-
64
- def notify_about_xhr_notifications
65
- uuids = []
66
- fetch_identifiers.each do |ident|
67
- Loco::WsConnectionManager.new(ident).connected_uuids.each do |uuid|
68
- next if uuids.include? uuid
69
-
70
- uuids << uuid
71
- SenderJob.perform_later uuid, loco: { xhr_notifications: true }
22
+ def send_notifications(obj, event, recipients, payload)
23
+ recipients.each do |recipient|
24
+ notification = Notification.create!(
25
+ obj: obj,
26
+ event: event,
27
+ recipient: recipient,
28
+ data: payload
29
+ )
30
+ sync_time = notification.created_at.iso8601(6)
31
+ send_notification(keify_recipient(recipient), notification, sync_time)
72
32
  end
73
33
  end
74
- end
75
-
76
- def set_sync_time_via_ws
77
- sync_time = notifications.last.created_at.iso8601(6)
78
- notifications.each do |notification|
79
- recipient = notification.recipient shallow: true
80
- SenderJob.perform_later recipient, loco: { sync_time: sync_time }
81
- end
82
- end
83
34
 
84
- def notifications_recipients
85
- notifications.map { |n| n.recipient shallow: true }.map do |o|
86
- o.instance_of?(Class) ? o.to_s.downcase : nil
35
+ def keify_recipient(recipient)
36
+ case recipient
37
+ when String then { 'token' => recipient }
38
+ when Class then { 'class' => recipient.name }
39
+ else recipient
40
+ end
87
41
  end
88
- end
89
42
 
90
- def fetch_identifiers
91
- recipients = notifications_recipients
92
- uniq_recipients = recipients.compact.uniq
93
- Loco::WsConnectedResourcesManager.identifiers.find_all do |str|
94
- if recipients.include? nil
95
- true
43
+ def send_notification(recipient, notification, sync_time)
44
+ if notification.recipient_id
45
+ Sender.(recipient, loco: { notification: notification.compact })
46
+ Sender.(recipient, loco: { sync_time: sync_time })
96
47
  else
97
- uniq_recipients.include? str.split(':').first
48
+ SenderJob.perform_later(recipient, loco: { notification: notification.compact })
49
+ SenderJob.perform_later(recipient, loco: { sync_time: sync_time })
98
50
  end
99
51
  end
100
52
  end