Problem/Motivation
From #2281691: User interface for migration-based upgrades. Much of the exception handling in the Migrate UI is problematic, including:
- Throwing or catching
\Exception
instead of something more specific. - Very long try/catch blocks for unclear purpose.
- Catching exceptions without reporting what they were or why.
- Possibly relying on exceptions for normal/expected validation code flow?
Much has happened to the code since this original review, and the once large form is now split into several forms. See #2918761: Break up MigrateUpgradeForm into smaller forms and #2682585: Rename MigrationCreationTrait as it no longer creates migrations - it configures them. Each of the six points includes a new note stating where the relevant piece of code now resides. Of the original 6 points, 2 are no longer relevant.
The following points are from reviews on #2281691: User interface for migration-based upgrades:
-
This is now in CredentialForm.php, it is no longer a massive try block.
+++ b/core/modules/drupal_upgrade/src/Form/MigrateUpgradeForm.php @@ -0,0 +1,1196 @@ + try { + ... + catch (\Exception $e) {
This is a MASSIVE try block. Which calls are we catching exceptions for? Why can't they be individually wrapped with classed exceptions?
Also there is a blank line. -
This is now in CredentialForm.php, part of the try block associated with #6.
+++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php @@ -0,0 +1,205 @@ + catch (\Exception $e) { ... + throw new \Exception('Source database does not contain a recognizable Drupal version.');
Is there a classed exception to catch or throw instead? Using \Exception is not as ideal
-
This is now in MigrationConfigurationTrait.php
+++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php @@ -0,0 +1,222 @@ + catch (\Exception $e) { + // The table might not exist for example in tests. + }
I think @tim.plunkett mentioned this too, but this is way too broad a
catch
. We should have a followup issue to fix this because it is just swallowing any error. If there is a specific exception that is happening then we should handle it more directly or look at underlying causes. -
This looks fixed. This is now in MigrationConfigurationTrait.php and the PluginNotFoundException was removed (somewhere) from the getMigrations method.
+++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php @@ -0,0 +1,222 @@ + // Migrations which are not applicable given the source and destination + // site configurations (e.g., what modules are enabled) will be silently + // ignored. + catch (RequirementsException $e) { + } + catch (PluginNotFoundException $e) { + }
This seems a fragile way to handle this; is this related to the existing followup about detecting applicable migrations?
-
This is now in MigrationConfigurationTrait.php and can be fixed in this issue.
+++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php @@ -0,0 +1,222 @@ + catch (\PDOException $e) { + $version_string = FALSE; + }
We are doing this
catch
if the system table exists, but not if the key_value table exists. -
This is now in CredentialForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php @@ -0,0 +1,1227 @@ + catch (\Exception $e) { + $error_message = [ + '#type' => 'inline_template', + '#template' => '{% trans %}Resolve the issue below to continue the upgrade.{% endtrans%}{{ errors }}', + '#context' => [ + 'errors' => [ + '#theme' => 'item_list', + '#items' => [$e->getMessage()], + ], + ], + ];
Also (again),
\Exception
is way generic, and using a try/catch like this to handle expected validation code flow is problematic.
Proposed resolution
Evaluate the types of exceptions used, the reasons they are being caught, the use of try/catch for code flow, and the potential loss/hiding of debug information.
- No longer relevant.
- Reworked that section of code.
- Changed to a PDOException in this patch
- No longer relevant. Fixed elsewhere
- Accessing the key_value tables is change to a try/catch in this patch
- Changed from catching Exception to catching DatabaseException in this patch
Remaining tasks
TBD
User interface changes
N/A
API changes
TBD
Data model changes
TBD
Comment | File | Size | Author |
---|---|---|---|
#60 | interdiff-55-60.txt | 3.82 KB | quietone |
#60 | 2679835-60.patch | 8.46 KB | quietone |
#55 | interdiff.txt | 3.3 KB | quietone |
#55 | 2679835-55.patch | 8.4 KB | quietone |
#54 | interdiff.txt | 4.21 KB | quietone |
Comments
Comment #2
xjmComment #3
mikeryanIt originally wrapped a single call to $this->createMigrations(). At the moment, I think only the getMigrationTemplates() call at the beginning of the try needs to be in here, and that's throwing \Exception (see next point).
There isn't any existing exception that's relevant here - we should create a DrupalVersionException or somesuch.
@abhishek-anand? Did you have a particular testing problem inspiring that catch?
I.e., catching PluginNotFoundException and RequirementsException - yes, this is for skipping inapplicable migrations. There are two scenarios - where a migration references a source or destination plugin which is defined by a module which is not enabled (thus, we do not want to perform migration for that module), we get PluginNotFoundException. And if the plugin's module is enabled but there's a missing requirement (e.g., the corresponding module is not enabled in the source database), checkRequirements() throws RequirementsException.
Yes, the key_value branch should also be prepared for PDO exceptions.
See the first two points above.
Comment #4
abhishek-anand CreditAttribution: abhishek-anand at Acquia commentedHi Mike,
No, I had to get the system data, so I reused to code from \Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase::getSystemData
Didn't pay enough attention to the catch, just replicated it.
Comment #7
quietone CreditAttribution: quietone as a volunteer commentedThis patch only addresses item #5 in the IS.
Comment #8
maxocub CreditAttribution: maxocub commentedAssigning for review.
Comment #9
maxocub CreditAttribution: maxocub commentedI think the item #5 from the issue summary is fixed with this patch, but we need to keep thinking about the other points and @mikeryan's suggestions in #3. Back to needs work.
Comment #11
mikeryanComment #12
heddnI think this addresses all the points in the IS. Let's see how tests fair. I did all the work, then reviewed back to see that there was already a patch in #7 that does about the same thing. So, there's also an interdiff also against @quietone's work.
Comment #15
quietone CreditAttribution: quietone as a volunteer commentedAssigning to myself.
Comment #16
quietone CreditAttribution: quietone as a volunteer commentedComment #18
quietone CreditAttribution: quietone as a volunteer commentedPostponed on #2918761: Break up MigrateUpgradeForm into smaller forms
Comment #19
quietone CreditAttribution: quietone as a volunteer commentedNo longer postponed, and will need a reroll.
Comment #20
quietone CreditAttribution: quietone as a volunteer commentedIt was reverted, back to postponed.
Comment #21
quietone CreditAttribution: quietone as a volunteer commented#2918761: Break up MigrateUpgradeForm into smaller forms has been committed.
Comment #22
quietone CreditAttribution: quietone as a volunteer commentedThe try catch blocks in the IS have moved to new files. As a first step to identify what needs to be done, add the new filenames to the IS.
Comment #23
quietone CreditAttribution: quietone as a volunteer commentedLet's try a reroll.
Comment #24
quietone CreditAttribution: quietone as a volunteer commentedComment #26
quietone CreditAttribution: quietone as a volunteer commentedDidn't think that was going to work. How about this.
Comment #28
quietone CreditAttribution: quietone as a volunteer commentedAnd again.
Comment #29
quietone CreditAttribution: quietone as a volunteer commentedAdd a catch for BadPluginDefinitionException to CredentialForm and a wider catch \Exception in the IncrementalForm.
Comment #31
quietone CreditAttribution: quietone as a volunteer commentedI think I uploaded a wrong patch.
Comment #32
heddnDo we still need to catch /Exception here or can we catch something else?
We need to update the comment here then. If we are catching \Exception, we are hiding all errors, not just from the database.
Comment #33
heddnComment #34
quietone CreditAttribution: quietone as a volunteer commented1. Changed to DatabaseException
2. Now that the next step for the Incremental form is the Credential form, there isn't anything to validate here. The validate function can be removed.
Comment #35
heddnThere are no more large try/catch blocks. There is no more generic catching of
\Exception
. The only thing I could find was a few unused Use statements, but that can be fixed on commit. IS seems up to date. LGTM.BTW, I did some work on this way back in #10, but things have changed dramatically since then and I don't think any of my changes from that patch are actually even in the final version. So I think that still means I can RTBC this thing.
CredentialForm:
Comment #36
quietone CreditAttribution: quietone as a volunteer commentedRemoving the two unused statements.
Comment #37
heddnComment #39
alexpottTestbot had a hiccup re-rtbcing
Comment #40
alexpottWe never have more than one item in the list as far as I can see so I think we don't need the item list anymore. Which means we can remove all the rendering.
PHPStorm reckons this is never thrown by setupMigrations. Which is interesting.
How can this be thrown? It's explicitly caught in \Drupal\migrate_drupal\MigrationConfigurationTrait::getMigrations()?
Comment #41
alexpottI think this is just a normal task. There's no bug that we're fixing explicitly here.
Comment #42
quietone CreditAttribution: quietone as a volunteer commentedPostponing on #2562845: Validate file path on Credential form which is also changing the error messages.
Comment #43
jofitz CreditAttribution: jofitz at ComputerMinds commented#2562845: Validate file path on Credential form is now in so no longer postponed.
Comment #44
jofitz CreditAttribution: jofitz at ComputerMinds commentedI'll take a look at the reroll.
Comment #45
jofitz CreditAttribution: jofitz at ComputerMinds commentedRe-rolled.
Comment #47
jofitz CreditAttribution: jofitz at ComputerMinds commentedFix test failures.
Comment #49
jofitz CreditAttribution: jofitz at ComputerMinds commentedResolve the latest test failures.
Comment #51
jofitz CreditAttribution: jofitz at ComputerMinds commentedHmm, I'm not sure I've grasped this fully. @quietone would you mind taking over?
Comment #52
quietone CreditAttribution: quietone as a volunteer commentedNot at all.
Comment #53
quietone CreditAttribution: quietone as a volunteer commented@Jo Fitzgerald, I don't know why but the migrations aren't getting the database connection.
Comment #54
quietone CreditAttribution: quietone as a volunteer commentedLet's try this.
Comment #55
quietone CreditAttribution: quietone as a volunteer commentedNow that it has passed tests, time for some cleanup and I wasn't working from head when making the previous patch. This removes the renderer service from the Credential form and adds another if block in the CredentialFrom:validateForm() for readability. At least I find it easier to skim through that code now.
Haven't run the test locally so here goes.
Comment #56
quietone CreditAttribution: quietone as a volunteer commentedAll ready for a review.
Comment #57
heddnAssigning to myself for review this week.
Comment #58
phenaproximaThis is a definite improvement. Awesome work!
Is this the exception we should catch? I know DatabaseExceptionWrapper is a thing, and it always wraps a \PDOException, so it might help to catch that instead (or its interface, DatabaseException)? Not sure what the best course of action is here.
Same here.
The key here is very hard to parse at a glance. Can we do this in two lines, and re-use $error_key elsewhere in the method:
Let's move this comment above the
if (!$this->errors)
and expand it to "Validate the Drupal version of the source database."Let's move this above the
if (!$this->errors)
.Comment #59
phenaproximaUnassigning.
Comment #60
quietone CreditAttribution: quietone as a volunteer commented@phenaproxima, Thanks for the quick review.
1 and 2. I wasn't sure either but the DatabaseExceptionWrapper doc states "This wrapper class serves only to provide additional debug information."" and that seems like a good thing. Therefor these exceptions are changed to the DatabaseExceptionWrapper.
3,4,5. Fixed.
Comment #61
phenaproximaI like it! RTBC once green on all backends.
Comment #63
catchCommitted 543a559 and pushed to 8.7.x. Thanks!
Fixed this on commit: