Visual Console Refactor: finished the color cloud model

Former-commit-id: a8a98ba44a459044e9f262d6dc062bde64743406
This commit is contained in:
Alejandro Gallardo Escobar 2019-04-03 17:46:43 +02:00
parent adc0f3c21b
commit 1f8d115130
2 changed files with 448 additions and 35 deletions

View File

@ -35,82 +35,242 @@ final class ColorCloud extends Item
*
* @return array Data structure representing the model.
*
* @overrides Item::decode.
* @overrides Item->decode.
*/
protected function decode(array $data): array
{
$colorCloudData = parent::decode($data);
$colorCloudData['type'] = COLOR_CLOUD;
$colorCloudData['color'] = $this->extractColor($data);
$colorCloudData['colorRanges'] = '';
$decodedData = parent::decode($data);
$decodedData['type'] = COLOR_CLOUD;
$decodedData['label'] = null;
$decodedData['defaultColor'] = static::extractDefaultColor($data);
$decodedData['colorRanges'] = static::extractColorRanges($data);
$decodedData['color'] = static::notEmptyStringOr($data['color'], null);
return $colorCloudData;
return $decodedData;
}
/**
* Extract a color value.
* Extract the default color value.
*
* @param array $data Unknown input data structure.
*
* @return string
* @return string Default color.
* @throws \InvalidArgumentException If the default color cannot be
* extracted.
*/
private function extractColor(array $data): string
private static function extractDefaultColor(array $data): string
{
$color = static::notEmptyStringOr(
static::issetInArray($data, ['color']),
null
);
if (empty($color) === true) {
$color = static::notEmptyStringOr(
static::issetInArray($data, ['label']),
if (isset($data['defaultColor'])) {
$defaultColor = static::notEmptyStringOr(
$data['defaultColor'],
null
);
$color_decode = \json_decode($color);
if (empty($color) === true || empty($color_decode->default_color) === true) {
if ($defaultColor === null) {
throw new \InvalidArgumentException(
'the color property is required and should be string'
'the default color property is required and should be a not empty string'
);
} else {
return $color_decode->default_color;
}
return $defaultColor;
} else {
return $color;
$dynamicData = static::extractDynamicData($data);
return $dynamicData['defaultColor'];
}
}
/**
* Extract a color ranges value.
* Extract a list of color ranges.
*
* @param array $data Unknown input data structure.
*
* @return string
* @return array Color ranges list.
* @throws \InvalidArgumentException If any of the color ranges is invalid.
*/
private function extractColorRanges(array $data): array
private static function extractColorRanges(array $data): array
{
if (isset($data['colorRanges']) && \is_array($data['colorRanges'])) {
foreach ($data['colorRanges'] as $key => $value) {
if ((!isset($data['colorRanges'][$key]['fromValue']) || !\is_float($data['colorRanges'][$key]['fromValue']))
|| (!isset($data['colorRanges'][$key]['toValue']) || !\is_float($data['colorRanges'][$key]['toValue']))
|| (!isset($data['colorRanges'][$key]['color']) | !\is_string($data['colorRanges'][$key]['color']) || strlen($data['colorRanges'][$key]['color']) == 0)
// Validate the color ranges.
foreach ($data['colorRanges'] as $colorRange) {
if (\is_numeric($colorRange['fromValue']) === false
|| \is_numeric($colorRange['toValue']) === false
|| static::notEmptyStringOr($colorRange['color'], null) === null
) {
throw new \InvalidArgumentException(
'the color property is required and should be string'
);
throw new \InvalidArgumentException('invalid color range');
}
}
return $data['colorRanges'];
} else if (isset($data['label']) === true) {
$colorRanges_decode = \json_decode($data['label']);
return $colorRanges_decode->color_ranges;
$dynamicData = static::extractDynamicData($data);
return $dynamicData['colorRanges'];
} else {
return [];
}
}
/**
* Extract a dynamic data structure from the 'label' field.
*
* @param array $data Unknown input data structure.
*
* @return array Dynamic data structure.
* @throws \InvalidArgumentException If the structure cannot be built.
*
* @example [
* 'defaultColor' => '#FFF',
* 'colorRanges' => [
* [
* 'fromValue' => 50.0,
* 'toValue' => 150.5,
* 'color' => '#000',
* ],
* [
* 'fromValue' => 200.0,
* 'toValue' => 300.5,
* 'color' => '#F0F0F0',
* ],
* ]
* ]
*/
private static function extractDynamicData(array $data): array
{
$dynamicDataEncoded = static::notEmptyStringOr($data['label'], null);
if ($dynamicDataEncoded === null) {
throw new \InvalidArgumentException('dynamic data not found');
}
$result = [];
try {
$dynamicData = \json_decode(
\base64_decode($dynamicDataEncoded),
true
);
$result['defaultColor'] = $dynamicData['default_color'];
$result['colorRanges'] = [];
if (\is_array($dynamicData['color_ranges']) === true) {
foreach ($dynamicData['color_ranges'] as $colorRange) {
if (\is_numeric($colorRange['from_value']) === true
&& \is_numeric($colorRange['to_value']) === true
&& static::notEmptyStringOr(
$colorRange['color'],
null
) !== null
) {
$result['colorRanges'][] = [
'color' => $colorRange['color'],
'fromValue' => (float) $colorRange['from_value'],
'toValue' => (float) $colorRange['to_value'],
];
}
}
}
} catch (\Exception $e) {
throw new \InvalidArgumentException('invalid dynamic data');
}
return $result;
}
/**
* Fetch a vc item data structure from the database using a filter.
*
* @param array $filter Filter of the Visual Console Item.
*
* @return array The Visual Console Item data structure stored into the DB.
* @throws \InvalidArgumentException When an agent Id cannot be found.
*
* @override Item::fetchDataFromDB.
*/
protected static function fetchDataFromDB(array $filter): array
{
// Due to this DB call, this function cannot be unit tested without
// a proper mock.
$data = parent::fetchDataFromDB($filter);
/*
* Retrieve extra data.
*/
// Load side libraries.
global $config;
include_once $config['homedir'].'/include/functions_graph.php';
include_once $config['homedir'].'/include/functions_modules.php';
// Get the linked module Id.
$linkedModule = static::extractLinkedModule($data);
$moduleId = static::parseIntOr($linkedModule['moduleId'], null);
$metaconsoleId = static::parseIntOr(
$linkedModule['metaconsoleId'],
null
);
if ($moduleId === null) {
throw new \InvalidArgumentException('missing module Id');
}
$dynamicData = static::extractDynamicData($data);
// Set the initial color.
$data['color'] = $dynamicData['defaultColor'];
// Search for a matching color range.
if (empty($dynamicData['colorRanges']) === false) {
// Connect to node.
$nodeConnected = false;
if (is_metaconsole() === true && $metaconsoleId !== null) {
$nodeConnected = metaconsole_connect(
null,
$metaconsoleId
) === NOERR;
}
// Fetch module value.
$value = false;
if ($metaconsoleId === null
|| ($metaconsoleId !== null && $nodeConnected)
) {
$value = modules_get_last_value($moduleId);
}
// Restore connection.
if ($nodeConnected === true) {
metaconsole_restore_db();
}
// Value found.
if ($value !== false) {
/*
* TODO: It would be ok to give support to string values in the
* future?
*
* It can be done by matching the range value with the value
* if it is a string. I think the function to retrieve the value
* only supports numeric values.
*/
$value = (float) $value;
foreach ($dynamicData['colorRanges'] as $colorRange) {
if ($colorRange['fromValue'] <= $value
&& $colorRange['toValue'] >= $value
) {
// Range matched. Use the range color.
$data['color'] = $colorRange['color'];
break;
}
}
}
}
return $data;
}
}

View File

@ -0,0 +1,253 @@
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use Models\VisualConsole\Items\ColorCloud;
/**
* Test for the Visual Console color cloud item model.
*/
class ColorCloudTest extends TestCase
{
/**
* Test if the instance is created using a valid data structure.
*
* @return void
*/
public function testCanBeCreatedFromValidUserStructure(): void
{
$this->assertInstanceOf(
ColorCloud::class,
ColorCloud::fromArray(
[
'id' => 345,
'type' => COLOR_CLOUD,
'label' => null,
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '0',
'height' => '0',
'x' => -666,
'y' => 76,
'defaultColor' => '#FFF',
'colorRanges' => [],
]
)
);
$this->assertInstanceOf(
ColorCloud::class,
ColorCloud::fromArray(
[
'id' => 1000,
'type' => COLOR_CLOUD,
'width' => 100,
'height' => 900,
'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==',
'colorRanges' => [
[
'color' => '#000',
'fromValue' => 10.05,
'toValue' => 100.0,
],
],
'color' => '#000',
]
)
);
$this->assertInstanceOf(
ColorCloud::class,
ColorCloud::fromArray(
[
'id' => 1000,
'type' => COLOR_CLOUD,
'width' => 100,
'height' => 900,
'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==',
]
)
);
}
/**
* Test if the model has a valid JSON representation.
*
* @return void
*/
public function testContainerIsRepresentedAsJson(): void
{
$this->assertEquals(
'{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}',
(string) ColorCloud::fromArray(
[
'id' => 7,
'type' => COLOR_CLOUD,
'label' => null,
'labelPosition' => 'up',
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '0',
'height' => '0',
'x' => -666,
'y' => 76,
'defaultColor' => '#FFF',
'colorRanges' => [
[
'color' => '#000',
'fromValue' => 10.05,
'toValue' => 100.0,
],
],
'color' => '#000',
]
)
);
$this->assertEquals(
'{"aclGroupId":null,"agentId":null,"agentName":null,"color":null,"colorRanges":[],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":null,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}',
(string) ColorCloud::fromArray(
[
'id' => 7,
'type' => COLOR_CLOUD,
'label' => null,
'labelPosition' => 'up',
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '0',
'height' => '0',
'x' => -666,
'y' => 76,
'defaultColor' => '#FFF',
'colorRanges' => [],
]
)
);
$this->assertEquals(
'{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":3,"linkedLayoutId":2,"linkedLayoutStatusType":"default","metaconsoleId":5,"moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}',
(string) ColorCloud::fromArray(
[
'id' => 7,
'type' => COLOR_CLOUD,
'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==',
'labelPosition' => 'up',
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '0',
'height' => '0',
'x' => -666,
'y' => 76,
'color' => '#000',
'id_metaconsole' => 5,
'linked_layout_node_id' => 3,
'linkedLayoutId' => 2,
]
)
);
$this->assertEquals(
'{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":1,"linkedLayoutStatusType":"default","moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}',
(string) ColorCloud::fromArray(
[
'id' => 7,
'type' => COLOR_CLOUD,
'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==',
'labelPosition' => 'up',
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '0',
'height' => '0',
'x' => -666,
'y' => 76,
'defaultColor' => '#FFF',
'color' => '#000',
'id_layout_linked' => 1,
]
)
);
$this->assertEquals(
'{"aclGroupId":null,"agentId":null,"agentName":null,"color":"#000","colorRanges":[{"color":"#000","fromValue":10.05,"toValue":100}],"defaultColor":"#FFF","height":0,"id":7,"isLinkEnabled":true,"isOnTop":false,"label":null,"labelPosition":"up","linkedLayoutAgentId":null,"linkedLayoutId":2,"linkedLayoutStatusType":"service","linkedLayoutStatusTypeCriticalThreshold":80,"linkedLayoutStatusTypeWarningThreshold":50,"moduleId":null,"moduleName":null,"parentId":null,"type":20,"width":0,"x":-666,"y":76}',
(string) ColorCloud::fromArray(
[
'id' => 7,
'type' => COLOR_CLOUD,
'label' => 'eyJkZWZhdWx0X2NvbG9yIjoiI0ZGRiIsImNvbG9yX3JhbmdlcyI6W3siY29sb3IiOiIjMDAwIiwiZnJvbV92YWx1ZSI6MTAuMDUsInRvX3ZhbHVlIjoxMDAuMH1dfQ==',
'labelPosition' => 'up',
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '0',
'height' => '0',
'x' => -666,
'y' => 76,
'colorRanges' => [
[
'color' => '#000',
'fromValue' => 10.05,
'toValue' => 100.0,
],
],
'color' => '#000',
'linkedLayoutId' => 2,
'linked_layout_status_type' => 'service',
'linkedLayoutStatusTypeWarningThreshold' => 50,
'linked_layout_status_as_service_critical' => 80,
]
)
);
}
/**
* Test if the instance is not created when using a invalid dynamic data.
*
* @return void
*/
public function testCannotBeCreatedWithInvalidDynamicData(): void
{
$this->expectException(InvalidArgumentException::class);
// Invalid dynamic data.
ColorCloud::fromArray(
[
'id' => 3,
'type' => COLOR_CLOUD,
'label' => null,
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '330',
'height' => '0',
'x' => 511,
'y' => 76,
]
);
// Missing dynamic data.
ColorCloud::fromArray(
[
'id' => 3,
'type' => COLOR_CLOUD,
'label' => null,
'isLinkEnabled' => true,
'isOnTop' => false,
'parentId' => null,
'width' => '330',
'height' => '0',
'x' => 511,
'y' => 76,
]
);
}
}