MDL-12886 external lib parameter validation routine + some unit tests

This commit is contained in:
skodak 2009-10-02 12:13:56 +00:00
parent f60ecadf2c
commit c9c5cc81e9
4 changed files with 118 additions and 28 deletions

View File

@ -45,7 +45,7 @@ class moodle_group_external extends external_api {
global $CFG;
require_once("$CFG->dirroot/group/lib.php");
$params = self::validate_prameters(self::create_groups_parameters(), array('groups'=>$groups));
$params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups));
$groups = array();
@ -82,13 +82,7 @@ class moodle_group_external extends external_api {
public static function get_groups_parameters() {
return new external_function_parameters(
array(
'groups' => new external_multiple_structure(
new external_single_structure(
array(
'groupid' => new external_param(PARAM_INT, 'Group ID')
)
)
)
'groups' => new external_multiple_structure(new external_param(PARAM_INT, 'Group ID'))
)
);
}
@ -101,7 +95,7 @@ class moodle_group_external extends external_api {
public static function get_groups($groupids) {
$groups = array();
$params = self::validate_prameters(self::get_groups_parameters(), array('groupids'=>$groupids));
$params = self::validate_parameters(self::get_groups_parameters(), array('groupids'=>$groupids));
//TODO: we do need to search for groups in courses too,
// fetching by id is not enough!
@ -147,7 +141,7 @@ class moodle_group_external extends external_api {
global $CFG;
require_once("$CFG->dirroot/group/lib.php");
$params = self::validate_prameters(self::delete_groups_parameters(), array('groupids'=>$groupids));
$params = self::validate_parameters(self::delete_groups_parameters(), array('groupids'=>$groupids));
$groups = array();
@ -185,7 +179,7 @@ class moodle_group_external extends external_api {
public static function get_groupmembers($groupids) {
$groups = array();
$params = self::validate_prameters(self::get_groupmembers_parameters(), array('groupids'=>$groupids));
$params = self::validate_parameters(self::get_groupmembers_parameters(), array('groupids'=>$groupids));
foreach ($params['groupids'] as $groupid) {
// validate params
@ -221,7 +215,7 @@ class moodle_group_external extends external_api {
global $CFG;
require_once("$CFG->dirroot/group/lib.php");
$params = self::validate_prameters(self::add_groupmembers_parameters(), array('members'=>$members));
$params = self::validate_parameters(self::add_groupmembers_parameters(), array('members'=>$members));
foreach ($params['members'] as $member) {
// validate params
@ -259,7 +253,7 @@ class moodle_group_external extends external_api {
global $CFG;
require_once("$CFG->dirroot/group/lib.php");
$params = self::validate_prameters(self::delete_groupmembers_parameters(), array($members=>'members'));
$params = self::validate_parameters(self::delete_groupmembers_parameters(), array($members=>'members'));
foreach ($params['members'] as $member) {
// validate params

View File

@ -53,16 +53,55 @@ class external_api {
}
/**
* Validates submitted function barameters, if anything is incorrect
* Validates submitted function parameters, if anything is incorrect
* invalid_parameter_exception is thrown.
* @param ? $description description of parameters
* @param ? $params the actual parameters
* @return ? params with added defaults for optional items, invalid_parameters_exception thrown if any problem found
* @param external_description $description description of parameters
* @param mixed $params the actual parameters
* @return mixed params with added defaults for optional items, invalid_parameters_exception thrown if any problem found
*/
public static function validate_prameters($description, $params) {
//TODO: we need to define the structure of param descriptions
public static function validate_parameters(external_description $description, $params) {
if ($description instanceof external_param) {
if (is_array($params) or is_object($params)) {
throw new invalid_parameter_exception('Scalar type expected, array or object received.');
}
return validate_param($params, $description->type, $description->allownull, 'Invalid external api parameter');
return $params;
} else if ($description instanceof external_single_structure) {
if (!is_array($params)) {
throw new invalid_parameter_exception('Only arrays accepted.');
}
$result = array();
foreach ($description->keys as $key=>$subdesc) {
if (!array_key_exists($key, $params)) {
if ($subdesc->required) {
throw new invalid_parameter_exception('Missing required key in single structure.');
}
if ($subdesc instanceof external_param) {
$result[$key] = self::validate_parameters($subdesc, $subdesc->default);
}
} else {
$result[$key] = self::validate_parameters($subdesc, $params[$key]);
}
unset($params[$key]);
}
if (!empty($params)) {
throw new invalid_parameter_exception('Unexpected keys detected in parameter array.');
}
return $result;
} else if ($description instanceof external_multiple_structure) {
if (!is_array($params)) {
throw new invalid_parameter_exception('Only arrays accepted.');
}
$result = array();
foreach ($params as $param) {
$result[] = self::validate_parameters($description->content, $param);
}
return $result;
} else {
throw new invalid_parameter_exception('Invalid external api description.');
}
}
/**
@ -97,7 +136,7 @@ class external_api {
// proper enrolment and course visibility check
// similar to require_login() (which can not be used
// because it can be used only once and redirects)
// oh - did I tell we need to rewrite enrolments in 2.0
// oh - did I say we need to rewrite enrolments in 2.0
// to solve this bloody mess?
//
// missing: hidden courses and categories, groupmembersonly,
@ -121,7 +160,7 @@ abstract class external_description {
* @param string $desc
* @param bool $required
*/
public function __contruct($desc, $required) {
public function __construct($desc, $required) {
$this->desc = $desc;
$this->required = $required;
}
@ -146,8 +185,8 @@ class external_param extends external_description {
* @param mixed $default
* @param bool $allownull
*/
public function __contruct($type, $desc='', $required=true, $default=null, $allownull=true) {
parent::_construct($desc, $required);
public function __construct($type, $desc='', $required=true, $default=null, $allownull=true) {
parent::__construct($desc, $required);
$this->type = $type;
$this->default = $default;
$this->allownull = $allownull;
@ -168,7 +207,7 @@ class external_single_structure extends external_description {
* @param bool $required
*/
public function __construct(array $keys, $desc='', $required=true) {
parent::_construct($desc, $required);
parent::__construct($desc, $required);
$this->keys = $keys;
}
}
@ -187,7 +226,7 @@ class external_multiple_structure extends external_description {
* @param bool $required
*/
public function __construct(external_description $content, $desc='', $required=true) {
parent::_construct($desc, $required);
parent::__construct($desc, $required);
$this->content = $content;
}
}

View File

@ -0,0 +1,57 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for /lib/externallib.php.
*
* @package webservices
* @copyright 2009 Pwetr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->libdir . '/externallib.php');
class externallib_test extends UnitTestCase {
public function test_validate_params1() {
$params = array('text'=>'aaa', 'someid'=>'6',);
$description = new external_function_parameters(array('someid' => new external_param(PARAM_INT, 'Some int value'),
'text' => new external_param(PARAM_ALPHA, 'Some text value')));
$result = external_api::validate_parameters($description, $params);
$this->assertEqual(count($result), 2);
reset($result);
$this->assertTrue(key($result) === 'someid');
$this->assertTrue($result['someid'] === 6);
$this->assertTrue($result['text'] === 'aaa');
$params = array('someids'=>array('1', 2, 'a'=>'3'), 'scalar'=>666);
$description = new external_function_parameters(array('someids' => new external_multiple_structure(new external_param(PARAM_INT, 'Some ID')),
'scalar' => new external_param(PARAM_ALPHANUM, 'Some text value')));
$result = external_api::validate_parameters($description, $params);
$this->assertEqual(count($result), 2);
reset($result);
$this->assertTrue(key($result) === 'someids');
$this->assertTrue($result['someids'] == array(0=>1, 1=>2, 2=>3));
$this->assertTrue($result['scalar'] === '666');
}
//TODO: add unittests for all description options and validation failures
}

View File

@ -87,7 +87,7 @@ class moodle_user_external extends external_api {
// 2) All required items were sent
// 3) All data passes clean_param without changes (yes this is strict)
// If any problems are found then exceptions are thrown with helpful error messages
$params = self::validate_prameters(self::create_users_parameters(), $params);
$params = self::validate_parameters(self::create_users_parameters(), $params);
// Perform further checks and build up a clean array of user data
@ -184,7 +184,7 @@ class moodle_user_external extends external_api {
require_capability('moodle/user:viewdetails', $context);
self::validate_context($context);
$params = self::validate_prameters(self::get_users_parameters(), $params);
$params = self::validate_parameters(self::get_users_parameters(), $params);
//TODO: this search is probably useless for external systems because it is not exact
// 1/ we should specify multiple search parameters including the mnet host id