Robert Vogel has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/345852 )
Change subject: [WiP] Adding TaskData Schema ...................................................................... [WiP] Adding TaskData Schema Change-Id: I6d6b7d4d2e5b619728e843853634e3491ad0e3cb --- M extension.json M i18n/api/en.json M i18n/api/qqq.json M includes/api/BSApiTasksBase.php M includes/api/BSApiWikiPageTasks.php A includes/utility/BSTasksApiSpec.php A tests/utility/BSTasksApiSpecTest.php 7 files changed, 391 insertions(+), 44 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/BlueSpiceFoundation refs/changes/52/345852/1 diff --git a/extension.json b/extension.json index c924686..cc5dd02 100644 --- a/extension.json +++ b/extension.json @@ -560,7 +560,8 @@ "BSApiExtJSStoreTestBase": "tests/BSApiExtJSStoreTestBase.php", "BSApiTasksTestBase": "tests/BSApiTasksTestBase.php", "BSTemplateHelper": "includes/TemplateHelper.php", - "ResourceLoaderBSTemplateModule": "includes/resourceloader/ResourceLoaderBSTemplateModule.php" + "ResourceLoaderBSTemplateModule": "includes/resourceloader/ResourceLoaderBSTemplateModule.php", + "BSTasksApiSpec": "includes/utility/BSTasksApiSpec.php" }, "load_composer_autoloader": true, "manifest_version": 1, diff --git a/i18n/api/en.json b/i18n/api/en.json index e56870d..81b44ba 100644 --- a/i18n/api/en.json +++ b/i18n/api/en.json @@ -42,5 +42,6 @@ "bs-wikipage-tasks-error-page-read-not-allowed": "You are not allowed to read page '$1'.", "bs-wikipage-tasks-error-nothingtoremove": "There are no categories to remove", "apihelp-bs-category-treestore-description": "Lists the sub-categories of a specific category", - "apihelp-bs-category-treestore-param-node": "A path of categories separated by slash" + "apihelp-bs-category-treestore-param-node": "A path of categories separated by slash", + "bs-api-task-taskdata-help": "[$1 Schema], [$2 Parameter examples]" } diff --git a/i18n/api/qqq.json b/i18n/api/qqq.json index 9026adc..e353359 100644 --- a/i18n/api/qqq.json +++ b/i18n/api/qqq.json @@ -43,5 +43,6 @@ "bs-wikipage-tasks-error-page-read-not-allowed": "An error message that occurs if a user is not allowed to read page with name '$1'.\n\nParameters:\n* $1 - a page title", "bs-wikipage-tasks-error-nothingtoremove": "This error message occurs if empty array is passed to task_removeCategories method", "apihelp-bs-category-treestore-description": "{{doc-apihelp-description|bs-category-treestore}}", - "apihelp-bs-category-treestore-param-node": "{{doc-apihelp-param|bs-category-treestore|node}}" + "apihelp-bs-category-treestore-param-node": "{{doc-apihelp-param|bs-category-treestore|node}}", + "bs-api-task-taskdata-help": "Text for API help page.\\n\nParameters:\n* $1 - an url to the API endpoint schema\n* $2 - an url to parameter examples" } diff --git a/includes/api/BSApiTasksBase.php b/includes/api/BSApiTasksBase.php index 539a827..c7b7de8 100644 --- a/includes/api/BSApiTasksBase.php +++ b/includes/api/BSApiTasksBase.php @@ -1,6 +1,6 @@ <?php /** - * Provides the base api for BlueSpice. + * Provides the base task api for BlueSpice. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +41,22 @@ /** * Methods that can be called by task param + * e.g. + * array( + * 'taskname' => array( + * 'examples' => [ + * [ + * 'paramname' => 'Some string' + * ] + * ], + * 'params' => array( + * 'paramname' => array( + * 'type' => string, + * 'required' => true + * ) + * ) + * ) + * ); * @var array */ protected $aTasks = array(); @@ -65,6 +81,23 @@ protected $oExtendedContext = null; /** + * + * @var BSTasksApiSpec + */ + protected $oTasksSpec = null; + + /** + * @param ApiMain $mainModule + * @param string $moduleName Name of this module + * @param string $modulePrefix Prefix to use for parameter names + */ + public function __construct( \ApiMain $mainModule, $moduleName, $modulePrefix = '' ) { + $this->aTasks = array_merge( $this->aTasks, $this->aGlobalTasks ); + $this->oTasksSpec = new BSTasksApiSpec( $this->aTasks ); + parent::__construct($mainModule, $moduleName, $modulePrefix); + } + + /** * The execute() method will be invoked directly by ApiMain immediately * before the result of the module is output. Aside from the * constructor, implementations should assume that no other methods @@ -74,6 +107,19 @@ */ public function execute() { $aParams = $this->extractRequestParams(); + + /** + * As we disable "needToken" of one of the following flags is set we + * need to make sure that no task is being executed! + */ + if( isset( $aParams['schema'] ) ) { + $this->returnTaskDataSchema( $aParams['task'] ); + return; + } + if( isset( $aParams['examples'] ) ) { + $this->returnTaskDataExamples( $aParams['task'] ); + return; + } $this->initContext(); //Avoid API warning: register the parameter used to bust browser cache @@ -84,7 +130,7 @@ $oResult = $this->makeStandardReturn(); if( !is_callable( array( $this, $sMethod ) ) ) { - $oResult->errors['task'] = 'Task '.$sTask.' not implemented'; + $oResult->errors['task'] = "Task '$sTask' not implemented!"; } else { $res = $this->checkTaskPermission( $sTask ); @@ -103,7 +149,7 @@ $oTaskData = $this->getParameter( 'taskData' ); Hooks::run( 'BSApiTasksBaseBeforeExecuteTask', array( $this, $sTask, &$oTaskData , &$aParams ) ); - $oResult = $this->validateTaskData( $oTaskData ); + $oResult = $this->validateTaskData( $sTask, $oTaskData ); if( empty( $oResult->errors ) && empty( $oResult->message ) ) { try { $oResult = $this->$sMethod( $oTaskData , $aParams ); @@ -239,34 +285,39 @@ * @return array */ protected function getAllowedParams() { - $this->aTasks = array_merge( $this->aTasks, $this->aGlobalTasks ); return array( 'task' => array( ApiBase::PARAM_REQUIRED => true, - ApiBase::PARAM_TYPE => $this->aTasks, - 10 /*ApiBase::PARAM_HELP_MSG*/ => 'apihelp-bs-task-param-task', + ApiBase::PARAM_TYPE => $this->oTasksSpec->getTaskNames(), + ApiBase::PARAM_HELP_MSG => 'apihelp-bs-task-param-task', + ApiBase::PARAM_HELP_MSG_PER_VALUE => $this->makeTaskHelpMessages() ), 'taskData' => array( ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_REQUIRED => false, ApiBase::PARAM_DFLT => '{}', - 10 /*ApiBase::PARAM_HELP_MSG*/ => 'apihelp-bs-task-param-taskdata', + ApiBase::PARAM_HELP_MSG => 'apihelp-bs-task-param-taskdata', ), 'context' => array( ApiBase::PARAM_TYPE => 'string', ApiBase::PARAM_REQUIRED => false, ApiBase::PARAM_DFLT => '{}', - 10 /*ApiBase::PARAM_HELP_MSG*/ => 'apihelp-bs-task-param-context', + ApiBase::PARAM_HELP_MSG => 'apihelp-bs-task-param-context', + ), + 'schema' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false, + ApiBase::PARAM_HELP_MSG => 'apihelp-bs-task-param-schema', + ), + 'examples' => array( + ApiBase::PARAM_TYPE => 'string', + ApiBase::PARAM_REQUIRED => false, + ApiBase::PARAM_HELP_MSG => 'apihelp-bs-task-param-examples', ), 'format' => array( ApiBase::PARAM_DFLT => 'json', ApiBase::PARAM_TYPE => array( 'json', 'jsonfm' ), - 10 /*ApiBase::PARAM_HELP_MSG*/ => 'apihelp-bs-task-param-format', - ), - 'token' => array( - ApiBase::PARAM_TYPE => 'string', - ApiBase::PARAM_REQUIRED => true, - 10 /*ApiBase::PARAM_HELP_MSG*/ => 'apihelp-bs-task-param-token', + ApiBase::PARAM_HELP_MSG => 'apihelp-bs-task-param-format', ) ); } @@ -311,8 +362,9 @@ * @return type */ public function getExamples() { + $aTaskNames = $this->oTasksSpec->getTaskNames(); return array( - 'api.php?action='.$this->getModuleName().'&task='.$this->aTasks[0].'&taskData={someKey:"someValue",isFalse:true}', + 'api.php?action='.$this->getModuleName().'&task='.$aTaskNames[0].'&taskData={someKey:"someValue",isFalse:true}', ); } @@ -379,36 +431,18 @@ /** * NOT IMPLEMENTED YET - * Return the param definition for each task - * array( - * taskname => array( - * paramname => array( - * type => string, - * required => true, - * default => '', - * ) - * ) - * ); - * @return array - or false to skip validation - */ - public function getTaskDataDefinitions() { - return false; - } - - /** - * NOT IMPLEMENTED YET * Use ParamProcessor to validate taskData params + * @param string $sTask * @param stdClass $oTaskData * @return stdClass - Standard return */ - protected function validateTaskData( $oTaskData ) { - $aDefinitions = $this->getTaskDataDefinitions(); + protected function validateTaskData( $sTask, $oTaskData ) { + $aDefinitions = $this->oTasksSpec->getTaskDataDefinition( $sTask ); $oReturn = $this->makeStandardReturn(); if( $aDefinitions === false ) { return $oReturn; } //TODO: Use ParamProcessor to validate params defined by - //$this->getTaskDataDefinitions(). return $oReturn; } @@ -417,6 +451,10 @@ * @return string */ public function needsToken() { + if( $this->isTaskDataSchemaCall() || $this->isTaskDataExamplesCall() ) { + return false; + } + return 'csrf'; } @@ -489,4 +527,65 @@ 'getUserTaskPermissions' => array( 'read' ) ); } + + protected function makeTaskHelpMessages() { + $aMessages = []; + $aUrlParams = [ + 'path' => wfScript( 'api' ) + ]; + + foreach( $this->oTasksSpec->getTaskNames() as $sTaskName ) { + $aMessages[$sTaskName] = [ + 'bs-api-task-taskdata-help', + wfExpandUrl( wfAssembleUrl( $aUrlParams + [ + 'query' => wfArrayToCgi( [ + 'action' => $this->getModuleName(), + 'task' => $sTaskName, + 'schema' => '' + ] ) + ] ) ), + wfExpandUrl( wfAssembleUrl( $aUrlParams + [ + 'query' => wfArrayToCgi( [ + 'action' => $this->getModuleName(), + 'task' => $sTaskName, + 'examples' => '' + ] ) + ] ) ) + ]; + } + + return $aMessages; + } + + protected function returnTaskDataSchema( $sTaskName ) { + $this->getResult()->addValue( + null, + 'taskData', + $this->oTasksSpec->getSchema( $sTaskName ) + ); + } + + protected function returnTaskDataExamples( $sTaskName ) { + $this->getResult()->addValue( + null, + 'taskData', + $this->oTasksSpec->getExamples( $sTaskName ) + ); + } + + public function getCustomPrinter() { + if( $this->isTaskDataSchemaCall() || $this->isTaskDataExamplesCall() ) { + return new BSApiFormatJson( $this->getMain(), 'jsonfm' ); + } + + return parent::getCustomPrinter(); + } + + protected function isTaskDataSchemaCall() { + return $this->getRequest()->getVal( 'schema', null ) !== null; + } + + protected function isTaskDataExamplesCall() { + return $this->getRequest()->getVal( 'examples', null ) !== null; + } } diff --git a/includes/api/BSApiWikiPageTasks.php b/includes/api/BSApiWikiPageTasks.php index 9aeb7fb..e5d2512 100644 --- a/includes/api/BSApiWikiPageTasks.php +++ b/includes/api/BSApiWikiPageTasks.php @@ -32,13 +32,146 @@ */ class BSApiWikiPageTasks extends BSApiTasksBase { protected $aTasks = array( - 'setCategories', - 'getExplicitCategories', - 'addCategories', - 'removeCategories', - 'getDiscussionCount' + 'setCategories' => [ + //'permissions' => [], //TODO migrate "getRequiredTaskPermissions" + 'examples' => [ + [ + 'page_id' => 3234, + 'categories' => [ 'Category A', 'Category_B' ] + ], + [ + 'page_title' => 'SomeNamespace:Some page title', + 'categories' => [] + ] + ], + 'params' => [ + 'page_id' => [ + 'type' => 'integer', + 'required' => true, + 'alternative_to' => [ 'page_title' ] + ], + 'page_title' => [ + 'type' => 'string', + 'required' => true, + 'alternative_to' => [ 'page_id' ] + ], + 'categories' => [ + 'type' => 'list', + 'required' => false, + 'default' => [], + ] + ] + ], + 'getExplicitCategories' => [ + 'examples' => [ + [ + 'page_id' => 3234 + ], + [ + 'page_title' => 'SomeNamespace:Some page title', + ] + ], + 'params' => [ + 'page_id' => [ + 'type' => 'integer', + 'required' => true, + 'alternative_to' => [ 'page_title' ] + ], + 'page_title' => [ + 'type' => 'string', + 'required' => true, + 'alternative_to' => [ 'page_id' ] + ] + ] + ], + 'addCategories' => [ + 'examples' => [ + [ + 'page_id' => 3234, + 'categories' => [ 'Category A', 'Category_B' ] + ], + [ + 'page_title' => 'SomeNamespace:Some page title', + 'categories' => [ 'Category A', 'Category_B' ] + ] + ], + 'params' => [ + 'page_id' => [ + 'type' => 'integer', + 'required' => true, + 'alternative_to' => [ 'page_title' ] + ], + 'page_title' => [ + 'type' => 'string', + 'required' => true, + 'alternative_to' => [ 'page_id' ] + ], + 'categories' => [ + 'type' => 'list', + 'required' => false, + 'default' => [], + ] + ] + ], + 'removeCategories' => [ + 'examples' => [ + [ + 'page_id' => 3234, + 'categories' => [ 'Category A', 'Category_B' ] + ], + [ + 'page_title' => 'SomeNamespace:Some page title', + 'categories' => [ 'Category A', 'Category_B' ] + ] + ], + 'params' => [ + 'page_id' => [ + 'descMsg' => 'bs-api-tadk-taskData-page_id', + 'type' => 'integer', + 'required' => true, + 'alternative_to' => [ 'page_title' ] + ], + 'page_title' => [ + 'type' => 'string', + 'required' => true, + 'alternative_to' => [ 'page_id' ] + ], + 'categories' => [ + 'type' => 'list', + 'required' => false, + 'default' => [], + ] + ] + ], + 'examples' => [ + [ + 'page_id' => 3234 + ], + [ + 'page_title' => 'SomeNamespace:Some page title' + ] + ], + 'getDiscussionCount' => [ + //'readonly' => true, //TODO migrate "$this->aReadTasks" + 'params' => [ + 'page_id' => [ + 'type' => 'integer', + 'required' => true, + 'alternative_to' => [ 'page_title' ] + ], + 'page_title' => [ + 'type' => 'string', + 'required' => true, + 'alternative_to' => [ 'page_id' ] + ] + ] + ] ); + protected $aReadTasks = [ + 'getDiscussionCount', 'getExplicitCategories' + ]; + /** * Configures the global permission requirements * @return array diff --git a/includes/utility/BSTasksApiSpec.php b/includes/utility/BSTasksApiSpec.php new file mode 100644 index 0000000..03a2ce4 --- /dev/null +++ b/includes/utility/BSTasksApiSpec.php @@ -0,0 +1,65 @@ +<?php + +class BSTasksApiSpec { + protected $aInitialConfig = []; + + protected $aTaskNames = []; + + public function __construct( $aTasks ) { + $this->aInitialConfig = $aTasks; + $this->extractTaskNames(); + } + + /** + * + * @return string[] + */ + public function getTaskNames() { + return $this->aTaskNames; + } + + /** + * + * @param string $sTaskName + * @return array the spec + */ + public function getTaskSpec( $sTaskName ) { + + } + + /** + * + * @param string $sTaskName + * @return array + */ + public function getTaskDataDefinition( $sTaskName ) { + $aTaskSpec = $this->getTaskSpec( $sTaskName ); + return isset( $aTaskSpec['params'] ) ? $aTaskSpec['params'] : []; + } + + protected function extractTaskNames() { + foreach( $this->aInitialConfig as $mKey => $mValue ) { + if( is_string( $mKey ) && is_array( $mValue ) ) { + $this->aTaskNames[] = $mKey; + } + elseif( is_int( $mKey ) && is_string( $mValue ) ) { + $this->aTaskNames[] = $mValue; + } + else { + throw new MWException( 'Unsupported TaskAPI spec format!' ); + } + } + } + + public function getSchema( $sTaskName ) { + return [ + 'type' => 'object', + 'properties' => [ 'Not implemented yet' ] + ]; + } + + public function getExamples( $sTaskName ) { + return [ 'Not implemented yet' ]; + } + +} \ No newline at end of file diff --git a/tests/utility/BSTasksApiSpecTest.php b/tests/utility/BSTasksApiSpecTest.php new file mode 100644 index 0000000..d4d9823 --- /dev/null +++ b/tests/utility/BSTasksApiSpecTest.php @@ -0,0 +1,47 @@ +<?php +/** + * @group BlueSpice + */ +class BSTasksApiSpecTest extends MediaWikiTestCase { + + public function testGetTaskNames() { + $aTasks = [ 'test1', 'test2', 'test3' ]; + $oSpec = new BSTasksApiSpec( $aTasks ); + $this->assertArrayEquals( $aTasks, $oSpec->getTaskNames() ); + + $aTasks2 = [ + 'test1' => [], + 'test2' => [], + 'test3' => [], + ]; + $oSpec2 = new BSTasksApiSpec( $aTasks2 ); + $this->assertArrayEquals( $aTasks, $oSpec2->getTaskNames() ); + + $aTasks3 = [ + 'test1' => [], + 'test2', + 'test3' => [], + ]; + + $oSpec3 = new BSTasksApiSpec( $aTasks3 ); + $this->assertArrayEquals( $aTasks, $oSpec3->getTaskNames() ); + } + + /** + * @expectedException MWException + */ + public function testUnsupportedTaskSpecException1() { + new BSTasksApiSpec( [ + 'is a string' => 'is not an array' + ] ); + } + + /** + * @expectedException MWException + */ + public function testUnsupportedTaskSpecException2() { + new BSTasksApiSpec( [ + 0 => [ 'key is int, but value is not array' ] + ] ); + } +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/345852 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I6d6b7d4d2e5b619728e843853634e3491ad0e3cb Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation Gerrit-Branch: master Gerrit-Owner: Robert Vogel <vo...@hallowelt.biz> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits