Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
12.00% covered (danger)
12.00%
6 / 50
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
LiftWingClient
12.00% covered (danger)
12.00%
6 / 50
33.33% covered (danger)
33.33%
2 / 6
147.57
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 get
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
90
 getBaseUrl
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHostHeader
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserAgent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createErrorResponse
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 *
17 * @file
18 */
19
20namespace AutoModerator;
21
22use MediaWiki\Json\FormatJson;
23use MediaWiki\MediaWikiServices;
24use RuntimeException;
25
26class LiftWingClient {
27
28    /** @var string */
29    private $model;
30
31    /** @var string */
32    private $lang;
33
34    /** @var string */
35    private string $baseUrl;
36
37    /** @var ?string */
38    private ?string $hostHeader;
39
40    /** @var string */
41    private string $userAgent;
42
43    public function __construct(
44        string $model,
45        string $lang,
46        string $baseUrl,
47        ?string $hostHeader = null
48    ) {
49        $this->model = $model;
50        $this->lang = $lang;
51        $this->baseUrl = $baseUrl;
52        $this->hostHeader = $hostHeader;
53        $this->userAgent = 'mediawiki.ext.AutoModerator.' . $this->lang;
54    }
55
56    /**
57     * Make a single call to LW revert risk model for one revid and return the decoded result.
58     *
59     * @param int $revId
60     *
61     * @return array Decoded response
62     */
63    public function get( $revId ) {
64        $url = $this->baseUrl . $this->model . ':predict';
65        $httpRequestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
66        $req = $httpRequestFactory->create( $url, [
67            'method' => 'POST',
68            'postData' => json_encode( [
69                'rev_id' => (int)$revId,
70                'lang' => $this->lang,
71            ] ),
72        ], __METHOD__ );
73        if ( $this->hostHeader ) {
74            $req->setHeader( 'Host', $this->hostHeader );
75        }
76        $req->setHeader( 'User-Agent', $this->userAgent );
77        $response = $req->execute();
78        if ( !$response->isOK() ) {
79            $httpStatus = $req->getStatus();
80            $data = FormatJson::decode( $req->getContent(), true );
81
82            if ( !$data ) {
83                $data = [];
84                $data['error'] = "$url returned $httpStatus for rev $revId {$this->lang}";
85            }
86
87            $errorMessage = $data['error'] ?? $data['detail'];
88            if ( ( $httpStatus >= 400 ) && ( $httpStatus <= 499 ) ) {
89                return $this->createErrorResponse( $httpStatus, $errorMessage, false );
90            } else {
91                $req = $httpRequestFactory->create( $url, [
92                    'method' => 'POST',
93                    'postData' => json_encode( [
94                        'rev_id' => (int)$revId,
95                        'lang' => $this->lang,
96                    ] ),
97                ], __METHOD__ );
98                $response = $req->execute();
99                if ( !$response->isOK() ) {
100                    return $this->createErrorResponse( $httpStatus, $errorMessage, true );
101                }
102            }
103        }
104        $json = $req->getContent();
105        $data = FormatJson::decode( $json, true );
106        if ( !$data || !empty( $data['error'] ) ) {
107            throw new RuntimeException( "Bad response from Lift Wing endpoint [{$url}]: {$json}" );
108        }
109        return $data;
110    }
111
112    /**
113     * @return string
114     */
115    public function getBaseUrl(): string {
116        return $this->baseUrl;
117    }
118
119    /**
120     * @return ?string
121     */
122    public function getHostHeader(): ?string {
123        return $this->hostHeader;
124    }
125
126    /**
127     * @return string
128     */
129    public function getUserAgent(): string {
130        return $this->userAgent;
131    }
132
133    /**
134     * @param int $httpStatus
135     * @param string $errorMessage
136     * @return array
137     */
138    public function createErrorResponse(
139        int $httpStatus,
140        string $errorMessage,
141        bool $allowRetries
142    ): array {
143        return [
144            "httpStatus" => $httpStatus,
145            "errorMessage" => $errorMessage,
146            "allowRetries" => $allowRetries
147        ];
148    }
149
150}