From 2cc4b0c077cc269b84fe21759473056118b7bab2 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 13 Sep 2021 17:05:24 -0700 Subject: [PATCH] (REF-1) Convert Civi/WorkflowMessage/Examples.php to Civi/Test/ExampleDataLoader.php --- Civi/Api4/Action/ExampleData/Get.php | 17 +- Civi/Test.php | 23 +++ Civi/Test/ExampleDataInterface.php | 32 ++++ Civi/Test/ExampleDataLoader.php | 135 ++++++++++++++ Civi/WorkflowMessage/Examples.php | 172 ------------------ .../phpunit/api/v4/Entity/ExampleDataTest.php | 21 ++- 6 files changed, 211 insertions(+), 189 deletions(-) create mode 100644 Civi/Test/ExampleDataInterface.php create mode 100644 Civi/Test/ExampleDataLoader.php delete mode 100644 Civi/WorkflowMessage/Examples.php diff --git a/Civi/Api4/Action/ExampleData/Get.php b/Civi/Api4/Action/ExampleData/Get.php index 6281548675..9f91c82221 100644 --- a/Civi/Api4/Action/ExampleData/Get.php +++ b/Civi/Api4/Action/ExampleData/Get.php @@ -14,7 +14,7 @@ namespace Civi\Api4\Action\ExampleData; use Civi\Api4\Generic\BasicGetAction; use Civi\Api4\Generic\Result; -use Civi\WorkflowMessage\Examples; +use Civi\Test\ExampleDataLoader; /** * Get a list of example data-sets. @@ -27,13 +27,7 @@ use Civi\WorkflowMessage\Examples; */ class Get extends BasicGetAction { - /** - * @var \Civi\WorkflowMessage\Examples - */ - private $_scanner; - public function _run(Result $result) { - $this->_scanner = new Examples(); if ($this->select !== [] && !in_array('name', $this->select)) { $this->select[] = 'name'; } @@ -41,16 +35,19 @@ class Get extends BasicGetAction { } protected function getRecords() { - return $this->_scanner->findAll(); + return \Civi\Test::examples()->getMetas(); } protected function selectArray($values) { $result = parent::selectArray($values); - $heavyFields = array_intersect(['data', 'asserts'], $this->select ?: []); + $heavyFields = array_intersect( + explode(',', ExampleDataLoader::HEAVY_FIELDS), + $this->select ?: [] + ); if (!empty($heavyFields)) { foreach ($result as &$item) { - $heavy = $this->_scanner->getHeavy($item['name']); + $heavy = \Civi\Test::examples()->getFull($item['name']); $item = array_merge($item, \CRM_Utils_Array::subset($heavy, $heavyFields)); } } diff --git a/Civi/Test.php b/Civi/Test.php index cf9c841ab7..ff49854423 100644 --- a/Civi/Test.php +++ b/Civi/Test.php @@ -182,6 +182,29 @@ class Test { return self::$singletons['data']; } + /** + * @return \Civi\Test\ExampleDataLoader + */ + public static function examples(): \Civi\Test\ExampleDataLoader { + if (!isset(self::$singletons['examples'])) { + self::$singletons['examples'] = new \Civi\Test\ExampleDataLoader(); + } + return self::$singletons['examples']; + } + + /** + * @param string $name + * Symbolic name of the data-set. + * @return array + */ + public static function example(string $name): array { + $result = static::examples()->getFull($name); + if ($result === NULL) { + throw new \CRM_Core_Exception("Failed to load example data-set: $name"); + } + return $result; + } + /** * Prepare and execute a batch of SQL statements. * diff --git a/Civi/Test/ExampleDataInterface.php b/Civi/Test/ExampleDataInterface.php new file mode 100644 index 0000000000..1389b05cdd --- /dev/null +++ b/Civi/Test/ExampleDataInterface.php @@ -0,0 +1,32 @@ +build($example);` + * - They are not generated by '$ex->getExamples();' + * - They are returned by `$this->getFull()` + * - They are not returned by `$this->getMeta()`. + */ + const HEAVY_FIELDS = 'data,asserts'; + + /** + * @var array|null + */ + private $metas; + + /** + * Get a list of all examples, including basic metadata (name, title, workflow). + * + * @return array + * Ex: ['my_example' => ['title' => ..., 'workflow' => ..., 'tags' => ...]] + * @throws \ReflectionException + */ + public function getMetas(): array { + if ($this->metas === NULL) { + // $cache = new \CRM_Utils_Cache_NoCache([]); + $cache = \CRM_Utils_Constant::value('CIVICRM_TEST') ? new \CRM_Utils_Cache_NoCache([]) : \Civi::cache('long'); + $cacheKey = \CRM_Utils_String::munge(__CLASS__); + $this->metas = $cache->get($cacheKey); + if ($this->metas === NULL) { + $this->metas = $this->findMetas(); + $cache->set($cacheKey, $this->metas); + } + } + return $this->metas; + } + + public function getMeta(string $name): ?array { + $all = $this->getMetas(); + return $all[$name] ?? NULL; + } + + /** + * @param string $name + * + * @return array|null + */ + public function getFull(string $name): ?array { + $example = $this->getMeta($name); + if ($example === NULL) { + return NULL; + } + + if ($example['file']) { + include_once $example['file']; + } + $obj = new $example['class'](); + $obj->build($example); + return $example; + } + + /** + * Get a list of all examples, including basic metadata (name, title, workflow). + * + * @return array + * Ex: ['my_example' => ['title' => ..., 'workflow' => ..., 'tags' => ...]] + * @throws \ReflectionException + */ + protected function findMetas(): array { + $classes = array_merge( + // This scope of search is decidedly narrow - it should probably be expanded. + $this->scanExampleClasses(\Civi::paths()->getPath('[civicrm.root]/'), 'CRM/*/WorkflowMessage', '_'), + $this->scanExampleClasses(\Civi::paths()->getPath('[civicrm.root]/'), 'Civi/*/WorkflowMessage', '\\'), + $this->scanExampleClasses(\Civi::paths()->getPath('[civicrm.root]/'), 'Civi/WorkflowMessage', '\\'), + $this->scanExampleClasses(\Civi::paths()->getPath('[civicrm.root]/tests/phpunit/'), 'CRM/*/WorkflowMessage', '_'), + $this->scanExampleClasses(\Civi::paths()->getPath('[civicrm.root]/tests/phpunit/'), 'Civi/*/WorkflowMessage', '\\') + ); + + $all = []; + foreach ($classes as $file => $class) { + require_once $file; + $obj = new $class(); + $offset = 0; + foreach ($obj->getExamples() as $example) { + $example['file'] = $file; + $example['class'] = $class; + if (!isset($example['name'])) { + $example['name'] = $example['class'] . '#' . $offset; + } + $all[$example['name']] = $example; + $offset++; + } + } + + return $all; + } + + /** + * @param $classRoot + * Ex: Civi root dir. + * @param $classDir + * Folder to search (within the parent). + * @param $classDelim + * Namespace separator, eg underscore or backslash. + * @return array + * Array(string $relativeFileName => string $className). + */ + private function scanExampleClasses($classRoot, $classDir, $classDelim): array { + $r = []; + $exDirs = (array) glob($classRoot . $classDir); + foreach ($exDirs as $exDir) { + foreach (\CRM_Utils_File::findFiles($exDir, '*.ex.php') as $file) { + $file = str_replace(DIRECTORY_SEPARATOR, '/', $file); + $file = \CRM_Utils_File::relativize($file, \CRM_Utils_File::addTrailingSlash($classRoot, '/')); + $class = str_replace('/', $classDelim, preg_replace('/\.ex\.php$/', '', $file)); + $r[$file] = $class; + } + } + return $r; + } + +} diff --git a/Civi/WorkflowMessage/Examples.php b/Civi/WorkflowMessage/Examples.php deleted file mode 100644 index 52ec6dae42..0000000000 --- a/Civi/WorkflowMessage/Examples.php +++ /dev/null @@ -1,172 +0,0 @@ -cache = $cache ?: \Civi::cache('short' /* long */); - $this->cacheKey = \CRM_Utils_String::munge(__CLASS__); - } - - /** - * Get a list of all examples, including basic metadata (name, title, workflow). - * - * @return array - * Ex: ['my_example' => ['title' => ..., 'workflow' => ..., 'tags' => ...]] - * @throws \ReflectionException - */ - public function findAll(): array { - $all = $this->cache->get($this->cacheKey); - if ($all === NULL) { - $all = []; - $wfClasses = Invasive::call([WorkflowMessage::class, 'getWorkflowNameClassMap']); - foreach ($wfClasses as $workflow => $class) { - try { - $classFile = (new \ReflectionClass($class))->getFileName(); - } - catch (\ReflectionException $e) { - throw new \RuntimeException("Failed to locate workflow class ($class)", 0, $e); - } - $classDir = preg_replace('/\.php$/', '', $classFile); - if (is_dir($classDir)) { - $all = array_merge($all, $this->scanDir($classDir, $workflow)); - } - } - } - return $all; - } - - /** - * @param string $dir - * @param string $workflow - * @return array - * Ex: ['my_example' => ['title' => ..., 'workflow' => ..., 'tags' => ...]] - */ - protected function scanDir($dir, $workflow) { - $all = []; - $files = (array) glob($dir . "/*.ex.php"); - foreach ($files as $file) { - $name = $workflow . '.' . preg_replace('/\.ex.php/', '', basename($file)); - $scanRecord = [ - 'name' => $name, - 'title' => $name, - 'workflow' => $workflow, - 'tags' => [], - 'file' => $file, - // ^^ relativize? - ]; - $rawRecord = $this->loadFile($file); - $all[$name] = array_merge($scanRecord, \CRM_Utils_Array::subset($rawRecord, ['name', 'title', 'workflow', 'tags'])); - } - return $all; - } - - /** - * Load an example data file (based on its file path). - * - * @param string $_exFile - * Loadable PHP filename. - * @return array - * The raw/unevaluated dataset. - */ - public function loadFile($_exFile): array { - // Isolate variables. - // If you need export values, use something like `extract($_tplVars);` - return require $_exFile; - } - - /** - * Get example data (based on its symbolic name). - * - * @param string|string[] $nameOrPath - * Ex: "foo" -> load all the data from example "foo" - * Ex: "foo.b.a.r" -> load the example "foo" and pull out the data from $foo['b']['a']['r'] - * Ex: ["foo","b","a","r"] - Same as above. But there is no ambiguity with nested dots. - * @return array - */ - public function get($nameOrPath) { - $path = is_array($nameOrPath) ? $nameOrPath : explode('.', $nameOrPath); - $exampleName = array_shift($path) . '.' . array_shift($path); - return \CRM_Utils_Array::pathGet($this->getHeavy($exampleName), $path); - } - - /** - * Get one of the "heavy" properties. - * - * @param string $name - * @return array - * @throws \ReflectionException - */ - public function getHeavy(string $name): array { - if (isset($this->heavyCache[$name])) { - return $this->heavyCache[$name]; - - } - $all = $this->findAll(); - if (!isset($all[$name])) { - throw new \RuntimeException("Cannot load example ($name)"); - } - $heavyRecord = $all[$name]; - $loaded = $this->loadFile($all[$name]['file']); - foreach (['data', 'asserts'] as $heavyField) { - if (isset($loaded[$heavyField])) { - $heavyRecord[$heavyField] = $loaded[$heavyField] instanceof \Closure - ? call_user_func($loaded[$heavyField], $this) - : $loaded[$heavyField]; - } - } - - $this->heavyCache[$name] = $heavyRecord; - return $this->heavyCache[$name]; - } - - /** - * Get an example and merge/extend it with more data. - * - * @param string|string[] $nameOrPath - * Ex: "foo" -> load all the data from example "foo" - * Ex: "foo.b.a.r" -> load the example "foo" and pull out the data from $foo['b']['a']['r'] - * Ex: ["foo","b","a","r"] - Same as above. But there is no ambiguity with nested dots. - * @param array $overrides - * Data to add. - * @return array - * The result of merging the original example with the $overrides. - */ - public function extend($nameOrPath, $overrides = []) { - $data = $this->get($nameOrPath); - \CRM_Utils_Array::extend($data, $overrides); - return $data; - } - -} diff --git a/tests/phpunit/api/v4/Entity/ExampleDataTest.php b/tests/phpunit/api/v4/Entity/ExampleDataTest.php index 540bf3311f..88ace338ff 100644 --- a/tests/phpunit/api/v4/Entity/ExampleDataTest.php +++ b/tests/phpunit/api/v4/Entity/ExampleDataTest.php @@ -33,9 +33,8 @@ class ExampleDataTest extends UnitTestCase { * @throws \Civi\API\Exception\UnauthorizedException */ public function testGet() { - $file = \Civi::paths()->getPath('[civicrm.root]/Civi/WorkflowMessage/GenericWorkflowMessage/alex.ex.php'); - $workflow = 'generic'; - $name = 'generic.alex'; + $file = \Civi::paths()->getPath('[civicrm.root]/Civi/WorkflowMessage/GenericWorkflowMessage/Alex.ex.php'); + $name = 'workflow/generic/alex'; $this->assertTrue(file_exists($file), "Expect find canary file ($file)"); @@ -43,16 +42,24 @@ class ExampleDataTest extends UnitTestCase { ->addWhere('name', '=', $name) ->execute() ->single(); - $this->assertEquals($workflow, $get['workflow']); - $this->assertTrue(!isset($get['data'])); - $this->assertTrue(!isset($get['asserts'])); + $this->assertEquals($name, $get['name']); + $this->assertTrue(!isset($get['data']), 'Default "get" should not return "data"'); + $this->assertTrue(!isset($get['asserts']), 'Default "get" should not return "asserts"'); + + $get = \Civi\Api4\ExampleData::get() + ->addWhere('name', 'LIKE', 'workflow/generic/%') + ->execute(); + $this->assertTrue($get->count() > 0); + foreach ($get as $gotten) { + $this->assertStringStartsWith('workflow/generic/', $gotten['name']); + } $get = \Civi\Api4\ExampleData::get() ->addWhere('name', '=', $name) ->addSelect('workflow', 'data') ->execute() ->single(); - $this->assertEquals($workflow, $get['workflow']); + $this->assertEquals($name, $get['name']); $this->assertEquals(100, $get['data']['modelProps']['contact']['contact_id']); } -- 2.25.1