mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
MDL-78192 external: allow nullable non-scalar params and results
This commit is contained in:
parent
3cd84747cb
commit
16729b7f02
6
lib/external/classes/external_api.php
vendored
6
lib/external/classes/external_api.php
vendored
@ -312,6 +312,9 @@ class external_api {
|
||||
* @since Moodle 2.0
|
||||
*/
|
||||
public static function validate_parameters(external_description $description, $params) {
|
||||
if ($params === null && $description->allownull == NULL_ALLOWED) {
|
||||
return null;
|
||||
}
|
||||
if ($description instanceof external_value) {
|
||||
if (is_array($params) || is_object($params)) {
|
||||
throw new invalid_parameter_exception('Scalar type expected, array or object received.');
|
||||
@ -398,6 +401,9 @@ class external_api {
|
||||
* @since Moodle 2.0
|
||||
*/
|
||||
public static function clean_returnvalue(external_description $description, $response) {
|
||||
if ($response === null && $description->allownull == NULL_ALLOWED) {
|
||||
return null;
|
||||
}
|
||||
if ($description instanceof external_value) {
|
||||
if (is_array($response) || is_object($response)) {
|
||||
throw new invalid_response_exception('Scalar type expected, array or object received.');
|
||||
|
@ -33,14 +33,18 @@ abstract class external_description {
|
||||
/** @var mixed Default value */
|
||||
public $default;
|
||||
|
||||
/** @var bool Allow null values */
|
||||
public $allownull;
|
||||
|
||||
/**
|
||||
* Contructor.
|
||||
*
|
||||
* @param string $desc Description of element
|
||||
* @param int $required Whether the element value is required. Valid values are VALUE_DEFAULT, VALUE_REQUIRED, VALUE_OPTIONAL.
|
||||
* @param mixed $default The default value
|
||||
* @param bool $allownull Allow null value
|
||||
*/
|
||||
public function __construct($desc, $required, $default) {
|
||||
public function __construct($desc, $required, $default, $allownull = NULL_NOTALLOWED) {
|
||||
if (!in_array($required, [VALUE_DEFAULT, VALUE_REQUIRED, VALUE_OPTIONAL], true)) {
|
||||
$requiredstr = $required;
|
||||
if (is_array($required)) {
|
||||
@ -52,5 +56,6 @@ abstract class external_description {
|
||||
$this->desc = $desc;
|
||||
$this->required = $required;
|
||||
$this->default = $default;
|
||||
$this->allownull = (bool)$allownull;
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,6 @@ class external_function_parameters extends external_single_structure {
|
||||
}
|
||||
}
|
||||
}
|
||||
parent::__construct($keys, $desc, $required, $default);
|
||||
parent::__construct($keys, $desc, $required, $default, NULL_NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
@ -35,14 +35,16 @@ class external_multiple_structure extends external_description {
|
||||
* @param string $desc
|
||||
* @param int $required
|
||||
* @param array $default
|
||||
* @param bool $allownull
|
||||
*/
|
||||
public function __construct(
|
||||
external_description $content,
|
||||
$desc = '',
|
||||
$required = VALUE_REQUIRED,
|
||||
$default = null
|
||||
$default = null,
|
||||
$allownull = NULL_NOT_ALLOWED
|
||||
) {
|
||||
parent::__construct($desc, $required, $default);
|
||||
parent::__construct($desc, $required, $default, $allownull);
|
||||
$this->content = $content;
|
||||
}
|
||||
}
|
||||
|
@ -35,14 +35,16 @@ class external_single_structure extends external_description {
|
||||
* @param string $desc
|
||||
* @param int $required
|
||||
* @param array $default
|
||||
* @param bool $allownull
|
||||
*/
|
||||
public function __construct(
|
||||
array $keys,
|
||||
$desc = '',
|
||||
$required = VALUE_REQUIRED,
|
||||
$default = null
|
||||
$default = null,
|
||||
$allownull = NULL_NOT_ALLOWED
|
||||
) {
|
||||
parent::__construct($desc, $required, $default);
|
||||
parent::__construct($desc, $required, $default, $allownull);
|
||||
$this->keys = $keys;
|
||||
}
|
||||
}
|
||||
|
6
lib/external/classes/external_value.php
vendored
6
lib/external/classes/external_value.php
vendored
@ -28,9 +28,6 @@ class external_value extends external_description {
|
||||
/** @var mixed Value type PARAM_XX */
|
||||
public $type;
|
||||
|
||||
/** @var bool Allow null values */
|
||||
public $allownull;
|
||||
|
||||
/**
|
||||
* Constructor for the external_value class.
|
||||
*
|
||||
@ -47,8 +44,7 @@ class external_value extends external_description {
|
||||
$default = null,
|
||||
$allownull = NULL_ALLOWED
|
||||
) {
|
||||
parent::__construct($desc, $required, $default);
|
||||
parent::__construct($desc, $required, $default, $allownull);
|
||||
$this->type = $type;
|
||||
$this->allownull = $allownull;
|
||||
}
|
||||
}
|
||||
|
96
lib/external/tests/external_api_test.php
vendored
96
lib/external/tests/external_api_test.php
vendored
@ -82,6 +82,52 @@ class external_api_test extends \advanced_testcase {
|
||||
$this->assertSame('someid', key($result));
|
||||
$this->assertSame(6, $result['someid']);
|
||||
$this->assertSame('aaa', $result['text']);
|
||||
|
||||
// Missing required value (an exception is thrown).
|
||||
$testdata = [];
|
||||
try {
|
||||
external_api::clean_returnvalue($description, $testdata);
|
||||
$this->fail('Exception expected');
|
||||
} catch (\moodle_exception $ex) {
|
||||
$this->assertInstanceOf(\invalid_response_exception::class, $ex);
|
||||
$this->assertSame('Invalid response value detected (Error in response - '
|
||||
. 'Missing following required key in a single structure: text)', $ex->getMessage());
|
||||
}
|
||||
|
||||
// Test nullable external_value may optionally return data.
|
||||
$description = new external_function_parameters([
|
||||
'value' => new external_value(PARAM_INT, '', VALUE_REQUIRED, null, NULL_ALLOWED)
|
||||
]);
|
||||
$testdata = ['value' => null];
|
||||
$cleanedvalue = external_api::clean_returnvalue($description, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
$testdata = ['value' => 1];
|
||||
$cleanedvalue = external_api::clean_returnvalue($description, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
|
||||
// Test nullable external_single_structure may optionally return data.
|
||||
$description = new external_function_parameters([
|
||||
'value' => new external_single_structure(['value2' => new external_value(PARAM_INT)],
|
||||
'', VALUE_REQUIRED, null, NULL_ALLOWED)
|
||||
]);
|
||||
$testdata = ['value' => null];
|
||||
$cleanedvalue = external_api::clean_returnvalue($description, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
$testdata = ['value' => ['value2' => 1]];
|
||||
$cleanedvalue = external_api::clean_returnvalue($description, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
|
||||
// Test nullable external_multiple_structure may optionally return data.
|
||||
$description = new external_function_parameters([
|
||||
'value' => new external_multiple_structure(
|
||||
new external_value(PARAM_INT), '', VALUE_REQUIRED, null, NULL_ALLOWED)
|
||||
]);
|
||||
$testdata = ['value' => null];
|
||||
$cleanedvalue = external_api::clean_returnvalue($description, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
$testdata = ['value' => [1]];
|
||||
$cleanedvalue = external_api::clean_returnvalue($description, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,8 +208,56 @@ class external_api_test extends \advanced_testcase {
|
||||
$singlestructure['object'] = $object;
|
||||
$singlestructure['value2'] = 'Some text';
|
||||
$testdata = [$singlestructure];
|
||||
$this->expectException('invalid_response_exception');
|
||||
try {
|
||||
external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->fail('Exception expected');
|
||||
} catch (\moodle_exception $ex) {
|
||||
$this->assertInstanceOf(\invalid_response_exception::class, $ex);
|
||||
$this->assertSame('Invalid response value detected (object => Invalid response value detected '
|
||||
. '(Error in response - Missing following required key in a single structure: value1): Error in response - '
|
||||
. 'Missing following required key in a single structure: value1)', $ex->getMessage());
|
||||
}
|
||||
|
||||
// Fail if no data provided when value required.
|
||||
$testdata = null;
|
||||
try {
|
||||
external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->fail('Exception expected');
|
||||
} catch (\moodle_exception $ex) {
|
||||
$this->assertInstanceOf(\invalid_response_exception::class, $ex);
|
||||
$this->assertSame('Invalid response value detected (Only arrays accepted. The bad value is: \'\')',
|
||||
$ex->getMessage());
|
||||
}
|
||||
|
||||
// Test nullable external_multiple_structure may optionally return data.
|
||||
$returndesc = new external_multiple_structure(
|
||||
new external_value(PARAM_INT),
|
||||
'', VALUE_REQUIRED, null, NULL_ALLOWED);
|
||||
$testdata = null;
|
||||
$cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
$testdata = [1];
|
||||
$cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
|
||||
// Test nullable external_single_structure may optionally return data.
|
||||
$returndesc = new external_single_structure(['value' => new external_value(PARAM_INT)],
|
||||
'', VALUE_REQUIRED, null, NULL_ALLOWED);
|
||||
$testdata = null;
|
||||
$cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
$testdata = ['value' => 1];
|
||||
$cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
|
||||
// Test nullable external_value may optionally return data.
|
||||
$returndesc = new external_value(PARAM_INT, '', VALUE_REQUIRED, null, NULL_ALLOWED);
|
||||
$testdata = null;
|
||||
$cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
$testdata = 1;
|
||||
$cleanedvalue = external_api::clean_returnvalue($returndesc, $testdata);
|
||||
$this->assertSame($testdata, $cleanedvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user