Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
GrantIDLookup
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
7 / 7
9
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 doLookup
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAgreementAt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGrantData
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 requestGrantData
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
3
 getColsParam
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getFiltersParam
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare( strict_types=1 );
4
5namespace MediaWiki\Extension\WikimediaCampaignEvents\Grants;
6
7use MediaWiki\Extension\WikimediaCampaignEvents\Grants\Exception\FluxxRequestException;
8use MediaWiki\Extension\WikimediaCampaignEvents\Grants\Exception\InvalidGrantIDException;
9use StatusValue;
10use Wikimedia\ObjectCache\WANObjectCache;
11
12/**
13 * This class is responsible for looking up information about grant IDs (e.g., whether they exist, when they were
14 * granted, etc.).
15 */
16class GrantIDLookup {
17    public const SERVICE_NAME = 'WikimediaCampaignEventsGrantIDLookup';
18
19    private const ENDPOINT = 'grant_request/list';
20    private const GRANTS_FILTER_PERIOD_DAYS = 730;
21
22    private FluxxClient $fluxxClient;
23    private WANObjectCache $cache;
24
25    public function __construct(
26        FluxxClient $fluxxClient,
27        WANObjectCache $cache
28    ) {
29        $this->fluxxClient = $fluxxClient;
30        $this->cache = $cache;
31    }
32
33    /**
34     * @param string $grantID
35     * @return StatusValue Always good
36     * @throws InvalidGrantIDException
37     * @throws FluxxRequestException
38     */
39    public function doLookup( string $grantID ): StatusValue {
40        $this->getGrantData( $grantID );
41        return StatusValue::newGood();
42    }
43
44    /**
45     * @param string $grantID
46     * @return string The agreement_at timestamp
47     * @throws InvalidGrantIDException
48     * @throws FluxxRequestException
49     */
50    public function getAgreementAt( string $grantID ): string {
51        return $this->getGrantData( $grantID )['grant_agreement_at'];
52    }
53
54    /**
55     * @param string $grantID
56     * @return array
57     * @throws FluxxRequestException
58     * @throws InvalidGrantIDException
59     */
60    private function getGrantData( string $grantID ): array {
61        return $this->cache->getWithSetCallback(
62            $this->cache->makeKey( 'WikimediaCampaignEvents-GrantData', $grantID ),
63            WANObjectCache::TTL_HOUR,
64            function () use ( $grantID, &$grantStatus )  {
65                // TODO Cache failures due to invalid grant ID, but NOT network issues
66                return $this->requestGrantData( $grantID );
67            },
68            [ 'pcTTL' => WANObjectCache::TTL_PROC_LONG ]
69        );
70    }
71
72    /**
73     * @param string $grantID
74     * @return array
75     * @throws FluxxRequestException
76     * @throws InvalidGrantIDException
77     */
78    private function requestGrantData( string $grantID ): array {
79        $cols = $this->getColsParam();
80        $filters = $this->getFiltersParam( $grantID );
81        $postData = [
82            'cols' => json_encode( $cols ),
83            'filter' => json_encode( $filters ),
84        ];
85
86        $responseData = $this->fluxxClient->makePostRequest( self::ENDPOINT, $postData );
87        $grant = $responseData[ 'records' ][ 'grant_request' ][ 0 ] ?? null;
88
89        if ( $grant !== null && $grant[ 'base_request_id' ] === $grantID ) {
90            return [
91                'grant_agreement_at' => wfTimestamp(
92                    TS_MW,
93                    $grant[ 'grant_agreement_at' ]
94                )
95            ];
96        }
97
98        throw new InvalidGrantIDException();
99    }
100
101    /**
102     * @return array
103     */
104    private function getColsParam(): array {
105        return [
106            "granted",
107            "request_received_at",
108            "base_request_id",
109            "grant_agreement_at",
110        ];
111    }
112
113    /**
114     * @param string $grantID
115     * @return array
116     */
117    private function getFiltersParam( string $grantID ): array {
118        return [
119            "group_type" => "and",
120            "conditions" => [
121                [ "base_request_id", "eq", $grantID ],
122                [ "granted", "eq", true ],
123                // last-n-months does not include the current month T367465
124                [ "grant_agreement_at", "last-n-days", self::GRANTS_FILTER_PERIOD_DAYS ],
125            ],
126        ];
127    }
128}