Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
FlowRevisionsDb
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 10
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setAssociation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getImportedId
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 save
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 rollback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCollectionId
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
6
 getObjectRevision
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getUserTuple
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getUser
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getUUIDRange
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Flow\Import\SourceStore;
4
5use Flow\Import\IImportHeader;
6use Flow\Import\IImportObject;
7use Flow\Import\IImportPost;
8use Flow\Import\IImportSummary;
9use Flow\Import\IImportTopic;
10use Flow\Import\IObjectRevision;
11use Flow\Import\IRevisionableObject;
12use Flow\Model\UserTuple;
13use Flow\Model\UUID;
14use MediaWiki\User\User;
15use MediaWiki\Utils\MWTimestamp;
16use Wikimedia\IPUtils;
17use Wikimedia\Rdbms\IDatabase;
18use Wikimedia\Timestamp\TimestampException;
19
20/**
21 * Unlike other source stores, this doesn't really "store" anything. This just
22 * does a lookup for certain types of objects to the database to figure out if
23 * they have already been imported.
24 *
25 * This is less versatile than other source stores (you can't just throw
26 * anything at it, it's tied to a specific schema and throwing new objects at it
27 * will prompt changes in here) but it's more reliable (if the source store is
28 * lost, it can use the "result" of a previous import)
29 */
30class FlowRevisionsDb implements SourceStoreInterface {
31    /**
32     * @var IDatabase
33     */
34    protected $dbr;
35
36    /**
37     * @param IDatabase $dbr
38     */
39    public function __construct( IDatabase $dbr ) {
40        $this->dbr = $dbr;
41    }
42
43    public function setAssociation( UUID $objectId, $importSourceKey ) {
44        return '';
45    }
46
47    public function getImportedId( IImportObject $object ) {
48        if ( $object instanceof IImportHeader ) {
49            $conds = [ 'rev_type' => 'header' ];
50        } elseif ( $object instanceof IImportSummary ) {
51            $conds = [ 'rev_type' => 'post-summary' ];
52        } elseif ( $object instanceof IImportTopic ) {
53            $conds = [ 'rev_type' => 'post', 'tree_parent_id' => null ];
54        } elseif ( $object instanceof IImportPost ) {
55            $conds = [ 'rev_type' => 'post', 'tree_parent_id IS NOT NULL' ];
56        } else {
57            throw new Exception( 'Import object of type ' . get_class( $object ) . ' not supported.' );
58        }
59
60        $revision = $this->getObjectRevision( $object );
61        return $this->getCollectionId( $revision->getTimestamp(), $revision->getAuthor(), $conds );
62    }
63
64    public function save() {
65    }
66
67    public function rollback() {
68    }
69
70    /**
71     * @param string $timestamp
72     * @param string $author
73     * @param array $conds
74     * @return bool|UUID
75     * @throws Exception
76     * @throws \Wikimedia\Rdbms\DBUnexpectedError
77     * @throws \Flow\Exception\FlowException
78     * @throws \Flow\Exception\InvalidInputException
79     */
80    protected function getCollectionId( $timestamp, $author, array $conds = [] ) {
81        $range = $this->getUUIDRange( new MWTimestamp( $timestamp ) );
82        $tuple = $this->getUserTuple( $author );
83
84        // flow_revision will LEFT JOIN against flow_tree_revision, meaning that
85        // we'll also have info about the parent; or it can just be ignored if
86        // there is no parent
87        $rows = $this->dbr->select(
88            [ 'flow_revision', 'flow_tree_revision' ],
89            [ 'rev_type_id' ],
90            array_merge(
91                [
92                    'rev_type_id >= ' . $this->dbr->addQuotes( $range[0]->getBinary() ),
93                    'rev_type_id < ' . $this->dbr->addQuotes( $range[1]->getBinary() ),
94                ],
95                $tuple->toArray( 'rev_user_' ),
96                $conds
97            ),
98            __METHOD__,
99            [ 'LIMIT' => 1 ],
100            [
101                'flow_tree_revision' => [
102                    'LEFT OUTER JOIN',
103                    [ 'tree_rev_descendant_id = rev_type_id' ]
104                ],
105            ]
106        );
107
108        if ( $rows->numRows() === 0 ) {
109            return false;
110        }
111
112        return UUID::create( $rows->fetchObject()->rev_type_id );
113    }
114
115    /**
116     * @param IRevisionableObject $object
117     * @return IObjectRevision
118     */
119    protected function getObjectRevision( IRevisionableObject $object ) {
120        $revisions = $object->getRevisions();
121        $revisions->rewind();
122        return $revisions->current();
123    }
124
125    /**
126     * @param string $name
127     * @return UserTuple
128     * @throws Exception
129     */
130    protected function getUserTuple( $name ) {
131        $user = $this->getUser( $name );
132        if ( $user === false ) {
133            throw new Exception( 'Invalid author: ' . $name );
134        }
135        return UserTuple::newFromUser( $user );
136    }
137
138    /**
139     * @param string $name
140     * @return bool|User
141     */
142    protected function getUser( $name ) {
143        if ( IPUtils::isIPAddress( $name ) ) {
144            return User::newFromName( $name, false );
145        }
146
147        return User::newFromName( $name );
148    }
149
150    /**
151     * Gets the min <= ? < max boundaries for a UUID that has a given
152     * timestamp. Returns an array where [0] = min & [1] is max.
153     *
154     * @param MWTimestamp $timestamp
155     * @return UUID[] [min, max]
156     * @throws TimestampException
157     */
158    protected function getUUIDRange( MWTimestamp $timestamp ) {
159        return [
160            UUID::getComparisonUUID( (int)$timestamp->getTimestamp( TS_UNIX ) ),
161            UUID::getComparisonUUID( (int)$timestamp->getTimestamp( TS_UNIX ) + 1 ),
162        ];
163    }
164}