spree_cm_commissioner 2.8.3.pre.pre12 → 2.8.3.pre.pre13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc7986faabd73e3ae1e70e3a80e14f59119301779f847a112d585d15bf52406f
4
- data.tar.gz: 97c8e0f4d601a7ddb553e786bc23f0f8b9a70a1eae307d7cf79da8e05d70d5c9
3
+ metadata.gz: eedbddb863eb9f58dad73a83892a225e971a89736c37af047df757d76bcfc058
4
+ data.tar.gz: 59eaad5012be611640f1c27f6566bfb91cd7b5cea176d465be69c4230c8b9241
5
5
  SHA512:
6
- metadata.gz: a1bec82d194a2943c597dc804c2a3f69c7a3f09da8c9734d82e76c1af7c102748175307e1a530a7107a0011bf5d1556cefc1c2d9fd7929e1e52ea2979912ed2c
7
- data.tar.gz: 992f9e6d0c5b58e06d726ed5967121d85530bf654410a475ce7b694f7e559dc42d6bc0e021136e550b000517b6d9a505e45db16bede3bc2ecc0b70c308c267ec
6
+ metadata.gz: aa9db90a1c39aaa74f66135c5034cb8762e09123edaad154fb5fe6e013609b5344d23910bd8449380030c5b3e391b3a3792c92b569f5fbe2271d6e78420e55e6
7
+ data.tar.gz: 80c51833f840ba8b4db0611309153c7a7723fd986f065113a06063ac7d5aee78cce2af29dd2eb591b64a90c57a75f1f4716b9d7daf2664903ff34ad031f663e4
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.8.3.pre.pre12)
37
+ spree_cm_commissioner (2.8.3.pre.pre13)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -4,7 +4,10 @@ module Spree
4
4
  class WaitingRoomController < Spree::Admin::BaseController
5
5
  def show
6
6
  @fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new
7
+ @lobby_fetcher = SpreeCmCommissioner::WaitingRoomLobbyMetadataFetcher.new
8
+
7
9
  @fetcher.load_document_data
10
+ @lobby_fetcher.load_document_data
8
11
 
9
12
  @active_sesions_count = SpreeCmCommissioner::WaitingRoomSession.active.count
10
13
  end
@@ -29,6 +32,18 @@ module Spree
29
32
 
30
33
  redirect_back fallback_location: admin_system_waiting_room_path
31
34
  end
35
+
36
+ def publish_lobby_path
37
+ result = SpreeCmCommissioner::WaitingRoom::PublishLobbyPath.call
38
+
39
+ if result.success?
40
+ flash[:success] = "Published waiting guests records path: #{result.value[:records_path]}"
41
+ else
42
+ flash[:error] = result.error.to_s
43
+ end
44
+
45
+ redirect_back fallback_location: admin_system_waiting_room_path
46
+ end
32
47
  end
33
48
  end
34
49
  end
@@ -19,18 +19,47 @@ module SpreeCmCommissioner
19
19
  max_sessions - active_sessions
20
20
  end
21
21
 
22
- # This query required index. create them in Firebase beforehand.
23
- # Client side must create waiting_guests document with :queued_at & :allow_to_enter_room_at to null to allow fillter & order.
22
+ # This query requires an index; create it in Firebase beforehand.
23
+ # Client must create waiting_guests documents with :queued_at and :allow_to_enter_room_at set to nil to allow filter + order queries.
24
+ #
25
+ # Yesterday's guests are always older than today's, so fill from yesterday first, then use any
26
+ # leftover slots for today. This way no one queued before the midnight rollover gets skipped.
27
+ # e.g. 5 slots, 2 waiting in yesterday -> take both, then take 3 from today.
24
28
  def fetch_long_waiting_guests(available_slots)
25
- firestore.col('waiting_guests')
26
- .doc(current_date)
27
- .col('records')
29
+ previous_guests = eligible_guests_in(previous_records_path, available_slots)
30
+
31
+ # Pre-flip window: the lobby pointer still points at yesterday, so both paths resolve to the
32
+ # same partition — return now to avoid querying (and double-counting) it twice.
33
+ return previous_guests if records_path == previous_records_path
34
+
35
+ remaining_slots = available_slots - previous_guests.size
36
+ return previous_guests if remaining_slots <= 0
37
+
38
+ previous_guests + eligible_guests_in(records_path, remaining_slots)
39
+ end
40
+
41
+ def eligible_guests_in(records_path, limit)
42
+ firestore.col(records_path)
28
43
  .where('allow_to_enter_room_at', '==', nil)
29
44
  .order('queued_at')
30
- .limit(available_slots)
45
+ .limit(limit)
31
46
  .get.to_a
32
47
  end
33
48
 
49
+ # Published path is authoritative; fall back to the server's own date if not yet published.
50
+ def records_path
51
+ lobby_data&.dig(:waiting_guests_records_path).presence || default_records_path(current_date)
52
+ end
53
+
54
+ # Drain target is derived from the server date, never the (possibly stale) lobby pointer.
55
+ def previous_records_path
56
+ default_records_path(previous_date)
57
+ end
58
+
59
+ def default_records_path(date)
60
+ "waiting_guests/#{date}/records"
61
+ end
62
+
34
63
  # For alert waiting guests to enter room, we just update :allow_to_enter_room_at.
35
64
  # App will listen to firebase & start refresh session token to enter room.
36
65
  def calling_all(waiting_guests)
@@ -45,9 +74,22 @@ module SpreeCmCommissioner
45
74
  Time.zone.now.strftime('%Y-%m-%d')
46
75
  end
47
76
 
77
+ def previous_date
78
+ 1.day.ago.strftime('%Y-%m-%d')
79
+ end
80
+
48
81
  # When open app, app request to check whether room is full or not via Firebase instead of server to minimize server requests.
82
+ # merge: true so we preserve the published `waiting_guests_records_path` on the lobby doc.
49
83
  def mark_as(full:, available_slots:)
50
- firestore.col('waiting_rooms').doc('lobby').set({ full: full, available_slots: available_slots })
84
+ lobby_document.set({ full: full, available_slots: available_slots }, merge: true)
85
+ end
86
+
87
+ def lobby_data
88
+ @lobby_data ||= lobby_document.get.data
89
+ end
90
+
91
+ def lobby_document
92
+ @lobby_document ||= firestore.col('waiting_rooms').doc('lobby')
51
93
  end
52
94
 
53
95
  def fetch_max_sessions
@@ -0,0 +1,17 @@
1
+ # waiting_room_lobby_path_publisher:
2
+ # cron: "0 0 * * * Asia/Phnom_Penh" # Once per day at local midnight (date rollover)
3
+ # class: "SpreeCmCommissioner::WaitingRoom::PublishLobbyPathJob"
4
+ module SpreeCmCommissioner
5
+ module WaitingRoom
6
+ class PublishLobbyPathJob < ApplicationJob
7
+ queue_as :waiting_room
8
+
9
+ def perform
10
+ return if ENV['WAITING_ROOM_DISABLED'] == 'yes'
11
+
12
+ # call! so a publish failure raises (Sidekiq retries/alerts) instead of being silently swallowed.
13
+ SpreeCmCommissioner::WaitingRoom::PublishLobbyPath.call!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ require 'google/cloud/firestore'
2
+
3
+ module SpreeCmCommissioner
4
+ module WaitingRoom
5
+ # Publishes the server-owned waiting-guests records path to the lobby document, so mobile
6
+ # and the waiting-room caller share one source of truth for the date partition instead of
7
+ # each building it from their own (possibly skewed) clock/timezone.
8
+ #
9
+ # CRON note: ecause we use Time.zone.now to determine the path, it's important that the CRON job runs at the same
10
+ # timezone as well — otherwise the cron could fire before/after Time.zone.now rolls to the new date.
11
+ # Example: cron: "0 0 * * * Asia/Phnom_Penh"
12
+ #
13
+ # Caching note: No caching needed since the service is expected to be called once per day
14
+ # so a single daily merge write is negligible.
15
+ class PublishLobbyPath
16
+ prepend ::Spree::ServiceModule::Base
17
+ extend SpreeCmCommissioner::ServiceModuleThrowable
18
+
19
+ def call
20
+ # merge: true so we never clobber the lobby's `full` / `available_slots` fields.
21
+ lobby_document.set({ waiting_guests_records_path: records_path }, merge: true)
22
+
23
+ success(records_path: records_path)
24
+ rescue StandardError => e
25
+ failure(nil, e.message)
26
+ end
27
+
28
+ private
29
+
30
+ # Server date is authoritative.
31
+ def records_path
32
+ @records_path ||= "waiting_guests/#{Time.zone.now.strftime('%Y-%m-%d')}/records"
33
+ end
34
+
35
+ def lobby_document
36
+ @lobby_document ||= firestore.col('waiting_rooms').doc('lobby')
37
+ end
38
+
39
+ def firestore
40
+ @firestore ||= Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account)
41
+ end
42
+
43
+ def service_account
44
+ @service_account ||= Rails.application.credentials.cloud_firestore_service_account
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,47 @@
1
+ require 'google/cloud/firestore'
2
+
3
+ module SpreeCmCommissioner
4
+ # Reads the lobby document (waiting_rooms/lobby) — the read-side counterpart to
5
+ # WaitingRoom::PublishLobbyPath. Kept separate from WaitingRoomSystemMetadataFetcher,
6
+ # which owns the unrelated metadata/system document.
7
+ class WaitingRoomLobbyMetadataFetcher
8
+ attr_reader :document_data
9
+
10
+ def initialize(firestore: nil)
11
+ @firestore = firestore if firestore.present?
12
+ end
13
+
14
+ def load_document_data
15
+ @document_data = document.get.data || {}
16
+ end
17
+
18
+ # firebase metadata
19
+
20
+ # server-owned date partition — see WaitingRoom::PublishLobbyPath.
21
+ def waiting_guests_records_path
22
+ document_data[:waiting_guests_records_path]
23
+ end
24
+
25
+ # written by WaitingGuestsCaller#mark_as.
26
+ def full
27
+ document_data[:full]
28
+ end
29
+
30
+ # written by WaitingGuestsCaller#mark_as.
31
+ def available_slots
32
+ document_data[:available_slots]
33
+ end
34
+
35
+ def document
36
+ @document ||= firestore.col('waiting_rooms').doc('lobby')
37
+ end
38
+
39
+ def firestore
40
+ @firestore ||= Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account)
41
+ end
42
+
43
+ def service_account
44
+ @service_account ||= Rails.application.credentials.cloud_firestore_service_account
45
+ end
46
+ end
47
+ end
@@ -4,6 +4,7 @@
4
4
  <%= button_link_to Spree.t(:force_pull), force_pull_admin_system_waiting_room_path, method: :post, class: "btn btn-outline-primary" %>
5
5
  <% end %>
6
6
 
7
+ <h6 class="mb-2 text-muted">System</h6>
7
8
  <div class="bg-white border rounded table-responsive">
8
9
  <table class="table" data-hook>
9
10
  <thead class="text-muted">
@@ -71,3 +72,55 @@
71
72
  </tbody>
72
73
  </table>
73
74
  </div>
75
+
76
+ <h6 class="mt-4 mb-2 text-muted">Lobby</h6>
77
+ <div class="bg-white border rounded table-responsive">
78
+ <table class="table" data-hook>
79
+ <thead class="text-muted">
80
+ <tr data-hook="admin_system_lobby_headers">
81
+ <th><%= Spree.t(:field_name) %></th>
82
+ <th><%= Spree.t(:current_value) %></th>
83
+ <th></th>
84
+ </tr>
85
+ </thead>
86
+ <tbody>
87
+ <% lobby_items = [
88
+ {
89
+ field_name: :waiting_guests_records_path,
90
+ value: @lobby_fetcher.waiting_guests_records_path.presence || "Not published yet",
91
+ reset_path: publish_lobby_path_admin_system_waiting_room_path
92
+ },
93
+ {
94
+ field_name: :full,
95
+ value: @lobby_fetcher.full ? "Yes" : "No",
96
+ },
97
+ {
98
+ field_name: :available_slots,
99
+ value: @lobby_fetcher.available_slots,
100
+ },
101
+ ] %>
102
+
103
+ <% lobby_items.each do |item| %>
104
+ <tr data-hook="admin_system_lobby_rows">
105
+ <td>
106
+ <%= item[:field_name].to_s.humanize %>
107
+ <span class="badge text-lowercase"><%= item[:field_name] %></span>
108
+ </td>
109
+ <td>
110
+ <%= item[:value] %>
111
+ </td>
112
+ <td>
113
+ <% if item[:reset_path].present? %>
114
+ <%= link_to_with_icon('arrow-counterclockwise.svg', "Publish path now", item[:reset_path],
115
+ method: :post,
116
+ remote: false,
117
+ class: 'icon_link btn btn-sm outline text-dark m-0 p-0',
118
+ data: { confirm: "Publish today's records path to the lobby now?" }, no_text: true
119
+ ) %>
120
+ <% end %>
121
+ </td>
122
+ </tr>
123
+ <% end %>
124
+ </tbody>
125
+ </table>
126
+ </div>
data/config/routes.rb CHANGED
@@ -14,6 +14,7 @@ Spree::Core::Engine.add_routes do
14
14
  resource :waiting_room, controller: :waiting_room, only: [:show] do
15
15
  collection do
16
16
  post :force_pull
17
+ post :publish_lobby_path
17
18
  post :modify_multiplier
18
19
  post :modify_max_thread_count
19
20
  end
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.8.3.pre.pre12'.freeze
2
+ VERSION = '2.8.3.pre.pre13'.freeze
3
3
 
4
4
  module_function
5
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_cm_commissioner
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.3.pre.pre12
4
+ version: 2.8.3.pre.pre13
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
@@ -1416,6 +1416,7 @@ files:
1416
1416
  - app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb
1417
1417
  - app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb
1418
1418
  - app/jobs/spree_cm_commissioner/waiting_guests_caller_job.rb
1419
+ - app/jobs/spree_cm_commissioner/waiting_room/publish_lobby_path_job.rb
1419
1420
  - app/jobs/spree_cm_commissioner/waiting_room_latest_system_metadata_puller_job.rb
1420
1421
  - app/jobs/spree_cm_commissioner/waiting_room_session_firebase_logger_job.rb
1421
1422
  - app/jobs/spree_cm_commissioner/webhook_subscriber_orders_sender_job.rb
@@ -2341,6 +2342,8 @@ files:
2341
2342
  - app/services/spree_cm_commissioner/voting_leaderboards/calculate_score.rb
2342
2343
  - app/services/spree_cm_commissioner/voting_leaderboards/combined_result.rb
2343
2344
  - app/services/spree_cm_commissioner/voting_sessions/finalize.rb
2345
+ - app/services/spree_cm_commissioner/waiting_room/publish_lobby_path.rb
2346
+ - app/services/spree_cm_commissioner/waiting_room_lobby_metadata_fetcher.rb
2344
2347
  - app/services/spree_cm_commissioner/waiting_room_system_metadata_fetcher.rb
2345
2348
  - app/services/spree_cm_commissioner/waiting_room_system_metadata_setter.rb
2346
2349
  - app/services/spree_cm_commissioner/webhooks/subscribers/handle_request_decorator.rb