Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 63 additions & 52 deletions core/collector.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ abstract class Collector
* @var string TABLENAME_PATTERN used to validate data synchro table name
*/
const TABLENAME_PATTERN = '/^[A-Za-z0-9_]*$/';
const READONLY_FIELDS = [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Such a list depends on the datamodel: depending on class properties, the list of magical fields (related to obsolescence or archive) may vary.
    The class "User" is not often subject to change, but The class "Contact" can be customized easily.
  2. It is weird that you did not encounter the case with obsolescence_date and archive_date.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly fields here are related to DataSynchro objects. not Contact.

'friendlyname',
'user_id_friendlyname',
'user_id_finalclass_recall',
'notify_contact_id_friendlyname',
'notify_contact_id_finalclass_recall',
'notify_contact_id_obsolescence_flag',
'notify_contact_id_archive_flag',
];

protected $sProjectName;
protected $sSynchroDataSourceDefinitionFile;
Expand Down Expand Up @@ -251,6 +260,17 @@ public function GetName()
return get_class($this);
}

public function GetSourceId()
{
return $this->iSourceId;
}

protected function SetSourceId($iSourceId)
{
$this->iSourceId = $iSourceId;
}


public function GetVersion()
{
if ($this->sVersion == null) {
Expand Down Expand Up @@ -508,7 +528,7 @@ public function InitSynchroDataSource($aPlaceholders)
} else {
$iKey = (int)$aData['key'];
}
$this->iSourceId = $iKey;
$this->SetSourceId($iKey);
RestClient::GetFullSynchroDataSource($aCurrentSourceDefinition, $this->iSourceId);
if ($this->DataSourcesAreEquivalent($aExpectedSourceDefinition, $aCurrentSourceDefinition)) {
Utils::Log(LOG_INFO, "Ok, the Synchro Data Source '{$this->sSourceName}' exists in iTop and is up to date");
Expand Down Expand Up @@ -818,18 +838,22 @@ public static function CallItopViaHttp($sUri, $aAdditionalData, $iTimeOut = -1)
return static::$oCallItopService->CallItopViaHttp($sUri, $aAdditionalData, $iTimeOut);
}

protected function CreateSynchroDataSource($aSourceDefinition, $sComment)
protected function CreateSynchroDataSource($aSourceDefinition, $sComment, RestClient $oClient = null)
{
$oClient = new RestClient();
if ($oClient === null)
{
$oClient = new RestClient();
}

$ret = false;

// Ignore read-only fields
unset($aSourceDefinition['friendlyname']);
unset($aSourceDefinition['user_id_friendlyname']);
unset($aSourceDefinition['user_id_finalclass_recall']);
unset($aSourceDefinition['notify_contact_id_friendlyname']);
unset($aSourceDefinition['notify_contact_id_finalclass_recall']);
unset($aSourceDefinition['notify_contact_id_obsolescence_flag']);
foreach (self::READONLY_FIELDS as $sField){
if (array_key_exists($sField, $aSourceDefinition)){
unset($aSourceDefinition[$sField]);
}
}

// SynchroAttributes will be processed one by one, below
$aSynchroAttr = $aSourceDefinition['attribute_list'];
unset($aSourceDefinition['attribute_list']);
Expand All @@ -839,7 +863,7 @@ protected function CreateSynchroDataSource($aSourceDefinition, $sComment)
$aCreatedObj = reset($aResult['objects']);
$aExpectedAttrDef = $aCreatedObj['fields']['attribute_list'];
$iKey = (int)$aCreatedObj['key'];
$this->iSourceId = $iKey;
$this->SetSourceId($iKey);

if ($this->UpdateSDSAttributes($aExpectedAttrDef, $aSynchroAttr, $sComment)) {
$ret = $this->iSourceId;
Expand All @@ -851,18 +875,19 @@ protected function CreateSynchroDataSource($aSourceDefinition, $sComment)
return $ret;
}

protected function UpdateSynchroDataSource($aSourceDefinition, $sComment)
protected function UpdateSynchroDataSource($aSourceDefinition, $sComment, RestClient $oClient = null)
{
$bRet = true;
$oClient = new RestClient();

if ($oClient === null)
{
$oClient = new RestClient();
}
// Ignore read-only fields
unset($aSourceDefinition['friendlyname']);
unset($aSourceDefinition['user_id_friendlyname']);
unset($aSourceDefinition['user_id_finalclass_recall']);
unset($aSourceDefinition['notify_contact_id_friendlyname']);
unset($aSourceDefinition['notify_contact_id_finalclass_recall']);
unset($aSourceDefinition['notify_contact_id_obsolescence_flag']);
foreach (Collector::READONLY_FIELDS as $sField){
if (array_key_exists($sField, $aSourceDefinition)){
unset($aSourceDefinition[$sField]);
}
}

// SynchroAttributes will be processed one by one, below
$aSynchroAttr = $aSourceDefinition['attribute_list'];
unset($aSourceDefinition['attribute_list']);
Expand Down Expand Up @@ -952,19 +977,14 @@ protected function FindAttr($sAttCode, $aExpectedAttrDef)
protected function DataSourcesAreEquivalent($aDS1, $aDS2)
{
foreach ($aDS1 as $sKey => $value) {
switch ($sKey) {
case 'friendlyname':
case 'user_id_friendlyname':
case 'user_id_finalclass_recall':
case 'notify_contact_id_friendlyname':
case 'notify_contact_id_finalclass_recall':
case 'notify_contact_id_obsolescence_flag':
case 'notify_contact_id_archive_flag':
// Ignore all read-only attributes
break;
if (in_array($sKey, Collector::READONLY_FIELDS)){
// Ignore all read-only attributes
continue;
}

switch ($sKey) {
case 'attribute_list':
foreach ($value as $sKey => $aDef) {
foreach ($value as $aDef) {
$sAttCode = $aDef['attcode'];
$aDef2 = $this->FindAttr($sAttCode, $aDS2['attribute_list']);
if ($aDef2 === false) {
Expand All @@ -973,13 +993,13 @@ protected function DataSourcesAreEquivalent($aDS1, $aDS2)
Utils::Log(LOG_DEBUG, "Comparison: ignoring the missing, but optional, attribute: '$sAttCode'.");
$this->aSkippedAttributes[] = $sAttCode;
continue;
} else {
// Missing non-optional attribute
Utils::Log(LOG_DEBUG, "Comparison: The definition of the non-optional attribute '$sAttCode' is missing. Data sources differ.");

return false;
}

// Missing non-optional attribute
Utils::Log(LOG_DEBUG, "Comparison: The definition of the non-optional attribute '$sAttCode' is missing. Data sources differ.");

return false;

} else {
if (($aDef != $aDef2) && (!$this->AttributeIsOptional($sAttCode))) {
// Definitions are different
Expand Down Expand Up @@ -1012,23 +1032,14 @@ protected function DataSourcesAreEquivalent($aDS1, $aDS2)
}
//Check the other way around
foreach ($aDS2 as $sKey => $value) {
switch ($sKey) {
case 'friendlyname':
case 'user_id_friendlyname':
case 'user_id_finalclass_recall':
case 'notify_contact_id_friendlyname':
case 'notify_contact_id_finalclass_recall':
case 'notify_contact_id_obsolescence_flag':
case 'notify_contact_id_archive_flag':
// Ignore all read-only attributes
break;

default:
if (!array_key_exists($sKey, $aDS1)) {
Utils::Log(LOG_DEBUG, "Comparison: Found an extra property '$sKey' in iTop. Data sources differ.");
if (in_array($sKey, Collector::READONLY_FIELDS)){
// Ignore all read-only attributes
continue;
}

return false;
}
if (! array_key_exists($sKey, $aDS1)) {
//locally unknown fields can NOT be updated. so it is useless to update datasynchro on itop
Utils::Log(LOG_DEBUG, "Comparison: Found an extra property '$sKey' in iTop. Data sources differ.");
}
}

Expand Down
23 changes: 13 additions & 10 deletions core/restclient.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,24 @@ public static function GetNewestKnownVersion()
*
* @param hash $aSource The definition of 'fields' the Synchro DataSource, as retrieved by Get
* @param integer $iSourceId The identifier (key) of the Synchro Data Source
* @param RestClient|null $oClient : used for tests only
*/
public static function GetFullSynchroDataSource(&$aSource, $iSourceId)
public static function GetFullSynchroDataSource(&$aSource, $iSourceId, RestClient $oRestClient = null)
{
$bResult = true;
$aAttributes = array();
$aAttributes = [];
// Optimize the calls to the REST API: one call per finalclass
foreach ($aSource['attribute_list'] as $aAttr) {
if (!array_key_exists($aAttr['finalclass'], $aAttributes)) {
$aAttributes[$aAttr['finalclass']] = array();
$aAttributes[$aAttr['finalclass']] = [];
}
$aAttributes[$aAttr['finalclass']][] = $aAttr['attcode'];
}

$oRestClient = new RestClient();
if ($oRestClient === null)
{
$oRestClient = new RestClient();
}
foreach ($aAttributes as $sFinalClass => $aAttCodes) {
Utils::Log(LOG_DEBUG, "RestClient::Get SELECT $sFinalClass WHERE attcode IN ('".implode("','", $aAttCodes)."') AND sync_source_id = $iSourceId");
$aResult = $oRestClient->Get($sFinalClass, "SELECT $sFinalClass WHERE attcode IN ('".implode("','", $aAttCodes)."') AND sync_source_id = $iSourceId");
Expand Down Expand Up @@ -196,12 +200,11 @@ public static function GetFullSynchroDataSource(&$aSource, $iSourceId)
}

// Don't care about these read-only fields
unset($aSource['friendlyname']);
unset($aSource['user_id_friendlyname']);
unset($aSource['user_id_finalclass_recall']);
unset($aSource['notify_contact_id_friendlyname']);
unset($aSource['notify_contact_id_finalclass_recall']);
unset($aSource['notify_contact_id_obsolescence_flag']);
foreach (Collector::READONLY_FIELDS as $sField){
if (array_key_exists($sField, $aSource)){
unset($aSource[$sField]);
}
}

return $bResult;
}
Expand Down
Loading