mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-18 23:08:46 +02:00
OQL normalization and dashlets have been made independent from the class MetaModel
Added OQL normalization unit tests (to be run on a standard installation) SVN:trunk[2767]
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
|
||||
require_once(APPROOT.'application/dashboardlayout.class.inc.php');
|
||||
require_once(APPROOT.'application/dashlet.class.inc.php');
|
||||
require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
|
||||
/**
|
||||
* A user editable dashboard page
|
||||
@@ -82,7 +83,7 @@ abstract class Dashboard
|
||||
$iRank = (float)$oRank->textContent;
|
||||
}
|
||||
$sId = $oDomNode->getAttribute('id');
|
||||
$oNewDashlet = new $sDashletClass($sId);
|
||||
$oNewDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sId);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
|
||||
}
|
||||
@@ -183,7 +184,7 @@ abstract class Dashboard
|
||||
{
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
$oNewDashlet = new $sDashletClass($sId);
|
||||
$oNewDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sId);
|
||||
|
||||
$oForm = $oNewDashlet->GetForm();
|
||||
$oForm->SetParamsContainer($sId);
|
||||
@@ -687,7 +688,7 @@ EOF
|
||||
foreach($aDashlets as $sDashletClass => $aDashletInfo)
|
||||
{
|
||||
$oSubForm = new DesignerForm();
|
||||
$oDashlet = new $sDashletClass(0);
|
||||
$oDashlet = new $sDashletClass(new ModelReflectionRuntime(), 0);
|
||||
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
|
||||
|
||||
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
|
||||
|
||||
@@ -26,14 +26,16 @@ require_once(APPROOT.'application/forms.class.inc.php');
|
||||
*/
|
||||
abstract class Dashlet
|
||||
{
|
||||
protected $oModelReflection;
|
||||
protected $sId;
|
||||
protected $bRedrawNeeded;
|
||||
protected $bFormRedrawNeeded;
|
||||
protected $aProperties; // array of {property => value}
|
||||
protected $aCSSClasses;
|
||||
|
||||
public function __construct($sId)
|
||||
public function __construct(ModelReflection $oModelReflection, $sId)
|
||||
{
|
||||
$this->oModelReflection = $oModelReflection;
|
||||
$this->sId = $sId;
|
||||
$this->bRedrawNeeded = true; // By default: redraw each time a property changes
|
||||
$this->bFormRedrawNeeded = false; // By default: no need to redraw the form (independent fields)
|
||||
@@ -268,9 +270,9 @@ EOF
|
||||
|
||||
class DashletEmptyCell extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
}
|
||||
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
@@ -299,9 +301,9 @@ class DashletEmptyCell extends Dashlet
|
||||
|
||||
class DashletPlainText extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['text'] = Dict::S('UI:DashletPlainText:Prop-Text:Default');
|
||||
}
|
||||
|
||||
@@ -332,9 +334,9 @@ class DashletPlainText extends Dashlet
|
||||
|
||||
class DashletObjectList extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = '';
|
||||
$this->aProperties['query'] = 'SELECT Contact';
|
||||
$this->aProperties['menu'] = false;
|
||||
@@ -406,9 +408,9 @@ class DashletObjectList extends Dashlet
|
||||
|
||||
abstract class DashletGroupBy extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = '';
|
||||
$this->aProperties['query'] = 'SELECT Contact';
|
||||
$this->aProperties['group_by'] = 'status';
|
||||
@@ -439,13 +441,13 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$sAttCode = $sGroupBy;
|
||||
$sFunction = null;
|
||||
}
|
||||
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
|
||||
if (!$this->oModelReflection->IsValidAttCode($sClass, $sAttCode))
|
||||
{
|
||||
$oPage->add('<p>'.Dict::S('UI:DashletGroupBy:MissingGroupBy').'</p>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sAttLabel = MetaModel::GetLabel($sClass, $sAttCode);
|
||||
$sAttLabel = $this->oModelReflection->GetLabel($sClass, $sAttCode);
|
||||
if (!is_null($sFunction))
|
||||
{
|
||||
$sFunction = $aMatches[2];
|
||||
@@ -534,7 +536,7 @@ abstract class DashletGroupBy extends Dashlet
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$sClass = $oSearch->GetClass();
|
||||
$aGroupBy = array();
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
foreach($this->oModelReflection->ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (!$oAttDef->IsScalar()) continue; // skip link sets
|
||||
if ($oAttDef instanceof AttributeFriendlyName) continue;
|
||||
@@ -691,9 +693,9 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
class DashletGroupByPie extends DashletGroupBy
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['style'] = 'pie';
|
||||
}
|
||||
|
||||
@@ -710,9 +712,9 @@ class DashletGroupByPie extends DashletGroupBy
|
||||
|
||||
class DashletGroupByBars extends DashletGroupBy
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['style'] = 'bars';
|
||||
}
|
||||
|
||||
@@ -728,9 +730,9 @@ class DashletGroupByBars extends DashletGroupBy
|
||||
|
||||
class DashletGroupByTable extends DashletGroupBy
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['style'] = 'table';
|
||||
}
|
||||
|
||||
@@ -747,11 +749,11 @@ class DashletGroupByTable extends DashletGroupBy
|
||||
|
||||
class DashletHeaderStatic extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = Dict::S('UI:DashletHeaderStatic:Prop-Title:Default');
|
||||
$sIcon = MetaModel::GetClassIcon('Contact', false);
|
||||
$sIcon = $this->oModelReflection->GetClassIcon('Contact', false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIcon);
|
||||
$this->aProperties['icon'] = $sIcon;
|
||||
}
|
||||
@@ -827,11 +829,11 @@ class DashletHeaderStatic extends Dashlet
|
||||
|
||||
class DashletHeaderDynamic extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['title'] = Dict::S('UI:DashletHeaderDynamic:Prop-Title:Default');
|
||||
$sIcon = MetaModel::GetClassIcon('Contact', false);
|
||||
$sIcon = $this->oModelReflection->GetClassIcon('Contact', false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIcon);
|
||||
$this->aProperties['icon'] = $sIcon;
|
||||
$this->aProperties['subtitle'] = Dict::S('UI:DashletHeaderDynamic:Prop-Subtitle:Default');
|
||||
@@ -854,11 +856,11 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$sIconPath = utils::GetAbsoluteUrlModulesRoot().$sIcon;
|
||||
|
||||
if (MetaModel::IsValidAttCode($sClass, $sGroupBy))
|
||||
if ($this->oModelReflection->IsValidAttCode($sClass, $sGroupBy))
|
||||
{
|
||||
if (count($aValues) == 0)
|
||||
{
|
||||
$aAllowed = MetaModel::GetAllowedValues_att($sClass, $sGroupBy);
|
||||
$aAllowed = $this->oModelReflection->GetAllowedValues_att($sClass, $sGroupBy);
|
||||
if (is_array($aAllowed))
|
||||
{
|
||||
$aValues = array_keys($aAllowed);
|
||||
@@ -929,9 +931,9 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
|
||||
$sClass = $oSearch->GetClass();
|
||||
$aGroupBy = array();
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
foreach($this->oModelReflection->ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (!$oAttDef instanceof AttributeEnum && (!$oAttDef instanceof AttributeFinalClass || !MetaModel::HasChildrenClasses($sClass))) continue;
|
||||
if (!$oAttDef instanceof AttributeEnum && (!$oAttDef instanceof AttributeFinalClass || !$this->oModelReflection->HasChildrenClasses($sClass))) continue;
|
||||
$sLabel = $oAttDef->GetLabel();
|
||||
$aGroupBy[$sAttCode] = $sLabel;
|
||||
}
|
||||
@@ -948,9 +950,9 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$oField = new DesignerComboField('values', Dict::S('UI:DashletHeaderDynamic:Prop-Values'), $this->aProperties['values']);
|
||||
$oField->MultipleSelection(true);
|
||||
if (isset($sClass) && MetaModel::IsValidAttCode($sClass, $this->aProperties['group_by']))
|
||||
if (isset($sClass) && $this->oModelReflection->IsValidAttCode($sClass, $this->aProperties['group_by']))
|
||||
{
|
||||
$aValues = MetaModel::GetAllowedValues_att($sClass, $this->aProperties['group_by']);
|
||||
$aValues = $this->oModelReflection->GetAllowedValues_att($sClass, $this->aProperties['group_by']);
|
||||
$oField->SetAllowedValues($aValues);
|
||||
}
|
||||
else
|
||||
@@ -1008,9 +1010,9 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
class DashletBadge extends Dashlet
|
||||
{
|
||||
public function __construct($sId)
|
||||
public function __construct($oModelReflection, $sId)
|
||||
{
|
||||
parent::__construct($sId);
|
||||
parent::__construct($oModelReflection, $sId);
|
||||
$this->aProperties['class'] = 'Contact';
|
||||
$this->aCSSClasses[] = 'dashlet-inline';
|
||||
$this->aCSSClasses[] = 'dashlet-badge';
|
||||
@@ -1046,9 +1048,9 @@ class DashletBadge extends Dashlet
|
||||
|
||||
$aLinkClasses = array();
|
||||
|
||||
foreach(MetaModel::GetClasses('bizmodel') as $sClass)
|
||||
foreach($this->oModelReflection->GetClasses('bizmodel') as $sClass)
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
foreach($this->oModelReflection->ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef instanceof AttributeLinkedSetIndirect)
|
||||
{
|
||||
@@ -1065,14 +1067,14 @@ class DashletBadge extends Dashlet
|
||||
{
|
||||
if (!array_key_exists($sClass, $aLinkClasses))
|
||||
{
|
||||
$sIconUrl = MetaModel::GetClassIcon($sClass, false);
|
||||
$sIconUrl = $this->oModelReflection->GetClassIcon($sClass, false);
|
||||
$sIconFilePath = str_replace(utils::GetAbsoluteUrlAppRoot(), APPROOT, $sIconUrl);
|
||||
if (($sIconUrl == '') || !file_exists($sIconFilePath))
|
||||
{
|
||||
// The icon does not exist, leet's use a transparent one of the same size.
|
||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().'images/transparent_32_32.png';
|
||||
}
|
||||
$aValues[] = array('value' => $sClass, 'label' => MetaModel::GetName($sClass), 'icon' => $sIconUrl);
|
||||
$aValues[] = array('value' => $sClass, 'label' => $this->oModelReflection->GetName($sClass), 'icon' => $sIconUrl);
|
||||
}
|
||||
}
|
||||
$oField->SetAllowedValues($aValues);
|
||||
|
||||
@@ -720,6 +720,19 @@ class DBObjectSearch
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
{
|
||||
if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode))
|
||||
{
|
||||
throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}'");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !($oAttExtKey instanceof AttributeHierarchicalKey))
|
||||
{
|
||||
throw new CoreException("The specified tree operator $iOperatorCode is not applicable to the key '{$this->GetClass()}::$sExtKeyAttCode', which is not a HierarchicalKey");
|
||||
}
|
||||
// Note: though it seems to be a good practice to clone the given source filter
|
||||
// (as it was done and fixed an issue in MergeWith())
|
||||
// this was not implemented here because it was causing a regression (login as admin, select an org, click on any badge)
|
||||
@@ -734,20 +747,6 @@ class DBObjectSearch
|
||||
|
||||
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode)
|
||||
{
|
||||
if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode))
|
||||
{
|
||||
throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !($oAttExtKey instanceof AttributeHierarchicalKey))
|
||||
{
|
||||
throw new CoreException("The specified tree operator $iOperatorCode is not applicable to the key '{$this->GetClass()}::$sExtKeyAttCode', which is not a HierarchicalKey");
|
||||
}
|
||||
|
||||
// Find the node on which the new tree must be attached (most of the time it is "this")
|
||||
$oReceivingFilter = $this->GetNode($this->GetClassAlias());
|
||||
|
||||
@@ -757,6 +756,17 @@ class DBObjectSearch
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
|
||||
{
|
||||
throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}'");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
// à refaire en spécifique dans FromOQL
|
||||
throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
// Note: though it seems to be a good practice to clone the given source filter
|
||||
// (as it was done and fixed an issue in MergeWith())
|
||||
// this was not implemented here because it was causing a regression (login as admin, select an org, click on any badge)
|
||||
@@ -772,16 +782,6 @@ class DBObjectSearch
|
||||
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
$sForeignClassAlias = $oFilter->GetClassAlias();
|
||||
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
|
||||
{
|
||||
throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
|
||||
// Find the node on which the new tree must be attached (most of the time it is "this")
|
||||
$oReceivingFilter = $this->GetNode($this->GetClassAlias());
|
||||
@@ -1129,7 +1129,7 @@ class DBObjectSearch
|
||||
$sFltCode = $oExpression->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
// Try to find an alias
|
||||
// Need to find the right alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aClassAliases as $sAlias => $sReal)
|
||||
@@ -1139,29 +1139,8 @@ class DBObjectSearch
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sQuery, $oExpression->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sClassAlias, $aClassAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oExpression->GetParentDetails(), array_keys($aClassAliases));
|
||||
}
|
||||
$sClass = $aClassAliases[$sClassAlias];
|
||||
if (!MetaModel::IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), MetaModel::GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
|
||||
return new FieldExpression($sFltCode, $sClassAlias);
|
||||
}
|
||||
elseif ($oExpression instanceof VariableOqlExpression)
|
||||
@@ -1242,15 +1221,13 @@ class DBObjectSearch
|
||||
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oOqlQuery = $oOql->ParseObjectQuery();
|
||||
|
||||
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
|
||||
|
||||
$sClass = $oOqlQuery->GetClass();
|
||||
$sClassAlias = $oOqlQuery->GetClassAlias();
|
||||
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sQuery, $oOqlQuery->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
|
||||
$oResultFilter = new DBObjectSearch($sClass, $sClassAlias);
|
||||
$aAliases = array($sClassAlias => $sClass);
|
||||
|
||||
@@ -1266,21 +1243,6 @@ class DBObjectSearch
|
||||
{
|
||||
$sJoinClass = $oJoinSpec->GetClass();
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!MetaModel::IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sQuery, $oJoinSpec->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $aAliases))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sQuery, $oJoinSpec->GetClassAliasDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sQuery, $oJoinSpec->GetClassDetails());
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: ext key on the left only !!!
|
||||
// normalization should take care of this
|
||||
@@ -1290,32 +1252,17 @@ class DBObjectSearch
|
||||
|
||||
$oRightField = $oJoinSpec->GetRightField();
|
||||
$sToClass = $oRightField->GetParent();
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, $oRightField->GetNameDetails(), array('id'));
|
||||
}
|
||||
|
||||
$aAliases[$sJoinClassAlias] = $sJoinClass;
|
||||
$aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias);
|
||||
|
||||
if (!array_key_exists($sFromClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, $oLeftField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, $oRightField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
$aExtKeys = array_keys(MetaModel::GetExternalKeys($aAliases[$sFromClass]));
|
||||
if (!in_array($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, $oLeftField->GetNameDetails(), $aExtKeys);
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$aJoinItems[$sToClass]->AddCondition_ReferencedBy($aJoinItems[$sFromClass], $sExtKeyAttCode);
|
||||
$oReceiver = $aJoinItems[$sToClass];
|
||||
$oNewComer = $aJoinItems[$sFromClass];
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oReceiver->AddCondition_ReferencedBy_InNameSpace($oNewComer, $sExtKeyAttCode, $oReceiver->m_aClasses, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1350,7 +1297,11 @@ class DBObjectSearch
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
||||
break;
|
||||
}
|
||||
$aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode, $iOperatorCode);
|
||||
$oReceiver = $aJoinItems[$sFromClass];
|
||||
$oNewComer = $aJoinItems[$sToClass];
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oReceiver->AddCondition_PointingTo_InNameSpace($oNewComer, $sExtKeyAttCode, $oReceiver->m_aClasses, $aAliasTranslation, $iOperatorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1360,10 +1311,6 @@ class DBObjectSearch
|
||||
foreach ($oOqlQuery->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
if (!array_key_exists($sClassToSelect, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
$oResultFilter->m_aClasses = $aAliases;
|
||||
|
||||
@@ -127,15 +127,38 @@ class OqlJoinSpec
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOqlExpression extends BinaryExpression
|
||||
interface CheckableExpression
|
||||
{
|
||||
/**
|
||||
* Check the validity of the expression with regard to the data model
|
||||
* and the query in which it is used
|
||||
*
|
||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
||||
* @param array $aAliases Aliases to class names (for the current query)
|
||||
* @param string $sSourceQuery For the reporting
|
||||
* @throws OqlNormalizeException
|
||||
*/
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
|
||||
class ScalarOqlExpression extends ScalarExpression
|
||||
class BinaryOqlExpression extends BinaryExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
$this->m_oLeftExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
$this->m_oRightExpr->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression
|
||||
class ScalarOqlExpression extends ScalarExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
// a scalar is always fine
|
||||
}
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression implements CheckableExpression
|
||||
{
|
||||
protected $m_oParent;
|
||||
protected $m_oName;
|
||||
@@ -161,22 +184,84 @@ class FieldOqlExpression extends FieldExpression
|
||||
{
|
||||
return $this->m_oName;
|
||||
}
|
||||
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
$sClassAlias = $this->GetParent();
|
||||
$sFltCode = $this->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
// Try to find an alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aAliases as $sAlias => $sReal)
|
||||
{
|
||||
foreach($oModelReflection->GetFiltersList($sReal) as $sAnFltCode)
|
||||
{
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sSourceQuery, $this->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sClassAlias, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $this->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
$sClass = $aAliases[$sClassAlias];
|
||||
if (!$oModelReflection->IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sSourceQuery, $this->GetNameDetails(), $oModelReflection->GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VariableOqlExpression extends VariableExpression
|
||||
class VariableOqlExpression extends VariableExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
// a scalar is always fine
|
||||
}
|
||||
}
|
||||
|
||||
class ListOqlExpression extends ListExpression
|
||||
class ListOqlExpression extends ListExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
foreach ($this->GetItems() as $oItemExpression)
|
||||
{
|
||||
$oItemExpression->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionOqlExpression extends FunctionExpression
|
||||
class FunctionOqlExpression extends FunctionExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
foreach ($this->GetArgs() as $oArgExpression)
|
||||
{
|
||||
$oArgExpression->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IntervalOqlExpression extends IntervalExpression
|
||||
class IntervalOqlExpression extends IntervalExpression implements CheckableExpression
|
||||
{
|
||||
public function Check(ModelReflection $oModelReflection, $aAliases, $sSourceQuery)
|
||||
{
|
||||
// an interval is always fine (made of a scalar and unit)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OqlQuery
|
||||
@@ -235,6 +320,153 @@ class OqlObjectQuery extends OqlQuery
|
||||
{
|
||||
return $this->m_oClassAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively check the validity of the expression with regard to the data model
|
||||
* and the query in which it is used
|
||||
*
|
||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
||||
* @throws OqlNormalizeException
|
||||
*/
|
||||
public function Check(ModelReflection $oModelReflection, $sSourceQuery)
|
||||
{
|
||||
$sClass = $this->GetClass();
|
||||
$sClassAlias = $this->GetClassAlias();
|
||||
|
||||
if (!$oModelReflection->IsValidClass($sClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sSourceQuery, $this->GetClassDetails(), $oModelReflection->GetClasses('bizmodelx'));
|
||||
}
|
||||
|
||||
$aAliases = array($sClassAlias => $sClass);
|
||||
|
||||
$aJoinSpecs = $this->GetJoins();
|
||||
if (is_array($aJoinSpecs))
|
||||
{
|
||||
foreach ($aJoinSpecs as $oJoinSpec)
|
||||
{
|
||||
$sJoinClass = $oJoinSpec->GetClass();
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!$oModelReflection->IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new UnknownClassOqlException($sSourceQuery, $oJoinSpec->GetClassDetails(), $oModelReflection->GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $aAliases))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sSourceQuery, $oJoinSpec->GetClassAliasDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sSourceQuery, $oJoinSpec->GetClassDetails());
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: ext key on the left only !!!
|
||||
// normalization should take care of this
|
||||
$oLeftField = $oJoinSpec->GetLeftField();
|
||||
$sFromClass = $oLeftField->GetParent();
|
||||
$sExtKeyAttCode = $oLeftField->GetName();
|
||||
|
||||
$oRightField = $oJoinSpec->GetRightField();
|
||||
$sToClass = $oRightField->GetParent();
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sSourceQuery, $oRightField->GetNameDetails(), array('id'));
|
||||
}
|
||||
|
||||
$aAliases[$sJoinClassAlias] = $sJoinClass;
|
||||
|
||||
if (!array_key_exists($sFromClass, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sSourceQuery, $oLeftField->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sSourceQuery, $oRightField->GetParentDetails(), array_keys($aAliases));
|
||||
}
|
||||
$aExtKeys = array_keys($oModelReflection->GetExternalKeys($aAliases[$sFromClass]));
|
||||
if (!in_array($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sSourceQuery, $oLeftField->GetNameDetails(), $aExtKeys);
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$oAttExtKey = $oModelReflection->GetAttributeDef($aAliases[$sFromClass], $sExtKeyAttCode);
|
||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new OqlNormalizeException("The joined class ($aAliases[$sFromClass]) is not compatible with the external key, which is pointing to {$oAttExtKey->GetTargetClass()}", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOperator = $oJoinSpec->GetOperator();
|
||||
switch($sOperator)
|
||||
{
|
||||
case '=':
|
||||
$iOperatorCode = TREE_OPERATOR_EQUALS;
|
||||
break;
|
||||
case 'BELOW':
|
||||
$iOperatorCode = TREE_OPERATOR_BELOW;
|
||||
break;
|
||||
case 'BELOW_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_BELOW_STRICT;
|
||||
break;
|
||||
case 'NOT_BELOW':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_BELOW;
|
||||
break;
|
||||
case 'NOT_BELOW_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_BELOW_STRICT;
|
||||
break;
|
||||
case 'ABOVE':
|
||||
$iOperatorCode = TREE_OPERATOR_ABOVE;
|
||||
break;
|
||||
case 'ABOVE_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_ABOVE_STRICT;
|
||||
break;
|
||||
case 'NOT_ABOVE':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE;
|
||||
break;
|
||||
case 'NOT_ABOVE_STRICT':
|
||||
$iOperatorCode = TREE_OPERATOR_NOT_ABOVE_STRICT;
|
||||
break;
|
||||
}
|
||||
$oAttExtKey = $oModelReflection->GetAttributeDef($aAliases[$sFromClass], $sExtKeyAttCode);
|
||||
if(!$oModelReflection->IsSameFamilyBranch($aAliases[$sToClass], $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new OqlNormalizeException("The joined class ($aAliases[$sToClass]) is not compatible with the external key, which is pointing to {$oAttExtKey->GetTargetClass()}", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
if(($iOperatorCode != TREE_OPERATOR_EQUALS) && !($oAttExtKey instanceof AttributeHierarchicalKey))
|
||||
{
|
||||
throw new OqlNormalizeException("The specified tree operator $sOperator is not applicable to the key", $sSourceQuery, $oLeftField->GetNameDetails());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the select information
|
||||
//
|
||||
$aSelected = array();
|
||||
foreach ($this->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
if (!array_key_exists($sClassToSelect, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sSourceQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
|
||||
// Check the condition tree
|
||||
//
|
||||
if ($this->m_oCondition instanceof Expression)
|
||||
{
|
||||
$this->m_oCondition->Check($oModelReflection, $aAliases, $sSourceQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -739,7 +739,7 @@ try
|
||||
$sDashletId = utils::ReadParam('dashlet_id', '', false, 'raw_data');
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet'))
|
||||
{
|
||||
$oDashlet = new $sDashletClass($sDashletId);
|
||||
$oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId);
|
||||
$offset = $oPage->start_capture();
|
||||
$oDashlet->DoRender($oPage, true /* bEditMode */, false /* bEnclosingDiv */);
|
||||
$sHtml = addslashes($oPage->end_capture($offset));
|
||||
@@ -767,7 +767,7 @@ try
|
||||
$aPreviousValues = $aParams['previous_values']; // hash array: 'attr_xxx' => 'old_value'
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet'))
|
||||
{
|
||||
$oDashlet = new $sDashletClass($sDashletId);
|
||||
$oDashlet = new $sDashletClass(new ModelReflectionRuntime(), $sDashletId);
|
||||
$oForm = $oDashlet->GetForm();
|
||||
$aValues = $oForm->ReadParams(); // hash array: 'xxx' => 'new_value'
|
||||
|
||||
@@ -867,7 +867,7 @@ EOF
|
||||
|
||||
if (is_subclass_of($sDashletClass, 'Dashlet'))
|
||||
{
|
||||
$oDashlet = new $sDashletClass(0);
|
||||
$oDashlet = new $sDashletClass(new ModelReflectionRuntime(), 0);
|
||||
$oDashlet->FromParams($aValues);
|
||||
|
||||
ApplicationMenu::LoadAdditionalMenus();
|
||||
|
||||
@@ -402,6 +402,8 @@ abstract class TestBizModel extends TestHandler
|
||||
// abstract static public function GetBusinessModelFile();
|
||||
// abstract static public function GetConfigFile();
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
$sConfigFile = APPROOT.$this->GetConfigFile();
|
||||
|
||||
@@ -62,6 +62,12 @@ function IsAValidTestClass($sClassName)
|
||||
return true;
|
||||
}
|
||||
|
||||
function GetTestClassLine($sClassName)
|
||||
{
|
||||
$oReflectionClass = new ReflectionClass($sClassName);
|
||||
return $oReflectionClass->getStartLine();
|
||||
}
|
||||
|
||||
function DisplayEvents($aEvents, $sTitle)
|
||||
{
|
||||
echo "<h4>$sTitle</h4>\n";
|
||||
@@ -122,7 +128,9 @@ else if ($sTodo == 'exec')
|
||||
else
|
||||
{
|
||||
$oTest = new $sTestClass();
|
||||
$iStartLine = GetTestClassLine($sTestClass);
|
||||
echo "<h3>Testing: ".$oTest->GetName()."</h3>\n";
|
||||
echo "<h6>testlist.inc.php: $iStartLine</h6>\n";
|
||||
$bRes = $oTest->Execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -255,6 +255,141 @@ class TestOQLParser extends TestFunction
|
||||
}
|
||||
}
|
||||
|
||||
class TestOQLNormalization extends TestBizModel
|
||||
{
|
||||
static public function GetName() {return 'Check OQL normalization';}
|
||||
static public function GetDescription() {return 'Attempts a series of queries, and in particular those with unknown or inconsistent class/attributes. Assumes a very standard installation!';}
|
||||
|
||||
protected function CheckQuery($sQuery, $bIsCorrectQuery)
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sQuery);
|
||||
self::DumpVariable($sQuery);
|
||||
}
|
||||
catch (OQLNormalizeException $OqlException)
|
||||
{
|
||||
if ($bIsCorrectQuery)
|
||||
{
|
||||
echo "<p>More info on this unexpected failure:<br/>".$OqlException->getHtmlDesc()."</p>\n";
|
||||
throw $OqlException;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything is fine :-)
|
||||
echo "<p>More info on this expected failure:<br/>".$OqlException->getHtmlDesc()."</p>\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if ($bIsCorrectQuery)
|
||||
{
|
||||
echo "<p>More info on this <b>un</b>expected failure:<br/>".htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8')."</p>\n";
|
||||
throw $e;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything is fine :-)
|
||||
echo "<p>More info on this expected failure:<br/>".htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8')."</p>\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// The query was correctly parsed, was it expected to be correct ?
|
||||
if ($bIsCorrectQuery)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function TestQuery($sQuery, $bIsCorrectQuery)
|
||||
{
|
||||
if (!$this->CheckQuery($sQuery, $bIsCorrectQuery))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function DoExecute()
|
||||
{
|
||||
$aQueries = array(
|
||||
'SELECT Contact' => true,
|
||||
'SELECT Contact WHERE nom_de_famille = "foo"' => false,
|
||||
'SELECT Contact AS c WHERE name = "foo"' => true,
|
||||
'SELECT Contact AS c WHERE nom_de_famille = "foo"' => false,
|
||||
'SELECT Contact AS c WHERE c.name = "foo"' => true,
|
||||
'SELECT Contact AS c WHERE Contact.name = "foo"' => false,
|
||||
'SELECT Contact AS c WHERE x.name = "foo"' => false,
|
||||
|
||||
'SELECT RelationProfessionnelle' => false,
|
||||
'SELECT RelationProfessionnelle AS c WHERE name = "foo"' => false,
|
||||
|
||||
// The first query is the base query altered only in one place in the subsequent queries
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.id WHERE p.name LIKE "foo"' => true,
|
||||
'SELECT Person AS p JOIN lnkXXXXXXXXXXXX AS lnk ON lnk.person_id = p.id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON p.person_id = p.id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON person_id = p.id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.role = p.id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.team_id = p.id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id BELOW p.id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.org_id WHERE p.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON p.id = lnk.person_id WHERE p.name LIKE "foo"' => false, // inverted the JOIN spec
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.id WHERE name LIKE "foo"' => true,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.id WHERE x.name LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.id WHERE p.eman LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.id WHERE eman LIKE "foo"' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON lnk.person_id = p.id WHERE id = 1' => false,
|
||||
'SELECT Person AS p JOIN lnkPersonToTeam AS lnk ON p.id = lnk.person_id WHERE p.name LIKE "foo"' => false,
|
||||
|
||||
'SELECT Person AS p JOIN Organization AS o ON p.org_id = o.id WHERE p.name LIKE "foo" AND o.name LIKE "land"' => true,
|
||||
'SELECT Person AS p JOIN Organization AS o ON p.location_id = o.id WHERE p.name LIKE "foo" AND o.name LIKE "land"' => false,
|
||||
'SELECT Person AS p JOIN Organization AS o ON p.name = o.id WHERE p.name LIKE "foo" AND o.name LIKE "land"' => false,
|
||||
|
||||
'SELECT Person AS p JOIN Organization AS o ON p.org_id = o.id JOIN Person AS p ON p.org_id = o.id' => false,
|
||||
'SELECT Person JOIN Organization AS o ON Person.org_id = o.id JOIN Person ON Person.org_id = o.id' => false,
|
||||
|
||||
'SELECT Person AS p JOIN Location AS l ON p.location_id = l.id' => true,
|
||||
'SELECT Person AS p JOIN Location AS l ON p.location_id BELOW l.id' => false,
|
||||
|
||||
'SELECT Person FROM Person JOIN Location ON Person.location_id = Location.id' => true,
|
||||
'SELECT p FROM Person AS p JOIN Location AS l ON p.location_id = l.id' => true,
|
||||
'SELECT l FROM Person AS p JOIN Location AS l ON p.location_id = l.id' => true,
|
||||
'SELECT l, p FROM Person AS p JOIN Location AS l ON p.location_id = l.id' => true,
|
||||
'SELECT p, l FROM Person AS p JOIN Location AS l ON p.location_id = l.id' => true,
|
||||
'SELECT foo FROM Person AS p JOIN Location AS l ON p.location_id = l.id' => false,
|
||||
'SELECT p, foo FROM Person AS p JOIN Location AS l ON p.location_id = l.id' => false,
|
||||
);
|
||||
|
||||
$iErrors = 0;
|
||||
|
||||
foreach($aQueries as $sQuery => $bIsCorrectQuery)
|
||||
{
|
||||
$sIsOk = $bIsCorrectQuery ? 'good' : 'bad';
|
||||
echo "<h4>Testing query: $sQuery ($sIsOk)</h4>\n";
|
||||
try
|
||||
{
|
||||
$bRet = $this->TestQuery($sQuery, $bIsCorrectQuery);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->m_aErrors[] = $e->getMessage();
|
||||
$bRet = false;
|
||||
}
|
||||
if (!$bRet) $iErrors++;
|
||||
}
|
||||
|
||||
return ($iErrors == 0);
|
||||
}
|
||||
}
|
||||
|
||||
class TestCSVParser extends TestFunction
|
||||
{
|
||||
@@ -1127,8 +1262,6 @@ class TestItopEfficiency extends TestBizModel
|
||||
return 'Measure time to perform the queries';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoBenchmark($sOqlQuery)
|
||||
{
|
||||
echo "<h3>Testing query: $sOqlQuery</h3>";
|
||||
@@ -1252,8 +1385,6 @@ class TestQueries extends TestBizModel
|
||||
return 'Try as many queries as possible';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoBenchmark($sOqlQuery)
|
||||
{
|
||||
echo "<h5>Testing query: $sOqlQuery</h5>";
|
||||
@@ -1314,9 +1445,9 @@ class TestQueries extends TestBizModel
|
||||
'SELECT Person AS PP WHERE PP.friendlyname LIKE "%dali"',
|
||||
'SELECT Person AS PP WHERE PP.location_id_friendlyname LIKE "%ce ch%"',
|
||||
'SELECT Organization AS OO JOIN Person AS PP ON PP.org_id = OO.id',
|
||||
'SELECT lnkTeamToContact AS lnk JOIN Team AS T ON lnk.team_id = T.id',
|
||||
'SELECT lnkTeamToContact AS lnk JOIN Team AS T ON lnk.team_id = T.id JOIN Contact AS C ON lnk.contact_id = C.id',
|
||||
'SELECT Incident JOIN Person ON Incident.agent_id = Person.id WHERE Person.id = 5',
|
||||
'SELECT lnkPersonToTeam AS lnk JOIN Team AS T ON lnk.team_id = T.id',
|
||||
'SELECT lnkPersonToTeam AS lnk JOIN Team AS T ON lnk.team_id = T.id JOIN Person AS p ON lnk.person_id = p.id',
|
||||
'SELECT UserRequest AS ur JOIN Person ON ur.agent_id = Person.id WHERE Person.id = 5',
|
||||
// this one is failing...
|
||||
//'SELECT L, P FROM Person AS P JOIN Location AS L ON P.location_id = L.id',
|
||||
);
|
||||
@@ -1364,8 +1495,6 @@ class TestQueriesByAPI extends TestBizModel
|
||||
return 'Validate the DBObjectSearch API, through a set of complex (though realistic cases)';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
// Note: relying on eval() - after upgrading to PHP 5.3 we can move to closure (aka anonymous functions)
|
||||
@@ -1464,9 +1593,6 @@ class TestItopBulkLoad extends TestBizModel
|
||||
return 'Execute a bulk change at the Core API level';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
$sLogin = 'testbulkload_'.time();
|
||||
@@ -2033,8 +2159,6 @@ class TestDataExchange extends TestBizModel
|
||||
return 'Test REST services: synchro_import and synchro_exec';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoExecScenario($aSingleScenario)
|
||||
{
|
||||
echo "<div style=\"padding: 10;\">\n";
|
||||
@@ -3037,8 +3161,6 @@ abstract class TestSoapDirect extends TestBizModel
|
||||
static public function GetName() {return 'Test web services locally';}
|
||||
static public function GetDescription() {return 'Invoke the service directly (troubleshooting)';}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected $m_aTestSpecs;
|
||||
|
||||
protected function DoExecute()
|
||||
@@ -3137,8 +3259,6 @@ class TestTriggerAndEmail extends TestBizModel
|
||||
static public function GetName() {return 'Test trigger and email';}
|
||||
static public function GetDescription() {return 'Create a trigger and an email, then activates the trigger';}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function CreateEmailSpec($oTrigger, $sStatus, $sTo, $sCC, $sTesterEmail)
|
||||
{
|
||||
$oAction = MetaModel::NewObject("ActionEmail");
|
||||
@@ -3281,8 +3401,6 @@ class TestDBProperties extends TestBizModel
|
||||
return 'Write and read a dummy property';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
$sName = 'test';
|
||||
@@ -3304,8 +3422,6 @@ class TestCreateObjects extends TestBizModel
|
||||
return 'Create weird objects (reproduce a bug?)';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
$oMyObj = MetaModel::NewObject("Server");
|
||||
@@ -3365,8 +3481,6 @@ class TestSetLinkset extends TestBizModel
|
||||
return 'Create a user account, setting its profile by the mean of a string (prerequisite to CSV import of linksets)';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
$oUser = new UserLocal();
|
||||
@@ -3399,8 +3513,6 @@ class TestEmailAsynchronous extends TestBizModel
|
||||
return 'Queues a request to send an email';
|
||||
}
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
for ($i = 0 ; $i < 2 ; $i++)
|
||||
|
||||
Reference in New Issue
Block a user