gw event improvements: status field, members_during_event endpoint
This commit is contained in:
parent
7d27d3c8b1
commit
26718b5a3e
9 changed files with 116 additions and 14 deletions
|
|
@ -3,7 +3,11 @@
|
||||||
module Api
|
module Api
|
||||||
module V1
|
module V1
|
||||||
class GwEventBlueprint < ApiBlueprint
|
class GwEventBlueprint < ApiBlueprint
|
||||||
fields :name, :element, :start_date, :end_date, :event_number
|
fields :start_date, :end_date, :event_number
|
||||||
|
|
||||||
|
field :element do |event|
|
||||||
|
GwEvent.elements[event.element]
|
||||||
|
end
|
||||||
|
|
||||||
field :status do |event|
|
field :status do |event|
|
||||||
if event.active?
|
if event.active?
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,39 @@ module Api
|
||||||
render json: CrewGwParticipationBlueprint.render(@participation, view: :with_individual_scores, root: :crew_gw_participation)
|
render json: CrewGwParticipationBlueprint.render(@participation, view: :with_individual_scores, root: :crew_gw_participation)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# GET /crew/gw_participations/by_event/:event_id
|
||||||
|
def by_event
|
||||||
|
# Support lookup by event_id (UUID) or event_number (integer)
|
||||||
|
event = if params[:event_id].match?(/\A\d+\z/)
|
||||||
|
GwEvent.find_by(event_number: params[:event_id])
|
||||||
|
else
|
||||||
|
GwEvent.find_by(id: params[:event_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
return render json: { gw_event: nil, crew_gw_participation: nil, members_during_event: [] } unless event
|
||||||
|
|
||||||
|
participation = @crew.crew_gw_participations
|
||||||
|
.includes(:gw_event, gw_individual_scores: [:crew_membership, :phantom_player])
|
||||||
|
.find_by(gw_event: event)
|
||||||
|
|
||||||
|
# Get all members who were active during the event (includes retired members who left after event started)
|
||||||
|
# Also include all currently active members for score entry purposes
|
||||||
|
# Uses joined_at (editable) for historical accuracy
|
||||||
|
members_during_event = @crew.crew_memberships
|
||||||
|
.includes(:user)
|
||||||
|
.active_during(event.start_date, event.end_date)
|
||||||
|
|
||||||
|
# Get all phantom players who were active during the event or currently active
|
||||||
|
phantom_players = @crew.phantom_players.active_during(event.start_date, event.end_date)
|
||||||
|
|
||||||
|
render json: {
|
||||||
|
gw_event: GwEventBlueprint.render_as_hash(event),
|
||||||
|
crew_gw_participation: participation ? CrewGwParticipationBlueprint.render_as_hash(participation, view: :with_individual_scores) : nil,
|
||||||
|
members_during_event: CrewMembershipBlueprint.render_as_hash(members_during_event, view: :with_user),
|
||||||
|
phantom_players: PhantomPlayerBlueprint.render_as_hash(phantom_players)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# POST /gw_events/:id/participations
|
# POST /gw_events/:id/participations
|
||||||
def create
|
def create
|
||||||
event = GwEvent.find(params[:id])
|
event = GwEvent.find(params[:id])
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ module Api
|
||||||
end
|
end
|
||||||
|
|
||||||
def event_params
|
def event_params
|
||||||
params.require(:gw_event).permit(:name, :element, :start_date, :end_date, :event_number)
|
params.require(:gw_event).permit(:element, :start_date, :end_date, :event_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_admin!
|
def require_admin!
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ class GwEvent < ApplicationRecord
|
||||||
|
|
||||||
enum :element, ELEMENTS
|
enum :element, ELEMENTS
|
||||||
|
|
||||||
validates :name, presence: true
|
|
||||||
validates :element, presence: true
|
validates :element, presence: true
|
||||||
validates :start_date, presence: true
|
validates :start_date, presence: true
|
||||||
validates :end_date, presence: true
|
validates :end_date, presence: true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ChangeGwEventsNameNullable < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
remove_column :gw_events, :name, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_12_04_075226) do
|
ActiveRecord::Schema[8.0].define(version: 2025_12_04_102935) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "btree_gin"
|
enable_extension "btree_gin"
|
||||||
enable_extension "pg_catalog.plpgsql"
|
enable_extension "pg_catalog.plpgsql"
|
||||||
|
|
@ -269,6 +269,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_04_075226) do
|
||||||
t.datetime "retired_at"
|
t.datetime "retired_at"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.datetime "joined_at"
|
||||||
t.index ["crew_id", "role"], name: "index_crew_memberships_on_crew_id_and_role"
|
t.index ["crew_id", "role"], name: "index_crew_memberships_on_crew_id_and_role"
|
||||||
t.index ["crew_id", "user_id"], name: "index_crew_memberships_on_crew_id_and_user_id", unique: true
|
t.index ["crew_id", "user_id"], name: "index_crew_memberships_on_crew_id_and_user_id", unique: true
|
||||||
t.index ["crew_id"], name: "index_crew_memberships_on_crew_id"
|
t.index ["crew_id"], name: "index_crew_memberships_on_crew_id"
|
||||||
|
|
@ -462,7 +463,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_04_075226) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "gw_events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
create_table "gw_events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||||
t.string "name", null: false
|
|
||||||
t.integer "element", null: false
|
t.integer "element", null: false
|
||||||
t.date "start_date", null: false
|
t.date "start_date", null: false
|
||||||
t.date "end_date", null: false
|
t.date "end_date", null: false
|
||||||
|
|
@ -664,6 +664,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_04_075226) do
|
||||||
t.boolean "claim_confirmed", default: false, null: false
|
t.boolean "claim_confirmed", default: false, null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.boolean "retired", default: false, null: false
|
||||||
|
t.datetime "retired_at"
|
||||||
|
t.datetime "joined_at"
|
||||||
t.index ["claimed_by_id"], name: "index_phantom_players_on_claimed_by_id"
|
t.index ["claimed_by_id"], name: "index_phantom_players_on_claimed_by_id"
|
||||||
t.index ["claimed_from_membership_id"], name: "index_phantom_players_on_claimed_from_membership_id"
|
t.index ["claimed_from_membership_id"], name: "index_phantom_players_on_claimed_from_membership_id"
|
||||||
t.index ["crew_id", "granblue_id"], name: "index_phantom_players_on_crew_id_and_granblue_id", unique: true, where: "(granblue_id IS NOT NULL)"
|
t.index ["crew_id", "granblue_id"], name: "index_phantom_players_on_crew_id_and_granblue_id", unique: true, where: "(granblue_id IS NOT NULL)"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :gw_event do
|
factory :gw_event do
|
||||||
sequence(:name) { |n| "Unite and Fight ##{n}" }
|
|
||||||
element { %i[Fire Water Earth Wind Light Dark].sample }
|
element { %i[Fire Water Earth Wind Light Dark].sample }
|
||||||
start_date { 1.week.from_now.to_date }
|
start_date { 1.week.from_now.to_date }
|
||||||
end_date { 2.weeks.from_now.to_date }
|
end_date { 2.weeks.from_now.to_date }
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,65 @@ RSpec.describe 'Api::V1::CrewGwParticipations', type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/crew/gw_participations/by_event/:event_id' do
|
||||||
|
context 'when participating in the event' do
|
||||||
|
let!(:participation) { create(:crew_gw_participation, crew: crew, gw_event: gw_event) }
|
||||||
|
|
||||||
|
it 'returns the event and participation by event ID' do
|
||||||
|
get "/api/v1/crew/gw_participations/by_event/#{gw_event.id}", headers: auth_headers
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json['gw_event']['id']).to eq(gw_event.id)
|
||||||
|
expect(json['crew_gw_participation']['id']).to eq(participation.id)
|
||||||
|
expect(json['members_during_event']).to be_an(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the event and participation by event number' do
|
||||||
|
get "/api/v1/crew/gw_participations/by_event/#{gw_event.event_number}", headers: auth_headers
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json['gw_event']['id']).to eq(gw_event.id)
|
||||||
|
expect(json['crew_gw_participation']['id']).to eq(participation.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes members who were active during the event' do
|
||||||
|
get "/api/v1/crew/gw_participations/by_event/#{gw_event.id}", headers: auth_headers
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
member_ids = json['members_during_event'].map { |m| m['id'] }
|
||||||
|
expect(member_ids).to include(membership.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not participating in the event' do
|
||||||
|
it 'returns event but null participation' do
|
||||||
|
get "/api/v1/crew/gw_participations/by_event/#{gw_event.id}", headers: auth_headers
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json['gw_event']['id']).to eq(gw_event.id)
|
||||||
|
expect(json['crew_gw_participation']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when event does not exist' do
|
||||||
|
it 'returns null for both' do
|
||||||
|
get '/api/v1/crew/gw_participations/by_event/99999', headers: auth_headers
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json['gw_event']).to be_nil
|
||||||
|
expect(json['crew_gw_participation']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without a crew' do
|
||||||
|
let!(:membership) { nil }
|
||||||
|
|
||||||
|
it 'returns unprocessable_entity' do
|
||||||
|
get "/api/v1/crew/gw_participations/by_event/#{gw_event.id}", headers: auth_headers
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'PUT /api/v1/crew/gw_participations/:id' do
|
describe 'PUT /api/v1/crew/gw_participations/:id' do
|
||||||
let!(:participation) { create(:crew_gw_participation, crew: crew, gw_event: gw_event) }
|
let!(:participation) { create(:crew_gw_participation, crew: crew, gw_event: gw_event) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,7 @@ RSpec.describe 'Api::V1::GwEvents', type: :request do
|
||||||
get "/api/v1/gw_events/#{event.id}"
|
get "/api/v1/gw_events/#{event.id}"
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json_response['gw_event']['id']).to eq(event.id)
|
expect(json_response['gw_event']['id']).to eq(event.id)
|
||||||
expect(json_response['gw_event']['name']).to eq(event.name)
|
expect(json_response['gw_event']['element']).to eq(GwEvent.elements[event.element])
|
||||||
expect(json_response['gw_event']['element']).to eq(event.element)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 404 for non-existent event' do
|
it 'returns 404 for non-existent event' do
|
||||||
|
|
@ -45,7 +44,6 @@ RSpec.describe 'Api::V1::GwEvents', type: :request do
|
||||||
let(:valid_params) do
|
let(:valid_params) do
|
||||||
{
|
{
|
||||||
gw_event: {
|
gw_event: {
|
||||||
name: 'Unite and Fight #50',
|
|
||||||
element: 'Fire',
|
element: 'Fire',
|
||||||
start_date: 1.week.from_now.to_date,
|
start_date: 1.week.from_now.to_date,
|
||||||
end_date: 2.weeks.from_now.to_date,
|
end_date: 2.weeks.from_now.to_date,
|
||||||
|
|
@ -61,12 +59,12 @@ RSpec.describe 'Api::V1::GwEvents', type: :request do
|
||||||
}.to change(GwEvent, :count).by(1)
|
}.to change(GwEvent, :count).by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
expect(json_response['gw_event']['name']).to eq('Unite and Fight #50')
|
expect(json_response['gw_event']['element']).to eq(GwEvent.elements['Fire'])
|
||||||
expect(json_response['gw_event']['element']).to eq('Fire')
|
expect(json_response['gw_event']['event_number']).to eq(50)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns errors for invalid params' do
|
it 'returns errors for invalid params' do
|
||||||
post '/api/v1/gw_events', params: { gw_event: { name: '' } }, headers: admin_headers
|
post '/api/v1/gw_events', params: { gw_event: { element: '' } }, headers: admin_headers
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -88,13 +86,13 @@ RSpec.describe 'Api::V1::GwEvents', type: :request do
|
||||||
|
|
||||||
describe 'PUT /api/v1/gw_events/:id' do
|
describe 'PUT /api/v1/gw_events/:id' do
|
||||||
let!(:event) { create(:gw_event) }
|
let!(:event) { create(:gw_event) }
|
||||||
let(:update_params) { { gw_event: { name: 'Updated Event Name' } } }
|
let(:update_params) { { gw_event: { event_number: 99 } } }
|
||||||
|
|
||||||
context 'as admin' do
|
context 'as admin' do
|
||||||
it 'updates the event' do
|
it 'updates the event' do
|
||||||
put "/api/v1/gw_events/#{event.id}", params: update_params, headers: admin_headers
|
put "/api/v1/gw_events/#{event.id}", params: update_params, headers: admin_headers
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json_response['gw_event']['name']).to eq('Updated Event Name')
|
expect(json_response['gw_event']['event_number']).to eq(99)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue