Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.92% covered (warning)
77.92%
60 / 77
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
WikifunctionsClientUsageUpdateJob
77.92% covered (warning)
77.92%
60 / 77
50.00% covered (danger)
50.00%
1 / 2
8.69
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 run
72.58% covered (warning)
72.58%
45 / 62
0.00% covered (danger)
0.00%
0 / 1
8.01
1<?php
2
3/**
4 * @file
5 * @ingroup Extensions
6 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
7 * @license MIT
8 */
9
10namespace MediaWiki\Extension\WikiLambda\Jobs;
11
12use MediaWiki\Config\Config;
13use MediaWiki\Extension\WikiLambda\WikifunctionsClientStore;
14use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
15use MediaWiki\JobQueue\GenericParameterJob;
16use MediaWiki\JobQueue\Job;
17use MediaWiki\Logger\LoggerFactory;
18use MediaWiki\MediaWikiServices;
19use MediaWiki\Title\Title;
20use MediaWiki\WikiMap\WikiMap;
21use Psr\Log\LoggerInterface;
22
23/**
24 * Asynchronous job to record that a page is using a function in the database,
25 * which allows us to avoid a database write on an API GET.
26 */
27class WikifunctionsClientUsageUpdateJob extends Job implements GenericParameterJob {
28
29    private LoggerInterface $logger;
30    private WikifunctionsClientStore $wikifunctionsClientStore;
31    private Config $config;
32
33    private string $targetFunction;
34    private string $targetPageText;
35    private int $targetPageNamespace;
36
37    public function __construct( array $params ) {
38        // Note: This will set $this->params, though we don't use it.
39        parent::__construct( 'wikifunctionsClientUsageUpdate', $params );
40
41        $this->targetFunction = $params['targetFunction'];
42        $this->targetPageText = $params['targetPageText'];
43        $this->targetPageNamespace = $params['targetPageNamespace'];
44
45        // Non-injected items
46        $this->logger = LoggerFactory::getInstance( 'WikiLambdaClient' );
47        $this->wikifunctionsClientStore = WikiLambdaServices::getWikifunctionsClientStore();
48
49        $this->config = MediaWikiServices::getInstance()->getMainConfig();
50
51        $this->logger->debug(
52            __CLASS__ . ' created for {targetFunction} on {targetPageNS}:{targetPage}',
53            [
54                'targetFunction' => $this->targetFunction,
55                'targetPage' => $this->targetPageText,
56                'targetPageNS' => $this->targetPageNamespace,
57            ]
58        );
59    }
60
61    /**
62     * @return bool
63     */
64    public function run() {
65        $this->logger->debug(
66            __CLASS__ . ' initiated for {targetFunction} on {targetPageNS}:{targetPage}',
67            [
68                'targetFunction' => $this->targetFunction,
69                'targetPage' => $this->targetPageText,
70                'targetPageNS' => $this->targetPageNamespace,
71            ]
72        );
73
74        if (
75            // If the wgWikiLambdaEnableClientMode flag is not set, don't try to run anything
76            !$this->config->get( 'WikiLambdaEnableClientMode' ) &&
77            // … but don't do this if the server thinks the wiki isn't configured for WikiLambda at all, which will
78            // happen when one server thinks we're configured and one doesn't (i.e., we're admist a deployment)
79            $this->config->has( 'WikiLambdaEnableClientMode' )
80        ) {
81            $this->logger->warning(
82                __CLASS__ . ' triggered for {targetFunction} on {targetPageNS}:{targetPage}; not in client mode.',
83                [
84                    'targetFunction' => $this->targetFunction,
85                    'targetPage' => $this->targetPageText,
86                    'targetPageNS' => $this->targetPageNamespace,
87                ]
88            );
89
90            // Nothing for us to do.
91            return true;
92        }
93
94        // FIXME: Don't proceed but evict from page if cache job finds that parser object doesn't have our flag? We
95        // have set it (on the PC, not Title) via $extApi->getMetadata()->setExtensionData( 'wikilambda', 'present' );
96
97        $title = Title::newFromText( $this->targetPageText, $this->targetPageNamespace );
98
99        if ( !$title ) {
100            $this->logger->warning(
101                __CLASS__ . ' got an unparseable title for {targetFunction} on {targetPageNS}:{targetPage}',
102                [
103                    'targetFunction' => $this->targetFunction,
104                    'targetPage' => $this->targetPageText,
105                    'targetPageNS' => $this->targetPageNamespace,
106                ]
107            );
108            return true;
109        }
110
111        $success = $this->wikifunctionsClientStore->insertWikifunctionsUsage(
112            $this->targetFunction,
113            $title
114        );
115
116        // Dual-write to the shared cross-wiki usage table on x1 (T390557). We resolve the
117        // page_id here, in the job, rather than at parse time: by the time the job runs the
118        // page row exists for a real save, whereas a preview of a not-yet-created page
119        // resolves to id 0 and is skipped below — so unsaved previews can't pollute the
120        // shared table. Cleanup of removed usage is handled by ClientHooks::onPageSaveComplete.
121        $pageId = $title->getId();
122        if ( $pageId > 0 ) {
123            WikiLambdaServices::getWikifunctionsUsageStore()->insertUsage(
124                $this->targetFunction,
125                WikiMap::getCurrentWikiId(),
126                $pageId,
127                $title->getNamespace(),
128                // Store null rather than the empty string for the main namespace.
129                $title->getNsText() ?: null,
130                $title->getDBkey()
131            );
132        }
133
134        if ( $success ) {
135            $this->logger->debug(
136                __CLASS__ . ' Updated usage table for {targetFunction} on {targetPageNS}:{targetPage}',
137                [
138                    'targetFunction' => $this->targetFunction,
139                    'targetPage' => $this->targetPageText,
140                    'targetPageNS' => $this->targetPageNamespace,
141                ]
142            );
143        } else {
144            $this->logger->info(
145                __CLASS__ . ' Didn\'t update usage for {targetFunction} on {targetPageNS}:{targetPage}; already there?',
146                [
147                    'targetFunction' => $this->targetFunction,
148                    'targetPage' => $this->targetPageText,
149                    'targetPageNS' => $this->targetPageNamespace,
150                ]
151            );
152
153        }
154
155        return true;
156    }
157}