mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
365 lines
11 KiB
PHP
365 lines
11 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Helpers for implementing REST services
|
|
*
|
|
* @api
|
|
* @package RESTAPI
|
|
*/
|
|
class RestUtils
|
|
{
|
|
/**
|
|
* Registering tracking information. Any further object modification be associated with the given comment, when the modification gets
|
|
* recorded into the DB
|
|
*
|
|
* @param StdClass $oData Structured input data. Must contain 'comment'.
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
* @api
|
|
*
|
|
*/
|
|
public static function InitTrackingComment($oData)
|
|
{
|
|
$sComment = self::GetMandatoryParam($oData, 'comment');
|
|
CMDBObject::SetTrackInfo($sComment);
|
|
}
|
|
|
|
/**
|
|
* Read a mandatory parameter from from a Rest/Json structure.
|
|
*
|
|
* @param string $sParamName Name of the parameter to fetch from the input data
|
|
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
|
*
|
|
* @return mixed parameter value if present
|
|
* @throws Exception If the parameter is missing
|
|
* @api
|
|
*/
|
|
public static function GetMandatoryParam($oData, $sParamName)
|
|
{
|
|
if (isset($oData->$sParamName)) {
|
|
return $oData->$sParamName;
|
|
} else {
|
|
throw new Exception("Missing parameter '$sParamName'");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read an optional parameter from a Rest/Json structure.
|
|
*
|
|
* @param string $sParamName Name of the parameter to fetch from the input data
|
|
* @param mixed $default Default value if the parameter is not found in the input data
|
|
*
|
|
* @param StdClass $oData Structured input data.
|
|
*
|
|
* @return mixed
|
|
* @throws Exception
|
|
* @api
|
|
*/
|
|
public static function GetOptionalParam($oData, $sParamName, $default)
|
|
{
|
|
if (isset($oData->$sParamName)) {
|
|
return $oData->$sParamName;
|
|
} else {
|
|
return $default;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a class from a Rest/Json structure.
|
|
*
|
|
* @param string $sParamName Name of the parameter to fetch from the input data
|
|
* @param StdClass $oData Structured input data. Must contain the entry defined by sParamName.
|
|
*
|
|
* @return string
|
|
* @throws Exception If the parameter is missing or the class is unknown
|
|
* @api
|
|
*/
|
|
public static function GetClass($oData, $sParamName)
|
|
{
|
|
$sClass = self::GetMandatoryParam($oData, $sParamName);
|
|
if (!MetaModel::IsValidClass($sClass)) {
|
|
throw new Exception("$sParamName: '$sClass' is not a valid class'");
|
|
}
|
|
|
|
return $sClass;
|
|
}
|
|
|
|
/**
|
|
* Read a list of attribute codes from a Rest/Json structure.
|
|
*
|
|
* @param StdClass $oData Structured input data.
|
|
* @param string $sParamName Name of the parameter to fetch from the input data
|
|
*
|
|
* @param string $sClass Name of the class
|
|
*
|
|
* @return array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
|
|
* @throws Exception
|
|
* @api
|
|
*/
|
|
public static function GetFieldList($sClass, $oData, $sParamName)
|
|
{
|
|
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
|
|
$aShowFields = [];
|
|
if ($sFields == '*') {
|
|
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
|
|
$aShowFields[$sClass][] = $sAttCode;
|
|
}
|
|
} elseif ($sFields == '*+') {
|
|
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass) {
|
|
foreach (MetaModel::ListAttributeDefs($sRefClass) as $sAttCode => $oAttDef) {
|
|
$aShowFields[$sRefClass][] = $sAttCode;
|
|
}
|
|
}
|
|
} else {
|
|
foreach (explode(',', $sFields) as $sAttCode) {
|
|
$sAttCode = trim($sAttCode);
|
|
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode))) {
|
|
throw new Exception("$sParamName: invalid attribute code '$sAttCode'");
|
|
}
|
|
$aShowFields[$sClass][] = $sAttCode;
|
|
}
|
|
}
|
|
|
|
return $aShowFields;
|
|
}
|
|
|
|
/**
|
|
* Read and interpret object search criteria from a Rest/Json structure
|
|
*
|
|
* @param string $sClass Name of the class
|
|
* @param StdClass $oCriteria Hash of attribute code => value (can be a substructure or a scalar, depending on the nature of the
|
|
* attriute)
|
|
*
|
|
* @return object The object found
|
|
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
|
* @api
|
|
*/
|
|
protected static function FindObjectFromCriteria($sClass, $oCriteria)
|
|
{
|
|
$aCriteriaReport = [];
|
|
if (isset($oCriteria->finalclass)) {
|
|
if (!MetaModel::IsValidClass($oCriteria->finalclass)) {
|
|
throw new Exception("finalclass: Unknown class '".$oCriteria->finalclass."'");
|
|
}
|
|
if (!MetaModel::IsParentClass($sClass, $oCriteria->finalclass)) {
|
|
throw new Exception("finalclass: '".$oCriteria->finalclass."' is not a child class of '$sClass'");
|
|
}
|
|
$sClass = $oCriteria->finalclass;
|
|
}
|
|
$oSearch = new DBObjectSearch($sClass);
|
|
foreach ($oCriteria as $sAttCode => $value) {
|
|
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
|
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
|
if (is_object($value) || is_array($value)) {
|
|
$value = json_encode($value);
|
|
}
|
|
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
|
|
}
|
|
$oSet = new DBObjectSet($oSearch);
|
|
$iCount = $oSet->Count();
|
|
if ($iCount == 0) {
|
|
throw new Exception("No item found with criteria: ".implode(', ', $aCriteriaReport));
|
|
} elseif ($iCount > 1) {
|
|
throw new Exception("Several items found ($iCount) with criteria: ".implode(', ', $aCriteriaReport));
|
|
}
|
|
$res = $oSet->Fetch();
|
|
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* Find an object from a polymorph search specification (Rest/Json)
|
|
*
|
|
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
|
* @param bool $bAllowNullValue Allow the cases such as key = 0 or key = {null} and return null then
|
|
* @param string $sClass Name of the class
|
|
*
|
|
* @return DBObject The object found
|
|
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
|
*
|
|
* @api
|
|
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
|
|
*/
|
|
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
|
|
{
|
|
if (is_object($key)) {
|
|
$res = static::FindObjectFromCriteria($sClass, $key);
|
|
} elseif (is_numeric($key)) {
|
|
if ($bAllowNullValue && ($key == 0)) {
|
|
$res = null;
|
|
} else {
|
|
$res = MetaModel::GetObject($sClass, $key, false);
|
|
if (is_null($res)) {
|
|
throw new Exception("Invalid object $sClass::$key");
|
|
}
|
|
}
|
|
} elseif (is_string($key)) {
|
|
// OQL
|
|
$oSearch = DBObjectSearch::FromOQL($key);
|
|
$oSet = new DBObjectSet($oSearch);
|
|
$iCount = $oSet->Count();
|
|
if ($iCount == 0) {
|
|
throw new Exception("No item found for query: $key");
|
|
} elseif ($iCount > 1) {
|
|
throw new Exception("Several items found ($iCount) for query: $key");
|
|
}
|
|
$res = $oSet->Fetch();
|
|
} else {
|
|
throw new Exception("Wrong format for key");
|
|
}
|
|
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* Search objects from a polymorph search specification (Rest/Json)
|
|
*
|
|
* @param string $sClass Name of the class
|
|
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
|
|
* @param int $iLimit The limit of results to return
|
|
* @param int $iOffset The offset of results to return
|
|
*
|
|
* @return DBObjectSet The search result set
|
|
* @throws Exception If the input structure is not valid
|
|
* @api
|
|
*/
|
|
public static function GetObjectSetFromKey($sClass, $key, $iLimit = 0, $iOffset = 0)
|
|
{
|
|
if (is_object($key)) {
|
|
if (isset($key->finalclass)) {
|
|
$sClass = $key->finalclass;
|
|
if (!MetaModel::IsValidClass($sClass)) {
|
|
throw new Exception("finalclass: Unknown class '$sClass'");
|
|
}
|
|
}
|
|
|
|
$oSearch = new DBObjectSearch($sClass);
|
|
foreach ($key as $sAttCode => $value) {
|
|
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
|
$oSearch->AddCondition($sAttCode, $realValue, '=');
|
|
}
|
|
} elseif (is_numeric($key)) {
|
|
$oSearch = new DBObjectSearch($sClass);
|
|
$oSearch->AddCondition('id', $key);
|
|
} elseif (is_string($key)) {
|
|
// OQL
|
|
try {
|
|
$oSearch = DBObjectSearch::FromOQL($key);
|
|
} catch (Exception $e) {
|
|
throw new CoreOqlException('Query failed to execute', [
|
|
'query' => $key,
|
|
'exception_class' => get_class($e),
|
|
'exception_message' => $e->getMessage(),
|
|
]);
|
|
}
|
|
} else {
|
|
throw new Exception("Wrong format for key");
|
|
}
|
|
$oObjectSet = new DBObjectSet($oSearch, [], [], null, $iLimit, $iOffset);
|
|
|
|
return $oObjectSet;
|
|
}
|
|
|
|
/**
|
|
* Interpret the Rest/Json value and get a valid attribute value
|
|
*
|
|
* @param string $sAttCode Attribute code
|
|
* @param mixed $value Depending on the type of attribute (a scalar, or search criteria, or list of related objects...)
|
|
* @param string $sClass Name of the class
|
|
*
|
|
* @return mixed The value that can be used with DBObject::Set()
|
|
* @throws Exception If the specification of the value is not valid.
|
|
* @api
|
|
*/
|
|
public static function MakeValue($sClass, $sAttCode, $value)
|
|
{
|
|
try {
|
|
if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
|
throw new Exception("Unknown attribute");
|
|
}
|
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
if ($oAttDef instanceof AttributeExternalKey) {
|
|
$oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
|
|
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
|
|
} elseif ($oAttDef instanceof AttributeLinkedSet) {
|
|
if (!is_array($value)) {
|
|
throw new Exception("A link set must be defined by an array of objects");
|
|
}
|
|
$sLnkClass = $oAttDef->GetLinkedClass();
|
|
$aLinks = [];
|
|
foreach ($value as $oValues) {
|
|
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
|
|
// Fix for N°1939
|
|
if (($oAttDef instanceof AttributeLinkedSetIndirect) && ($oLnk->Get($oAttDef->GetExtKeyToRemote()) == 0)) {
|
|
continue;
|
|
}
|
|
$aLinks[] = $oLnk;
|
|
}
|
|
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
|
|
} elseif ($oAttDef instanceof AttributeTagSet) {
|
|
if (!is_array($value)) {
|
|
throw new Exception("A tag set must be defined by an array of tag codes");
|
|
}
|
|
$value = $oAttDef->FromJSONToValue($value);
|
|
} else {
|
|
$value = $oAttDef->FromJSONToValue($value);
|
|
}
|
|
} catch (Exception $e) {
|
|
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Interpret a Rest/Json structure that defines attribute values, and build an object
|
|
*
|
|
* @param array $aFields A hash of attribute code => value specification.
|
|
* @param string $sClass Name of the class
|
|
*
|
|
* @return DBObject The newly created object
|
|
* @throws Exception If the specification of the values is not valid
|
|
* @api
|
|
*/
|
|
public static function MakeObjectFromFields($sClass, $aFields)
|
|
{
|
|
$oObject = MetaModel::NewObject($sClass);
|
|
foreach ($aFields as $sAttCode => $value) {
|
|
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
|
try {
|
|
$oObject->Set($sAttCode, $realValue);
|
|
} catch (Exception $e) {
|
|
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
|
|
}
|
|
}
|
|
|
|
return $oObject;
|
|
}
|
|
|
|
/**
|
|
* Interpret a Rest/Json structure that defines attribute values, and update the given object
|
|
*
|
|
* @param array $aFields A hash of attribute code => value specification.
|
|
* @param DBObject $oObject The object being modified
|
|
*
|
|
* @return DBObject The object modified
|
|
* @throws Exception If the specification of the values is not valid
|
|
* @api
|
|
*/
|
|
public static function UpdateObjectFromFields($oObject, $aFields)
|
|
{
|
|
$sClass = get_class($oObject);
|
|
foreach ($aFields as $sAttCode => $value) {
|
|
$realValue = static::MakeValue($sClass, $sAttCode, $value);
|
|
try {
|
|
$oObject->Set($sAttCode, $realValue);
|
|
} catch (Exception $e) {
|
|
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
|
|
}
|
|
}
|
|
|
|
return $oObject;
|
|
}
|
|
}
|