Passed
Pull Request — main (#50)
by Simon
01:09
created

DataObjectElasticExtension   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 44
dl 0
loc 125
rs 10
c 3
b 1
f 0
wmc 16

6 Methods

Rating   Name   Duplication   Size   Complexity  
A pushToElastic() 0 16 3
A onAfterWrite() 0 13 6
A getDeleteQuery() 0 8 1
A onAfterDelete() 0 4 1
A executeQuery() 0 16 2
A deleteFromElastic() 0 11 3
1
<?php
2
/**
3
 * class DataObjectExtension|Firesphere\ElasticSearch\Extensions\DataObjectExtension Adds checking if changes should be
4
 * pushed to Elastic
5
 *
6
 * @package Firesphere\Elastic\Search
7
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
8
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
9
 */
10
11
namespace Firesphere\ElasticSearch\Extensions;
12
13
use Elastic\Elasticsearch\Exception\ClientResponseException;
14
use Elastic\Elasticsearch\Exception\ServerResponseException;
15
use Elastic\Elasticsearch\Response\Elasticsearch;
16
use Exception;
17
use Firesphere\ElasticSearch\Indexes\ElasticIndex;
18
use Firesphere\ElasticSearch\Services\ElasticCoreService;
19
use Http\Promise\Promise;
20
use Psr\Container\NotFoundExceptionInterface;
21
use Psr\Log\LoggerInterface;
22
use SilverStripe\Core\Injector\Injector;
23
use SilverStripe\ORM\ArrayList;
24
use SilverStripe\ORM\DataExtension;
25
use SilverStripe\ORM\DataObject;
26
use SilverStripe\ORM\ValidationException;
27
use SilverStripe\Versioned\Versioned;
28
29
/**
30
 * Class \Firesphere\ElasticSearch\Extensions\DataObjectElasticExtension
31
 *
32
 * @property DataObject|DataObjectElasticExtension $owner
33
 */
34
class DataObjectElasticExtension extends DataExtension
35
{
36
    /**
37
     * @throws NotFoundExceptionInterface
38
     * @throws ValidationException
39
     */
40
    public function onAfterDelete()
41
    {
42
        parent::onAfterDelete();
43
        $this->deleteFromElastic();
44
    }
45
46
    /**
47
     * Can be called directly, if a DataObject needs to be removed
48
     * immediately.
49
     * @return void
50
     * @throws NotFoundExceptionInterface
51
     */
52
    public function deleteFromElastic(): void
53
    {
54
        $service = new ElasticCoreService();
55
        $indexes = $service->getValidIndexes();
56
        foreach ($indexes as $index) {
57
            /** @var ElasticIndex $idx */
58
            $idx = Injector::inst()->get($index);
59
            $config = ElasticIndex::config()->get($idx->getIndexName());
60
            if (in_array($this->owner->ClassName, $config['Classes'])) {
1 ignored issue
show
Bug Best Practice introduced by
The property ClassName does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. Did you maybe forget to declare it?
Loading history...
61
                $deleteQuery = $this->getDeleteQuery($index);
62
                $this->executeQuery($service, $deleteQuery);
63
            }
64
        }
65
    }
66
67
    /**
68
     * @param mixed $index
69
     * @return array
70
     */
71
    private function getDeleteQuery(mixed $index): array
72
    {
73
        return [
74
            'index' => $index,
75
            'body'  => [
76
                'query' => [
77
                    'match' => [
78
                        'id' => sprintf('%s-%s', $this->owner->ClassName, $this->owner->ID)
2 ignored issues
show
Bug introduced by
The property ID does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. Did you mean IDs?
Loading history...
Bug Best Practice introduced by
The property ClassName does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. Did you maybe forget to declare it?
Loading history...
79
                    ]
80
                ]
81
            ]
82
        ];
83
    }
84
85
    /**
86
     * @param ElasticCoreService $service
87
     * @param array $deleteQuery
88
     * @return Elasticsearch|Promise|bool
89
     * @throws NotFoundExceptionInterface
90
     */
91
    protected function executeQuery(ElasticCoreService $service, array $deleteQuery)
92
    {
93
        try {
94
            return $service->getClient()->deleteByQuery($deleteQuery);
95
        } catch (Exception $e) {
96
            // DirtyClass handling is a DataObject Search Core extension
97
            $dirty = $this->owner->getDirtyClass('DELETE');
0 ignored issues
show
Bug introduced by
The method getDirtyClass() does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

97
            /** @scrutinizer ignore-call */ 
98
            $dirty = $this->owner->getDirtyClass('DELETE');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
98
            $ids = json_decode($dirty->IDs ?? '[]');
99
            $ids[] = $this->owner->ID;
1 ignored issue
show
Bug introduced by
The property ID does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. Did you mean IDs?
Loading history...
100
            $dirty->IDs = json_encode($ids);
101
            $dirty->write();
102
            /** @var LoggerInterface $logger */
103
            $logger = Injector::inst()->get(LoggerInterface::class);
104
            $logger->error($e->getMessage(), $e->getTrace());
105
106
            return false;
107
        }
108
    }
109
110
    /**
111
     * Reindex after write, if it's an indexed new/updated object
112
     * @throws ClientResponseException
113
     * @throws NotFoundExceptionInterface
114
     * @throws ServerResponseException
115
     */
116
    public function onAfterWrite()
117
    {
118
        parent::onAfterWrite();
119
        if (
120
            !$this->owner->hasExtension(Versioned::class) ||
0 ignored issues
show
Bug introduced by
The method hasExtension() does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

120
            !$this->owner->/** @scrutinizer ignore-call */ hasExtension(Versioned::class) ||

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
121
            ($this->owner->hasExtension(Versioned::class) && $this->owner->isPublished())
0 ignored issues
show
Bug introduced by
The method isPublished() does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
            ($this->owner->hasExtension(Versioned::class) && $this->owner->/** @scrutinizer ignore-call */ isPublished())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
122
        ) {
123
            $this->pushToElastic();
124
        }
125
126
        // @codeCoverageIgnoreStart Elastic during tests isn't fast enough to pick this up properly
127
        if ($this->owner->isChanged('ShowInSearch') && !$this->owner->ShowInSearch) {
2 ignored issues
show
Bug Best Practice introduced by
The property ShowInSearch does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. Did you maybe forget to declare it?
Loading history...
Bug introduced by
The method isChanged() does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
        if ($this->owner->/** @scrutinizer ignore-call */ isChanged('ShowInSearch') && !$this->owner->ShowInSearch) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
128
            $this->deleteFromElastic();
129
        }
130
        // @codeCoverageIgnoreEnd
131
    }
132
133
    /**
134
     * This is a separate method from the delete action, as it's a different route
135
     * and query components.
136
     * It can be called to add an object to the index immediately, without
137
     * requiring a write.
138
     * @return array|void|bool
139
     * @throws ClientResponseException
140
     * @throws NotFoundExceptionInterface
141
     * @throws ServerResponseException
142
     */
143
    public function pushToElastic()
144
    {
145
        $list = ArrayList::create();
146
        $list->push($this->owner);
147
        /** @var ElasticCoreService $service */
148
        $service = Injector::inst()->get(ElasticCoreService::class);
149
        foreach ($service->getValidIndexes() as $indexStr) {
150
            /** @var ElasticIndex $index */
151
            $index = Injector::inst()->get($indexStr);
152
            $idxConfig = ElasticIndex::config()->get($index->getIndexName());
153
            if (in_array($this->owner->ClassName, $idxConfig['Classes'])) {
1 ignored issue
show
Bug Best Practice introduced by
The property ClassName does not exist on Firesphere\ElasticSearch...aObjectElasticExtension. Did you maybe forget to declare it?
Loading history...
154
                $result = $service->updateIndex($index, $list);
155
            }
156
        }
157
158
        return $result ?? false;
159
    }
160
}
161