/** * 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; } } }
/** * 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); }
/** * 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); } }
/** * 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'"); }