From fcbd9b8911f788906e4fa9e39b940e7530e9edec Mon Sep 17 00:00:00 2001
From: Romain Quetiez
Date: Tue, 23 Nov 2010 16:16:01 +0000
Subject: [PATCH] Improved Web services: opened to services coming from an
optional module
SVN:trunk[965]
---
core/config.class.inc.php | 20 +++
core/dbobjectset.class.php | 24 +++
core/metamodel.class.php | 4 +
setup/index.php | 25 ++-
setup/setuppage.class.inc.php | 11 +-
test/testlist.inc.php | 228 +++++++++++++++++++++---
webservices/itop.wsdl.php | 50 +++++-
webservices/itop.wsdl.tpl | 24 +++
webservices/itopsoap.examples.php | 51 ++++++
webservices/itopsoaptypes.class.inc.php | 48 +++--
webservices/soapserver.php | 46 ++++-
webservices/webservices.basic.php | 215 ++++++++++++++++++++++
webservices/webservices.class.inc.php | 162 ++++-------------
13 files changed, 722 insertions(+), 186 deletions(-)
create mode 100644 webservices/webservices.basic.php
diff --git a/core/config.class.inc.php b/core/config.class.inc.php
index 4a113be59..5b04fe61d 100644
--- a/core/config.class.inc.php
+++ b/core/config.class.inc.php
@@ -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)
{
diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php
index 979826c5b..a55e1172f 100644
--- a/core/dbobjectset.class.php
+++ b/core/dbobjectset.class.php
@@ -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();
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index c266b0d64..26c8f6089 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -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);
diff --git a/setup/index.php b/setup/index.php
index 226238472..152dcc05c 100644
--- a/setup/index.php
+++ b/setup/index.php
@@ -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);
}
diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php
index 7834c5549..3e62f2764 100644
--- a/setup/setuppage.class.inc.php
+++ b/setup/setuppage.class.inc.php
@@ -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;
+ }
}
}
}
diff --git a/test/testlist.inc.php b/test/testlist.inc.php
index d54682ec1..a15ac5992 100644
--- a/test/testlist.inc.php
+++ b/test/testlist.inc.php
@@ -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 "Note: You may also want to try the sample SOAP client itopsoap.examples.php
\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 "SOAP call #$iPos - {$aWebService['verb']}
\n";
echo "{$aWebService['explain result']}
\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 "SOAP call #$iPos - {$aWebService['verb']}
\n";
echo "{$aWebService['explain result']}
\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';}
diff --git a/webservices/itop.wsdl.php b/webservices/itop.wsdl.php
index d36e227e3..aa71bb405 100644
--- a/webservices/itop.wsdl.php
+++ b/webservices/itop.wsdl.php
@@ -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___',
diff --git a/webservices/itop.wsdl.tpl b/webservices/itop.wsdl.tpl
index f4a2b4051..8d6ddf6ca 100644
--- a/webservices/itop.wsdl.tpl
+++ b/webservices/itop.wsdl.tpl
@@ -161,6 +161,14 @@
+
+
+
+
+
+
+
+
@@ -177,6 +185,13 @@
+
+
+ Create a ticket, return information about reconciliation on external keys and the created ticket
+ -->
+
+
+
@@ -198,6 +213,15 @@
+
+
+
+
+
+
+
diff --git a/webservices/itopsoap.examples.php b/webservices/itopsoap.examples.php
index 402ac0f27..08e191e35 100644
--- a/webservices/itopsoap.examples.php
+++ b/webservices/itopsoap.examples.php
@@ -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 "\n";
echo "
\n";
+
+ $oRes = $oSoapClient->SearchObjects
+ (
+ 'admin', /* login */
+ 'admin', /* password */
+ 'SELECT URP_Profiles' /* oql */
+ );
+
+ echo "SearchObjects() returned:\n";
+ if ($oRes->status)
+ {
+ $aResults = $oRes->result;
+
+ echo "
\n";
+
+ // Header made after the first line
+ echo "\n";
+ foreach ($aResults[0]->values as $aKeyValuePair)
+ {
+ echo " | ".$aKeyValuePair->key." | \n";
+ }
+ echo "
\n";
+
+ foreach ($aResults as $iRow => $aData)
+ {
+ echo "\n";
+ foreach ($aData->values as $aKeyValuePair)
+ {
+ echo " | ".$aKeyValuePair->value." | \n";
+ }
+ echo "
\n";
+ }
+ echo "
\n";
+ }
+ else
+ {
+ $aErrors = array();
+ foreach ($oRes->errors->messages as $oMessage)
+ {
+ $aErrors[] = $oMessage->text;
+ }
+ $sErrorMsg = implode(', ', $aErrors);
+ echo "SearchObjects() failed with message: $sErrorMsg
\n";
+ //echo "\n";
+ //print_r($oRes);
+ //echo "
\n";
+ }
+ echo "\n";
}
catch(SoapFault $e)
{
diff --git a/webservices/itopsoaptypes.class.inc.php b/webservices/itopsoaptypes.class.inc.php
index c55a8283f..5811236a3 100644
--- a/webservices/itopsoaptypes.class.inc.php
+++ b/webservices/itopsoaptypes.class.inc.php
@@ -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;
+ }
+}
?>
diff --git a/webservices/soapserver.php b/webservices/soapserver.php
index fb7515924..28d67c794 100644
--- a/webservices/soapserver.php
+++ b/webservices/soapserver.php
@@ -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 "\n";
foreach($aFunctions as $sFunc)
{
+ if ($sFunc == 'GetWSDLContents') continue;
+
echo "- $sFunc
\n";
}
echo "
\n";
echo "Here the WSDL file
";
+
+ echo "You may also want to try the following service categories: ";
+ echo "
\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 "- $sServiceCategory
\n";
+ }
+ }
+ echo "
\n";
}
?>
diff --git a/webservices/webservices.basic.php b/webservices/webservices.basic.php
new file mode 100644
index 000000000..c40e09c4d
--- /dev/null
+++ b/webservices/webservices.basic.php
@@ -0,0 +1,215 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @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;
+ }
+}
+?>
diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php
index 8575b33b5..e55b72b94 100644
--- a/webservices/webservices.class.inc.php
+++ b/webservices/webservices.class.inc.php
@@ -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;
}
}
?>