mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-07-31 01:34:12 +02:00
ImportSourceRestApi: Add header and deeper extract_property
Allows a user to add additional headers, e.g. by setting a specific `Accept` or any authentication header. Also `extract_property` now has a logic for deeper keys like "result.objects", "key.deeper_key.very_deep"
This commit is contained in:
parent
d5c94d8757
commit
50521bdecb
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Icinga\Module\Director\Import;
|
namespace Icinga\Module\Director\Import;
|
||||||
|
|
||||||
|
use Icinga\Exception\InvalidPropertyException;
|
||||||
use Icinga\Module\Director\Hook\ImportSourceHook;
|
use Icinga\Module\Director\Hook\ImportSourceHook;
|
||||||
use Icinga\Module\Director\RestApi\RestApiClient;
|
use Icinga\Module\Director\RestApi\RestApiClient;
|
||||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||||
@ -16,18 +17,12 @@ class ImportSourceRestApi extends ImportSourceHook
|
|||||||
|
|
||||||
public function fetchData()
|
public function fetchData()
|
||||||
{
|
{
|
||||||
$result = $this->getRestApi()->get($this->getUrl());
|
$result = $this->getRestApi()->get(
|
||||||
if ($property = $this->getSetting('extract_property')) {
|
$this->getUrl(),
|
||||||
if (\property_exists($result, $property)) {
|
null,
|
||||||
$result = $result->$property;
|
$this->buildHeaders()
|
||||||
} else {
|
);
|
||||||
throw new \RuntimeException(sprintf(
|
$result = $this->extractProperty($result);
|
||||||
'Result has no "%s" property. Available keys: %s',
|
|
||||||
$property,
|
|
||||||
\implode(', ', \array_keys((array) $result))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (array) $result;
|
return (array) $result;
|
||||||
}
|
}
|
||||||
@ -48,6 +43,66 @@ class ImportSourceRestApi extends ImportSourceHook
|
|||||||
return $columns;
|
return $columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract result from a property specified
|
||||||
|
*
|
||||||
|
* A simple key, like "objects", will take the final result from key objects
|
||||||
|
*
|
||||||
|
* If you have a deeper key like "objects" under the key "results", specify this as "results.objects".
|
||||||
|
*
|
||||||
|
* When a key of the JSON object contains a literal ".", this can be escaped as
|
||||||
|
*
|
||||||
|
* @param $result
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function extractProperty($result)
|
||||||
|
{
|
||||||
|
$property = $this->getSetting('extract_property');
|
||||||
|
if (! $property) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = preg_split('~(?<!\\\\)\.~', $property);
|
||||||
|
|
||||||
|
// iterate over parts of the attribute path
|
||||||
|
$data = $result;
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
// un-escape any dots
|
||||||
|
$part = preg_replace('~\\\\.~', '.', $part);
|
||||||
|
|
||||||
|
if (property_exists($data, $part)) {
|
||||||
|
$data = $data->$part;
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException(sprintf(
|
||||||
|
'Result has no "%s" property. Available keys: %s',
|
||||||
|
$part,
|
||||||
|
implode(', ', array_keys((array) $data))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildHeaders()
|
||||||
|
{
|
||||||
|
$headers = [];
|
||||||
|
|
||||||
|
$text = $this->getSetting('headers', '');
|
||||||
|
foreach (preg_split("~\r?\n~", $text) as $header) {
|
||||||
|
$header = trim($header);
|
||||||
|
$parts = preg_split('~\s*:\s*~', $header, 2);
|
||||||
|
if (count($parts) < 2) {
|
||||||
|
throw new InvalidPropertyException('Could not parse header: %s', $header);
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers[$parts[0]] = $parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param QuickForm $form
|
* @param QuickForm $form
|
||||||
* @throws \Zend_Form_Exception
|
* @throws \Zend_Form_Exception
|
||||||
@ -59,6 +114,7 @@ class ImportSourceRestApi extends ImportSourceHook
|
|||||||
static::addUrl($form);
|
static::addUrl($form);
|
||||||
static::addResultProperty($form);
|
static::addResultProperty($form);
|
||||||
static::addAuthentication($form);
|
static::addAuthentication($form);
|
||||||
|
static::addHeader($form);
|
||||||
static::addProxy($form);
|
static::addProxy($form);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +139,23 @@ class ImportSourceRestApi extends ImportSourceHook
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param QuickForm $form
|
||||||
|
* @throws \Zend_Form_Exception
|
||||||
|
*/
|
||||||
|
protected static function addHeader(QuickForm $form)
|
||||||
|
{
|
||||||
|
$form->addElement('textarea', 'headers', [
|
||||||
|
'label' => $form->translate('HTTP Header'),
|
||||||
|
'description' => join(' ', [
|
||||||
|
$form->translate('Additional headers for the HTTP request.'),
|
||||||
|
$form->translate('Specify headers in text format "Header: Value", each header on a new line.'),
|
||||||
|
]),
|
||||||
|
'class' => 'preformatted',
|
||||||
|
'rows' => 4,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param QuickForm $form
|
* @param QuickForm $form
|
||||||
* @throws \Zend_Form_Exception
|
* @throws \Zend_Form_Exception
|
||||||
@ -131,11 +204,15 @@ class ImportSourceRestApi extends ImportSourceHook
|
|||||||
protected static function addResultProperty(QuickForm $form)
|
protected static function addResultProperty(QuickForm $form)
|
||||||
{
|
{
|
||||||
$form->addElement('text', 'extract_property', array(
|
$form->addElement('text', 'extract_property', array(
|
||||||
'label' => 'Extract property',
|
'label' => 'Extract property',
|
||||||
'description' => $form->translate(
|
'description' => join("\n", [
|
||||||
'Often the expected result is provided in a property like "objects".'
|
$form->translate('Often the expected result is provided in a property like "objects".'
|
||||||
. ' Please specify this if required'
|
. ' Please specify this if required.'),
|
||||||
),
|
$form->translate('Also deeper keys can be specific by a dot-notation:'),
|
||||||
|
'"result.objects", "key.deeper_key.very_deep"',
|
||||||
|
$form->translate('Literal dots in a key name can be written in the escape notation:'),
|
||||||
|
'"key\.with\.dots"',
|
||||||
|
])
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,4 +106,22 @@ abstract class BaseTestCase extends PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
return self::$app;
|
return self::$app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a protected function for a class during testing
|
||||||
|
*
|
||||||
|
* @param $obj
|
||||||
|
* @param $name
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @throws \ReflectionException
|
||||||
|
*/
|
||||||
|
public static function callMethod($obj, $name, array $args)
|
||||||
|
{
|
||||||
|
$class = new \ReflectionClass($obj);
|
||||||
|
$method = $class->getMethod($name);
|
||||||
|
$method->setAccessible(true);
|
||||||
|
return $method->invokeArgs($obj, $args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
29
test/php/library/Director/Import/ImportSourceRestApiTest.php
Normal file
29
test/php/library/Director/Import/ImportSourceRestApiTest.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Icinga\Module\Director\Import;
|
||||||
|
|
||||||
|
use Icinga\Module\Director\Import\ImportSourceRestApi;
|
||||||
|
use Icinga\Module\Director\Test\BaseTestCase;
|
||||||
|
|
||||||
|
class ImportSourceRestApiTest extends BaseTestCase
|
||||||
|
{
|
||||||
|
public function testExtractProperty()
|
||||||
|
{
|
||||||
|
$examples = [
|
||||||
|
'' => json_decode('[{"name":"blau"}]'),
|
||||||
|
'objects' => json_decode('{"objects":[{"name":"blau"}]}'),
|
||||||
|
'results.objects.all' => json_decode('{"results":{"objects":{"all":[{"name":"blau"}]}}}'),
|
||||||
|
'results\.objects.all' => json_decode('{"results.objects":{"all":[{"name":"blau"}]}}'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$source = new ImportSourceRestApi();
|
||||||
|
|
||||||
|
foreach ($examples as $property => $data) {
|
||||||
|
$source->setSettings(['extract_property' => $property]);
|
||||||
|
$result = static::callMethod($source, 'extractProperty', [$data]);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertArrayHasKey('name', (array) $result[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user