Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.23% covered (success)
96.23%
51 / 53
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpdateGrantIdHandler
96.23% covered (success)
96.23%
51 / 53
83.33% covered (warning)
83.33%
5 / 6
13
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 validate
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
3
 getParamSettings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBodyParamSettings
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 tryUpdateGrantId
91.30% covered (success)
91.30%
21 / 23
0.00% covered (danger)
0.00%
0 / 1
6.02
1<?php
2
3declare( strict_types=1 );
4
5namespace MediaWiki\Extension\WikimediaCampaignEvents\Rest;
6
7use MediaWiki\Extension\CampaignEvents\Event\Store\IEventLookup;
8use MediaWiki\Extension\CampaignEvents\MWEntity\MWAuthorityProxy;
9use MediaWiki\Extension\CampaignEvents\Permissions\PermissionChecker;
10use MediaWiki\Extension\CampaignEvents\Rest\EventIDParamTrait;
11use MediaWiki\Extension\WikimediaCampaignEvents\Grants\Exception\FluxxRequestException;
12use MediaWiki\Extension\WikimediaCampaignEvents\Grants\Exception\InvalidGrantIDException;
13use MediaWiki\Extension\WikimediaCampaignEvents\Grants\GrantIDLookup;
14use MediaWiki\Extension\WikimediaCampaignEvents\Grants\GrantsStore;
15use MediaWiki\Rest\LocalizedHttpException;
16use MediaWiki\Rest\Response;
17use MediaWiki\Rest\SimpleHandler;
18use MediaWiki\Rest\TokenAwareHandlerTrait;
19use MediaWiki\Rest\Validator\Validator;
20use RuntimeException;
21use Wikimedia\Message\MessageValue;
22use Wikimedia\ParamValidator\ParamValidator;
23
24class UpdateGrantIdHandler extends SimpleHandler {
25    use EventIDParamTrait;
26    use TokenAwareHandlerTrait;
27
28    private IEventLookup $eventLookup;
29    private PermissionChecker $permissionChecker;
30    private GrantsStore $grantsStore;
31    private GrantIDLookup $grantIDLookup;
32
33    public function __construct(
34        IEventLookup $eventLookup,
35        GrantIDLookup $grantIDLookup,
36        PermissionChecker $permissionChecker,
37        GrantsStore $grantsStore
38    ) {
39        $this->eventLookup = $eventLookup;
40        $this->grantIDLookup = $grantIDLookup;
41        $this->permissionChecker = $permissionChecker;
42        $this->grantsStore = $grantsStore;
43    }
44
45    /**
46     * @inheritDoc
47     */
48    public function validate( Validator $restValidator ): void {
49        parent::validate( $restValidator );
50        $this->validateToken();
51    }
52
53    protected function run( int $eventID ): Response {
54        $registration = $this->getRegistrationOrThrow( $this->eventLookup, $eventID );
55
56        $body = $this->getValidatedBody();
57        $grantID = $body['grant_id'] ?? null;
58        if ( !$grantID ) {
59            throw new LocalizedHttpException(
60                MessageValue::new( 'wikimediacampaignevents-rest-grant-id-edit-empty' ),
61                400
62            );
63        }
64
65        $performer = new MWAuthorityProxy( $this->getAuthority() );
66        if ( !$this->permissionChecker->userCanEditRegistration( $performer, $registration ) ) {
67            throw new LocalizedHttpException(
68                MessageValue::new( 'wikimediacampaignevents-rest-grant-id-edit-permission-denied' ),
69                403
70            );
71        }
72
73        $this->tryUpdateGrantId( $grantID, $eventID );
74
75        return $this->getResponseFactory()->createNoContent();
76    }
77
78    /**
79     * @inheritDoc
80     */
81    public function getParamSettings(): array {
82        return $this->getIDParamSetting();
83    }
84
85    /**
86     * @inheritDoc
87     */
88    public function getBodyParamSettings(): array {
89        return [
90                'grant_id' => [
91                    ParamValidator::PARAM_REQUIRED => true,
92                    ParamValidator::PARAM_TYPE => 'string',
93                    static::PARAM_SOURCE => 'body'
94                ],
95            ] + $this->getTokenParamDefinition();
96    }
97
98    private function tryUpdateGrantId( string $grantID, int $eventID ): void {
99        // TODO Avoid duplicating EventRegistrationFormHandler.
100        $pattern = "/^\d+-\d+$/";
101        if ( !preg_match( $pattern, $grantID ) ) {
102            throw new LocalizedHttpException(
103                MessageValue::new( 'wikimediacampaignevents-rest-grant-id-edit-invalid' ),
104                400
105            );
106        }
107
108        try {
109            $this->grantIDLookup->doLookup( $grantID );
110        } catch ( InvalidGrantIDException $_ ) {
111            throw new LocalizedHttpException(
112                MessageValue::new( 'wikimediacampaignevents-rest-grant-id-edit-invalid' ),
113                400
114            );
115        } catch ( FluxxRequestException $_ ) {
116            throw new LocalizedHttpException(
117                MessageValue::new( 'wikimediacampaignevents-rest-grant-id-edit-api-error' ),
118                503
119            );
120        }
121
122        $previousGrantID = $this->grantsStore->getGrantID( $eventID );
123        if ( $grantID !== $previousGrantID ) {
124            try {
125                $grantAgreementAt = $this->grantIDLookup->getAgreementAt( $grantID );
126            } catch ( FluxxRequestException | InvalidGrantIDException $e ) {
127                throw new RuntimeException( "Could not retrieve agreement_at: $e" );
128            }
129            $this->grantsStore->updateGrantID( $grantID, $eventID, $grantAgreementAt );
130        }
131    }
132}