Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 47
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 / 47
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 / 18
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\IReadableDatabase;
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 IReadableDatabase
33     */
34    protected $dbr;
35
36    public function __construct( IReadableDatabase $dbr ) {
37        $this->dbr = $dbr;
38    }
39
40    public function setAssociation( UUID $objectId, $importSourceKey ) {
41        return '';
42    }
43
44    public function getImportedId( IImportObject $object ) {
45        if ( $object instanceof IImportHeader ) {
46            $conds = [ 'rev_type' => 'header' ];
47        } elseif ( $object instanceof IImportSummary ) {
48            $conds = [ 'rev_type' => 'post-summary' ];
49        } elseif ( $object instanceof IImportTopic ) {
50            $conds = [ 'rev_type' => 'post', 'tree_parent_id' => null ];
51        } elseif ( $object instanceof IImportPost ) {
52            $conds = [ 'rev_type' => 'post', $this->dbr->expr( 'tree_parent_id', '!=', null ) ];
53        } else {
54            throw new Exception( 'Import object of type ' . get_class( $object ) . ' not supported.' );
55        }
56
57        $revision = $this->getObjectRevision( $object );
58        return $this->getCollectionId( $revision->getTimestamp(), $revision->getAuthor(), $conds );
59    }
60
61    public function save() {
62    }
63
64    public function rollback() {
65    }
66
67    /**
68     * @param string $timestamp
69     * @param string $author
70     * @param array $conds
71     * @return bool|UUID
72     * @throws Exception
73     * @throws \Wikimedia\Rdbms\DBUnexpectedError
74     * @throws \Flow\Exception\FlowException
75     * @throws \Flow\Exception\InvalidInputException
76     */
77    protected function getCollectionId( $timestamp, $author, array $conds = [] ) {
78        $range = $this->getUUIDRange( new MWTimestamp( $timestamp ) );
79        $tuple = $this->getUserTuple( $author );
80
81        // flow_revision will LEFT JOIN against flow_tree_revision, meaning that
82        // we'll also have info about the parent; or it can just be ignored if
83        // there is no parent
84        $rev_type_id = $this->dbr->newSelectQueryBuilder()
85            ->select( 'rev_type_id' )
86            ->from( 'flow_revision' )
87            ->leftJoin( 'flow_tree_revision', null, 'tree_rev_descendant_id = rev_type_id' )
88            ->where( [
89                $this->dbr->expr( 'rev_type_id', '>=', $range[0]->getBinary() ),
90                $this->dbr->expr( 'rev_type_id', '<', $range[1]->getBinary() ),
91            ] )
92            ->andWhere( $tuple->toArray( 'rev_user_' ) )
93            ->andWhere( $conds )
94            ->caller( __METHOD__ )
95            ->fetchField();
96
97        if ( $rev_type_id === false ) {
98            return false;
99        }
100
101        return UUID::create( $rev_type_id );
102    }
103
104    /**
105     * @param IRevisionableObject $object
106     * @return IObjectRevision
107     */
108    protected function getObjectRevision( IRevisionableObject $object ) {
109        $revisions = $object->getRevisions();
110        $revisions->rewind();
111        return $revisions->current();
112    }
113
114    /**
115     * @param string $name
116     * @return UserTuple
117     * @throws Exception
118     */
119    protected function getUserTuple( $name ) {
120        $user = $this->getUser( $name );
121        if ( $user === false ) {
122            throw new Exception( 'Invalid author: ' . $name );
123        }
124        return UserTuple::newFromUser( $user );
125    }
126
127    /**
128     * @param string $name
129     * @return bool|User
130     */
131    protected function getUser( $name ) {
132        if ( IPUtils::isIPAddress( $name ) ) {
133            return User::newFromName( $name, false );
134        }
135
136        return User::newFromName( $name );
137    }
138
139    /**
140     * Gets the min <= ? < max boundaries for a UUID that has a given
141     * timestamp. Returns an array where [0] = min & [1] is max.
142     *
143     * @param MWTimestamp $timestamp
144     * @return UUID[] [min, max]
145     * @throws TimestampException
146     */
147    protected function getUUIDRange( MWTimestamp $timestamp ) {
148        return [
149            UUID::getComparisonUUID( (int)$timestamp->getTimestamp( TS_UNIX ) ),
150            UUID::getComparisonUUID( (int)$timestamp->getTimestamp( TS_UNIX ) + 1 ),
151        ];
152    }
153}