added cron job manager for service jobs and scheduler jobs

This commit is contained in:
Taras Machyshyn
2014-02-06 10:36:57 +02:00
parent fa888b015d
commit 36d9892b11
21 changed files with 938 additions and 66 deletions
+4 -1
View File
@@ -101,7 +101,10 @@ class Application
{
$auth = $this->getAuth();
$auth->useNoAuth();
// TODO cron manager
$cronManager = new \Espo\Core\CronManager($this->container);
$cronManager->run();
}
protected function routeHooks()
+9
View File
@@ -175,6 +175,15 @@ class Container
$this->get('entityManager')
);
}
private function loadClassMerger()
{
return new \Espo\Core\Utils\File\ClassMerger(
$this->get('fileManager'),
$this->get('config'),
$this->get('metadata')
);
}
public function setUser($user)
{
@@ -0,0 +1,89 @@
<?php
namespace Espo\Core\Cron;
use Espo\Core\Exceptions\NotFound,
Espo\Core\Utils\Util;
class ScheduledJob
{
private $container;
protected $data = null;
protected $cacheFile = 'data/cache/application/jobs.php';
protected $allowedMethod = 'run';
/**
* @var array - path to cron job files
*/
private $paths = array(
'corePath' => 'application/Espo/Jobs',
'modulePath' => 'application/Espo/Modules/{*}/Jobs',
'customPath' => 'application/Espo/Custom/Jobs',
);
public function __construct(\Espo\Core\Container $container)
{
$this->container = $container;
}
protected function getContainer()
{
return $this->container;
}
protected function getEntityManager()
{
return $this->container->get('entityManager');
}
public function run(array $job)
{
$jobName = $job['method'];
$className = $this->getClassName($jobName);
if ($className === false) {
throw new NotFound();
}
$jobClass = new $className($this->container);
$method = $this->allowedMethod;
$jobClass->$method();
}
protected function getClassName($name)
{
$name = Util::normilizeClassName($name);
if (!isset($this->data)) {
$this->init();
}
$name = ucfirst($name);
if (isset($this->data[$name])) {
return $this->data[$name];
}
return false;
}
/**
* Load scheduler classes. It loads from ...Jobs, ex. \Espo\Jobs
* @return null
*/
protected function init()
{
$classMerger = $this->getContainer()->get('classMerger');
$classMerger->setAllowedMethods( array($this->allowedMethod) );
$this->data = $classMerger->getData($this->cacheFile, $this->paths);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Espo\Core\Cron;
use Espo\Core\Utils\Json,
Espo\Core\Exceptions\NotFound;
class Service
{
private $serviceFactory;
public function __construct(\Espo\Core\ServiceFactory $serviceFactory)
{
$this->serviceFactory = $serviceFactory;
}
protected function getServiceFactory()
{
return $this->serviceFactory;
}
public function run($job)
{
$serviceName = $job['service_name'];
if (!$this->getServiceFactory()->checkExists($serviceName)) {
throw new NotFound();
}
$service = $this->getServiceFactory()->create($serviceName);
$serviceMethod = $job['method'];
if (!method_exists($service, $serviceMethod)) {
throw new NotFound();
}
$data = $job['data'];
if (Json::isJSON($data)) {
$data = Json::decode($data, true);
}
$service->$serviceMethod($data);
}
}
+193
View File
@@ -0,0 +1,193 @@
<?php
namespace Espo\Core;
class CronManager
{
private $container;
private $config;
private $fileManager;
private $scheduledJobCron;
private $serviceCron;
private $jobFactory;
private $scheduledJobFactory;
protected $lastRunTime = 'data/cache/application/cronLastRunTime.php';
public function __construct(\Espo\Core\Container $container)
{
$this->container = $container;
$this->config = $this->container->get('config');
$this->fileManager = $this->container->get('fileManager');
$this->scheduledJobCron = new \Espo\Core\Cron\ScheduledJob( $this->container );
$this->serviceCron = new \Espo\Core\Cron\Service( $this->container->get('serviceFactory') );
$this->jobFactory = $this->container->get('serviceFactory')->create('job');
$this->scheduledJobFactory = $this->container->get('serviceFactory')->create('scheduledJob');
}
protected function getContainer()
{
return $this->container;
}
protected function getConfig()
{
return $this->config;
}
protected function getFileManager()
{
return $this->fileManager;
}
protected function getJobFactory()
{
return $this->jobFactory;
}
protected function getScheduledJobFactory()
{
return $this->scheduledJobFactory;
}
protected function getScheduledJobCron()
{
return $this->scheduledJobCron;
}
protected function getServiceCron()
{
return $this->serviceCron;
}
protected function getLastRunTime()
{
$lastRunTime = $this->getFileManager()->getContent($this->lastRunTime);
if (!is_int($lastRunTime)) {
$lastRunTime = time() - (intval($this->getConfig()->get('cron.minExecutionTime')) + 60);
}
return $lastRunTime;
}
protected function setLastRunTime($time)
{
return $this->getFileManager()->setContentPHP($time, $this->lastRunTime);
}
protected function checkLastRunTime()
{
$currentTime = time();
$lastRunTime = $this->getLastRunTime();
$minTime = $this->getConfig()->get('cron.minExecutionTime');
if ($currentTime > ($lastRunTime + $minTime) ) {
return true;
}
return false;
}
public function run()
{
if (!$this->checkLastRunTime()) {
$GLOBALS['log']->add('INFO', 'Cron Manager: Stop cron running, too frequency execution');
return; //stop cron running, too frequency execution
}
$this->setLastRunTime(time());
//Check scheduled jobs and create related jobs
$this->createJobsFromScheduledJobs();
$pendingJobs = $this->getJobFactory()->getPendingJobs();
foreach ($pendingJobs as $job) {
$this->getJobFactory()->updateEntity($job['id'], array(
'status' => 'Running',
));
$isSuccess = true;
try {
if (!empty($job['scheduled_job_id'])) {
$this->getScheduledJobCron()->run($job);
} else {
$this->getServiceCron()->run($job);
}
} catch (\Exception $e) {
$isSuccess = false;
$GLOBALS['log']->add('INFO', 'Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage());
}
$status = $isSuccess ? 'Success' : 'Failed';
$this->getJobFactory()->updateEntity($job['id'], array(
'status' => $status,
));
//set status in the schedulerJobLog
if (!empty($job['scheduled_job_id'])) {
$this->getScheduledJobFactory()->addLogRecord($job['scheduled_job_id'], $status);
}
}
}
/**
* Check scheduled jobs and create related jobs
* @return array List of created Jobs
*/
protected function createJobsFromScheduledJobs()
{
$activeScheduledJobs = $this->getScheduledJobFactory()->getActiveJobs();
$createdJobs = array();
foreach ($activeScheduledJobs as $scheduledJob) {
$scheduling = $scheduledJob['scheduling'];
$cronExpression = \Cron\CronExpression::factory($scheduling);
try {
//$nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s');
$prevDate = $cronExpression->getPreviousRunDate()->format('Y-m-d H:i:s');
} catch (\Exception $e) {
$GLOBALS['log']->add('Exception', 'ScheduledJob ['.$scheduledJob['id'].']: CronExpression - Impossible CRON expression ['.$scheduling.']');
continue;
}
if ($cronExpression->isDue()) {
$prevDate = date('Y-m-d H:i:00');
}
$existsJob = $this->getJobFactory()->getJobByScheduledJob($scheduledJob['id'], $prevDate);
if (!isset($existsJob) || empty($existsJob)) {
//create a job
$data = array(
'name' => $scheduledJob['name'],
'status' => 'Pending',
'scheduledJobId' => $scheduledJob['id'],
'executeTime' => $prevDate,
'method' => $scheduledJob['job'],
);
$createdJobs[] = $this->getJobFactory()->createEntity($data);
}
}
return $createdJobs;
}
}
+6 -45
View File
@@ -16,7 +16,7 @@ class EntryPointManager
protected $cacheFile = 'data/cache/application/entryPoints.php';
protected $allowMethods = array(
protected $allowedMethods = array(
'run',
);
@@ -85,50 +85,11 @@ class EntryPointManager
protected function init()
{
$config = $this->getContainer()->get('config');
if (file_exists($this->cacheFile) && $config->get('useCache')) {
$this->data = $this->getFileManager()->getContent($this->cacheFile);
} else {
$this->data = $this->getClassNameHash(array($this->paths['corePath'], $this->paths['customPath']) );
foreach ($this->getContainer()->get('metadata')->getModuleList() as $moduleName) {
$path = str_replace('{*}', $moduleName, $this->paths['modulePath']);
$this->data = array_merge($this->data, $this->getClassNameHash(array($path)));
}
if ($config->get('useCache')) {
$result = $this->getFileManager()->setContentPHP($this->data, $this->cacheFile);
if ($result == false) {
throw new \Espo\Core\Exceptions\Error();
}
}
}
}
// TODO delegate to another class
protected function getClassNameHash(array $dirs)
{
$data = array();
foreach ($dirs as $dir) {
if (file_exists($dir)) {
$fileList = $this->getFileManager()->getFileList($dir, false, '\.php$', 'file');
foreach ($fileList as $file) {
$filePath = Util::concatPath($dir, $file);
$className = Util::getClassName($filePath);
$fileName = $this->getFileManager()->getFileName($filePath);
$fileName = ucfirst($fileName);
foreach ($this->allowMethods as $methodName) {
if (method_exists($className, $methodName)) {
$data[$fileName] = $className;
}
}
}
}
}
return $data;
}
$classMerger = $this->getContainer()->get('classMerger');
$classMerger->setAllowedMethods($this->allowedMethods);
$this->data = $classMerger->getData($this->cacheFile, $this->paths);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Espo\Core\Jobs;
use \Espo\Core\Container;
abstract class Base
{
private $container;
protected function getContainer()
{
return $this->container;
}
protected function getEntityManager()
{
return $this->getContainer()->get('entityManager');
}
protected function getServiceFactory()
{
return $this->getContainer()->get('serviceFactory');
}
protected function getConfig()
{
return $this->getContainer()->get('config');
}
protected function getMetadata()
{
return $this->getContainer()->get('metadata');
}
public function __construct(Container $container)
{
$this->container = $container;
}
abstract public function run();
}
@@ -0,0 +1,108 @@
<?php
namespace Espo\Core\Utils\File;
use \Espo\Core\Utils\Util;
class ClassMerger
{
private $fileManager;
private $config;
private $metadata;
protected $cacheFile = null;
protected $allowedMethods = array(
'run',
);
public function __construct(\Espo\Core\Utils\File\Manager $fileManager, \Espo\Core\Utils\Config $config, \Espo\Core\Utils\Metadata $metadata)
{
$this->fileManager = $fileManager;
$this->config = $config;
$this->metadata = $metadata;
}
protected function getFileManager()
{
return $this->fileManager;
}
protected function getConfig()
{
return $this->config;
}
protected function getMetadata()
{
return $this->metadata;
}
public function setAllowedMethods(array $methods)
{
$this->allowedMethods = $methods;
}
/**
* Return path data of classes
* @param string $cacheFile full path for a cache file, ex. data/cache/application/entryPoints.php
* @param array $paths in format array(
* 'corePath' => '',
* 'modulePath' => '',
* 'customPath' => '',
* );
* @return array
*/
public function getData($cacheFile, array $paths)
{
$data = null;
if (file_exists($cacheFile) && $this->getConfig()->get('useCache')) {
$data = $this->getFileManager()->getContent($cacheFile);
} else {
$data = $this->getClassNameHash(array($paths['corePath'], $paths['customPath']) );
foreach ($this->getMetadata()->getModuleList() as $moduleName) {
$path = str_replace('{*}', $moduleName, $paths['modulePath']);
$data = array_merge($data, $this->getClassNameHash(array($path)));
}
if ($this->getConfig()->get('useCache')) {
$result = $this->getFileManager()->setContentPHP($data, $cacheFile);
if ($result == false) {
throw new \Espo\Core\Exceptions\Error();
}
}
}
return $data;
}
protected function getClassNameHash(array $dirs)
{
$data = array();
foreach ($dirs as $dir) {
if (file_exists($dir)) {
$fileList = $this->getFileManager()->getFileList($dir, false, '\.php$', 'file');
foreach ($fileList as $file) {
$filePath = Util::concatPath($dir, $file);
$className = Util::getClassName($filePath);
$fileName = $this->getFileManager()->getFileName($filePath);
$fileName = ucfirst($fileName);
foreach ($this->allowedMethods as $methodName) {
if (method_exists($className, $methodName)) {
$data[$fileName] = $className;
}
}
}
}
}
return $data;
}
}
@@ -37,6 +37,12 @@ return array (
'dateFormat' => 'MM/DD/YYYY',
'timeFormat' => 'HH:mm',
'cron' => array(
'maxJobNumber' => 15, /*Max number of jobs per one execution*/
'jobPeriod' => 7800, /*Period for jobs, ex. if cron executed at 15:35, it will execute all pending jobs for times from 14:05 to 15:35*/
'minExecutionTime' => 50, /*to avoid too frequency execution*/
),
'systemUser' => array(
'id' => 'system',
'userName' => 'system',
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Espo\Entities;
class Job extends \Espo\Core\ORM\Entity
{
}
@@ -0,0 +1,17 @@
<?php
namespace Espo\Jobs;
use \Espo\Core\Exceptions;
class CheckInboundEmails extends \Espo\Core\Jobs\Base
{
public function run()
{
//some code
//for problems use Exceptions. In this case job status will be "Failed", otherwise "Success"
}
}
@@ -0,0 +1,51 @@
{
"fields": {
"name": {
"type": "varchar",
"required": true
},
"status": {
"type": "enum",
"options": ["Pending", "Running", "Success", "Failed"],
"default": "Pending"
},
"executeTime": {
"type": "datetime",
"required": true
},
"serviceName": {
"type": "varchar",
"required": true,
"len":100
},
"method": {
"type": "varchar",
"required": true,
"len":100
},
"data": {
"type": "text"
},
"scheduledJob": {
"type": "link"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
}
},
"links": {
"scheduledJob": {
"type": "belongsTo",
"entity": "ScheduledJob"
}
},
"collection": {
"sortBy": "createdAt",
"asc": false
}
}
@@ -0,0 +1,7 @@
{
"entity": true,
"layouts": false,
"tab": false,
"acl": false,
"customizable":false
}
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace Espo\Services;
use \PDO;
class Job extends Record
{
public function getPendingJobs()
{
$jobConfigs = (array) $this->getConfig()->get('cron');
$currentTime = time();
$periodTime = $currentTime - intval($jobConfigs['jobPeriod']);
$limit = empty($jobConfigs['maxJobNumber']) ? '' : 'LIMIT '.$jobConfigs['maxJobNumber'];
$query = "SELECT * FROM job WHERE
`status` = 'Pending'
AND execute_time BETWEEN '".date('Y-m-d H:i:s', $periodTime)."' AND '".date('Y-m-d H:i:s', $currentTime)."'
ORDER BY execute_time DESC ".$limit;
$pdo = $this->getEntityManager()->getPDO();
$sth = $pdo->prepare($query);
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
$list = array();
foreach ($rows as $row) {
$list[] = $row;
}
return $list;
}
public function getJobByScheduledJob($scheduledJobId, $date)
{
$query = "SELECT * FROM job WHERE
scheduled_job_id = '".$scheduledJobId."'
AND execute_time = '".$date."'
LIMIT 1";
$pdo = $this->getEntityManager()->getPDO();
$sth = $pdo->prepare($query);
$sth->execute();
$scheduledJob = $sth->fetchAll(PDO::FETCH_ASSOC);
return $scheduledJob;
}
//todo remove, used for tests
public function testMethod($data)
{
}
//todo remove, used for tests
public function testFailed($data)
{
throw new \Espo\Core\Exceptions\Error();
}
}
@@ -0,0 +1,63 @@
<?php
namespace Espo\Services;
use \PDO;
class ScheduledJob extends \Espo\Services\Record
{
public function getActiveJobs()
{
$query = "SELECT * FROM scheduled_job WHERE
`status` = 'Active'
AND deleted = 0";
$pdo = $this->getEntityManager()->getPDO();
$sth = $pdo->prepare($query);
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
$list = array();
foreach ($rows as $row) {
$list[] = $row;
}
return $list;
}
/**
* Add record to ScheduledJobLogRecord about executed job
* @param string $scheduledJobId
* @param string $status
*
* @return string Id of created ScheduledJobLogRecord
*/
public function addLogRecord($scheduledJobId, $status)
{
$lastRun = date('Y-m-d H:i:s');
$entityManager = $this->getEntityManager();
$scheduledJob = $entityManager->getEntity('ScheduledJob', $scheduledJobId);
$scheduledJob->set('lastRun', $lastRun);
$entityManager->saveEntity($scheduledJob);
$scheduledJobLog = $entityManager->getEntity('ScheduledJobLogRecord');
$scheduledJobLog->set(array(
'scheduledJobId' => $scheduledJobId,
'name' => $scheduledJob->get('name'),
'status' => $status,
'executionTime' => $lastRun,
));
$scheduledJobLogId = $entityManager->saveEntity($scheduledJobLog);
//$entityManager->getRepository('ScheduledJobLogRecord')->relate($scheduledJobLog, 'scheduledJob', $scheduledJob);
return $scheduledJobLogId;
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ class Auth extends \Slim\Middleware
if (!empty($routes[0])) {
$routeConditions = $routes[0]->getConditions();
if (isset($routeConditions['auth']) && $routeConditions['auth'] === false) {
//$this->container->setUser(new \Espo\Entities\User());
$this->container->setUser($this->entityManager->getRepository('User'));
$this->next->call();
return;
}
+98
View File
@@ -0,0 +1,98 @@
<?php
namespace tests\Espo\Core;
use tests\ReflectionHelper;
class CronManagerTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected $objects;
protected $filesPath= 'tests/testData/EntryPoints';
protected function setUp()
{
$this->objects['container'] = $this->getMockBuilder('\Espo\Core\Container')->disableOriginalConstructor()->getMock();
$this->objects['serviceFactory'] = $this->getMockBuilder('\Espo\Core\ServiceFactory')->disableOriginalConstructor()->getMock();
$this->objects['config'] = $this->getMockBuilder('\Espo\Core\Utils\Config')->disableOriginalConstructor()->getMock();
$this->objects['fileManager'] = $this->getMockBuilder('\Espo\Core\Utils\File\Manager')->disableOriginalConstructor()->getMock();
$map = array(
array('config', $this->objects['config']),
array('fileManager', $this->objects['fileManager']),
array('serviceFactory', $this->objects['serviceFactory']),
);
$this->objects['container']
->expects($this->any())
->method('get')
->will($this->returnValueMap($map));
$this->object = new \Espo\Core\CronManager( $this->objects['container'] );
$this->reflection = new ReflectionHelper($this->object);
}
protected function tearDown()
{
$this->object = NULL;
}
function testCheckLastRunTimeFileDoesnotExist()
{
$this->objects['fileManager']
->expects($this->once())
->method('getContent')
->will($this->returnValue(false));
$this->objects['config']
->expects($this->exactly(2))
->method('get')
->will($this->returnValue(50));
$this->assertTrue( $this->reflection->invokeMethod('checkLastRunTime', array()) );
}
function testCheckLastRunTime()
{
$this->objects['fileManager']
->expects($this->once())
->method('getContent')
->will($this->returnValue(time()-60));
$this->objects['config']
->expects($this->once())
->method('get')
->will($this->returnValue(50));
$this->assertTrue( $this->reflection->invokeMethod('checkLastRunTime', array()) );
}
function testCheckLastRunTimeTooFrequency()
{
$this->objects['fileManager']
->expects($this->once())
->method('getContent')
->will($this->returnValue(time()-49));
$this->objects['config']
->expects($this->once())
->method('get')
->will($this->returnValue(50));
$this->assertFalse( $this->reflection->invokeMethod('checkLastRunTime', array()) );
}
}
?>
+2 -17
View File
@@ -28,11 +28,7 @@ class EntryPointManagerTest extends \PHPUnit_Framework_TestCase
$this->reflection->setProperty('paths', array(
'corePath' => 'tests/testData/EntryPoints/Espo/EntryPoints',
'modulePath' => 'tests/testData/EntryPoints/Espo/Modules/Crm/EntryPoints',
));
$this->reflection->setProperty('customPaths', array(
'corePath' => '',
'modulePath' => '',
));
));
}
protected function tearDown()
@@ -40,24 +36,13 @@ class EntryPointManagerTest extends \PHPUnit_Framework_TestCase
$this->object = NULL;
}
function testGetData()
{
$result = array(
'Download' => '\tests\testData\EntryPoints\Espo\EntryPoints\Download',
'Test' => '\tests\testData\EntryPoints\Espo\EntryPoints\Test',
'InModule' => '\tests\testData\EntryPoints\Espo\Modules\Crm\EntryPoints\InModule'
);
$this->assertEquals( $result, $this->reflection->invokeMethod('getData', array($this->reflection->getProperty('paths'))) );
}
function testGet()
{
$this->reflection->setProperty('data', array(
'Test' => '\tests\testData\EntryPoints\Espo\EntryPoints\Test',
));
$this->assertEquals('\tests\testData\EntryPoints\Espo\EntryPoints\Test', $this->reflection->invokeMethod('get', array('test')) );
$this->assertEquals('\tests\testData\EntryPoints\Espo\EntryPoints\Test', $this->reflection->invokeMethod('getClassName', array('test')) );
}
@@ -0,0 +1,107 @@
<?php
namespace tests\Espo\Core\Utils\File;
use tests\ReflectionHelper;
class ClassMergerTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected $objects;
protected $reflection;
protected function setUp()
{
$this->objects['fileManager'] = new \Espo\Core\Utils\File\Manager( (object) array());
$this->objects['config'] = $this->getMockBuilder('\Espo\Core\Utils\Config')->disableOriginalConstructor()->getMock();
$this->objects['metadata'] = $this->getMockBuilder('\Espo\Core\Utils\Metadata')->disableOriginalConstructor()->getMock();
$this->object = new \Espo\Core\Utils\File\ClassMerger($this->objects['fileManager'], $this->objects['config'], $this->objects['metadata']);
$this->reflection = new ReflectionHelper($this->object);
}
protected function tearDown()
{
$this->object = NULL;
}
function testGetClassNameHash()
{
$paths = array(
'tests/testData/EntryPoints/Espo/EntryPoints',
'tests/testData/EntryPoints/Espo/Modules/Crm/EntryPoints',
);
$result = array(
'Download' => '\tests\testData\EntryPoints\Espo\EntryPoints\Download',
'Test' => '\tests\testData\EntryPoints\Espo\EntryPoints\Test',
'InModule' => '\tests\testData\EntryPoints\Espo\Modules\Crm\EntryPoints\InModule'
);
$this->assertEquals( $result, $this->reflection->invokeMethod('getClassNameHash', array($paths)) );
}
function testGetDataWithCache()
{
$this->objects['config']
->expects($this->once())
->method('get')
->will($this->returnValue(true));
$cacheFile = 'tests/testData/EntryPoints/cache/entryPoints.php';
$paths = array(
'corePath' => 'tests/testData/EntryPoints/Espo/EntryPoints',
'modulePath' => 'tests/testData/EntryPoints/Espo/Modules/{*}/EntryPoints',
'customPath' => 'tests/testData/EntryPoints/Espo/Custom/EntryPoints',
);
$result = array (
'Download' => '\\tests\\testData\\EntryPoints\\Espo\\EntryPoints\\Download',
);
$this->assertEquals( $result, $this->reflection->invokeMethod('getData', array($cacheFile, $paths)) );
}
function testGetDataWithNoCache()
{
$this->objects['config']
->expects($this->exactly(2))
->method('get')
->will($this->returnValue(false));
$this->objects['metadata']
->expects($this->once())
->method('getModuleList')
->will($this->returnValue(
array(
'Crm',
)
));
$cacheFile = 'tests/testData/EntryPoints/cache/entryPoints.php';
$paths = array(
'corePath' => 'tests/testData/EntryPoints/Espo/EntryPoints',
'modulePath' => 'tests/testData/EntryPoints/Espo/Modules/{*}/EntryPoints',
'customPath' => 'tests/testData/EntryPoints/Espo/Custom/EntryPoints',
);
$result = array(
'Download' => '\tests\testData\EntryPoints\Espo\EntryPoints\Download',
'Test' => '\tests\testData\EntryPoints\Espo\EntryPoints\Test',
'InModule' => '\tests\testData\EntryPoints\Espo\Modules\Crm\EntryPoints\InModule'
);
$this->assertEquals( $result, $this->reflection->invokeMethod('getData', array($cacheFile, $paths)) );
}
}
?>
@@ -5,8 +5,7 @@ namespace tests\testData\EntryPoints\Espo\EntryPoints;
class Download extends \Espo\Core\EntryPoints\Base
{
protected $authRequired = true;
public function run()
{
+7
View File
@@ -0,0 +1,7 @@
<?php
return array (
'Download' => '\\tests\\testData\\EntryPoints\\Espo\\EntryPoints\\Download',
);
?>