Skip to content
Snippets Groups Projects
Commit 1e7278ee authored by Hoop, Bert Jan de's avatar Hoop, Bert Jan de
Browse files

Merge branch 'feature/L05DOCBST-41-webquery-class' into prod

* feature/L05DOCBST-41-webquery-class:
  L05DOCBST-41 add Library\Webquery class
parents c3c609d0 f24cd612
No related branches found
Tags v0.0.1
No related merge requests found
......@@ -10,11 +10,22 @@
}
],
"require": {
"php": ">=7.0.0"
"php": ">=7.0.0",
"guzzlehttp/guzzle": "^6.3"
},
"autoload": {
"psr-4": {
"Library\\": "src/library"
}
},
"require-dev": {
"phpunit/phpunit": ">=4.8 < 6.0"
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"test": "phpunit"
}
......
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="php-modules">
<directory>tests</directory>
</testsuite>
</testsuites>
<php>
<env name="UNITTEST" value="yes"/>
</php>
</phpunit>
<?php
namespace Library;
use GuzzleHttp\Psr7\Request;
class Webquery {
use \Library\LoggerTrait;
/**
* @var array log context
*/
protected $context = ['webquery'];
/**
* @var \GuzzleHttp\Client psr-7 http client
*/
protected $http_client;
/**
* @var string webquery host name, default: 'library.wur.nl'
*/
protected $host = 'library.wur.nl';
/**
* @var string webquery service name
*/
protected $service = '';
/**
* @var string webquery suffix name, default: 'xml'
*/
protected $suffix = 'xml';
/**
* @var GuzzleHttp\Psr7\Response
*/
protected $response;
/**
* @var SimpleXMLElement parsed xml from response
*/
protected $resp_xml;
/**
* @var array with children and attributes from error element
*/
protected $resp_error;
public function __construct() {
}
/**
* setter for http_client
* @param \GuzzleHttp\Client $http_client
* @return $this
*/
public function set_http_client(\GuzzleHttp\Client $http_client) {
$this->http_client = $http_client;
return $this;
}
/**
* getter for http_client
* if no http_client is set a default http_client is created
* @return \GuzzleHttp\Client
*/
public function get_http_client() {
if (!isset($this->http_client)) {
$this->http_client = new \GuzzleHttp\Client();
}
return $this->http_client;
}
/**
* set host name for webquery url
* @param sting $host webquery host
* @return $this
*/
public function set_host($host) {
$this->host = $host;
return $this;
}
/**
* set webquery service name for url
* @param string $service webquery service
* @return $this
*/
public function set_service($service) {
$this->service = $service;
return $this;
}
/**
* set webquery suffix for url
* @param string $suffix webquery suffix
* @return $this
*/
public function set_suffix($suffix) {
$this->suffix = $suffix;
return $this;
}
public function get_url() {
return "http://$this->host/WebQuery/$this->service/new_$this->suffix";
}
/**
* convert xml tree to x-www-form-urlencoded string
* @param SimpleXMLElement $xml xml tree
* @return string xml as x-www-form-urlencoded string
*/
public function xml_to_wwwform($xml) {
$result = [];
foreach($xml as $key=>$value) {
$result[] = "$key=".($value->count() > 0 ? '&'.$this->xml_to_wwwform($value) : urlencode($value));
}
return implode('&', $result);
}
/**
* create new record in webquery
* uses properties host, service and suffix for the url
* @param SimpleXMLElement $xml post data (xmol tree)
* @return boolean true on success
*/
public function create_record($xml) {
array_push($this->context, 'create_record');
$this->reset_response();
if (empty($this->service)) {
$this->error("create_record: service is not set", $this->context);
array_pop($this->context);
return false;
}
$url = $this->get_url();
$headers = ['Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'];
$body = $this->xml_to_wwwform($xml);
$this->debug("request url: $url", $this->context);
$this->debug("request data: $body", $this->context);
$request = new Request('POST', $url, $headers, $body);
$options = [
'timeout' => 2,
'http_errors' => false, // disable exceptions on 4xx and 5xx responses
];
try {
$this->response = $this->http_client->send($request, $options);
} catch (\GuzzleHttp\Exception\RequestException $ex) {
$this->error("exception posting to webquery: ".$ex->getMessage(), $this->context);
$this->resp_error = [
'status' => '500',
'code' => $ex->getCode(),
'message' => 'request exception: '.$ex->getMessage(),
];
array_pop($this->context);
return false;
}
$success = $this->is_success();
$code = $this->get_error_code();
$message = $this->get_error_message();
if ($success) {
$this->info("success, code=$code, message=$message, isn=".$this->get_isn(), $this->context);
} else {
$this->error("failed, code=$code, message=$message", $this->context);
}
array_pop($this->context);
return $success;
}
/**
* retuns success status of last webquery action
* @return boolean true on success
*/
public function is_success() {
// try to parse response xml
$this->resp_xml = $this->parse_xml($this->response->getBody());
if (empty($this->resp_xml)) {
$fake_resp = '<root><error status="500"><code>NO_XML_RESP</code><message>failed to parse xml response</message></error></root>';
$this->resp_xml = $this->parse_xml($fake_resp);
return false;
}
// get content of first error element
$error = $this->resp_xml->error[0];
if (isset($error)) {
foreach ($error->attributes() as $attr => $value) {
$this->resp_error[$attr] = (string) $value;
}
foreach ($error->children() as $key => $value) {
$this->resp_error[$key] = (string) $value;
}
}
if ($this->response->getStatusCode() != 200) {
return false;
}
if (!isset($this->resp_error['status']) || $this->resp_error['status'] != 200) {
return false;
}
return true;
}
/**
* return response object of last webquery action
* @return psr-7 response object
*/
public function get_response() {
return $this->response;
}
/**
* retuns content of first webquery error element of last webquery action
* @return array with all webquery error attributes and elements
*/
public function get_error() {
return $this->resp_error;
}
/**
* return value of given error property of last webquery action
* @param string $name webquery error property name (attribute or ellement
* @return string value or empty string if not set
*/
public function get_error_value($name) {
return isset($this->resp_error[$name]) ? $this->resp_error[$name] : '';
}
/**
* return value of webquery error code of last webquery action
* @return string value of code error property or empty string
*/
public function get_error_code() {
return $this->get_error_value('code');
}
/**
* return value of webquery error status of last webquery action
* @return string value of status error property or empty string
*/
public function get_error_status() {
return $this->get_error_value('status');
}
/**
* return value of webquery error message of last webquery action
* the return message is a composite of the message, field and xmlerror properties
* @return string value of message error property or empty string
*/
public function get_error_message() {
$message = $this->get_error_value('message');
$field = $this->get_error_value('field');
if (!empty($field)) {
$message = "$message: $field";
}
$xmlerror = $this->get_error_value('xmlerror');
return empty($xmlerror) ? $message : "$message ($xmlerror)";
}
/**
* return value of webquery error isn of last webquery action
* isn is set even if webquery failed to create a record
* @return string value of isn error property or empty string
*/
public function get_isn() {
return $this->get_error_value('isn');
}
protected function reset_response() {
$this->response = null;
$this->resp_xml = null;
$this->resp_error = [];
}
/**
* parse xml content from message
*
* @param $text
* @return SimpleXMLElement parsed xml or false on errors
*/
public function parse_xml($text) {
if (trim($text) == '') {
$this->error("xml string is empty", $this->context);
return false;
}
libxml_use_internal_errors(true);
$xml = simplexml_load_string($text);
$errors = libxml_get_errors();
libxml_clear_errors();
$error_count = count($errors);
if ($error_count > 0) {
$this->error("parsing xml string failed with $error_count errors (max. 10 listed", $this->context);
// only log first 10 error messages
foreach (array_slice($errors, 0, 9) as $error) {
$this->error('xml parse error: ' . print_r($error, true), $this->context);
}
return false;
}
return $xml;
}
}
<?php
namespace Tests\Functional\Library;
use GuzzleHttp\Psr7\Response;
class WebqueryTest extends \PHPUnit_Framework_TestCase {
public function test_get_url() {
// with default settings
$wq = new \Library\Webquery();
$expect = 'http://library.wur.nl/WebQuery//new_xml';
$this->assertEquals($expect, $wq->get_url(), 'no properties set, default url');
// with url properties set
$wq = new \Library\Webquery();
$wq->set_host('devel.library.wur.nl')
->set_service('testservice')
->set_suffix('record');
$expect = 'http://devel.library.wur.nl/WebQuery/testservice/new_record';
$this->assertEquals($expect, $wq->get_url(), 'no properties set, default url');
}
public function test_xml_to_wwwform() {
$xml = get_test_xml();
$wq = new \Library\Webquery();
$wwwform = $wq->xml_to_wwwform($xml);
$expect = 'sub1=subval1&sub2=&child1=childval1&child2=childval2&sub3=subval+3';
$this->assertEquals($expect, $wwwform, 'record in x-www-form-urlencoded string');
}
public function test_create_record() {
// create mock http client that stores requests and responses
$request_history = [];
$responses = [
new Response(200, [], '<recordset><error status="200" isn="13"><code>WQW_OK</code><message>record created</message></error></recordset>'),
new Response(500, [], '<recordset><error status="501"><code>WQE_FAILED</code><message>record not created</message></error></recordset>'),
];
$mock_httpclient = get_mock_httpclient($responses, $request_history);
$wq = new \Library\Webquery();
$wq->set_http_client($mock_httpclient);
//$wq->set_logger(new \Monolog\Logger('create_record'));
$wq->set_host('devel.library.wur.nl')
->set_service('testservice');
$xml = get_test_xml();
// first request succeeds
$result = $wq->create_record($xml);
$this->assertTrue($result, 'first request return value');
$this->assertEquals('200', $wq->get_error_status(), 'first response error status');
$this->assertEquals('WQW_OK', $wq->get_error_code(), 'first response error code');
$this->assertEquals('record created', $wq->get_error_message(), 'first response error message');
$this->assertEquals('13', $wq->get_isn(), 'first response isn');
// second request fails
$wq->set_suffix('record');
$result = $wq->create_record($xml);
$this->assertFalse($result, 'second request return value');
$this->assertEquals('501', $wq->get_error_status(), 'second response error status');
$this->assertEquals('WQE_FAILED', $wq->get_error_code(), 'second response error code');
$this->assertEquals('record not created', $wq->get_error_message(), 'second response error message');
$this->assertEquals('', $wq->get_isn(), 'second response isn');
$this->assertEquals(2, count($request_history), 'request history count');
// check first request
$request = $request_history[0]['request'];
$this->assertEquals('POST', $request->getMethod(), 'request 1 method');
$expect = 'http://devel.library.wur.nl/WebQuery/testservice/new_xml';
$this->assertEquals($expect, $request->getUri(), 'request 1 url');
$expect = 'sub1=subval1&sub2=&child1=childval1&child2=childval2&sub3=subval+3';
$this->assertEquals($expect, $request->getBody(), 'request 1 body');
// check second request
$request = $request_history[1]['request'];
$this->assertEquals('POST', $request->getMethod(), 'request 2 method');
$expect = 'http://devel.library.wur.nl/WebQuery/testservice/new_record';
$this->assertEquals($expect, $request->getUri(), 'request 2 url');
$expect = 'sub1=subval1&sub2=&child1=childval1&child2=childval2&sub3=subval+3';
$this->assertEquals($expect, $request->getBody(), 'request 2 body');
}
}
/*********** Helper Functions **************/
function get_test_xml() {
$xml_txt = <<<EOT
<record>
<sub1>subval1</sub1>
<sub2>
<child1>childval1</child1>
<child2>childval2</child2>
</sub2>
<sub3>subval 3</sub3>
</record>
EOT;
$wq = new \Library\Webquery();
return $wq->parse_xml($xml_txt);
}
function get_mock_httpclient(array $mock_responses, array &$history = null) {
foreach ($mock_responses as $key => $value) {
if (is_int($value)) {
$mock_responses[$key] = new \GuzzleHttp\Psr7\Response($value);
}
}
$mock_handler = new \GuzzleHttp\Handler\MockHandler($mock_responses);
$mock_stack = \GuzzleHttp\HandlerStack::create($mock_handler);
if (isset($history)) {
$mock_history = \GuzzleHttp\Middleware::history($history);
$mock_stack->push($mock_history);
}
return new \GuzzleHttp\Client(['handler' => $mock_stack]);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment