Passed
Push — sheepy/elevation-configuration ( f8fadb...77a4b0 )
by Marco
07:42
created

SolrConfigureTask::getMethod()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

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