-
Notifications
You must be signed in to change notification settings - Fork 653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate ::FeatureFlag and ::FeatureFlagsUser to ActiveRecord #15841
Migrate ::FeatureFlag and ::FeatureFlagsUser to ActiveRecord #15841
Conversation
07499b4
to
a67da0b
Compare
it { should be_false } | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was moved to user_commons_spec.rb
SequelRails.connection["select count(*) from feature_flags_users where user_id = '#{user_id}'"].first[:count].should eq 0 | ||
SequelRails.connection["select count(*) from feature_flags where id = '#{ff.id}'"].first[:count].should eq 1 | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was moved to user_commons_spec.rb
as describe '#update_feature_flags' do ...
@feature_flag.id = feature_flag_params[:id] | ||
end | ||
@feature_flag.name = feature_flag_params[:name] | ||
@feature_flag.restricted = feature_flag_params[:restricted] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I refactored this code because it was a bit counterintuitive. We were assigning new values to the feature flag attributes even when the request only aimed to do a #destroy.
|
||
def create | ||
@feature_flag.save | ||
render json: @feature_flag, status: 204 | ||
Carto::FeatureFlag.create!(feature_flag_params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched things like #save
and #destroy
for the !
equivalents so we are able to spot an error. Otherwise we were always returning a 204
no matter if the action succeeded or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that indeed keeps the behavior consistent: https://www.rubydoc.info/github/jeremyevans/sequel/Sequel%2FModel%2FClassMethods:raise_on_save_failure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that indeed keeps the behavior consistent
Oh! I did not notice that it's indeed the Sequel
equivalent. So the consistency was "by accident" 🤣 🤣
end | ||
|
||
def feature_flag_params | ||
params.require(:feature_flag).permit(:id, :name, :restricted) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the standard Rails way to define which parameters are allowed to be updated from a controller
@@ -54,7 +54,7 @@ def create | |||
if @user.save | |||
@user.reload | |||
CartoDB::Visualization::CommonDataService.load_common_data(@user, self) if @user.should_load_common_data? | |||
@user.set_relationships_from_central(user_param) | |||
@user.update_feature_flags(user_param[:feature_flags]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For simplicity, I removed the additional level of indirection added by set_relationships_from_central
, since calling it is equivalent to calling update_feature_flags
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok to do @user.update_feature_flags(nil)
? that's the check you may be missing, right? What's the semantics? Hope it is not to reset feature flags to empty. EDIT: to be honest, I don't know if that situation is possible, but I think it is as Central only sends "attributes" (or pseudo-attributes) that have changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way of calling it should be:
user.update_feature_flags(["123", "456"])
Hope it is not to reset feature flags to empty
This is taken care of here. On the other hand you made me realize that this is an important-enough case to have a specific spec to check that flags are not reset by accident, so I'm gonna add it 👍
|
||
def self.find_by_user(user) | ||
FeatureFlag.where(restricted: false) + user.feature_flags.select(&:restricted) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔥 !
…ureflag-and-featureflaguser
@@ -160,7 +160,7 @@ def non_owner_users | |||
end | |||
|
|||
def inheritable_feature_flags | |||
inherit_owner_ffs ? owner.feature_flags_user : [] | |||
inherit_owner_ffs ? owner.self_feature_flags : Carto::FeatureFlag.none | |||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By returning Carto::FeatureFlag.none
instead of []
, we return an ActiveRecord
association proxy, so we allow chaining more conditions to the query afterwards without running the query yet.
The acceptance in staging looked ✅ 👍
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a lot of work and a lot of small conflicts solved nicely! 💪
The only thing that I think you need to review (or smoke test in staging) is syncing FF's from Central. EDIT: I mean, syncing something other than feature flags and checking it does not break with a nil
usage (or delete flags)
|
||
def create | ||
@feature_flag.save | ||
render json: @feature_flag, status: 204 | ||
Carto::FeatureFlag.create!(feature_flag_params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that indeed keeps the behavior consistent: https://www.rubydoc.info/github/jeremyevans/sequel/Sequel%2FModel%2FClassMethods:raise_on_save_failure
@@ -54,7 +54,7 @@ def create | |||
if @user.save | |||
@user.reload | |||
CartoDB::Visualization::CommonDataService.load_common_data(@user, self) if @user.should_load_common_data? | |||
@user.set_relationships_from_central(user_param) | |||
@user.update_feature_flags(user_param[:feature_flags]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok to do @user.update_feature_flags(nil)
? that's the check you may be missing, right? What's the semantics? Hope it is not to reset feature flags to empty. EDIT: to be honest, I don't know if that situation is possible, but I think it is as Central only sends "attributes" (or pseudo-attributes) that have changed.
|
||
def self.find_by_user(user) | ||
FeatureFlag.where(restricted: false) + user.feature_flags.select(&:restricted) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔥 !
@@ -306,4 +306,36 @@ def has_access_to_coverband? | |||
organization&.name == 'team' | |||
end | |||
|
|||
def feature_flags | |||
feature_flags_ids = self_feature_flags.pluck(:id) + Carto::FeatureFlag.not_restricted.pluck(:id) | |||
feature_flags_ids += organization.inheritable_feature_flags.pluck(:id) if organization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to look it up in the docs 😅 :
https://apidock.com/rails/Enumerable/pluck
https://apidock.com/rails/ActiveRecord/Calculations/pluck
@@ -39,8 +39,8 @@ class Carto::User < ActiveRecord::Base | |||
has_one :owned_organization, class_name: Carto::Organization, inverse_of: :owner, foreign_key: :owner_id | |||
has_one :static_notifications, class_name: Carto::UserNotification, inverse_of: :user | |||
|
|||
has_many :feature_flags_user, dependent: :destroy, foreign_key: :user_id, inverse_of: :user | |||
has_many :feature_flags, through: :feature_flags_user | |||
has_many :self_feature_flags_user, dependent: :destroy, foreign_key: :user_id, inverse_of: :user, class_name: Carto::FeatureFlagsUser |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(maybe I'm picking this out of context, but why the rename?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because the feature flags of a user are not only the ones he/she has, but also the ones inherited from the organization owner. I had manly two ways to address this to avoid ambiguity:
Option 1
Carto::User.self_feature_flags # => An association which only returns the user feature flags
Carto::User.feature_flags # => A method which returns self_feature_flags + organization feature flags
Option 2
Carto::User.feature_flags # => An association which only returns the user feature flags
Carto::User.effective_feature_flags # => A method which returns feature_flags + organization feature flags
Even though 2 would be more consistent with the naming we used to address the similar issue with the user quotas, finally I did 1 because I thought it was more natural for some other developer to try to retrieve the feature flags doing user.feature_flags
rather than knowing that the real method he/she has to use is user.effective_feature_flags
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! 👍
…ureflag-and-featureflaguser
Story details: https://app.clubhouse.io/cartoteam/story/104742
What does this PR do?
::FeatureFlag
and::FeatureFlagsUsere
Sequel models to the ActiveRecord equivalents:Carto::FeatureFlag
andCarto::FeatureFlagsUser
::User
andCarto::User
, and some method names were misleading.