Class: SpreeCmCommissioner::WaitingRoomSessionCreator

Inherits:
BaseInteractor
  • Object
show all
Defined in:
app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb

Constant Summary collapse

SESSION_CREATION_LOCK_KEY =
'waiting_room_session_creation'.freeze
PERMITTED_DEVICE_ATTRIBUTES =

Device attributes the client may set on the session. device_id and device_fingerprint are real columns; the rest are virtual columns backed by public/private metadata (see WaitingRoomSession). Anything else the client sends is dropped so we never persist arbitrary payloads.

%w[
  device_id device_fingerprint
  os_name os_version device_model device_manufacturer device_type
  device_environment app_version app_build timezone network_type
].freeze

Instance Method Summary collapse

Instance Method Details

#advisory_lock(lock_key) ⇒ Object



50
51
52
53
54
55
56
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 50

def advisory_lock(lock_key)
  lock_id = Zlib.crc32(lock_key)
  SpreeCmCommissioner::WaitingRoomSession.transaction do
    SpreeCmCommissioner::WaitingRoomSession.connection.execute("SELECT pg_advisory_xact_lock(#{lock_id})")
    yield
  end
end

#assign_token_and_create_session_to_dbObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 63

def assign_token_and_create_session_to_db
  # create or renew
  context.room_session = SpreeCmCommissioner::WaitingRoomSession.where(guest_identifier: waiting_guest_firebase_doc_id).first_or_initialize
  context.room_session.assign_attributes(
    {
      jwt_token: context.jwt_token,
      expired_at: expired_at,
      remote_ip: remote_ip,
      page_path: page_path,
      tenant_id: tenant_id
    }.merge(sanitized_device_attributes)
  )

  context.room_session.save!
end

#callObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 21

def call
  return context.fail!(message: 'must_provide_waiting_guest_firebase_doc_id') if waiting_guest_firebase_doc_id.blank?
  return context.fail!(message: 'must_provide_remote_ip') if remote_ip.blank?

  # Advisory lock ensures the capacity check and session creation are atomic across all app instances.
  advisory_lock(SESSION_CREATION_LOCK_KEY) do
    return context.fail!(message: 'sessions_reach_it_maximum') if full?

    generate_jwt_token
    assign_token_and_create_session_to_db
  end

  log_to_firebase

  # commented because of following bug: https://github.com/channainfo/commissioner/issues/2185
  # this job is already run every 1mn, disabling it still work.
  # call_other_waiting_guests
end

#call_other_waiting_guestsObject



97
98
99
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 97

def call_other_waiting_guests
  SpreeCmCommissioner::WaitingGuestsCallerJob.perform_later
end

#expired_atObject



101
102
103
104
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 101

def expired_at
  expired_duration = ENV['WAITING_ROOM_SESSION_EXPIRE_DURATION_IN_SECOND']&.presence&.to_i || (60 * 3)
  context.expired_at ||= expired_duration.seconds.from_now
end

#firestoreObject



106
107
108
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 106

def firestore
  @firestore ||= Google::Cloud::Firestore.new(project_id: [:project_id], credentials: )
end

#full?Boolean

Returns:

  • (Boolean)


40
41
42
43
44
45
46
47
48
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 40

def full?
  max = Rails.cache.fetch('waiting_room/max_sessions_count', expires_in: 1.hour) do
    fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new(firestore: firestore)
    fetcher.load_document_data
    fetcher.max_sessions_count_with_min
  end

  SpreeCmCommissioner::WaitingRoomSession.active.count >= max
end

#generate_jwt_tokenObject



58
59
60
61
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 58

def generate_jwt_token
  payload = { exp: expired_at.to_i }
  context.jwt_token = JWT.encode(payload, ENV.fetch('WAITING_ROOM_SESSION_SIGNATURE'), 'HS256')
end

#log_to_firebaseObject



90
91
92
93
94
95
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 90

def log_to_firebase
  SpreeCmCommissioner::WaitingRoomSessionFirebaseLoggerJob.perform_later(
    room_session_id: context.room_session.id,
    waiting_guest_firebase_doc_id: waiting_guest_firebase_doc_id
  )
end

#sanitized_device_attributesObject

Whitelisted, flat device attributes. Real columns (device_id, device_fingerprint) and virtual columns (StoreMetadata) are assigned the same way; the model routes each to the right store.



82
83
84
85
86
87
88
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 82

def sanitized_device_attributes
  attributes = device_attributes
  attributes = attributes.to_unsafe_h if attributes.respond_to?(:to_unsafe_h)
  return {} unless attributes.is_a?(Hash)

  attributes.symbolize_keys.slice(*PERMITTED_DEVICE_ATTRIBUTES.map(&:to_sym))
end

#service_accountObject



110
111
112
# File 'app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb', line 110

def 
  Rails.application.credentials.
end