diff -r c731ab9b934d -r 7fba86fa8604 server/src/app/Http/Controllers/Sparql/SparqlClientController.php --- a/server/src/app/Http/Controllers/Sparql/SparqlClientController.php Mon Oct 31 14:24:23 2016 +0100 +++ b/server/src/app/Http/Controllers/Sparql/SparqlClientController.php Thu Nov 03 01:52:26 2016 +0100 @@ -5,6 +5,9 @@ use Log; use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Pagination\Paginator; + use GuzzleHttp\Client; use EasyRdf\Sparql\Result as SparqlResult; use EasyRdf\Graph; @@ -52,17 +55,25 @@ $this->httpClient = $httpClient; } + public function getSparqlClient($timeout = null) { + if(is_null($timeout)) { + $timeout = config('corpusparole.sparql_client_timeout'); + } + $queryUrl = config('corpusparole.sesame_query_url'); + if($timeout > 0) { + $queryUrl = $queryUrl . + ((strlen(parse_url($queryUrl, PHP_URL_QUERY)) > 0)?"&":"?"). + "timeout=$timeout"; + } + return new \EasyRdf\Sparql\Client($queryUrl, config('corpusparole.sesame_update_url')); + } + // display form public function index() { return view('sparql/sparqlClientForm'); } - private function querySelect(Request $request, $query, $analyser) { - $countResult = $this->sparqlClient->query($analyser->getCountQuery()); - $countField = $countResult->getFields()[0]; - $count = $countResult->current()->$countField; - $docs = $this->sparqlClient->query($query); - $fields = $docs->getFields(); + private function readDocs($docs, $fields) { $results = []; foreach($docs as $row) { $results[] = array_reduce($fields, function($res, $field) use ($row) { @@ -74,10 +85,78 @@ return $res; }, []); } + return $results; + } + + private function abort($code, $message, $exception) { + throw new \Symfony\Component\HttpKernel\Exception\HttpException($code, $message, $exception, []); + } + + private function processQueryException($exception) { + $message = $exception->getMessage(); + if($exception instanceof \EasyRdf\Http\Exception) { + if(preg_match("/SPARQL\squery/", $message)) { + $this->abort(400, "La requête SPARQL n'est pas reconnue", $exception); + } else { + $this->abort(500, "Problème HTTP lors de la requête SPARQL", $exception); + } + } elseif($exception instanceof \EasyRdf\Exception) { + if(preg_match("/timed\sout/i", $message)) { + $this->abort(408, "Time-out causé par la requête SPARQL", $exception); + } else { + $this->abort(500, "Problème dans la requête SPARQL", $exception); + } + } else { + $this->abort(500, "Erreur serveur lors de la requête", $exception); + } + } + + private function querySelect(Request $request, $query, $analyser) { + + $limit = intval($request->input('limit', config('corpusparole.sparql_client_default_limit'))); + + if($limit === 0 || !is_null($analyser->getLimit()) || !is_null($analyser->getOffset()) ) { + try { + $docs = $this->getSparqlClient()->query($query); + } catch(\Exception $exception) { + $this->processQueryException($exception); + } + + $fields = $docs->getFields(); + $results = $this->readDocs($docs, $fields); + $count = count($results); + + } else { + + $page = Paginator::resolveCurrentPage(config('corpusparole.pagination_page_param')); + assert(is_null($page) || is_numeric($page)); + + $offset = max(0,($page - 1) * $limit); + + try { + $countResult = $this->getSparqlClient()->query($analyser->getCountQuery()); + $docs = $this->getSparqlClient()->query($query . " LIMIT $limit OFFSET $offset"); + } catch(\Exception $exception) { + $this->processQueryException($exception); + } + + + $countField = $countResult->getFields()[0]; + $count = $countResult->current()->$countField->getValue(); + + $fields = $docs->getFields(); + + $results = new LengthAwarePaginator($this->readDocs($docs, $fields), $count, $limit, $page, [ + 'path' => Paginator::resolveCurrentPath(), + 'pageName' => config('corpusparole.pagination_page_param'), + ]); + } + $namespaces = array_reduce(array_keys(RdfHelper::getPrefixes()), function($res, $p) { $res[$p] = RdfNamespace::namespaces()[$p]; return $res; }, []); + $data = [ 'query' => $query, 'count' => $count, @@ -92,8 +171,13 @@ } private function queryGraph(Request $request, $query, $analyser) { + try { + $docs = $this->getSparqlClient()->query($query); + } catch(\Exception $exception) { + $this->processQueryException($exception); + } - $docs = $this->sparqlClient->query($query); + $fields = ["subject", "predicate", "object"]; $results = []; foreach ($docs->resources() as $resource ) { @@ -129,8 +213,14 @@ } private function queryAsk(Request $request, $query, $analyser) { + try { + $result = $this->getSparqlClient()->query($query); + } catch(\Exception $exception) { + $this->processQueryException($exception); + } + $data = [ - 'results' => $this->sparqlClient->query($query), + 'results' => $result, 'namespaces' => $analyser->getPrefixes() ]; @@ -159,8 +249,7 @@ } elseif($queryType === SparqlQueryAnalyser::ASK_QUERY) { list($view, $data) = $this->queryAsk($request, $query, $analyser); } else { - //return 500 - abort(500, "Serialization format unknown"); + abort(400, "La requête n'est pas reconnue"); } return view($view, $data); @@ -182,7 +271,30 @@ $headers['Accept'] = $format; } - $sesameResp = $this->httpClient->get(config('corpusparole.sesame_query_url'), ['query' => $request->all(), 'headers' => $headers]); + $queryParams = $request->all(); + $queryParams['timeout'] = config('corpusparole.sparql_client_timeout'); + $queryUrl = config('corpusparole.sesame_query_url'); + + try { + $sesameResp = $this->httpClient->post($queryUrl, ['form_params' => $queryParams, 'headers' => $headers]); + } catch(\GuzzleHttp\Exception\ServerException $exception) { + if($exception->getCode() == 503) { + $this->abort(408, "Time-out causé par la requête SPARQL", $exception); + } else { + $this->abort(500, "Problème lors de la requête SPARQL", $exception); + } + + } catch(\GuzzleHttp\Exception\RequestException $exception) { + $message = $exception->getMessage(); + if(preg_match("/MALFORMED\sQUERY/i", $message)) { + $abortMessage = "Requête SPARQL mal-formée"; + } else { + $abortMessage = "Problème lors de la requête SPARQL"; + } + $this->abort($exception->getCode(), $abortMessage, $exception); + } catch(\Exception $exception) { + $this->abort(500, "Erreur serveur lors de la requête", $exception); + } $resp = response((string)$sesameResp->getBody(), $sesameResp->getStatusCode()); foreach ($sesameResp->getHeaders() as $name => $values) {