/** * Parse the given skiptoken, validate it using the given InternalOrderByInfo * and generates instance of InternalSkipTokenInfo. * * @param ResourceType &$resourceType The resource type of the * resource targeted by the * resource path. * @param InternalOrderByInfo &$internalOrderByInfo The $orderby details. * @param string $skipToken The $skiptoken value. * * @return InternalSkipTokenInfo * * @throws ODataException */ public static function parseSkipTokenClause(ResourceType &$resourceType, InternalOrderByInfo &$internalOrderByInfo, $skipToken) { $tokenValueDescriptor = null; if (!KeyDescriptor::tryParseValuesFromSkipToken($skipToken, $tokenValueDescriptor)) { throw ODataException::createSyntaxError(Messages::skipTokenParserSyntaxError($skipToken)); } $orderByPathSegments = null; //$positionalValues are of type array(int, array(string, IType)) $positionalValues =& $tokenValueDescriptor->getPositionalValuesByRef(); $count = count($positionalValues); $orderByPathSegments = $internalOrderByInfo->getOrderByPathSegments(); $orderByPathCount = count($orderByPathSegments); if ($count != $orderByPathCount) { throw ODataException::createBadRequestError(Messages::skipTokenParserSkipTokenNotMatchingOrdering($count, $skipToken, $orderByPathCount)); } $i = 0; foreach ($orderByPathSegments as $orderByPathSegment) { $typeProvidedInSkipToken = $positionalValues[$i][1]; if (!$typeProvidedInSkipToken instanceof Null1) { $orderBySubPathSegments = $orderByPathSegment->getSubPathSegments(); $j = count($orderBySubPathSegments) - 1; $expectedType = $orderBySubPathSegments[$j]->getInstanceType(); if (!$expectedType->isCompatibleWith($typeProvidedInSkipToken)) { throw ODataException::createSyntaxError(Messages::skipTokenParserInCompatibleTypeAtPosition($skipToken, $expectedType->getFullTypeName(), $i, $typeProvidedInSkipToken->getFullTypeName())); } } $i++; } return new InternalSkipTokenInfo($internalOrderByInfo, $positionalValues, $resourceType); }
/** * Gets a related entity instance from an entity set identifed by a key * * @param ResourceSet $sourceResourceSet The entity set related to * the entity to be fetched. * @param object $sourceEntityInstance The related entity instance. * @param ResourceSet $targetResourceSet The entity set from which * entity needs to be fetched. * @param ResourceProperty $targetProperty The metadata of the target * property. * @param KeyDescriptor $keyDescriptor The key to identify the entity * to be fetched. * * @return object|null Returns entity instance if found else null */ public function getResourceFromRelatedResourceSet(ResourceSet $sourceResourceSet, $sourceEntityInstance, ResourceSet $targetResourceSet, ResourceProperty $targetProperty, KeyDescriptor $keyDescriptor) { $result = array(); $srcClass = get_class($sourceEntityInstance); $navigationPropName = $targetProperty->getName(); $key = null; foreach ($keyDescriptor->getValidatedNamedValues() as $keyName => $valueDescription) { $key = $key . $keyName . '=' . $valueDescription[0] . ' and '; } $key = rtrim($key, ' and '); if ($srcClass === 'Customer') { if ($navigationPropName === 'Orders') { $query = "SELECT * FROM Orders WHERE CustomerID = '{$sourceEntityInstance->CustomerID}' and {$key}"; $stmt = sqlsrv_query($this->_connectionHandle, $query); if ($stmt === false) { $errorAsString = self::_getSQLSRVError(); throw ODataException::createInternalServerError($errorAsString); } $result = $this->_serializeOrders($stmt); } else { die('Customer does not have navigation porperty with name: ' . $navigationPropName); } } else { if ($srcClass === 'Order') { if ($navigationPropName === 'Order_Details') { $query = "SELECT * FROM [Order Details] WHERE OrderID = {$sourceEntityInstance->OrderID}"; $stmt = sqlsrv_query($this->_connectionHandle, $query); if ($stmt === false) { $errorAsString = self::_getSQLSRVError(); throw ODataException::createInternalServerError($errorAsString); } $result = $this->_serializeOrderDetails($stmt); } else { die('Order does not have navigation porperty with name: ' . $navigationPropName); } } } return empty($result) ? null : $result[0]; }
public function testKeyDescriptorValidation() { $northWindMetadata = NorthWindMetadata::Create(); $orderDetailsResourceType = $northWindMetadata->resolveResourceType('Order_Details'); $this->assertFalse(is_null($orderDetailsResourceType)); $keyDescriptor = null; //Test with a valid named value key predicate $keyPredicate = 'ProductID=11, OrderID=2546'; $validPredicateSyntax = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor); $this->assertTrue($validPredicateSyntax); $this->assertFalse(is_null($keyDescriptor)); $keyDescriptor->validate('Order_Details(ProductID=11, OrderID=2546)', $orderDetailsResourceType); $validatedNamedValues = $keyDescriptor->getValidatedNamedValues(); $this->assertTrue(array_key_exists('ProductID', $validatedNamedValues)); $this->assertTrue(array_key_exists('OrderID', $validatedNamedValues)); $productVal = $validatedNamedValues['ProductID']; $orderVal = $validatedNamedValues['OrderID']; $this->assertEquals($productVal[0], 11); $this->assertEquals($orderVal[0], 2546); $keyDescriptor = null; //Test with a valid positional value key predicate $keyPredicate = '11, 2546'; $validPredicateSyntax = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor); $this->assertTrue($validPredicateSyntax); $this->assertFalse(is_null($keyDescriptor)); $keyDescriptor->validate('Order_Details(11, 2546)', $orderDetailsResourceType); $validatedNamedValues = $keyDescriptor->getValidatedNamedValues(); $this->assertEquals(count($validatedNamedValues), 2); $this->assertTrue(array_key_exists('ProductID', $validatedNamedValues)); $this->assertTrue(array_key_exists('OrderID', $validatedNamedValues)); $productVal = $validatedNamedValues['ProductID']; $orderVal = $validatedNamedValues['OrderID']; $this->assertEquals($productVal[0], 11); $this->assertEquals($orderVal[0], 2546); //Test for key count $keyDescriptor = null; $keyPredicate = 'ProductID=11'; $validPredicateSyntax = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor); try { $keyDescriptor->validate('Order_Details(ProductID=11)', $orderDetailsResourceType); $this->fail('An expected ODataException for predicate key count has not been thrown'); } catch (ODataException $exception) { $this->assertStringEndsWith(' expect 2 keys but 1 provided', $exception->getMessage()); } //test for missing key $keyDescriptor = null; $keyPredicate = 'ProductID=11, OrderID1=2546'; $validPredicateSyntax = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor); try { $keyDescriptor->validate('Order_Details(ProductID=11, OrderID1=2546)', $orderDetailsResourceType); $this->fail('An expected ODataException for missing of keys in the predicate has not been thrown'); } catch (ODataException $exception) { $this->assertStringEndsWith('The key predicate expect the keys \'ProductID, OrderID\'', $exception->getMessage()); } //test for key type $keyDescriptor = null; $keyPredicate = 'ProductID=11.12, OrderID=2546'; $validPredicateSyntax = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor); try { $keyDescriptor->validate('Order_Details(ProductID=11.12, OrderID=2546)', $orderDetailsResourceType); $this->fail('An expected ODataException for type incompactibility has not been thrown'); } catch (ODataException $exception) { $this->assertStringEndsWith('The value of key property \'ProductID\' should be of type Edm.Int32, given Edm.Double', $exception->getMessage()); } //test for key type $keyDescriptor = null; $keyPredicate = '11, \'ABCD\''; $validPredicateSyntax = KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor); try { $keyDescriptor->validate('Order_Details(11, \'ABCD\')', $orderDetailsResourceType); $this->fail('An expected ODataException for type incompactibility has not been thrown'); } catch (ODataException $exception) { $this->assertStringEndsWith('The value of key property \'OrderID\' at position 1 should be of type Edm.Int32, given Edm.String', $exception->getMessage()); } }
/** * Creates an instance of KeyDescriptor by parsing a key predicate, also * validates the KeyDescriptor * * @param string $segment The uri segment in the form identifier * (keyPredicate) * @param ResourceType $resourceType The Resource type whose keys need to * be parsed * @param string $keyPredicate The key predicate to parse and generate * KeyDescriptor for * * @return KeyDescriptor Describes the key values in the $keyPredicate * * @throws ODataException Exception if any error occurs while parsing and * validating the key predicate */ private function _createKeyDescriptor($segment, ResourceType $resourceType, $keyPredicate) { /** * @var KeyDescriptor $keyDescriptor */ $keyDescriptor = null; if (!KeyDescriptor::tryParseKeysFromKeyPredicate($keyPredicate, $keyDescriptor)) { throw ODataException::createSyntaxError(Messages::syntaxError()); } // Note: Currently WCF Data Service does not support multiple // 'Positional values' so Order_Details(10248, 11) is not valid if (!$keyDescriptor->isEmpty() && !$keyDescriptor->areNamedValues() && $keyDescriptor->valueCount() > 1) { throw ODataException::createSyntaxError(Messages::segmentParserKeysMustBeNamed($segment)); } $keyDescriptor->validate($segment, $resourceType); return $keyDescriptor; }
/** * Gets a related entity instance from an entity set identifed by a key * * @param ResourceSet $sourceResourceSet The entity set related to * the entity to be fetched. * @param object $sourceEntityInstance The related entity instance. * @param ResourceSet $targetResourceSet The entity set from which * entity needs to be fetched. * @param ResourceProperty $targetProperty The metadata of the target * property. * @param KeyDescriptor $keyDescriptor The key to identify the entity * to be fetched. * * @return object|null Returns entity instance if found else null */ public function getResourceFromRelatedResourceSet(ResourceSet $sourceResourceSet, $sourceEntityInstance, ResourceSet $targetResourceSet, ResourceProperty $targetProperty, KeyDescriptor $keyDescriptor) { $result = array(); $srcClass = get_class($sourceEntityInstance); $navigationPropName = $targetProperty->getName(); $keys = array(); $namedKeyValues = $keyDescriptor->getValidatedNamedValues(); foreach ($namedKeyValues as $key => $value) { $keys[] = "{$key} = '{$value['0']}' "; } $conditionStr = implode(' AND ', $keys); switch (true) { case $srcClass == 'Post': if ($navigationPropName == 'Tags') { $query = "SELECT t.*, tt.description" . " FROM wp_terms AS t" . " INNER JOIN wp_term_taxonomy AS tt" . " ON tt.term_id = t.term_id" . " INNER JOIN wp_term_relationships AS tr" . " ON tr.term_taxonomy_id = tt.term_taxonomy_id" . " WHERE tt.taxonomy IN ('post_tag')" . " AND tr.object_id IN ({$sourceEntityInstance->PostID})" . " AND tt.term_id = " . $namedKeyValues['TagID'][0]; $stmt = mysql_query($query); $result = $this->_serializeTags($stmt); } elseif ($navigationPropName == 'Categories') { $query = "SELECT t.*, tt.description" . " FROM wp_terms AS t" . " INNER JOIN wp_term_taxonomy AS tt" . " ON tt.term_id = t.term_id" . " INNER JOIN wp_term_relationships AS tr" . " ON tr.term_taxonomy_id = tt.term_taxonomy_id" . " WHERE tt.taxonomy IN ('category')" . " AND tr.object_id IN ({$sourceEntityInstance->PostID})" . " AND tt.term_id = " . $namedKeyValues['CategoryID'][0]; $stmt = mysql_query($query); $result = $this->_serializeCategories($stmt); } else { if ($navigationPropName == 'Comments') { $query = "SELECT * FROM `wp_comments`" . " WHERE comment_approved = 1" . " AND comment_post_ID = {$sourceEntityInstance->PostID}" . " AND comment_ID = " . $namedKeyValues['CommentID'][0]; $stmt = mysql_query($query); $result = $this->_serializeComments($stmt); } else { die('Post does not have navigation porperty with name: ' . $navigationPropName); } } break; case $srcClass == 'Tag': if ($navigationPropName == 'Posts') { $query = "SELECT p . *" . " FROM wp_posts AS p" . " INNER JOIN wp_term_relationships AS tr" . " ON p.ID = tr.object_id" . " INNER JOIN wp_term_taxonomy AS tt" . " ON tr.term_taxonomy_id = tt.term_taxonomy_id" . " WHERE tt.term_id = {$sourceEntityInstance->TagID}" . " AND p.post_type = 'post'" . " AND p.post_status = 'publish'" . " AND p.ID = " . $namedKeyValues['PostID'][0]; $stmt = mysql_query($query); $result = $this->_serializePosts($stmt); } else { die('Tag does not have navigation porperty with name: ' . $navigationPropName); } break; case $srcClass == 'Category': if ($navigationPropName == 'Posts') { $query = "SELECT p . *" . " FROM wp_posts AS p" . " INNER JOIN wp_term_relationships AS tr" . " ON p.ID = tr.object_id" . " INNER JOIN wp_term_taxonomy AS tt" . " ON tr.term_taxonomy_id = tt.term_taxonomy_id" . " WHERE tt.term_id = {$sourceEntityInstance->CategoryID}" . " AND p.post_type = 'post'" . " AND p.post_status = 'publish'" . " AND p.ID = " . $namedKeyValues['PostID'][0]; $stmt = mysql_query($query); $result = $this->_serializePosts($stmt); } else { die('Category does not have navigation porperty with name: ' . $navigationPropName); } break; case $srcClass == 'Comment': die('Comment does not have navigation porperty with name: ' . $navigationPropName); break; case $srcClass == 'User': if ($navigationPropName == 'Posts') { $query = "SELECT * FROM `wp_posts` WHERE" . " wp_posts.post_type = 'post'" . " AND wp_posts.post_status = 'publish'" . " AND wp_posts.post_author = {$sourceEntityInstance->UserID}" . " AND wp_posts.ID = " . $namedKeyValues['PostID'][0]; $stmt = mysql_query($query); $result = $this->_serializePosts($stmt); } elseif ($navigationPropName == 'Comments') { $query = "SELECT * FROM `wp_comments`" . " WHERE comment_approved = 1" . " AND wp_comments.user_id = {$sourceEntityInstance->UserID}" . " AND wp_comments.comment_ID = " . $namedKeyValues['CommentID'][0]; $stmt = mysql_query($query); $result = $this->_serializeComments($stmt); } else { die('User does not have navigation porperty with name: ' . $navigationPropName); } break; } mysql_free_result($stmt); return empty($result) ? null : $result[0]; }
/** * Validate the given entity instance. * * @param object $entityInstance Entity instance to validate * @param ResourceSet &$resourceSet Resource set to which the entity * instance belongs to. * @param KeyDescriptor &$keyDescriptor The key descriptor. * @param string $methodName Method from which this function * invoked. * * @return void * * @throws ODataException */ private function _validateEntityInstance($entityInstance, ResourceSet &$resourceSet, KeyDescriptor &$keyDescriptor, $methodName) { if (is_null($entityInstance)) { throw ODataException::createResourceNotFoundError($resourceSet->getName()); } // lion: if ($_SERVER['REQUEST_METHOD'] == HTTPRequestMethod::DELETE()) { return; } $entityName = $resourceSet->getResourceType()->getInstanceType()->getName(); if (!is_object($entityInstance) || !$entityInstance instanceof $entityName) { throw ODataException::createInternalServerError(Messages::providersWrapperIDSQPMethodReturnsUnExpectedType($entityName, $methodName)); } foreach ($keyDescriptor->getValidatedNamedValues() as $keyName => $valueDescription) { try { $keyProperty = new \ReflectionProperty($entityInstance, $keyName); $keyValue = $keyProperty->getValue($entityInstance); if (is_null($keyValue)) { throw ODataException::createInternalServerError(Messages::providersWrapperIDSQPMethodReturnsInstanceWithNullKeyProperties($methodName)); } $convertedValue = $valueDescription[1]->convert($valueDescription[0]); if ($keyValue != $convertedValue) { throw ODataException::createInternalServerError(Messages::providersWrapperIDSQPMethodReturnsInstanceWithNonMatchingKeys($methodName)); } } catch (\ReflectionException $reflectionException) { //throw ODataException::createInternalServerError( // Messages::orderByParserFailedToAccessOrInitializeProperty( // $resourceProperty->getName(), $resourceType->getName() // ) //); } } }