From ba01ac715fc209f3d96032dbedeabe51b6fb081e Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 25 Nov 2020 18:32:54 +0100 Subject: [PATCH 1/4] =?UTF-8?q?N=C2=B03455=20-=20Passing=20json=5Fdata=20a?= =?UTF-8?q?s=20file=20to=20REST=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/webservices/RestTest.php | 169 ++++++++++++++++++++++++++++++++++ webservices/rest.php | 18 +++- 2 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 test/webservices/RestTest.php diff --git a/test/webservices/RestTest.php b/test/webservices/RestTest.php new file mode 100644 index 000000000..0adc3525d --- /dev/null +++ b/test/webservices/RestTest.php @@ -0,0 +1,169 @@ +sTmpFile)){ + unlink($this->sTmpFile); + } + } + + private function GetTicketViaRest($iId){ + $sJsonGetContent = <<CallRestApi($sJsonGetContent); + } + + private function UpdateTicketViaApi($iId, $description){ + $sJsonUpdateContent = <<CallRestApi($sJsonUpdateContent); + } + + private function CreateTicketViaApi($description){ + $sJsonCreateContent = <<CallRestApi($sJsonCreateContent); + } + + private function DeleteTicketFromApi($iId){ + $sJson = <<CallRestApi($sJson); + + } + + public function BasicProvider(){ + return [ + 'call rest call' => [ 'bCallApiViaFile' => false], + 'pass json_data as file' => [ 'bCallApiViaFile' => true] + ]; + } + + /** + * @dataProvider BasicProvider + */ + public function testBasic($bCallApiViaFile) + { + $this->bCallApiViaFile = $bCallApiViaFile; + + //create ticket + $description = date('dmY H:i:s'); + + $sOuputJson = $this->CreateTicketViaApi($description); + $aJson = json_decode($sOuputJson, true); + $this->assertContains("0", "".$aJson['code']); + $sUserRequestKey = array_key_first($aJson['objects']); + $this->assertContains('UserRequest::', $sUserRequestKey); + $iId = $aJson['objects'][$sUserRequestKey]['key']; + $sExpectedJsonOuput=<<assertEquals($sExpectedJsonOuput, $sOuputJson); + + $sExpectedJsonOuput=<<$description<\/p>"}}},"code":0,"message":"Found: 1"} +JSON; + $this->assertEquals($sExpectedJsonOuput, $this->GetTicketViaRest($iId)); + + //update ticket + $description = date('Ymd H:i:s'); + $sExpectedJsonOuput=<<$description<\/p>"}}},"code":0,"message":null} +JSON; + $this->assertEquals($sExpectedJsonOuput, $this->UpdateTicketViaApi($iId, $description)); + + //delete ticket + $sExpectedJsonOuput=<<assertContains($sExpectedJsonOuput, $this->DeleteTicketFromApi($iId)); + + $sExpectedJsonOuput=<<assertEquals($sExpectedJsonOuput, $this->GetTicketViaRest($iId)); + } + + private function CallRestApi($sJsonDataContent){ + $ch = curl_init(); + $aPostFields = [ + 'version' => '1.3', + 'auth_user' => 'admin', + 'auth_pwd' => 'admin', + ]; + + if ($this->bCallApiViaFile){ + $this->sTmpFile = tempnam(sys_get_temp_dir(), 'jsondata_'); + file_put_contents($this->sTmpFile, $sJsonDataContent); + + $oCurlFile = curl_file_create($this->sTmpFile); + $aPostFields['json_data'] = $oCurlFile; + }else{ + $aPostFields['json_data'] = $sJsonDataContent; + } + + curl_setopt($ch, CURLOPT_URL, "http://localhost/iTop/webservices/rest.php"); + curl_setopt($ch, CURLOPT_POST, 1);// set post data to true + curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $sJson = curl_exec($ch); + curl_close ($ch); + + return $sJson; + } + +} diff --git a/webservices/rest.php b/webservices/rest.php index 647a035f2..32cc6ac77 100644 --- a/webservices/rest.php +++ b/webservices/rest.php @@ -70,7 +70,21 @@ $oCtx = new ContextTag(ContextTag::TAG_REST); $sVersion = utils::ReadParam('version', null, false, 'raw_data'); $sOperation = utils::ReadParam('operation', null); -$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data'); +if(isset($_FILES['json_data']['name']) && !empty($_FILES['json_data']['name'])) +{ + $sTmpFilePath = $_FILES['json_data']['tmp_name']; + if (is_file($sTmpFilePath)){ + $sValue = file_get_contents($sTmpFilePath); + unlink($sTmpFilePath); + if (! empty($sValue)){ + $sJsonString = utils::Sanitize($sValue, null, 'raw_data'); + } + } +} +if (empty($sJsonString)){ + $sJsonString = utils::ReadParam('json_data', null, false, 'raw_data'); +} + $sProvider = ''; $oKPI = new ExecutionKPI(); @@ -125,7 +139,7 @@ try { throw new Exception("Missing parameter 'version' (e.g. '1.0')", RestResult::MISSING_VERSION); } - + if ($sJsonString == null) { throw new Exception("Missing parameter 'json_data'", RestResult::MISSING_JSON); From 7f0e8abc09060c373ee02c24d066bce1e2ea2406 Mon Sep 17 00:00:00 2001 From: odain Date: Fri, 27 Nov 2020 16:33:03 +0100 Subject: [PATCH 2/4] =?UTF-8?q?N=C2=B03455:=20clean=20code=20+=20test=20af?= =?UTF-8?q?ter=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/webservices/RestTest.php | 126 +++++++++++++++++----------------- webservices/rest.php | 23 ++++--- 2 files changed, 75 insertions(+), 74 deletions(-) diff --git a/test/webservices/RestTest.php b/test/webservices/RestTest.php index 0adc3525d..193749f6b 100644 --- a/test/webservices/RestTest.php +++ b/test/webservices/RestTest.php @@ -31,68 +31,6 @@ class RestTest extends ItopDataTestCase } } - private function GetTicketViaRest($iId){ - $sJsonGetContent = <<CallRestApi($sJsonGetContent); - } - - private function UpdateTicketViaApi($iId, $description){ - $sJsonUpdateContent = <<CallRestApi($sJsonUpdateContent); - } - - private function CreateTicketViaApi($description){ - $sJsonCreateContent = <<CallRestApi($sJsonCreateContent); - } - - private function DeleteTicketFromApi($iId){ - $sJson = <<CallRestApi($sJson); - - } - - public function BasicProvider(){ - return [ - 'call rest call' => [ 'bCallApiViaFile' => false], - 'pass json_data as file' => [ 'bCallApiViaFile' => true] - ]; - } - /** * @dataProvider BasicProvider */ @@ -138,7 +76,69 @@ JSON; $this->assertEquals($sExpectedJsonOuput, $this->GetTicketViaRest($iId)); } - private function CallRestApi($sJsonDataContent){ + private function GetTicketViaRest($iId){ + $sJsonGetContent = <<CallRestApi($sJsonGetContent); + } + + public function BasicProvider(){ + return [ + 'call rest call' => [ 'bCallApiViaFile' => false], + 'pass json_data as file' => [ 'bCallApiViaFile' => true] + ]; + } + + private function UpdateTicketViaApi($iId, $description){ + $sJsonUpdateContent = <<CallRestApi($sJsonUpdateContent); + } + + private function CreateTicketViaApi($description){ + $sJsonCreateContent = <<CallRestApi($sJsonCreateContent); + } + + private function DeleteTicketFromApi($iId){ + $sJson = <<CallRestApi($sJson); + + } + + private function CallRestApi($sJsonDataContent){ $ch = curl_init(); $aPostFields = [ 'version' => '1.3', diff --git a/webservices/rest.php b/webservices/rest.php index 32cc6ac77..bc5f20e29 100644 --- a/webservices/rest.php +++ b/webservices/rest.php @@ -70,20 +70,21 @@ $oCtx = new ContextTag(ContextTag::TAG_REST); $sVersion = utils::ReadParam('version', null, false, 'raw_data'); $sOperation = utils::ReadParam('operation', null); -if(isset($_FILES['json_data']['name']) && !empty($_FILES['json_data']['name'])) -{ - $sTmpFilePath = $_FILES['json_data']['tmp_name']; - if (is_file($sTmpFilePath)){ - $sValue = file_get_contents($sTmpFilePath); - unlink($sTmpFilePath); - if (! empty($sValue)){ - $sJsonString = utils::Sanitize($sValue, null, 'raw_data'); +$sJsonString = utils::ReadParam('json_data', null, false, 'raw_data'); + +if (empty($sJsonString)){ + if(isset($_FILES['json_data']['tmp_name'])) + { + $sTmpFilePath = $_FILES['json_data']['tmp_name']; + if (is_file($sTmpFilePath)){ + $sValue = file_get_contents($sTmpFilePath); + unlink($sTmpFilePath); + if (! empty($sValue)){ + $sJsonString = utils::Sanitize($sValue, null, 'raw_data'); + } } } } -if (empty($sJsonString)){ - $sJsonString = utils::ReadParam('json_data', null, false, 'raw_data'); -} $sProvider = ''; From 59e9cdbfe610e2554cbf9d67e933a67b434962b4 Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 14 Dec 2020 17:07:15 +0100 Subject: [PATCH 3/4] =?UTF-8?q?N=C2=B03455:=20review=20with=20Romain=20(ne?= =?UTF-8?q?w=20code=20documented)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webservices/rest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webservices/rest.php b/webservices/rest.php index bc5f20e29..fd7e87523 100644 --- a/webservices/rest.php +++ b/webservices/rest.php @@ -70,9 +70,12 @@ $oCtx = new ContextTag(ContextTag::TAG_REST); $sVersion = utils::ReadParam('version', null, false, 'raw_data'); $sOperation = utils::ReadParam('operation', null); + +//read json_data parameter via as a string (standard behaviour) $sJsonString = utils::ReadParam('json_data', null, false, 'raw_data'); if (empty($sJsonString)){ + //N °3455: read json_data parameter via a file passed by http protocol if(isset($_FILES['json_data']['tmp_name'])) { $sTmpFilePath = $_FILES['json_data']['tmp_name']; From c6816318a9b36955c43a46585bcdf688342c4658 Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 14 Dec 2020 23:19:54 +0100 Subject: [PATCH 4/4] =?UTF-8?q?N=C2=B03455:=20test=20when=20no=20json=5Fda?= =?UTF-8?q?ta=20is=20passed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/webservices/RestTest.php | 169 ++++++++++++++++++++++++++++++---- 1 file changed, 153 insertions(+), 16 deletions(-) diff --git a/test/webservices/RestTest.php b/test/webservices/RestTest.php index 193749f6b..d9e98f8a7 100644 --- a/test/webservices/RestTest.php +++ b/test/webservices/RestTest.php @@ -13,11 +13,16 @@ use Exception; */ class RestTest extends ItopDataTestCase { - const CREATE_TEST_ORG = true; - const USE_TRANSACTION = true; + const USE_TRANSACTION = false; + + const MODE = [ 'JSONDATA_AS_STRING' => 0, 'JSONDATA_AS_FILE' => 1 , 'NO_JSONDATA' => 2 ]; private $sTmpFile = ""; - private $bCallApiViaFile = false; + /** @var int $iJsonDataMode */ + private $sJsonDataMode; + private $sUrl; + private $sLogin; + private $sPassword = "Iuytrez9876543ç_è-("; /** * @throws Exception @@ -25,26 +30,50 @@ class RestTest extends ItopDataTestCase protected function setUp() { parent::setUp(); + require_once(APPROOT.'application/startup.inc.php'); + $this->sLogin = "rest-user-" . date('dmYHis'); + $this->CreateTestOrganization(); if (!empty($this->sTmpFile)){ unlink($this->sTmpFile); } + + $sConfigFile = \utils::GetConfig()->GetLoadedFile(); + @chmod($sConfigFile, 0770); + $this->sUrl = \MetaModel::GetConfig()->Get('app_root_url'); + @chmod($sConfigFile, 0444); // Read-only + + $oRestProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'REST Services User'), true); + $oAdminProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'Administrator'), true); + + if (is_object($oRestProfile) && is_object($oAdminProfile)) + { + $oUser = $this->CreateUser($this->sLogin, $oRestProfile->GetKey(), $this->sPassword); + $this->AddProfileToUser($oUser, $oAdminProfile->GetKey()); + } } /** * @dataProvider BasicProvider + * @param int $iJsonDataMode */ - public function testBasic($bCallApiViaFile) + public function testCreateApi($iJsonDataMode) { - $this->bCallApiViaFile = $bCallApiViaFile; + $this->iJsonDataMode = $iJsonDataMode; //create ticket $description = date('dmY H:i:s'); - $sOuputJson = $this->CreateTicketViaApi($description); $aJson = json_decode($sOuputJson, true); - $this->assertContains("0", "".$aJson['code']); - $sUserRequestKey = array_key_first($aJson['objects']); + + if ($this->iJsonDataMode === self::MODE['NO_JSONDATA']){ + $this->assertContains("3", "".$aJson['code'], $sOuputJson); + $this->assertContains("Error: Missing parameter 'json_data'", "".$aJson['message'], $sOuputJson); + return; + } + + $this->assertContains("0", "".$aJson['code'], $sOuputJson); + $sUserRequestKey = $this->array_key_first($aJson['objects']); $this->assertContains('UserRequest::', $sUserRequestKey); $iId = $aJson['objects'][$sUserRequestKey]['key']; $sExpectedJsonOuput=<<assertEquals($sExpectedJsonOuput, $this->GetTicketViaRest($iId)); + $aCmdbChangeUserInfo = $this->GetCmdbChangeUserInfo($iId); + $this->assertEquals(['CMDBChangeOpCreate' => 'test'], $aCmdbChangeUserInfo); + + //delete ticket + $this->DeleteTicketFromApi($iId); + } + + /** + * array_key_first comes with PHP7.3 + * itop should also work with previous PHP versions + */ + private function array_key_first($aTab){ + if (!is_array($aTab) || empty($aTab)){ + return false; + } + + foreach ($aTab as $sKey => $sVal){ + return $sKey; + } + } + + /** + * @dataProvider BasicProvider + * @param int $iJsonDataMode + */ + public function testUpdateApi($iJsonDataMode) + { + $this->iJsonDataMode = $iJsonDataMode; + + //create ticket + $description = date('dmY H:i:s'); + $sOuputJson = $this->CreateTicketViaApi($description); + $aJson = json_decode($sOuputJson, true); + + if ($this->iJsonDataMode === self::MODE['NO_JSONDATA']){ + $this->assertContains("3", "".$aJson['code'], $sOuputJson); + $this->assertContains("Error: Missing parameter 'json_data'", "".$aJson['message'], $sOuputJson); + return; + } + + $this->assertContains("0", "".$aJson['code'], $sOuputJson); + $sUserRequestKey = $this->array_key_first($aJson['objects']); + $this->assertContains('UserRequest::', $sUserRequestKey); + $iId = $aJson['objects'][$sUserRequestKey]['key']; + //update ticket $description = date('Ymd H:i:s'); $sExpectedJsonOuput=<<assertEquals($sExpectedJsonOuput, $this->UpdateTicketViaApi($iId, $description)); + $aCmdbChangeUserInfo = $this->GetCmdbChangeUserInfo($iId); + $this->assertEquals(['CMDBChangeOpCreate' => 'test', 'CMDBChangeOpSetAttributeHTML' => 'test'], $aCmdbChangeUserInfo); + + + //delete ticket + $this->DeleteTicketFromApi($iId); + } + /** + * @dataProvider BasicProvider + * @param int $iJsonDataMode + */ + public function testDeleteApi($iJsonDataMode) + { + $this->iJsonDataMode = $iJsonDataMode; + + //create ticket + $description = date('dmY H:i:s'); + + $sOuputJson = $this->CreateTicketViaApi($description); + $aJson = json_decode($sOuputJson, true); + + if ($this->iJsonDataMode === self::MODE['NO_JSONDATA']){ + $this->assertContains("3", "".$aJson['code'], $sOuputJson); + $this->assertContains("Error: Missing parameter 'json_data'", "".$aJson['message'], $sOuputJson); + return; + } + + $this->assertContains("0", "".$aJson['code'], $sOuputJson); + $sUserRequestKey = $this->array_key_first($aJson['objects']); + $this->assertContains('UserRequest::', $sUserRequestKey); + $iId = $aJson['objects'][$sUserRequestKey]['key']; + //delete ticket $sExpectedJsonOuput=<< [ 'bCallApiViaFile' => false], - 'pass json_data as file' => [ 'bCallApiViaFile' => true] + 'call rest call' => [ 'sJsonDataMode' => self::MODE['JSONDATA_AS_STRING']], + 'pass json_data as file' => [ 'sJsonDataMode' => self::MODE['JSONDATA_AS_FILE']], + 'no json data' => [ 'sJsonDataMode' => self::MODE['NO_JSONDATA']] ]; } - + private function UpdateTicketViaApi($iId, $description){ $sJsonUpdateContent = << '1.3', - 'auth_user' => 'admin', - 'auth_pwd' => 'admin', + 'auth_user' => $this->sLogin, + 'auth_pwd' => $this->sPassword, ]; - if ($this->bCallApiViaFile){ + if ($this->iJsonDataMode === self::MODE['JSONDATA_AS_STRING']){ $this->sTmpFile = tempnam(sys_get_temp_dir(), 'jsondata_'); file_put_contents($this->sTmpFile, $sJsonDataContent); $oCurlFile = curl_file_create($this->sTmpFile); $aPostFields['json_data'] = $oCurlFile; - }else{ + }else if ($this->iJsonDataMode === self::MODE['JSONDATA_AS_FILE']){ $aPostFields['json_data'] = $sJsonDataContent; } - curl_setopt($ch, CURLOPT_URL, "http://localhost/iTop/webservices/rest.php"); + curl_setopt($ch, CURLOPT_URL, "$this->sUrl/webservices/rest.php"); curl_setopt($ch, CURLOPT_POST, 1);// set post data to true curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @@ -166,4 +273,34 @@ JSON; return $sJson; } + /** + * @param $iId + * Get CMDBChangeOp info to test + * @return array + */ + private function GetCmdbChangeUserInfo($iId){ + $sJsonGetContent = <<CallRestApi($sJsonGetContent); + $aJson = json_decode($sOutput, true); + if (is_array($aJson) && array_key_exists('objects', $aJson)){ + $aObjects = $aJson['objects']; + if (!empty($aObjects)){ + foreach ($aObjects as $aObject){ + $sClass = $aObject['class']; + $sUserInfo = $aObject['fields']['userinfo']; + $aUserInfo[$sClass] = $sUserInfo; + } + } + } + return $aUserInfo; + } }