Merge branch 'timer' into 'master'
Timer See merge request turniere/turniere-backend!30
This commit is contained in:
commit
f71c7ec57f
|
|
@ -328,6 +328,7 @@ PLATFORMS
|
||||||
aarch64-linux-musl
|
aarch64-linux-musl
|
||||||
arm64-darwin-22
|
arm64-darwin-22
|
||||||
arm64-darwin-23
|
arm64-darwin-23
|
||||||
|
arm64-darwin-24
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class TournamentsController < ApplicationController
|
class TournamentsController < ApplicationController
|
||||||
before_action :set_tournament, only: %i[show update destroy]
|
before_action :set_tournament, only: %i[show update destroy set_timer_end timer_end]
|
||||||
before_action :authenticate_user!, only: %i[create update destroy]
|
before_action :authenticate_user!, only: %i[create update destroy set_timer_end]
|
||||||
before_action -> { require_owner! @tournament.owner }, only: %i[update destroy]
|
before_action -> { require_owner! @tournament.owner }, only: %i[update destroy set_timer_end]
|
||||||
before_action :validate_create_params, only: %i[create]
|
before_action :validate_create_params, only: %i[create]
|
||||||
before_action :validate_update_params, only: %i[update]
|
before_action :validate_update_params, only: %i[update]
|
||||||
|
before_action :validate_set_timer_end_params, only: %i[set_timer_end]
|
||||||
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_error
|
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_error
|
||||||
|
|
||||||
# GET /tournaments
|
# GET /tournaments
|
||||||
|
|
@ -93,8 +94,27 @@ class TournamentsController < ApplicationController
|
||||||
@tournament.destroy
|
@tournament.destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /tournaments/:id/timer_end
|
||||||
|
def timer_end
|
||||||
|
render json: { timer_end: @tournament.timer_end }
|
||||||
|
end
|
||||||
|
|
||||||
|
# PATCH /tournaments/:id/set_timer_end
|
||||||
|
def set_timer_end
|
||||||
|
if @tournament.update(timer_end_params)
|
||||||
|
render json: @tournament
|
||||||
|
else
|
||||||
|
render json: @tournament.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def timer_end_params
|
||||||
|
{ timer_end: params[:timer_end] }
|
||||||
|
end
|
||||||
|
|
||||||
def organize_teams_in_groups(teams)
|
def organize_teams_in_groups(teams)
|
||||||
# each team gets put into a array of teams depending on the group specified in team[:group]
|
# each team gets put into a array of teams depending on the group specified in team[:group]
|
||||||
teams.group_by { |team| team['group'] }.values.map do |group|
|
teams.group_by { |team| team['group'] }.values.map do |group|
|
||||||
|
|
@ -158,3 +178,39 @@ class TournamentsController < ApplicationController
|
||||||
}, status: :unprocessable_entity
|
}, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_set_timer_end_params
|
||||||
|
timer_end = params[:timer_end]
|
||||||
|
timer_end_seconds = params[:timer_end_seconds]
|
||||||
|
|
||||||
|
# throw error if both timer_end and timer_end_seconds are present
|
||||||
|
if timer_end.present? && timer_end_seconds.present?
|
||||||
|
return render json: { error: 'Only one of timer_end or timer_end_seconds is allowed' }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
if timer_end_seconds.present?
|
||||||
|
begin
|
||||||
|
timer_end_seconds = Integer(timer_end_seconds)
|
||||||
|
rescue ArgumentError
|
||||||
|
return render json: { error: 'Invalid seconds format' }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
return render json: { error: 'Timer end must be in the future' }, status: :unprocessable_entity if timer_end_seconds <= 0
|
||||||
|
|
||||||
|
parsed_time = Time.zone.now + timer_end_seconds
|
||||||
|
params[:timer_end] = parsed_time
|
||||||
|
elsif timer_end.present?
|
||||||
|
begin
|
||||||
|
parsed_time = Time.zone.parse(timer_end)
|
||||||
|
if parsed_time.nil?
|
||||||
|
return render json: { error: 'Invalid datetime format' }, status: :unprocessable_entity
|
||||||
|
elsif !parsed_time.future?
|
||||||
|
return render json: { error: 'Timer end must be in the future' }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
return render json: { error: 'Invalid datetime format' }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return render json: { error: 'Timer end is required' }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class TournamentSerializer < SimpleTournamentSerializer
|
class TournamentSerializer < SimpleTournamentSerializer
|
||||||
attributes :description, :playoff_teams_amount,
|
attributes :description, :playoff_teams_amount,
|
||||||
:instant_finalists_amount, :intermediate_round_participants_amount
|
:instant_finalists_amount, :intermediate_round_participants_amount, :timer_end
|
||||||
has_many :stages
|
has_many :stages
|
||||||
has_many :teams
|
has_many :teams
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@ Rails.application.routes.draw do
|
||||||
resources :tournaments do
|
resources :tournaments do
|
||||||
resources :statistics, only: %i[index]
|
resources :statistics, only: %i[index]
|
||||||
resources :matches, only: %i[index]
|
resources :matches, only: %i[index]
|
||||||
|
member do
|
||||||
|
get :timer_end
|
||||||
|
patch :set_timer_end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
resources :match_scores, only: %i[show update]
|
resources :match_scores, only: %i[show update]
|
||||||
resources :groups, only: %i[show]
|
resources :groups, only: %i[show]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddTimerEndToTournaments < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :tournaments, :timer_end, :datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -100,7 +100,11 @@ RSpec.describe TournamentsController, type: :controller do
|
||||||
|
|
||||||
it 'returns the requested tournament' do
|
it 'returns the requested tournament' do
|
||||||
get :show, params: { id: @tournament.to_param }
|
get :show, params: { id: @tournament.to_param }
|
||||||
expect(deserialize_response(response)[:id].to_i).to eq(@tournament.id)
|
json = deserialize_response(response)
|
||||||
|
expect(json[:id].to_i).to eq(@tournament.id)
|
||||||
|
expected_keys = %i[id name code public description playoff_teams_amount instant_finalists_amount intermediate_round_participants_amount timer_end owner_username stages teams]
|
||||||
|
expect(json.keys).to match_array(expected_keys)
|
||||||
|
expect(json).to eq(TournamentSerializer.new(@tournament).as_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with simple=true parameter' do
|
context 'with simple=true parameter' do
|
||||||
|
|
@ -477,4 +481,96 @@ RSpec.describe TournamentsController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'PATCH #set_timer_end' do
|
||||||
|
before(:each) do
|
||||||
|
apply_authentication_headers_for @tournament.owner
|
||||||
|
@request.env['HTTP_ACCEPT'] = 'application/json'
|
||||||
|
end
|
||||||
|
context 'timer_end' do
|
||||||
|
context 'when timer_end is missing' do
|
||||||
|
it 'returns unprocessable entity' do
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id }
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(JSON.parse(response.body)).to include('error' => 'Timer end is required')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when timer_end is invalid datetime' do
|
||||||
|
it 'returns unprocessable entity' do
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end: 'invalid' }
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(JSON.parse(response.body)).to include('error' => 'Invalid datetime format')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when timer_end is in the past' do
|
||||||
|
it 'returns unprocessable entity' do
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end: 1.day.ago }
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(JSON.parse(response.body)).to include('error' => 'Timer end must be in the future')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when timer_end is valid' do
|
||||||
|
it 'updates the timer_end' do
|
||||||
|
valid_timer_end = 1.day.from_now.change(usec: 0)
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end: valid_timer_end }
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(@tournament.reload.timer_end).to eq(valid_timer_end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
context 'when timer_end_seconds is provided' do
|
||||||
|
it 'returns unprocessable entity for invalid seconds format' do
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end_seconds: 'invalid' }
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(JSON.parse(response.body)).to include('error' => 'Invalid seconds format')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unprocessable entity for negative seconds' do
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end_seconds: -3600 }
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(JSON.parse(response.body)).to include('error' => 'Timer end must be in the future')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the timer_end with valid seconds' do
|
||||||
|
valid_timer_end_seconds = 3600
|
||||||
|
expected_timer_end = (Time.zone.now + valid_timer_end_seconds).change(usec: 0)
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end_seconds: valid_timer_end_seconds }
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(@tournament.reload.timer_end.change(usec: 0)).to eq(expected_timer_end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
context 'when both timer_end and timer_end_seconds are provided' do
|
||||||
|
it 'returns unprocessable entity' do
|
||||||
|
patch :set_timer_end, params: { id: @tournament.id, timer_end: 1.day.from_now, timer_end_seconds: 3600 }
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(JSON.parse(response.body)).to include('error' => 'Only one of timer_end or timer_end_seconds is allowed')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #timer_end' do
|
||||||
|
before do
|
||||||
|
@request.env['HTTP_ACCEPT'] = 'application/json'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns success response' do
|
||||||
|
get :timer_end, params: { id: @tournament.to_param }
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns timer_end value' do
|
||||||
|
@tournament.update(timer_end: Time.zone.now + 1.day)
|
||||||
|
get :timer_end, params: { id: @tournament.to_param }
|
||||||
|
expect(JSON.parse(response.body)['timer_end']).to eq(@tournament.timer_end.as_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil if timer_end is not set' do
|
||||||
|
@tournament.update(timer_end: nil)
|
||||||
|
get :timer_end, params: { id: @tournament.to_param }
|
||||||
|
expect(JSON.parse(response.body)['timer_end']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ RSpec.configure do |config|
|
||||||
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
||||||
|
|
||||||
# Run only focused tests
|
# Run only focused tests
|
||||||
|
# TODO REVERT ME
|
||||||
config.filter_run_when_matching :focus
|
config.filter_run_when_matching :focus
|
||||||
|
|
||||||
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue