Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
58.06% covered (warning)
58.06%
18 / 31
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
RemoteApiImportTitleChecker
58.06% covered (warning)
58.06%
18 / 31
66.67% covered (warning)
66.67%
2 / 3
6.84
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 importAllowed
38.10% covered (danger)
38.10%
8 / 21
0.00% covered (danger)
0.00%
0 / 1
5.14
 getParams
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace FileImporter\Remote\MediaWiki;
4
5use FileImporter\Data\SourceUrl;
6use FileImporter\Exceptions\HttpRequestException;
7use FileImporter\Exceptions\ImportException;
8use FileImporter\Interfaces\ImportTitleChecker;
9use FileImporter\Services\Http\HttpRequestExecutor;
10use Psr\Log\LoggerInterface;
11
12/**
13 * @license GPL-2.0-or-later
14 * @author Addshore
15 */
16class RemoteApiImportTitleChecker implements ImportTitleChecker {
17
18    private const ERROR_TITLE_STATE = 'noTitleStateFetched';
19
20    private HttpApiLookup $httpApiLookup;
21    private HttpRequestExecutor $httpRequestExecutor;
22    private LoggerInterface $logger;
23
24    public function __construct(
25        HttpApiLookup $httpApiLookup,
26        HttpRequestExecutor $httpRequestExecutor,
27        LoggerInterface $logger
28    ) {
29        $this->httpApiLookup = $httpApiLookup;
30        $this->httpRequestExecutor = $httpRequestExecutor;
31        $this->logger = $logger;
32    }
33
34    /**
35     * @param SourceUrl $sourceUrl
36     * @param string $intendedFileName File name without the File: prefix
37     *
38     * @return bool false if a file with this name already exists
39     * @throws ImportException when the request failed
40     */
41    public function importAllowed( SourceUrl $sourceUrl, string $intendedFileName ): bool {
42        $api = $this->httpApiLookup->getApiUrl( $sourceUrl );
43        $apiParameters = $this->getParams( $intendedFileName );
44
45        try {
46            $imageInfoRequest = $this->httpRequestExecutor->execute( $api, $apiParameters );
47        } catch ( HttpRequestException $e ) {
48            $this->logger->error(
49                __METHOD__ . ' failed to check title state from: ' . $api,
50                [
51                    'url' => $e->getHttpRequest()->getFinalUrl(),
52                    'content' => $e->getHttpRequest()->getContent(),
53                    'errors' => $e->getStatusValue()->getErrors(),
54                    'apiUrl' => $api,
55                    'apiParameters' => $apiParameters,
56                ]
57            );
58            throw new ImportException(
59                'Failed to check title state from: ' . $api, self::ERROR_TITLE_STATE, $e );
60        }
61
62        $requestData = json_decode( $imageInfoRequest->getContent(), true );
63
64        if ( !isset( $requestData['query']['pages'][0] ) ) {
65            $this->logger->error( __METHOD__ . ' failed, could not find page in result.' );
66            return false;
67        }
68
69        // Possible return values in output format version 2:
70        // { "query": { "pages": [ { "pageid": 123, … when the title exists
71        // { "query": { "pages": [ { "missing": true, … otherwise
72        // Note the legacy format uses { "missing": "" }, and -1 etc. as keys for missing pages.
73        return array_key_exists( 'missing', $requestData['query']['pages'][0] );
74    }
75
76    private function getParams( string $titleString ): array {
77        return [
78            'format' => 'json',
79            'action' => 'query',
80            'titles' => 'File:' . $titleString,
81            'errorformat' => 'plaintext',
82            'formatversion' => 2,
83        ];
84    }
85
86}