Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConfigDump
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 11
600
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
56
 addGlobals
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 ensureAssociative
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 addConcreteNamespaceMap
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 addReplicaGroup
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 addProfiles
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 addUserTesting
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 addExpectedIndices
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 isInternal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace CirrusSearch\Api;
4
5use CirrusSearch\Maintenance\ExpectedIndicesBuilder;
6use CirrusSearch\Profile\SearchProfileService;
7use CirrusSearch\SearchConfig;
8use CirrusSearch\UserTestingEngine;
9use MediaWiki\Api\ApiBase;
10use MediaWiki\Api\ApiResult;
11use MediaWiki\MediaWikiServices;
12use Wikimedia\ParamValidator\ParamValidator;
13
14/**
15 * Dumps CirrusSearch configuration for easy viewing.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 * http://www.gnu.org/copyleft/gpl.html
31 */
32class ConfigDump extends ApiBase {
33    use ApiTrait;
34
35    /** @var string[] */
36    public static $PUBLICLY_SHAREABLE_CONFIG_VARS = [
37        'CirrusSearchDisableUpdate',
38        'CirrusSearchConnectionAttempts',
39        'CirrusSearchSlowSearch',
40        'CirrusSearchUseExperimentalHighlighter',
41        'CirrusSearchOptimizeIndexForExperimentalHighlighter',
42        'CirrusSearchNamespaceMappings',
43        'CirrusSearchExtraIndexes',
44        'CirrusSearchExtraIndexClusters',
45        'CirrusSearchFetchConfigFromApi',
46        'CirrusSearchUpdateShardTimeout',
47        'CirrusSearchClientSideUpdateTimeout',
48        'CirrusSearchSearchShardTimeout',
49        'CirrusSearchClientSizeSearchTimeout',
50        'CirrusSearchMaintenanceTimeout',
51        'CirrusSearchPrefixSearchStartsWithAnyWord',
52        'CirrusSearchPhraseSlop',
53        'CirrusSearchPhraseRescoreBoost',
54        'CirrusSearchPhraseRescoreWindowSize',
55        'CirrusSearchFunctionRescoreWindowSize',
56        'CirrusSearchMoreAccurateScoringMode',
57        'CirrusSearchPhraseSuggestUseText',
58        'CirrusSearchPhraseSuggestUseOpeningText',
59        'CirrusSearchIndexedRedirects',
60        'CirrusSearchLinkedArticlesToUpdate',
61        'CirrusSearchUnlikedArticlesToUpdate',
62        'CirrusSearchWeights',
63        'CirrusSearchBoostOpening',
64        'CirrusSearchNearMatchWeight',
65        'CirrusSearchStemmedWeight',
66        'CirrusSearchNamespaceWeights',
67        'CirrusSearchDefaultNamespaceWeight',
68        'CirrusSearchTalkNamespaceWeight',
69        'CirrusSearchLanguageWeight',
70        'CirrusSearchPreferRecentDefaultDecayPortion',
71        'CirrusSearchPreferRecentUnspecifiedDecayPortion',
72        'CirrusSearchPreferRecentDefaultHalfLife',
73        'CirrusSearchMoreLikeThisConfig',
74        'CirrusSearchInterwikiSources',
75        'CirrusSearchRefreshInterval',
76        'CirrusSearchFragmentSize',
77        'CirrusSearchIndexAllocation',
78        'CirrusSearchFullTextQueryBuilderProfile',
79        'CirrusSearchRescoreProfile',
80        'CirrusSearchPrefixSearchRescoreProfile',
81        'CirrusSearchSimilarityProfile',
82        'CirrusSearchCrossProjectProfiles',
83        'CirrusSearchCrossProjectOrder',
84        'CirrusSearchCrossProjectSearchBlockList',
85        'CirrusSearchExtraIndexBoostTemplates',
86        'CirrusSearchEnableCrossProjectSearch',
87        'CirrusSearchEnableAltLanguage',
88        'CirrusSearchEnableArchive',
89        'CirrusSearchUseIcuFolding',
90        'CirrusSearchUseIcuTokenizer',
91        'CirrusSearchPhraseSuggestProfiles',
92        'CirrusSearchCrossProjectBlockScorerProfiles',
93        'CirrusSearchSimilarityProfiles',
94        'CirrusSearchRescoreFunctionChains',
95        'CirrusSearchCompletionProfiles',
96        'CirrusSearchCompletionSettings',
97        'CirrusSearchCompletionSuggesterUseDefaultSort',
98        // All the config below was added when moving this data
99        // from CirrusSearch config to a static array in this class
100        'CirrusSearchDevelOptions',
101        'CirrusSearchPrefixIds',
102        'CirrusSearchMoreLikeThisFields',
103        'CirrusSearchMoreLikeThisTTL',
104        'CirrusSearchFiletypeAliases',
105        'CirrusSearchDefaultCluster',
106        'CirrusSearchClientSideConnectTimeout',
107        'CirrusSearchReplicaGroup',
108        'CirrusSearchExtraBackendLatency',
109        'CirrusSearchAllowLeadingWildcard',
110        'CirrusSearchClientSideSearchTimeout',
111        'CirrusSearchStripQuestionMarks',
112        'CirrusSearchFullTextQueryBuilderProfiles',
113        'CirrusSearchEnableRegex',
114        'CirrusSearchWikimediaExtraPlugin',
115        'CirrusSearchRegexMaxDeterminizedStates',
116        'CirrusSearchMaxIncategoryOptions',
117        'CirrusSearchEnablePhraseSuggest',
118        'CirrusSearchClusterOverrides',
119        'CirrusSearchRescoreProfiles',
120        'CirrusSearchRescoreFunctionScoreChains',
121        'CirrusSearchNumCrossProjectSearchResults',
122        'CirrusSearchLanguageToWikiMap',
123        'CirrusSearchWikiToNameMap',
124        'CirrusSearchIncLinksAloneW',
125        'CirrusSearchIncLinksAloneK',
126        'CirrusSearchIncLinksAloneA',
127        'CirrusSearchNewCrossProjectPage',
128        'CirrusSearchQueryStringMaxDeterminizedStates',
129        'CirrusSearchElasticQuirks',
130        'CirrusSearchPhraseSuggestMaxErrors',
131        'CirrusSearchPhraseSuggestReverseField',
132        'CirrusSearchBoostTemplates',
133        'CirrusSearchIgnoreOnWikiBoostTemplates',
134        'CirrusSearchIndexBaseName',
135        'CirrusSearchInterleaveConfig',
136        'CirrusSearchMaxPhraseTokens',
137        'LanguageCode',
138        'ContentNamespaces',
139        'NamespacesToBeSearchedDefault',
140        'CirrusSearchCategoryDepth',
141        'CirrusSearchCategoryMax',
142        'CirrusSearchCategoryEndpoint',
143        'CirrusSearchFallbackProfile',
144        'CirrusSearchFallbackProfiles',
145    ];
146
147    public function execute() {
148        $result = $this->getResult();
149        $props = array_flip( $this->extractRequestParams()[ 'prop' ] );
150        if ( isset( $props['globals'] ) ) {
151            $this->addGlobals( $result );
152        }
153        if ( isset( $props['namespacemap'] ) ) {
154            $this->addConcreteNamespaceMap( $result );
155        }
156        if ( isset( $props['profiles'] ) ) {
157            $this->addProfiles( $result );
158        }
159        if ( isset( $props['replicagroup'] ) ) {
160            $this->addReplicaGroup( $result );
161        }
162        if ( isset( $props['usertesting'] ) ) {
163            $this->addUserTesting( $result );
164        }
165        if ( isset( $props['expectedindices'] ) ) {
166            $this->addExpectedIndices( $result );
167        }
168    }
169
170    protected function addGlobals( ApiResult $result ): void {
171        $config = $this->getConfig();
172        foreach ( self::$PUBLICLY_SHAREABLE_CONFIG_VARS as $key ) {
173            if ( $config->has( $key ) ) {
174                $result->addValue( null, $key, $config->get( $key ) );
175            }
176        }
177    }
178
179    /**
180     * When encoding to json when an array is constructed starting
181     * from zero and adding only sequential keys it will be emit
182     * as a list, instead of a map. Re-order the array so it doesn't
183     * start at zero, unless it's a single element list.
184     *
185     * This does not solve the single element list problem, but in
186     * practice the use case always has multiple values.
187     *
188     * @param array $items
189     * @return array associative array version of source if 2+ elements exist.
190     */
191    private function ensureAssociative( array $items ): array {
192        if ( isset( $items[0] ) ) {
193            $value = $items[0];
194            unset( $items[0] );
195            $items[0] = $value;
196        }
197        return $items;
198    }
199
200    /**
201     * Include a complete mapping from namespace id to index containing pages.
202     *
203     * Intended for external services/users that need to interact
204     * with elasticsearch or cirrussearch dumps directly.
205     *
206     * @param ApiResult $result Impl to write results to
207     */
208    private function addConcreteNamespaceMap( ApiResult $result ) {
209        $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
210        $conn = $this->getCirrusConnection();
211        $indexBaseName = $conn->getConfig()->get( SearchConfig::INDEX_BASE_NAME );
212        $items = [];
213        foreach ( $nsInfo->getValidNamespaces() as $ns ) {
214            $indexSuffix = $conn->getIndexSuffixForNamespace( $ns );
215            $indexName = $conn->getIndexName( $indexBaseName, $indexSuffix );
216            $items[$ns] = $indexName;
217        }
218        foreach ( self::ensureAssociative( $items ) as $ns => $indexName ) {
219            $result->addValue( 'CirrusSearchConcreteNamespaceMap', $ns, $indexName );
220        }
221    }
222
223    private function addReplicaGroup( ApiResult $result ) {
224        $result->addValue( null, 'CirrusSearchConcreteReplicaGroup',
225            $this->getCirrusConnection()->getConfig()->getClusterAssignment()->getCrossClusterName() );
226    }
227
228    /**
229     * Profile names and types
230     * @var string[]
231     */
232    private static $PROFILES = [
233        'CirrusSearchPhraseSuggestProfiles' => SearchProfileService::PHRASE_SUGGESTER,
234        'CirrusSearchCrossProjectBlockScorerProfiles' => SearchProfileService::CROSS_PROJECT_BLOCK_SCORER,
235        'CirrusSearchSimilarityProfiles' => SearchProfileService::SIMILARITY,
236        'CirrusSearchRescoreFunctionChains' => SearchProfileService::RESCORE_FUNCTION_CHAINS,
237        'CirrusSearchCompletionProfiles' => SearchProfileService::COMPLETION,
238        'CirrusSearchFullTextQueryBuilderProfiles' => SearchProfileService::FT_QUERY_BUILDER,
239        'CirrusSearchRescoreProfiles' => SearchProfileService::RESCORE,
240    ];
241
242    /**
243     * Add data from profiles
244     */
245    private function addProfiles( ApiResult $result ) {
246        $config = new SearchConfig();
247        $profileService = $config->getProfileService();
248        foreach ( self::$PROFILES as $var => $profileType ) {
249            $data = $profileService->listExposedProfiles( $profileType );
250            $result->addValue( null, $var, $data, ApiResult::OVERRIDE );
251        }
252    }
253
254    /**
255     * @param ApiResult $result
256     * @return void
257     * @throws \CirrusSearch\NoActiveTestException
258     */
259    protected function addUserTesting( ApiResult $result ): void {
260        // UserTesting only automatically assigns test buckets during web requests.
261        // This api call is different from a typical search request though, this is
262        // used from non-search pages to find out what bucket to provide to a new
263        // autocomplete session.
264        $engine = UserTestingEngine::fromConfig( $this->getConfig() );
265        $status = $engine->decideTestByAutoenroll();
266        $result->addValue( null, 'CirrusSearchActiveUserTest',
267            $status->isActive() ? $status->getTrigger() : '' );
268    }
269
270    /**
271     * @param ApiResult $result
272     * @return void
273     */
274    protected function addExpectedIndices( ApiResult $result ): void {
275        $builder = new ExpectedIndicesBuilder( $this->getSearchConfig() );
276        $result->addValue( null, 'CirrusSearchExpectedIndices',
277            $builder->build( false, null ) );
278    }
279
280    /** @inheritDoc */
281    public function getAllowedParams() {
282        return [
283            'prop' => [
284                ParamValidator::PARAM_DEFAULT => 'globals|namespacemap|profiles|replicagroup',
285                ParamValidator::PARAM_TYPE => [
286                    'globals',
287                    'namespacemap',
288                    'profiles',
289                    'replicagroup',
290                    'usertesting',
291                    'expectedindices'
292                ],
293                ParamValidator::PARAM_ISMULTI => true,
294            ],
295        ];
296    }
297
298    /**
299     * Mark as internal. This isn't meant to be used by normal api users
300     * @return bool
301     */
302    public function isInternal() {
303        return true;
304    }
305
306    /**
307     * @see ApiBase::getExamplesMessages
308     * @return array
309     */
310    protected function getExamplesMessages() {
311        return [
312            'action=cirrus-config-dump' =>
313                'apihelp-cirrus-config-dump-example'
314        ];
315    }
316
317}