Skip to content

Commit

Permalink
Merge pull request #958 from bullet-train-co/jeremy/team-default-time…
Browse files Browse the repository at this point in the history
…-zone

`Team#time_zone` now defaults to UTC and is updated when we know the first user's time zone
  • Loading branch information
jagthedrummer authored Nov 21, 2024
2 parents 6715da2 + 989c3d7 commit edef27e
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 18 deletions.
2 changes: 2 additions & 0 deletions bullet_train/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ GEM
parallel (1.22.1)
parser (3.1.3.0)
ast (~> 2.4.1)
pg (1.5.9)
phonelib (0.8.4)
possessive (1.0.1)
premailer (1.21.0)
Expand Down Expand Up @@ -503,6 +504,7 @@ DEPENDENCIES
bullet_train-themes!
bullet_train-themes-light!
minitest-reporters
pg (~> 1.3)
pry
pry-stack_explorer
sprockets-rails
Expand Down
10 changes: 10 additions & 0 deletions bullet_train/app/models/concerns/memberships/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ module Memberships::Base

after_commit :publish_changed_quantity

after_create :set_team_time_zone, if: :is_first_membership?

after_validation :remove_user_profile_photo, if: :user_profile_photo_removal?

scope :excluding_platform_agents, -> { where(platform_agent_of: nil) }
Expand Down Expand Up @@ -157,5 +159,13 @@ def publish_changed_quantity
ActiveSupport::Notifications.instrument("memberships.quantity-changed", {team:})
end

def set_team_time_zone
team.set_time_zone_from_user(user)
end

def is_first_membership?
team.memberships.count == 1 && team.memberships.first.id == id
end

ActiveSupport.run_load_hooks :bullet_train_memberships_base, self
end
12 changes: 12 additions & 0 deletions bullet_train/app/models/concerns/teams/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ module Teams::Base
validates :time_zone, inclusion: {in: ActiveSupport::TimeZone.all.map(&:name)}, allow_nil: true
end

def initialize(attributes = nil)
super
self.time_zone = "UTC" if time_zone.blank?
end

def set_time_zone_from_user(user)
if time_zone.blank? || time_zone == "UTC"
self.time_zone = user.time_zone if user.time_zone.present?
save
end
end

def platform_agent_access_tokens
Platform::AccessToken.joins(:application).where(resource_owner_id: users.where.not(platform_agent_of_id: nil), application: {team: nil})
end
Expand Down
2 changes: 1 addition & 1 deletion bullet_train/app/models/concerns/users/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def developer?
end

def set_teams_time_zone
teams.where(time_zone: nil).each do |team|
teams.where(time_zone: [nil, "UTC"]).each do |team|
team.update(time_zone: time_zone) if team.users.count == 1
end
end
Expand Down
1 change: 1 addition & 0 deletions bullet_train/bullet_train.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Gem::Specification.new do |spec|
# We don't want to develop in a world where we don't have `binding.pry` or `object.pry` for debugging.
spec.add_development_dependency "pry"
spec.add_development_dependency "pry-stack_explorer"
spec.add_development_dependency "pg", "~> 1.3"

# Password strength.
spec.add_dependency "devise-pwned_password"
Expand Down
37 changes: 37 additions & 0 deletions bullet_train/config/models/roles.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
default:
models:
Team: read
Document: read
Membership:
- read
- search

crud_role:
models:
Team: crud

editor:
models:
Scaffolding::AbsolutelyAbstract::CreativeConcept:
- read
- update

manager:
includes:
- editor

supervisor:
includes:
- manager

admin:
includes:
- editor
manageable_roles:
- admin
- editor
models:
Team: manage
Membership: manage
Document: manage
Scaffolding::AbsolutelyAbstract::CreativeConcept: manage
21 changes: 4 additions & 17 deletions bullet_train/test/dummy/config/database.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem "sqlite3"
#
default: &default
adapter: sqlite3
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000

development:
<<: *default
database: db/development.sqlite3
database: bullet_train-core_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: db/test.sqlite3

production:
<<: *default
database: db/production.sqlite3
database: bullet_train-core_test
120 changes: 120 additions & 0 deletions bullet_train/test/dummy/db/schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2022_03_02_235728) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

create_table "documents", force: :cascade do |t|
t.bigint "membership_id", null: false
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["membership_id"], name: "index_documents_on_membership_id"
end

create_table "memberships", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.integer "team_id"
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.bigint "invitation_id"
t.string "user_first_name"
t.string "user_last_name"
t.string "user_profile_photo_id"
t.string "user_email"
t.bigint "added_by_id"
t.bigint "platform_agent_of_id"
t.jsonb "role_ids", default: []
t.boolean "platform_agent", default: false
t.index ["added_by_id"], name: "index_memberships_on_added_by_id"
t.index ["invitation_id"], name: "index_memberships_on_invitation_id"
t.index ["platform_agent_of_id"], name: "index_memberships_on_platform_agent_of_id"
t.index ["team_id"], name: "index_memberships_on_team_id"
t.index ["user_id"], name: "index_memberships_on_user_id"
end

create_table "scaffolding_absolutely_abstract_creative_concepts", force: :cascade do |t|
t.string "name"
t.text "description"
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["team_id"], name: "index_scaffold_absolutely_abstract_creative_concept_on_team_id"
end

create_table "scaffolding_absolutely_abstract_creative_concepts_collaborators", force: :cascade do |t|
t.jsonb "role_ids"
t.bigint "creative_concept_id", null: false
t.bigint "membership_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["creative_concept_id"], name: "index_creative_concepts_collaborators_on_creative_concept_id"
t.index ["membership_id"], name: "index_creative_concepts_collaborators_on_membership_id"
end

create_table "teams", force: :cascade do |t|
t.string "name"
t.string "slug"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "being_destroyed"
t.string "time_zone"
t.string "locale"
end

create_table "users", id: :serial, force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at", precision: nil
t.datetime "remember_created_at", precision: nil
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at", precision: nil
t.datetime "last_sign_in_at", precision: nil
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.integer "current_team_id"
t.string "first_name"
t.string "last_name"
t.string "time_zone"
t.datetime "last_seen_at", precision: nil
t.string "profile_photo_id"
t.jsonb "ability_cache"
t.datetime "last_notification_email_sent_at", precision: nil
t.boolean "former_user", default: false, null: false
t.string "encrypted_otp_secret"
t.string "encrypted_otp_secret_iv"
t.string "encrypted_otp_secret_salt"
t.integer "consumed_timestep"
t.boolean "otp_required_for_login"
t.string "otp_backup_codes", array: true
t.string "locale"
t.bigint "platform_agent_of_id"
t.string "otp_secret"
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["platform_agent_of_id"], name: "index_users_on_platform_agent_of_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end

add_foreign_key "documents", "memberships"
add_foreign_key "memberships", "teams"
add_foreign_key "memberships", "users"
add_foreign_key "scaffolding_absolutely_abstract_creative_concepts", "teams"
add_foreign_key "scaffolding_absolutely_abstract_creative_concepts_collaborators", "scaffolding_absolutely_abstract_creative_concepts", column: "creative_concept_id"
end
75 changes: 75 additions & 0 deletions bullet_train/test/models/team_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require "test_helper"

class TeamTest < ActiveSupport::TestCase
test "a new team defaults time_zone to UTC" do
team = Team.new
assert_equal "UTC", team.time_zone
end

test "explicitly set time_zone is not clobbered by first user" do
team = Team.create!(name: "new test team", time_zone: "Eastern Time (US & Canada)")
user = User.create!(email: "[email protected]", password: "password", password_confirmation: "password", time_zone: "Central Time (US & Canada)")
Membership.create!(team: team, user: user)
team.reload
assert_equal "Eastern Time (US & Canada)", team.time_zone
end

test "a new team gets the time_zone of the first user when they join" do
team = Team.create!(name: "new test team")
user = User.create!(email: "[email protected]", password: "password", password_confirmation: "password", time_zone: "Central Time (US & Canada)")
Membership.create!(team: team, user: user)
team.reload
assert_equal "Central Time (US & Canada)", team.time_zone
end

test "a team with a nil time_zone gets the time_zone of the first user when they join" do
team = Team.create!(name: "new test team")
team.time_zone = nil
team.save
team.reload
assert_equal nil, team.time_zone

user = User.create!(email: "[email protected]", password: "password", password_confirmation: "password", time_zone: "Central Time (US & Canada)")
Membership.create!(team: team, user: user)
team.reload
assert_equal "Central Time (US & Canada)", team.time_zone
end

test "default UTC time_zone is not clobbered if first user doesn't have a time zone set" do
team = Team.create!(name: "new test team")
user = User.create!(email: "[email protected]", password: "password", password_confirmation: "password", time_zone: nil)
Membership.create!(team: team, user: user)
team.reload
assert_equal "UTC", team.time_zone
end

test "default UTC time_zone is overwritten once the first user sets a time zone" do
team = Team.create!(name: "new test team")
user = User.create!(email: "[email protected]", password: "password", password_confirmation: "password", time_zone: nil)
Membership.create!(team: team, user: user)
team.reload
assert_equal "UTC", team.time_zone

user.time_zone = "Central Time (US & Canada)"
user.save

team.reload
assert_equal "Central Time (US & Canada)", team.time_zone
end

test "nil time_zone is overwritten once the first user sets a time zone" do
team = Team.create!(name: "new test team", time_zone: nil)
user = User.create!(email: "[email protected]", password: "password", password_confirmation: "password", time_zone: nil)
Membership.create!(team: team, user: user)
team.time_zone = nil
team.save
team.reload
assert_equal nil, team.time_zone

user.time_zone = "Central Time (US & Canada)"
user.save

team.reload
assert_equal "Central Time (US & Canada)", team.time_zone
end
end

0 comments on commit edef27e

Please sign in to comment.