Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiInvalidatePersonalizedPraiseSuggestion
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 6
156
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
 execute
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
56
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isWriteMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isInternal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GrowthExperiments\Api;
4
5use GrowthExperiments\EventLogging\PersonalizedPraiseLogger;
6use GrowthExperiments\MentorDashboard\PersonalizedPraise\PraiseworthyMenteeSuggester;
7use GrowthExperiments\Mentorship\Provider\MentorProvider;
8use GrowthExperiments\Mentorship\Store\MentorStore;
9use MediaWiki\Api\ApiBase;
10use MediaWiki\Api\ApiMain;
11use MediaWiki\Logger\LoggerFactory;
12use MediaWiki\ParamValidator\TypeDef\UserDef;
13use MediaWiki\User\UserIdentity;
14use Wikimedia\ParamValidator\ParamValidator;
15
16class ApiInvalidatePersonalizedPraiseSuggestion extends ApiBase {
17
18    private MentorProvider $mentorProvider;
19    private MentorStore $mentorStore;
20    private PraiseworthyMenteeSuggester $praiseworthyMenteeSuggester;
21
22    private PersonalizedPraiseLogger $personalizedPraiseLogger;
23
24    public function __construct(
25        ApiMain $mainModule,
26        string $moduleName,
27        MentorProvider $mentorProvider,
28        MentorStore $mentorStore,
29        PraiseworthyMenteeSuggester $praiseworthyMenteeSuggester,
30        PersonalizedPraiseLogger $personalizedPraiseLogger
31    ) {
32        parent::__construct( $mainModule, $moduleName );
33
34        $this->mentorProvider = $mentorProvider;
35        $this->mentorStore = $mentorStore;
36        $this->praiseworthyMenteeSuggester = $praiseworthyMenteeSuggester;
37        $this->personalizedPraiseLogger = $personalizedPraiseLogger;
38    }
39
40    /** @inheritDoc */
41    public function execute() {
42        if ( !$this->mentorProvider->isMentor( $this->getUser() ) ) {
43            $this->dieWithError( [ 'apierror-permissiondenied-generic' ] );
44        }
45
46        $params = $this->extractRequestParams();
47        /** @var UserIdentity $mentee */
48        $mentee = $params['mentee'];
49
50        if ( $params['reason'] === PersonalizedPraiseLogger::ACTION_PRAISED ) {
51            $this->praiseworthyMenteeSuggester->markMenteeAsPraised( $mentee );
52        } elseif ( $params['reason'] === PersonalizedPraiseLogger::ACTION_SKIPPED ) {
53            $this->praiseworthyMenteeSuggester->markMenteeAsSkipped( $mentee );
54        }
55
56        $this->getResult()->addValue( null, $this->getModuleName(), [
57            'status' => 'ok',
58            'mentee' => $mentee->getName(),
59        ] );
60
61        $mentor = $this->mentorStore->loadMentorUser( $mentee, MentorStore::ROLE_PRIMARY );
62        if ( !$mentor ) {
63            LoggerFactory::getInstance( 'GrowthExperiments' )->warning(
64                'ApiInvalidatePersonalizedPraiseSuggestion failed to load mentor for {mentee}',
65                [ 'mentee' => $mentee->getName() ]
66            );
67            return;
68        }
69
70        if ( $params['reason'] === PersonalizedPraiseLogger::ACTION_PRAISED ) {
71            $this->personalizedPraiseLogger->logPraised(
72                $mentor,
73                $mentee
74            );
75        } elseif ( $params['reason'] === PersonalizedPraiseLogger::ACTION_SKIPPED ) {
76            $this->personalizedPraiseLogger->logSkipped(
77                $mentor,
78                $mentee,
79                $params['skipreason']
80            );
81        }
82    }
83
84    public function needsToken() {
85        return 'csrf';
86    }
87
88    /** @inheritDoc */
89    public function isWriteMode() {
90        return true;
91    }
92
93    /** @inheritDoc */
94    public function isInternal() {
95        return true;
96    }
97
98    /** @inheritDoc */
99    protected function getAllowedParams() {
100        return [
101            'mentee' => [
102                ParamValidator::PARAM_REQUIRED => true,
103                ParamValidator::PARAM_TYPE => 'user',
104                UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id' ],
105                UserDef::PARAM_RETURN_OBJECT => true,
106            ],
107            'reason' => [
108                ParamValidator::PARAM_TYPE => [
109                    'skipped',
110                    'praised'
111                ],
112            ],
113            'skipreason' => [
114                // NOTE: Keep in sync with SkipMenteeDialog.vue's reasonItems
115                ParamValidator::PARAM_TYPE => [
116                    'already-praised',
117                    'not-praiseworthy',
118                    'not-now',
119                    'other'
120                ],
121            ],
122        ];
123    }
124}