Skip to content

Commit 379c455

Browse files
committed
feat: finish elasticsearch manager
1 parent c3693d8 commit 379c455

6 files changed

Lines changed: 335 additions & 8 deletions

File tree

src/Components/Elasticsearch/ElasticsearchManager.php

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,45 @@
22

33
namespace Frosh\Tools\Components\Elasticsearch;
44

5+
use Doctrine\DBAL\Connection;
56
use Elasticsearch\Client;
7+
use Shopware\Core\Framework\Increment\Exception\IncrementGatewayNotFoundException;
8+
use Shopware\Core\Framework\Increment\IncrementGatewayRegistry;
9+
use Shopware\Elasticsearch\Framework\ElasticsearchOutdatedIndexDetector;
10+
use Shopware\Elasticsearch\Framework\Indexing\CreateAliasTaskHandler;
11+
use Shopware\Elasticsearch\Framework\Indexing\ElasticsearchIndexer;
12+
use Shopware\Elasticsearch\Framework\Indexing\ElasticsearchIndexingMessage;
13+
use Symfony\Component\Messenger\MessageBusInterface;
614

715
class ElasticsearchManager
816
{
17+
protected ElasticsearchIndexer $indexer;
18+
protected MessageBusInterface $messageBus;
19+
protected CreateAliasTaskHandler $createAliasTaskHandler;
20+
protected ElasticsearchOutdatedIndexDetector $outdatedIndexDetector;
21+
protected Connection $connection;
22+
protected IncrementGatewayRegistry $gatewayRegistry;
923
private Client $client;
1024
private bool $enabled;
1125

12-
public function __construct(Client $client, bool $enabled)
13-
{
26+
public function __construct(
27+
Client $client,
28+
bool $enabled,
29+
ElasticsearchIndexer $indexer,
30+
MessageBusInterface $messageBus,
31+
CreateAliasTaskHandler $createAliasTaskHandler,
32+
ElasticsearchOutdatedIndexDetector $outdatedIndexDetector,
33+
Connection $connection,
34+
IncrementGatewayRegistry $gatewayRegistry
35+
) {
1436
$this->client = $client;
1537
$this->enabled = $enabled;
38+
$this->indexer = $indexer;
39+
$this->messageBus = $messageBus;
40+
$this->createAliasTaskHandler = $createAliasTaskHandler;
41+
$this->outdatedIndexDetector = $outdatedIndexDetector;
42+
$this->connection = $connection;
43+
$this->gatewayRegistry = $gatewayRegistry;
1644
}
1745

1846
public function isEnabled(): bool
@@ -53,4 +81,59 @@ public function deleteIndex(string $name): array
5381
{
5482
return $this->client->indices()->delete(['index' => $name]);
5583
}
84+
85+
public function proxy(string $method, string $path, array $params, array $body): array
86+
{
87+
$response = $this->client->transport->performRequest($method, $path, $params, $body);
88+
89+
return $this->client->transport->resultOrFuture($response);
90+
}
91+
92+
public function flushAll(): void
93+
{
94+
$this->client->indices()->flushSynced();
95+
}
96+
97+
public function reindex(): void
98+
{
99+
$offset = null;
100+
while ($message = $this->indexer->iterate($offset)) {
101+
$this->messageBus->dispatch($message);
102+
$offset = $message->getOffset();
103+
}
104+
}
105+
106+
public function switchAlias(): void
107+
{
108+
$this->createAliasTaskHandler->run();
109+
}
110+
111+
public function deleteUnusedIndices(): void
112+
{
113+
$indices = $this->outdatedIndexDetector->get();
114+
115+
foreach ($indices as $index) {
116+
$this->client->indices()->delete(['index' => $index]);
117+
}
118+
}
119+
120+
public function reset(): void
121+
{
122+
$indices = $this->outdatedIndexDetector->getAllUsedIndices();
123+
124+
foreach ($indices as $index) {
125+
$this->client->indices()->delete(['index' => $index]);
126+
}
127+
128+
$this->connection->executeStatement('TRUNCATE elasticsearch_index_task');
129+
130+
try {
131+
$gateway = $this->gatewayRegistry->get(IncrementGatewayRegistry::MESSAGE_QUEUE_POOL);
132+
$gateway->reset('message_queue_stats', ElasticsearchIndexingMessage::class);
133+
} catch (IncrementGatewayNotFoundException $exception) {
134+
// In case message_queue pool is disabled
135+
}
136+
137+
$this->connection->executeStatement('DELETE FROM enqueue WHERE body LIKE "%ElasticsearchIndexingMessage%"');
138+
}
56139
}

src/Controller/ElasticsearchController.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Frosh\Tools\Components\Elasticsearch\ElasticsearchManager;
66
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
77
use Symfony\Component\HttpFoundation\JsonResponse;
8+
use Symfony\Component\HttpFoundation\Request;
89
use Symfony\Component\HttpFoundation\Response;
910
use Symfony\Component\Routing\Annotation\Route;
1011

@@ -56,4 +57,68 @@ public function deleteIndex(string $indexName): Response
5657

5758
return new JsonResponse($this->manager->deleteIndex($indexName));
5859
}
60+
61+
/**
62+
* @Route(path="/console/{path}", name="api.frosh.tools.elasticsearch.proxy", requirements={"path" = ".*"})
63+
*/
64+
public function console(Request $request, string $path): Response
65+
{
66+
if (!$this->manager->isEnabled()) {
67+
return new Response('', Response::HTTP_PRECONDITION_FAILED);
68+
}
69+
70+
$data = $this->manager->proxy($request->getMethod(), '/' . $path, $request->query->all(), $request->request->all());
71+
72+
return new JsonResponse($data);
73+
}
74+
75+
/**
76+
* @Route(path="/flush_all", methods={"POST"}, name="api.frosh.tools.elasticsearch.flush")
77+
*/
78+
public function flushAll(): Response
79+
{
80+
$this->manager->flushAll();
81+
82+
return new Response('', Response::HTTP_NO_CONTENT);
83+
}
84+
85+
/**
86+
* @Route(path="/reindex", methods={"POST"}, name="api.frosh.tools.elasticsearch.reindex")
87+
*/
88+
public function reindex(): Response
89+
{
90+
$this->manager->reindex();
91+
92+
return new Response('', Response::HTTP_NO_CONTENT);
93+
}
94+
95+
/**
96+
* @Route(path="/switch_alias", methods={"POST"}, name="api.frosh.tools.elasticsearch.switch_alias")
97+
*/
98+
public function switchAlias(): Response
99+
{
100+
$this->manager->switchAlias();
101+
102+
return new Response('', Response::HTTP_NO_CONTENT);
103+
}
104+
105+
/**
106+
* @Route(path="/cleanup", methods={"POST"}, name="api.frosh.tools.elasticsearch.cleanup")
107+
*/
108+
public function deleteUnusedIndices(): Response
109+
{
110+
$this->manager->deleteUnusedIndices();
111+
112+
return new Response('', Response::HTTP_NO_CONTENT);
113+
}
114+
115+
/**
116+
* @Route(path="/reset", methods={"POST"}, name="api.frosh.tools.elasticsearch.reset")
117+
*/
118+
public function reset(): Response
119+
{
120+
$this->manager->reset();
121+
122+
return new Response('', Response::HTTP_NO_CONTENT);
123+
}
59124
}

src/Resources/app/administration/src/api/elasticsearch.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,88 @@ class Elasticsearch extends ApiService {
4040
return ApiService.handleResponse(response);
4141
});
4242
}
43+
44+
console(method, path, payload) {
45+
const apiRoute = `${this.getApiBasePath()}/console` + path;
46+
return this.httpClient.request(
47+
{
48+
url: apiRoute,
49+
method: method,
50+
headers: {
51+
...this.getBasicHeaders(),
52+
'content-type': 'application/json',
53+
},
54+
data: payload,
55+
},
56+
).then((response) => {
57+
return ApiService.handleResponse(response);
58+
});
59+
}
60+
61+
flushAll() {
62+
const apiRoute = `${this.getApiBasePath()}/flush_all`;
63+
return this.httpClient.post(
64+
apiRoute,
65+
{},
66+
{
67+
headers: this.getBasicHeaders()
68+
}
69+
).then((response) => {
70+
return ApiService.handleResponse(response);
71+
});
72+
}
73+
74+
reindex() {
75+
const apiRoute = `${this.getApiBasePath()}/reindex`;
76+
return this.httpClient.post(
77+
apiRoute,
78+
{},
79+
{
80+
headers: this.getBasicHeaders()
81+
}
82+
).then((response) => {
83+
return ApiService.handleResponse(response);
84+
});
85+
}
86+
87+
switchAlias() {
88+
const apiRoute = `${this.getApiBasePath()}/switch_alias`;
89+
return this.httpClient.post(
90+
apiRoute,
91+
{},
92+
{
93+
headers: this.getBasicHeaders()
94+
}
95+
).then((response) => {
96+
return ApiService.handleResponse(response);
97+
});
98+
}
99+
100+
cleanup() {
101+
const apiRoute = `${this.getApiBasePath()}/cleanup`;
102+
return this.httpClient.post(
103+
apiRoute,
104+
{},
105+
{
106+
headers: this.getBasicHeaders()
107+
}
108+
).then((response) => {
109+
return ApiService.handleResponse(response);
110+
});
111+
}
112+
113+
reset() {
114+
const apiRoute = `${this.getApiBasePath()}/reset`;
115+
return this.httpClient.post(
116+
apiRoute,
117+
{},
118+
{
119+
headers: this.getBasicHeaders()
120+
}
121+
).then((response) => {
122+
return ApiService.handleResponse(response);
123+
});
124+
}
43125
}
44126

45127
export default Elasticsearch;

src/Resources/app/administration/src/module/frosh-tools/component/frosh-tools-tab-elasticsearch/index.js

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import template from './template.twig';
22

3-
const { Component } = Shopware;
3+
const { Mixin, Component } = Shopware;
44

55
Component.register('frosh-tools-tab-elasticsearch', {
66
template,
77

88
inject: ['froshElasticSearch'],
9+
mixins: [
10+
Mixin.getByName('notification')
11+
],
912

1013
data() {
1114
return {
1215
isLoading: true,
1316
isActive: true,
1417
statusInfo: {},
1518
indices: [],
16-
consoleInput: 'GET /_cat/indices'
19+
consoleInput: 'GET /_cat/indices',
20+
consoleOutput: {},
1721
};
1822
},
1923

@@ -87,6 +91,74 @@ Component.register('frosh-tools-tab-elasticsearch', {
8791
async deleteIndex(indexName) {
8892
await this.froshElasticSearch.deleteIndex(indexName);
8993
await this.createdComponent();
94+
},
95+
96+
async onConsoleEnter() {
97+
const lines = this.consoleInput.split("\n")
98+
const requestLine = lines.shift();
99+
const payload = lines.join("\n").trim();
100+
const [method, uri] = requestLine.split(" ");
101+
102+
try {
103+
this.consoleOutput = await this.froshElasticSearch.console(method, uri, payload);
104+
} catch (e) {
105+
this.consoleOutput = e.response.data
106+
}
107+
},
108+
109+
async reindex() {
110+
await this.froshElasticSearch.reindex();
111+
112+
this.createNotificationSuccess({
113+
message: this.$tc('global.default.success')
114+
}
115+
);
116+
117+
await this.createdComponent()
118+
},
119+
120+
async switchAlias() {
121+
await this.froshElasticSearch.switchAlias();
122+
123+
this.createNotificationSuccess({
124+
message: this.$tc('global.default.success')
125+
}
126+
);
127+
128+
await this.createdComponent()
129+
},
130+
131+
async flushAll() {
132+
await this.froshElasticSearch.flushAll();
133+
134+
this.createNotificationSuccess({
135+
message: this.$tc('global.default.success')
136+
}
137+
);
138+
139+
await this.createdComponent()
140+
},
141+
142+
async resetElasticsearch() {
143+
await this.froshElasticSearch.reset();
144+
145+
this.createNotificationSuccess({
146+
message: this.$tc('global.default.success')
147+
}
148+
);
149+
150+
await this.createdComponent()
151+
},
152+
153+
async cleanup() {
154+
await this.froshElasticSearch.cleanup();
155+
156+
this.createNotificationSuccess({
157+
message: this.$tc('global.default.success')
158+
}
159+
);
160+
161+
await this.createdComponent()
90162
}
91163
}
92164
})

0 commit comments

Comments
 (0)