-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Refactor Loggable system #16707
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 Loggable system #16707
Conversation
First pass at huge Actionlog refactor Yank observers from AppServiceProvider Only 3 tests left to go! All but one test passing All tests now pass. Cleanups, renames, clearing out unneeded changes, unused log messages Cleanup, removing unnecessary log types on restores Added some comments and switched main Loggable interface method WIP - moving to new logging system and changing checkin/checkout match Start to move asset checkout methods to 'new-style' Small fixes; new helpers, cleanups Got Checkout tests working for UI and API Begin work on Accessory CheckIn refactor More progress on Accessories checkin/checkout Got Licenses handled now on checkin/checkout, API and GUI I think that's all of 'Licenses' now That looks like Components handled now. Have all of the main classes - minus quantities on Consumables - working Getting more and more tests passing Some work on fixing tests, but I'm getting stuck Reworked NoteAdded to fit in new scheme get rid of logUpload method More cleanups of ActionLogs usage
PR Summary
These changes aimed at enhancing the overall maintainability and readability of the code, while also ensuring that the actions were logged and recorded more effectively. |
$logaction->target_type = User::class; | ||
$logaction->location_id = $user->location_id ?? null; | ||
$logaction->logaction('request canceled'); | ||
$asset->setLogTarget(Auth::user()); |
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.
Can we please use auth()->user
here instead?
if (($request->filled('checkin_at')) && ($request->get('checkin_at') != date('Y-m-d'))) { | ||
$originalValues['action_date'] = $checkin_at; | ||
\Log::error("Setting checkin time of ".$request->get('checkin_at')." because that's not today which is: ".date('Y-m-d')); | ||
$asset->setLogDate(new Carbon($request->get('checkin_at'))); |
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 this only affects action_date, can we rename it to be more specific, like setLogActionDate
?
$asset->setLogNote($request->input('note')); | ||
// Create the image (if one was chosen.) | ||
if ($request->hasFile('image')) { | ||
$asset->setLogFilename($request->handleFile('private_uploads/audits/', 'audit-'.$asset->id, $request->file('image'))); |
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 think we need to use handleFile()
here
|
||
trait CheckInOutRequest | ||
{ | ||
/** |
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 file should probably be in the Traits directory?
|
||
// If the checked-in qty is exactly the same as the assigned_qty, | ||
// we can simply delete the associated components_assets record | ||
if ($qty_remaining_in_checkout == 0) { |
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.
Where are we checking and and deleting the components_assets
record now?
$component->setLogQuantity($request->input('assigned_qty', 1)); | ||
$component->setLogTarget($asset); | ||
$component->setLogNote($request->input('note')); | ||
$component->checkInAndSave(); |
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 think this should be checkOutAndSave()
} | ||
|
||
if($licenseSeat->assigned_to != null){ | ||
$return_to = User::find($licenseSeat->assigned_to); |
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 think $return_to
here ties into the redirector that @Godmartinz wrote. Not sure we can pull it from here, but we'll need to test further.
$licenseSeat->assigned_to = null; | ||
$licenseSeat->asset_id = null; | ||
$licenseSeat->notes = $request->input('notes'); | ||
$licenseSeat->notes = $request->input('notes'); //huh. Weird. |
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 think it's not so weird - I think the $licenseSeat->notes
is the pivot table notes
, versus the action_logs
notes
$request->request->add(['assigned_user' => $target->id]); | ||
session()->put(['redirect_option' => $request->get('redirect_option'), 'checkout_to_type' => 'user']); | ||
} else { | ||
throw new \Exception("No valid checkout target for license"); |
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 should maybe be handled in validation? Form request, or something else.
|
||
|
||
return redirect()->route('licenses.index')->with('error', trans('Something went wrong handling this checkout.')); | ||
return redirect()->route('licenses.index')->with('error', trans('Something went wrong handling this checkout.')); //TODO - translate? |
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, definitely should translate - this isn't a valid key. :(
|
||
if (! $licenseSeat->license->is($license)) { | ||
throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.mismatch'))); | ||
throw new HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.mismatch'))); |
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 think just handling this via regular redirect (and add withInput()
if it makes sense should be fine
return redirect()->back()->with('error', trans('general.not_deleted', ['item_type' => trans('general.location')])); | ||
} | ||
|
||
if ($location->restore()) { |
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 one thing that worries me here i that some FCOs use a special logAndSaveIfBlah()
method, and some have the magic overrides in the save()
, saving()
, restoring()
methods, and I'm just concerned that it could be confusing to figure out where the magic is.
$user->setLogNote($request->input('notes')); | ||
|
||
if (!$user->logAndSaveIfNeeded(ActionType::Uploaded)) { | ||
return JsonResponse::create(['error' => 'Failed validation: '.print_r($logAction->getErrors(), true)], 500); |
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 should use the UploadedFilesTransformer for this, or at least our standard API JSON response formatter.
//dump($item); | ||
if ($editingAsset) { | ||
$asset->update($item); | ||
$asset->update($item); // FIXME - I don't know why this is happening 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.
Not sure what this comment means. What is happening like what?
if (get_class($this) !== UserImporter::class) { | ||
// $this->item["user"] = $this->createOrFetchUser($row); | ||
$this->item['checkout_target'] = $this->determineCheckout($row); | ||
$this->item['checkout_target'] = $this->determineCheckout($row); //BERZINGOES?! |
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 don't know what this comment means
|
||
foreach ($uploads as $file) { | ||
try { | ||
Storage::delete('private_uploads/consumables/'.$file->filename); |
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 we soft-delete and can possibly restore, we should do this in the purge instead. Otherwise a restored consumable's files will be broken.
} | ||
|
||
// setter functions for private variables | ||
public function setLogAction(ActionType $message) |
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.
Should we use the more modern accessor/mutator laravel syntax here?
protected $table = 'users'; | ||
protected $injectUniqueIdentifier = true; | ||
|
||
public static array $hide_changes = ['password', 'remember_token', 'two_factor_secret', 'reset_password_code', 'persist_code']; |
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.
|
||
Event::assertDispatched(function (CheckoutableCheckedIn $event) { | ||
return $event->action_date === '2023-01-02' && $event->note === 'hello'; | ||
return $event->action_date->isSameDay(new Carbon('2023-01-02')) && $event->note === 'hello'; |
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.
Let's confirm this conforms with the slightly changed newer Carbon syntax.
at the factories. So the asset's location should be changed from its rtd_location_id to the User's location_id - which is definitively null. So I think this test | ||
might be wrong? But, at the same time, if that's something we've been doing for years, we might have to keep doing that for years. Which is weird. Very, very weird. | ||
Not sure what to do on this one. | ||
*/ |
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 think this might have already handled that? #16682
A lot of what I did here is done, in a smaller way, in #16779 - which does a lot of the groundwork used here, without going quite as deep as this change goes. I suspect we can and will probably go in this specific direction, but not until we've worked out any/all bugs with the aforementioned PR - and then, these changes will be 'smaller' and more reviewable, relative to that. |
This does....a LOT.
For "Action Type" we've been using strings and while we haven't run into problems with that before, I thought something a little safer would be to use PHP Enums instead. This means you can't accidentally create an action log with type 'updatedblork' accidentally.
I moved the Loggable trait to the Traits directory.
The loggable trait now has several private properties, and setters and getters for those properties. Those properties determine how the actual log entry will be generated. This reads a little clearer than some of the longer function signatures we have used.
Much of how the Loggable trait operates now is via various built-in events, such as 'restored' or 'saved' or 'created' - automatically creating the appropriate log entries when those events occur. That's explicitly laid out directly within the Loggable trait, which is the key file that powers much of what we have here.
I have removed several Events that we'd been using to implement functionality. In my opinions, events should be the results of actions, they shouldn't cause actions.
Instead of using the LogListener class to handle doing log things, that's now injected directly into the Trait, which makes it easier to see what exactly is happening when logging occurs.
In the Asset class, we now associate asset-specific listeners via the Boot method rather than in a separate listener file. This makes it clearer to see which effects are firing in the context of Assets.
Similarly with Consumables; the boot method handles what we had been doing in the separate listener.
And so you'll see that several observers have been removed, and their effects are directly integrated into the boot methods of the things that were being observed. Again, the thinking here is that things that affect class Foo should be in the file that has class Foo.
The biggest problem I've run into with this is that it's just so damned BIG - @snipe and I have been discussing ways to implement pieces of this in smaller, less dangerous ways in order to get it live faster. Instead of biting off more than I can chew, are there ways of shrinking this down and still making things cleaner, without boiling the ocean? We shall see.