Passed
Push — primary ( 4876b7...460939 )
by Simon
08:40
created

SolrConfigureTask::getStore()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 8
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Class SolrConfigureTask|Firesphere\SolrSearch\Tasks\SolrConfigureTask Configure Solr cores
4
 *
5
 * @package Firesphere\Solr\Search
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\Solr\Search
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 37
    public function __construct()
63
    {
64 37
        parent::__construct();
65 37
    }
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 GuzzleException
74
     * @throws InvalidArgumentException
75
     * @throws ValidationException
76
     */
77 36
    public function run($request)
78
    {
79
        /** @var CacheInterface $cache */
80 36
        $cache = Injector::inst()->get(CacheInterface::class . '.SolrCache');
81 36
        $cache->delete('ValidClasses');
82 36
        $this->extend('onBeforeSolrConfigureTask', $request);
83
84 36
        $indexes = (new SolrCoreService())->getValidIndexes();
85
86 36
        foreach ($indexes as $index) {
87
            try {
88 36
                $this->configureIndex($index);
89
            } catch (Exception $error) {
90
                // @codeCoverageIgnoreStart
91
                $this->logException($index, $error);
92
                $this->getLogger()->error(sprintf('Core loading failed for %s', $index));
93
                $this->getLogger()->error($error->getMessage()); // in browser mode, it might not always show
94
                // Continue to the next index
95
                continue;
96
                // @codeCoverageIgnoreEnd
97
            }
98 36
            $this->extend('onAfterConfigureIndex', $index);
99
        }
100
101 36
        $this->extend('onAfterSolrConfigureTask');
102
        // Grab the latest logs
103 36
        $solrLogger = new SolrLogger();
104 36
        $solrLogger->saveSolrLog('Config');
105 36
    }
106
107
    /**
108
     * Update the index on the given store
109
     *
110
     * @param string $index Core to index
111
     */
112 36
    protected function configureIndex($index): void
113
    {
114
        /** @var BaseIndex $instance */
115 36
        $instance = Injector::inst()->get($index, false);
116
117 36
        $index = $instance->getIndexName();
118
119
        // Then tell Solr to use those config files
120
        /** @var SolrCoreService $service */
121 36
        $service = Injector::inst()->get(SolrCoreService::class);
122
123 36
        $configStore = $this->createConfigForIndex($instance);
124 36
        $method = $this->getMethod($index, $service);
125 36
        $service->$method($index, $configStore);
126 36
        $this->getLogger()->info(sprintf('Core %s successfully loaded', $index));
127 36
    }
128
129
    /**
130
     * Get the config and load it to Solr
131
     *
132
     * @param BaseIndex $instance
133
     * @return ConfigStore
134
     */
135 36
    protected function createConfigForIndex(BaseIndex $instance): ConfigStore
136
    {
137 36
        $storeConfig = SolrCoreService::config()->get('store');
138 36
        $configStore = $this->getStore($storeConfig);
139 36
        $instance->uploadConfig($configStore);
140
141 36
        return $configStore;
142
    }
143
144
    /**
145
     * Get the store for the given config
146
     *
147
     * @param $storeConfig
148
     * @return ConfigStore
149
     */
150 36
    protected function getStore($storeConfig): ConfigStore
151
    {
152 36
        $configStore = Injector::inst()->create(ConfigStore::class, $storeConfig);
153
154
        // Allow changing the configStore if it needs to change to a different store
155 36
        $this->extend('onBeforeConfig', $configStore, $storeConfig);
156
157 36
        return $configStore;
158
    }
159
160
    /**
161
     * Figure out the method needed for the given core.
162
     *
163
     * @param $index
164
     * @param SolrCoreService $service
165
     * @return string
166
     */
167 36
    protected function getMethod($index, SolrCoreService $service): string
168
    {
169 36
        $status = $service->coreStatus($index);
170
        // Default to create
171 36
        $method = 'coreCreate';
172
        // Switch to reload if the core is loaded
173
        // Assuming a core that doesn't exist doesn't have uptime, as per Solr docs
174
        // And it has a start time.
175
        // You'd have to be pretty darn fast to hit 0 uptime and 0 starttime for an existing core!
176 36
        if ($status && ($status->getUptime() && $status->getStartTime() !== null)) {
177 36
            $method = 'coreReload';
178
        }
179
180 36
        return $method;
181
    }
182
183
    /**
184
     * Log an exception error
185
     *
186
     * @codeCoverageIgnore Can't be tested because of accessibility and the actual throw of exception
187
     * @param string $index Name of the index
188
     * @param Exception $error
189
     * @throws GuzzleException
190
     * @throws ValidationException
191
     */
192
    private function logException($index, Exception $error): void
193
    {
194
        $this->getLogger()->error($error);
195
        $msg = sprintf(
196
            'Error loading core %s%s, Please log in to the CMS to find out more about Configuration errors %s',
197
            $index,
198
            PHP_EOL,
199
            PHP_EOL
200
        );
201
        SolrLogger::logMessage('ERROR', $msg);
202
    }
203
}
204