diff options
Diffstat (limited to 'library/Director/Objects/IcingaArguments.php')
-rw-r--r-- | library/Director/Objects/IcingaArguments.php | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/library/Director/Objects/IcingaArguments.php b/library/Director/Objects/IcingaArguments.php new file mode 100644 index 0000000..e788da8 --- /dev/null +++ b/library/Director/Objects/IcingaArguments.php @@ -0,0 +1,442 @@ +<?php + +namespace Icinga\Module\Director\Objects; + +use Countable; +use Exception; +use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer; +use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; +use InvalidArgumentException; +use Iterator; + +class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer +{ + const COMMENT_DSL_UNSUPPORTED = '/* Icinga 2 does not export DSL function bodies via API */'; + + /** @var IcingaCommandArgument[] */ + protected $storedArguments = []; + + /** @var IcingaCommandArgument[] */ + protected $arguments = []; + + protected $modified = false; + + protected $object; + + private $position = 0; + + protected $idx = []; + + public function __construct(IcingaObject $object) + { + $this->object = $object; + } + + #[\ReturnTypeWillChange] + public function count() + { + return count($this->arguments); + } + + #[\ReturnTypeWillChange] + public function rewind() + { + $this->position = 0; + } + + public function hasBeenModified() + { + return $this->modified; + } + + #[\ReturnTypeWillChange] + public function current() + { + if (! $this->valid()) { + return null; + } + + return $this->arguments[$this->idx[$this->position]]; + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->idx[$this->position]; + } + + #[\ReturnTypeWillChange] + public function next() + { + ++$this->position; + } + + #[\ReturnTypeWillChange] + public function valid() + { + return array_key_exists($this->position, $this->idx); + } + + public function get($key) + { + if (array_key_exists($key, $this->arguments)) { + if ($this->arguments[$key]->shouldBeRemoved()) { + return null; + } + + return $this->arguments[$key]; + } + + return null; + } + + public function set($key, $value) + { + if ($value === null) { + return $this->remove($key); + } + + if ($value instanceof IcingaCommandArgument) { + $argument = $value; + } else { + $argument = IcingaCommandArgument::create( + $this->mungeCommandArgument($key, $value) + ); + } + + $argument->set('command_id', $this->object->get('id')); + + $key = $argument->argument_name; + if (array_key_exists($key, $this->arguments)) { + $this->arguments[$key]->replaceWith($argument); + if ($this->arguments[$key]->hasBeenModified()) { + $this->modified = true; + } + } elseif (array_key_exists($key, $this->storedArguments)) { + $this->arguments[$key] = clone($this->storedArguments[$key]); + $this->arguments[$key]->replaceWith($argument); + if ($this->arguments[$key]->hasBeenModified()) { + $this->modified = true; + } + } else { + $this->add($argument); + $this->modified = true; + } + + return $this; + } + + protected function mungeCommandArgument($key, $value) + { + $attrs = [ + 'argument_name' => (string) $key, + ]; + + $map = [ + 'skip_key' => 'skip_key', + 'repeat_key' => 'repeat_key', + 'required' => 'required', + // 'order' => 'sort_order', + 'description' => 'description', + 'set_if' => 'set_if', + ]; + + $argValue = null; + if (is_object($value)) { + if (property_exists($value, 'order')) { + $attrs['sort_order'] = (string) $value->order; + } + + foreach ($map as $apiKey => $dbKey) { + if (property_exists($value, $apiKey)) { + $attrs[$dbKey] = $value->$apiKey; + } + } + if (property_exists($value, 'type')) { + // argument is directly set as function, no further properties + if ($value->type === 'Function') { + $attrs['argument_value'] = self::COMMENT_DSL_UNSUPPORTED; + $attrs['argument_format'] = 'expression'; + } + } elseif (property_exists($value, 'value')) { + // argument is a dictionary with further settings + if (is_object($value->value)) { + if ($value->value->type === 'Function' && property_exists($value->value, 'body')) { + // likely an export from Baskets that contains the actual function body + $attrs['argument_value'] = $value->value->body; + $attrs['argument_format'] = 'expression'; + } elseif ($value->value->type === 'Function') { + $attrs['argument_value'] = self::COMMENT_DSL_UNSUPPORTED; + $attrs['argument_format'] = 'expression'; + } else { + die('Unable to resolve command argument'); + } + } else { + $argValue = $value->value; + if (is_string($argValue)) { + $attrs['argument_value'] = $argValue; + $attrs['argument_format'] = 'string'; + } else { + $attrs['argument_value'] = $argValue; + $attrs['argument_format'] = 'json'; + } + } + } + } else { + if (is_string($value)) { + $attrs['argument_value'] = $value; + $attrs['argument_format'] = 'string'; + } else { + $attrs['argument_value'] = $value; + $attrs['argument_format'] = 'json'; + } + } + + if (array_key_exists('set_if', $attrs)) { + if (is_object($attrs['set_if']) && $attrs['set_if']->type === 'Function') { + $attrs['set_if'] = self::COMMENT_DSL_UNSUPPORTED; + $attrs['set_if_format'] = 'expression'; + } elseif (property_exists($value, 'set_if_format')) { + if (in_array($value->set_if_format, ['string', 'expression', 'json'])) { + $attrs['set_if_format'] = $value->set_if_format; + } + } + } + + return $attrs; + } + + public function setArguments($arguments) + { + $arguments = (array) $arguments; + + foreach ($arguments as $arg => $val) { + $this->set($arg, $val); + } + + foreach (array_diff( + array_keys($this->arguments), + array_keys($arguments) + ) as $arg) { + if ($this->arguments[$arg]->hasBeenLoadedFromDb()) { + $this->arguments[$arg]->markForRemoval(); + $this->modified = true; + } else { + unset($this->arguments[$arg]); + } + } + + return $this; + } + + /** + * Magic isset check + * + * @param string $argument + * @return boolean + */ + public function __isset($argument) + { + return array_key_exists($argument, $this->arguments); + } + + public function remove($argument) + { + if (array_key_exists($argument, $this->arguments)) { + $this->arguments[$argument]->markForRemoval(); + $this->modified = true; + $this->refreshIndex(); + } + + return $this; + } + + protected function refreshIndex() + { + ksort($this->arguments); + $this->idx = array_keys($this->arguments); + } + + public function add(IcingaCommandArgument $argument) + { + $name = $argument->get('argument_name'); + if (array_key_exists($name, $this->arguments)) { + // TODO: Fail unless $argument equals existing one + return $this; + } + + $this->arguments[$name] = $argument; + $this->modified = true; + $this->refreshIndex(); + + return $this; + } + + protected function getGroupTableName() + { + return $this->object->getTableName() . 'group'; + } + + protected function loadFromDb() + { + $db = $this->object->getDb(); + $connection = $this->object->getConnection(); + + $table = $this->object->getTableName(); + $query = $db->select()->from( + ['o' => $table], + [] + )->join( + ['a' => 'icinga_command_argument'], + 'o.id = a.command_id', + '*' + )->where('o.object_name = ?', $this->object->getObjectName()) + ->order('a.sort_order')->order('a.argument_name'); + + $this->arguments = IcingaCommandArgument::loadAll($connection, $query, 'argument_name'); + $this->cloneStored(); + $this->refreshIndex(); + + return $this; + } + + public function toPlainObject( + $resolved = false, + $skipDefaults = false, + array $chosenProperties = null, + $resolveIds = true + ) { + if ($chosenProperties !== null) { + throw new InvalidArgumentException( + 'IcingaArguments does not support chosenProperties[]' + ); + } + + $args = []; + foreach ($this->arguments as $arg) { + if ($arg->shouldBeRemoved()) { + continue; + } + + $args[$arg->get('argument_name')] = $arg->toPlainObject( + $resolved, + $skipDefaults, + null, + $resolveIds + ); + } + + return $args; + } + + public function toUnmodifiedPlainObject() + { + $args = []; + foreach ($this->storedArguments as $key => $arg) { + $args[$arg->argument_name] = $arg->toPlainObject(); + } + + return $args; + } + + protected function cloneStored() + { + $this->storedArguments = []; + foreach ($this->arguments as $k => $v) { + $this->storedArguments[$k] = clone($v); + } + } + + public static function loadForStoredObject(IcingaObject $object) + { + $arguments = new static($object); + return $arguments->loadFromDb(); + } + + public function setBeingLoadedFromDb() + { + foreach ($this->arguments as $argument) { + $argument->setBeingLoadedFromDb(); + } + $this->refreshIndex(); + $this->cloneStored(); + } + + /** + * @return $this + * @throws \Icinga\Module\Director\Exception\DuplicateKeyException + */ + public function store() + { + $db = $this->object->getConnection(); + $deleted = []; + foreach ($this->arguments as $key => $argument) { + if ($argument->shouldBeRemoved()) { + $deleted[] = $key; + } else { + if ($argument->hasBeenModified()) { + if ($argument->hasBeenLoadedFromDb()) { + $argument->setLoadedProperty('command_id', $this->object->get('id')); + } else { + $argument->set('command_id', $this->object->get('id')); + } + $argument->store($db); + } + } + } + + foreach ($deleted as $key) { + $argument = $this->arguments[$key]; + $argument->setLoadedProperty('command_id', $this->object->get('id')); + $argument->setConnection($this->object->getConnection()); + $argument->delete(); + unset($this->arguments[$key]); + } + + $this->cloneStored(); + + return $this; + } + + public function toConfigString() + { + if (empty($this->arguments)) { + return ''; + } + + $args = []; + foreach ($this->arguments as $arg) { + if ($arg->shouldBeRemoved()) { + continue; + } + + $args[$arg->get('argument_name')] = $arg->toConfigString(); + } + return c::renderKeyOperatorValue('arguments', '+=', c::renderDictionary($args)); + } + + public function __toString() + { + try { + return $this->toConfigString(); + } catch (Exception $e) { + trigger_error($e); + $previousHandler = set_exception_handler( + function () { + } + ); + restore_error_handler(); + if ($previousHandler !== null) { + call_user_func($previousHandler, $e); + die(); + } else { + die($e->getMessage()); + } + } + } + + public function toLegacyConfigString() + { + return 'UNSUPPORTED'; + } +} |