Daniel Kinzler has uploaded a new change for review. https://gerrit.wikimedia.org/r/123860
Change subject: (bug 62644) snak validation in ChangeOpMainSnak. ...................................................................... (bug 62644) snak validation in ChangeOpMainSnak. Change-Id: I99ed03d15643a3b42a60072d2fb3824ca80622d6 --- M repo/includes/ChangeOp/ChangeOpClaim.php M repo/includes/ChangeOp/ChangeOpFactory.php M repo/includes/ChangeOp/ChangeOpMainSnak.php M repo/includes/api/ClaimModificationHelper.php M repo/includes/api/CreateClaim.php M repo/includes/api/SetClaimValue.php M repo/tests/phpunit/includes/ChangeOp/ChangeOpMainSnakTest.php M repo/tests/phpunit/includes/api/CreateClaimTest.php M repo/tests/phpunit/includes/api/SetClaimValueTest.php 9 files changed, 176 insertions(+), 55 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase refs/changes/60/123860/1 diff --git a/repo/includes/ChangeOp/ChangeOpClaim.php b/repo/includes/ChangeOp/ChangeOpClaim.php index c04b0c8..1f434cb 100644 --- a/repo/includes/ChangeOp/ChangeOpClaim.php +++ b/repo/includes/ChangeOp/ChangeOpClaim.php @@ -200,6 +200,8 @@ } /** + * @since 0.5 + * * @throws ChangeOpException */ protected function validate() { diff --git a/repo/includes/ChangeOp/ChangeOpFactory.php b/repo/includes/ChangeOp/ChangeOpFactory.php index c7793ee..6fa44e2 100644 --- a/repo/includes/ChangeOp/ChangeOpFactory.php +++ b/repo/includes/ChangeOp/ChangeOpFactory.php @@ -229,7 +229,7 @@ * @return ChangeOp */ public function newSetMainSnakOp( $claimGuid, Snak $snak ) { - return new ChangeOpMainSnak( $claimGuid, $snak, $this->guidGenerator ); + return new ChangeOpMainSnak( $claimGuid, $snak, $this->guidGenerator, $this->snakValidator ); } /** diff --git a/repo/includes/ChangeOp/ChangeOpMainSnak.php b/repo/includes/ChangeOp/ChangeOpMainSnak.php index ef60baf..83a0982 100644 --- a/repo/includes/ChangeOp/ChangeOpMainSnak.php +++ b/repo/includes/ChangeOp/ChangeOpMainSnak.php @@ -8,6 +8,7 @@ use Wikibase\DataModel\Snak\Snak; use Wikibase\Lib\ClaimGuidGenerator; use Wikibase\Summary; +use Wikibase\Validators\SnakValidator; /** * Class for mainsnak change operation @@ -15,6 +16,7 @@ * @since 0.4 * @licence GNU GPL v2+ * @author Tobias Gritschacher < tobias.gritschac...@wikimedia.de > + * @author Daniel Kinzler */ class ChangeOpMainSnak extends ChangeOpBase { @@ -33,6 +35,11 @@ protected $snak; /** + * @var SnakValidator + */ + private $snakValidator; + + /** * Constructs a new mainsnak change operation * * @since 0.4 @@ -40,10 +47,16 @@ * @param string $claimGuid * @param Snak|null $snak * @param ClaimGuidGenerator $guidGenerator + * @param SnakValidator $snakValidator * * @throws InvalidArgumentException */ - public function __construct( $claimGuid, $snak, ClaimGuidGenerator $guidGenerator ) { + public function __construct( + $claimGuid, + $snak, + ClaimGuidGenerator $guidGenerator, + SnakValidator $snakValidator + ) { if ( !is_string( $claimGuid ) ) { throw new InvalidArgumentException( '$claimGuid needs to be a string' ); } @@ -55,6 +68,7 @@ $this->claimGuid = $claimGuid; $this->snak = $snak; $this->guidGenerator = $guidGenerator; + $this->snakValidator = $snakValidator; } /** @@ -70,6 +84,8 @@ * - the claim's mainsnak gets set to $snak when $claimGuid and $snak are set */ public function apply( Entity $entity, Summary $summary = null ) { + $this->validate(); + $claims = new Claims( $entity->getClaims() ); if ( is_null( $this->claimGuid ) || empty( $this->claimGuid ) ) { @@ -129,23 +145,6 @@ /** * @since 0.4 * - * @param Claims $claims - * @param Summary $summary - * - * @throws ChangeOpException - */ - protected function removeClaim( Claims $claims, Summary $summary = null ) { - if( !$claims->hasClaimWithGuid( $this->claimGuid ) ) { - throw new ChangeOpException( "Entity does not have claim with GUID $this->claimGuid" ); - } - $removedSnak = $claims->getClaimWithGuid( $this->claimGuid )->getMainSnak(); - $claims->removeClaimWithGuid( $this->claimGuid ); - $this->updateSummary( $summary, 'remove', '', $this->getClaimSummaryArgs( $removedSnak ) ); - } - - /** - * @since 0.4 - * * @param Snak $mainSnak * * @return array @@ -154,4 +153,17 @@ $propertyId = $mainSnak->getPropertyId(); return array( array( $propertyId->getPrefixedId() => $mainSnak ) ); } + + /** + * @since 0.5 + * + * @throws ChangeOpException + */ + protected function validate() { + $result = $this->snakValidator->validate( $this->snak ); + + if ( !$result->isValid() ) { + throw new ChangeOpValidationException( $result ); + } + } } diff --git a/repo/includes/api/ClaimModificationHelper.php b/repo/includes/api/ClaimModificationHelper.php index 5602471..bc5dab9 100644 --- a/repo/includes/api/ClaimModificationHelper.php +++ b/repo/includes/api/ClaimModificationHelper.php @@ -149,6 +149,7 @@ } /** @var Snak $snak */ + //FIXME: remove snak validation logic here, once it is applied consistently by ChangeOps. $this->snakValidation->validateSnak( $snak ); return $snak; } diff --git a/repo/includes/api/CreateClaim.php b/repo/includes/api/CreateClaim.php index 66434a2..696f80c 100644 --- a/repo/includes/api/CreateClaim.php +++ b/repo/includes/api/CreateClaim.php @@ -54,6 +54,8 @@ try { $changeOp->apply( $entity, $summary ); } catch ( ChangeOpException $e ) { + //FIXME: implement generic localization of exceptions, + // especially ChangeOpValidationException. $this->dieUsage( $e->getMessage(), 'failed-save' ); } diff --git a/repo/includes/api/SetClaimValue.php b/repo/includes/api/SetClaimValue.php index d87ecfc..3684ec0 100644 --- a/repo/includes/api/SetClaimValue.php +++ b/repo/includes/api/SetClaimValue.php @@ -47,6 +47,8 @@ try { $changeOp->apply( $entity, $summary ); } catch ( ChangeOpException $e ) { + //FIXME: implement generic localization of exceptions, + // especially ChangeOpValidationException. $this->dieUsage( $e->getMessage(), 'failed-save' ); } diff --git a/repo/tests/phpunit/includes/ChangeOp/ChangeOpMainSnakTest.php b/repo/tests/phpunit/includes/ChangeOp/ChangeOpMainSnakTest.php index 7414cf5..c558cc0 100644 --- a/repo/tests/phpunit/includes/ChangeOp/ChangeOpMainSnakTest.php +++ b/repo/tests/phpunit/includes/ChangeOp/ChangeOpMainSnakTest.php @@ -3,6 +3,7 @@ namespace Wikibase\Test; use DataValues\DataValue; +use DataValues\NumberValue; use DataValues\StringValue; use Wikibase\ChangeOp\ChangeOp; use Wikibase\ChangeOp\ChangeOpMainSnak; @@ -10,7 +11,10 @@ use Wikibase\DataModel\Entity\Entity; use Wikibase\DataModel\Entity\Item; use Wikibase\DataModel\Entity\ItemId; +use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\DataModel\Snak\PropertyNoValueSnak; use Wikibase\DataModel\Snak\PropertyValueSnak; +use Wikibase\DataModel\Snak\Snak; use Wikibase\ItemContent; use Wikibase\Lib\ClaimGuidGenerator; use InvalidArgumentException; @@ -25,22 +29,37 @@ * * @licence GNU GPL v2+ * @author Tobias Gritschacher < tobias.gritschac...@wikimedia.de > + * @author Daniel Kinzler */ class ChangeOpMainSnakTest extends \PHPUnit_Framework_TestCase { + /** + * @var ClaimTestHelper + */ + protected $helper; + + /** + * @param null $name + * @param array $data + * @param string $dataName + */ + public function __construct( $name = null, array $data = array(), $dataName = '' ) { + parent::__construct( $name, $data, $dataName ); + + $this->helper = new ClaimTestHelper(); + } + public function invalidArgumentProvider() { $item = ItemContent::newFromArray( array( 'entity' => 'q42' ) )->getEntity(); - $validGuidGenerator = new ClaimGuidGenerator(); - $guidGenerator = new ClaimGuidGenerator(); - $validClaimGuid = $guidGenerator->newGuid( $item->getId( $item->getId() ) ); + $validClaimGuid = $this->helper->getGuidGenerator()->newGuid( $item->getId() ); $validSnak = new PropertyValueSnak( 7201010, new StringValue( 'o_O' ) ); $args = array(); - $args[] = array( 123, $validSnak, $validGuidGenerator ); - $args[] = array( 123, null, $validGuidGenerator ); - $args[] = array( $validClaimGuid, 'notASnak', $validGuidGenerator ); - $args[] = array( '', 'notASnak', $validGuidGenerator ); - $args[] = array( '', null, $validGuidGenerator ); + $args[] = array( 123, $validSnak ); + $args[] = array( 123, null ); + $args[] = array( $validClaimGuid, 'notASnak' ); + $args[] = array( '', 'notASnak' ); + $args[] = array( '', null ); return $args; } @@ -50,30 +69,50 @@ * * @expectedException InvalidArgumentException */ - public function testInvalidConstruct( $claimGuid, $snak, $guidGenerator ) { - new ChangeOpMainSnak( $claimGuid, $snak, $guidGenerator ); + public function testInvalidConstruct( $claimGuid, $snak ) { + new ChangeOpMainSnak( + $claimGuid, + $snak, + $this->helper->getGuidGenerator(), + $this->helper->getMockSnakValidator() + ); + } + + /** + * @param string $claimGuid + * @param Snak $snak + * + * @return ChangeOpMainSnak + */ + protected function newChangeOpMainSnak( $claimGuid, Snak $snak ) { + return new ChangeOpMainSnak( + $claimGuid, + $snak, + $this->helper->getGuidGenerator(), + $this->helper->getMockSnakValidator() + ); } public function provideChangeOps() { - $snak = new PropertyValueSnak( 2754236, new StringValue( 'test' ) ); + $snak = $this->makeSnak( 'P5', 'test' ); $args = array(); // add a new claim - $item = $this->makeNewItemWithClaim( 'q123', $snak ); - $newSnak = new PropertyValueSnak( 78462378, new StringValue( 'newSnak' ) ); + $item = $this->makeNewItemWithClaim( 'Q123', $snak ); + $newSnak = $this->makeSnak( 'P8', 'newSnak' ); $claimGuid = ''; - $changeOp = new ChangeOpMainSnak( $claimGuid, $newSnak, new ClaimGuidGenerator() ); + $changeOp = $this->newChangeOpMainSnak( $claimGuid, $newSnak ); $expected = $newSnak->getDataValue(); $args['add new claim'] = array ( $item, $changeOp, $expected ); // update an existing claim with a new main snak value - $item = $this->makeNewItemWithClaim( 'q234', $snak ); - $newSnak = new PropertyValueSnak( 2754236, new StringValue( 'changedSnak' ) ); + $item = $this->makeNewItemWithClaim( 'Q234', $snak ); + $newSnak = $this->makeSnak( 'P5', 'changedSnak' ); $claims = $item->getClaims(); $claim = reset( $claims ); $claimGuid = $claim->getGuid(); - $changeOp = new ChangeOpMainSnak( $claimGuid, $newSnak, new ClaimGuidGenerator() ); + $changeOp = $this->newChangeOpMainSnak( $claimGuid, $newSnak ); $expected = $newSnak->getDataValue(); $args['update claim by guid'] = array ( $item, $changeOp, $expected ); @@ -99,31 +138,39 @@ } public function provideInvalidApply() { - $snak = new PropertyValueSnak( 67573284, new StringValue( 'test' ) ); - $newSnak = new PropertyValueSnak( 12651236, new StringValue( 'newww' ) ); - $item = $this->makeNewItemWithClaim( 'q777', $snak ); + $snak = $this->makeSnak( 'P11', 'test' ); + $item = $this->makeNewItemWithClaim( 'Q777', $snak ); $claims = $item->getClaims(); $claim = reset( $claims ); $claimGuid = $claim->getGuid(); - $guidGenerator = new ClaimGuidGenerator(); // apply change to the wrong item $wrongItem = Item::newEmpty(); $wrongItem->setId( new ItemId( "Q888" ) ); - $args['wrong entity'] = array ( $wrongItem, new ChangeOpMainSnak( $claimGuid, $newSnak, $guidGenerator ) ); + $newSnak = $this->makeSnak( 'P12', 'newww' ); + $args['wrong entity'] = array ( $wrongItem, $this->newChangeOpMainSnak( $claimGuid, $newSnak ) ); // apply change to an unknown claim $wrongClaimId = $item->getId()->getPrefixedId() . '$DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF'; - $args['unknown claim'] = array ( $item, new ChangeOpMainSnak( $wrongClaimId, $newSnak, $guidGenerator ) ); + $args['unknown claim'] = array ( $item, $this->newChangeOpMainSnak( $wrongClaimId, $newSnak ) ); // update an existing claim with wrong main snak property - $newSnak = new PropertyValueSnak( 78462378, new StringValue( 'changedSnak' ) ); + $newSnak = $this->makeSnak( 'P13', 'changedSnak' ); $claims = $item->getClaims(); $claim = reset( $claims ); $claimGuid = $claim->getGuid(); - $changeOp = new ChangeOpMainSnak( $claimGuid, $newSnak, $guidGenerator ); + $changeOp = $this->newChangeOpMainSnak( $claimGuid, $newSnak ); $args['wrong main snak property'] = array ( $item, $changeOp ); + + // apply invalid main snak + $badSnak = $this->makeSnak( 'P12', new NumberValue( 5 ) ); + $args['bad value type'] = array ( $wrongItem, $this->newChangeOpMainSnak( $claimGuid, $badSnak ) ); + + // apply invalid main snak + // NOTE: the mock validator considers "INVALID" to be invalid. + $badSnak = $this->makeSnak( 'P12', 'INVALID' ); + $args['invalid value'] = array ( $wrongItem, $this->newChangeOpMainSnak( $claimGuid, $badSnak ) ); return $args; } @@ -140,11 +187,27 @@ protected function makeNewItemWithClaim( $itemId, $snak ) { $entity = Item::newFromArray( array( 'entity' => $itemId ) ); $claim = $entity->newClaim( $snak ); - $claim->setGuid( $entity->getId()->getPrefixedId() . '$D8404CDA-25E4-4334-AG93-A3290BCD9C0P' ); + $claim->setGuid( $this->helper->getGuidGenerator()->newGuid( $entity->getId() ) ); $claims = new Claims(); $claims->addClaim( $claim ); $entity->setClaims( $claims ); return $entity; } + + protected function makeSnak( $propertyId, $value ) { + if ( is_string( $propertyId ) ) { + $propertyId = new PropertyId( $propertyId ); + } + + if ( is_string( $value ) ) { + $value = new StringValue( $value ); + } + + if ( $value === null ) { + return new PropertyNoValueSnak( $propertyId ); + } else { + return new PropertyValueSnak( $propertyId, $value ); + } + } } diff --git a/repo/tests/phpunit/includes/api/CreateClaimTest.php b/repo/tests/phpunit/includes/api/CreateClaimTest.php index 96f7d11..ed17af0 100644 --- a/repo/tests/phpunit/includes/api/CreateClaimTest.php +++ b/repo/tests/phpunit/includes/api/CreateClaimTest.php @@ -180,6 +180,16 @@ ); $argLists[] = array( 'invalid-snak', $params ); + //10 + $params = array( + 'action' => 'wbcreateclaim', + 'entity' => '-', + 'snaktype' => 'value', + 'property' => '-', + 'value' => '" "', //blank is invalid + ); + $argLists[] = array( 'invalid-snak-value', $params ); + return $argLists; } diff --git a/repo/tests/phpunit/includes/api/SetClaimValueTest.php b/repo/tests/phpunit/includes/api/SetClaimValueTest.php index 0a6f3b2..23dae2c 100644 --- a/repo/tests/phpunit/includes/api/SetClaimValueTest.php +++ b/repo/tests/phpunit/includes/api/SetClaimValueTest.php @@ -11,6 +11,7 @@ use Wikibase\DataModel\Claim\Claim; use Wikibase\DataModel\Entity\EntityId; use Wikibase\DataModel\Entity\Item; +use Wikibase\DataModel\Entity\ItemId; use Wikibase\Lib\EntityIdLinkFormatter; use Wikibase\Lib\SnakFormatter; use Wikibase\DataModel\Entity\Property; @@ -45,6 +46,17 @@ * @var ValueFormatter */ private $propertyValueFormatter = null; + + public function setUp() { + parent::setUp(); + + static $hasEntities = false; + + if ( !$hasEntities ) { + $this->initTestEntities( array( 'StringProp', 'Berlin' ) ); + $hasEntities = true; + } + } /** * @param Entity $entity @@ -144,29 +156,46 @@ } /** - * @dataProvider invalidClaimProvider + * @dataProvider invalidRequestProvider */ - public function testInvalidClaimGuid( $claimGuid ) { + public function testInvalidRequest( $itemHandle, $claimGuid, $snakType, $value, $error ) { + $itemId = new ItemId( EntityTestHelper::getId( $itemHandle ) ); + $item = WikibaseRepo::getDefaultInstance()->getEntityLookup()->getEntity( $itemId ); + + if ( $claimGuid === null ) { + $claims = $item->getClaims(); + + /* @var Claim $claim */ + $claim = reset( $claims ); + $claimGuid = $claim->getGuid(); + } + + if ( !is_string( $value ) ) { + $value = json_encode( $value ); + } + $params = array( 'action' => 'wbsetclaimvalue', 'claim' => $claimGuid, - 'snaktype' => 'value', - 'value' => 'abc', + 'snaktype' => $snakType, + 'value' => $value, ); try { $this->doApiRequestWithToken( $params ); - $this->fail( 'Invalid claim guid did not raise an error' ); + $this->fail( 'Invalid request did not raise an error' ); } catch ( \UsageException $e ) { - $this->assertEquals( 'invalid-guid', $e->getCodeString(), 'Invalid claim guid raised correct error' ); + $this->assertEquals( $error, $e->getCodeString(), 'Invalid claim guid raised correct error' ); } } - public function invalidClaimProvider() { + public function invalidRequestProvider() { return array( - array( 'xyz' ), - array( 'x$y$z' ), - array( 'i1813$358fa2a0-4345-82b6-12a4-7b0fee494a5f' ) + 'bad guid 1' => array( 'Berlin', 'xyz', 'value', 'abc', 'invalid-guid' ), + 'bad guid 2' => array( 'Berlin', 'x$y$z', 'value', 'abc', 'invalid-guid' ), + 'bad guid 3' => array( 'Berlin', 'i1813$358fa2a0-4345-82b6-12a4-7b0fee494a5f', 'value', 'abc', 'invalid-guid' ), + 'bad snak type' => array( 'Berlin', null, 'alksdjf', 'abc', 'unknown_snaktype' ), + 'bad snak value' => array( 'Berlin', null, 'value', ' ', 'invalid-snak' ), ); } -- To view, visit https://gerrit.wikimedia.org/r/123860 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I99ed03d15643a3b42a60072d2fb3824ca80622d6 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits