Skip to content
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

Fix-EZP-27373: Error when edit empty not required date #1996

Conversation

kmadejski
Copy link
Member

@kmadejski kmadejski commented May 26, 2017

JIRA issue: https://jira.ez.no/browse/EZP-27373

Description

If Date FieldType is used and Searchable setting is checked and also default value is set to Empty then it is impossible to save content without setting the value of this field.
API returns an error as described in issue.

@@ -29,12 +29,17 @@ class SearchField implements Indexable
*/
public function getIndexData(Field $field, FieldDefinition $fieldDefinition)
{
$dateTime = new DateTime("@{$field->value->data['timestamp']}");
if ($field->value->data['timestamp']) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timestamp equal to 0 is still a valid one (1 Jan 1970), though this code would not set it.
I would go for if (isset($field->value->data['timestamp']) && $field->value->data['timestamp'] !== ''), because:

  • AFAICS when not set, it is null there, so isset solves it.
  • timestamp array key might not be set at all (probably doesn't happen though).
  • Unfortunately the string '1495808222' is also a valid timestamp, but '' is not.

However I've been wrong in such cases, so pinging @andrerom and @Nattfarinn :)

Copy link
Member Author

@kmadejski kmadejski May 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alongosz You are definitely right. Thank you. I changed it as you proposed.

$dateTime = new DateTime("@{$field->value->data['timestamp']}");
$dataValue = $dateTime->format('Y-m-d\\Z');
} else {
$dataValue = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable named $dataValue looks weird. Rather $searchFieldValue or just $value.

alongosz
alongosz previously approved these changes May 26, 2017
@alongosz
Copy link
Member

@kmadejski I took a closer look at PRs and it looks like this is related to #1967 and #1992 as a follow-up on #1942.
This seems to affect DateAndTime field, so same fix for SearchField there is required.

@gggeek You've mentioned in #1942 that you'd prefer here cast to int.
Casting null to int gives us 0, so it means 1 Jan 1970. However if null gets passed here, it rather means that SearchField value should be null, thus not indexed (IMHO).
Is this ok with you?

@andrerom
Copy link
Contributor

@kmadejski See the other PR's above, there are some more cases to cover.

@kmadejski
Copy link
Member Author

@alongosz @andrerom thank you, I didn't find it before, I will investigate it more.

@alongosz
Copy link
Member

Note: one of the cases involves Value::fromTimestamp method. I wouldn't be so sure about it, because this method is designed to get int and only int. If it gets something else, like null it should throw InvalidArgument exception, which it already does.

@kmadejski
Copy link
Member Author

kmadejski commented May 29, 2017

@andrerom
Copy link
Contributor

andrerom commented May 29, 2017

@kmadejski you are right, and it's documented to only support int.

andrerom
andrerom previously approved these changes May 29, 2017
@kmadejski
Copy link
Member Author

@andrerom Thanks, I will prepare fix also for DateAndTime/SearchField.php, it seems to be the same issue there.

@andrerom
Copy link
Contributor

@kmadejski Ok, then also check Time, and make sure the originally issue fixed in #1942 is solved for all of them.

@kmadejski
Copy link
Member Author

@andrerom fixed also for Time and DateAndTime FieldTypes. I did manual tests and solution looks working as expected.

@kmadejski
Copy link
Member Author

@alongosz I changed validation in Time regarding your advice. Thank you.

Copy link
Member

@alongosz alongosz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Friday I had a feeling I've missed something important while reviewing this PR...
First of all, I wondered why our integration tests don't uncover this case. Well, it turns out we actually test content create with empty values, but not content publish, which triggers getIndexData.

So here's the test to be added to eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTest:

    /**
     * Test that publishing (and thus indexing) content with an empty field value does not fail.
     *
     * @depends testCreateContentWithEmptyFieldValue
     *
     * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft
     */
    public function testPublishContentWithEmptyFieldValue(Content $contentDraft)
    {
        $this->getRepository(false)->getContentService()->publishVersion(
            $contentDraft->versionInfo
        );
    }

Second, I looked into how exactly Field object is initialized and my change requests add unnecessary complexity, so they're wrong. Below there are correct simplifications with explanation as to why.
(@kmadejski please don't kill me... :-))

@@ -29,12 +29,17 @@ class SearchField implements Indexable
*/
public function getIndexData(Field $field, FieldDefinition $fieldDefinition)
{
$dateTime = new DateTime("@{$field->value->data['timestamp']}");
if (isset($field->value->data['timestamp']) && $field->value->data['timestamp'] !== '') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ($field->value->data !== null) is enough because:

  • DateConverter ensures that the data property remains null or is an array with timestamp key containing actual integer.
  • Content Mapper properly casts value set to dataInt retriveved from the db.

@@ -28,10 +29,17 @@ class SearchField implements Indexable
*/
public function getIndexData(Field $field, FieldDefinition $fieldDefinition)
{
if (isset($field->value->data['timestamp']) && $field->value->data['timestamp'] !== '') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ($field->value->data !== null) is enough because DateAndTime Converter ensures the same thing as above.

Copy link
Contributor

@andrerom andrerom May 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we are sure this only comes from storage and not user (via input validation and such)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we are sure this only comes from storage and not user (via input validation and such)?

Yes. getIndexData is called by eZ\Publish\SPI\Search\Handler::indexContent which always gets its content from eZ\Publish\SPI\Persistence\Content\Handler::load

@@ -28,10 +29,17 @@ class SearchField implements Indexable
*/
public function getIndexData(Field $field, FieldDefinition $fieldDefinition)
{
if ($field->value->data !== null && $field->value->data !== '') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ($field->value->data !== null) is enough because TimeConverter also ensures data remains null if not set.

@alongosz
Copy link
Member

I've also found another bug related to this line.
Basically if you set your DateTime to 1st Jan 1970, which is 0, reloading content will not set data property. Removing || $value->dataInt == 0 seems to fix it.

Not sure if this can be included here or should be a separate PR. @andrerom?

Test cases for eZ\Publish\API\Repository\Tests\FieldType\DateIntegrationTest:

    /**
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
     */
    public function testCreateContentWithZeroDate()
    {
        $fieldValue = DateValue::fromTimestamp(0);
        $content = $this->createContent($fieldValue);
        $loadedContent = $this->getRepository(false)->getContentService()->loadContentByContentInfo($content->contentInfo);
        self::assertEquals($content, $loadedContent);

        return $loadedContent->versionInfo;
    }

    /**
     * @depends testPublishContentWithZeroDate
     *
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
     */
    public function testPublishContentWithZeroDate(VersionInfo $versionInfo)
    {
        $this->getRepository(false)->getContentService()->publishVersion($versionInfo);
    }

@kmadejski
Copy link
Member Author

@alongosz don't worry, your advice are always very helpful, thank you! I'll fix it in few minutes.

@andrerom
Copy link
Contributor

andrerom commented May 30, 2017

Not sure if this can be included here or should be a separate PR. @andrerom?

lets make that separate, this is already covering to many things (regression and second look at the original issue).

Keep that small and we can fast track it and rebase this on top, if you require it you can temporarily set base branch to the new PR here.

@kmadejski kmadejski force-pushed the EZP-27373_Error_when_edit_empty_not_required_date branch from 36ec684 to 1b62c5a Compare May 30, 2017 11:36
@alongosz
Copy link
Member

And we've uncovered two separate issues...

  • InputParserPassTest::testProcess fails for Symfony 3.3, because service ids are case sensitive (or rather will be in the future). To fix this in a BC way let's change UnitTest to lowercase in this method.
  • Empty MapLocation for Elasticsearch is incorrectly mapped, so fails (hurrah, we've found actual bug!)
    Looking at ES logs it seems that the method
    eZ\Publish\Core\Search\Elasticsearch\Content\FieldValueMapper\GeoLocationMapper::map should have an extra check, like:
        if (!isset($field->value['latitude'], $field->value['longitude'])) {
            return null;
        }

These probably need to be separate PRs.

@andrerom
Copy link
Contributor

andrerom commented May 30, 2017

Looks good now, but one control question on what we ended up with here:

Same solution was provided also in Time and DateAndTime FieldTypes.

Was Time and DateAndTime actually affected by the timezone issue solved in #1942? Or could they just have been kept as they where (as in just passing on the timezone, if there is one. aka only fix would be to make sure to check that data is set in DateAndTime).

@kmadejski
Copy link
Member Author

kmadejski commented May 30, 2017

@andrerom DateAndTime wasn't affected with #1942 because there always was @ in Value::fromTimestamp, but @ was missing in SearchField::getIndexData so it can cause a similar error like this one related to this PR or returns the wrong date (without timezone) to the search index.

In Time problem described in #1942 does not exist because timestamp is passed in method DateTime::setTimestamp which is timezone aware.
There is also @ missing in SearchField::getIndexData, but in this case, I'm not sure if it is really needed. @alongosz what do you think?

Should I implement something from things mentioned by @alongosz here: #1996 (comment)?

@andrerom
Copy link
Contributor

Should I implement something from things mentioned by @alongosz here: #1996 (comment)?

Hmm, well you can, the second one is caused by tour new test, as this is depending on each other I suggest we end up with three commits that can be merged normally (not squashed):

  • commit to add testing of empty fields (several fields should fail on this commit alone it seems by the look of this PR and it's travis failures)
  • commit to fix date/time/dateAndTime where needed
  • commit to fix GeoLocationMapper::map
  • one separate PR for Symfony 3.3, @alongosz can you handle that one? (6.7 and up to unblock PR's)

@alongosz
Copy link
Member

Was Time and DateAndTime actually affected by the timezone issue solved in #1942?

It wasn't AFAIR.
If we look at the original code for DateAndTime\SearchField and Time\SearchField, integers are indexed there which makes no sense for search engine. Code itself will work, as in PHP null['whatever'] silently results in null... (@Nattfarinn remindend me of this today :-))

Anyway, I thought this would be a good improvement, but it's probably a little bit out of scope of PR. One could also argue that it's a BC break, but it's hard for me to imagine someone actually relying on unix timestamps in search index ;)

@alongosz
Copy link
Member

one separate PR for Symfony 3.3, @alongosz can you handle that one? (6.7 and up to unblock PR's)

#1998

@kmadejski kmadejski force-pushed the EZP-27373_Error_when_edit_empty_not_required_date branch from 1b62c5a to d419113 Compare May 30, 2017 14:35
@kmadejski
Copy link
Member Author

@andrerom I discussed it with @alongosz and it seems to be unnecessary in FieldTypes other than Date so right now I kept changes only there. Added commit with test and also fix for GeoLocationMapper. Is it okay?

@andrerom
Copy link
Contributor

@kmadejski Almost. 1. you have 6 commits and not 3 :) and 2. while at it rebase on 6.7 which now has fix for symfony 3.3.

@kmadejski kmadejski force-pushed the EZP-27373_Error_when_edit_empty_not_required_date branch from 86781e4 to aa5c035 Compare May 31, 2017 07:03
@kmadejski kmadejski force-pushed the EZP-27373_Error_when_edit_empty_not_required_date branch from 2ec906f to 9712e75 Compare May 31, 2017 08:57
@slaci
Copy link
Contributor

slaci commented Jun 1, 2017

Is that possible: this getIndexData is called for non searchable fields too on publish?

I have an ezdate field where searchable is 0 (I can see it in ezcontentclass_attribute table too its really 0), but publish fails with the invalidargument exception, cause the timestamp is null.

This only happens with solr, not with legacy search.

So currently ezdate cannot be optional :\

@kmadejski
Copy link
Member Author

@slaci I checked it right now, content can be published without errors when is not Searchable no matter which search engine is used. I don't know what caused your problem. Anyway, this solution might be helpful also in the case which you described.

@andrerom andrerom merged commit 89fe48c into ezsystems:6.7 Jun 1, 2017
@kmadejski kmadejski deleted the EZP-27373_Error_when_edit_empty_not_required_date branch June 20, 2018 12:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
4 participants