This commit is contained in:
Sara Arjona 2022-12-14 17:51:44 +01:00
commit d98be6a34e
5 changed files with 401 additions and 9 deletions

257
lib/apis.json Normal file
View File

@ -0,0 +1,257 @@
{
"access": {
"component": "core_access",
"allowedlevel2": true,
"allowedspread": false
},
"admin": {
"component": "core_admin",
"allowedlevel2": false,
"allowedspread": false
},
"adminpresets": {
"component": "core_adminpresets",
"allowedlevel2": true,
"allowedspread": false
},
"analytics": {
"component": "core_analytics",
"allowedlevel2": true,
"allowedspread": true
},
"availability": {
"component": "core_availability",
"allowedlevel2": false,
"allowedspread": false
},
"backup": {
"component": "core_backup",
"allowedlevel2": true,
"allowedspread": true
},
"badges": {
"component": "core_badges",
"allowedlevel2": false,
"allowedspread": false
},
"cache": {
"component": "core_cache",
"allowedlevel2": true,
"allowedspread": true
},
"calendar": {
"component": "core_calendar",
"allowedlevel2": false,
"allowedspread": false
},
"check": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"comment": {
"component": "core_comment",
"allowedlevel2": false,
"allowedspread": false
},
"competency": {
"component": "core_competency",
"allowedlevel2": false,
"allowedspread": false
},
"completion": {
"component": "core_completion",
"allowedlevel2": true,
"allowedspread": true
},
"core": {
"component": null,
"allowedlevel2": false,
"allowedspread": false
},
"ddl": {
"component": "core",
"allowedlevel2": true,
"allowedspread": false
},
"dml": {
"component": "core",
"allowedlevel2": true,
"allowedspread": false
},
"enrol": {
"component": "core_enrol",
"allowedlevel2": false,
"allowedspread": false
},
"event": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"external": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"files": {
"component": "core_files",
"allowedlevel2": true,
"allowedspread": false
},
"form": {
"component": "core_form",
"allowedlevel2": true,
"allowedspread": true
},
"grade": {
"component": "core_grade",
"allowedlevel2": false,
"allowedspread": false
},
"grading": {
"component": "core_grading",
"allowedlevel2": false,
"allowedspread": false
},
"group": {
"component": "core_group",
"allowedlevel2": false,
"allowedspread": false
},
"h5p": {
"component": "core_h5p",
"allowedlevel2": true,
"allowedspread": true
},
"lock": {
"component": "core",
"allowedlevel2": true,
"allowedspread": false
},
"log": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"media": {
"component": "core_media",
"allowedlevel2": false,
"allowedspread": false
},
"message": {
"component": "core_message",
"allowedlevel2": true,
"allowedspread": true
},
"navigation": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"oauth2": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"output": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"page": {
"component": "core",
"allowedlevel2": false,
"allowedspread": false
},
"payment": {
"component": "core_payment",
"allowedlevel2": true,
"allowedspread": true
},
"plagiarism": {
"component": "core_plagiarism",
"allowedlevel2": false,
"allowedspread": false
},
"portfolio": {
"component": "core_portfolio",
"allowedlevel2": false,
"allowedspread": false
},
"preference": {
"component": "core",
"allowedlevel2": false,
"allowedspread": false
},
"privacy": {
"component": "core_privacy",
"allowedlevel2": true,
"allowedspread": true
},
"question": {
"component": "core_question",
"allowedlevel2": true,
"allowedspread": true
},
"rating": {
"component": "core_rating",
"allowedlevel2": false,
"allowedspread": false
},
"reportbuilder": {
"component": "core_reportbuilder",
"allowedlevel2": true,
"allowedspread": true
},
"rss": {
"component": "core_rss",
"allowedlevel2": false,
"allowedspread": false
},
"search": {
"component": "core_search",
"allowedlevel2": true,
"allowedspread": true
},
"string": {
"component": "core",
"allowedlevel2": false,
"allowedspread": false
},
"tag": {
"component": "core_tag",
"allowedlevel2": false,
"allowedspread": false
},
"task": {
"component": "core",
"allowedlevel2": true,
"allowedspread": true
},
"test": {
"component": "core",
"allowedlevel2": false,
"allowedspread": false
},
"time": {
"component": "core",
"allowedlevel2": false,
"allowedspread": false
},
"upgrade": {
"component": "core",
"allowedlevel2": true,
"allowedspread": false
},
"webservice": {
"component": "core_webservice",
"allowedlevel2": false,
"allowedspread": false
},
"xapi": {
"component": "core_xapi",
"allowedlevel2": true,
"allowedspread": true
}
}

30
lib/apis.schema.json Normal file
View File

@ -0,0 +1,30 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://moodle.org/apis.schema.json",
"title": "APIs",
"description": "Moodle valid APIs",
"type": "object",
"patternProperties": {
"^[a-z][a-z0-9]+$": {
"type": "object",
"properties": {
"component": {
"description": "Component the API belongs to, usually a subsystem or core. Null for the 'core' API itself",
"type": [ "string", "null" ],
"pattern": "^(core|[a-z][a-z0-9_]+)$"
},
"allowedlevel2": {
"description": "Can the API be used as level 2 namespace",
"type": "boolean"
},
"allowedspread": {
"description": "Can the API be used out from its own component",
"type": "boolean"
}
},
"minProperties": 3,
"maxProperties": 3,
"additionalProperties": false
}
}
}

View File

@ -72,6 +72,8 @@ class core_component {
protected static $parents = null;
/** @var array subplugins */
protected static $subplugins = null;
/** @var array cache of core APIs */
protected static $apis = null;
/** @var array list of all known classes that can be autoloaded */
protected static $classmap = null;
/** @var array list of all classes that have been renamed to be autoloaded */
@ -256,6 +258,7 @@ class core_component {
self::$subsystems = $cache['subsystems'];
self::$parents = $cache['parents'];
self::$subplugins = $cache['subplugins'];
self::$apis = $cache['apis'];
self::$classmap = $cache['classmap'];
self::$classmaprenames = $cache['classmaprenames'];
self::$filemap = $cache['filemap'];
@ -296,6 +299,7 @@ class core_component {
self::$subsystems = $cache['subsystems'];
self::$parents = $cache['parents'];
self::$subplugins = $cache['subplugins'];
self::$apis = $cache['apis'];
self::$classmap = $cache['classmap'];
self::$classmaprenames = $cache['classmaprenames'];
self::$filemap = $cache['filemap'];
@ -382,6 +386,7 @@ class core_component {
'plugins' => self::$plugins,
'parents' => self::$parents,
'subplugins' => self::$subplugins,
'apis' => self::$apis,
'classmap' => self::$classmap,
'classmaprenames' => self::$classmaprenames,
'filemap' => self::$filemap,
@ -406,6 +411,8 @@ $cache = '.var_export($cache, true).';
self::$plugins[$type] = self::fetch_plugins($type, $fulldir);
}
self::$apis = self::fetch_apis();
self::fill_classmap_cache();
self::fill_classmap_renames_cache();
self::fill_filemap_cache();
@ -455,6 +462,14 @@ $cache = '.var_export($cache, true).';
return $info;
}
/**
* Returns list of core APIs.
* @return stdClass[]
*/
protected static function fetch_apis() {
return (array) json_decode(file_get_contents(__DIR__ . '/../apis.json'));
}
/**
* Returns list of known plugin types.
* @return array
@ -751,6 +766,22 @@ $cache = '.var_export($cache, true).';
return self::$subsystems;
}
/**
* List all core APIs and their attributes.
*
* This is a list of all the existing / allowed APIs in moodle, each one with the
* following attributes:
* - component: the component, usually a subsystem or core, the API belongs to.
* - allowedlevel2: if the API is allowed as level2 namespace or no.
* - allowedspread: if the API can spread out from its component or no.
*
* @return stdClass[] array of APIs (as keys) with their attributes as object instances.
*/
public static function get_core_apis() {
self::init();
return self::$apis;
}
/**
* Get list of available plugin types together with their location.
*
@ -1176,6 +1207,16 @@ $cache = '.var_export($cache, true).';
return isset(self::$subsystems[$subsystemname]);
}
/**
* Return true if apiname is a core API.
*
* @param string $apiname name of the API.
* @return bool true if core API.
*/
public static function is_core_api($apiname) {
return isset(self::$apis[$apiname]);
}
/**
* Records all class renames that have been made to facilitate autoloading.
*/
@ -1283,4 +1324,13 @@ $cache = '.var_export($cache, true).';
}
return $componentnames;
}
/**
* Returns the list of available API names.
*
* @return string[] the list of available API names.
*/
public static function get_core_api_names(): array {
return array_keys(self::get_core_apis());
}
}

View File

@ -18,16 +18,11 @@
* core_component related tests.
*
* @package core
* @category phpunit
* @category test
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class core_component_testcase.
*
* @covers \core_component
*/
class component_test extends advanced_testcase {
@ -843,4 +838,64 @@ class component_test extends advanced_testcase {
$this->assertContains('tool_usertours', $componentnames);
$this->assertContains('core_favourites', $componentnames);
}
/**
* Basic tests for APIs related functions in the core_component class.
*/
public function test_apis_methods() {
$apis = core_component::get_core_apis();
$this->assertIsArray($apis);
$apinames = core_component::get_core_api_names();
$this->assertIsArray($apis);
// Both should return the very same APIs.
$this->assertEquals($apinames, array_keys($apis));
$this->assertFalse(core_component::is_core_api('lalala'));
$this->assertTrue(core_component::is_core_api('privacy'));
}
/**
* Test that the apis.json structure matches expectations
*
* While we include an apis.schema.json file in core, there isn't any PHP built-in allowing us
* to validate it (3rd part libraries needed). Plus the schema doesn't allow to validate things
* like uniqueness or sorting. We are going to do all that here.
*/
public function test_apis_json_validation() {
$apis = $sortedapis = core_component::get_core_apis();
ksort($sortedapis); // We'll need this later.
// General structure validations.
$this->assertIsArray($apis);
$this->assertGreaterThan(25, count($apis));
$this->assertArrayHasKey('privacy', $apis); // Verify a few.
$this->assertArrayHasKey('external', $apis);
$this->assertArrayHasKey('search', $apis);
$this->assertEquals(array_keys($sortedapis), array_keys($apis)); // Verify json is sorted alphabetically.
// Iterate over all apis and perform more validations.
foreach ($apis as $apiname => $attributes) {
// Message, to be used later and easier finding the problem.
$message = "Validation problem found with API: ${apiname}";
$this->assertIsObject($attributes, $message);
$this->assertMatchesRegularExpression('/^[a-z][a-z0-9]+$/', $apiname, $message);
$this->assertEquals(['component', 'allowedlevel2', 'allowedspread'], array_keys((array)$attributes), $message);
// Verify attributes.
if ($apiname !== 'core') { // Exception for core api, it doesn't have component.
$this->assertMatchesRegularExpression('/^(core|[a-z][a-z0-9_]+)$/', $attributes->component, $message);
} else {
$this->assertNull($attributes->component, $message);
}
$this->assertIsBool($attributes->allowedlevel2, $message);
$this->assertIsBool($attributes->allowedspread, $message);
// Cannot spread if level2 is not allowed.
$this->assertLessThanOrEqual($attributes->allowedlevel2, $attributes->allowedspread, $message);
}
}
}

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2022120900.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2022120900.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.2dev (Build: 20221209)'; // Human-friendly version name