Improved Web services: opened to services coming from an optional module

SVN:trunk[965]
This commit is contained in:
Romain Quetiez
2010-11-23 16:16:01 +00:00
parent 9dd2061f60
commit fcbd9b8911
13 changed files with 722 additions and 186 deletions

View File

@@ -70,6 +70,7 @@ class Config
protected $m_aAppModules;
protected $m_aDataModels;
protected $m_aWebServiceCategories;
protected $m_aAddons;
protected $m_aDictionaries;
@@ -281,6 +282,9 @@ class Config
'core/trigger.class.inc.php',
);
$this->m_aDataModels = array();
$this->m_aWebServiceCategories = array(
'webservices/webservices.basic.php',
);
$this->m_aAddons = array(
// Default AddOn, always present can be moved to an official iTop Module later if needed
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
@@ -405,6 +409,7 @@ class Config
}
$this->m_aAppModules = $MyModules['application'];
$this->m_aDataModels = $MyModules['business'];
$this->m_aWebServiceCategories = $MyModules['webservices'];
$this->m_aAddons = $MyModules['addons'];
$this->m_aDictionaries = $MyModules['dictionaries'];
@@ -489,6 +494,15 @@ class Config
$this->m_aDataModels = $aDataModels;
}
public function GetWebServiceCategories()
{
return $this->m_aWebServiceCategories;
}
public function SetWebServiceCategories($aWebServiceCategories)
{
$this->m_aWebServiceCategories = $aWebServiceCategories;
}
public function GetAddons()
{
return $this->m_aAddons;
@@ -856,6 +870,12 @@ class Config
fwrite($hFile, "\t\t'$sFile',\n");
}
fwrite($hFile, "\t),\n");
fwrite($hFile, "\t'webservices' => array (\n");
foreach($this->m_aWebServiceCategories as $sFile)
{
fwrite($hFile, "\t\t'$sFile',\n");
}
fwrite($hFile, "\t),\n");
fwrite($hFile, "\t'addons' => array (\n");
foreach($this->m_aAddons as $sKey => $sFile)
{

View File

@@ -158,6 +158,30 @@ class DBObjectSet
return $aRet;
}
public function ToArrayOfValues()
{
if (!$this->m_bLoaded) $this->Load();
$aRet = array();
foreach($this->m_aData as $iRow => $aObjects)
{
foreach($aObjects as $sClassAlias => $oObject)
{
$aRet[$iRow][$sClassAlias.'.'.'id'] = $oObject->GetKey();
$sClass = get_class($oObject);
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
if ($oAttDef->IsScalar())
{
$sAttName = $sClassAlias.'.'.$sAttCode;
$aRet[$iRow][$sAttName] = $oObject->Get($sAttCode);
}
}
}
}
return $aRet;
}
public function GetColumnAsArray($sAttCode, $bWithId = true)
{
$aRet = array();

View File

@@ -3299,6 +3299,10 @@ abstract class MetaModel
{
self::Plugin($sConfigFile, 'business', $sToInclude);
}
foreach (self::$m_oConfig->GetWebServiceCategories() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'webservice', $sToInclude);
}
foreach (self::$m_oConfig->GetAddons() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'addons', $sToInclude);

View File

@@ -668,23 +668,38 @@ function BuildConfig(SetupWebpage $oP, Config &$oConfig, $aParamValues)
$aAddOns = $oConfig->GetAddOns();
$aAppModules = $oConfig->GetAppModules();
$aDataModels = $oConfig->GetDataModels();
$aWebServiceCategories = $oConfig->GetWebServiceCategories();
$aDictionaries = $oConfig->GetDictionaries();
// Merge the values with the ones provided by the modules
// Make sure when don't load the same file twice...
foreach($aParamValues['module'] as $sModuleId)
{
$oP->log('Installed iTop module: '. $sModuleId);
$aDataModels = array_unique(array_merge($aDataModels, $aAvailableModules[$sModuleId]['datamodel']));
$aDictionaries = array_unique(array_merge($aDictionaries, $aAvailableModules[$sModuleId]['dictionary']));
foreach($aAvailableModules[$sModuleId]['settings'] as $sProperty => $value)
if (isset($aAvailableModules[$sModuleId]['datamodel']))
{
list($sName, $sVersion) = GetModuleName($sModuleId);
$oConfig->SetModuleSetting($sName, $sProperty, $value);
$aDataModels = array_unique(array_merge($aDataModels, $aAvailableModules[$sModuleId]['datamodel']));
}
if (isset($aAvailableModules[$sModuleId]['webservice']))
{
$aWebServiceCategories = array_unique(array_merge($aWebServiceCategories, $aAvailableModules[$sModuleId]['webservice']));
}
if (isset($aAvailableModules[$sModuleId]['dictionary']))
{
$aDictionaries = array_unique(array_merge($aDictionaries, $aAvailableModules[$sModuleId]['dictionary']));
}
if (isset($aAvailableModules[$sModuleId]['settings']))
{
foreach($aAvailableModules[$sModuleId]['settings'] as $sProperty => $value)
{
list($sName, $sVersion) = GetModuleName($sModuleId);
$oConfig->SetModuleSetting($sName, $sProperty, $value);
}
}
}
$oConfig->SetAddOns($aAddOns);
$oConfig->SetAppModules($aAppModules);
$oConfig->SetDataModels($aDataModels);
$oConfig->SetWebServiceCategories($aWebServiceCategories);
$oConfig->SetDictionaries($aDictionaries);
}

View File

@@ -259,7 +259,7 @@ table.formTable {
static $m_aModules = array();
// All the entries below are list of file paths relative to the module directory
static $m_aFilesList = array('datamodel', 'dictionary', 'data.struct', 'data.sample');
static $m_aFilesList = array('datamodel', 'webservice', 'dictionary', 'data.struct', 'data.sample');
static $m_sModulePath = null;
public function SetModulePath($sModulePath)
@@ -281,11 +281,14 @@ table.formTable {
foreach(self::$m_aFilesList as $sAttribute)
{
// All the items below are list of files, that are relative to the current file
// being loaded, let's update their path to store path relative to the application directory
foreach(self::$m_aModules[$sId][$sAttribute] as $idx => $sRelativePath)
if (isset(self::$m_aModules[$sId][$sAttribute]))
{
// All the items below are list of files, that are relative to the current file
// being loaded, let's update their path to store path relative to the application directory
foreach(self::$m_aModules[$sId][$sAttribute] as $idx => $sRelativePath)
{
self::$m_aModules[$sId][$sAttribute][$idx] = self::$m_sModulePath.'/'.$sRelativePath;
}
}
}
}

View File

@@ -1612,14 +1612,17 @@ class TestImportRESTMassive extends TestImportREST
// Test SOAP services
///////////////////////////////////////////////////////////////////////////
$aWebServices = array(
$aCreateTicketSpecs = array(
array(
'service_category' => 'BasicServices',
'verb' => 'GetVersion',
'expected result' => WebServices::GetVersion(),
// 'expected result' => '1.0.1',
'expected result' => '$ITOP_VERSION$ [dev]',
'explain result' => 'no comment!',
'args' => array(),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => true,
'explain result' => 'link attribute unknown + a CI not found',
@@ -1656,6 +1659,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => true,
'explain result' => 'caller not specified',
@@ -1682,6 +1686,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => false,
'explain result' => 'wrong class on CI to attach',
@@ -1708,6 +1713,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => false,
'explain result' => 'wrong search condition on CI to attach',
@@ -1734,6 +1740,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => true,
'explain result' => 'no CI to attach (empty array)',
@@ -1755,6 +1762,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => true,
'explain result' => 'no CI to attach (null)',
@@ -1775,6 +1783,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => true,
'explain result' => 'caller unknown',
@@ -1796,6 +1805,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => false,
'explain result' => 'wrong values for impact and urgency',
@@ -1817,6 +1827,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => false,
'explain result' => 'wrong password',
@@ -1838,6 +1849,7 @@ $aWebServices = array(
),
),
array(
'service_category' => '',
'verb' => 'CreateIncidentTicket',
'expected result' => false,
'explain result' => 'wrong login',
@@ -1861,41 +1873,145 @@ $aWebServices = array(
);
class TestSoap extends TestSoapWebService
$aManageCloudUsersSpecs = array(
array(
'service_category' => '',
'verb' => 'SearchObjects',
'expected result' => false,
'explain result' => 'wrong OQL',
'args' => array(
'admin', /* sLogin */
'admin', /* sPassword */
'SELECT ThisClassDoesNotExist', /* sOQL */
),
),
array(
'service_category' => '',
'verb' => 'SearchObjects',
'expected result' => true,
'explain result' => 'ok',
'args' => array(
'admin', /* sLogin */
'admin', /* sPassword */
'SELECT Organization', /* sOQL */
),
),
array(
'service_category' => 'CloudUsersManagementService',
'verb' => 'CreateAccount',
'expected result' => true,
'explain result' => 'ok',
'args' => array(
'admin', /* sAdminLogin */
'admin', /* sAdminPassword */
'andros@combodo.com', /* sLogin */
'André', /* sFirstName */
'Dupont', /* sLastName */
1, /* iOrgId */
'FR FR', /* sLanguage */
array(
array(
new SOAPKeyValue('profile_id', '2'),
new SOAPKeyValue('reason', 'whynot'),
),
array(
new SOAPKeyValue('profile_id', '3'),
new SOAPKeyValue('reason', 'because'),
),
), /* aProfiles (array of key/value pairs) */
array(
), /* aAllowedOrgs (array of key/value pairs) */
'comment on the creation operation', /* sComment */
),
),
array(
'service_category' => 'CloudUsersManagementService',
'verb' => 'ModifyAccount',
'expected result' => true,
'explain result' => 'ok',
'args' => array(
'admin', /* sAdminLogin */
'admin', /* sAdminPassword */
'andros@combodo.com', /* sLogin */
'nono', /* sFirstName */
'robot', /* sLastName */
2, /* iOrgId */
'EN US', /* sLanguage */
array(
array(
new SOAPKeyValue('profile_id', '3'),
new SOAPKeyValue('reason', 'because'),
),
), /* aProfiles (array of key/value pairs) */
array(
), /* aAllowedOrgs (array of key/value pairs) */
'comment on the modify operation', /* sComment */
),
),
array(
'service_category' => 'CloudUsersManagementService',
'verb' => 'DeleteAccount',
'expected result' => true,
'explain result' => '',
'args' => array(
'admin', /* sAdminLogin */
'admin', /* sAdminPassword */
'andros@combodo.com', /* sLogin */
'comment on the deletion operation', /* sComment */
),
),
array(
'service_category' => 'CloudUsersManagementService',
'verb' => 'DeleteAccount',
'expected result' => false,
'explain result' => 'wrong login',
'args' => array(
'admin', /* sAdminLogin */
'admin', /* sAdminPassword */
'taratatata@sdf.com', /* sLogin */
'comment on the deletion operation', /* sComment */
),
),
);
abstract class TestSoap extends TestSoapWebService
{
static public function GetName() {return 'Test SOAP';}
static public function GetDescription() {return 'Do basic stuff to test the SOAP capability';}
protected $m_aTestSpecs;
protected function DoExecute()
{
echo "<p>Note: You may also want to try the sample SOAP client <a href=\"../webservices/itopsoap.examples.php\">itopsoap.examples.php</a></p>\n";
global $aSOAPMapping;
$aSOAPMapping = SOAPMapping::GetMapping();
// this file is generated dynamically with location = here
$sWsdlUri = 'http'.(isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off') ? 's' : '').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php';
ini_set("soap.wsdl_cache_enabled","0");
$this->m_SoapClient = new SoapClient
(
$sWsdlUri,
array(
'classmap' => $aSOAPMapping,
'trace' => 1,
)
);
if (false)
{
self::DumpVariable($this->m_SoapClient->__getTypes());
}
global $aWebServices;
foreach ($aWebServices as $iPos => $aWebService)
foreach ($this->m_aTestSpecs as $iPos => $aWebService)
{
echo "<h2>SOAP call #$iPos - {$aWebService['verb']}</h2>\n";
echo "<p>{$aWebService['explain result']}</p>\n";
$sWsdlUriForService = $sWsdlUri.'?service_category='.$aWebService['service_category'];
$this->m_SoapClient = new SoapClient
(
$sWsdlUriForService,
array(
'classmap' => $aSOAPMapping,
'trace' => 1,
)
);
if (false)
{
self::DumpVariable($this->m_SoapClient->__getTypes());
}
try
{
$oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']);
@@ -1921,6 +2037,10 @@ class TestSoap extends TestSoapWebService
{
$res = $oRes->status;
}
elseif ($oRes instanceof SOAPSimpleResult)
{
$res = $oRes->status;
}
else
{
$res = $oRes;
@@ -1937,20 +2057,23 @@ class TestSoap extends TestSoapWebService
}
}
class TestWebServicesDirect extends TestBizModel
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 '/config-itop.php';}
protected $m_aTestSpecs;
protected function DoExecute()
{
$oWebServices = new WebServices();
global $aWebServices;
foreach ($aWebServices as $iPos => $aWebService)
foreach ($this->m_aTestSpecs as $iPos => $aWebService)
{
$sServiceClass = $aWebService['service_category'];
if (empty($sServiceClass)) $sServiceClass = 'BasicServices';
$oWebServices = new $sServiceClass();
echo "<h2>SOAP call #$iPos - {$aWebService['verb']}</h2>\n";
echo "<p>{$aWebService['explain result']}</p>\n";
$oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']);
@@ -1960,6 +2083,10 @@ class TestWebServicesDirect extends TestBizModel
{
$res = $oRes->status;
}
elseif ($oRes instanceof SOAPSimpleResult)
{
$res = $oRes->status;
}
else
{
$res = $oRes;
@@ -1977,6 +2104,59 @@ class TestWebServicesDirect extends TestBizModel
}
}
class TestSoap_Tickets extends TestSoap
{
static public function GetName() {return 'Test SOAP - create ticket';}
protected function DoExecute()
{
global $aCreateTicketSpecs;
$this->m_aTestSpecs = $aCreateTicketSpecs;
return parent::DoExecute();
}
}
class TestSoapDirect_Tickets extends TestSoapDirect
{
static public function GetName() {return 'Test SOAP without SOAP - create ticket';}
protected function DoExecute()
{
global $aCreateTicketSpecs;
$this->m_aTestSpecs = $aCreateTicketSpecs;
return parent::DoExecute();
}
}
class TestSoap_ManageCloudUsers extends TestSoap
{
static public function GetName() {return 'Test SOAP - manage Cloud Users';}
protected function DoExecute()
{
global $aManageCloudUsersSpecs;
$this->m_aTestSpecs = $aManageCloudUsersSpecs;
return parent::DoExecute();
}
}
class TestSoapDirect_ManageCloudUsers extends TestSoapDirect
{
static public function GetName() {return 'Test SOAP without SOAP - manage Cloud Users';}
protected function DoExecute()
{
global $aManageCloudUsersSpecs;
$this->m_aTestSpecs = $aManageCloudUsersSpecs;
return parent::DoExecute();
}
}
////////////////////// End of SOAP TESTS
class TestTriggerAndEmail extends TestBizModel
{
static public function GetName() {return 'Test trigger and email';}

View File

@@ -23,17 +23,53 @@
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
// This is to make sure that the client will accept it....
if (isset($_REQUEST['debug']))
{
if ($_REQUEST['debug'] == 'text')
{
header('Content-Type: text/plain; charset=UTF-8');
}
else
{
header('Content-Type: application/xml; charset=UTF-8');
}
}
else
{
// This is to make sure that the client will accept it....
//
header('Content-Type: application/xml; charset=UTF-8');
////header('Content-Disposition: attachment; filename="itop.wsdl"');
header('Content-Disposition: online; filename="itop.wsdl"');
}
require_once('../approot.inc.php');
require_once(APPROOT.'webservices/webservices.class.inc.php');
require_once(APPROOT.'core/config.class.inc.php');
// Load the modules installed and enabled
//
header('Content-Type: application/xml; charset=UTF-8');
//header('Content-Disposition: attachment; filename="itop.wsdl"');
header('Content-Disposition: online; filename="itop.wsdl"');
$oConfig = new Config(APPROOT.'config-itop.php');
$aFiles = $oConfig->GetWebServiceCategories();
foreach ($aFiles as $sFile)
{
require_once(APPROOT.$sFile);
}
$sMyWsdl = './itop.wsdl.tpl';
$sRawFile = file_get_contents($sMyWsdl);
if (isset($_REQUEST['service_category']) && (!empty($_REQUEST['service_category'])))
{
$sRawFile = WebServicesBase::GetWSDLContents($_REQUEST['service_category']);
}
else
{
$sRawFile = WebServicesBase::GetWSDLContents();
}
$sServerURI = 'http'.((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off')) ? 's' : '').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/soapserver.php';
if (isset($_REQUEST['service_category']) && (!empty($_REQUEST['service_category'])))
{
$sServerURI .= "?service_category=".$_REQUEST['service_category'];
}
$sFinalFile = str_replace(
'___SOAP_SERVER_URI___',

View File

@@ -161,6 +161,14 @@
<message name="CreateIncidentTicketResponse">
<part name="CreateIncidentTicketReturn" type="typens:Result"/>
</message>
<message name="SearchObjects">
<part name="login" type="xsd:string"/>
<part name="password" type="xsd:string"/>
<part name="oql" type="xsd:string"/>
</message>
<message name="SearchObjectsResponse">
<part name="SearchObjectsReturn" type="typens:Result"/>
</message>
<portType name="WebServicePortType">
<operation name="GetVersion">
<wsdl:documentation>
@@ -177,6 +185,13 @@
<input message="typens:CreateIncidentTicket"/>
<output message="typens:CreateIncidentTicketResponse"/>
</operation>
<operation name="SearchObjects">
<wsdl:documentation>
Create a ticket, return information about reconciliation on external keys and the created ticket
</wsdl:documentation> -->
<input message="typens:SearchObjects"/>
<output message="typens:SearchObjectsResponse"/>
</operation>
</portType>
<binding name="WebServiceBinding" type="typens:WebServicePortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
@@ -198,6 +213,15 @@
<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
<operation name="SearchObjects">
<soap:operation soapAction="urn:WebServiceAction"/>
<input>
<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body namespace="urn:ITop" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="ITopService">
<wsdl:documentation>

View File

@@ -28,6 +28,9 @@ require_once('itopsoaptypes.class.inc.php');
$sItopRoot = 'http'.((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off')) ? 's' : '').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/..';
$sWsdlUri = $sItopRoot.'/webservices/itop.wsdl.php';
//$sWsdlUri .= '?service_category=';
$aSOAPMapping = SOAPMapping::GetMapping();
ini_set("soap.wsdl_cache_enabled","0");
$oSoapClient = new SoapClient(
@@ -81,6 +84,54 @@ try
print_r($oRes);
echo "</pre>\n";
echo "</p>\n";
$oRes = $oSoapClient->SearchObjects
(
'admin', /* login */
'admin', /* password */
'SELECT URP_Profiles' /* oql */
);
echo "<p>SearchObjects() returned:\n";
if ($oRes->status)
{
$aResults = $oRes->result;
echo "<table>\n";
// Header made after the first line
echo "<tr>\n";
foreach ($aResults[0]->values as $aKeyValuePair)
{
echo " <th>".$aKeyValuePair->key."</th>\n";
}
echo "</tr>\n";
foreach ($aResults as $iRow => $aData)
{
echo "<tr>\n";
foreach ($aData->values as $aKeyValuePair)
{
echo " <td>".$aKeyValuePair->value."</td>\n";
}
echo "</tr>\n";
}
echo "</table>\n";
}
else
{
$aErrors = array();
foreach ($oRes->errors->messages as $oMessage)
{
$aErrors[] = $oMessage->text;
}
$sErrorMsg = implode(', ', $aErrors);
echo "<p>SearchObjects() failed with message: $sErrorMsg</p>\n";
//echo "<pre>\n";
//print_r($oRes);
//echo "</pre>\n";
}
echo "</p>\n";
}
catch(SoapFault $e)
{

View File

@@ -107,7 +107,7 @@ class SOAPResultLog
}
class SOAPResultData
class SOAPKeyValue
{
public $key; // string
public $value; // string
@@ -119,11 +119,10 @@ class SOAPResultData
}
}
class SOAPResultMessage
{
public $label; // string
public $values; // array of SOAPResultData
public $values; // array of SOAPKeyValue
public function __construct($sLabel, $aValues)
{
@@ -151,17 +150,38 @@ class SOAPResult
}
}
$aSOAPMapping = array(
'SearchCondition' => 'SOAPSearchCondition',
'ExternalKeySearch' => 'SOAPExternalKeySearch',
'AttributeValue' => 'SOAPAttributeValue',
'LinkCreationSpec' => 'SOAPLinkCreationSpec',
'LogMessage' => 'SOAPLogMessage',
'ResultLog' => 'SOAPResultLog',
'ResultData' => 'SOAPResultData',
'ResultMessage' => 'SOAPResultMessage',
'Result' => 'SOAPResult',
);
class SOAPSimpleResult
{
public $status; // boolean
public $message; // string
public function __construct($bStatus, $sMessage)
{
$this->status = $bStatus;
$this->message = $sMessage;
}
}
class SOAPMapping
{
static function GetMapping()
{
$aSOAPMapping = array(
'SearchCondition' => 'SOAPSearchCondition',
'ExternalKeySearch' => 'SOAPExternalKeySearch',
'AttributeValue' => 'SOAPAttributeValue',
'LinkCreationSpec' => 'SOAPLinkCreationSpec',
'KeyValue' => 'SOAPKeyValue',
'LogMessage' => 'SOAPLogMessage',
'ResultLog' => 'SOAPResultLog',
'ResultData' => 'SOAPKeyValue',
'ResultMessage' => 'SOAPResultMessage',
'Result' => 'SOAPResult',
'SimpleResult' => 'SOAPSimpleResult',
);
return $aSOAPMapping;
}
}
?>

View File

@@ -30,14 +30,17 @@ require_once('../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/startup.inc.php');
require('./webservices.class.inc.php');
// this file is generated dynamically with location = here
$sWsdlUri = 'http'.((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off')) ? 's' : '').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php';
if (isset($_REQUEST['service_category']) && (!empty($_REQUEST['service_category'])))
{
$sWsdlUri .= "?service_category=".$_REQUEST['service_category'];
}
ini_set("soap.wsdl_cache_enabled","0");
$aSOAPMapping = SOAPMapping::GetMapping();
$oSoapServer = new SoapServer
(
$sWsdlUri,
@@ -46,7 +49,28 @@ $oSoapServer = new SoapServer
)
);
// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION);
$oSoapServer->setClass('WebServices', null);
if (isset($_REQUEST['service_category']) && (!empty($_REQUEST['service_category'])))
{
$sServiceClass = $_REQUEST['service_category'];
if (!class_exists($sServiceClass))
{
// not a valid class name (not a PHP class at all)
throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not a PHP class");
}
elseif (!is_subclass_of($sServiceClass, 'WebServicesBase'))
{
// not a valid class name (not deriving from WebServicesBase)
throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '$sServiceClass' is not derived from WebServicesBase");
}
else
{
$oSoapServer->setClass($sServiceClass, null);
}
}
else
{
$oSoapServer->setClass('BasicServices', null);
}
if ($_SERVER["REQUEST_METHOD"] == "POST")
{
@@ -59,9 +83,25 @@ else
echo "<ul>\n";
foreach($aFunctions as $sFunc)
{
if ($sFunc == 'GetWSDLContents') continue;
echo "<li>$sFunc</li>\n";
}
echo "</ul>\n";
echo "<p>Here the <a href=\"$sWsdlUri\">WSDL file</a><p>";
echo "You may also want to try the following service categories: ";
echo "<ul>\n";
foreach(get_declared_classes() as $sPHPClass)
{
if (is_subclass_of($sPHPClass, 'WebServicesBase'))
{
$sServiceCategory = $sPHPClass;
$sSoapServerUri = 'http'.((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off')) ? 's' : '').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/soapserver.php';
$sSoapServerUri .= "?service_category=$sServiceCategory";
echo "<li><a href=\"$sSoapServerUri\">$sServiceCategory</a></li>\n";
}
}
echo "</ul>\n";
}
?>

View File

@@ -0,0 +1,215 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Implementation of iTop SOAP services
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/webservices/webservices.class.inc.php');
class BasicServices extends WebServicesBase
{
static protected function GetWSDLFilePath()
{
return APPROOT.'/webservices/itop.wsdl.tpl';
}
/**
* Get the server version (TODO: get it dynamically, where ?)
*
* @return WebServiceResult
*/
static public function GetVersion()
{
if (ITOP_REVISION == '$WCREV$')
{
$sVersionString = ITOP_VERSION.' [dev]';
}
else
{
// This is a build made from SVN, let display the full information
$sVersionString = ITOP_VERSION."-".ITOP_REVISION." ".ITOP_BUILD_DATE;
}
return $sVersionString;
}
public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
{
if (!UserRights::CheckCredentials($sLogin, $sPassword))
{
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
return $oRes->ToSoapStructure();
}
UserRights::Login($sLogin);
$aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
$aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
$aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
$aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
$aImpactedCIs = array();
if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
foreach($aSOAPImpactedCIs as $oImpactedCIs)
{
$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
}
$oRes = $this->_CreateIncidentTicket
(
$sTitle,
$sDescription,
$aCallerDesc,
$aCustomerDesc,
$aServiceDesc,
$aServiceSubcategoryDesc,
$sProduct,
$aWorkgroupDesc,
$aImpactedCIs,
$sImpact,
$sUrgency
);
return $oRes->ToSoapStructure();
}
/**
* Create an incident ticket from a monitoring system
* Some CIs might be specified (by their name/IP)
*
* @param string sTitle
* @param string sDescription
* @param array aCallerDesc
* @param array aCustomerDesc
* @param array aServiceDesc
* @param array aServiceSubcategoryDesc
* @param string sProduct
* @param array aWorkgroupDesc
* @param array aImpactedCIs
* @param string sImpact
* @param string sUrgency
*
* @return WebServiceResult
*/
protected function _CreateIncidentTicket($sTitle, $sDescription, $aCallerDesc, $aCustomerDesc, $aServiceDesc, $aServiceSubcategoryDesc, $sProduct, $aWorkgroupDesc, $aImpactedCIs, $sImpact, $sUrgency)
{
$oRes = new WebServiceResult();
try
{
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$oMyChange->Set("userinfo", "Administrator");
$iChangeId = $oMyChange->DBInsertNoReload();
$oNewTicket = MetaModel::NewObject('Incident');
$this->MyObjectSetScalar('title', 'title', $sTitle, $oNewTicket, $oRes);
$this->MyObjectSetScalar('description', 'description', $sDescription, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
$this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
$aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
if (count($aDevicesNotFound) > 0)
{
$this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
}
else
{
$this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
}
$this->MyObjectSetScalar('impact', 'impact', $sImpact, $oNewTicket, $oRes);
$this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
$this->MyObjectInsert($oNewTicket, 'created', $oMyChange, $oRes);
}
catch (CoreException $e)
{
$oRes->LogError($e->getMessage());
}
catch (Exception $e)
{
$oRes->LogError($e->getMessage());
}
$this->LogUsage(__FUNCTION__, $oRes);
return $oRes;
}
/**
* Given an OQL, returns a set of objects (several objects could be on the same row)
*
* @param string sOQL
*/
public function SearchObjects($sLogin, $sPassword, $sOQL)
{
if (!UserRights::CheckCredentials($sLogin, $sPassword))
{
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
return $oRes->ToSoapStructure();
}
UserRights::Login($sLogin);
$oRes = $this->_SearchObjects($sOQL);
return $oRes->ToSoapStructure();
}
protected function _SearchObjects($sOQL)
{
$oRes = new WebServiceResult();
try
{
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch);
$aData = $oSet->ToArrayOfValues();
foreach($aData as $iRow => $aRow)
{
$oRes->AddResultRow("row_$iRow", $aRow);
}
}
catch (CoreException $e)
{
$oRes->LogError($e->getMessage());
}
catch (Exception $e)
{
$oRes->LogError($e->getMessage());
}
$this->LogUsage(__FUNCTION__, $oRes);
return $oRes;
}
}
?>

View File

@@ -84,7 +84,7 @@ class WebServiceResult
$aValues = array();
foreach($aData as $sKey => $value)
{
$aValues[] = new SoapResultData($sKey, $value);
$aValues[] = new SOAPKeyValue($sKey, $value);
}
$aResults[] = new SoapResultMessage($sLabel, $aValues);
}
@@ -140,6 +140,17 @@ class WebServiceResult
);
}
/**
* Add result details - a table row
*
* @param string sLabel
* @param object oObject
*/
public function AddResultRow($sLabel, $aRow)
{
$this->m_aResult[$sLabel] = $aRow;
}
/**
* Log an error
*
@@ -236,8 +247,18 @@ class WebServiceResultFailedLogin extends WebServiceResult
*
* @package iTopORM
*/
class WebServices
abstract class WebServicesBase
{
static public function GetWSDLContents($sServiceCategory = '')
{
if ($sServiceCategory == '')
{
$sServiceCategory = 'BasicServices';
}
$sWsdlFilePath = call_user_func(array($sServiceCategory, 'GetWSDLFilePath'));
return file_get_contents($sWsdlFilePath);
}
/**
* Helper to log a service delivery
*
@@ -537,138 +558,21 @@ class WebServices
return $aRes;
}
/**
* Get the server version (TODO: get it dynamically, where ?)
*
* @return WebServiceResult
*/
static public function GetVersion()
static protected function SoapStructToAssociativeArray($aArrayOfAssocArray)
{
if (ITOP_REVISION == '$WCREV$')
if (is_null($aArrayOfAssocArray)) return array();
$aRes = array();
foreach($aArrayOfAssocArray as $aAssocArray)
{
$sVersionString = ITOP_VERSION.' [dev]';
}
else
{
// This is a build made from SVN, let display the full information
$sVersionString = ITOP_VERSION."-".ITOP_REVISION." ".ITOP_BUILD_DATE;
}
return $sVersionString;
}
public function CreateIncidentTicket($sLogin, $sPassword, $sTitle, $sDescription, $oCallerDesc, $oCustomerDesc, $oServiceDesc, $oServiceSubcategoryDesc, $sProduct, $oWorkgroupDesc, $aSOAPImpactedCIs, $sImpact, $sUrgency)
{
if (!UserRights::CheckCredentials($sLogin, $sPassword))
{
$oRes = new WebServiceResultFailedLogin($sLogin);
$this->LogUsage(__FUNCTION__, $oRes);
return $oRes->ToSoapStructure();
}
UserRights::Login($sLogin);
$aCallerDesc = self::SoapStructToExternalKeySearch($oCallerDesc);
$aCustomerDesc = self::SoapStructToExternalKeySearch($oCustomerDesc);
$aServiceDesc = self::SoapStructToExternalKeySearch($oServiceDesc);
$aServiceSubcategoryDesc = self::SoapStructToExternalKeySearch($oServiceSubcategoryDesc);
$aWorkgroupDesc = self::SoapStructToExternalKeySearch($oWorkgroupDesc);
$aImpactedCIs = array();
if (is_null($aSOAPImpactedCIs)) $aSOAPImpactedCIs = array();
foreach($aSOAPImpactedCIs as $oImpactedCIs)
{
$aImpactedCIs[] = self::SoapStructToLinkCreationSpec($oImpactedCIs);
}
$oRes = $this->_CreateIncidentTicket
(
$sTitle,
$sDescription,
$aCallerDesc,
$aCustomerDesc,
$aServiceDesc,
$aServiceSubcategoryDesc,
$sProduct,
$aWorkgroupDesc,
$aImpactedCIs,
$sImpact,
$sUrgency
);
return $oRes->ToSoapStructure();
}
/**
* Create an incident ticket from a monitoring system
* Some CIs might be specified (by their name/IP)
*
* @param string sTitle
* @param string sDescription
* @param array aCallerDesc
* @param array aCustomerDesc
* @param array aServiceDesc
* @param array aServiceSubcategoryDesc
* @param string sProduct
* @param array aWorkgroupDesc
* @param array aImpactedCIs
* @param string sImpact
* @param string sUrgency
*
* @return WebServiceResult
*/
protected function _CreateIncidentTicket($sTitle, $sDescription, $aCallerDesc, $aCustomerDesc, $aServiceDesc, $aServiceSubcategoryDesc, $sProduct, $aWorkgroupDesc, $aImpactedCIs, $sImpact, $sUrgency)
{
$oRes = new WebServiceResult();
try
{
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$oMyChange->Set("userinfo", "Administrator");
$iChangeId = $oMyChange->DBInsertNoReload();
$oNewTicket = MetaModel::NewObject('Incident');
$this->MyObjectSetScalar('title', 'title', $sTitle, $oNewTicket, $oRes);
$this->MyObjectSetScalar('description', 'description', $sDescription, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('service_id', 'service', $aServiceDesc, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('servicesubcategory_id', 'servicesubcategory', $aServiceSubcategoryDesc, $oNewTicket, $oRes);
$this->MyObjectSetScalar('product', 'product', $sProduct, $oNewTicket, $oRes);
$this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes);
$aDevicesNotFound = $this->AddLinkedObjects('ci_list', 'impacted_cis', 'FunctionalCI', $aImpactedCIs, $oNewTicket, $oRes);
if (count($aDevicesNotFound) > 0)
$aRow = array();
foreach ($aAssocArray as $oKeyValuePair)
{
$this->MyObjectSetScalar('description', 'n/a', $sDescription.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes);
$aRow[$oKeyValuePair->key] = $oKeyValuePair->value;
}
else
{
$this->MyObjectSetScalar('description', 'n/a', $sDescription, $oNewTicket, $oRes);
}
$this->MyObjectSetScalar('impact', 'impact', $sImpact, $oNewTicket, $oRes);
$this->MyObjectSetScalar('urgency', 'urgency', $sUrgency, $oNewTicket, $oRes);
$this->MyObjectInsert($oNewTicket, 'created', $oMyChange, $oRes);
$aRes[] = $aRow;
}
catch (CoreException $e)
{
$oRes->LogError($e->getMessage());
}
catch (Exception $e)
{
$oRes->LogError($e->getMessage());
}
$this->LogUsage(__FUNCTION__, $oRes);
return $oRes;
return $aRes;
}
}
?>