Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.18% covered (success)
91.18%
31 / 34
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
IndexTemplateBuilder
91.18% covered (success)
91.18%
31 / 34
50.00% covered (danger)
50.00%
3 / 6
9.06
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 build
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 execute
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
3.00
 getTemplateName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSearchConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createIndexTemplate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace CirrusSearch\Maintenance;
4
5use CirrusSearch\Connection;
6use CirrusSearch\SearchConfig;
7use Elastica\IndexTemplate;
8use Wikimedia\Assert\Assert;
9
10class IndexTemplateBuilder {
11    /**
12     * @var array
13     */
14    private $templateDefinition;
15
16    /**
17     * @var string
18     */
19    private $templateName;
20
21    /**
22     * @var string[]
23     */
24    private $availablePlugins;
25
26    /**
27     * @var Connection
28     */
29    private $connection;
30
31    /**
32     * @var string
33     */
34    private $languageCode;
35
36    /**
37     * @param Connection $connection
38     * @param string $templateName
39     * @param array $templateDefinition
40     * @param string[] $availablePlugins
41     * @param string $languageCode
42     */
43    public function __construct(
44        Connection $connection,
45        $templateName,
46        array $templateDefinition,
47        array $availablePlugins,
48        $languageCode
49    ) {
50        Assert::parameter( isset( $templateDefinition['mappings']['properties'] ), '$templateDefinition',
51            'Mapping types are no longer supported, properties must be top level in mappings' );
52        $this->connection = $connection;
53        $this->templateName = $templateName;
54        $this->templateDefinition = $templateDefinition;
55        $this->availablePlugins = $availablePlugins;
56        $this->languageCode = $languageCode;
57    }
58
59    /**
60     * @param Connection $connection
61     * @param array $templateDefinition
62     * @param string[] $availablePlugins
63     * @return self
64     * @throws \InvalidArgumentException
65     */
66    public static function build(
67        Connection $connection,
68        array $templateDefinition,
69        array $availablePlugins
70    ): self {
71        $templateName = $templateDefinition['template_name'] ?? null;
72        $langCode = $templateDefinition['language_code'] ?? 'int';
73        if ( $templateName === null ) {
74            throw new \InvalidArgumentException( "Missing template name in profile." );
75        }
76        unset( $templateDefinition['template_name'] );
77        unset( $templateDefinition['language_code'] );
78        return new self( $connection, $templateName, $templateDefinition, $availablePlugins, $langCode );
79    }
80
81    public function execute() {
82        $indexTemplate = $this->createIndexTemplate();
83        $analysisConfigBuilder = new AnalysisConfigBuilder( $this->languageCode, $this->availablePlugins, $this->getSearchConfig() );
84        $filter = new AnalysisFilter();
85        [ $analysis, $mappings ] = $filter->filterAnalysis( $analysisConfigBuilder->buildConfig(),
86            $this->templateDefinition['mappings'], true );
87        $templateDefinition = array_merge_recursive( $this->templateDefinition, [ 'settings' => [ 'analysis' => $analysis ] ] );
88        $templateDefinition['mappings'] = $mappings;
89        $response = $indexTemplate->create( $templateDefinition );
90        if ( !$response->isOk() ) {
91            $message = $response->getErrorMessage();
92            if ( $message ) {
93                $message = 'Received HTTP ' . $response->getStatus();
94            }
95            throw new \RuntimeException( "Cannot add template {$this->templateName}$message" );
96        }
97    }
98
99    /**
100     * @return string
101     */
102    public function getTemplateName() {
103        return $this->templateName;
104    }
105
106    private function getSearchConfig(): SearchConfig {
107        return $this->connection->getConfig();
108    }
109
110    private function createIndexTemplate(): IndexTemplate {
111        // Can go back to plain IndexTemplate when upgrading to Elastica 7
112        return new class( $this->connection->getClient(), $this->templateName ) extends IndexTemplate {
113            /** @inheritDoc */
114            public function request( $method, $data = [], array $query = [] ) {
115                $path = '_template/' . $this->getName();
116                return $this->getClient()->request( $path, $method, $data, $query );
117            }
118
119            /** @inheritDoc */
120            public function create( array $args = [] ) {
121                return $this->request( \Elastica\Request::PUT, $args );
122            }
123        };
124    }
125}