Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
35.14% covered (danger)
35.14%
13 / 37
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
35.14% covered (danger)
35.14%
13 / 37
0.00% covered (danger)
0.00%
0 / 4
31.11
0.00% covered (danger)
0.00%
0 / 1
 newInstance
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 onGetStreamConfigs
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
3.00
 onBeforePageDisplay
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
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 MediaWiki\Extension\MetricsPlatform;
21
22use MediaWiki\Config\Config;
23use MediaWiki\Config\ServiceOptions;
24use MediaWiki\Extension\EventStreamConfig\Hooks\GetStreamConfigsHook;
25use MediaWiki\MediaWikiServices;
26use MediaWiki\Output\Hook\BeforePageDisplayHook;
27use MediaWiki\Output\OutputPage;
28use Skin;
29
30class Hooks implements
31    GetStreamConfigsHook,
32    BeforePageDisplayHook
33{
34    public const CONSTRUCTOR_OPTIONS = [
35        'MetricsPlatformEnableStreamConfigsMerging',
36        'MetricsPlatformEnableExperimentOverrides',
37    ];
38
39    /** @var string */
40    public const PRODUCT_METRICS_WEB_BASE_SCHEMA_TITLE = 'analytics/product_metrics/web/base';
41
42    /** @var string */
43    public const PRODUCT_METRICS_STREAM_PREFIX = 'product_metrics.';
44
45    /** @var string */
46    public const PRODUCT_METRICS_DESTINATION_EVENT_SERVICE = 'eventgate-analytics-external';
47
48    private InstrumentConfigsFetcher $configsFetcher;
49    private ExperimentManagerFactory $experimentManagerFactory;
50    private ServiceOptions $options;
51
52    public static function newInstance(
53        InstrumentConfigsFetcher $configsFetcher,
54        ExperimentManagerFactory $experimentManagerFactory,
55        Config $config
56    ): self {
57        return new self(
58            $configsFetcher,
59            $experimentManagerFactory,
60            new ServiceOptions( self::CONSTRUCTOR_OPTIONS, $config )
61        );
62    }
63
64    public function __construct(
65        InstrumentConfigsFetcher $configsFetcher,
66        ExperimentManagerFactory $experimentManagerFactory,
67        ServiceOptions $options
68    ) {
69        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
70        $this->configsFetcher = $configsFetcher;
71        $this->experimentManagerFactory = $experimentManagerFactory;
72        $this->options = $options;
73    }
74
75    /**
76     * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetStreamConfigs
77     * @param array &$streamConfigs
78     */
79    public function onGetStreamConfigs( array &$streamConfigs ): void {
80        if ( !$this->options->get( 'MetricsPlatformEnableStreamConfigsMerging' ) ) {
81            return;
82        }
83
84        $instrumentConfigs = $this->configsFetcher->getInstrumentConfigs();
85
86        foreach ( $instrumentConfigs as $value ) {
87            $streamConfigs[ $value['stream_name'] ] = [
88                'schema_title' => self::PRODUCT_METRICS_WEB_BASE_SCHEMA_TITLE,
89                'producers' => [
90                    'metrics_platform_client' => [
91                        'provide_values' => $value['contextual_attributes']
92                    ]
93                ],
94                'sample' => $value['sample'],
95                'destination_event_service' => self::PRODUCT_METRICS_DESTINATION_EVENT_SERVICE,
96            ];
97        }
98    }
99
100    /**
101     * This hook adds a JavaScript configuration variable to the output.
102     *
103     * In order to provide experiment enrollment data with bucketing assignments
104     * for a logged-in user, we take the user's id to deterministically sample and
105     * bucket the user. Based on the sample rates of active experiments, the user's
106     * participation in experimentation cohorts is written to a configuration variable
107     * that will be read by the Metrics Platform client libraries and instrument code
108     * to send that data along during events submission.
109     *
110     * @param OutputPage $out
111     * @param Skin $skin
112     */
113    public function onBeforePageDisplay( $out, $skin ): void {
114        // Skip if the user is not logged in or is a temporary user.
115        if ( !$out->getUser()->isNamed() ) {
116            return;
117        }
118        $services = MediaWikiServices::getInstance();
119
120        // Get the user's central ID (for assigning buckets later) and skip if 0.
121        $lookup = $services->getCentralIdLookupFactory()->getLookup();
122        $userId = $lookup->centralIdFromLocalUser( $out->getUser() );
123        if ( $userId === 0 ) {
124            return;
125        }
126
127        $experimentManager = $this->experimentManagerFactory->newInstance();
128
129        // Set the JS config variable for the user's experiment enrollment data.
130        $out->addJsConfigVars(
131            'wgMetricsPlatformUserExperiments',
132            $experimentManager->enrollUser( $out->getUser(), $out->getRequest() )
133        );
134
135        // Optimization:
136        //
137        // The `ext.metricsPlatform` module only contains a QA-related function right now. Only send the module to the
138        // browser when we allow experiment enrollment overrides.
139        if ( $this->options->get( 'MetricsPlatformEnableExperimentOverrides' ) ) {
140            $out->addModules( 'ext.metricsPlatform' );
141        }
142    }
143}