#472 REST Services: added core/delete (to bulk delete, full-featured), and validated the operation core/apply_stimulus

SVN:trunk[2616]
This commit is contained in:
Romain Quetiez
2013-03-13 11:01:16 +00:00
parent 462f163d8a
commit 3cf398618e
4 changed files with 213 additions and 2 deletions

View File

@@ -587,6 +587,10 @@ class RestResult
* Result: the requested operation is not valid for the specified version
*/
const UNKNOWN_OPERATION = 11;
/**
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
*/
const UNSAFE = 12;
/**
* Result: the operation could not be performed, see the message for troubleshooting
*/
@@ -789,7 +793,11 @@ class RestUtils
}
elseif (is_numeric($key))
{
$res = MetaModel::GetObject($sClass, $key);
$res = MetaModel::GetObject($sClass, $key, false);
if (is_null($res))
{
throw new Exception("Invalid object $sClass::$key");
}
}
elseif (is_string($key))
{

View File

@@ -82,6 +82,9 @@ class DeletionPlan
public function ComputeResults()
{
$this->m_iToDelete = 0;
$this->m_iToUpdate = 0;
foreach($this->m_aToDelete as $sClass => $aToDelete)
{
foreach($aToDelete as $iId => $aData)

View File

@@ -155,6 +155,7 @@ class RestResultWithObjects extends RestResult
if ($oObject)
{
$oObjRes->class = get_class($oObject);
foreach ($aFields as $sAttCode)
{
$oObjRes->AddField($oObject, $sAttCode);
@@ -165,6 +166,44 @@ class RestResultWithObjects extends RestResult
}
}
/**
* Deletion result codes for a target object (either deleted or updated)
*
* @package Extensibility
* @api
* @since 2.0.1
*/
class RestDelete
{
/**
* Result: Object deleted as per the initial request
*/
const OK = 0;
/**
* Result: general issue (user rights or ... ?)
*/
const ISSUE = 1;
/**
* Result: Must be deleted to preserve database integrity
*/
const AUTO_DELETE = 2;
/**
* Result: Must be deleted to preserve database integrity, but that is NOT possible
*/
const AUTO_DELETE_ISSUE = 3;
/**
* Result: Must be deleted to preserve database integrity, but this must be requested explicitely
*/
const REQUEST_EXPLICITELY = 4;
/**
* Result: Must be updated to preserve database integrity
*/
const AUTO_UPDATE = 5;
/**
* Result: Must be updated to preserve database integrity, but that is NOT possible
*/
const AUTO_UPDATE_ISSUE = 6;
}
/**
* Implementation of core REST services (create/get/update... objects)
@@ -200,6 +239,10 @@ class CoreServices implements iRestServiceProvider
'verb' => 'core/get',
'description' => 'Search for objects'
);
$aOps[] = array(
'verb' => 'core/delete',
'description' => 'Delete objects'
);
}
return $aOps;
}
@@ -294,7 +337,7 @@ class CoreServices implements iRestServiceProvider
}
break;
case 'core/get':
case 'core/get':
$sClass = RestUtils::GetClass($aParams, 'class');
$key = RestUtils::GetMandatoryParam($aParams, 'key');
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
@@ -306,10 +349,147 @@ class CoreServices implements iRestServiceProvider
}
$oResult->message = "Found: ".$oObjectSet->Count();
break;
case 'core/delete':
$sClass = RestUtils::GetClass($aParams, 'class');
$key = RestUtils::GetMandatoryParam($aParams, 'key');
$bSimulate = RestUtils::GetOptionalParam($aParams, 'simulate', false);
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key);
$aObjects = $oObjectSet->ToArray();
$this->DeleteObjects($oResult, $aObjects, $bSimulate);
break;
default:
// unknown operation: handled at a higher level
}
return $oResult;
}
/**
* Helper for object deletion
*/
public function DeleteObjects($oResult, $aObjects, $bSimulate)
{
$oDeletionPlan = new DeletionPlan();
foreach($aObjects as $oObj)
{
if ($bSimulate)
{
$oObj->CheckToDelete($oDeletionPlan);
}
else
{
$oObj->DBDelete($oDeletionPlan);
}
}
foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes)
{
foreach ($aDeletes as $iId => $aData)
{
$oToDelete = $aData['to_delete'];
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
if (array_key_exists('issue', $aData))
{
if ($bAutoDel)
{
if (isset($aData['requested_explicitely'])) // i.e. in the initial list of objects to delete
{
$iCode = RestDelete::ISSUE;
$sPlanned = 'Cannot be deleted: '.$aData['issue'];
}
else
{
$iCode = RestDelete::AUTO_DELETE_ISSUE;
$sPlanned = 'Should be deleted automatically... but: '.$aData['issue'];
}
}
else
{
$iCode = RestDelete::REQUEST_EXPLICITELY;
$sPlanned = 'Must be deleted explicitely... but: '.$aData['issue'];
}
}
else
{
if ($bAutoDel)
{
if (isset($aData['requested_explicitely']))
{
$iCode = RestDelete::OK;
$sPlanned = '';
}
else
{
$iCode = RestDelete::AUTO_DELETE;
$sPlanned = 'Deleted automatically';
}
}
else
{
$iCode = RestDelete::REQUEST_EXPLICITELY;
$sPlanned = 'Must be deleted explicitely';
}
}
$oResult->AddObject($iCode, $sPlanned, $oToDelete, array('id', 'friendlyname'));
}
}
foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate)
{
foreach ($aToUpdate as $iId => $aData)
{
$oToUpdate = $aData['to_reset'];
if (array_key_exists('issue', $aData))
{
$iCode = RestDelete::AUTO_UPDATE_ISSUE;
$sPlanned = 'Should be updated automatically... but: '.$aData['issue'];
}
else
{
$iCode = RestDelete::AUTO_UPDATE;
$sPlanned = 'Reset external keys: '.$aData['attributes_list'];
}
$oResult->AddObject($iCode, $sPlanned, $oToUpdate, array('id', 'friendlyname'));
}
}
if ($oDeletionPlan->FoundStopper())
{
if ($oDeletionPlan->FoundSecurityIssue())
{
$iRes = RestResult::UNAUTHORIZED;
$sRes = 'Deletion not allowed on some objects';
}
elseif ($oDeletionPlan->FoundManualOperation())
{
$iRes = RestResult::UNSAFE;
$sRes = 'The deletion requires that other objects be deleted/updated, and those operations must be requested explicitely';
}
else
{
$iRes = RestResult::INTERNAL_ERROR;
$sRes = 'Some issues have been encountered. See the list of planned changes for more information about the issue(s).';
}
}
else
{
$iRes = RestResult::OK;
$sRes = 'Deleted: '.count($aObjects);
$iIndirect = $oDeletionPlan->GetTargetCount() - count($aObjects);
if ($iIndirect > 0)
{
$sRes .= ' plus (for DB integrity) '.$iIndirect;
}
}
$oResult->code = $iRes;
if ($bSimulate)
{
$oResult->message = 'SIMULATING: '.$sRes;
}
else
{
$oResult->message = $sRes;
}
}
}

View File

@@ -130,6 +130,26 @@ $aOperations = array(
'key' => 'SELECT UserRequest',
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
),
array(
'operation' => 'core/delete', // operation code
'comment' => 'Cleanup for synchro with...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 'SELECT UserRequest WHERE org_id = 2',
'simulate' => true,
),
array(
'operation' => 'core/apply_stimulus', // operation code
'comment' => 'Synchronization from blah...', // comment recorded in the change tracking log
'class' => 'UserRequest',
'key' => 1,
'stimulus' => 'ev_assign',
// Values to set
'fields' => array(
'team_id' => 15, // Helpdesk
'agent_id' => 9 // Jules Verne
),
'output_fields' => 'id, friendlyname, title, contacts_list', // list of fields to show in the results (* or a,b,c)
),
);
$sUrl = "http://localhost/rest-services/webservices/rest.php?version=1.0";