Skip to content

Commit

Permalink
Capacity to add new itemtypes on schema (#143)
Browse files Browse the repository at this point in the history
* Fix tests

* Capacity to add new itemtypes on schema

* Make phpstan happy
  • Loading branch information
trasher authored Sep 11, 2024
1 parent 1d0517c commit 6cefd6a
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 55 deletions.
123 changes: 73 additions & 50 deletions lib/php/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class Converter
private array $extra_properties = [];
/** @var array<string, array<string, array<string, string>>> */
private array $extra_sub_properties = [];
/** @var array<string> */
private array $extra_itemtypes = [];

/**
* @var array<string, array<int, string>>
Expand Down Expand Up @@ -194,6 +196,16 @@ public function setExtraSubProperties(array $properties): self
return $this;
}

/**
* @param array<string> $itemtypes
* @return $this
*/
public function setExtraItemtypes(array $itemtypes): self
{
$this->extra_itemtypes = $itemtypes;
return $this;
}

/**
* Build (extended) JSON schema
* @return mixed
Expand All @@ -206,63 +218,74 @@ public function buildSchema()
}
$schema = json_decode($string);

$known_itemtypes = [];
preg_match('/\^\((.+)\)\$/', $schema->properties->itemtype->pattern, $known_itemtypes);
if (isset($known_itemtypes[1])) {
$known_itemtypes = explode('|', $known_itemtypes[1]);
foreach ($this->extra_itemtypes as $extra_itemtype) {
if (!in_array($extra_itemtype, $known_itemtypes)) {
$known_itemtypes[] = addslashes($extra_itemtype);
}
}
$schema->properties->itemtype->pattern = sprintf(
'^(%s)$',
implode('|', $known_itemtypes)
);
}

$properties = $schema->properties->content->properties;

if ($this->extra_properties != null) {
foreach ($this->extra_properties as $extra_property => $extra_config) {
if (!property_exists($properties, $extra_property)) {
$properties->$extra_property = json_decode((string)json_encode($extra_config));
} else {
trigger_error(
sprintf('Property %1$s already exists in schema.', $extra_property),
E_USER_WARNING
);
}
foreach ($this->extra_properties as $extra_property => $extra_config) {
if (!property_exists($properties, $extra_property)) {
$properties->$extra_property = json_decode((string)json_encode($extra_config));
} else {
trigger_error(
sprintf('Property %1$s already exists in schema.', $extra_property),
E_USER_WARNING
);
}
}

if ($this->extra_sub_properties != null) {
foreach ($this->extra_sub_properties as $extra_sub_property => $extra_sub_config) {
if (property_exists($properties, $extra_sub_property)) {
foreach ($extra_sub_config as $subprop => $subconfig) {
$type = $properties->$extra_sub_property->type;
switch ($type) {
case 'array':
if (!property_exists($properties->$extra_sub_property->items->properties, $subprop)) {
$properties->$extra_sub_property->items->properties->$subprop =
json_decode((string)json_encode($subconfig));
} else {
trigger_error(
sprintf('Property %1$s already exists in schema.', $subprop),
E_USER_WARNING
);
}
break;
case 'object':
if (!property_exists($properties->$extra_sub_property->properties, $subprop)) {
$properties->$extra_sub_property->properties->$subprop =
json_decode((string)json_encode($subconfig));
} else {
trigger_error(
sprintf(
'Property %1$s/%2$s already exists in schema.',
$extra_sub_property,
$subprop
),
E_USER_WARNING
);
}
break;
default:
trigger_error('Unknown type ' . $type, E_USER_WARNING);
}
foreach ($this->extra_sub_properties as $extra_sub_property => $extra_sub_config) {
if (property_exists($properties, $extra_sub_property)) {
foreach ($extra_sub_config as $subprop => $subconfig) {
$type = $properties->$extra_sub_property->type;
switch ($type) {
case 'array':
if (!property_exists($properties->$extra_sub_property->items->properties, $subprop)) {
$properties->$extra_sub_property->items->properties->$subprop =
json_decode((string)json_encode($subconfig));
} else {
trigger_error(
sprintf('Property %1$s already exists in schema.', $subprop),
E_USER_WARNING
);
}
break;
case 'object':
if (!property_exists($properties->$extra_sub_property->properties, $subprop)) {
$properties->$extra_sub_property->properties->$subprop =
json_decode((string)json_encode($subconfig));
} else {
trigger_error(
sprintf(
'Property %1$s/%2$s already exists in schema.',
$extra_sub_property,
$subprop
),
E_USER_WARNING
);
}
break;
default:
trigger_error('Unknown type ' . $type, E_USER_WARNING);
}
} else {
trigger_error(
sprintf('Property %1$s does not exists in schema.', $extra_sub_property),
E_USER_WARNING
);
}
} else {
trigger_error(
sprintf('Property %1$s does not exists in schema.', $extra_sub_property),
E_USER_WARNING
);
}
}

Expand Down
67 changes: 62 additions & 5 deletions tests/Glpi/Inventory/tests/units/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -717,53 +717,97 @@ public function testNetdisco() {
);
}

public function testValidate() {
public function testValidateOK()
{
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'hardware' => ['name' => 'my inventory']]]));
$instance = new \Glpi\Inventory\Converter();
$this->assertTrue($instance->validate($json));
}

public function testValidateVersionClient()
{
//required "versionclient" is missing
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['hardware' => ['name' => 'my inventory']]]));
$instance = new \Glpi\Inventory\Converter();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Required property missing: versionclient');
$this->assertFalse($instance->validate($json));
}

public function testValidateUnknownItemtype()
{
//itemtype \Glpi\Custom\Asset\Mine is unknown
$itemtype = '\Glpi\Custom\Asset\Mine';
$json = json_decode(json_encode(['deviceid' => 'myid', 'itemtype' => $itemtype, 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'hardware' => ['name' => 'my inventory']]]));
$instance = new \Glpi\Inventory\Converter();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('\\\\Glpi\\\\Custom\\\\Asset\\\\Mine" does not match to ^(Unmanaged|Computer|Phone|NetworkEquipment|Printer)$');
$this->assertFalse($instance->validate($json));
}

public function testValidateNewItemtype()
{
//itemtype \Glpi\Custom\Asset\Mine is unknown
$itemtype = '\Glpi\Custom\Asset\Mine';
$json = json_decode(json_encode(['deviceid' => 'myid', 'itemtype' => $itemtype, 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'hardware' => ['name' => 'my inventory']]]));
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraItemtypes([$itemtype]));
$this->assertTrue($instance->validate($json));
}

public function testValidateUnknownExtraPlugin_node()
{
//extra "plugin_node" is unknown
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'plugin_node' => 'plugin node']]));
$instance = new \Glpi\Inventory\Converter();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Additional properties not allowed: plugin_node');
$this->assertFalse($instance->validate($json));
}

public function testValidateExtraPlugin_node()
{
//add extra "plugin_node" as string
$extra_prop = ['plugin_node' => ['type' => 'string']];
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'plugin_node' => 'plugin node']]));
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraProperties($extra_prop));
$this->assertTrue($instance->validate($json));

}

public function testValidateUnknownHwPlugin_node()
{
//extra "hardware/hw_plugin_node" is unknown
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'hardware' => ['hw_plugin_node' => 'plugin node']]]));
$instance = new \Glpi\Inventory\Converter();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Additional properties not allowed: hw_plugin_node');
$this->assertFalse($instance->validate($json));
}

public function testValidateHwPlugin_node()
{
//add extra "hardware/hw_plugin_node" as string
$extra_sub_prop = ['hardware' => ['hw_plugin_node' => ['type' => 'string']]];
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'hardware' => ['hw_plugin_node' => 'plugin node']]]));
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraSubProperties($extra_sub_prop));
$this->assertTrue($instance->validate($json));
}

public function testValidateUnknownVmPlugin_node()
{
//extra "virtualmachines/vm_plugin_node" is unknown
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0', 'virtualmachines' => [['name' => 'My VM', 'vmtype' => 'libvirt', 'vm_plugin_node' => 'plugin node']]]]));
$instance = new \Glpi\Inventory\Converter();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Additional properties not allowed: vm_plugin_node');
$this->assertFalse($instance->validate($json));
}

public function testValidateVmPlugin_node()
{
//add extra "virtualmachines/vm_plugin_node" as string
$extra_sub_prop = ['virtualmachines' => ['vm_plugin_node' => ['type' => 'string']]];
$json = json_decode(json_encode([
Expand All @@ -782,28 +826,41 @@ public function testValidate() {
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraSubProperties($extra_sub_prop));
$this->assertTrue($instance->validate($json));
}

public function testValidateAlreadyExistingExtraNode()
{
//try add extra node already existing
$extra_prop = ['accesslog' => ['type' => 'string']];
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0']]));
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraProperties($extra_prop));
$this->assertTrue($instance->validate($json));

$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Property accesslog already exists in schema.');
$this->assertFalse($instance->validate($json));
}

public function testValidateUAlreadyExistingExtraSubNode()
{
//try add extra sub node already existing
$extra_sub_prop = ['hardware' => ['chassis_type' => ['type' => 'string']]];
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0']]));
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraSubProperties($extra_sub_prop));
$this->assertTrue($instance->validate($json));
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Property hardware/chassis_type already exists in schema.');
$this->assertFalse($instance->validate($json));
}

public function testValidateMissingParentSubNode() {
//try add extra sub node with missing parent
$extra_sub_prop = ['unknown' => ['chassis_type' => ['type' => 'string']]];
$json = json_decode(json_encode(['deviceid' => 'myid', 'content' => ['versionclient' => 'GLPI-Agent_v1.0']]));
$instance = new \Glpi\Inventory\Converter();
$this->assertInstanceOf(\Glpi\Inventory\Converter::class, $instance->setExtraSubProperties($extra_sub_prop));
$this->assertTrue($instance->validate($json));
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Property unknown does not exists in schema.');
$this->assertFalse($instance->validate($json));
}

public function testAssetTagFromDiscovery()
Expand Down

0 comments on commit 6cefd6a

Please sign in to comment.