Jeroen De Dauw has submitted this change and it was merged.

Change subject: Introduced 'qualifiers-order' parameter in ClaimSerializer
......................................................................


Introduced 'qualifiers-order' parameter in ClaimSerializer

(bug 52391) Since, in the serialized JSON structure, the qualifiers are 
encapsulated
in an object indexed by property id, the 'qualifiers-order' parameter specifies 
the
order of those property ids.

Change-Id: I6f8399b621dddb6c84082db5fd35286a2ecd438c
---
M .jshintignore
M lib/includes/serializers/ClaimSerializer.php
M lib/resources/wikibase.datamodel/wikibase.Claim.js
M lib/resources/wikibase.datamodel/wikibase.SnakList.js
M lib/tests/phpunit/serializers/ClaimSerializerTest.php
M lib/tests/qunit/wikibase.datamodel/Wikibase.SnakList.tests.js
M repo/tests/phpunit/includes/api/SetClaimTest.php
7 files changed, 246 insertions(+), 30 deletions(-)

Approvals:
  Jeroen De Dauw: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/.jshintignore b/.jshintignore
index fe966a7..eea9c1c 100644
--- a/.jshintignore
+++ b/.jshintignore
@@ -22,7 +22,6 @@
 ./lib/tests/qunit/wikibase.datamodel/datamodel.Property.tests.js
 ./lib/tests/qunit/wikibase.datamodel/Wikibase.claim.tests.js
 ./lib/tests/qunit/wikibase.datamodel/Wikibase.reference.tests.js
-./lib/tests/qunit/wikibase.datamodel/Wikibase.SnakList.tests.js
 ./lib/tests/qunit/wikibase.datamodel/Wikibase.snak.tests.js
 ./lib/tests/qunit/wikibase.datamodel/wikibase.Statement.tests.js
 ./lib/tests/qunit/wikibase.RepoApi/wikibase.RepoApiError.tests.js
diff --git a/lib/includes/serializers/ClaimSerializer.php 
b/lib/includes/serializers/ClaimSerializer.php
index f041c8e..ac00c7b 100644
--- a/lib/includes/serializers/ClaimSerializer.php
+++ b/lib/includes/serializers/ClaimSerializer.php
@@ -109,6 +109,12 @@
 
                if ( $qualifiers !== array() ) {
                        $serialization['qualifiers'] = $qualifiers;
+
+                       $serialization['qualifiers-order'] = array();
+                       foreach( $qualifiers as $propertyId => $snaks ) {
+                               $serialization['qualifiers-order'][] = 
$propertyId;
+                       }
+                       $this->setIndexedTagName( 
$serialization['qualifiers-order'], 'qualifiers-order' );
                }
 
                $serialization['type'] = $claim instanceof Statement ? 
'statement' : 'claim';
@@ -180,11 +186,7 @@
 
                $claim->setGuid( $serialization['id'] );
 
-               if ( array_key_exists( 'qualifiers', $serialization ) ) {
-                       $snaksUnserializer = new ByPropertyListUnserializer( 
$snakUnserializer );
-                       $snaks = $snaksUnserializer->newFromSerialization( 
$serialization['qualifiers'] );
-                       $claim->setQualifiers( new \Wikibase\SnakList( $snaks ) 
);
-               }
+               $claim->setQualifiers( $this->unserializeQualifiers( 
$serialization, $snakUnserializer ) );
 
                if ( $isStatement ) {
                        if ( !in_array( $serialization['rank'], self::$rankMap 
) ) {
@@ -212,4 +214,50 @@
                return $claim;
        }
 
+       /**
+        * Deserializes qualifiers from a serialized claim.
+        *
+        * @since 0.4
+        *
+        * @param array $serialization
+        * @param SnakSerializer $snakUnserializer
+        * @return \Wikibase\SnakList
+        * @throws \MWException
+        */
+       protected function unserializeQualifiers( $serialization, 
$snakUnserializer ) {
+               if ( !array_key_exists( 'qualifiers', $serialization ) ) {
+                       return new \Wikibase\SnakList();
+               } else {
+                       $sortedQualifiers = array();
+
+                       if( !array_key_exists( 'qualifiers-order', 
$serialization ) ) {
+                               $sortedQualifiers = 
$serialization['qualifiers'];
+
+                       } else {
+                               foreach( $serialization['qualifiers-order'] as 
$propertyId ) {
+                                       if( !isset( 
$serialization['qualifiers'][$propertyId] ) ) {
+                                               throw new MWException( 'No 
snaks with property id "' . $propertyId . '" '
+                                               . 'found in "qualifiers" 
parameter although specified in '
+                                               . '"qualifiers-order"' );
+                                       }
+
+                                       $sortedQualifiers[$propertyId] = 
$serialization['qualifiers'][$propertyId];
+                               }
+
+                               $missingProperties = array_diff_key(
+                                       $sortedQualifiers,
+                                       $serialization['qualifiers']
+                               );
+
+                               if( count( $missingProperties ) > 0 ) {
+                                       throw new MWException( 'Property ids ' 
. implode( ', ', $missingProperties )
+                                       . ' have not been specified in 
"qualifiers-order"' );
+                               }
+                       }
+
+                       $snaksUnserializer = new ByPropertyListUnserializer( 
$snakUnserializer );
+                       return new \Wikibase\SnakList( 
$snaksUnserializer->newFromSerialization( $sortedQualifiers ) );
+               }
+       }
+
 }
diff --git a/lib/resources/wikibase.datamodel/wikibase.Claim.js 
b/lib/resources/wikibase.datamodel/wikibase.Claim.js
index 1f0aa75..94b7199 100644
--- a/lib/resources/wikibase.datamodel/wikibase.Claim.js
+++ b/lib/resources/wikibase.datamodel/wikibase.Claim.js
@@ -139,6 +139,7 @@
 
                if ( this._qualifiers ) {
                        json.qualifiers = this._qualifiers.toJSON();
+                       json['qualifiers-order'] = 
this._qualifiers.getPropertyOrder();
                }
 
                return json;
@@ -162,7 +163,7 @@
                isStatement = json.type === 'statement';
 
        if ( json.qualifiers !== undefined ) {
-               qualifiers = wb.SnakList.newFromJSON( json.qualifiers );
+               qualifiers = wb.SnakList.newFromJSON( json.qualifiers, 
json['qualifiers-order'] );
        }
 
        if ( isStatement && json.references !== undefined ) {
diff --git a/lib/resources/wikibase.datamodel/wikibase.SnakList.js 
b/lib/resources/wikibase.datamodel/wikibase.SnakList.js
index 6b05b17..97384c1 100644
--- a/lib/resources/wikibase.datamodel/wikibase.SnakList.js
+++ b/lib/resources/wikibase.datamodel/wikibase.SnakList.js
@@ -152,6 +152,22 @@
        },
 
        /**
+        * Returns a list of property ids representing the order of the snaks 
grouped by property.
+        *
+        * @return {string[]}
+        */
+       getPropertyOrder: function() {
+               var json = this.toJSON(),
+                       propertyIds = [];
+
+               $.each( json, function( propertyId, snak ) {
+                       propertyIds.push( propertyId );
+               } );
+
+               return propertyIds;
+       },
+
+       /**
         * Returns a simple JSON structure representing this Snak. The 
structure will be a map, having
         * property IDs as keys and an array of the Snak's JSON as values.
         *
@@ -190,17 +206,45 @@
 /**
  * Creates a new Snak Object from a given JSON structure.
  *
- * @param {String} json
- * @return wb.Snak|null
+ * @param {string} json
+ * @param {string[]} [order] List of property ids defining the order of the 
snaks grouped by
+ *        property.
+ * @return {wikibase.SnakList|null}
  */
-SELF.newFromJSON = function( json ) {
+SELF.newFromJSON = function( json, order ) {
        var snaksList = new SELF();
 
-       $.each( json, function( propertyId, snaksPerProperty ) {
-               $.each( snaksPerProperty, function( i, snakJson ) {
-                       snaksList.addSnak( wb.Snak.newFromJSON( snakJson ) );
+       if( !order ) {
+               // No order specified: Just loop through the json object:
+               $.each( json, function( propertyId, snaksPerProperty ) {
+                       $.each( snaksPerProperty, function( i, snakJson ) {
+                               snaksList.addSnak( wb.Snak.newFromJSON( 
snakJson ) );
+                       } );
                } );
-       } );
+
+       } else {
+               // Check whether all property ids that are featured by snaks 
are specified in the order
+               // list:
+               $.each( json, function( propertyId, snakListJson ) {
+                       if( $.inArray( propertyId, order ) === -1 ) {
+                               throw new Error( 'Snak featuring the property 
id ' + propertyId + ' is not present '
+                                       + 'within list of property ids defined 
for ordering' );
+                       }
+               } );
+
+               // Add all snaks grouped by property according to the order 
specified via the "order"
+               // parameter:
+               for( var i = 0; i < order.length; i++ ) {
+                       if( !json[order[i]] ) {
+                               throw new Error( 'Trying to oder by property ' 
+ order[i] + ' without any snak '
+                                       + ' featuring this property being 
present' );
+                       }
+
+                       for( var j = 0; j < json[order[i]].length; j++ ) {
+                               snaksList.addSnak( wb.Snak.newFromJSON( 
json[order[i]][j] ) );
+                       }
+               }
+       }
 
        return snaksList;
 };
diff --git a/lib/tests/phpunit/serializers/ClaimSerializerTest.php 
b/lib/tests/phpunit/serializers/ClaimSerializerTest.php
index c0a18d0..74695cf 100644
--- a/lib/tests/phpunit/serializers/ClaimSerializerTest.php
+++ b/lib/tests/phpunit/serializers/ClaimSerializerTest.php
@@ -1,8 +1,11 @@
 <?php
 
 namespace Wikibase\Test;
+
+use Wikibase\Claim;
 use Wikibase\Statement;
 use Wikibase\Lib\Serializers\ClaimSerializer;
+use Wikibase\Lib\Serializers\SnakSerializer;
 
 /**
  * Tests for the Wikibase\ClaimSerializer class.
@@ -34,6 +37,7 @@
  *
  * @licence GNU GPL v2+
  * @author Jeroen De Dauw < jeroended...@gmail.com >
+ * @author H. Snater < mediaw...@snater.com >
  */
 class ClaimSerializerTest extends SerializerBaseTest {
 
@@ -60,13 +64,13 @@
 
                $id = new \Wikibase\EntityId( \Wikibase\Property::ENTITY_TYPE, 
42 );
 
-               $validArgs[] = new \Wikibase\Claim( new 
\Wikibase\PropertyNoValueSnak( $id ) );
+               $validArgs[] = new Claim( new \Wikibase\PropertyNoValueSnak( 
$id ) );
 
-               $validArgs[] = new \Wikibase\Claim( new 
\Wikibase\PropertySomeValueSnak( $id ) );
+               $validArgs[] = new Claim( new \Wikibase\PropertySomeValueSnak( 
$id ) );
 
                $validArgs = $this->arrayWrap( $validArgs );
 
-               $claim = new \Wikibase\Claim( new 
\Wikibase\PropertyNoValueSnak( $id ) );
+               $claim = new Claim( new \Wikibase\PropertyNoValueSnak( $id ) );
 
                $validArgs[] = array(
                        $claim,
@@ -80,7 +84,7 @@
                        ),
                );
 
-               $statement = new \Wikibase\Statement( new 
\Wikibase\PropertyNoValueSnak( $id ) );
+               $statement = new Statement( new \Wikibase\PropertyNoValueSnak( 
$id ) );
 
                $validArgs[] = array(
                        $statement,
@@ -95,6 +99,44 @@
                        ),
                );
 
+               $claim = new Claim(
+                       new \Wikibase\PropertyNoValueSnak( $id ),
+                       new \Wikibase\SnakList( array(
+                               new \Wikibase\PropertyNoValueSnak( $id ),
+                               new \Wikibase\PropertySomeValueSnak( $id ),
+                               new \Wikibase\PropertyNoValueSnak(
+                                       new \Wikibase\EntityId( 
\Wikibase\Property::ENTITY_TYPE, 1 )
+                               ),
+                       ) )
+               );
+
+               $snakSerializer = new SnakSerializer();
+
+               $validArgs[] = array(
+                       $claim,
+                       array(
+                               'id' => $claim->getGuid(),
+                               'mainsnak' => $snakSerializer->getSerialized(
+                                       new \Wikibase\PropertyNoValueSnak( $id )
+                               ),
+                               'qualifiers' => array(
+                                       'p42' => array(
+                                               $snakSerializer->getSerialized( 
new \Wikibase\PropertyNoValueSnak( $id ) ),
+                                               $snakSerializer->getSerialized(
+                                                       new 
\Wikibase\PropertySomeValueSnak( $id )
+                                               ),
+                                       ),
+                                       'p1' => array(
+                                               $snakSerializer->getSerialized( 
new \Wikibase\PropertyNoValueSnak(
+                                                       new \Wikibase\EntityId( 
\Wikibase\Property::ENTITY_TYPE, 1 )
+                                               ) ),
+                                       ),
+                               ),
+                               'qualifiers-order' => array( 'p42', 'p1' ),
+                               'type' => 'claim',
+                       ),
+               );
+
                return $validArgs;
        }
 
diff --git a/lib/tests/qunit/wikibase.datamodel/Wikibase.SnakList.tests.js 
b/lib/tests/qunit/wikibase.datamodel/Wikibase.SnakList.tests.js
index f75470a..4117d5c 100644
--- a/lib/tests/qunit/wikibase.datamodel/Wikibase.SnakList.tests.js
+++ b/lib/tests/qunit/wikibase.datamodel/Wikibase.SnakList.tests.js
@@ -1,12 +1,10 @@
 /**
- * QUnit tests for wikibase.SnakList
- * @see https://www.mediawiki.org/wiki/Extension:Wikibase
- *
  * @since 0.4
  * @ingroup WikibaseLib
  *
  * @licence GNU GPL v2+
  * @author Daniel Werner < daniel.wer...@wikimedia.de >
+ * @author H. Snater < mediaw...@snater.com >
  */
 
 ( function( wb, dv, $, QUnit ) {
@@ -15,10 +13,10 @@
        QUnit.module( 'wikibase.datamodel.SnakList.js', 
QUnit.newMwEnvironment() );
 
        var snaks = [
-               new wb.PropertyNoValueSnak( '9001' ),
-               new wb.PropertySomeValueSnak( '42' ),
-               new wb.PropertySomeValueSnak( '42' ), // two times 42!
-               new wb.PropertyValueSnak( '42', new dv.StringValue( 
'~=[,,_,,]:3' ) )
+               new wb.PropertyNoValueSnak( 'p9001' ),
+               new wb.PropertySomeValueSnak( 'p42' ),
+               new wb.PropertySomeValueSnak( 'p42' ), // two times 42!
+               new wb.PropertyValueSnak( 'p42', new dv.StringValue( 
'~=[,,_,,]:3' ) )
        ];
        var anotherSnak = new wb.PropertySomeValueSnak( 'p1' ),
                anotherSnak2 = new wb.PropertySomeValueSnak( 'p2' );
@@ -66,10 +64,35 @@
 
                assert.throws(
                        function() {
-                               var newList = new wb.SnakList( 'foo' );
+                               return new wb.SnakList( 'foo' );
                        },
                        'Can not create SnakList with strange constructor 
argument'
                );
+       } );
+
+       QUnit.test( 'newFromJSON()', function( assert ) {
+               var snakList = new wb.SnakList( snaks ),
+                       initialOrder = snakList.getPropertyOrder(),
+                       clonedSnakList = wb.SnakList.newFromJSON( 
snakList.toJSON() );
+
+               assert.ok(
+                       snakList.equals( clonedSnakList ),
+                       'Cloned snak list using the JSON representation.'
+               );
+
+               var reorderedClone = wb.SnakList.newFromJSON( 
snakList.toJSON(), ['p42', 'p9001'] ),
+                       cloneOrder = reorderedClone.getPropertyOrder();
+
+               assert.ok(
+                       snakList.equals( reorderedClone ),
+                       'Cloned snak list with applying a different property 
order.'
+               );
+
+               assert.ok(
+                       initialOrder[0] === cloneOrder[1] && initialOrder[1] 
=== cloneOrder[0],
+                       'Verified differing property order.'
+               );
+
        } );
 
        QUnit.test( 'SnakList list operations', function( assert ) {
@@ -92,6 +115,12 @@
                        'New Snak list does not recognize another Snak, not in 
the list as one of its own'
                );
 
+               assert.deepEqual(
+                       newSnakList.getPropertyOrder(),
+                       ['p9001', 'p42'],
+                       'Verified property order.'
+               );
+
                assert.ok(
                        newSnakList.addSnak( anotherSnak ) && 
newSnakList.length === initialLength + 1,
                        'Another snak added, length attribute increased by one'
@@ -100,6 +129,12 @@
                assert.ok(
                        newSnakList.hasSnak( anotherSnak ),
                        'Newly added Snak recognized as one of the list\'s own 
Snaks now'
+               );
+
+               assert.deepEqual(
+                       newSnakList.getPropertyOrder(),
+                       ['p9001', 'p42', 'p1'],
+                       'Verified property order.'
                );
 
                var clonedSnak = wb.Snak.newFromJSON( anotherSnak.toJSON() );
@@ -123,6 +158,12 @@
                        newSnakList.removeSnak( clonedSnak ) && 
newSnakList.length === initialLength + 1,
                        'Newly added Snak removed again (identified by cloned 
Snak, so we test for non === ' +
                                'case; list length decreased by one'
+               );
+
+               assert.deepEqual(
+                       newSnakList.getPropertyOrder(),
+                       ['p9001', 'p42', 'p2'],
+                       'Verified property order.'
                );
 
                var i = 0;
@@ -153,7 +194,7 @@
 
                assert.throws(
                        function() {
-                               newSnakList.addSnak( 'foo' )
+                               newSnakList.addSnak( 'foo' );
                        },
                        'Can not add some strange thing to the Snak list'
                );
diff --git a/repo/tests/phpunit/includes/api/SetClaimTest.php 
b/repo/tests/phpunit/includes/api/SetClaimTest.php
index 3b798bb..ed0e642 100644
--- a/repo/tests/phpunit/includes/api/SetClaimTest.php
+++ b/repo/tests/phpunit/includes/api/SetClaimTest.php
@@ -105,6 +105,12 @@
                $statement->getReferences()->addReference( new 
\Wikibase\Reference( $snaks ) );
                $statements[] = $statement;
 
+               $statement = clone $statement;
+               $snaks = new \Wikibase\SnakList( $this->snakProvider() );
+               $statement->setQualifiers( $snaks );
+               $statement->getReferences()->addReference( new 
\Wikibase\Reference( $snaks ) );
+               $statements[] = $statement;
+
                $ranks = array(
                        \Wikibase\Statement::RANK_DEPRECATED,
                        \Wikibase\Statement::RANK_NORMAL,
@@ -135,6 +141,19 @@
                        // Addition request
                        $this->makeRequest( $claim, $item->getId(), 1 );
 
+                       // Reorder qualifiers:
+                       if( count( $claim->getQualifiers() ) > 0 ) {
+                               // Simply reorder the qualifiers by putting the 
first qualifier to the end. This is
+                               // supposed to be done in the serialized 
representation since changing the actual
+                               // object might apply intrinsic sorting.
+                               $serializerFactory = new 
\Wikibase\Lib\Serializers\SerializerFactory();
+                               $serializer = 
$serializerFactory->newSerializerForObject( $claim );
+                               $serializedClaim = $serializer->getSerialized( 
$claim );
+                               $firstPropertyId = array_shift( 
$serializedClaim['qualifiers-order'] );
+                               array_push( 
$serializedClaim['qualifiers-order'], $firstPropertyId );
+                               $this->makeRequest( $serializedClaim, 
$item->getId(), 1 );
+                       }
+
                        $claim = new \Wikibase\Statement( new 
\Wikibase\PropertyNoValueSnak( 9001 ) );
                        $claim->setGuid( $guid );
 
@@ -143,13 +162,26 @@
                }
        }
 
-       protected function makeRequest( Claim $claim, \Wikibase\EntityId 
$entityId, $claimCount ) {
+       /**
+        * @param \Wikibase\Claim|array $claim Native or serialized claim 
object.
+        * @param EntityId $entityId
+        * @param $claimCount
+        */
+       protected function makeRequest( $claim, \Wikibase\EntityId $entityId, 
$claimCount ) {
                $serializerFactory = new 
\Wikibase\Lib\Serializers\SerializerFactory();
-               $serializer = $serializerFactory->newSerializerForObject( 
$claim );
+
+               if( is_a( $claim, '\Wikibase\Claim' ) ) {
+                       $serializer = 
$serializerFactory->newSerializerForObject( $claim );
+                       $serializedClaim = $serializer->getSerialized( $claim );
+               } else {
+                       $serializer = 
$serializerFactory->newUnserializerForClass( 'Wikibase\Claim' );
+                       $serializedClaim = $claim;
+                       $claim = $serializer->newFromSerialization( 
$serializedClaim );
+               }
 
                $params = array(
                        'action' => 'wbsetclaim',
-                       'claim' => \FormatJson::encode( 
$serializer->getSerialized( $claim ) ),
+                       'claim' => \FormatJson::encode( $serializedClaim ),
                );
 
                $this->makeValidRequest( $params );
@@ -162,6 +194,11 @@
 
                $this->assertTrue( $claims->hasClaim( $claim ) );
 
+               $savedClaim = $claims->getClaimWithGuid( $claim->getGuid() );
+               if( count( $claim->getQualifiers() ) ) {
+                       $this->assertArrayEquals( 
$claim->getQualifiers()->toArray(), $savedClaim->getQualifiers()->toArray(), 
true );
+               }
+
                $this->assertEquals( $claimCount, $claims->count() );
        }
 
@@ -173,6 +210,10 @@
                $this->assertArrayHasKey( 'pageinfo', $resultArray, 'top level 
element has a pageinfo key' );
                $this->assertArrayHasKey( 'claim', $resultArray, 'top level 
element has a statement key' );
 
+               if( isset( $resultArray['claim']['qualifiers'] ) ) {
+                       $this->assertArrayHasKey( 'qualifiers-order', 
$resultArray['claim'], '"qualifiers-order" key is set when returning 
qualifiers' );
+               }
+
                return $resultArray;
        }
 

-- 
To view, visit https://gerrit.wikimedia.org/r/77862
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I6f8399b621dddb6c84082db5fd35286a2ecd438c
Gerrit-PatchSet: 9
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <henning.sna...@wikimedia.de>
Gerrit-Reviewer: Daniel Werner <daniel.wer...@wikimedia.de>
Gerrit-Reviewer: Jeroen De Dauw <jeroended...@gmail.com>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to