diff --git a/composer.json b/composer.json index f3d45cf..3c0be5b 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "symfony/yaml": "~3.0|~4.0" }, "require-dev": { - "phpunit/phpunit": "^7.4" + "phpunit/phpunit": "^8.5.3" }, "authors": [ { diff --git a/config.yaml b/config.yaml new file mode 100755 index 0000000..407cae9 --- /dev/null +++ b/config.yaml @@ -0,0 +1,7 @@ +logger: + file: /home/aerex/baikal-storage-plugin.log + level: 'DEBUG' +taskwarrior: + taskdata: /home/aerex/.task + taskrc: /home/aerex/.taskrc + project_tag_suffix: project_ diff --git a/lib/Configs/ConfigBuilder.php b/lib/Configs/ConfigBuilder.php index 0ea0040..78a6d12 100644 --- a/lib/Configs/ConfigBuilder.php +++ b/lib/Configs/ConfigBuilder.php @@ -9,10 +9,10 @@ use Symfony\Component\Yaml\Yaml; class ConfigBuilder implements ConfigurationInterface { private $configs = []; - private $configDir; + private $configFile; - public function __construct($configDir) { - $this->configDir = $configDir; + public function __construct($configFile) { + $this->configFile = $configFile; $this->processor = new Processor(); } @@ -23,7 +23,20 @@ class ConfigBuilder implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('configs'); - $ref = $rootNode->children(); + $ref = $rootNode->children() + ->arrayNode('logger') + ->canBeEnabled() + ->children() + ->scalarNode('file') + ->end() + ->scalarNode('level') + ->defaultValue('ERROR') + ->validate() + ->IfNotInArray(['DEBUG', 'INFO', 'NOTICE', 'WARNING', 'ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']) + ->thenInvalid('Invalid log level %s') + ->end() + ->end(); + foreach ($this->configs as $config) { $ref = $ref->append($config->get()); } @@ -32,8 +45,7 @@ class ConfigBuilder implements ConfigurationInterface { } public function readContent() { - $contents = sprintf('%s/storage.yaml', $this->configDir); - return file_get_contents($contents); + return file_get_contents($this->configFile); } public function loadYaml() { diff --git a/lib/Configs/TaskwarriorConfig.php b/lib/Configs/TaskwarriorConfig.php index fb9e972..a2e684a 100644 --- a/lib/Configs/TaskwarriorConfig.php +++ b/lib/Configs/TaskwarriorConfig.php @@ -15,6 +15,9 @@ class TaskwarriorConfig { ->scalarNode('taskrc') ->defaultValue('~/.taskrc') ->end() + ->scalarNode('project_tag_suffix') + ->defaultValue('project_') + ->end() ->end(); return $node; diff --git a/lib/Logger.php b/lib/Logger.php new file mode 100644 index 0000000..3d143f2 --- /dev/null +++ b/lib/Logger.php @@ -0,0 +1,63 @@ + false]; + + function __construct($configs, $tag) { + if (isset($configs['logger'])) { + $this->configs = $configs['logger']; + } + if ($this->configs['enabled']) { + $this->logger = new Monolog($tag); + $logLevel = Monolog::getLevels()[$this->configs['level']]; + $this->logger->pushHandler(new StreamHandler($this->configs['file'], $logLevel)); + } + } + + public function debug($message) { + if ($this->configs['enabled']) { + $this->logger->debug($message); + } + } + + public function info($message) { + if ($this->configs['enabled']) { + $this->logger->info($message); + } + } + + public function notice($message) { + if ($this->configs['enabled']) { + $this->logger->notice($message); + } + } + + public function error($message) { + if ($this->configs['enabled']) { + $this->logger->error($message); + } + } + + public function critical($message) { + if ($this->configs['enabled']) { + $this->logger->critical($message); + } + } + public function alert($message) { + if ($this->configs['enabled']) { + $this->logger->alert($message); + } + } + + public function emergency($message) { + if ($this->configs['enabled']) { + $this->logger->emergency($message); + } + } + +} diff --git a/lib/Plugin.php b/lib/Plugin.php index bdbc4c7..6b6a054 100644 --- a/lib/Plugin.php +++ b/lib/Plugin.php @@ -38,14 +38,14 @@ class Plugin extends ServerPlugin { * @param CalendarProcessor $TWCalManager * */ - function __construct($configDir){ - $configs = $this->buildConfigurations($configDir); + function __construct($configFile){ + $configs = $this->buildConfigurations($configFile); $this->storageManager = new StorageManager($configs); - $this->initializeStorages($configDir, $configs); + $this->initializeStorages($configs); } - public function buildConfigurations($configDir) { - $this->config = new ConfigBuilder($configDir); + public function buildConfigurations($configFile) { + $this->config = new ConfigBuilder($configFile); $this->config->add(new TaskwarriorConfig()); return $this->config->loadYaml(); } @@ -55,8 +55,8 @@ class Plugin extends ServerPlugin { * */ - public function initializeStorages($configDir, $configs) { - $taskwarrior = new Taskwarrior(new Console(['rc.verbose=nothing', 'rc.hooks=off']), $configDir, $configs); + public function initializeStorages($configs) { + $taskwarrior = new Taskwarrior(new Console(['rc.verbose=nothing', 'rc.hooks=off']), $configs); $this->storageManager->addStorage(Taskwarrior::NAME, $taskwarrior); } diff --git a/lib/Storages/Taskwarrior.php b/lib/Storages/Taskwarrior.php index 36a5cf7..d5a9742 100644 --- a/lib/Storages/Taskwarrior.php +++ b/lib/Storages/Taskwarrior.php @@ -3,19 +3,20 @@ namespace Aerex\BaikalStorage\Storages; use Sabre\VObject\Component\VCalendar as Calendar; +use Aerex\BaikalStorage\Logger; use Carbon\Carbon; class Taskwarrior implements IStorage { - private const DATA_FILES = ['pending.data', 'completed.data', 'undo.data']; public const NAME = 'taskwarrior'; private $tasks = []; - private $configDir; private $configs; - public function __construct($console, $configDir, $configs) { + private $logger; + + public function __construct($console, $configs) { $this->console = $console; - $this->configDir = $configDir; $this->configs = $configs['taskwarrior']; + $this->logger = new Logger($configs, 'Taskwarrior'); } public function getConfig() { @@ -23,45 +24,22 @@ class Taskwarrior implements IStorage { } public function refresh() { - $fp = fopen(sprintf('%s/taskwarrior-baikal-storage.lock', $this->configDir), 'a'); - - if (!$fp || !flock($fp, LOCK_EX | LOCK_NB, $eWouldBlock) || $eWouldBlock) { - fputs(STDERR, 'Could not get lock'); - } - - $mtime = 0; - $tasksUpdated = false; - foreach (Taskwarrior::DATA_FILES as $dataFile) { - $fmtime = filemtime(sprintf('%s/%s', $this->configs['taskdata'], $dataFile)); - if ($fmtime > $mtime) { - $mtime = $fmtime; - $tasksUpdated = true; - } - } - - if ($tasksUpdated) { - $tasks = json_decode($this->console->execute('task', ['export'], null, - ['TASKRC' => $this->configs['taskrc'], 'TASKDATA' => $this->configs['taskdata']]), true); - foreach ($tasks as $task) { - $this->tasks[$task['uuid']] = $task; - } - } - fclose($fp); - unlink(sprintf('%s/taskwarrior-baikal-storage.lock', $this->configDir)); + $output = $this->console->execute('task', ['sync'], null, + ['TASKRC' => $this->configs['taskrc'],'TASKDATA' => $this->configs['taskdata']]); + $this->logger->info($output); } public function vObjectToTask($vtodo) { - if (isset($this->tasks['uid']) && $this->tasks['uid'] == $vtodo->UID) { - $task = $this->tasks['uid']; + if (isset($this->tasks[(string)$vtodo->UID])) { + $task = $this->tasks[(string)$vtodo->UID]; } else { $task = []; $task['uid'] = (string)$vtodo->UID; } - - if (!isset($vtodo->DESCRIPTION) && isset($vtodo->SUMMARY)){ + if (isset($vtodo->SUMMARY) && !isset($vtodo->DESCRIPTION)){ $task['description'] = (string)$vtodo->SUMMARY; - } else { + } else if(isset($vtodo->DESCRIPTION)) { $task['description'] = (string)$vtodo->DESCRIPTION; } @@ -108,49 +86,55 @@ class Taskwarrior implements IStorage { if (isset($vtodo->STATUS)) { switch((string)$vtodo->STATUS) { - case 'NEEDS-ACTION': - $task['status'] = 'pending'; - break; - case 'COMPLETED': - $task['status'] = 'completed'; - if (!isset($task['end'])) { - $task['end'] = new Carbon($vtodo->DTSTAMP->getDateTime()->format(\DateTime::W3C)); - } - break; - case 'CANCELED': - $task['status'] = 'deleted'; - if (!isset($task['end'])) { - $task['end'] = new Carbon($vtodo->DTSTAMP->getDateTime()->format(\DateTime::W3C)); - } - break; + case 'NEEDS-ACTION': + $task['status'] = 'pending'; + break; + case 'COMPLETED': + $task['status'] = 'completed'; + if (!isset($task['end'])) { + $task['end'] = new Carbon($vtodo->DTSTAMP->getDateTime()->format(\DateTime::W3C)); + } + break; + case 'CANCELED': + $task['status'] = 'deleted'; + if (!isset($task['end'])) { + $task['end'] = new Carbon($vtodo->DTSTAMP->getDateTime()->format(\DateTime::W3C)); + } + break; } - } if (isset($vtodo->CATEGORIES)) { $task['tags'] = []; - foreach ($vtodo->CATEGORIES as $category) { - if (isset($this->configs['project_tag_suffix'])) { - $projTagSuffixRegExp = sprintf('/^%s_/', $this->configs['project_tag_suffix']); - if (preg_match($category, $projTagSuffixRegExp)) { - $task['project'] = preg_replace($projTagSuffixRegExp, '', $category); - continue; + foreach ($vtodo->CATEGORIES as $category) { + if (isset($this->configs['project_tag_suffix'])) { + $projTagSuffixRegExp = sprintf('/^%s_/', $this->configs['project_tag_suffix']); + if (preg_match($category, $projTagSuffixRegExp)) { + $task['project'] = preg_replace($projTagSuffixRegExp, '', $category); + continue; + } } + $task['tags'] = $category; } - $task['tags'] = $category; - } - } - - return $task; } - public function save(Calendar $c) { - if (!isset($c->VTODO)){ - throw new \Exception('Calendar event does not contain VTODO'); - } - $this->refresh(); - $task = $this->vObjectToTask($c->VTODO); - $this->console->execute('task', ['import'], $task, - ['TASKRC' => $this->configs['taskrc'],'TASKDATA' => $this->configs['taskdata']]); - } + return $task; + } + + public function save(Calendar $c) { + if (!isset($c->VTODO)){ + throw new \Exception('Calendar event does not contain VTODO'); + $this->logger->error('Calendar event does not contain VTODO'); } + $this->logger->info($c->VTODO->getJsonValue()); + $this->refresh(); + $task = $this->vObjectToTask($c->VTODO); + $this->logger->info(json_encode($task)); + $this->logger->info( + sprintf('Executing TASKRC = %s TASKDATA = %s task import %s', $this->configs['taskrc'], $this->configs['taskdata'], $task) + ); + $output = $this->console->execute('task', ['import'], $task, + ['TASKRC' => $this->configs['taskrc'],'TASKDATA' => $this->configs['taskdata']]); + $this->logger->info($output); + } +} diff --git a/tests/Configs/ConfigTest.php b/tests/Configs/ConfigTest.php new file mode 100644 index 0000000..0be757f --- /dev/null +++ b/tests/Configs/ConfigTest.php @@ -0,0 +1,31 @@ +loadYaml(); + $this->assertEquals(sizeof($contents), 1); + $this->assertArrayHasKey('logger', $contents, 'config missing logger property'); + $this->assertArrayHasKey('file', $contents['logger'], 'config missing logger.file property'); + $this->assertEquals($contents['logger']['file'], '/home/user/logger.yaml'); + $this->assertArrayHasKey('level', $contents['logger'], 'config missing logger.level property'); + $this->assertEquals($contents['logger']['level'], 'ERROR', 'ERROR is not set as default logger level'); + $this->assertArrayHasKey('enabled', $contents['logger'], 'config missing logger.enabled property'); + $this->assertTrue($contents['logger']['enabled']); + + } + + +} + diff --git a/tests/Configs/Fixtures/LoggerConfig.yaml b/tests/Configs/Fixtures/LoggerConfig.yaml new file mode 100755 index 0000000..dccda79 --- /dev/null +++ b/tests/Configs/Fixtures/LoggerConfig.yaml @@ -0,0 +1,2 @@ +logger: + file: /home/user/logger.yaml diff --git a/tests/Configs/Fixtures/TaskwarriorConfig.yaml b/tests/Configs/Fixtures/TaskwarriorConfig.yaml new file mode 100755 index 0000000..e98cc3f --- /dev/null +++ b/tests/Configs/Fixtures/TaskwarriorConfig.yaml @@ -0,0 +1,7 @@ +logger: + file: /home/aerex/baikal-storage-plugin.log + level: DEBUG +taskwarrior: + taskdata: /home/aerex/.task + taskrc: /home/aerex/.taskrc + project_tag_suffix: project_ diff --git a/tests/Fixtures/taskwarrior_config.yml b/tests/Fixtures/taskwarrior_config.yml deleted file mode 100644 index d6cdbd4..0000000 --- a/tests/Fixtures/taskwarrior_config.yml +++ /dev/null @@ -1,4 +0,0 @@ -taskwarrior: - taskdata: '/home/user/.task' - taskrc: '~/home/user/.taskrc' - project_tag_suffix: 'project_' diff --git a/tests/StorageManagerTest.php b/tests/StorageManagerTest.php index 0bd36b6..7774ea9 100644 --- a/tests/StorageManagerTest.php +++ b/tests/StorageManagerTest.php @@ -5,7 +5,6 @@ namespace Aerex\BaikalStorage; use PHPUnit\Framework\TestCase; use Aerex\BaikalStorage\AbstractConsole; use Aerex\BaikalStorage\Configs\ConfigBuilder; -use Aerex\BaikalStorage\Configs\TaskwarriorConfig; use Aerex\BaikalStorage\Storages\Taskwarrior; use Aerex\BaikalStorage\Storages\IStorage; use Sabre\VObject\Component\VCalendar as Calendar; @@ -21,18 +20,18 @@ class StorageManagerTest extends TestCase { public $mockConfigBuilder; - function setUp() { + protected function setUp(): void { $this->mockConfigBuilder = $this->getMockBuilder(ConfigBuilder::class) ->setMethods(['readContent']) ->setConstructorArgs(['']) ->getMock(); $this->mockConsole = $this->createMock(AbstractConsole::class); - $this->mockStorage = $this->createMock(IStorage::class); + $this->mockStorage = $this->createMock(Taskwarrior::class); } public function testAddTaskwarriorStorage() { $configs = ['taskwarrior' => ['taskrc' => '', 'taskdata' => '']]; - $tw = new Taskwarrior($this->mockConsole, '', $configs); + $tw = new Taskwarrior($this->mockConsole, $configs); $manager = new StorageManager($this->mockConfigBuilder); $manager->addStorage(Taskwarrior::NAME, $tw); $storages = $manager->getStorages(); diff --git a/tests/Storages/TaskwarriorTest.php b/tests/Storages/TaskwarriorTest.php index 48de9fd..f5773a1 100644 --- a/tests/Storages/TaskwarriorTest.php +++ b/tests/Storages/TaskwarriorTest.php @@ -14,13 +14,13 @@ class TaskwarriorTest extends TestCase { * */ private $mockConsole; - function setUp() { + protected function setUp(): void { $this->mockConsole = $this->createMock(AbstractConsole::class); } public function testVObjectToTask() { - $configs = ['taskwarrior' => ['taskrc' => '', 'taskdata' => '']]; - $this->taskwarrior = new Taskwarrior($this->mockConsole, '', $configs); + $configs = ['taskwarrior' => ['taskrc' => '', 'taskdata' => ''], 'logger' => ['file' => '', 'level'=> 'DEBUG', 'enabled' => true]]; + $this->taskwarrior = new Taskwarrior($this->mockConsole, $configs); $vcalendar = new Calendar([ 'VTODO' => [ 'SUMMARY' => 'Finish project', @@ -33,7 +33,6 @@ class TaskwarriorTest extends TestCase { 'RRULE' => 'FREQ=MONTHLY' ] ]); - echo $vcalendar->VTODO->RRULE->getJsonValue()[0]['freq']; $task = $this->taskwarrior->vObjectToTask($vcalendar->VTODO); $this->assertArrayHasKey('uid', $task, 'task should have a uid');