Mwjames has uploaded a new change for review. https://gerrit.wikimedia.org/r/89147
Change subject: \SMW\ApiBrowse enables to browse a subject via api.php?action=browse ...................................................................... \SMW\ApiBrowse enables to browse a subject via api.php?action=browse This is the corresponding Api for the Special:Browse interface (without support of incoming properties). In order to work with the Api, the following schema [2] can be used. - Uses the new SMW\Serializer [1] - JSON and XML output are supported [1] https://gerrit.wikimedia.org/r/#/c/89098/ [2] api.php?action=browse&subject=Main_Page Change-Id: I00c2fab386d0c74ef0b576876edb2f873c17bfde --- M SemanticMediaWiki.classes.php M includes/Setup.php A includes/api/ApiBrowse.php M tests/phpunit/MockObjectRepository.php A tests/phpunit/includes/api/ApiBrowseTest.php 5 files changed, 349 insertions(+), 1 deletion(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticMediaWiki refs/changes/47/89147/1 diff --git a/SemanticMediaWiki.classes.php b/SemanticMediaWiki.classes.php index faaee16..7c40699 100644 --- a/SemanticMediaWiki.classes.php +++ b/SemanticMediaWiki.classes.php @@ -357,6 +357,7 @@ 'SMW\ApiAsk' => 'includes/api/ApiAsk.php', 'SMW\ApiAskArgs' => 'includes/api/ApiAskArgs.php', 'SMW\ApiInfo' => 'includes/api/ApiInfo.php', + 'SMW\ApiBrowse' => 'includes/api/ApiBrowse.php', // Maintenance scripts 'SMWSetupScript' => 'maintenance/SMW_setup.php', diff --git a/includes/Setup.php b/includes/Setup.php index 3a59cbd..fed4ac9 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -151,6 +151,7 @@ $this->globals['wgAPIModules']['smwinfo'] = '\SMW\ApiInfo'; $this->globals['wgAPIModules']['ask'] = '\SMW\ApiAsk'; $this->globals['wgAPIModules']['askargs'] = '\SMW\ApiAskArgs'; + $this->globals['wgAPIModules']['browse'] = '\SMW\ApiBrowse'; } diff --git a/includes/api/ApiBrowse.php b/includes/api/ApiBrowse.php new file mode 100644 index 0000000..3489f06 --- /dev/null +++ b/includes/api/ApiBrowse.php @@ -0,0 +1,180 @@ +<?php + +namespace SMW; + +use Title; + +/** + * API module to browse a subject + * + * @file + * + * @license GNU GPL v2+ + * @since 1.6.2 + * + * @author mwjames + */ + +/** + * API module to browse a subject + * + * @ingroup Api + */ +class ApiBrowse extends ApiBase { + + /** + * @see ApiBase::execute + */ + public function execute() { + + $params = $this->extractRequestParams(); + + $serialized = Serializer::serialize( $this->getSemanticData( $this->getSubject( $params['subject'] ) ) ); + + $this->runFormatter( $serialized ); + + $this->getResult()->addValue( null, 'result', $serialized ); + } + + /** + * @since 1.9 + */ + protected function getSubject( $text ) { + + $subject = DIWikiPage::newFromTitle( Title::newFromText( $text ) ); + + if ( !$subject instanceOf DIWikiPage ) { + var_dump( 'ld'); + $this->dieUsageMsg( array( 'invalidtitle', $text ) ); + } + + return $subject; + } + + /** + * @note The original SemanticData container does not include its subobjects + * therefore these are added here as well to ensure a "complete object" for all + * available entities that belong to this subject (excluding incoming properties) + * + * @since 1.9 + */ + protected function getSemanticData( $subject ) { + + $semanticData = $this->getStore()->getSemanticData( $subject ); + + foreach ( $semanticData->getProperties() as $property ) { + + if ( $property->getKey() === '_SOBJ' || $property->getKey() === '_ASK' ) { + + $values = $semanticData->getPropertyValues( $property ); + + foreach ( $values as $value ) { + if ( $value instanceOf DIWikiPage ){ + $semanticData->addSubSemanticData( $this->getStore()->getSemanticData( $value ) ); + } + } + } + } + + return $semanticData; + } + + /** + * @since 1.9 + */ + protected function runFormatter( &$serialized ) { + + $this->addIndexTags( $serialized ); + + if ( isset( $serialized['sobj'] ) ) { + + $this->getResult()->setIndexedTagName( $serialized['sobj'], 'subobject' ); + + foreach ( $serialized['sobj'] as $key => &$value ) { + $this->addIndexTags( $value ); + } + } + + } + + /** + * @since 1.9 + */ + protected function addIndexTags( &$serialized ) { + + if ( isset( $serialized['data'] ) ) { + + $this->getResult()->setIndexedTagName( $serialized['data'], 'property' ); + + foreach ( $serialized['data'] as $key => $value ) { + if ( isset( $serialized['data'][ $key ]['dataitem'] ) ) { + $this->getResult()->setIndexedTagName( $serialized['data'][ $key ]['dataitem'], 'value' ); + } + } + } + + } + + /** + * @codeCoverageIgnore + * @see ApiBase::getAllowedParams + * + * @return array + */ + public function getAllowedParams() { + return array( + 'subject' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_ISMULTI => false, + ApiBase::PARAM_REQUIRED => true, + ) + ); + } + + /** + * @codeCoverageIgnore + * @see ApiBase::getParamDescription + * + * @return array + */ + public function getParamDescription() { + return array( + 'subject' => 'The subject to be queried', + ); + } + + /** + * @codeCoverageIgnore + * @see ApiBase::getDescription + * + * @return array + */ + public function getDescription() { + return array( + 'API module to query a subject.' + ); + } + + /** + * @codeCoverageIgnore + * @see ApiBase::getExamples + * + * @return array + */ + protected function getExamples() { + return array( + 'api.php?action=browse&subject=Main_Page', + ); + } + + /** + * @codeCoverageIgnore + * @see ApiBase::getVersion + * + * @return string + */ + public function getVersion() { + return __CLASS__ . '-' . SMW_VERSION; + } + +} diff --git a/tests/phpunit/MockObjectRepository.php b/tests/phpunit/MockObjectRepository.php index bf7abd7..c594030 100644 --- a/tests/phpunit/MockObjectRepository.php +++ b/tests/phpunit/MockObjectRepository.php @@ -634,7 +634,7 @@ $store->expects( $this->any() ) ->method( 'getSemanticData' ) - ->will( $this->returnValue( $this->builder->setValue( 'getSemanticData' ) ) ); + ->will( $this->builder->setCallback( 'getSemanticData' ) ); $store->expects( $this->any() ) ->method( 'getUnusedPropertiesSpecial' ) diff --git a/tests/phpunit/includes/api/ApiBrowseTest.php b/tests/phpunit/includes/api/ApiBrowseTest.php new file mode 100644 index 0000000..9b95a75 --- /dev/null +++ b/tests/phpunit/includes/api/ApiBrowseTest.php @@ -0,0 +1,166 @@ +<?php + +namespace SMW\Test; + +use SMW\DataValueFactory; +use SMW\ApiBrowse; +use SMW\SemanticData; +use SMW\DIWikiPage; +use SMW\Serializer; +use SMW\Subobject; + +/** + * Tests for the ApiBrowse class + * + * @file + * + * @license GNU GPL v2+ + * @since 1.9 + * + * @author mwjames + */ + +/** + * @covers \SMW\ApiBrowse + * + * @ingroup Test + * + * @group SMW + * @group SMWExtension + * @group API + */ +class ApiBrowseTest extends ApiTestCase { + + /** + * Returns the name of the class to be tested + * + * @return string|false + */ + public function getClass() { + return '\SMW\ApiBrowse'; + } + + /** + * @dataProvider subjectDataProvider + * + * @since 1.9 + */ + public function testExecuteOnSQLStore( $queryParameters ) { + + $this->runOnlyOnSQLStore(); + + $result = $this->doApiRequest( array( + 'action' => 'browse', + 'subject' => $queryParameters + ) ); + + $this->assertInternalType( 'array', $result['result'] ); + $this->assertInternalType( 'string', $result['result']['subject'] ); + + } + + /** + * @dataProvider semanticDataProvider + * + * @since 1.9 + */ + public function testExecuteOnMockStore( $setup ) { + + $api = new ApiBrowse( $this->getApiMain( array( 'subject' => $setup['subject'] ) ), 'browse' ); + $api->setStore( $setup['store'] ); + $api->execute(); + + $result = $api->getResultData(); + + // We gimmick a bit here otherwise matching the array will be cumbersome, + // we'll use the result from the Api and recreate the SemanticData + // container using the Serializer to compare the hash from the original + // container with that of the newly created container from the Api + $this->assertEquals( + $setup['data']->getHash(), + Serializer::unserialize( $result['result'] )->getHash(), + 'Asserts that getHash() mathes both SemanticData containers' + ); + + } + + /** + * @return array + */ + public function subjectDataProvider() { + + $provider = array(); + + $provider[] = array( 'Main_Page' ); + + return $provider; + } + + /** + * @return array + */ + public function semanticDataProvider() { + + $provider = array(); + + $title = $this->newTitle( NS_MAIN, 'Foo' ); + + // #0 Empty container + $data = new SemanticData( DIWikiPage::newFromTitle( $title ) ); + + $mockStore = $this->newMockBuilder()->newObject( 'Store', array( + 'getSemanticData' => $data + ) ); + + $provider[] = array( + array( + 'subject' => 'Foo-0', + 'store' => $mockStore, + 'data' => $data + ) + ); + + // #1 Single entry + $data = new SemanticData( DIWikiPage::newFromTitle( $title ) ); + $data->addDataValue( DataValueFactory::newPropertyValue( 'Has fooQuex', 'Bar' ) ); + + $mockStore = $this->newMockBuilder()->newObject( 'Store', array( + 'getSemanticData' => $data + ) ); + + $provider[] = array( + array( + 'subject' => 'Foo-1', + 'store' => $mockStore, + 'data' => $data + ) + ); + + // #2 Single + single subobject entry + $data = new SemanticData( DIWikiPage::newFromTitle( $title ) ); + $data->addDataValue( DataValueFactory::newPropertyValue( 'Has fooQuex', 'Bar' ) ); + + $subobject = new Subobject( $title ); + $subobject->setSemanticData( 'Foo-sub' ); + $subobject->addDataValue( DataValueFactory::newPropertyValue( 'Has subobjects', 'Bam' ) ); + + $data->addPropertyObjectValue( $subobject->getProperty(), $subobject->getContainer() ); + + $mockStore = $this->newMockBuilder()->newObject( 'Store', array( + 'getSemanticData' => function ( $subject ) use( $data, $subobject ) { + return $subject->getSubobjectName() === 'Foo-sub' ? $subobject->getSemanticData() : $data; + } + ) ); + + $provider[] = array( + array( + 'subject' => 'Foo-2', + 'store' => $mockStore, + 'data' => $data + ) + ); + + return $provider; + } + +} -- To view, visit https://gerrit.wikimedia.org/r/89147 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I00c2fab386d0c74ef0b576876edb2f873c17bfde Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/SemanticMediaWiki Gerrit-Branch: master Gerrit-Owner: Mwjames <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
