/**
  * Process the request Uri and creates an instance of
  * RequestDescription from the processed uri.
  * 
  * @param IService $service        Reference to the data service instance.
  *
  * @return RequestDescription
  * 
  * @throws ODataException If any exception occurs while processing the segments
  *                        or in case of any version incompatibility.
  */
 public static function process(IService $service)
 {
     $host = $service->getHost();
     $absoluteRequestUri = $host->getAbsoluteRequestUri();
     $absoluteServiceUri = $host->getAbsoluteServiceUri();
     $requestUriSegments = array_slice($absoluteRequestUri->getSegments(), $absoluteServiceUri->getSegmentCount());
     $segments = SegmentParser::parseRequestUriSegments($requestUriSegments, $service->getProvidersWrapper(), true);
     $request = new RequestDescription($segments, $absoluteRequestUri, $service->getConfiguration()->getMaxDataServiceVersion(), $host->getRequestVersion(), $host->getRequestMaxVersion());
     $kind = $request->getTargetKind();
     if ($kind == TargetKind::METADATA() || $kind == TargetKind::BATCH() || $kind == TargetKind::SERVICE_DIRECTORY()) {
         return $request;
     }
     if ($kind == TargetKind::PRIMITIVE_VALUE() || $kind == TargetKind::MEDIA_RESOURCE()) {
         // http://odata/NW.svc/Orders/$count
         // http://odata/NW.svc/Orders(123)/Customer/CustomerID/$value
         // http://odata/NW.svc/Employees(1)/$value
         // http://odata/NW.svc/Employees(1)/ThumbNail_48X48/$value
         $request->setContainerName($segments[count($segments) - 2]->getIdentifier());
     } else {
         $request->setContainerName($request->getIdentifier());
     }
     if ($request->getIdentifier() === ODataConstants::URI_COUNT_SEGMENT) {
         if (!$service->getConfiguration()->getAcceptCountRequests()) {
             throw ODataException::createBadRequestError(Messages::configurationCountNotAccepted());
         }
         $request->queryType = QueryType::COUNT();
         // use of $count requires request DataServiceVersion
         // and MaxDataServiceVersion greater than or equal to 2.0
         $request->raiseResponseVersion(2, 0);
         $request->raiseMinVersionRequirement(2, 0);
     } else {
         if ($request->isNamedStream()) {
             $request->raiseMinVersionRequirement(3, 0);
         } else {
             if ($request->getTargetKind() == TargetKind::RESOURCE()) {
                 if (!$request->isLinkUri()) {
                     $resourceSetWrapper = $request->getTargetResourceSetWrapper();
                     //assert($resourceSetWrapper != null)
                     $hasNamedStream = $resourceSetWrapper->hasNamedStreams($service->getProvidersWrapper());
                     $hasBagProperty = $resourceSetWrapper->hasBagProperty($service->getProvidersWrapper());
                     if ($hasNamedStream || $hasBagProperty) {
                         $request->raiseResponseVersion(3, 0);
                     }
                 }
             } else {
                 if ($request->getTargetKind() == TargetKind::BAG()) {
                     $request->raiseResponseVersion(3, 0);
                 }
             }
         }
     }
     return $request;
 }
Example #2
0
 /**
  * Checks whether etag headers are allowed for this request.
  * 
  * @return boolean True if ETag header (If-Match or If-NoneMatch)
  *                 is allowed for the request, False otherwise.
  */
 public function isETagHeaderAllowed()
 {
     return $this->lastSegment->isSingleResult() && $this->queryType != QueryType::COUNT() && !$this->isLinkUri() && (is_null($this->_rootProjectionNode) || !$this->_rootProjectionNode->isExpansionSpecified());
 }
Example #3
0
 /**
  * Applies the query options to the resource(s) retrieved from the data source.
  * 
  * @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied.
  *
  */
 private function applyQueryOptions(SegmentDescriptor $segment)
 {
     //TODO: I'm not really happy with this..i think i'd rather keep the result the QueryResult
     //not even bother with the setCountValue stuff (shouldn't counts be on segments?)
     //and just work with the QueryResult in the object model serializer
     $result = $segment->getResult();
     if (!$result instanceof QueryResult) {
         //If the segment isn't a query result, then there's no paging or counting to be done
         return;
     }
     // Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first
     // regardless if POData does the paging or not.
     if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT()) {
         if ($this->providers->handlesOrderedPaging()) {
             $this->request->setCountValue($result->count);
         } else {
             $this->request->setCountValue(count($result->results));
         }
     }
     //Have POData perform paging if necessary
     if (!$this->providers->handlesOrderedPaging() && !empty($result->results)) {
         $result->results = $this->performPaging($result->results);
     }
     //a bit surprising, but $skip and $top affects $count so update it here, not above
     //IE  data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries
     if ($this->request->queryType == QueryType::COUNT()) {
         if ($this->providers->handlesOrderedPaging()) {
             $this->request->setCountValue($result->count);
         } else {
             $this->request->setCountValue(count($result->results));
         }
     }
     $segment->setResult($result->results);
 }
 public function testGetRelatedResourceSetReturnsCountWhenQueryTypeIsCountProviderHandlesPaging()
 {
     $orderBy = null;
     $top = 10;
     $skip = 10;
     $fakeQueryResult = new QueryResult();
     $fakeQueryResult->count = null;
     //null is not numeric
     //Because the provider handles paging and this request needs a count, the count must be numeric
     Phockito::when($this->mockQueryProvider->handlesOrderedPaging())->return(true);
     $fakeSourceEntity = new \stdClass();
     Phockito::when($this->mockQueryProvider->getRelatedResourceSet(QueryType::COUNT(), $this->mockResourceSet, $fakeSourceEntity, $this->mockResourceSet2, $this->mockResourceProperty, $this->mockFilterInfo, $orderBy, $top, $skip))->return($fakeQueryResult);
     $wrapper = $this->getMockedWrapper();
     try {
         $wrapper->getRelatedResourceSet(QueryType::COUNT(), $this->mockResourceSet, $fakeSourceEntity, $this->mockResourceSet2, $this->mockResourceProperty, $this->mockFilterInfo, $orderBy, $top, $skip);
         $this->fail("expected exception not thrown");
     } catch (ODataException $ex) {
         $this->assertEquals(Messages::queryProviderResultCountMissing("IQueryProvider::getRelatedResourceSet", QueryType::COUNT()), $ex->getMessage());
         $this->assertEquals(500, $ex->getStatusCode());
     }
 }
Example #5
0
 /**
  * For queries like http://localhost/NorthWind.svc/Customers
  */
 public function getResourceSet(QueryType $queryType, ResourceSet $resourceSet, $filterInfo = null, $orderBy = null, $top = null, $skip = null)
 {
     $result = new QueryResult();
     $entityClassName = $resourceSet->getResourceType()->getInstanceType()->name;
     $entityName = $this->getEntityName($entityClassName);
     $tableName = $this->getTableName($entityName);
     $option = null;
     if ($queryType == QueryType::ENTITIES_WITH_COUNT()) {
         //tell mysql we want to know the count prior to the LIMIT
         //$option = 'SQL_CALC_FOUND_ROWS';
     }
     $where = $filterInfo ? ' WHERE ' . $filterInfo->getExpressionAsString() : '';
     $order = $orderBy ? ' ORDER BY ' . $this->getOrderByExpressionAsString($orderBy) : '';
     $sqlCount = 'SELECT COUNT(*) FROM ' . $tableName . $where;
     if ($queryType == QueryType::ENTITIES() || $queryType == QueryType::ENTITIES_WITH_COUNT()) {
         $sql = 'SELECT ' . $option . ' * FROM ' . $tableName . $where . $order . ($top ? ' LIMIT ' . $top : '') . ($skip ? ' OFFSET ' . $skip : '');
         $data = $this->queryAll($sql);
         if ($queryType == QueryType::ENTITIES_WITH_COUNT()) {
             //get those found rows
             //$result->count = $this->queryScalar('SELECT FOUND_ROWS()');
             $result->count = $this->queryScalar($sqlCount);
         }
         $result->results = array_map($entityClassName . '::fromRecord', $data);
     } elseif ($queryType == QueryType::COUNT()) {
         $result->count = QueryResult::adjustCountForPaging($this->queryScalar($sqlCount), $top, $skip);
     }
     return $result;
 }
Example #6
0
 public function testProcessRequestForCollectionCountProviderHandlesPaging()
 {
     $requestURI = new Url('http://host.com/data.svc/Collection/$count');
     Phockito::when($this->mockServiceHost->getAbsoluteRequestUri())->return($requestURI);
     $this->fakeServiceConfig->setAcceptCountRequests(true);
     $this->fakeServiceConfig->setMaxDataServiceVersion(ProtocolVersion::V2());
     $uriProcessor = UriProcessor::process($this->mockService);
     $fakeQueryResult = new QueryResult();
     $fakeQueryResult->results = array(1, 2, 3);
     $fakeQueryResult->count = 10;
     //note this differs from the size of the results array
     Phockito::when($this->mockProvidersWrapper->getResourceSet(QueryType::COUNT(), $this->mockCollectionResourceSetWrapper, null, null, null, null))->return($fakeQueryResult);
     //indicate that the Provider performs the paging (thus it will use the count in the QueryResult)
     Phockito::when($this->mockProvidersWrapper->handlesOrderedPaging())->return(true);
     $uriProcessor->execute();
     $request = $uriProcessor->getRequest();
     $actual = $request->getTargetResult();
     $this->assertEquals(10, $actual);
 }
Example #7
0
 /**
  * Process the $inlinecount option and update the request description.
  *
  * @return void
  * 
  * @throws ODataException Throws bad request error in the following cases
  *                          (1) If $inlinecount is disabled by the developer
  *                          (2) If both $count and $inlinecount specified
  *                          (3) If $inlinecount value is unknown
  *                          (4) If capability negotiation over version fails
  */
 private function _processCount()
 {
     $inlineCount = $this->service->getHost()->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_INLINECOUNT);
     //If it's not specified, we're done
     if (is_null($inlineCount)) {
         return;
     }
     //If the service doesn't allow count requests..then throw an exception
     if (!$this->service->getConfiguration()->getAcceptCountRequests()) {
         throw ODataException::createBadRequestError(Messages::configurationCountNotAccepted());
     }
     $inlineCount = trim($inlineCount);
     //if it's set to none, we don't do inline counts
     if ($inlineCount === ODataConstants::URI_ROWCOUNT_OFFOPTION) {
         return;
     }
     //You can't specify $count & $inlinecount together
     //TODO: ensure there's a test for this case see #55
     if ($this->request->queryType == QueryType::COUNT()) {
         throw ODataException::createBadRequestError(Messages::queryProcessorInlineCountWithValueCount());
     }
     $this->_checkSetQueryApplicable();
     //TODO: why do we do this check?
     if ($inlineCount === ODataConstants::URI_ROWCOUNT_ALLOPTION) {
         $this->request->queryType = QueryType::ENTITIES_WITH_COUNT();
         $this->request->raiseMinVersionRequirement(2, 0);
         $this->request->raiseResponseVersion(2, 0);
     } else {
         throw ODataException::createBadRequestError(Messages::queryProcessorInvalidInlineCountOptionError());
     }
 }
Example #8
0
 private function ValidateQueryResult($queryResult, QueryType $queryType, $methodName)
 {
     if (!$queryResult instanceof QueryResult) {
         throw ODataException::createInternalServerError(Messages::queryProviderReturnsNonQueryResult($methodName));
     }
     if ($queryType == QueryType::COUNT() || $queryType == QueryType::ENTITIES_WITH_COUNT()) {
         //and the provider is supposed to handle the ordered paging they must return a count!
         if ($this->queryProvider->handlesOrderedPaging() && !is_numeric($queryResult->count)) {
             throw ODataException::createInternalServerError(Messages::queryProviderResultCountMissing($methodName, $queryType));
         }
         //If POData is supposed to handle the ordered aging they must return results! (possibly empty)
         if (!$this->queryProvider->handlesOrderedPaging() && !is_array($queryResult->results)) {
             throw ODataException::createInternalServerError(Messages::queryProviderResultsMissing($methodName, $queryType));
         }
     }
     if (($queryType == QueryType::ENTITIES() || $queryType == QueryType::ENTITIES_WITH_COUNT()) && !is_array($queryResult->results)) {
         throw ODataException::createInternalServerError(Messages::queryProviderResultsMissing($methodName, $queryType));
     }
 }