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 +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/system/waiting_room_controller.rb +15 -0
- data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +49 -7
- data/app/jobs/spree_cm_commissioner/waiting_room/publish_lobby_path_job.rb +17 -0
- data/app/services/spree_cm_commissioner/waiting_room/publish_lobby_path.rb +48 -0
- data/app/services/spree_cm_commissioner/waiting_room_lobby_metadata_fetcher.rb +47 -0
- data/app/views/spree/admin/system/waiting_room/show.html.erb +53 -0
- data/config/routes.rb +1 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eedbddb863eb9f58dad73a83892a225e971a89736c37af047df757d76bcfc058
|
|
4
|
+
data.tar.gz: 59eaad5012be611640f1c27f6566bfb91cd7b5cea176d465be69c4230c8b9241
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa9db90a1c39aaa74f66135c5034cb8762e09123edaad154fb5fe6e013609b5344d23910bd8449380030c5b3e391b3a3792c92b569f5fbe2271d6e78420e55e6
|
|
7
|
+
data.tar.gz: 80c51833f840ba8b4db0611309153c7a7723fd986f065113a06063ac7d5aee78cce2af29dd2eb591b64a90c57a75f1f4716b9d7daf2664903ff34ad031f663e4
|
data/Gemfile.lock
CHANGED
|
@@ -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
|
|
23
|
-
# Client
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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(
|
|
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
|
-
|
|
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
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.
|
|
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
|