Passed
Pull Request — master (#188)
by Simon
08:49 queued 06:27
created

SolrConfigureTask   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Test Coverage

Coverage 95%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 53
c 6
b 0
f 0
dl 0
loc 176
ccs 38
cts 40
cp 0.95
rs 10
wmc 13

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A run() 0 26 3
A getStore() 0 9 1
A getMethod() 0 14 4
A createConfigForIndex() 0 7 1
A configureIndex() 0 23 2
A logException() 0 9 1
1
<?php
2
/**
3
 * Class SolrConfigureTask|Firesphere\SolrSearch\Tasks\SolrConfigureTask Configure Solr cores
4
 *
5
 * @package Firesphere\SolrSearch\Tasks
6
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
7
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
8
 */
9
10
namespace Firesphere\SolrSearch\Tasks;
11
12
use Exception;
13
use Firesphere\SolrSearch\Helpers\SolrLogger;
14
use Firesphere\SolrSearch\Indexes\BaseIndex;
15
use Firesphere\SolrSearch\Interfaces\ConfigStore;
16
use Firesphere\SolrSearch\Services\SolrCoreService;
17
use Firesphere\SolrSearch\Stores\FileConfigStore;
18
use Firesphere\SolrSearch\Stores\PostConfigStore;
19
use Firesphere\SolrSearch\Traits\LoggerTrait;
20
use GuzzleHttp\Exception\GuzzleException;
21
use ReflectionException;
22
use RuntimeException;
23
use SilverStripe\Control\HTTPRequest;
24
use SilverStripe\Core\Injector\Injector;
25
use SilverStripe\Dev\BuildTask;
26
use SilverStripe\ORM\ValidationException;
27
28
/**
29
 * Class SolrConfigureTask
30
 *
31
 * @package Firesphere\SolrSearch\Tasks
32
 */
33
class SolrConfigureTask extends BuildTask
34
{
35
    use LoggerTrait;
36
37
    /**
38
     * @var array Available stores
39
     */
40
    protected static $storeModes = [
41
        'file' => FileConfigStore::class,
42
        'post' => PostConfigStore::class,
43
        //        'webdav' => WebdavConfigStore::class,
44
    ];
45
    /**
46
     * @var string URLSegment
47
     */
48
    private static $segment = 'SolrConfigureTask';
49
    /**
50
     * @var string Title
51
     */
52
    protected $title = 'Configure Solr cores';
53
    /**
54
     * @var string Description
55
     */
56
    protected $description = 'Create or reload a Solr Core by adding or reloading a configuration.';
57
58
    /**
59
     * SolrConfigureTask constructor.
60
     */
61 36
    public function __construct()
62
    {
63 36
        parent::__construct();
64 36
    }
65
66
    /**
67
     * Implement this method in the task subclass to
68
     * execute via the TaskRunner
69
     *
70
     * @param HTTPRequest $request
71
     * @return bool|Exception
72
     * @throws ReflectionException
73
     * @throws ValidationException
74
     * @throws GuzzleException
75
     */
76 35
    public function run($request)
77
    {
78 35
        $this->extend('onBeforeSolrConfigureTask', $request);
79
80 35
        $indexes = (new SolrCoreService())->getValidIndexes();
81
82 35
        foreach ($indexes as $index) {
83
            try {
84 35
                $this->configureIndex($index);
85
            } catch (Exception $error) {
86
                // @codeCoverageIgnoreStart
87
                $this->getLogger()->error(sprintf('Core loading failed for %s', $index));
88
                $this->getLogger()->error($error); // in browser mode, it might not always show
89
                // Continue to the next index
90
                continue;
91
                // @codeCoverageIgnoreEnd
92
            }
93 35
            $this->extend('onAfterConfigureIndex', $index);
94
        }
95
96 35
        $this->extend('onAfterSolrConfigureTask');
97
        // Grab the latest logs
98 35
        $solrLogger = new SolrLogger();
99 35
        $solrLogger->saveSolrLog('Config');
100
101 35
        return true;
102
    }
103
104
    /**
105
     * Update the index on the given store
106
     *
107
     * @param string $index
108
     * @throws ValidationException
109
     * @throws GuzzleException
110
     */
111 35
    protected function configureIndex($index): void
112
    {
113
        /** @var BaseIndex $instance */
114 35
        $instance = Injector::inst()->get($index, false);
115
116 35
        $index = $instance->getIndexName();
117
118
        // Then tell Solr to use those config files
119
        /** @var SolrCoreService $service */
120 35
        $service = Injector::inst()->get(SolrCoreService::class);
121
122
        // Assuming a core that doesn't exist doesn't have uptime, as per Solr docs
123
        // And it has a start time.
124
        // You'd have to be pretty darn fast to hit 0 uptime and 0 starttime for an existing core!
125 35
        $configStore = $this->createConfigForIndex($instance);
126 35
        $method = $this->getMethod($index, $service);
127
        try {
128 35
            $service->$method($index, $configStore);
129 35
            $this->getLogger()->info(sprintf('Core %s successfully loaded', $index));
130
        } catch (Exception $error) {
131
            // @codeCoverageIgnoreStart
132
            $this->logException($index, $error);
133
            throw new RuntimeException($error);
134
            // @codeCoverageIgnoreEnd
135
        }
136 35
    }
137
138
    /**
139
     * Get the config and load it to Solr
140
     *
141
     * @param BaseIndex $instance
142
     * @return ConfigStore
143
     */
144 35
    protected function createConfigForIndex(BaseIndex $instance): ConfigStore
145
    {
146 35
        $storeConfig = SolrCoreService::config()->get('store');
147 35
        $configStore = $this->getStore($storeConfig);
148 35
        $instance->uploadConfig($configStore);
149
150 35
        return $configStore;
151
    }
152
153
    /**
154
     * Get the store for the given config
155
     *
156
     * @param $storeConfig
157
     * @return ConfigStore
158
     */
159 35
    protected function getStore($storeConfig): ConfigStore
160
    {
161 35
        $store = static::$storeModes[$storeConfig['mode']];
162 35
        $configStore = Injector::inst()->create($store, $storeConfig);
163
164
        // Allow changing the configStore if it needs to change to a different store
165 35
        $this->extend('onBeforeConfig', $configStore, $storeConfig);
166
167 35
        return $configStore;
168
    }
169
170
    /**
171
     * @param $index
172
     * @param SolrCoreService $service
173
     * @return string
174
     */
175 35
    protected function getMethod($index, SolrCoreService $service): string
176
    {
177 35
        $status = $service->coreStatus($index);
178
        // Default to create
179 35
        $method = 'coreCreate';
180
        // Switch to reload if the core is loaded
181
        // Assuming a core that doesn't exist doesn't have uptime, as per Solr docs
182
        // And it has a start time.
183
        // You'd have to be pretty darn fast to hit 0 uptime and 0 starttime for an existing core!
184 35
        if ($status && ($status->getUptime() && $status->getStartTime() !== null)) {
185 35
            $method = 'coreReload';
186
        }
187
188 35
        return $method;
189
    }
190
191
    /**
192
     * Log an exception error
193
     *
194
     * @codeCoverageIgnore Can't be tested because of accessibility and the actual throw of exception
195
     * @param $index
196
     * @param Exception $error
197
     * @throws GuzzleException
198
     * @throws ValidationException
199
     */
200
    private function logException($index, Exception $error): void
201
    {
202
        $this->getLogger()->error($error);
203
        $msg = sprintf(
204
            'Error loading core %s,' . PHP_EOL .
205
            'Please log in to the CMS to find out more about Configuration errors' . PHP_EOL,
206
            $index
207
        );
208
        SolrLogger::logMessage('ERROR', $msg, $index);
209
    }
210
}
211