-
Notifications
You must be signed in to change notification settings - Fork 824
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
NEW: Add method to DB to check if a class is ready for db queries. #10041
Conversation
Some tests are failing - marking as draft until I get that fixed. |
src/Security/Security.php
Outdated
Permission::class, | ||
]; | ||
foreach ($toClear as $class) { | ||
DB::clear_database_is_ready($class); |
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 feels like a subtle, but still significant change in behaviour. When before this would completely reset the ready
state of the database, we are now only resetting the ready state of some particular classes. As such, I think this should call DB::clear_database_is_ready()
(no argument) to completely reset the state and so that it has the equivalent behaviour to before.
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 way I think of it, the only reason it completely set the "ready" state of the database is because the "ready" state only recorded classes relevant to Security anyway - there was no way to record a "ready" state for any other classes/tables.
I'll make this change if you want me to, but I think it is more logically consistent for Security::clear_database_is_ready
to only clear the ready state of classes relevant to Security
- i.e. the classes that have their ready state set by Security::database_is_ready
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 have the list of 3 classes is correct, because that does mimick the previous behaviour because there was no way that a class other than Member, Group or Permission would be added to the $database_is_ready array
However it also previously had ClassInfo::dataClassesFor(Member::class)
meaning subclasses of Member (though looks like it was assumed Group and Permission would never be subclassed, which seems fair enough, we've make a similar assumption about Folder in the assets module)
So you'll need to update this array to include all Member subclasses
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 you'll need to update this array to include all Member subclasses
DB::clearDatabaseIsReady()
has an optional $clearFullHierarchy
parameter which defaults to true, which the adds ClassInfo::dataClassesFor($class)
to the list of classes to be cleared - so subclasses of Member
are already cleared with the current implementation.
src/Security/Security.php
Outdated
Permission::class, | ||
]; | ||
foreach ($toForce as $class) { | ||
DB::force_database_is_ready($class, $isReady); |
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 with clear_database_is_ready()
- I think this is a subtle but significant behaviour change
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.
My thinking is the same as with clear_database_is_ready
- but I'll make a change if you want me to. What change would you recommend here? I don't think it would be appropriate to force DB to consider all classes as "ready".
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.
Ditto include Member subclasses
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 this is only used for tests, and those tests don't need us to check subclasses of Member
are ready, and databaseIsReady
returns true if the class passed in is in $databaseReadyClasses
(with the assumption that it has therefore already gone through the full hierarchy check), I don't think we need to add Member
subclasses here.
This is also consistent with the old check:
if (self::$force_database_is_ready !== null) {
return self::$force_database_is_ready;
}
This is the test that is failing: silverstripe-framework/tests/php/Security/SecurityTest.php Lines 691 to 709 in dc3b907
whilst I can't see exactly why it is failing, stepping through with a debugger should reveal what is going on. |
I had seen that that was the test that was failing, and as I mentioned I replicated what it was doing outside of a test context and saw the expected values returned... |
9ce74ed
to
9c8f942
Compare
Tests are passing! I've made the changes requested by @dhensby except the ones that I don't agree with - see discussion above. |
* @var boolean If set to TRUE or FALSE, {@link database_is_ready()} | ||
* will always return FALSE. Used for unit testing. | ||
*/ | ||
protected static $force_database_is_ready; |
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 an API deletion, would require a new major
Just leave it in and deprecate it. Along with $database_is_ready below
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.
For deprecating this, do you also want it to be populated when the method is called, and to affect the outcome of the checks? Or just leave the variable here unused? Same with $database_is_ready
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.
Probably best to keep them functioning as they currently are if it's not too much extra work
src/Security/Security.php
Outdated
Permission::class, | ||
]; | ||
foreach ($toClear as $class) { | ||
DB::clear_database_is_ready($class); |
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 have the list of 3 classes is correct, because that does mimick the previous behaviour because there was no way that a class other than Member, Group or Permission would be added to the $database_is_ready array
However it also previously had ClassInfo::dataClassesFor(Member::class)
meaning subclasses of Member (though looks like it was assumed Group and Permission would never be subclassed, which seems fair enough, we've make a similar assumption about Folder in the assets module)
So you'll need to update this array to include all Member subclasses
src/Security/Security.php
Outdated
Permission::class, | ||
]; | ||
foreach ($toForce as $class) { | ||
DB::force_database_is_ready($class, $isReady); |
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.
Ditto include Member subclasses
This effectively makes the functionality from Security::database_is_ready() reusable for any arbitrary DataObject subclass.
9c8f942
to
014dc19
Compare
@emteknetnz requested changes made - there's a question there about how far to go with the deprecation and more discussion to be had perhaps about including |
* @param string $class | ||
* @return boolean | ||
*/ | ||
public static function databaseIsReady(string $class): bool |
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 a bit of a misleading method name since it's for a specific dataobject, rather than the entire database. Maybe rename it to something like tablesCreated(string $class): bool
. Also update the protected static property to match
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.
That's a fair point. tablesCreated
isn't quite right, since it's checking more than just if the tables are created (e.g. the table may be created but missing columns), but you're right that the current name isn't right either. I'll think of something more accurately descriptive.
* @param string $class The class to be forced as ready/not ready. | ||
* @param boolean $isReady The value to force. | ||
*/ | ||
public static function forceDatabaseIsReady(string $class, bool $isReady) |
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 method is pretty unecessary IMO and it just clutters up the API. I think you're better off just update the visibility of the property to public via Reflection in the unit test and then updating the property directly
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.
Security::force_database_is_ready()
is a public method, which is what's called by the test. I agree that ideally the method wouldn't exist in the new location... should I replace it with reflection in Security::force_database_is_ready()
(so that method still does what it has always done)?
And then any new tests that might need that functionality can use reflection directly in the tests?
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.
Leave the existing Security
method as is and use reflection within the new unit test. That will minimise the amount of API changes
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.
nvm I realised a much cleaner way to handle this, which goes in line with #10041 (comment)
Edit: Posted this without seeing your latest reply - yup that's exactly the thinking.
* @var boolean If set to TRUE or FALSE, {@link database_is_ready()} | ||
* will always return FALSE. Used for unit testing. | ||
*/ | ||
protected static $force_database_is_ready; |
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.
Probably best to keep them functioning as they currently are if it's not too much extra work
I've realised that |
Closing this in favour of #10276 as I no longer have write access to this PR's forked repo. |
This effectively makes the functionality from Security::database_is_ready() reusable for any arbitrary DataObject subclass.
Let me know if I need to write any specific tests for this, but my assumption is that this is already being tested through the tests that target
Security
specifically.