Add sync schema contract guardrails
This commit is contained in:
parent
f18c37401e
commit
5126e94c26
|
|
@ -1,6 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TournamentSnapshotBuilder
|
||||
ROOT_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(Tournament).map(&:to_sym).freeze
|
||||
TEAM_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(Team).map(&:to_sym).freeze
|
||||
STAGE_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(Stage).map(&:to_sym).freeze
|
||||
GROUP_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(Group).map(&:to_sym).freeze
|
||||
MATCH_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(Match).map(&:to_sym).freeze
|
||||
MATCH_SCORE_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(MatchScore).map(&:to_sym).freeze
|
||||
GROUP_SCORE_ATTRIBUTES = TournamentSyncSchema.synced_columns_for(GroupScore).map(&:to_sym).freeze
|
||||
|
||||
def self.build(tournament)
|
||||
new(tournament).build
|
||||
end
|
||||
|
|
@ -10,24 +18,16 @@ class TournamentSnapshotBuilder
|
|||
end
|
||||
|
||||
def build
|
||||
{
|
||||
id: tournament.id,
|
||||
name: tournament.name,
|
||||
code: tournament.code,
|
||||
description: tournament.description,
|
||||
public: tournament.public,
|
||||
playoff_teams_amount: tournament.playoff_teams_amount,
|
||||
instant_finalists_amount: tournament.instant_finalists_amount,
|
||||
intermediate_round_participants_amount: tournament.intermediate_round_participants_amount,
|
||||
sync_generated_at: Time.current.iso8601(6),
|
||||
timer_end: tournament.timer_end&.iso8601,
|
||||
teams: teams_payload,
|
||||
stages: stages_payload,
|
||||
groups: groups_payload,
|
||||
matches: matches_payload,
|
||||
match_scores: match_scores_payload,
|
||||
group_scores: group_scores_payload
|
||||
}
|
||||
payload = tournament.attributes.symbolize_keys.slice(*ROOT_ATTRIBUTES)
|
||||
payload[:timer_end] = tournament.timer_end&.iso8601
|
||||
payload[:sync_generated_at] = Time.current.iso8601(6)
|
||||
payload[:teams] = teams_payload
|
||||
payload[:stages] = stages_payload
|
||||
payload[:groups] = groups_payload
|
||||
payload[:matches] = matches_payload
|
||||
payload[:match_scores] = match_scores_payload
|
||||
payload[:group_scores] = group_scores_payload
|
||||
payload
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -36,69 +36,37 @@ class TournamentSnapshotBuilder
|
|||
|
||||
def teams_payload
|
||||
teams_for_snapshot.sort_by(&:id).map do |team|
|
||||
{
|
||||
id: team.id,
|
||||
name: team.name
|
||||
}
|
||||
team.attributes.symbolize_keys.slice(*TEAM_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
|
||||
def stages_payload
|
||||
tournament.stages.order(:id).map do |stage|
|
||||
{
|
||||
id: stage.id,
|
||||
level: stage.level,
|
||||
state: stage.state
|
||||
}
|
||||
stage.attributes.symbolize_keys.slice(*STAGE_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
|
||||
def groups_payload
|
||||
tournament.stages.flat_map(&:groups).sort_by(&:id).map do |group|
|
||||
{
|
||||
id: group.id,
|
||||
stage_id: group.stage_id,
|
||||
number: group.number
|
||||
}
|
||||
group.attributes.symbolize_keys.slice(*GROUP_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
|
||||
def matches_payload
|
||||
tournament.matches.sort_by(&:id).map do |match|
|
||||
{
|
||||
id: match.id,
|
||||
stage_id: match.stage&.id,
|
||||
group_id: match.group&.id,
|
||||
state: match.state,
|
||||
position: match.position,
|
||||
decider: match.decider
|
||||
}
|
||||
match.attributes.symbolize_keys.slice(*MATCH_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
|
||||
def match_scores_payload
|
||||
tournament.matches.flat_map(&:match_scores).sort_by(&:id).map do |match_score|
|
||||
{
|
||||
id: match_score.id,
|
||||
match_id: match_score.match_id,
|
||||
team_id: match_score.team_id,
|
||||
points: match_score.points,
|
||||
hidden_points: match_score.hidden_points
|
||||
}
|
||||
match_score.attributes.symbolize_keys.slice(*MATCH_SCORE_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
|
||||
def group_scores_payload
|
||||
tournament.stages.flat_map(&:groups).flat_map(&:group_scores).sort_by(&:id).map do |group_score|
|
||||
{
|
||||
id: group_score.id,
|
||||
group_id: group_score.group_id,
|
||||
team_id: group_score.team_id,
|
||||
group_points: group_score.group_points,
|
||||
scored_points: group_score.scored_points,
|
||||
received_points: group_score.received_points,
|
||||
position: group_score.position
|
||||
}
|
||||
group_score.attributes.symbolize_keys.slice(*GROUP_SCORE_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TournamentSyncSchema
|
||||
MODEL_COLUMNS = {
|
||||
Tournament => {
|
||||
synced: %w[
|
||||
id
|
||||
name
|
||||
code
|
||||
description
|
||||
public
|
||||
playoff_teams_amount
|
||||
instant_finalists_amount
|
||||
intermediate_round_participants_amount
|
||||
timer_end
|
||||
].freeze,
|
||||
ignored: %w[
|
||||
user_id
|
||||
created_at
|
||||
updated_at
|
||||
read_only_mode
|
||||
sync_target_url
|
||||
sync_auth_token
|
||||
sync_source_tournament_id
|
||||
sync_last_pushed_at
|
||||
sync_last_push_error
|
||||
sync_last_imported_snapshot_at
|
||||
].freeze
|
||||
}.freeze,
|
||||
Team => {
|
||||
synced: %w[id name].freeze,
|
||||
ignored: %w[tournament_id created_at updated_at sync_source_id].freeze
|
||||
}.freeze,
|
||||
Stage => {
|
||||
synced: %w[id level state].freeze,
|
||||
ignored: %w[tournament_id created_at updated_at sync_source_id].freeze
|
||||
}.freeze,
|
||||
Group => {
|
||||
synced: %w[id number stage_id].freeze,
|
||||
ignored: %w[created_at updated_at sync_source_id].freeze
|
||||
}.freeze,
|
||||
Match => {
|
||||
synced: %w[id state position stage_id group_id decider].freeze,
|
||||
ignored: %w[created_at updated_at sync_source_id].freeze
|
||||
}.freeze,
|
||||
MatchScore => {
|
||||
synced: %w[id points match_id team_id hidden_points].freeze,
|
||||
ignored: %w[created_at updated_at sync_source_id].freeze
|
||||
}.freeze,
|
||||
GroupScore => {
|
||||
synced: %w[id group_points scored_points received_points team_id group_id position].freeze,
|
||||
ignored: %w[created_at updated_at sync_source_id].freeze
|
||||
}.freeze
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
def synced_columns_for(model_class)
|
||||
MODEL_COLUMNS.fetch(model_class).fetch(:synced)
|
||||
end
|
||||
|
||||
def ignored_columns_for(model_class)
|
||||
MODEL_COLUMNS.fetch(model_class).fetch(:ignored)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TournamentSyncSchema do
|
||||
describe 'column coverage' do
|
||||
described_class::MODEL_COLUMNS.each_key do |model_class|
|
||||
it "accounts for every #{model_class.name} column in sync contract" do
|
||||
synced_columns = described_class.synced_columns_for(model_class)
|
||||
ignored_columns = described_class.ignored_columns_for(model_class)
|
||||
accounted_columns = synced_columns + ignored_columns
|
||||
|
||||
expect(accounted_columns.uniq).to match_array(model_class.column_names)
|
||||
expect(synced_columns & ignored_columns).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue