Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
LinkRecommendationHelper
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 4
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getLinkRecommendation
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 deleteLinkRecommendation
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 configError
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GrowthExperiments\NewcomerTasks\AddLink;
4
5use GrowthExperiments\ErrorException;
6use GrowthExperiments\NewcomerTasks\ConfigurationLoader\ConfigurationLoader;
7use GrowthExperiments\NewcomerTasks\TaskType\LinkRecommendationTaskType;
8use GrowthExperiments\NewcomerTasks\TaskType\LinkRecommendationTaskTypeHandler;
9use GrowthExperiments\Util;
10use GrowthExperiments\WikiConfigException;
11use MediaWiki\Deferred\DeferredUpdates;
12use MediaWiki\Linker\LinkTarget;
13use MediaWiki\Page\ProperPageIdentity;
14use StatusValue;
15use Wikimedia\Rdbms\IDBAccessObject;
16
17/**
18 * Shared functionality for various classes that consume link recommendations.
19 */
20class LinkRecommendationHelper {
21
22    /** @var ConfigurationLoader */
23    private $configurationLoader;
24
25    /** @var LinkRecommendationProvider */
26    private $linkRecommendationProvider;
27
28    /** @var LinkRecommendationStore */
29    private $linkRecommendationStore;
30
31    /**
32     * @var callable returning {@link \CirrusSearch\WeightedTagsUpdater}
33     */
34    private $weightedTagsUpdaterProvider;
35
36    /**
37     * @param ConfigurationLoader $configurationLoader
38     * @param LinkRecommendationProvider $linkRecommendationProvider
39     * @param LinkRecommendationStore $linkRecommendationStore
40     * @param callable(): \CirrusSearch\WeightedTagsUpdater $weightedTagsUpdaterProvider
41     */
42    public function __construct(
43        ConfigurationLoader $configurationLoader,
44        LinkRecommendationProvider $linkRecommendationProvider,
45        LinkRecommendationStore $linkRecommendationStore,
46        callable $weightedTagsUpdaterProvider
47    ) {
48        $this->configurationLoader = $configurationLoader;
49        $this->linkRecommendationProvider = $linkRecommendationProvider;
50        $this->linkRecommendationStore = $linkRecommendationStore;
51        $this->weightedTagsUpdaterProvider = $weightedTagsUpdaterProvider;
52    }
53
54    /**
55     * Return the link recommendation stored for the given title.
56     * Returns null when all links of the recommendation are invalid.
57     * @param LinkTarget $title
58     * @return LinkRecommendation|null
59     * @throws ErrorException
60     */
61    public function getLinkRecommendation( LinkTarget $title ): ?LinkRecommendation {
62        $taskTypeId = LinkRecommendationTaskTypeHandler::TASK_TYPE_ID;
63        $taskType = $this->configurationLoader->getTaskTypes()[$taskTypeId] ?? null;
64        if ( !$taskType ) {
65            throw $this->configError( "No such task type: $taskTypeId" );
66        } elseif ( !$taskType instanceof LinkRecommendationTaskType ) {
67            throw $this->configError( "Not a link recommendation task type: $taskTypeId" );
68        }
69        $linkRecommendation = $this->linkRecommendationProvider->get( $title, $taskType );
70        if ( $linkRecommendation instanceof StatusValue ) {
71            throw new ErrorException( $linkRecommendation );
72        }
73        return $linkRecommendation;
74    }
75
76    /**
77     * Delete recommendations for a given page from the database and optionally from the search index.
78     * @param ProperPageIdentity $pageIdentity
79     * @param bool $deleteFromSearchIndex
80     * @param bool $allowJoiningSearchIndexDeletes if true, then CirrusSearch will join this with
81     *                                             other events from the revision, waiting for up to 10 minutes
82     */
83    public function deleteLinkRecommendation(
84        ProperPageIdentity $pageIdentity,
85        bool $deleteFromSearchIndex,
86        bool $allowJoiningSearchIndexDeletes = false
87    ): void {
88        if (
89            $this->linkRecommendationStore->getByPageId( $pageIdentity->getId(), IDBAccessObject::READ_NORMAL ) !== null
90        ) {
91            DeferredUpdates::addCallableUpdate( function () use ( $pageIdentity ) {
92                $this->linkRecommendationStore->deleteByPageIds( [ $pageIdentity->getId() ] );
93            } );
94        }
95        if ( $deleteFromSearchIndex ) {
96            DeferredUpdates::addCallableUpdate( function () use ( $pageIdentity, $allowJoiningSearchIndexDeletes ) {
97                ( $this->weightedTagsUpdaterProvider )()->resetWeightedTags(
98                    $pageIdentity,
99                    [ LinkRecommendationTaskTypeHandler::WEIGHTED_TAG_PREFIX ],
100                    $allowJoiningSearchIndexDeletes ? 'revision' : null
101                );
102            } );
103        }
104    }
105
106    /**
107     * @param string $errorMessage
108     * @return ErrorException
109     */
110    private function configError( string $errorMessage ): ErrorException {
111        Util::logException( new WikiConfigException( $errorMessage ) );
112        return new ErrorException( StatusValue::newFatal( 'rawmessage', $errorMessage ) );
113    }
114
115}