From 6efc1ccb0a37fc9fb94092749d8f737d0df9eb0f Mon Sep 17 00:00:00 2001 From: Thea Choem <29684683+theachoem@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:45:07 +0700 Subject: [PATCH] close #1991 add virtual waiting room --- Gemfile.lock | 20 +++++++ .../waiting_room_authorization.rb | 27 ++++++++++ .../waiting_room_sessions_controller.rb | 23 ++++++++ .../server_instances_bulk_creator.rb | 33 ++++++++++++ .../waiting_room_session_creator.rb | 44 ++++++++++++++++ .../waiting_room_sessions_releaser.rb | 34 ++++++++++++ .../server_instances_bulk_creator_job.rb | 10 ++++ .../waiting_room_sessions_releaser_job.rb | 10 ++++ .../spree_cm_commissioner/server_instance.rb | 12 +++++ .../waiting_room_session.rb | 13 +++++ ...9032456_create_cm_waiting_room_sessions.rb | 14 +++++ ...241119090545_create_cm_server_instances.rb | 15 ++++++ lib/tasks/server_instances_bulk_creator.rake | 7 +++ ...e_active_servers_inactive_all_services.yml | 52 +++++++++++++++++++ .../server_instances_bulk_creator_spec.rb | 35 +++++++++++++ .../waiting_room_session_creator_spec.rb | 14 +++++ .../server_instance_spec.rb | 5 ++ .../waiting_room_session_spec.rb | 5 ++ spree_cm_commissioner.gemspec | 2 + 19 files changed, 375 insertions(+) create mode 100644 app/controllers/concerns/spree_cm_commissioner/waiting_room_authorization.rb create mode 100644 app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb create mode 100644 app/interactors/spree_cm_commissioner/server_instances_bulk_creator.rb create mode 100644 app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb create mode 100644 app/interactors/spree_cm_commissioner/waiting_room_sessions_releaser.rb create mode 100644 app/jobs/spree_cm_commissioner/server_instances_bulk_creator_job.rb create mode 100644 app/jobs/spree_cm_commissioner/waiting_room_sessions_releaser_job.rb create mode 100644 app/models/spree_cm_commissioner/server_instance.rb create mode 100644 app/models/spree_cm_commissioner/waiting_room_session.rb create mode 100644 db/migrate/20241119032456_create_cm_waiting_room_sessions.rb create mode 100644 db/migrate/20241119090545_create_cm_server_instances.rb create mode 100644 lib/tasks/server_instances_bulk_creator.rake create mode 100644 spec/cassettes/SpreeCmCommissioner_ServerInstancesBulkCreator/_call/request_all_tasks_create_active_servers_inactive_all_services.yml create mode 100644 spec/interactors/spree_cm_commissioner/server_instances_bulk_creator_spec.rb create mode 100644 spec/interactors/spree_cm_commissioner/waiting_room_session_creator_spec.rb create mode 100644 spec/models/spree_cm_commissioner/server_instance_spec.rb create mode 100644 spec/models/spree_cm_commissioner/waiting_room_session_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 559e877f8..6963c1175 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,12 +37,14 @@ PATH spree_cm_commissioner (0.0.1) activerecord_json_validator (~> 2.1, >= 2.1.3) aws-sdk-cloudfront + aws-sdk-ecs aws-sdk-s3 byebug dry-validation (~> 1.10) elasticsearch (~> 8.5) exception_notification font-awesome-sass (~> 6.4.0) + google-cloud-firestore google-cloud-recaptcha_enterprise googleauth interactor (~> 3.1) @@ -190,6 +192,9 @@ GEM aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) + aws-sdk-ecs (1.147.0) + aws-sdk-core (~> 3, >= 3.193.0) + aws-sigv4 (~> 1.1) aws-sdk-kms (1.72.0) aws-sdk-core (~> 3, >= 3.184.0) aws-sigv4 (~> 1.1) @@ -205,6 +210,7 @@ GEM execjs (~> 2.0) base64 (0.1.1) bcrypt (3.1.19) + bigdecimal (3.1.8) bootstrap (4.6.2) autoprefixer-rails (>= 9.1.0) popper_js (>= 1.16.1, < 2) @@ -382,6 +388,19 @@ GEM google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) google-cloud-errors (1.3.1) + google-cloud-firestore (2.16.0) + bigdecimal (~> 3.0) + concurrent-ruby (~> 1.0) + google-cloud-core (~> 1.5) + google-cloud-firestore-v1 (>= 0.10, < 2.a) + rbtree (~> 0.4.2) + google-cloud-firestore-v1 (0.10.0) + gapic-common (>= 0.17.1, < 2.a) + google-cloud-errors (~> 1.0) + google-cloud-location (>= 0.4, < 2.a) + google-cloud-location (0.6.0) + gapic-common (>= 0.20.0, < 2.a) + google-cloud-errors (~> 1.0) google-cloud-recaptcha_enterprise (1.3.0) google-cloud-core (~> 1.6) google-cloud-recaptcha_enterprise-v1 (>= 0.0, < 2.a) @@ -605,6 +624,7 @@ GEM activerecord (>= 6.0.4) activesupport (>= 6.0.4) i18n + rbtree (0.4.6) redis (5.0.7) redis-client (>= 0.9.0) redis-client (0.17.0) diff --git a/app/controllers/concerns/spree_cm_commissioner/waiting_room_authorization.rb b/app/controllers/concerns/spree_cm_commissioner/waiting_room_authorization.rb new file mode 100644 index 000000000..b1298c04a --- /dev/null +++ b/app/controllers/concerns/spree_cm_commissioner/waiting_room_authorization.rb @@ -0,0 +1,27 @@ +module SpreeCmCommissioner + module WaitingRoomAuthorization + extend ActiveSupport::Concern + + included do + rescue_from JWT::ExpiredSignature, with: :handle_waiting_room_session_token_error + rescue_from JWT::VerificationError, with: :handle_waiting_room_session_token_error + rescue_from JWT::DecodeError, with: :handle_waiting_room_session_token_error + + before_action :ensure_waiting_room_session_token + end + + def ensure_waiting_room_session_token + return if disabled_authorization? + + JWT.decode(params[:waiting_room_session_token], ENV.fetch('WAITING_ROOM_SIGNATURE'), true, { algorithm: 'HS256' }) + end + + def disabled_authorization? + ENV['DISABLE_WAITING_ROOM_AUTHORIZATION'] == 'yes' + end + + def handle_waiting_room_session_token_error + render_error_payload(exception.message, 400) + end + end +end diff --git a/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb b/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb new file mode 100644 index 000000000..8515bdfc6 --- /dev/null +++ b/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb @@ -0,0 +1,23 @@ +# We use lambda instead of following API to generate token. Following are just sample. +# +# POST: /api/v2/storefront/waiting_room_sesions +# Response: { token: token } +module Spree + module Api + module V2 + module Storefront + class WaitingRoomSessionsController < ::Spree::Api::V2::ResourceController + skip_before_action :ensure_waiting_room_session_token + + def create + context = SpreeCmCommissioner::WaitingRoomSessionCreator.call( + remote_ip: request.remote_ip, + firebase_document_id: params[:firebase_document_id] + ) + render json: { token: context.token } + end + end + end + end + end +end diff --git a/app/interactors/spree_cm_commissioner/server_instances_bulk_creator.rb b/app/interactors/spree_cm_commissioner/server_instances_bulk_creator.rb new file mode 100644 index 000000000..964c655b6 --- /dev/null +++ b/app/interactors/spree_cm_commissioner/server_instances_bulk_creator.rb @@ -0,0 +1,33 @@ +require 'aws-sdk-ecs' + +# TODO: UI to check all instances, max sessions, active_sessions, and available slots +module SpreeCmCommissioner + class ServerInstancesBulkCreator < BaseInteractor + def call + ecs_client = Aws::ECS::Client.new( + region: ENV.fetch('AWS_REGION'), + access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'), + secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY') + ) + + tasks_response = ecs_client.list_tasks( + cluster: ENV.fetch('AWS_CLUSTER_NAME'), + launch_type: 'FARGATE' + ) + + context.servers = [] + context.task_arns = tasks_response.task_arns + context.task_arns.each do |task_arn| + server = SpreeCmCommissioner::ServerInstance.where(instance_id: task_arn).first_or_initialize + server.concurrent_requests ||= 5 + server.status = :active + server.save! if server.changed? + context.servers << server + end + + SpreeCmCommissioner::ServerInstance.where.not(id: context.servers.pluck(:id)).find_each do |server| + server.update!(status: :inactive) + end + end + end +end diff --git a/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb b/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb new file mode 100644 index 000000000..0c736a7e6 --- /dev/null +++ b/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb @@ -0,0 +1,44 @@ +require 'google/cloud/firestore' + +module SpreeCmCommissioner + class WaitingRoomSessionCreator < BaseInteractor + delegate :remote_ip, :firebase_document_id, to: :context + + def call + return context.fail!(message: 'must_provide_firebase_document_id') if firebase_document_id.blank? + return context.fail!(message: 'must_provide_remote_ip') if remote_ip.blank? + + generate_jwt_token + assign_token_and_create_session_to_db + log_to_firebase + end + + def generate_jwt_token + payload = { exp: expired_at.to_i } + context.jwt_token = JWT.encode(payload, ENV.fetch('WAITING_ROOM_SIGNATURE'), 'HS256') + end + + def assign_token_and_create_session_to_db + context.room_sesion = SpreeCmCommissioner::WaitingRoomSession.create!(token: context.jwt_token, expired_at: expired_at, remote_ip: remote_ip) + end + + def log_to_firebase + firestore = Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account) + document = firestore.col('waiting_guests').doc(firebase_document_id) + + data = document.get.data.dup + data[:entered_room_at] = Time.zone.now + + document.update(data) + end + + def expired_at + expired_duration = ENV['WAITING_ROOM_SESSION_EXPIRE_DURATION_IN_SECOND']&.presence&.to_i || (60 * 5) + context.expired_at ||= expired_duration.seconds.from_now + end + + def service_account + Rails.application.credentials.cloud_firestore_service_account + end + end +end diff --git a/app/interactors/spree_cm_commissioner/waiting_room_sessions_releaser.rb b/app/interactors/spree_cm_commissioner/waiting_room_sessions_releaser.rb new file mode 100644 index 000000000..9d7c904f2 --- /dev/null +++ b/app/interactors/spree_cm_commissioner/waiting_room_sessions_releaser.rb @@ -0,0 +1,34 @@ +require 'google/cloud/firestore' + +module SpreeCmCommissioner + class WaitingRoomSessionsReleaser < BaseInteractor + def call + context.max_sessions = SpreeCmCommissioner::ServerInstance.max_sessions + context.active_sessions = SpreeCmCommissioner::WaitingRoomSession.active.count + + available_slots = context.max_sessions - context.active_sessions + waiting_guests = query_waiting_guests(available_slots) + + waiting_guests.each do |document| + data = document.get.data.dup + data[:allow_to_refresh_to_enter_room_at] = Time.utc.now + document.update(data) + end + end + + # This query required index. create them in Firebase beforehand. + # Client side must create waiting_guests document with :queued_at & :allow_to_refresh_to_enter_room_at to null to allow fillter & order. + def query_waiting_guests(available_slots) + firestore = Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account) + firestore.col('waiting_guests') + .where('allow_to_refresh_to_enter_room_at', '==', nil) + .order('queued_at') + .limit(available_slots) + .get + end + + def service_account + Rails.application.credentials.cloud_firestore_service_account + end + end +end diff --git a/app/jobs/spree_cm_commissioner/server_instances_bulk_creator_job.rb b/app/jobs/spree_cm_commissioner/server_instances_bulk_creator_job.rb new file mode 100644 index 000000000..38d73a5f6 --- /dev/null +++ b/app/jobs/spree_cm_commissioner/server_instances_bulk_creator_job.rb @@ -0,0 +1,10 @@ +# server_instances_bulk_creator: +# cron: cron: "*/30 * * * *" # Every 30 minute +# class: SpreeCmCommissioner::ServerInstanceBulkCreatorJob +module SpreeCmCommissioner + class ServerInstancesBulkCreatorJob < ApplicationJob + def perform + SpreeCmCommissioner::ServerInstancesBulkCreator.call + end + end +end diff --git a/app/jobs/spree_cm_commissioner/waiting_room_sessions_releaser_job.rb b/app/jobs/spree_cm_commissioner/waiting_room_sessions_releaser_job.rb new file mode 100644 index 000000000..22f3cfcff --- /dev/null +++ b/app/jobs/spree_cm_commissioner/waiting_room_sessions_releaser_job.rb @@ -0,0 +1,10 @@ +# waiting_room_sessions_releaser: +# cron: cron: "*/1 * * * *" # Every minute +# class: SpreeCmCommissioner::WaitingRoomSessionsReleaserJob +module SpreeCmCommissioner + class WaitingRoomSessionsReleaserJob < ApplicationJob + def perform + SpreeCmCommissioner::WaitingRoomSessionsReleaser.call + end + end +end diff --git a/app/models/spree_cm_commissioner/server_instance.rb b/app/models/spree_cm_commissioner/server_instance.rb new file mode 100644 index 000000000..b5820124d --- /dev/null +++ b/app/models/spree_cm_commissioner/server_instance.rb @@ -0,0 +1,12 @@ +module SpreeCmCommissioner + class ServerInstance < Base + validates :instance_id, presence: true + validates :concurrent_requests, presence: true + + enum status: { active: 0, inactive: 1 } + + def max_sessions + ServerInstance.active.select('SUM(concurrent_requests * multiply_by) AS total').take.total + end + end +end diff --git a/app/models/spree_cm_commissioner/waiting_room_session.rb b/app/models/spree_cm_commissioner/waiting_room_session.rb new file mode 100644 index 000000000..0e5983926 --- /dev/null +++ b/app/models/spree_cm_commissioner/waiting_room_session.rb @@ -0,0 +1,13 @@ +module SpreeCmCommissioner + class WaitingRoomSession < Base + validates :token, presence: true + validates :expired_at, presence: true + validates :ip, presence: true + + scope :active, -> { SpreeCmCommissioner::WaitingRoomSession.where('expired_at > ?', Time.current) } + + def expired? + expired_at < Time.current + end + end +end diff --git a/db/migrate/20241119032456_create_cm_waiting_room_sessions.rb b/db/migrate/20241119032456_create_cm_waiting_room_sessions.rb new file mode 100644 index 000000000..a248453fc --- /dev/null +++ b/db/migrate/20241119032456_create_cm_waiting_room_sessions.rb @@ -0,0 +1,14 @@ +class CreateCmWaitingRoomSessions < ActiveRecord::Migration[7.0] + def change + create_table :cm_waiting_room_sessions, if_not_exists: true do |t| + t.string :token, null: false + t.string :remote_ip + t.datetime :expired_at, null: false + + t.timestamps + end + + add_index :cm_waiting_room_sessions, :expired_at, if_not_exists: true + add_index :cm_waiting_room_sessions, :token, if_not_exists: true + end +end diff --git a/db/migrate/20241119090545_create_cm_server_instances.rb b/db/migrate/20241119090545_create_cm_server_instances.rb new file mode 100644 index 000000000..b01ee6473 --- /dev/null +++ b/db/migrate/20241119090545_create_cm_server_instances.rb @@ -0,0 +1,15 @@ +class CreateCmServerInstances < ActiveRecord::Migration[7.0] + def change + create_table :cm_server_instances, if_not_exists: true do |t| + t.string :instance_id, null: false + t.integer :concurrent_requests, default: 5, null: false + t.integer :multiply_by, default: 1, null: false + t.integer :status, default: 0, null: false + + t.timestamps + end + + add_index :cm_server_instances, :instance_id, if_not_exists: true + add_index :cm_server_instances, :status, if_not_exists: true + end +end diff --git a/lib/tasks/server_instances_bulk_creator.rake b/lib/tasks/server_instances_bulk_creator.rake new file mode 100644 index 000000000..461b2be5d --- /dev/null +++ b/lib/tasks/server_instances_bulk_creator.rake @@ -0,0 +1,7 @@ +# recommend to be used in schedule.yml & manually access in /sidekiq/cron +namespace :spree_cm_commissioner do + desc 'Create services instances by fetch from AWS' + task server_instances_bulk_creator: :environment do + ServerInstancesBulkCreatorJob.perform_now + end +end diff --git a/spec/cassettes/SpreeCmCommissioner_ServerInstancesBulkCreator/_call/request_all_tasks_create_active_servers_inactive_all_services.yml b/spec/cassettes/SpreeCmCommissioner_ServerInstancesBulkCreator/_call/request_all_tasks_create_active_servers_inactive_all_services.yml new file mode 100644 index 000000000..80178ec06 --- /dev/null +++ b/spec/cassettes/SpreeCmCommissioner_ServerInstancesBulkCreator/_call/request_all_tasks_create_active_servers_inactive_all_services.yml @@ -0,0 +1,52 @@ +--- +http_interactions: +- request: + method: post + uri: https://ecs.ap-southeast-1.amazonaws.com/ + body: + encoding: UTF-8 + string: '{"cluster":"production-cm-web","launchType":"FARGATE"}' + headers: + Accept-Encoding: + - '' + Amz-Sdk-Invocation-Id: + - 93364359-a523-4894-8280-d5e84cf5ebc9 + Content-Type: + - application/x-amz-json-1.1 + X-Amz-Target: + - AmazonEC2ContainerServiceV20141113.ListTasks + User-Agent: + - aws-sdk-ruby3/3.196.1 ua/2.0 api/ecs#1.147.0 os/macos#23 md/arm64 lang/ruby#3.2.0 + md/3.2.0 cfg/retry-mode#legacy + Host: + - ecs.ap-southeast-1.amazonaws.com + X-Amz-Date: + - 20241119T133613Z + X-Amz-Content-Sha256: + - 3cab7d2858b9deaec4534d74e168e53aeda79894ec435dd2da69a1ed3a206ad5 + Authorization: + - AWS4-HMAC-SHA256 Credential=AxxxZIxxxxxxxxxxxx5xxxPUxxPU41119/ap-southeast-1/ecs/aws4_request, + SignedHeaders=amz-sdk-invocation-id;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target, + Signature=e4c2e44d5e179ac9838a2f3d6eeeff91fb94f28a4922e9f815199a1c36d1c8b9 + Content-Length: + - '54' + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + X-Amzn-Requestid: + - 30d09591-f00c-46a4-9d86-323a3c97b665 + Content-Type: + - application/x-amz-json-1.1 + Content-Length: + - '504' + Date: + - Tue, 19 Nov 2024 13:36:13 GMT + body: + encoding: UTF-8 + string: '{"taskArns":["arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/12640ba339c64b7bb4d463a92799815f","arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/4a571be9e5a44fc0875b0197288db3b3","arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/9ea7f8530e614aee899ed5bff489b6c8","arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/b8cd377ad58f480cb21633549c52d64a","arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/fddb2a41dc6f4938b1dec2ebd0c58354"]}' + recorded_at: Tue, 19 Nov 2024 13:36:13 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/interactors/spree_cm_commissioner/server_instances_bulk_creator_spec.rb b/spec/interactors/spree_cm_commissioner/server_instances_bulk_creator_spec.rb new file mode 100644 index 000000000..112ad8d7c --- /dev/null +++ b/spec/interactors/spree_cm_commissioner/server_instances_bulk_creator_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +RSpec.describe SpreeCmCommissioner::ServerInstancesBulkCreator do + describe '.call', :vcr do + let!(:old_service1) { SpreeCmCommissioner::ServerInstance.create(instance_id: 'old-server-1', status: :active) } + let!(:old_service2) { SpreeCmCommissioner::ServerInstance.create(instance_id: 'old-server-2', status: :active) } + + before do + ENV['AWS_REGION'] = 'ap-southeast-1' + ENV['AWS_ACCESS_KEY_ID'] = 'AxxxZIxxxxxxxxxxxx5xxxPU' + ENV['AWS_SECRET_ACCESS_KEY'] = 'uvc01M+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxN' + ENV['AWS_CLUSTER_NAME'] = 'production-cm-web' + end + + it 'request all tasks & create active servers & inactive all services' do + expect(SpreeCmCommissioner::ServerInstance.active.size).to eq 2 + + context = described_class.call + + # it inactive previous 2 & add more active + expect(SpreeCmCommissioner::ServerInstance.active.size).to eq 5 + expect(context.task_arns.size).to eq 5 + expect(context.task_arns).to eq [ + "arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/12640ba339c64b7bb4d463a92799815f", + "arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/4a571be9e5a44fc0875b0197288db3b3", + "arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/9ea7f8530e614aee899ed5bff489b6c8", + "arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/b8cd377ad58f480cb21633549c52d64a", + "arn:aws:ecs:ap-southeast-1:636758493619:task/production-cm-web/fddb2a41dc6f4938b1dec2ebd0c58354" + ] + + expect(old_service1.inactive?).to be false + expect(old_service2.inactive?).to be false + end + end +end diff --git a/spec/interactors/spree_cm_commissioner/waiting_room_session_creator_spec.rb b/spec/interactors/spree_cm_commissioner/waiting_room_session_creator_spec.rb new file mode 100644 index 000000000..72e80c09b --- /dev/null +++ b/spec/interactors/spree_cm_commissioner/waiting_room_session_creator_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +RSpec.describe SpreeCmCommissioner::WaitingRoomSessionCreator do + describe '.call', :vcr do + it 'generate jwt token, record session to db & log it in user firebase document' do + ENV['WAITING_ROOM_SIGNATURE'] = '4234f0a3-c42f-40b5-9057-7c006793cea4' + allow_any_instance_of(described_class).to receive(:log_to_firebase) + + context = described_class.call(ip: '43.230.192.190', firebase_document_id: 'FYFFEj0O4QSpipNsHzRh') + expect(context.room_sesion.token.present?).to be true + expect(context.room_sesion.token).to eq context.jwt_token + end + end +end diff --git a/spec/models/spree_cm_commissioner/server_instance_spec.rb b/spec/models/spree_cm_commissioner/server_instance_spec.rb new file mode 100644 index 000000000..fe938c53d --- /dev/null +++ b/spec/models/spree_cm_commissioner/server_instance_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe SpreeCmCommissioner::ServerInstance, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/spree_cm_commissioner/waiting_room_session_spec.rb b/spec/models/spree_cm_commissioner/waiting_room_session_spec.rb new file mode 100644 index 000000000..88f978b99 --- /dev/null +++ b/spec/models/spree_cm_commissioner/waiting_room_session_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe SpreeCmCommissioner::WaitingRoomSession, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spree_cm_commissioner.gemspec b/spree_cm_commissioner.gemspec index 357718e22..cf3ac9bc7 100644 --- a/spree_cm_commissioner.gemspec +++ b/spree_cm_commissioner.gemspec @@ -29,6 +29,8 @@ Gem::Specification.new do |s| s.add_dependency 'spree_extension' s.add_dependency 'phonelib' + s.add_dependency 'aws-sdk-ecs' + s.add_dependency 'google-cloud-firestore' s.add_dependency 'google-cloud-recaptcha_enterprise' s.add_dependency 'jwt', '>= 2.5.0' s.add_dependency 'elasticsearch', '~> 8.5'