-
Notifications
You must be signed in to change notification settings - Fork 5
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
Refactor update owner audit activity #700
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
class ChangeCaseOwnerForm | ||
include ActiveModel::Model | ||
include ActiveModel::Attributes | ||
|
||
attribute :owner_id | ||
attribute :owner_rationale | ||
|
||
validates_presence_of :owner_id | ||
validate :new_owner_must_be_active_user_or_team, if: -> { owner_id.present? } | ||
|
||
def owner | ||
user || team | ||
end | ||
|
||
private | ||
|
||
def new_owner_must_be_active_user_or_team | ||
errors.add(:owner_id, :not_found) unless owner | ||
end | ||
|
||
def user | ||
@user ||= User.active.find_by(id: owner_id) | ||
end | ||
|
||
def team | ||
@team ||= Team.find_by(id: owner_id) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,30 @@ | ||
class AuditActivity::Investigation::AutomaticallyUpdateOwner < AuditActivity::Investigation::Base | ||
def self.from(investigation) | ||
title = investigation.owner.id.to_s | ||
super(investigation, title) | ||
def self.from(*) | ||
raise "Deprecated - use DeleteUser.call instead" | ||
end | ||
|
||
def owner_id | ||
# We store owner_id in title field, this is getting it back | ||
# Using alias for accessing parent method causes errors elsewhere :( | ||
AuditActivity::Investigation::Base.instance_method(:title).bind(self).call | ||
def self.build_metadata(owner) | ||
{ | ||
owner_id: owner.id | ||
} | ||
end | ||
Comment on lines
+6
to
10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have this pattern elsewhere, but I wonder whether we could avoid the class method and just pass the Some possible implementations: class AuditActivity::Investigation::AutomaticallyUpdateOwner < AuditActivity::Investigation::Base
attr_writer :owner_id
def metadata
read_attribute(:metadata) || {
owner_id: @owner_id
}
end
end or overriding the initialiser: class AuditActivity::Investigation::AutomaticallyUpdateOwner < AuditActivity::Investigation::Base
attr_writer :owner_id
def initialize(*args)
super(args)
return unless new_record?
self.metadata = {
owner_id: owner_id
}
end
end or using the activerecord class AuditActivity::Investigation::AutomaticallyUpdateOwner < AuditActivity::Investigation::Base
attr_writer :owner_id
after_initialize :set_metadata
def set_metadata
return unless new_record?
self.metadata = {
owner_id: @owner_id
}
end
end Worth exploring? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that's an interesting idea. I think at the moment where we have dozens of classes inheriting from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to park this idea for this PR - we should try it separately and see how it looks. |
||
|
||
# We store owner_id in title field, this is computing title based on that | ||
def title | ||
def title(user) | ||
type = investigation.case_type.capitalize | ||
new_owner = (User.find_by(id: owner_id) || Team.find_by(id: owner_id))&.decorate&.display_name | ||
new_owner = owner.decorate.display_name(viewer: user) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Passing |
||
"Case owner automatically changed on #{type} to #{new_owner}" | ||
end | ||
|
||
def subtitle | ||
def subtitle(_viewer) | ||
"Case owner automatically changed, #{pretty_date_stamp}" | ||
end | ||
|
||
def entities_to_notify | ||
[] | ||
end | ||
private | ||
|
||
def email_subject_text; end | ||
def owner | ||
User.find_by(id: metadata["owner_id"]) || Team.find_by(id: metadata["owner_id"]) | ||
end | ||
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we could avoid this by storing the owner team ID and the owner user ID under separate metadata keys? Especially as we'll soon be always storing the team ID as the "owner" on a case, with an optional user id as "assigned officer"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also thought storing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had the same thought initially, but then remembered that we had planned to change "owner" to always be the team, and optionally additionally store the user. Don't think we've done that on the investigation model yet though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We would still need to store historical audit activity where it could be a user. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could potentially backfill those (by looking up the current team for that user), which is what I think we're planning to do for cases? Happy to leave this to a follow-up PR though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I started looking at this but I quickly realised it doesn't make a lot of sense to do this until the change to always be a team. Currently it can be either, and we can easily migrate the data at a later date. |
||
|
||
def email_update_text(viewer = nil); end | ||
# Do not send investigation_updated mail. This is handled by the DeleteUser service | ||
def notify_relevant_users; end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,25 @@ | ||
class AuditActivity::Investigation::UpdateOwner < AuditActivity::Investigation::Base | ||
include NotifyHelper | ||
|
||
def self.from(investigation) | ||
title = investigation.owner.id.to_s | ||
body = investigation.owner_rationale | ||
super(investigation, title, sanitize_text(body)) | ||
def self.from(*) | ||
raise "Deprecated - use ChangeCaseOwner.call instead" | ||
end | ||
|
||
def owner_id | ||
# We store owner_id in title field, this is getting it back | ||
# Using alias for accessing parent method causes errors elsewhere :( | ||
AuditActivity::Investigation::Base.instance_method(:title).bind(self).call | ||
def self.build_metadata(owner, rationale) | ||
{ | ||
owner_id: owner.id, | ||
rationale: rationale | ||
} | ||
Comment on lines
+6
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should also store the owner’s name, to guard against the user or team being deleted in future, and so that we can display the user/team’s name at the time they became the new owner? I think that’s what you'd expect to see when reading through the timeline of events, although it’s hard to know for sure. @tobyhughes @edwardhorsford? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure! We're disabling users, not deleting them - so this would mean name updates get reflected. On the other hand we could take the view that these audit entries are a point in time snapshot so could just store the value of the data at that time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Users should not be deleted entirely, just marked with a deleted flag. Their names and team membership can change over time. Storing their team as well might be a good shout, but this wouldn't be used anywhere currently and we'd have to do some work refactoring the way we display activities to actually use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think displaying the name at-the-time would probably be ideal. But it's also not really essential, so happy to park this for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this'll need a fair bit of re-jigging which is going to tie this up for a while, would be pleased to see a follow up PR for this though. |
||
end | ||
|
||
def title | ||
# We store owner_id in title field, this is computing title based on that | ||
"Case owner changed to #{(User.find_by(id: owner_id) || Team.find_by(id: owner_id))&.decorate&.display_name}" | ||
def title(user) | ||
"Case owner changed to #{owner.decorate.display_name(viewer: user)}" | ||
end | ||
|
||
def email_update_text(viewer = nil) | ||
body = [] | ||
body << "Case owner changed on #{investigation.case_type} to #{investigation.owner.decorate.display_name(viewer: viewer)} by #{source&.show(viewer)}." | ||
|
||
if investigation.owner_rationale.present? | ||
body << "Message from #{source&.show(viewer)}:" | ||
body << inset_text_for_notify(investigation.owner_rationale) | ||
end | ||
|
||
body.join("\n\n") | ||
def body | ||
metadata["rationale"] | ||
end | ||
|
||
def email_subject_text | ||
"Case owner changed for #{investigation.case_type}" | ||
def owner | ||
User.find_by(id: metadata["owner_id"]) || Team.find_by(id: metadata["owner_id"]) | ||
end | ||
|
||
private | ||
|
@@ -40,27 +28,6 @@ def subtitle_slug | |
"Changed" | ||
end | ||
|
||
def users_to_notify | ||
compute_relevant_entities(model: User, compute_users_from_entity: proc { |user| [user] }) | ||
end | ||
|
||
def teams_to_notify | ||
compute_relevant_entities(model: Team, compute_users_from_entity: proc { |team| team.users }) | ||
end | ||
|
||
def compute_relevant_entities(model:, compute_users_from_entity:) | ||
return [] unless investigation.saved_changes.key?("owner_id") | ||
|
||
previous_owner_id = investigation.saved_changes["owner_id"][0] | ||
previous_owner = model.find_by(id: previous_owner_id) | ||
new_owner = investigation.owner | ||
owner_changed_by = source.user | ||
|
||
old_users = previous_owner.present? ? compute_users_from_entity.call(previous_owner) : [] | ||
old_entities = previous_owner.present? ? [previous_owner] : [] | ||
new_entities = new_owner.is_a?(model) ? [new_owner] : [] | ||
return new_entities if old_users.include? owner_changed_by | ||
|
||
(new_entities + old_entities).uniq | ||
end | ||
# Do not send investigation_updated mail. This is handled by the ChangeCaseOwner service | ||
def notify_relevant_users; 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.
Previously the key was
:owner_id
which is too generic. We now use the form name and case ID in the key so that the user could have multiple forms open without them conflicting.