diff --git a/phpunit.xml b/phpunit.xml index 248d904..1ba583d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ - + tests diff --git a/tests/Webservice/Formatter/FormatterTest.php b/tests/Webservice/Formatter/FormatterTest.php new file mode 100644 index 0000000..f9895db --- /dev/null +++ b/tests/Webservice/Formatter/FormatterTest.php @@ -0,0 +1,78 @@ + class) + */ + protected static function registerFormats(array $formats) { + foreach($formats as $f) { + self::$formats[$f] = get_called_class(); + } + } + + /** + * @return Formatter The formatter to use for this request + */ + public static function getFormatter() { + self::loadFormatters(); + $format = self::getFormatFromHeader(); + + return new $format(); + } + + /** + * Load all formatters in the current directory + */ + private static function loadFormatters() { + preg_match('/(.+)\\\([a-zA-Z0-9]+)/', get_called_class(), $parts); + $us = $parts[2]; + $namespace = $parts[1]; + + $base = __DIR__.'/'; + $ext = '.php'; + $files = glob(sprintf('%s%s%s', $base, '*', $ext)); + foreach($files as $f) { + $c = str_replace(array($base, $ext), '', $f); + if($c !== $us) { + $c = $namespace.'\\'.$c; + call_user_func(array($c, 'init')); + } + } + } + + /** + * @return string The class name to instantiate in accord to the Accept header + */ + private static function getFormatFromHeader() { + //TODO this is ugly + return 'BSR\Webservice\Formatter\Json'; + if(isset($_SERVER['HTTP_ACCEPT'])) { + $formats = array_map(function($f) { + $parts = explode(';', $f); + $parts[1] = (isset($parts[1]) ? (float) preg_replace('/[^0-9\.]/', '', $parts[1]) : 1.0) * 100; + return $parts; + }, explode(',', $_SERVER['HTTP_ACCEPT'])); + + usort($formats, function($a, $b) { return $b[1] - $a[1]; }); + + foreach($formats as $f) { + if(isset(self::$formats[$f[0]])) { + return self::$formats[$f[0]]; + } + } + + } + + return 'BSR\Webservice\Formatter\Json'; + } + + /** + * Output the content for the given data + * @param array $data + */ + abstract public function render($data); +} diff --git a/tests/Webservice/Formatter/HtmlTest.php b/tests/Webservice/Formatter/HtmlTest.php new file mode 100644 index 0000000..e545d60 --- /dev/null +++ b/tests/Webservice/Formatter/HtmlTest.php @@ -0,0 +1,157 @@ + $limit) { + $v = substr($v, 0, $limit).$ellipsis; + } + return $v; + } + + protected function formatValue($v) { + if(is_numeric($v)) { + return $v; + } + if(is_bool($v)) { + return ''; + } + if(is_string($v) && strpos($v, 'http') !== false) { + $url = $v; + $v = $this->truncate($v, '...'); + return "$v"; + } + + return $this->truncate(print_r($v, true)); + } + + protected function result($data) { + // the format is array('result' => array('function name' => DATA)) + // so take the first element of the 'result' array + $func = key($data['result']); + $data = reset($data['result']); + $data = is_array($data) ? $data : array(); + $title = $func; + + $content = ''; + $after = ''; + + if($func == 'NewSearch') { + $content .= '

Count : '.$data['count'].'

'; + $after .= '

Extra :

'.print_r($data['facets'], true).'

'; + + unset($data['count']); + unset($data['facets']); + } + + $first = reset($data); + $single = ! is_array($first); + $columns = array(); + + $content .= ''; + if($single) { + $content .= ""; + } else { + $columns = array_keys($first); + + $title .= ' ('.count($data).' results)'; + + $content .= ''; + foreach($columns as $k) { + $content .= ""; + } + $content .= ''; + } + $content .= ''; + if($single) { + foreach($data as $k => $v) { + $content .= ""; + } + } else { + foreach($data as $row) { + $content .= ''; + foreach($columns as $c) { + $content .= ''; + } + $content .= ''; + } + } + $content .= '
FieldValue
$k
$k".$this->formatValue($v)."
'.$this->formatValue(isset($row[$c]) ? $row[$c] : '').'
'.$after; + + return array( + 'title' => $title, + 'content' => $content, + 'status' => 'success', + ); + } + + protected function error($data) { + $code = $data['error']['code']; + $name = $data['error']['name']; + $msg = $data['error']['reason']; + + return array( + 'title' => 'Error', + 'content' => "

[$code] $name : $msg

", + 'status' => 'warning', + ); + } + + protected function failure($data) { + $code = $data['failure']['code']; + $name = $data['failure']['reason']; + + return array( + 'title' => 'Failure', + 'content' => "

[$code] $name

", + 'status' => 'danger', + ); + } + + public function render($data) + { + $type = key($data); + + if (method_exists($this, $type)) { + $context = call_user_func_array(array($this, $type), array($data)); + } else { + $context = array( + 'title' => 'Formatter error', + 'content' => '

Unable to render this

', + 'status' => 'info', + ); + } + $info = Logger::data(); + $context['time'] = $info['time']; + + $panel = static::template($context, 'panel'); + + if(isset($data['extra'])) { + $panel .= $data['extra']; + } + + echo static::template(array( + 'version' => $info['version'], + 'title' => $context['title'], + 'content' => $panel, + )); + } + + public static function template(array $context = array(), $template = 'layout') { + $html = file_get_contents(sprintf('templates/%s.html', $template)); + + $patterns = array_map(function($p) { return "{{ $p }}"; }, array_keys($context)); + return str_replace($patterns, array_values($context), $html); + } +} diff --git a/tests/Webservice/Formatter/JsonTest.php b/tests/Webservice/Formatter/JsonTest.php new file mode 100644 index 0000000..45d46a8 --- /dev/null +++ b/tests/Webservice/Formatter/JsonTest.php @@ -0,0 +1,17 @@ +getDocComment(); + $params = $rm->getParameters(); + + preg_match_all('/@param\s+(?P[^\s]*?)\s*\$(?P[^\s]+?)\s+(?P[\w\s]*)/', $doc, $parametersDoc, PREG_SET_ORDER); + preg_match('/@return\s+(?P[^\s]*?)\s+(?P[\w\s]*)/', $doc, $returnDoc); + + $doc = array_filter(array_map(function($l) { + $l = trim($l, " \t\n\r\0\x0B"); + if(strpos($l, '/**') === 0 || strpos($l, '*/') === 0) { + $l = ''; + } + $l = trim($l, "* "); + if(strpos($l, '@') === 0) { + $l = ''; + } + return $l; + }, explode("\n", $doc))); + $doc = nl2br(implode("\n", $doc)); + + $paramsHtml = ''; + foreach($params as $p) { + foreach($parametersDoc as $d) { + if(isset($d['name']) && $p->name == $d['name']) { + $paramsHtml .= Html::template(array( + 'name' => $p->name, + 'optional' => $p->isDefaultValueAvailable() ? '(optional)' : '', + 'type' => isset($d['type']) ? $d['type'] : '', + 'doc' => isset($d['doc']) ? $d['doc'] : '', + ), 'param_help'); + } + } + } + + return Html::template(array( + 'func' => $func, + 'help' => $doc, + 'parameters' => $paramsHtml, + 'return' => Html::template(array( + 'type' => isset($returnDoc['type']) ? $returnDoc['type'] : '', + 'doc' => isset($returnDoc['doc']) ? $returnDoc['doc'] : '', + ), 'return_help'), + ), 'func_help'); + } + + + public static function content(WebService $ws) { + $rc = new \ReflectionClass($ws); + + $methods = array_filter(array_map(function(\ReflectionMethod $m) { + if($m->getName() == 'Run') { + // this is a method from WebService directly and is of not interests for the help + return ''; + } + return $m->getName(); + }, $rc->getMethods(\ReflectionMethod::IS_PUBLIC))); + + $html = ''; + foreach($methods as $m) { + $html .= static::func($ws, $m); + } + + return $html; + } + + public static function exception(WebException $e, WebService $ws, $func) { + return static::func($ws, $func); + } +} diff --git a/src/Webservice/Logger.php b/tests/Webservice/Logger.php similarity index 100% rename from src/Webservice/Logger.php rename to tests/Webservice/Logger.php diff --git a/tests/Webservice/RendererTest.php b/tests/Webservice/RendererTest.php new file mode 100644 index 0000000..407fefe --- /dev/null +++ b/tests/Webservice/RendererTest.php @@ -0,0 +1,29 @@ + 'Ok', + 400 => 'Bad request', + 404 => 'Not Found', + 403 => 'Not Authorized', + 500 => 'Server Error', + ); + + public function __construct() { + ob_start(); + } + + public function render($status, $data) { + header(sprintf('HTTP/1.0 %s %s', $status, self::$statusMessages[$status])); + header("Access-Control-Allow-Origin: *"); + ob_clean(); + flush(); + + $formatter = Formatter::getFormatter(); + $formatter->render($data); + } +}