-
Notifications
You must be signed in to change notification settings - Fork 900
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
Model changes for alerts #13233
Model changes for alerts #13233
Conversation
6ea83f2
to
f1a65dd
Compare
dedf472
to
a0083fc
Compare
@simon3z PTAL |
a0083fc
to
9e5cb2b
Compare
9e5cb2b
to
621d518
Compare
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 looks good to me 👍 @Fryguy are you ok with merging this?
virtual_column :assignee, :type => :string | ||
|
||
def assignee | ||
miq_alert_status_actions.where(:action_type => %w(assign unassign)).last.try(:assignee) |
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.
@moolitayer no need for explicit ordering here? Is there a guarantee for the actions to be ordered or is it just a coincidence?
Also, to simplify, isn't unassign
just an assign
to nil
? Can't you just use assign
?
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 order comes from the association there is a test for this here
-
I chose to use unassign since I must have an unacknowledge, for consistency
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.
Perhaps encapsulate the :action_type => %w(assign unassign)
, or even more of this logic, in a MiqAlertStatusAction scope.
Ordered association: neat, though I've seen a recommendation to avoid implicit ordering, or capture it in separate scope: http://blog.carbonfive.com/2016/11/16/rails-database-best-practices/.
LGTM either way.
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.
Perhaps encapsulate the :action_type => %w(assign unassign), or even more of this logic, in a MiqAlertStatusAction scope.
I could not find an elegant way of doing that, do you have a suggestion?
Ordered association: neat, though I've seen a recommendation to avoid implicit ordering, or capture it in separate scope: http://blog.carbonfive.com/2016/11/16/rails-database-best-practices/.
LGTM either way.
This seems to be a good use case since I do not have a consumer that would require a different order
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.
Perhaps encapsulate the :action_type => %w(assign unassign), or even more of this logic, in a MiqAlertStatusAction scope.
Yeah, I forgot it's not purely actions responsibility, it's only actions that belong to this status, which is easier expressed on this side.
The most I can think of (untested):
# in MiqAlertStatusAction
scope :assignee_changes, -> { where(:action_type => %w(assign unassign)) }
# in MiqAlertStatus
def assignee
miq_alert_status_actions.assignee_changes.last.try(:assignee)
Fine as-is.
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.
Looks pretty good. Didn't carefully review tests.
- How is severity used? Do you plan a severity-changing action?
- How is ancestry used?
- I'm curious, why maintain
acknowledged
column in sync on miq_alert_statuses vs computing last ack/unack like you did for assignee?
virtual_column :assignee, :type => :string | ||
|
||
def assignee | ||
miq_alert_status_actions.where(:action_type => %w(assign unassign)).last.try(:assignee) |
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.
Perhaps encapsulate the :action_type => %w(assign unassign)
, or even more of this logic, in a MiqAlertStatusAction scope.
Ordered association: neat, though I've seen a recommendation to avoid implicit ordering, or capture it in separate scope: http://blog.carbonfive.com/2016/11/16/rails-database-best-practices/.
LGTM either way.
@@ -0,0 +1,30 @@ | |||
class MiqAlertStatusAction < ApplicationRecord |
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 know we don't do comments in MIQ :-| but IMHO a few words on a model are worth it.
Specifically, mentioning that this table is intended to be append-only would be useful.
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'd be interested in having that enforced, did not find a way to do that. @kbrock do we have a mechanism for read only records?
They should only be created & deleted but never updated
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.
We've been overriding active record callbacks.
I'm not thrilled with this method, but that is what we've been doing.
Maybe @Fryguy or @chrisarcand has an alternative way
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.
It's easy to mark a relation as read-only, but read only implies you can't update or delete records. I believe you'll have to utilize callbacks as @kbrock mentions if you want to allow deletes but not updates.
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.
@chrisarcand that might be exactly what I need, I need to allow deletion of status actions only when the owning status is destroyed. I could not find a way to use readonly except as a callback:
def readonly?
!new_record?
end
I have a feeling you meant something else?
after_save :update_status_acknowledgement | ||
|
||
def only_assignee_can_acknowledge | ||
if action_type == 'acknowledge' && (miq_alert_status.nil? || miq_alert_status.assignee.id != user.id) |
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.
why allow miq_alert_status.nil?
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.
ah, you're not "allowing" it to be nil, you're considering it an error.
not sure it's needed (you have validation it's present, you assume it in other places) but harmless.
elsif "acknowledge" == action_type | ||
miq_alert_status.update(:acknowledged => true) | ||
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.
If a non-assignee tries "acknowledge", is only_assignee_can_acknowledge
validated before or after this function? Could .update(:acknowledged => true)
write to DB before validation failed?
class MiqAlertStatus < ApplicationRecord | ||
SEVERITY_LEVELS = %w(error warning info).freeze |
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.
do you want :accept => SEVERITY_LEVELS
validation?
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! I seem to have dropped it by mistake. Added a test as well
|
||
def update_status_acknowledgement | ||
if %w(assign unassign unacknowledge).include?(action_type) | ||
miq_alert_status.update(:acknowledged => false) |
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.
- assign > ack > unassign would remove ack preserving only_assignee_can_acknowledge invariant. Good.
Can you re-"assign" to other (or even same) person without "unassign" first?
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.
yes you can.
end | ||
|
||
def assigned? | ||
!assignee.nil? |
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.
assignee.present?
is much nicer
in the pushing side openshift collector logic should allow changing severity based on metadata, details not yet finalized.
ancestry will be used to link an alert status with its resolver. More details on it will be added in the resolve collection patch, but wanted to add the base here
Turns out it is quite hard to calculate, since we need to look at entire history of ack unack assign unassign actions Meaning there needs to be an ack that is not followed by unack assign or unassign. Since this property should be supplied in the API for every miq_alert_status I found It helpful to save on the alert status. |
621d518
to
c3e8873
Compare
c3e8873
to
dd7e092
Compare
after_save :update_status_acknowledgement | ||
|
||
def only_assignee_can_acknowledge | ||
if action_type == 'acknowledge' && (miq_alert_status.nil? || miq_alert_status.assignee.id != user.id) |
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.
ah, you're not "allowing" it to be nil, you're considering it an error.
not sure it's needed (you have validation it's present, you assume it in other places) but harmless.
let(:alert) { FactoryGirl.create(:miq_alert_status) } | ||
let(:user1) { FactoryGirl.create(:user, :name => 'user1') } | ||
let(:user2) { FactoryGirl.create(:user, :name => 'user2') } | ||
let(:acknowledgement_ticket) do |
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.
s/ticket/action/g
virtual_column :assignee, :type => :string | ||
|
||
def assignee | ||
miq_alert_status_actions.where(:action_type => %w(assign unassign)).last.try(:assignee) |
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.
Perhaps encapsulate the :action_type => %w(assign unassign), or even more of this logic, in a MiqAlertStatusAction scope.
Yeah, I forgot it's not purely actions responsibility, it's only actions that belong to this status, which is easier expressed on this side.
The most I can think of (untested):
# in MiqAlertStatusAction
scope :assignee_changes, -> { where(:action_type => %w(assign unassign)) }
# in MiqAlertStatus
def assignee
miq_alert_status_actions.assignee_changes.last.try(:assignee)
Fine as-is.
dd7e092
to
8520722
Compare
closed by mistake |
Checked commit moolitayer@8056603 with ruby 2.2.5, rubocop 0.37.2, and haml-lint 0.16.1 db/migrate/20161113091851_add_miq_alert_status_actions.rb
|
@Fryguy PTAL |
@Fryguy PTAL |
|
||
def update_status_acknowledgement | ||
if %w(assign unassign unacknowledge).include?(action_type) | ||
miq_alert_status.update(:acknowledged => false) |
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.
Ah, I see, assign also clears ack, so assign > ack > reassign keeps the invariant. LGTM.
@moolitayer @cben what's the reasons to have different approaches for assignment vs acknowledgement? IIUC for the assignee you have a virtual column on Couldn't you have a virtual column for acknowledgement as well? |
validates :assignee, :absence => true, :unless => "action_type == 'assign'" | ||
validate :only_assignee_can_acknowledge | ||
|
||
after_save :update_status_acknowledgement |
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.
@kbrock this saves a column on the owner of this action. Does it make sense to do it this way
@simon3z having a column for acknowledged is an optimization |
LGTM 👍 |
belongs_to :miq_alert | ||
belongs_to :resource, :polymorphic => true | ||
has_many :miq_alert_status_actions, -> { order "created_at" }, :dependent => :destroy | ||
virtual_column :assignee, :type => :string |
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.
@moolitayer In a follow up PR you can optimize this with a :uses => :miq_alert_status_actions
The :uses clause lets the reporting engine know to preload that. Alternately there might be a pure SQL way to do this, which perhaps @kbrock can help you with via the :arel =>
option.
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.
submitted in #13553
validates :miq_alert_status, :presence => true | ||
validates :comment, :presence => true, :if => "action_type == 'comment'" | ||
validates :assignee, :presence => true, :if => "action_type == 'assign'" | ||
validates :assignee, :absence => true, :unless => "action_type == 'assign'" |
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 feel like there's a nicer way to do these conditionals without giving SQL fragments. @kbrock ?
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.
ugh. sorry. MIA on this one
@moolitayer using a lambda here would make this better - essentially moving this check to ruby. well, at least the if
part of the check, presence is in the database.
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.
Sorry for taking long on this one, was unavailable for a while. Sent fix in #13797
@moolitayer I have a couple comments, but they can be addressed in followup PRs, perhaps with some guidance from @kbrock . |
FactoryGirl.define do | ||
factory :miq_alert_status_action do | ||
action_type 'comment' | ||
comment 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' |
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'm disappointed @chrisarcand and @Fryguy didn't see this. 👍 @moolitayer
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 showed this to you because I was literally, perfectly rick-rolled looking through commits working on something else. 😅
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 SOP shows up on the monitoring screen (it's supposed to help you solve the alert)
and we had a couple of rick rolling demos of it which was too much so I removed it from setup examples...
Model changes extending ManageIQ alerting capability to support operation teams use cases:
These new entities are used in #11962 and #13025