Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
43.33% covered (danger)
43.33%
13 / 30
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
EventSerializer
43.33% covered (danger)
43.33%
13 / 30
50.00% covered (danger)
50.00%
2 / 4
33.02
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 timestampToDt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createEvent
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 createMeta
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
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 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @author Andrew Otto <otto@wikimedia.org>
20 */
21
22namespace MediaWiki\Extension\EventBus\Serializers;
23
24use MediaWiki\WikiMap\WikiMap;
25use Wikimedia\UUID\GlobalIdGenerator;
26
27/**
28 * EventSerializer should be used to create an event array suitable
29 * for producing to WMF's Event Platform, usually via EventGate.
30 */
31class EventSerializer {
32    /**
33     * @var GlobalIdGenerator
34     */
35    private GlobalIdGenerator $globalIdGenerator;
36
37    /**
38     * @param GlobalIdGenerator $globalIdGenerator
39     */
40    public function __construct(
41        GlobalIdGenerator $globalIdGenerator,
42    ) {
43        $this->globalIdGenerator = $globalIdGenerator;
44    }
45
46    /**
47     * Format a timestamp for a date-time attribute in an event in ISO_8601 format.
48     *
49     * @param string|null $timestamp Timestamp, in a format supported by wfTimestamp(), or null for current timestamp.
50     * @return string
51     */
52    public static function timestampToDt( ?string $timestamp = null ): string {
53        return wfTimestamp( TS_ISO_8601, $timestamp );
54    }
55
56    /**
57     * Adds a meta subobject to $eventAttrs based on uri and stream.
58     *
59     * @param string $schema
60     *  Schema URI for '$schema' field.
61     *
62     * @param string $stream
63     *  Name of stream for meta.stream field.
64     *
65     * @param string $uri
66     *  Uri of this entity for meta.uri field.
67     *
68     * @param array $eventAttrs
69     *  Additional event attributes to include.
70     *
71     * @param string|null $wikiId
72     *  wikiId to use when looking up value for meta.domain.
73     *  If null, meta.domain will not be set.
74     *  NOTE: It would be better if wiki domain name was fetched and passed into createEvent,
75     *        rather than forcing createEvent to look up the domain itself.
76     *        However, this would require changing the createEvent method signature, which is used
77     *        by CirrusSearch extension.
78     *        See: https://phabricator.wikimedia.org/T392516
79     *
80     * @param bool|string|null $ingestionTimestamp
81     *  If true, meta.dt will be set to the current timestamp.
82     *  If a string, meta.dt will be set to value of timestampToDt($timestampToDt).
83     *  If null, meta.dt will not be set.
84     *  It is preferred to leave this value at null,
85     *  as EventBus produces through EventGate, which will handle setting meta.dt
86     *  to its ingestion time.
87     *  See: https://phabricator.wikimedia.org/T267648
88     *
89     * @param string|null $requestId
90     *  Will be set as meta.request_id if provided.
91     *  This should typically be obtained via Telemetry::getInstance()->getRequestId();
92     *
93     * @return array $eventAttrs + $schema + meta sub object
94     */
95    public function createEvent(
96        string $schema,
97        string $stream,
98        string $uri,
99        array $eventAttrs,
100        ?string $wikiId = null,
101        $ingestionTimestamp = null,
102        ?string $requestId = null,
103    ): array {
104        // Get canonical wiki domain 'display name' via wikiId.
105        // If WikiMap::getWiki is null, then we can't get a domain (and are likely in a unit test).
106        // In that case, don't provide a domain name.
107        $wikiDomainName = null;
108        if ( $wikiId !== null ) {
109            $wikiReference = WikiMap::getWiki( $wikiId );
110            $wikiDomainName = $wikiReference ? $wikiReference->getDisplayName() : null;
111        }
112
113        $metaDt = null;
114        if ( $ingestionTimestamp === true ) {
115            $metaDt = self::timestampToDt();
116        } elseif ( $ingestionTimestamp !== null ) {
117            $metaDt = self::timestampToDt( $ingestionTimestamp );
118        }
119        return array_merge(
120            $eventAttrs,
121            [
122                '$schema' => $schema,
123                'meta' => $this->createMeta( $stream, $uri, $wikiDomainName, $metaDt, $requestId )
124            ]
125        );
126    }
127
128    /**
129     * Creates the meta subobject that should be common for all events.
130     *
131     * @param string $stream
132     * @param string $uri
133     * @param string|null $domain
134     *    If null, 'domain' will not be set.
135     * @param string|null $dt
136     *    If null, 'dt' will not be set.
137     * @param string|null $requestId
138     *    If null, 'request_id' will not be set.
139     * @return array
140     */
141    private function createMeta(
142        string $stream,
143        string $uri,
144        ?string $domain = null,
145        ?string $dt = null,
146        ?string $requestId = null,
147    ): array {
148        $metaAttrs = [
149            'stream' => $stream,
150            'uri' => $uri,
151            'id' => $this->globalIdGenerator->newUUIDv4(),
152        ];
153
154        if ( $domain !== null ) {
155            $metaAttrs['domain'] = $domain;
156        }
157
158        if ( $dt !== null ) {
159            $metaAttrs['dt'] = $dt;
160        }
161
162        if ( $requestId !== null ) {
163            $metaAttrs['request_id'] = $requestId;
164        }
165
166        return $metaAttrs;
167    }
168}