-
Notifications
You must be signed in to change notification settings - Fork 141
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
Alert Definitions: add hash_expression #76
Conversation
@miq-bot add_label enhancement |
17ff0e2
to
ac335f9
Compare
@imtayadeway @moolitayer Please review |
raise BadRequestError, "Must not specify more than one expression for editing a #{type} resource" | ||
elsif exp_values.size == 1 | ||
if data["hash_expression"] | ||
data["miq_expression"] = 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.
So as we discussed with @zakiva this handles an edge case where a user wants to replace a hash expression with a miq expression or vice versa. Honestly I don't know if we should support that
@imtayadeway @abellotti We need an API review here. |
c6c6671
to
ba951e7
Compare
@zakiva can you provide an example of a |
example of
you can use expression/miq_expression/hash_expression as you like. |
ping @imtayadeway Can you please review? |
ping @abellotti @imtayadeway |
|
||
before_action :set_additional_attributes | ||
|
||
def create_resource(type, id, data = {}) | ||
assert_id_not_specified(data, type) | ||
assert_all_required_fields_exists(data, type, REQUIRED_FIELDS) | ||
if expression_values(data).size != 1 |
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 like business logic to me. What's more - it's duplicated on line 26. This should be added to the model as a validation, which will greatly simplify this code. The code here should be almost entirely concerned with deserializing the JSON request body and passing data to the model.
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.
removed that, will add a validation in the model later on.
@@ -1,26 +1,39 @@ | |||
module Api | |||
class AlertDefinitionsController < BaseController | |||
REQUIRED_FIELDS = %w(description db expression options).freeze | |||
REQUIRED_FIELDS = %w(description db options).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.
I know that you're only changing this part - but I'm also confused as to why these "required fields" aren't expressed in the model too.
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.
Agreed, I can remove them in this PR and then add the validations in the model.
ba951e7
to
9a06176
Compare
@imtayadeway PTAL |
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.
@zakiva this looks better 😁 but I think there is still too much here that could be pushed further down into the model for better encapsulation
@@ -1,26 +1,31 @@ | |||
module Api | |||
class AlertDefinitionsController < BaseController | |||
REQUIRED_FIELDS = %w(description db expression options).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.
You can nix this empty line too 😁
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.
done :-)
begin | ||
data["expression"] = MiqExpression.new(data["expression"]) | ||
create_miq_expression(data) | ||
data["enabled"] = true if data["enabled"].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.
Again, I know you're not touching this here (and feel free to leave for a follow up if appropriate) but this is more business logic - "an alert definition is enabled by default" - that belongs in the model
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.
Right, removing that too. This should be handled explicitly by the user.
cc @moolitayer @elad661
end | ||
|
||
def create_miq_expression(data) | ||
data["expression"] = MiqExpression.new(data["expression"]) if data["expression"] |
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 not sure I understand why this is necessary. Type coercion should be handled by the setter in the model - if that's not currently the case then that needs to be added so we don't have to munge the data here (and anywhere else for that matter).
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.
OK, moved it to the model: ManageIQ/manageiq#16397
rescue => err | ||
raise BadRequestError, "Failed to create a new alert definition - #{err}" | ||
end | ||
end | ||
|
||
def edit_resource(type, id = nil, data = {}) | ||
raise BadRequestError, "Must specify an id for editing a #{type} resource" unless id | ||
if expression_values(data).size == 1 |
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 this still needed? Again, this is another business rule that needs to be encapsulated in the model.
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 will remove that too.
The intention here was to handle the case of replacing a miq expression with hash expression (and vice versa) when editing an alert, but we can leave that to the user to explicitly nullify these fields.
cc @moolitayer @elad661
data["enabled"] = true if data["enabled"].nil? | ||
super(type, id, data).serializable_hash.merge("expression" => data["expression"]) | ||
super(type, id, data.deep_symbolize_keys).serializable_hash.merge("expression" => expression_values(data).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.
Why is the deep_symbolize_keys
needed? If it's for a particular serialized column that is keyed with symbols, I think it would be better to make that explicit rather than converting everything wholesale
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 want the created alerts via API to be identical to alerts created in other places.
It is not a particular (single) column, so I think it will be nicer not to list them.
9669ae4
to
79bbc02
Compare
@imtayadeway Thanks for the review! I made the requested changes. |
@imtayadeway now that the change in the model ( ManageIQ/manageiq#16397 ) is merged, can we get this PR merged too, or are there any other required changes? |
@imtayadeway @abellotti PTAL |
@imtayadeway Can you please take a look? This PR has been waiting for a re-review for almost a month now... |
@gtanzillo ping |
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.
@zakiva looking good, but there is some surprising behavior that I've outlined above that I think we need to address before this is merged. Thanks!
@@ -31,5 +28,10 @@ def edit_resource(type, id = nil, data = {}) | |||
def set_additional_attributes | |||
@additional_attributes = %w(expression) | |||
end | |||
|
|||
def update_miq_expression(data) | |||
data["miq_expression"] = data["expression"] |
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 still not sure why this is needed. Doesn't the model support this via both #expression=
and #miq_expression=
?
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 needed because the setter in the model for expression=
looks like this: https://github.com/ManageIQ/manageiq/blob/master/app/models/miq_alert.rb#L59-L65
if exp.kind_of?(MiqExpression)
self.miq_expression = exp
elsif exp.kind_of?(Hash)
self.hash_expression = exp
And the data we get from the api user is always a "hash" (but not a Hash Expression), because it's parsed json.
The model will only try to create a new MiqExpression from hash data if you explicitly use the miq_expression
setter, so we need to check if the user provided us an "expression" parameter, and if so explicitly say we want a miq_expression and not an hash expression.
Since hash expressions are just ruby hashes, there's no cleaner way to do this - I don't think we'd want the model to attempt parsing hashes to see if they fit the "miq expression" or "hash expression" forms and magically decide which type to use.
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 OK for now....can we look at deprecating the old API (i.e. MiqAlert#expression
) in a follow up PR? Then I think a lot of the complexity of this controller could go away.
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 fine with deprecating the MiqAlert#expression
, and I can do a follow-up PR.
@moolitayer is there any reason to keep the old MiqAlert#expression
API around?
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.
In the model we would need to keep the expression
due to the alerts screen.
In the API I agree we should deprecate and then drop.
(deprecate in Gaprindashvili and Drop in H?)
data["enabled"] = true if data["enabled"].nil? | ||
super(type, id, data).serializable_hash.merge("expression" => data["expression"]) | ||
update_miq_expression(data) if data["expression"] | ||
alert = super(type, id, data.deep_symbolize_keys).serializable_hash |
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 concerned that this deep_symbolize_keys
will corrupt an incoming MiqExpression
:
{"=" => {"field" => "foo", "value" => "bar"}}.deep_symbolize_keys # => {:"=" => {:field => "foo", :value => "bar"}}
@@ -81,7 +81,7 @@ | |||
alert_definition = MiqAlert.find(response.parsed_body["results"].first["id"]) | |||
expect(alert_definition).to be_truthy | |||
expect(alert_definition.expression.class).to eq(MiqExpression) | |||
expect(alert_definition.expression.exp).to eq(sample_alert_definition["expression"]) | |||
expect(alert_definition.expression.exp).to eq(sample_alert_definition["expression"].deep_symbolize_keys) |
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 wrong, because miq expressions are not keyed with symbols (see above)
alert_definition = MiqAlert.find(response.parsed_body["results"].first["id"]) | ||
expect(alert_definition).to be_truthy | ||
expect(alert_definition.expression.class).to eq(MiqExpression) | ||
expect(alert_definition.expression.exp).to eq(sample_alert_definition["miq_expression"].deep_symbolize_keys) |
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.
As above
sample_alert_definition = { | ||
"description" => "Test Alert Definition", | ||
"db" => "ContainerNode", | ||
"miq_expression" => { "eval_method" => "dwh_generic", "mode" => "internal", "options" => {} }, |
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 this supposed to be a hash expression standing in place of an miq expression? Even if this "works", I wouldn't want us to officially support this surprising behavior
"description" => "Test Alert Definition", | ||
"db" => "ContainerNode", | ||
"hash_expression" => { "eval_method" => "nothing", "mode" => "internal", "options" => {} }, | ||
"miq_expression" => { "eval_method" => "dwh_generic", "mode" => "internal", "options" => {} }, |
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.
As above
api_alert_definition_url(nil, alert_definition), | ||
:params => { | ||
:action => "edit", | ||
:miq_expression => exp, |
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.
As above
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.
fixed
:params => { | ||
:action => "edit", | ||
:hash_expression => { :eval_method => "event_threshold", :mode => "internal", :options => {} }, | ||
:miq_expression => { :eval_method => "mw_heap_used", :mode => "internal", :options => {} } |
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.
As above
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.
fixed
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 looks like this is still a "hash_expression" in place of an "miq_expression"?
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.
oh, you're right, I didn't actually fix this one when I said it was. I fixed it now.
79bbc02
to
cbe1aa7
Compare
Thanks for the review. I fixed the tests and changed the controller to only do |
cbe1aa7
to
07336f3
Compare
07336f3
to
5715db3
Compare
Checked commit zakiva@5715db3 with ruby 2.3.3, rubocop 0.47.1, haml-lint 0.20.0, and yamllint 1.10.0 |
@@ -71,7 +71,7 @@ | |||
sample_alert_definition = { | |||
"description" => "Test Alert Definition", | |||
"db" => "ContainerNode", | |||
"expression" => { "eval_method" => "dwh_generic", "mode" => "internal", "options" => {} }, | |||
"expression" => {"exp" => {"=" => {"field" => "Vm-name", "value" => "foo"}}, "context_type" => 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.
Just curious about these changes, description mentioned being backward compatible, why are these needed ?
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.
Because, as @imtayadeway noted in this comment #76 (comment), the expression there was not actually a MiqExpression, so that means the test was wrong. It wasn't supposed to "work" like that.
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.
👍 thanks for clarifying.
Thanks @zakiva for the API Enhancement and everyone for the reviews. This LGTM!! 👍 |
Depends on: ManageIQ/manageiq#16397
Changes included:
Currently, it is possible to create an alert definition via API only with an expression as MiqExpression. Following the changes in Add hash_expression to MiqAlert manageiq#15315, this PR adds the ability to create an alert with hash expression.
Backward Compatibility: we would like to keep the option to create an alert with expression as MiqExpression without explicitly passing a
miq_expression
attribute. So the user will have now 3 options when sending a create request and will need to pass exactly one of the following:expression
which will be saved as MiqExpression (same as before).miq_expression
.hash_expression
.Allowing editing an alert definition (with similar logic).
Deep symbolize for data.
(This PR replaces ManageIQ/manageiq#14979 with different implementation)