spree_cm_commissioner 2.8.15.pre.pre9 → 2.8.15

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.
Files changed (215) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +4 -0
  3. data/Gemfile.lock +2 -2
  4. data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +8 -0
  5. data/app/controllers/concerns/spree_cm_commissioner/load_test_account_management.rb +80 -0
  6. data/app/controllers/concerns/spree_cm_commissioner/load_test_account_scope.rb +39 -0
  7. data/app/controllers/concerns/spree_cm_commissioner/load_test_bypassable.rb +29 -0
  8. data/app/controllers/concerns/spree_cm_commissioner/load_test_login_token_generation.rb +53 -0
  9. data/app/controllers/concerns/spree_cm_commissioner/order_concern.rb +5 -0
  10. data/app/controllers/concerns/spree_cm_commissioner/turnstile_protectable.rb +9 -7
  11. data/app/controllers/spree/admin/guests_controller.rb +4 -2
  12. data/app/controllers/spree/admin/system/load_test_accounts_controller.rb +24 -0
  13. data/app/controllers/spree/admin/system/load_test_flows_controller.rb +11 -0
  14. data/app/controllers/spree/admin/system/load_test_login_tokens_controller.rb +16 -0
  15. data/app/controllers/spree/admin/system/load_testing_controller.rb +9 -0
  16. data/app/controllers/spree/admin/users_controller_decorator.rb +18 -0
  17. data/app/controllers/spree/api/chatrace/guests_controller.rb +1 -1
  18. data/app/controllers/spree/api/v2/operator/event_charts_controller.rb +1 -0
  19. data/app/controllers/spree/api/v2/operator/guest_json_gzips_controller.rb +14 -4
  20. data/app/controllers/spree/api/v2/storefront/account/guest_dynamic_fields_controller.rb +19 -48
  21. data/app/controllers/spree/api/v2/storefront/account/mark_guest_info_complete_controller.rb +2 -2
  22. data/app/controllers/spree/api/v2/storefront/account/orders_controller_decorator.rb +5 -15
  23. data/app/controllers/spree/api/v2/storefront/account/saved_guests_controller.rb +65 -0
  24. data/app/controllers/spree/api/v2/storefront/account_checker_controller.rb +4 -0
  25. data/app/controllers/spree/api/v2/storefront/draft_orders_controller.rb +52 -0
  26. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +10 -9
  27. data/app/controllers/spree/api/v2/storefront/order_histories_controller.rb +0 -1
  28. data/app/controllers/spree/api/v2/storefront/payment_methods_controller.rb +40 -0
  29. data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +1 -7
  30. data/app/controllers/spree/api/v2/storefront/telegram_oauth_tokens_controller.rb +61 -0
  31. data/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb +8 -1
  32. data/app/controllers/spree/api/v2/tenant/account/orders_controller.rb +1 -1
  33. data/app/controllers/spree/api/v2/tenant/account/saved_guests_controller.rb +70 -0
  34. data/app/controllers/spree/api/v2/tenant/draft_orders_controller.rb +50 -0
  35. data/app/controllers/spree/api/v2/tenant/episodes_controller.rb +3 -1
  36. data/app/controllers/spree/api/v2/tenant/guests_controller.rb +11 -21
  37. data/app/controllers/spree/api/v2/tenant/my_votes_controller.rb +51 -0
  38. data/app/controllers/spree/api/v2/tenant/order_histories_controller.rb +2 -1
  39. data/app/controllers/spree/api/v2/tenant/payment_methods_controller.rb +54 -0
  40. data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +1 -1
  41. data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +1 -7
  42. data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +1 -4
  43. data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +4 -1
  44. data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +6 -3
  45. data/app/controllers/spree/api/v2/tenant/waiting_room_sessions_controller.rb +8 -1
  46. data/app/controllers/spree_cm_commissioner/admin/orders_controller_decorator.rb +9 -0
  47. data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +17 -0
  48. data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +3 -3
  49. data/app/finders/spree_cm_commissioner/payment_methods/find.rb +59 -0
  50. data/app/finders/spree_cm_commissioner/routes/find.rb +1 -1
  51. data/app/interactors/spree_cm_commissioner/create_ticket.rb +7 -3
  52. data/app/interactors/spree_cm_commissioner/host_matcher.rb +6 -5
  53. data/app/interactors/spree_cm_commissioner/telegram_oauth_id_token_provider.rb +99 -0
  54. data/app/interactors/spree_cm_commissioner/telegram_oauth_token_exchanger.rb +71 -0
  55. data/app/interactors/spree_cm_commissioner/turnstile_token_validator.rb +43 -13
  56. data/app/interactors/spree_cm_commissioner/user_id_token_authenticator.rb +2 -5
  57. data/app/interactors/spree_cm_commissioner/user_password_authenticator.rb +23 -1
  58. data/app/interactors/spree_cm_commissioner/user_telegram_oauth_authenticator.rb +88 -0
  59. data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +11 -2
  60. data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +31 -6
  61. data/app/jobs/spree_cm_commissioner/maintenance_tasks/guest_data_fill_stage_batch_job.rb +28 -0
  62. data/app/mailers/spree/order_mailer_decorator.rb +2 -0
  63. data/app/models/concerns/spree_cm_commissioner/event_metadata.rb +17 -2
  64. data/app/models/concerns/spree_cm_commissioner/guest_data_fill_stage_concern.rb +82 -0
  65. data/app/models/concerns/spree_cm_commissioner/kyc_bitwise.rb +17 -0
  66. data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +4 -0
  67. data/app/models/concerns/spree_cm_commissioner/line_item_guests_concern.rb +1 -1
  68. data/app/models/concerns/spree_cm_commissioner/line_items_filter_scope.rb +5 -3
  69. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +0 -1
  70. data/app/models/concerns/spree_cm_commissioner/order_qr_codeable.rb +39 -0
  71. data/app/models/concerns/spree_cm_commissioner/order_scopes.rb +5 -0
  72. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +26 -31
  73. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +2 -0
  74. data/app/models/concerns/spree_cm_commissioner/purchase_limitable.rb +59 -0
  75. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +2 -0
  76. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +2 -0
  77. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +0 -1
  78. data/app/models/spree_cm_commissioner/check_in_session.rb +36 -0
  79. data/app/models/spree_cm_commissioner/current.rb +11 -0
  80. data/app/models/spree_cm_commissioner/dynamic_field.rb +12 -0
  81. data/app/models/spree_cm_commissioner/exports/operator_guest_json_gzip.rb +26 -1
  82. data/app/models/spree_cm_commissioner/guest.rb +5 -144
  83. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +43 -71
  84. data/app/models/spree_cm_commissioner/homepage_section_relatable.rb +25 -0
  85. data/app/models/spree_cm_commissioner/line_item_decorator.rb +4 -3
  86. data/app/models/spree_cm_commissioner/maintenance_tasks/event.rb +9 -1
  87. data/app/models/spree_cm_commissioner/maintenance_tasks/guest_data_fill_stage.rb +21 -0
  88. data/app/models/spree_cm_commissioner/order_decorator.rb +22 -31
  89. data/app/models/spree_cm_commissioner/payment_method_decorator.rb +2 -0
  90. data/app/models/spree_cm_commissioner/pricing_model_route.rb +8 -0
  91. data/app/models/spree_cm_commissioner/product_decorator.rb +2 -22
  92. data/app/models/spree_cm_commissioner/product_dynamic_field.rb +17 -0
  93. data/app/models/spree_cm_commissioner/request_guard.rb +19 -0
  94. data/app/models/spree_cm_commissioner/role_decorator.rb +52 -4
  95. data/app/models/spree_cm_commissioner/role_user_decorator.rb +11 -0
  96. data/app/models/spree_cm_commissioner/seat_layout.rb +12 -4
  97. data/app/models/spree_cm_commissioner/show.rb +3 -1
  98. data/app/models/spree_cm_commissioner/show_episode.rb +3 -1
  99. data/app/models/spree_cm_commissioner/show_person.rb +5 -1
  100. data/app/models/spree_cm_commissioner/show_person_assignment.rb +1 -1
  101. data/app/models/spree_cm_commissioner/taxon_decorator.rb +2 -0
  102. data/app/models/spree_cm_commissioner/tenant.rb +17 -6
  103. data/app/models/spree_cm_commissioner/trip.rb +18 -1
  104. data/app/models/spree_cm_commissioner/user_decorator.rb +67 -1
  105. data/app/models/spree_cm_commissioner/variant_options.rb +0 -4
  106. data/app/models/spree_cm_commissioner/vehicle_type.rb +1 -0
  107. data/app/models/spree_cm_commissioner/voting_contestant.rb +21 -7
  108. data/app/models/spree_cm_commissioner/waiting_room_session.rb +14 -0
  109. data/app/overrides/spree/admin/payment_methods/index/pagination.html.erb.deface +3 -0
  110. data/app/overrides/spree/admin/payment_methods/index/preserve_tab_in_search.html.erb.deface +4 -0
  111. data/app/overrides/spree/admin/products/_form/enable_social_sharing_email.html.erb.deface +13 -0
  112. data/app/overrides/spree/admin/shared/_order_summary/internal_note.html.erb.deface +23 -0
  113. data/app/overrides/spree/admin/shared/_order_summary/internal_note_modal.html.erb.deface +40 -0
  114. data/app/overrides/spree/admin/stores/_form/telegram_auth.html.erb.deface +73 -0
  115. data/app/overrides/spree/admin/variants/index/fixture_action.html.erb.deface +9 -0
  116. data/app/queries/spree_cm_commissioner/event_chart_queries.rb +25 -9
  117. data/app/queries/spree_cm_commissioner/guest_searcher_query.rb +16 -3
  118. data/app/queries/spree_cm_commissioner/multi_leg_trips_query.rb +7 -0
  119. data/app/queries/spree_cm_commissioner/pie_chart_event_aggregator_queries.rb +3 -2
  120. data/app/queries/spree_cm_commissioner/single_leg_trips_query.rb +2 -1
  121. data/app/queries/spree_cm_commissioner/user_votes_per_contestant_query.rb +48 -0
  122. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +2 -2
  123. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +4 -0
  124. data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +1 -0
  125. data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +5 -2
  126. data/app/serializers/spree/v2/storefront/variant_serializer_decorator.rb +2 -1
  127. data/app/serializers/spree/v2/tenant/my_vote_serializer.rb +19 -0
  128. data/app/serializers/spree/v2/tenant/product_serializer.rb +5 -0
  129. data/app/serializers/spree/v2/tenant/saved_guest_serializer.rb +23 -0
  130. data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +1 -9
  131. data/app/serializers/spree/v2/tenant/show_serializer.rb +2 -1
  132. data/app/serializers/spree/v2/tenant/user_serializer.rb +1 -1
  133. data/app/serializers/spree/v2/tenant/variant_serializer.rb +2 -1
  134. data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +2 -0
  135. data/app/serializers/spree_cm_commissioner/v2/storefront/cart_serializer_decorator.rb +1 -0
  136. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +3 -6
  137. data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_serializer.rb +2 -2
  138. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +2 -2
  139. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_serializer.rb +3 -1
  140. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +24 -3
  141. data/app/services/spree_cm_commissioner/check_ins/create_bulk.rb +10 -6
  142. data/app/services/spree_cm_commissioner/draft_order/create.rb +95 -0
  143. data/app/services/spree_cm_commissioner/{cart/create_guest.rb → guests/create.rb} +12 -10
  144. data/app/services/spree_cm_commissioner/guests/refresh_data_fill_stage.rb +20 -0
  145. data/app/services/spree_cm_commissioner/guests/send_dynamic_field_notification.rb +15 -0
  146. data/app/services/spree_cm_commissioner/guests/update.rb +68 -0
  147. data/app/services/spree_cm_commissioner/imports/contestants/create.rb +19 -1
  148. data/app/services/spree_cm_commissioner/inventory_holds/validate_limits.rb +8 -3
  149. data/app/services/spree_cm_commissioner/line_items/validate_purchase_limits.rb +97 -0
  150. data/app/services/spree_cm_commissioner/load_test/create_accounts.rb +68 -0
  151. data/app/services/spree_cm_commissioner/load_test/destroy_accounts.rb +47 -0
  152. data/app/services/spree_cm_commissioner/load_test/flow_registry.rb +26 -0
  153. data/app/services/spree_cm_commissioner/load_test/generate_booking_ticket_data.rb +204 -0
  154. data/app/services/spree_cm_commissioner/load_test/generate_voting_fixture_data.rb +178 -0
  155. data/app/services/spree_cm_commissioner/load_test/login_token.rb +46 -0
  156. data/app/services/spree_cm_commissioner/operator_guest_json_gzips/create.rb +14 -7
  157. data/app/services/spree_cm_commissioner/order/guests/update.rb +53 -0
  158. data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +12 -11
  159. data/app/services/spree_cm_commissioner/pricing_models/create_with_rule_groups.rb +9 -3
  160. data/app/services/spree_cm_commissioner/pricing_models/preview.rb +49 -6
  161. data/app/services/spree_cm_commissioner/pricing_models/update_with_rule_groups.rb +10 -7
  162. data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +5 -1
  163. data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +6 -1
  164. data/app/services/spree_cm_commissioner/user_authenticator.rb +22 -1
  165. data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +10 -3
  166. data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +20 -2
  167. data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +15 -9
  168. data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +13 -2
  169. data/app/services/spree_cm_commissioner/voting_leaderboards/time_range_result.rb +79 -0
  170. data/app/views/spree/admin/featured_trips/index.html.erb +1 -1
  171. data/app/views/spree/admin/guests/_form.html.erb +37 -0
  172. data/app/views/spree/admin/shared/_system_tabs.html.erb +7 -0
  173. data/app/views/spree/admin/system/load_test_accounts/_login_tokens_modal.html.erb +52 -0
  174. data/app/views/spree/admin/system/load_test_accounts/index.html.erb +175 -0
  175. data/app/views/spree/admin/system/load_test_flows/index.html.erb +66 -0
  176. data/app/views/spree/admin/system/load_testing/index.html.erb +25 -0
  177. data/app/views/spree/admin/tenants/_form.html.erb +40 -1
  178. data/app/views/spree/admin/tenants/form/_telegram_auth.html.erb +61 -0
  179. data/app/views/spree/admin/variants/fixture.html.erb +49 -0
  180. data/config/initializers/doorkeeper.rb +10 -6
  181. data/config/initializers/spree_permitted_attributes.rb +51 -0
  182. data/config/load_test_flows/platform.yml +25 -0
  183. data/config/load_test_flows/tenant.yml +93 -0
  184. data/config/locales/en.yml +127 -13
  185. data/config/locales/km.yml +39 -8
  186. data/config/routes.rb +26 -1
  187. data/db/migrate/20260630000001_add_trip_to_cm_pricing_model_routes.rb +7 -0
  188. data/db/migrate/20260701000001_add_device_info_to_cm_waiting_room_sessions.rb +14 -0
  189. data/db/migrate/20260703065239_add_position_to_cm_voting_contestants.rb +6 -0
  190. data/db/migrate/20260708150000_add_name_and_short_name_to_cm_trips.rb +6 -0
  191. data/db/migrate/20260713022227_add_advanced_to_episode_id_to_cm_voting_contestants.rb +8 -0
  192. data/db/migrate/20260714002626_rename_data_fill_stage_phase_on_cm_guests.rb +5 -0
  193. data/db/migrate/20260720000001_add_check_in_fields_to_spree_roles.rb +11 -0
  194. data/db/migrate/20260723000000_add_payment_host_to_cm_tenants.rb +6 -0
  195. data/db/migrate/20260723000001_add_metadata_to_spree_roles.rb +11 -0
  196. data/db/migrate/20260728074153_add_role_to_cm_exports.rb +5 -0
  197. data/db/migrate/20260729000000_add_visibility_to_cm_trips.rb +6 -0
  198. data/db/migrate/20260729000001_add_session_created_at_index_to_cm_votes.rb +12 -0
  199. data/db/migrate/20260730000000_add_purchase_limit_scopes_to_spree_orders.rb +22 -0
  200. data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +0 -6
  201. data/lib/spree_cm_commissioner/test_helper/factories/pricing_model_trip_factory.rb +11 -0
  202. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +1 -1
  203. data/lib/spree_cm_commissioner/trip_result.rb +3 -1
  204. data/lib/spree_cm_commissioner/version.rb +1 -1
  205. data/lib/tasks/recompute_guest_data_fill_stage.rake +19 -0
  206. metadata +78 -14
  207. data/app/controllers/spree/api/v2/storefront/saved_guests_controller.rb +0 -77
  208. data/app/interactors/spree_cm_commissioner/guest_dynamic_field_notification_sender.rb +0 -11
  209. data/app/interactors/spree_cm_commissioner/guest_dynamic_fields_manager.rb +0 -33
  210. data/app/serializers/spree/v2/storefront/order_serializer_decorator.rb +0 -63
  211. data/app/serializers/spree_cm_commissioner/v2/storefront/guest_minimal_serializer.rb +0 -30
  212. data/app/serializers/spree_cm_commissioner/v2/storefront/line_item_minimal_serializer.rb +0 -57
  213. data/app/serializers/spree_cm_commissioner/v2/storefront/ticket_transfer_minimal_serializer.rb +0 -17
  214. data/app/services/spree_cm_commissioner/update_guest_service.rb +0 -129
  215. data/app/services/spree_cm_commissioner/users/incomplete_guest_checker_service.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db6de320351a3caae8243230898ce35ae85406e84424101bc01b126ce7eefb4c
4
- data.tar.gz: 97a586260f667681ee89ca8aa224fc3ba05137dfcfbf4453191137e15d724735
3
+ metadata.gz: ca0138759a1da40541b5d076fb9709847d44d88885593f5b69921993cfd26591
4
+ data.tar.gz: 79408535d56a9d4b62cac5154a316f6652d6d8664c04a030f00588b39f7ddd24
5
5
  SHA512:
6
- metadata.gz: 8d9ab3710513dd89d55d6422d924ce10c0e83f661cdf2576650586b1a11a627db19a161a8aa657c04d305201a10d1f4823d1ea2fff44657638829d7ba7c2ef52
7
- data.tar.gz: de4d493586903410f18877bbb2e959383b42765b241c4d90cb08e0aef5cda6618433a24c6c18714391d60805176971b0aa12bb7165a694818abea06915164697
6
+ metadata.gz: 3e1a90b2f9115755fcd8cb777e0cd4d3c05b950a8a1b913ac526be4c5967d062533b5683bf48d3ca979e726347bb1945520b0701337872e7626d2fc3524d7952
7
+ data.tar.gz: 8b4813fe21cc8495e2a512ea1662e2105ba61994c1866794bd9da91ef5c805b1b49cc9d8ea5cdf53f35dd2fe6a96295491eea72853cfdaf899377264922e8a84
data/.env.example CHANGED
@@ -32,6 +32,10 @@ WAITING_ROOM_MAX_ETA_BATCH_SIZE=50 # cap on batch size assumed when estimating t
32
32
  VATTANAC_AES_SECRET_KEY= ""
33
33
  VATTANAC_PUBLIC_KEY=""
34
34
 
35
+ # Telegram OAuth (OpenID Connect) login client_id/client_secret are configured
36
+ # per tenant (or on the default store for tenant-less logins) from the admin
37
+ # under Telegram Authentication - see docs/authentication/telegram-login.md.
38
+
35
39
  # Organizer URL
36
40
  ORGANIZER_URL=http://127.0.0.1:4000/organizer
37
41
 
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.8.15.pre.pre9)
37
+ spree_cm_commissioner (2.8.15)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -886,7 +886,7 @@ GEM
886
886
  spree_backend (>= 4.3.0.rc1)
887
887
  spree_emails (>= 4.3.0.rc1)
888
888
  spree_extension
889
- spree_vpago (2.0.5.pre.beta)
889
+ spree_vpago (2.3.4)
890
890
  faraday
891
891
  google-cloud-firestore
892
892
  spree_api (>= 4.5)
@@ -27,6 +27,13 @@ module SpreeCmCommissioner
27
27
  PopularRoutesController
28
28
  RoutePlacesController
29
29
  ShowsController
30
+ EpisodesController
31
+ ShowPeopleController
32
+ ShowPersonAssignmentsController
33
+ ShowContestantsController
34
+ VotePackagesController
35
+ ShowEliminationSessionsController
36
+ PaymentMethodsController
30
37
  ].freeze
31
38
 
32
39
  # Priority 3: Moderate Freshness (30 minutes)
@@ -46,6 +53,7 @@ module SpreeCmCommissioner
46
53
  HIGH_FRESHNESS_CONTROLLERS = %w[
47
54
  TripsController
48
55
  TripSearchController
56
+ VotingContestantsController
49
57
  ].freeze
50
58
 
51
59
  def shared_max_age
@@ -0,0 +1,80 @@
1
+ module SpreeCmCommissioner
2
+ module LoadTestAccountManagement
3
+ extend ActiveSupport::Concern
4
+
5
+ include SpreeCmCommissioner::LoadTestAccountScope
6
+
7
+ included do
8
+ before_action :authorize_load_testing!, only: %i[create bulk_destroy]
9
+ end
10
+
11
+ # Ensures N load-test accounts exist (idempotent) for `load_test_tenant` (nil = platform).
12
+ def create
13
+ result = SpreeCmCommissioner::LoadTest::CreateAccounts.call(**pool_params)
14
+
15
+ if result.success?
16
+ flash[:success] = "#{result.value[:created]} load-test accounts created."
17
+ else
18
+ flash[:error] = result.error.value
19
+ end
20
+
21
+ redirect_to manage_accounts_redirect_path
22
+ rescue StandardError => e
23
+ flash[:error] = e.message
24
+ redirect_to manage_accounts_redirect_path
25
+ end
26
+
27
+ def bulk_destroy
28
+ result = SpreeCmCommissioner::LoadTest::DestroyAccounts.call(**pool_params)
29
+
30
+ if result.success?
31
+ flash[:success] = "#{result.value[:destroyed]} destroyed, #{result.value[:not_found]} not found (already removed)."
32
+ else
33
+ flash[:error] = result.error.value
34
+ end
35
+
36
+ redirect_to manage_accounts_redirect_path
37
+ rescue StandardError => e
38
+ flash[:error] = e.message
39
+ redirect_to manage_accounts_redirect_path
40
+ end
41
+
42
+ private
43
+
44
+ # Default: cancancan's `authorize!`. Override in the including controller if it doesn't use
45
+ # cancancan (e.g. the organizer dashboard, which authorizes via RoleAuthorization/can_manage?).
46
+ def authorize_load_testing!
47
+ authorize! :manage, :load_testing
48
+ end
49
+
50
+ # Where create/bulk_destroy redirect back to. Must be implemented by the including controller
51
+ # (points at its own Load Test Accounts page).
52
+ def manage_accounts_redirect_path
53
+ raise NotImplementedError, "#{self.class} must implement #manage_accounts_redirect_path"
54
+ end
55
+
56
+ def pool_params
57
+ {
58
+ client_id: resolve_client_id,
59
+ count: params[:count].presence&.to_i || 20,
60
+ email_prefix: params[:email_prefix].presence || 'loadtest',
61
+ email_domain: params[:email_domain].presence
62
+ }
63
+ end
64
+
65
+ # Carries the form field values through the redirect so the index page re-renders with the
66
+ # same count/prefix/domain/page/per_page the admin/organizer just submitted with.
67
+ def redirect_params
68
+ params.slice(:count, :email_prefix, :email_domain, :page, :per_page).permit!
69
+ end
70
+
71
+ def resolve_client_id
72
+ return nil if load_test_tenant.nil?
73
+
74
+ application = Spree::OauthApplication.where(tenant_id: load_test_tenant.id).order(:id).first
75
+ raise "No OAuth application found for tenant #{load_test_tenant.name}" unless application
76
+
77
+ application.uid
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,39 @@
1
+ module SpreeCmCommissioner
2
+ module LoadTestAccountScope
3
+ extend ActiveSupport::Concern
4
+
5
+ private
6
+
7
+ # The tenant these accounts are scoped to. nil means the platform (no tenant). Must be
8
+ # implemented by the including controller.
9
+ def load_test_tenant
10
+ raise NotImplementedError, "#{self.class} must implement #load_test_tenant"
11
+ end
12
+
13
+ DEFAULT_EXPORT_PER_PAGE = 10
14
+
15
+ # Queried fresh from the DB every time, so it's always in sync with the current pool rather than
16
+ # a stashed file or session state.
17
+ #
18
+ # Filters the `load_test_account` flag in SQL via the model's `load_test_accounts` scope (see
19
+ # UserDecorator), so only the tenant's load-test accounts are considered — never the whole users
20
+ # table. Unlike the old `order(:id).limit(2000).select(&:...)` approach, there's no scan cap:
21
+ # newly created accounts (highest ids, ordered first below) always show up even on tenants with
22
+ # far more than 2,000 users.
23
+ #
24
+ # Kaminari pages on the relation itself, so the DB only ever loads the current page's rows
25
+ # (`LIMIT`/`OFFSET`) — never the full pool into memory — via the same `page`/`per_page` params
26
+ # and `paginate` view helper every other index page in this app uses.
27
+ #
28
+ # Shared between LoadTestAccountManagement (lists/mutates the pool) and
29
+ # LoadTestLoginTokenGeneration (mints tokens for exactly the page shown), so both controllers
30
+ # resolve the same page the same way.
31
+ def export_accounts
32
+ Spree::User.where(tenant_id: load_test_tenant&.id)
33
+ .load_test_accounts
34
+ .order(id: :desc)
35
+ .page(params[:page])
36
+ .per(params[:per_page] || DEFAULT_EXPORT_PER_PAGE)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ module SpreeCmCommissioner
2
+ # Sets Current.load_test_bypass when the request carries the correct X-Request-Verify secret, so
3
+ # anti-abuse services (e.g. InventoryHolds::ValidateLimits) can skip their checks for trusted
4
+ # load tests. No-op unless LOAD_TEST_BYPASS_KEY is set. Include in any controller that can
5
+ # trigger such a check.
6
+ module LoadTestBypassable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ before_action :set_load_test_bypass_current
11
+ end
12
+
13
+ private
14
+
15
+ # Always assigns the flag (false unless the request carries the correct
16
+ # secret), so its value never leaks from a previous request.
17
+ def set_load_test_bypass_current
18
+ SpreeCmCommissioner::Current.load_test_bypass = load_test_bypass_requested?
19
+ end
20
+
21
+ def load_test_bypass_requested?
22
+ key = ENV.fetch('LOAD_TEST_BYPASS_KEY', nil)
23
+ return false if key.blank?
24
+
25
+ provided = request.headers.fetch('X-Request-Verify', nil).to_s
26
+ ActiveSupport::SecurityUtils.secure_compare(provided, key)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ module SpreeCmCommissioner
2
+ module LoadTestLoginTokenGeneration
3
+ extend ActiveSupport::Concern
4
+
5
+ include SpreeCmCommissioner::LoadTestAccountScope
6
+
7
+ included do
8
+ before_action :authorize_load_testing!, only: %i[create]
9
+ end
10
+
11
+ # Mints a fresh, stateless login-token JWT for each account on the requested page (same
12
+ # page/per_page as the Load Test Accounts index) and returns them as JSON. Nothing is
13
+ # persisted — the tokens are self-contained and verified by signature + expiry alone (see
14
+ # SpreeCmCommissioner::LoadTest::LoginToken).
15
+ def create
16
+ render json: login_tokens_payload(export_accounts)
17
+ rescue StandardError => e
18
+ render json: { error: e.message }, status: :unprocessable_entity
19
+ end
20
+
21
+ private
22
+
23
+ # Default: cancancan's `authorize!`. Override in the including controller if it doesn't use
24
+ # cancancan (e.g. the organizer dashboard, which authorizes via RoleAuthorization/can_manage?).
25
+ def authorize_load_testing!
26
+ authorize! :manage, :load_testing
27
+ end
28
+
29
+ # aud claim: the admin/organizer who minted the tokens. No fallback — both hosting controllers
30
+ # already require an authenticated user for every action here, so spree_current_user being
31
+ # absent would be a bug worth raising on, not silently mislabeling as 'system'.
32
+ def token_audience
33
+ spree_current_user.id
34
+ end
35
+
36
+ # user_id + the user_id_token are all k6 needs (email and expiry are already claims inside the
37
+ # JWT itself — sub / exp — and the token alone identifies + authenticates the account via
38
+ # UserPasswordAuthenticator's token-only path). expires_at is included too, purely so the
39
+ # generated-tokens list can show it to a human without decoding the JWT. Named `user_id_token`
40
+ # (not `id_token`) so UserAuthenticator can route on the params key alone, without decoding
41
+ # every request's `id_token` to check whether it's a load-test token.
42
+ def login_tokens_payload(accounts)
43
+ accounts.map do |user|
44
+ generated = SpreeCmCommissioner::LoadTest::LoginToken.generate(user: user, audience: token_audience)
45
+ {
46
+ user_id: user.id,
47
+ user_id_token: generated[:token],
48
+ expires_at: generated[:expires_at]
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -12,6 +12,7 @@ module SpreeCmCommissioner
12
12
  end
13
13
 
14
14
  @spree_current_order.last_ip_address = request.remote_ip
15
+ @spree_current_order.device_id = device_id if device_id.present?
15
16
  @spree_current_order
16
17
  end
17
18
 
@@ -25,5 +26,9 @@ module SpreeCmCommissioner
25
26
  state: params[:state]
26
27
  )
27
28
  end
29
+
30
+ def device_id
31
+ params[:device_id].presence
32
+ end
28
33
  end
29
34
  end
@@ -27,13 +27,15 @@ module SpreeCmCommissioner
27
27
  return unless result.failure?
28
28
 
29
29
  render json: {
30
- errors: [{
31
- status: '403',
32
- error_code: 'human_verification_failed',
33
- title: I18n.t('turnstile.verification_failed'),
34
- detail: result.message
35
- }
36
- ]
30
+ errors: [
31
+ {
32
+ status: '403',
33
+ error_code: 'human_verification_failed',
34
+ title: I18n.t('turnstile.verification_failed'),
35
+ detail: result.message,
36
+ turnstile_error_code: result.respond_to?(:error_code) ? result.error_code : nil
37
+ }
38
+ ]
37
39
  }, status: :forbidden
38
40
  end
39
41
 
@@ -80,8 +80,10 @@ module Spree
80
80
 
81
81
  def permitted_resource_params
82
82
  params.require(object_name).permit(:first_name, :last_name, :gender, :dob, :occupation_id,
83
- :nationality_id, :age, :emergency_contact, :line_item_id,
84
- :seat_number, :phone_number
83
+ :nationality_id, :age, :emergency_contact,
84
+ :line_item_id, :seat_number, :phone_number, :contact,
85
+ :other_organization, :expectation, :address,
86
+ :social_contact_platform, :social_contact
85
87
  )
86
88
  end
87
89
 
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ module Admin
3
+ module System
4
+ class LoadTestAccountsController < Spree::Admin::BaseController
5
+ include SpreeCmCommissioner::LoadTestAccountManagement
6
+
7
+ def index
8
+ @export_accounts = export_accounts
9
+ end
10
+
11
+ private
12
+
13
+ # nil = platform. Admin load testing never operates on a tenant.
14
+ def load_test_tenant
15
+ nil
16
+ end
17
+
18
+ def manage_accounts_redirect_path
19
+ admin_system_load_test_accounts_path(**redirect_params)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Admin
3
+ module System
4
+ class LoadTestFlowsController < Spree::Admin::BaseController
5
+ def index
6
+ @load_test_flows = SpreeCmCommissioner::LoadTest::FlowRegistry.for_workflow('platform')
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module Spree
2
+ module Admin
3
+ module System
4
+ class LoadTestLoginTokensController < Spree::Admin::BaseController
5
+ include SpreeCmCommissioner::LoadTestLoginTokenGeneration
6
+
7
+ private
8
+
9
+ # nil = platform. Admin load testing never operates on a tenant.
10
+ def load_test_tenant
11
+ nil
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Spree
2
+ module Admin
3
+ module System
4
+ class LoadTestingController < Spree::Admin::BaseController
5
+ def index; end
6
+ end
7
+ end
8
+ end
9
+ end
@@ -20,8 +20,26 @@ module Spree
20
20
  end
21
21
  end
22
22
 
23
+ # override
24
+ def update
25
+ preserve_non_form_roles
26
+ super
27
+ end
28
+
23
29
  private
24
30
 
31
+ # Role types the user form doesn't render (assigned in organizer dashboards), e.g. check-in groups and preview.
32
+ NON_FORM_ROLE_TYPES = %i[check_in preview].freeze
33
+
34
+ # spree_role_ids= replaces the whole role set, so a submit that omits these would drop them. Merge
35
+ # the user's existing ones back in so they survive. Skip when the form didn't submit roles at all.
36
+ def preserve_non_form_roles
37
+ return if params.dig(:user, :spree_role_ids).nil?
38
+
39
+ preserved_ids = @user.spree_roles.where(role_type: NON_FORM_ROLE_TYPES).pluck(:id).map(&:to_s)
40
+ params[:user][:spree_role_ids] = (params[:user][:spree_role_ids] + preserved_ids).uniq
41
+ end
42
+
25
43
  def build_profile
26
44
  return unless permitted_resource_params[:profile]
27
45
 
@@ -53,7 +53,7 @@ module Spree
53
53
  preferences upload_later social_contact_platform user_id
54
54
  bib_number bib_prefix bib_index public_metadata private_metadata
55
55
  seat_number intel_phone_number block_id saved_guest_id
56
- data_fill_stage_phase
56
+ data_fill_stage
57
57
  ].freeze
58
58
 
59
59
  def guest_base_columns
@@ -36,6 +36,7 @@ module Spree
36
36
  SpreeCmCommissioner::EventChartQueries.new(
37
37
  taxon_id: params[:taxon_id],
38
38
  chart_type: params[:id] || 'participation',
39
+ check_in_session_id: params[:check_in_session_id],
39
40
  refreshed: params[:refreshed] || false
40
41
  )
41
42
  end
@@ -26,6 +26,8 @@ module Spree
26
26
 
27
27
  result = SpreeCmCommissioner::OperatorGuestJsonGzips::Create.call(
28
28
  exportable: exportable,
29
+ # Scope the offline file to the requesting operator's check-in role (nil = unrestricted).
30
+ role: spree_current_user.check_in_role_for(exportable),
29
31
  force_create: false,
30
32
  options: {
31
33
  created_by_id: spree_current_user.id.to_s,
@@ -53,11 +55,19 @@ module Spree
53
55
  end
54
56
 
55
57
  # override
58
+ # Scope the list to the requesting operator's check-in group, the same way the create cache
59
+ # does — otherwise a restricted crew could list (and download via exported_file_url) other
60
+ # groups' files for the event.
56
61
  def scope
57
- SpreeCmCommissioner::Exports::OperatorGuestJsonGzip.where(
58
- exportable_type: 'Spree::Taxon',
59
- exportable_id: params[:taxon_id]
60
- ).order(created_at: :desc)
62
+ exportable = Spree::Taxon.find(params[:taxon_id])
63
+
64
+ SpreeCmCommissioner::Exports::OperatorGuestJsonGzip
65
+ .where(
66
+ exportable_type: 'Spree::Taxon',
67
+ exportable_id: params[:taxon_id],
68
+ role_id: spree_current_user.check_in_role_for(exportable)&.id
69
+ )
70
+ .order(created_at: :desc)
61
71
  end
62
72
 
63
73
  # override
@@ -4,69 +4,40 @@ module Spree
4
4
  module Storefront
5
5
  module Account
6
6
  class GuestDynamicFieldsController < ::Spree::Api::V2::ResourceController
7
+ ALLOWED_PHASES = %w[pre_registration post_registration].freeze
8
+
7
9
  before_action :require_spree_current_user
8
10
  before_action :load_guest
9
11
 
10
12
  def update_dynamic_field
11
- phase = params[:phase]
12
-
13
- return render_error_payload('Invalid phase', 422) unless %w[pre_registration post_registration].include?(phase)
14
-
15
- begin
16
- fields = dynamic_fields_params
17
- rescue ActionController::ParameterMissing
18
- return render_error_payload('No dynamic fields provided', 422)
19
- end
20
-
21
- return render_error_payload('No dynamic fields provided', 422) if fields.blank?
22
-
23
- ActiveRecord::Base.transaction do
24
- fields.each do |field|
25
- process_dynamic_field(field)
26
- end
27
-
28
- @guest.save_and_move_to_next_stage
13
+ return render_error_payload('Invalid phase') unless ALLOWED_PHASES.include?(params[:phase])
14
+ return render_error_payload('No dynamic fields provided') if dynamic_fields_params.blank?
15
+
16
+ result = SpreeCmCommissioner::Guests::Update.call(
17
+ guest: @guest,
18
+ guest_params: {
19
+ guest_dynamic_fields_attributes: dynamic_fields_params.map { |f| { dynamic_field_id: f[:id], value: f[:value] } }
20
+ }
21
+ )
22
+
23
+ if result.success?
24
+ render_serialized_payload { serialize_guest(result.value[:guest]) }
25
+ else
26
+ render_error_payload(result.error)
29
27
  end
30
-
31
- render_serialized_payload { serialize_guest(@guest) }
32
- rescue ActiveRecord::RecordInvalid => e
33
- render_error_payload(e.record.errors, 422)
34
- rescue ActiveRecord::RecordNotFound => e
35
- render_error_payload(e.message, 404)
36
28
  end
37
29
 
38
30
  private
39
31
 
40
32
  def dynamic_fields_params
41
- params.require(:dynamic_fields).map do |field|
42
- field.permit(:id, :value)
43
- end
33
+ Array(params[:dynamic_fields]).map { |field| field.permit(:id, :value) }
44
34
  end
45
35
 
46
36
  def load_guest
47
37
  @guest = SpreeCmCommissioner::Guest.find(params[:guest_id])
48
38
 
49
- unless spree_current_user.guests.exists?(id: @guest.id) ||
50
- @guest.line_item.order.user == spree_current_user
51
- render_error_payload('Unauthorized', 401)
52
- end
53
- end
54
-
55
- def process_dynamic_field(field)
56
- dynamic_field = SpreeCmCommissioner::DynamicField.find(field[:id])
57
- guest_field = @guest.guest_dynamic_fields.build(dynamic_field_id: dynamic_field.id)
58
-
59
- if dynamic_field.requires_dynamic_field_options?
60
- option = dynamic_field.dynamic_field_options.find_by(value: field[:value])
61
- raise ActiveRecord::RecordNotFound, "Invalid value for dynamic field #{dynamic_field.label}" unless option
62
-
63
- guest_field.dynamic_field_option = option
64
- guest_field.value = option.value
65
- else
66
- guest_field.value = field[:value]
67
- end
68
-
69
- guest_field.save!
39
+ raise CanCan::AccessDenied unless spree_current_user.guests.exists?(id: @guest.id) ||
40
+ @guest.line_item.order.user == spree_current_user
70
41
  end
71
42
 
72
43
  def serialize_guest(guest)
@@ -7,7 +7,7 @@ module Spree
7
7
  before_action :require_spree_current_user
8
8
 
9
9
  def update
10
- SpreeCmCommissioner::Users::IncompleteGuestCheckerService.new(spree_current_user).call
10
+ spree_current_user.clear_has_incomplete_guest_info_cache
11
11
  render_success
12
12
  end
13
13
 
@@ -18,7 +18,7 @@ module Spree
18
18
  {
19
19
  success: true,
20
20
  message: 'Guest information marked as complete',
21
- has_incomplete_guest_info: false
21
+ has_incomplete_guest_info: spree_current_user.has_incomplete_guest_info?
22
22
  }
23
23
  end
24
24
  end
@@ -24,21 +24,11 @@ module Spree
24
24
  private
25
25
 
26
26
  def find_orders_with_incomplete_guest_info
27
- orders = spree_current_user.orders
28
- .complete
29
- .includes(line_items: { guests: [:guest_dynamic_fields] })
30
-
31
- incomplete_order_ids = orders.select do |order|
32
- order.line_items.any? do |line_item|
33
- line_item.guests.any? do |guest|
34
- guest.pre_registration_completed? &&
35
- guest.post_registration_fields? &&
36
- !guest.post_registration_completed?
37
- end
38
- end
39
- end.map(&:id)
40
-
41
- spree_current_user.orders.where(id: incomplete_order_ids)
27
+ spree_current_user.orders
28
+ .complete
29
+ .joins(:guests)
30
+ .merge(SpreeCmCommissioner::Guest.post_registration)
31
+ .distinct
42
32
  end
43
33
 
44
34
  def collection_finder