/**
  * Test search InternalSkipTokenInfo::GetIndexOfFirstEntryInNextPage function
  */
 public function testGetIndexOfFirstEntryInNextPage2()
 {
     $this->markTestSkipped("Skipped because it depends on a query provider that isn't mocked");
     $northWindMetadata = NorthWindMetadata::Create();
     $configuration = new ServiceConfiguration($northWindMetadata);
     $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
     $providersWrapper = new ProvidersWrapper($northWindMetadata, $this->mockQueryProvider, $configuration);
     $resourceSetWrapper = $providersWrapper->resolveResourceSet('Orders');
     $resourceType = $resourceSetWrapper->getResourceType();
     $orderBy = 'ShipName asc, Freight';
     //Note: library will add prim key as last sort key
     $orderBy .= ', OrderID';
     $qp = new NorthWindQueryProvider1();
     $orders = $qp->getResourceSet($resourceSetWrapper->getResourceSet());
     $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $providersWrapper);
     $compFun = $internalOrderByInfo->getSorterFunction();
     $fun = $compFun->getReference();
     usort($orders, $fun);
     $numRecords = count($orders);
     //-----------------------------------------------------------------
     //Search with a key that exactly matches
     $skipToken = utf8_decode(urldecode("'Antonio%20Moreno%20Taquer%C3%ADa',22.0000M,10365"));
     $skipToken = urldecode($skipToken);
     $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
     $nextIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($orders);
     $this->assertTrue($nextIndex > 1);
     $this->assertTrue($nextIndex < $numRecords);
     //$nextIndex is the index of order record next to the searched record
     $this->assertEquals($orders[$nextIndex - 1]->OrderID, 10365);
     $this->assertEquals($orders[$nextIndex - 1]->Freight, 22.0);
     //-----------------------------------------------------------------
     //Search with a key that partially matches, in the DB there is no
     //order with ShipName 'An', but there are records start with
     //'An', so partial match, since its a parial match other two
     //key wont be used for comparsion
     $skipToken = "'An',22.0000M,10365";
     $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
     $nextIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($orders);
     $this->assertTrue($nextIndex > 1);
     $this->assertTrue($nextIndex < $numRecords);
     //Make sure this is the most matching record by comparing with previous record
     $prevOrder = $orders[$nextIndex - 1];
     $r = strcmp($prevOrder->ShipName, $orders[$nextIndex]->ShipName);
     $this->assertTrue($r < 0);
     //Make sure this is the most matching record by comparing with next record
     $nextOrder = $orders[$nextIndex + 1];
     $r = strcmp($nextOrder->ShipName, $orders[$nextIndex]->ShipName);
     $this->assertTrue($r >= 0);
     //-----------------------------------------------------------------
     //Search with a key that does not exists
     $skipToken = "'XXX',11,10365";
     $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
     $nextIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($orders);
     $this->assertTrue($nextIndex == -1);
     //-----------------------------------------------------------------
 }
 /**
  * Build 'Projection Tree' from the given expand path segments
  * 
  * @param array(array(string)) $expandPathSegments Collection of expand paths.
  *
  * 
  * @return void
  * 
  * @throws ODataException If any error occurs while processing the expand path segments
  *                        .
  */
 private function _buildProjectionTree($expandPathSegments)
 {
     foreach ($expandPathSegments as $expandSubPathSegments) {
         $currentNode = $this->_rootProjectionNode;
         foreach ($expandSubPathSegments as $expandSubPathSegment) {
             $resourceSetWrapper = $currentNode->getResourceSetWrapper();
             $resourceType = $currentNode->getResourceType();
             $resourceProperty = $resourceType->resolveProperty($expandSubPathSegment);
             if (is_null($resourceProperty)) {
                 throw ODataException::createSyntaxError(Messages::expandProjectionParserPropertyNotFound($resourceType->getFullName(), $expandSubPathSegment, false));
             } else {
                 if ($resourceProperty->getTypeKind() != ResourceTypeKind::ENTITY) {
                     throw ODataException::createBadRequestError(Messages::expandProjectionParserExpandCanOnlyAppliedToEntity($resourceType->getFullName(), $expandSubPathSegment));
                 }
             }
             $resourceSetWrapper = $this->_providerWrapper->getResourceSetWrapperForNavigationProperty($resourceSetWrapper, $resourceType, $resourceProperty);
             if (is_null($resourceSetWrapper)) {
                 throw ODataException::createBadRequestError(Messages::badRequestInvalidPropertyNameSpecified($resourceType->getFullName(), $expandSubPathSegment));
             }
             $singleResult = $resourceProperty->isKindOf(ResourcePropertyKind::RESOURCE_REFERENCE);
             $resourceSetWrapper->checkResourceSetRightsForRead($singleResult);
             $pageSize = $resourceSetWrapper->getResourceSetPageSize();
             $internalOrderByInfo = null;
             if ($pageSize != 0 && !$singleResult) {
                 $this->_rootProjectionNode->setPagedExpandedResult(true);
                 $rt = $resourceSetWrapper->getResourceType();
                 //assert($rt != null)
                 $keys = array_keys($rt->getKeyProperties());
                 //assert(!empty($keys))
                 $orderBy = null;
                 foreach ($keys as $key) {
                     $orderBy = $orderBy . $key . ', ';
                 }
                 $orderBy = rtrim($orderBy, ', ');
                 $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $rt, $orderBy, $this->_providerWrapper);
             }
             $node = $currentNode->findNode($expandSubPathSegment);
             if (is_null($node)) {
                 $maxResultCount = $this->_providerWrapper->getConfiguration()->getMaxResultsPerCollection();
                 $node = new ExpandedProjectionNode($expandSubPathSegment, $resourceProperty, $resourceSetWrapper, $internalOrderByInfo, null, $pageSize == 0 ? null : $pageSize, $maxResultCount == PHP_INT_MAX ? null : $maxResultCount);
                 $currentNode->addNode($node);
             }
             $currentNode = $node;
         }
     }
 }
Example #3
0
 /**
  * Test whether order by parser identify and remove path duplication
  */
 public function testOrderByWithPathDuplication()
 {
     $northWindMetadata = NorthWindMetadata::Create();
     $configuration = new ServiceConfiguration($northWindMetadata);
     $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
     $providersWrapper = new ProvidersWrapper($northWindMetadata, $this->mockQueryProvider, $configuration, false);
     $resourceSetWrapper = $providersWrapper->resolveResourceSet('Order_Details');
     $resourceType = $resourceSetWrapper->getResourceType();
     $orderBy = 'Order/Price desc, Product/ProductName asc, Order/Price asc';
     $internalOrderInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $providersWrapper);
     //The orderby path Order/Price appears twice, but parser will consider only first path
     $orderByInfo = $internalOrderInfo->getOrderByInfo();
     //There are navigation (resource reference) properties in the orderby path so getNavigationPropertiesUsed should
     //not be null
     $naviUsed = $orderByInfo->getNavigationPropertiesUsed();
     $this->assertFalse(is_null($naviUsed));
     //3 path segment are there, but last one is duplicate of first one, so parser removes last one
     $this->assertEquals(count($naviUsed), 2);
     $this->assertTrue(is_array($naviUsed[0]));
     $this->assertTrue(is_array($naviUsed[1]));
     //one navgations used in first orderby 'Order'
     $this->assertEquals(count($naviUsed[0]), 1);
     //one navgations used in second orderby 'Prodcut'
     $this->assertEquals(count($naviUsed[1]), 1);
     $this->assertEquals($naviUsed[0][0]->getName(), 'Order');
     $this->assertEquals($naviUsed[1][0]->getName(), 'Product');
     $orderByPathSegments = $orderByInfo->getOrderByPathSegments();
     $this->assertEquals(count($orderByPathSegments), 2);
 }
Example #4
0
 /**
  * Process $orderby option, This function requires _processSkipAndTopOption
  * function to be already called as this function need to know whether 
  * client has requested for skip, top or paging is enabled for the 
  * requested resource in these cases function generates additional orderby
  * expression using keys.
  * 
  * @return void
  * 
  * @throws ODataException If any error occurs while parsing orderby option.
  */
 private function _processOrderBy()
 {
     $orderBy = $this->service->getHost()->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_ORDERBY);
     if (!is_null($orderBy)) {
         $this->_checkSetQueryApplicable();
     }
     $targetResourceType = $this->request->getTargetResourceType();
     //assert($targetResourceType != null)
     /**
      * We need to do sorting in the folowing cases, irrespective of 
      * $orderby clause is present or not.
      * 1. If $top or $skip is specified
      *     skip and take will be applied on sorted list only. If $skip 
      *     is specified then RequestDescription::getSkipCount will give 
      *     non-null value. If $top is specified then 
      *     RequestDescription::getTopCount will give non-null value.
      * 2. If server side paging is enabled for the requested resource
      *     If server-side paging is enabled for the requested resource then 
      *     RequestDescription::getTopCount will give non-null value.
      *      
      */
     if (!is_null($this->request->getSkipCount()) || !is_null($this->request->getTopCount())) {
         $orderBy = !is_null($orderBy) ? $orderBy . ', ' : null;
         $keys = array_keys($targetResourceType->getKeyProperties());
         //assert(!empty($keys))
         foreach ($keys as $key) {
             $orderBy = $orderBy . $key . ', ';
         }
         $orderBy = rtrim($orderBy, ', ');
     }
     if (!is_null($orderBy)) {
         $internalOrderByInfo = OrderByParser::parseOrderByClause($this->request->getTargetResourceSetWrapper(), $targetResourceType, $orderBy, $this->service->getProvidersWrapper());
         $this->request->setInternalOrderByInfo($internalOrderByInfo);
     }
 }
Example #5
0
 /**
  * test the creation of nextlink from an object.
  * Test building of link with guid sub-value
  */
 public function testCreationOfNextLink4()
 {
     $northWindMetadata = NorthWindMetadata::Create();
     $configuration = new ServiceConfiguration($northWindMetadata);
     $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
     $providersWrapper = new ProvidersWrapper($northWindMetadata, $this->mockQueryProvider, $configuration, false);
     $resourceSetWrapper = $providersWrapper->resolveResourceSet('Customers');
     $resourceType = $resourceSetWrapper->getResourceType();
     $orderBy = 'CustomerID, CustomerGuid';
     $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $providersWrapper);
     $skipToken = "null, guid'05b242e752eb46bd8f0e6568b72cd9a5'";
     $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
     $keyObject = $internalSkipTokenInfo->getKeyObject();
     $lastObject = new Customer2();
     $lastObject->CustomerID = 'ABC';
     $lastObject->CustomerGuid = '{05b242e7-52eb-46bd-8f0e-6568b72cd9a5}';
     $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
     $this->assertEquals($nextLink, "'ABC', guid'%7B05b242e7-52eb-46bd-8f0e-6568b72cd9a5%7D'");
 }
Example #6
0
 /**
  * This function perform the following tasks with the help of internal helper
  * functions
  * (1) Read the orderby clause and perform basic syntax errors
  * (2) Build 'Order By Tree', creates anonymous sorter function for each leaf 
  *     node and check for error
  * (3) Build 'OrderInfo' structure, holds information about the navigation 
  *     properties used in the orderby clause (if any) and orderby path if 
  *     IDSQP implementor want to perform sorting
  * (4) Build top level anonymous sorter function
  * (4) Release resources hold by the 'Order By Tree'
  * (5) Create 'InternalOrderInfo' structure, which wraps 'OrderInfo' and top 
  *     level sorter function 
  * 
  * @param ResourceSetWrapper           $resourceSetWrapper ResourceSetWrapper for the resource targeted by resource path.
  * @param ResourceType                 $resourceType       ResourceType for the resource targeted by resource path.
  * @param string                       $orderBy            The orderby clause.
  * @param ProvidersWrapper $providerWrapper    Reference to the wrapper for IDSQP and IDSMP impl.
  * 
  * @return InternalOrderByInfo
  * 
  * @throws ODataException If any error occur while parsing orderby clause
  */
 public static function parseOrderByClause(ResourceSetWrapper $resourceSetWrapper, ResourceType $resourceType, $orderBy, ProvidersWrapper $providerWrapper)
 {
     $orderByParser = new OrderByParser($providerWrapper);
     try {
         $orderByParser->_dummyObject = $resourceType->getInstanceType()->newInstance();
     } catch (\ReflectionException $reflectionException) {
         throw ODataException::createInternalServerError(Messages::orderByParserFailedToCreateDummyObject());
     }
     $orderByParser->_rootOrderByNode = new OrderByRootNode($resourceSetWrapper, $resourceType);
     $orderByPathSegments = $orderByParser->_readOrderBy($orderBy);
     $orderByParser->_buildOrderByTree($orderByPathSegments);
     $orderByParser->_createOrderInfo($orderByPathSegments);
     $orderByParser->_generateTopLevelComparisonFunction();
     //Recursively release the resources
     $orderByParser->_rootOrderByNode->free();
     //creates internal order info wrapper
     $internalOrderInfo = new InternalOrderByInfo($orderByParser->_orderByInfo, $orderByParser->_comparisonFunctions, $orderByParser->_topLevelComparisonFunction, $orderByParser->_dummyObject);
     unset($orderByParser->_orderByInfo);
     unset($orderByParser->_topLevelComparisonFunction);
     return $internalOrderInfo;
 }