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

Reply via email to