/** * 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); }
/** * Creates new instance of Navigation * * @param ResourceType $resourceType The resource type for this navigation. * @throws \InvalidArgumentException when the resource type kind is not complex or entity */ public function __construct($resourceType) { if ($resourceType->getResourceTypeKind() != ResourceTypeKind::COMPLEX && $resourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY) { throw new \InvalidArgumentException(Messages::navigationInvalidResourceType()); } $this->_resourceType = $resourceType; }
/** * Creates new instance of ResourceSet * * @param string $name Name of the resource set (entity set) * @param ResourceType $resourceType ResourceType describing the resource * this entity set holds * * @throws \InvalidArgumentException */ public function __construct($name, ResourceType $resourceType) { if ($resourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY) { throw new \InvalidArgumentException(Messages::resourceSetContainerMustBeAssociatedWithEntityType()); } $this->_name = $name; $this->_resourceType = $resourceType; }
public function handleRequest() { $this->createProviders(); $this->getHost()->validateQueryParameters(); $requestMethod = $this->getOperationContext()->incomingRequest()->getMethod(); if ($requestMethod != HTTPRequestMethod::GET()) { throw ODataException::createNotImplementedError(Messages::onlyReadSupport($requestMethod)); } return UriProcessor::process($this); }
/** * Constructs a new instance of OrderByPathSegment * * @param OrderBySubPathSegment[] $orderBySubPathSegments Collection of orderby sub path segments for this path segment. * * @param boolean $isAscending sort order, * True for * ascending and * false * for desending. */ public function __construct($orderBySubPathSegments, $isAscending = true) { if (!is_array($orderBySubPathSegments)) { throw new \InvalidArgumentException(Messages::orderByPathSegmentOrderBySubPathSegmentArgumentShouldBeNonEmptyArray()); } if (empty($orderBySubPathSegments)) { throw new \InvalidArgumentException(Messages::orderByPathSegmentOrderBySubPathSegmentArgumentShouldBeNonEmptyArray()); } $this->_orderBySubPathSegments = $orderBySubPathSegments; $this->_isAscending = $isAscending; }
/** * Create new instance of AnonymousFunction * * @param array $parameters Array of parameters * @param string $code Body of the function */ public function __construct($parameters, $code) { $this->_parameters = $parameters; foreach ($this->_parameters as $parameter) { if (strpos($parameter, '$') !== 0) { throw new \InvalidArgumentException(Messages::anonymousFunctionParameterShouldStartWithDollarSymbol()); } } $this->_parametersAsString = implode(', ', $this->_parameters); $this->_code = $code; }
/** * Construct new instance of ResourceAssociationSet * * @param string $name Name of the association set * @param ResourceAssociationSetEnd $end1 First end set participating * in the association set * @param ResourceAssociationSetEnd $end2 Second end set participating * in the association set * * @throws \InvalidArgumentException */ public function __construct($name, ResourceAssociationSetEnd $end1, ResourceAssociationSetEnd $end2) { if (is_null($end1->getResourceProperty()) && is_null($end2->getResourceProperty())) { throw new \InvalidArgumentException(Messages::resourceAssociationSetResourcePropertyCannotBeBothNull()); } if ($end1->getResourceType() == $end2->getResourceType() && $end1->getResourceProperty() == $end2->getResourceProperty()) { throw new \InvalidArgumentException(Messages::resourceAssociationSetSelfReferencingAssociationCannotBeBiDirectional()); } $this->_name = $name; $this->_end1 = $end1; $this->_end2 = $end2; }
/** * 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; }
/** * @param string $name Name of the property * @param string $mimeType Mime type of the property * @param ResourcePropertyKind $kind The kind of property * @param ResourceType $propertyResourceType ResourceType of the property * * @throws InvalidArgumentException */ public function __construct($name, $mimeType, $kind, ResourceType $propertyResourceType) { if (!$this->_isValidResourcePropertyKind($kind)) { throw new InvalidArgumentException(Messages::resourcePropertyInvalidKindParameter('$kind')); } if (!$this->_isResourceKindValidForPropertyKind($kind, $propertyResourceType->getResourceTypeKind())) { throw new InvalidArgumentException(Messages::resourcePropertyPropertyKindAndResourceTypeKindMismatch('$kind', '$propertyResourceType')); } $this->_name = $name; $this->_mimeType = $mimeType; $this->_kind = $kind; $this->_propertyResourceType = $propertyResourceType; }
/** * Construct new instance of ResourceAssociationSetEnd * Note: The $resourceSet represents collection of an entity, The * $resourceType can be this entity's type or type of any of the * base resource of this entity, on which the navigation property * represented by $resourceProperty is defined. * * @param ResourceSet $resourceSet Resource set for the association end * @param ResourceType $resourceType Resource type for the association end * @param ResourceProperty $resourceProperty Resource property for the association end * * @throws \InvalidArgumentException */ public function __construct(ResourceSet $resourceSet, ResourceType $resourceType, $resourceProperty) { if (!is_null($resourceProperty) && !$resourceProperty instanceof ResourceProperty) { throw new \InvalidArgumentException(Messages::resourceAssociationSetPropertyMustBeNullOrInstanceofResourceProperty('$resourceProperty')); } if (!is_null($resourceProperty) && (is_null($resourceType->resolveProperty($resourceProperty->getName())) || $resourceProperty->getKind() != ResourcePropertyKind::RESOURCE_REFERENCE && $resourceProperty->getKind() != ResourcePropertyKind::RESOURCESET_REFERENCE)) { throw new \InvalidArgumentException(Messages::resourceAssociationSetEndPropertyMustBeNavigationProperty($resourceProperty->getName(), $resourceType->getFullName())); } if (!$resourceSet->getResourceType()->isAssignableFrom($resourceType) && !$resourceType->isAssignableFrom($resourceSet->getResourceType())) { throw new \InvalidArgumentException(Messages::resourceAssociationSetEndResourceTypeMustBeAssignableToResourceSet($resourceType->getFullName(), $resourceSet->getName())); } $this->_resourceSet = $resourceSet; $this->_resourceType = $resourceType; $this->_resourceProperty = $resourceProperty; }
/** * Construct new instance of ResourceAssociationTypeEnd * * @param string $name name of the end * @param ResourceType $resourceType resource type that the end * refers to * @param ResourceProperty|null $resourceProperty property of the end, can be * NULL if relationship is * uni-directional * @param ResourceProperty|null $fromProperty Property on the related end * that points to this end, can * be NULL if relationship is * uni-directional */ public function __construct($name, ResourceType $resourceType, $resourceProperty, $fromProperty) { if (is_null($resourceProperty) && is_null($fromProperty)) { throw new \InvalidArgumentException(Messages::resourceAssociationTypeEndBothPropertyCannotBeNull()); } if (!is_null($fromProperty) && !$fromProperty instanceof ResourceProperty) { throw new \InvalidArgumentException(Messages::resourceAssociationTypeEndPropertyMustBeNullOrInstanceofResourceProperty('$fromProperty')); } if (!is_null($resourceProperty) && !$resourceProperty instanceof ResourceProperty) { throw new \InvalidArgumentException(Messages::resourceAssociationTypeEndPropertyMustBeNullOrInstanceofResourceProperty('$$resourceProperty')); } $this->_name = $name; $this->_resourceType = $resourceType; $this->_resourceProperty = $resourceProperty; $this->_fromProperty = $fromProperty; }
/** * Constructs new instance of OrderByInfo * * @param OrderByPathSegment[] $orderByPathSegments Order by path segments * * @param array(array(ResourceProperty))|null $navigationPropertiesUsedInTheOrderByClause navigation properties used in the order by clause * * @throws \InvalidArgumentException */ public function __construct($orderByPathSegments, $navigationPropertiesUsedInTheOrderByClause) { if (!is_array($orderByPathSegments)) { throw new \InvalidArgumentException(Messages::orderByInfoPathSegmentsArgumentShouldBeNonEmptyArray()); } if (empty($orderByPathSegments)) { throw new \InvalidArgumentException(Messages::orderByInfoPathSegmentsArgumentShouldBeNonEmptyArray()); } if (!is_null($navigationPropertiesUsedInTheOrderByClause)) { if (!is_array($navigationPropertiesUsedInTheOrderByClause)) { throw new \InvalidArgumentException(Messages::orderByInfoNaviUsedArgumentShouldBeNullOrNonEmptyArray()); } if (empty($navigationPropertiesUsedInTheOrderByClause)) { throw new \InvalidArgumentException(Messages::orderByInfoNaviUsedArgumentShouldBeNullOrNonEmptyArray()); } } $this->_orderByPathSegments = $orderByPathSegments; $this->_navigationPropertiesUsedInTheOrderByClause = $navigationPropertiesUsedInTheOrderByClause; }
/** * Build nextpage link from the given object which will be the last object * in the page. * * @param mixed $lastObject Entity instance to build next page link from. * * @return string * * @throws ODataException If reflection exception occurs while accessing * property. */ public function buildNextPageLink($lastObject) { $nextPageLink = null; foreach ($this->_internalOrderByInfo->getOrderByPathSegments() as $orderByPathSegment) { $index = 0; $currentObject = $lastObject; $subPathSegments = $orderByPathSegment->getSubPathSegments(); $subPathCount = count($subPathSegments); foreach ($subPathSegments as &$subPathSegment) { $isLastSegment = $index == $subPathCount - 1; try { $dummyProperty = new \ReflectionProperty($currentObject, $subPathSegment->getName()); $currentObject = $dummyProperty->getValue($currentObject); if (is_null($currentObject)) { $nextPageLink .= 'null, '; break; } else { if ($isLastSegment) { $type = $subPathSegment->getInstanceType(); $value = $type->convertToOData($currentObject); $nextPageLink .= $value . ', '; } } } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::internalSkipTokenInfoFailedToAccessOrInitializeProperty($subPathSegment->getName())); } $index++; } } return rtrim($nextPageLink, ", "); }
/** * Populate map table * * @param AbstractExpression $nullCheckExpTree The expression to verfiy. * * @return void * * @throws ODataException */ private function _map($nullCheckExpTree) { if ($nullCheckExpTree instanceof LogicalExpression) { $this->_map($nullCheckExpTree->getLeft()); $this->_map($nullCheckExpTree->getRight()); } else { if ($nullCheckExpTree instanceof UnaryExpression) { $this->_map($nullCheckExpTree->getChild()); } else { if ($nullCheckExpTree instanceof FunctionCallExpression) { $param = $nullCheckExpTree->getParamExpressions(); $this->_map($param[0]); } else { if ($nullCheckExpTree instanceof PropertyAccessExpression) { $parent = $nullCheckExpTree; $key = null; do { $key = $parent->getResourceProperty()->getName() . '_' . $key; $parent = $parent->getParent(); } while ($parent != null); $this->_mapTable[$key] = $nullCheckExpTree; } else { throw ODataException::createSyntaxError(Messages::expressionParser2UnexpectedExpression(get_class($nullCheckExpTree))); } } } } }
/** * Returns the etag for the given resource. * Note: This function will not add W\" prefix and " suffix, its callers * repsonsability. * * @param mixed &$entryObject Resource for which etag value needs to * be returned * @param ResourceType &$resourceType Resource type of the $entryObject * * @return string|null ETag value for the given resource (with values encoded * for use in a URI) there are etag properties, NULL if * there is no etag property. */ protected function getETagForEntry(&$entryObject, ResourceType &$resourceType) { $eTag = null; $comma = null; foreach ($resourceType->getETagProperties() as $eTagProperty) { $type = $eTagProperty->getInstanceType(); self::assert(!is_null($type) && $type instanceof IType, '!is_null($type) && $type instanceof IType'); $value = null; try { //TODO #88...also this seems like dupe work $reflectionProperty = new \ReflectionProperty($entryObject, $eTagProperty->getName()); $value = $reflectionProperty->getValue($entryObject); } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::failedToAccessProperty($eTagProperty->getName(), $resourceType->getName())); } if (is_null($value)) { $eTag = $eTag . $comma . 'null'; } else { $eTag = $eTag . $comma . $type->convertToOData($value); } $comma = ','; } if (!is_null($eTag)) { // If eTag is made up of datetime or string properties then the above // IType::convertToOData will perform utf8 and url encode. But we don't // want this for eTag value. $eTag = urldecode(utf8_decode($eTag)); return rtrim($eTag, ','); } return null; }
/** * Gets and validate the ResourceAssociationSet instance for the * given source resource association end * This function first searches the ResourceAssociationSet cache, * if found return it otherwise * get the association set from metadata wrapper, * validate, cache and return it. * * @param ResourceSetWrapper $resourceSet Resource set of the * source association end * @param ResourceType $resourceType Resource type of the * source association end * @param ResourceProperty $navigationProperty Resource property of the * source association end * * @return ResourceAssociationSet|null The association set instance for the * given association set end, * NULL if the metadata wrapper * returns NULL * (either IDSMP implementation * returns null or * target resource set is invisible) * * @throws InvalidOperationException If validation of AssociationSet fails * @throws ODataException If validation fails at * ProvidersWrapper::getResourceAssociationSet */ private function _getResourceAssociationSet(ResourceSetWrapper $resourceSet, ResourceType $resourceType, ResourceProperty $navigationProperty) { $associationSetLookupKey = $resourceSet->getName() . '_' . $resourceType->getFullName() . '_' . $navigationProperty->getName(); if (array_key_exists($associationSetLookupKey, $this->_resourceAssociationSets)) { return $this->_resourceAssociationSets[$associationSetLookupKey]; } $resourceAssociationSet = $this->providersWrapper->getResourceAssociationSet($resourceSet, $resourceType, $navigationProperty); if (is_null($resourceAssociationSet)) { //Either the related ResourceSet is invisible or IDSMP implementation returns null return null; } /** * @var ResourceAssociationSetEnd */ $relatedEnd = $resourceAssociationSet->getRelatedResourceAssociationSetEnd($resourceSet->getResourceSet(), $resourceType, $navigationProperty); //For bidirectional relationship IDSMP::getResourceAssociationSet should //return same association set when called from either end if (!is_null($relatedEnd->getResourceProperty())) { //No need to check whether the following call returns NULL, //because the above call //providersWrapper::getResourceAssociationSet //causes the metadata wrapper to check the visibility of //related resource set and cache the corresponding wrapper. //If found invisible it would have return NULL, //which we are any way handling above. $relatedResourceSetWrapper = $this->providersWrapper->validateResourceSetAndGetWrapper($relatedEnd->getResourceSet()); $reverseResourceAssociationSet = $this->providersWrapper->getResourceAssociationSet($relatedResourceSetWrapper, $relatedEnd->getResourceType(), $relatedEnd->getResourceProperty()); if (is_null($reverseResourceAssociationSet) || !is_null($reverseResourceAssociationSet) && $resourceAssociationSet->getName() != $reverseResourceAssociationSet->getName()) { throw new InvalidOperationException(Messages::metadataAssociationTypeSetBidirectionalAssociationMustReturnSameResourceAssociationSetFromBothEnd()); } } $reverseAssociationSetLookupKey = null; if (!is_null($relatedEnd->getResourceProperty())) { $reverseAssociationSetLookupKey = $relatedEnd->getResourceSet()->getName() . '_' . $relatedEnd->getResourceProperty()->getResourceType()->getFullName() . '_' . $relatedEnd->getResourceProperty()->getName(); } else { $reverseAssociationSetLookupKey = $relatedEnd->getResourceSet()->getName() . '_Null_' . $resourceType->getFullName() . '_' . $navigationProperty->getName(); } if (array_key_exists($reverseAssociationSetLookupKey, $this->_resourceAssociationSets)) { throw new InvalidOperationException(Messages::metadataAssociationTypeSetMultipleAssociationSetsForTheSameAssociationTypeMustNotReferToSameEndSets($this->_resourceAssociationSets[$reverseAssociationSetLookupKey]->getName(), $resourceAssociationSet->getName(), $relatedEnd->getResourceSet()->getName())); } $this->_resourceAssociationSets[$associationSetLookupKey] = $resourceAssociationSet; $this->_resourceAssociationSets[$reverseAssociationSetLookupKey] = $resourceAssociationSet; return $resourceAssociationSet; }
/** * Parse primitive type literal. * * @param IType $targetType Expected type of the current literal. * * @return AbstractExpression * * @throws ODataException */ private function _parseTypedLiteral(IType $targetType) { $literal = $this->_lexer->getCurrentToken()->Text; $outVal = null; if (!$targetType->validate($literal, $outVal)) { throw ODataException::createSyntaxError(Messages::expressionParserUnrecognizedLiteral($targetType->getFullTypeName(), $literal, $this->_lexer->getCurrentToken()->Position)); } $result = new ConstantExpression($outVal, $targetType); $this->_lexer->nextToken(); return $result; }
public function testGetRelatedResourceSetReturnsArrayWhenQueryTypeIsEntitiesWithCount() { $orderBy = null; $top = 10; $skip = 10; $fakeQueryResult = new QueryResult(); $fakeQueryResult->count = 4; $fakeQueryResult->results = null; //null is not an array $fakeSourceEntity = new \stdClass(); Phockito::when($this->mockQueryProvider->getRelatedResourceSet(QueryType::ENTITIES_WITH_COUNT(), $this->mockResourceSet, $fakeSourceEntity, $this->mockResourceSet2, $this->mockResourceProperty, $this->mockFilterInfo, $orderBy, $top, $skip))->return($fakeQueryResult); $wrapper = $this->getMockedWrapper(); try { $wrapper->getRelatedResourceSet(QueryType::ENTITIES_WITH_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::queryProviderResultsMissing("IQueryProvider::getRelatedResourceSet", QueryType::ENTITIES_WITH_COUNT()), $ex->getMessage()); $this->assertEquals(500, $ex->getStatusCode()); } }
/** * Write a navigation property * * @param ResourceType $resourceType Resource type * @param ResourceAssociationType[] $associationTypesInResourceTypeNamespace Collection of association types for the given resource types * @param ResourceProperty $navigationProperty Navigation property * * @throws InvalidOperationException * @return void */ private function _writeNavigationProperty(ResourceType $resourceType, $associationTypesInResourceTypeNamespace, ResourceProperty $navigationProperty) { $associationTypeLookupName = $resourceType->getName() . '_' . $navigationProperty->getName(); if (!array_key_exists($associationTypeLookupName, $associationTypesInResourceTypeNamespace)) { throw new InvalidOperationException(Messages::metadataWriterNoResourceAssociationSetForNavigationProperty($navigationProperty->getName(), $resourceType->getName())); } $associationType = $associationTypesInResourceTypeNamespace[$associationTypeLookupName]; $thisEnd = $associationType->getResourceAssociationTypeEnd($resourceType, $navigationProperty); $relatedEnd = $associationType->getRelatedResourceAssociationSetEnd($resourceType, $navigationProperty); $this->_xmlWriter->startElement(ODataConstants::NAVIGATION_PROPERTY); $this->_xmlWriter->writeAttribute(ODataConstants::NAME, $navigationProperty->getName()); $this->_xmlWriter->writeAttribute(ODataConstants::RELATIONSHIP, $associationType->getFullName()); $this->_xmlWriter->writeAttribute(ODataConstants::FROM_ROLE, $thisEnd->getName()); $this->_xmlWriter->writeAttribute(ODataConstants::TO_ROLE, $relatedEnd->getName()); $this->_xmlWriter->endElement(); }
/** * Validates the given version in string format and returns the version as instance of Version * * @param string $versionHeader The DataServiceVersion or MaxDataServiceVersion header value * @param string $headerName The name of the header * * @return Version * * @throws ODataException If the version is malformed or not supported */ private static function parseVersionHeader($versionHeader, $headerName) { $libName = null; $versionHeader = trim($versionHeader); $libNameIndex = strpos($versionHeader, ';'); if ($libNameIndex !== false) { $libName = substr($versionHeader, $libNameIndex); } else { $libNameIndex = strlen($versionHeader); } $dotIndex = -1; for ($i = 0; $i < $libNameIndex; $i++) { if ($versionHeader[$i] == '.') { //Throw an exception if we find more than 1 dot if ($dotIndex != -1) { throw ODataException::createBadRequestError(Messages::requestDescriptionInvalidVersionHeader($versionHeader, $headerName)); } $dotIndex = $i; } else { if ($versionHeader[$i] < '0' || $versionHeader[$i] > '9') { throw ODataException::createBadRequestError(Messages::requestDescriptionInvalidVersionHeader($versionHeader, $headerName)); } } } $major = intval(substr($versionHeader, 0, $dotIndex)); $minor = 0; //Apparently the . is optional if ($dotIndex != -1) { if ($dotIndex == 0) { //If it starts with a ., throw an exception throw ODataException::createBadRequestError(Messages::requestDescriptionInvalidVersionHeader($versionHeader, $headerName)); } $minor = intval(substr($versionHeader, $dotIndex + 1, $libNameIndex)); } $version = new Version($major, $minor); //TODO: move this somewhere... /* $isSupportedVersion = false; foreach (self::getKnownDataServiceVersions() as $version1) { if ($version->compare($version1) == 0) { $isSupportedVersion = true; break; } } if (!$isSupportedVersion) { $availableVersions = null; foreach (self::getKnownDataServiceVersions() as $version1) { $availableVersions .= $version1->toString() . ', '; } $availableVersions = rtrim($availableVersions, ', '); throw ODataException::createBadRequestError( Messages::requestDescriptionUnSupportedVersion( $headerName, $versionHeader, $availableVersions ) ); } */ return $version; }
public function testProcessRequestForCollectionWithInlineCountWhenServiceVersionIs10() { $requestURI = new Url('http://host.com/data.svc/Collection/?$inlinecount=allpages'); Phockito::when($this->mockServiceHost->getAbsoluteRequestUri())->return($requestURI); //mock inline count as all pages Phockito::when($this->mockServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_INLINECOUNT))->return("allpages"); $this->fakeServiceConfig->setAcceptCountRequests(true); $this->fakeServiceConfig->setMaxDataServiceVersion(ProtocolVersion::V1()); try { UriProcessor::process($this->mockService); $this->fail("Expected exception not thrown"); } catch (ODataException $ex) { $expected = Messages::requestVersionTooLow("1.0", "2.0"); $this->assertEquals($expected, $ex->getMessage(), $ex->getTraceAsString()); } }
/** * Write value of a complex object. * Note: This method will not handle null complex value. * * @param mixed &$complexValue Complex object to write. * @param string $propertyName Name of the * complex property * whose value * need to be written. * @param ResourceType &$resourceType Expected type of the * property. * @param string $relativeUri Relative uri for the * complex type element. * @param ODataPropertyContent &$odataPropertyContent Content to write to. * * @return ResourceType The actual type of the complex object. * * @return void */ private function _complexObjectToContent(&$complexValue, $propertyName, ResourceType &$resourceType, $relativeUri, ODataPropertyContent &$odataPropertyContent) { $count = count($this->complexTypeInstanceCollection); for ($i = 0; $i < $count; $i++) { if ($this->complexTypeInstanceCollection[$i] === $complexValue) { throw new InvalidOperationException(Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName)); } } $this->complexTypeInstanceCollection[$count] =& $complexValue; //TODO function to resolve actual type from $resourceType $actualType = $resourceType; $odataEntry = null; $this->_writeObjectProperties($complexValue, $actualType, null, $relativeUri, $odataEntry, $odataPropertyContent); unset($this->complexTypeInstanceCollection[$count]); return $actualType; }
/** * This method verfies the client provided url query parameters and check whether * any of the odata query option specified more than once or check any of the * non-odata query parameter start will $ symbol or check any of the odata query * option specified with out value. If any of the above check fails throws * ODataException, else set _queryOptions member variable * * @return void * * @throws ODataException */ public function validateQueryParameters() { $queryOptions = $this->_operationContext->incomingRequest()->getQueryParameters(); reset($queryOptions); $namesFound = array(); while ($queryOption = current($queryOptions)) { $optionName = key($queryOption); $optionValue = current($queryOption); if (empty($optionName)) { if (!empty($optionValue)) { if ($optionValue[0] == '$') { if ($this->_isODataQueryOption($optionValue)) { throw ODataException::createBadRequestError(Messages::hostODataQueryOptionFoundWithoutValue($optionValue)); } else { throw ODataException::createBadRequestError(Messages::hostNonODataOptionBeginsWithSystemCharacter($optionValue)); } } } } else { if ($optionName[0] == '$') { if (!$this->_isODataQueryOption($optionName)) { throw ODataException::createBadRequestError(Messages::hostNonODataOptionBeginsWithSystemCharacter($optionName)); } if (array_search($optionName, $namesFound) !== false) { throw ODataException::createBadRequestError(Messages::hostODataQueryOptionCannotBeSpecifiedMoreThanOnce($optionName)); } if (empty($optionValue) && $optionValue !== '0') { throw ODataException::createBadRequestError(Messages::hostODataQueryOptionFoundWithoutValue($optionName)); } $namesFound[] = $optionName; } } next($queryOptions); } $this->_queryOptions = $queryOptions; }
/** * Process the resource path and query options of client's request uri. * * @param IService $service Reference to the data service instance. * * @return URIProcessor * * @throws ODataException */ public static function process(IService $service) { $absoluteRequestUri = $service->getHost()->getAbsoluteRequestUri(); $absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri(); if (!$absoluteServiceUri->isBaseOf($absoluteRequestUri)) { throw ODataException::createInternalServerError(Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri($absoluteRequestUri->getUrlAsString(), $absoluteServiceUri->getUrlAsString())); } $uriProcessor = new UriProcessor($service); //Parse the resource path part of the request Uri. $uriProcessor->request = ResourcePathProcessor::process($service); $uriProcessor->request->setUriProcessor($uriProcessor); //Parse the query string options of the request Uri. QueryProcessor::process($uriProcessor->request, $service); return $uriProcessor; }
/** * Assert that the given condition is true, if false throw * ODataException for syntax error * * @param boolean $condition The condition to assert * * @return void * * @throws ODataException */ private function _assertion($condition) { if (!$condition) { throw ODataException::createSyntaxError(Messages::syntaxError()); } }
/** * Build comparison function for this leaf node. * * @param string[] $ancestors Array of parent properties e.g. array('Orders', 'Customer', 'Customer_Demographics') * * @return AnonymousFunction */ public function buildComparisonFunction($ancestors) { if (count($ancestors) == 0) { throw new \InvalidArgumentException(Messages::orderByLeafNodeArgumentShouldBeNonEmptyArray()); } $parameterNames = null; $accessor1 = null; $accessor2 = null; $a = $this->_isAscending ? 1 : -1; foreach ($ancestors as $i => $anscestor) { if ($i == 0) { $parameterNames = array('$' . $anscestor . 'A', '$' . $anscestor . 'B'); $accessor1 = $parameterNames[0]; $accessor2 = $parameterNames[1]; $flag1 = '$flag1 = ' . 'is_null(' . $accessor1 . ') || '; $flag2 = '$flag2 = ' . 'is_null(' . $accessor2 . ') || '; } else { $accessor1 .= '->' . $anscestor; $accessor2 .= '->' . $anscestor; $flag1 .= 'is_null(' . $accessor1 . ')' . ' || '; $flag2 .= 'is_null(' . $accessor2 . ')' . ' || '; } } // $accessor1 .= '->' . $this->propertyName; // $accessor2 .= '->' . $this->propertyName; $accessor1 .= '->get' . ucfirst($this->propertyName) . '()'; $accessor2 .= '->get' . ucfirst($this->propertyName) . '()'; $flag1 .= 'is_null(' . $accessor1 . ')'; $flag2 .= 'is_null(' . $accessor2 . ')'; $code = "{$flag1}; \n {$flag2}; \n if(\$flag1 && \$flag2) { \n return 0;\n } else if (\$flag1) { \n return {$a}*-1;\n } else if (\$flag2) { \n return {$a}*1;\n }\n \n "; $type = $this->resourceProperty->getInstanceType(); if ($type instanceof DateTime) { $code .= " \$result = strtotime({$accessor1}) - strtotime({$accessor2});"; } else { if ($type instanceof String) { $code .= " \$result = strcmp({$accessor1}, {$accessor2});"; } else { if ($type instanceof Guid) { $code .= " \$result = strcmp({$accessor1}, {$accessor2});"; } else { $code .= " \$result = (({$accessor1} == {$accessor2}) ? 0 : (({$accessor1} > {$accessor2}) ? 1 : -1));"; } } } $code .= "\n return {$a}*\$result;"; $this->_anonymousFunction = new AnonymousFunction($parameterNames, $code); return $this->_anonymousFunction; }
/** * Validate current character is a digit. * * @return void */ private function _validateDigit() { if (!Char::isDigit($this->_ch)) { $this->_parseError(Messages::expressionLexerDigitExpected($this->_textPos)); } }
/** * To add a child node to the list of child nodes. * * @param ProjectionNode $node Node to add. * * @return void * * @throws InvalidArgumentException */ public function addNode($node) { if (!$node instanceof ProjectionNode && !$node instanceof ExpandedProjectionNode) { throw new \InvalidArgumentException(Messages::expandedProjectionNodeArgumentTypeShouldBeProjection()); } $this->_childNodes[$node->getPropertyName()] = $node; }
/** * Retrieve the complex type(s) used in the given resource type and cache them. * * @param ResourceType $resourceType The resource type to inspect * * @return void * * @throws InvalidOperationException If found any complex type bag property * with derived type(s) */ private function _populateComplexTypes(ResourceType $resourceType) { foreach ($resourceType->getPropertiesDeclaredOnThisType() as $property) { if ($property->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) { if ($property->isKindOf(ResourcePropertyKind::BAG)) { //Validate the bag complex type //as it should not have derived type if ($this->providersWrapper->hasDerivedTypes($resourceType)) { throw new InvalidOperationException(Messages::metadataResourceTypeSetBagOfComplexTypeWithDerivedTypes($resourceType->getFullName())); } } if ($this->_populateResourceTypes($property->getResourceType())) { $this->_populateComplexTypes($property->getResourceType()); } } } }
/** * Modify the 'Projection Tree' to include selection details * * @param array(array(string)) $selectPathSegments Collection of select * paths. * * @return void * * @throws ODataException If any error occurs while processing select * path segments */ private function _applySelectionToProjectionTree($selectPathSegments) { foreach ($selectPathSegments as $selectSubPathSegments) { $currentNode = $this->_rootProjectionNode; $subPathCount = count($selectSubPathSegments); foreach ($selectSubPathSegments as $index => $selectSubPathSegment) { if (!$currentNode instanceof RootProjectionNode && !$currentNode instanceof ExpandedProjectionNode) { throw ODataException::createBadRequestError(Messages::expandProjectionParserPropertyWithoutMatchingExpand($currentNode->getPropertyName())); } $currentNode->setSelectionFound(); $isLastSegment = $index == $subPathCount - 1; if ($selectSubPathSegment === '*') { $currentNode->setSelectAllImmediateProperties(); break; } $currentResourceType = $currentNode->getResourceType(); $resourceProperty = $currentResourceType->resolveProperty($selectSubPathSegment); if (is_null($resourceProperty)) { throw ODataException::createSyntaxError(Messages::expandProjectionParserPropertyNotFound($currentResourceType->getFullName(), $selectSubPathSegment, true)); } if (!$isLastSegment) { if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) { throw ODataException::createBadRequestError(Messages::expandProjectionParserBagPropertyAsInnerSelectSegment($currentResourceType->getFullName(), $selectSubPathSegment)); } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) { throw ODataException::createBadRequestError(Messages::expandProjectionParserPrimitivePropertyUsedAsNavigationProperty($currentResourceType->getFullName(), $selectSubPathSegment)); } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) { throw ODataException::createBadRequestError(Messages::expandProjectionParserComplexPropertyAsInnerSelectSegment($currentResourceType->getFullName(), $selectSubPathSegment)); } else { if ($resourceProperty->getKind() != ResourcePropertyKind::RESOURCE_REFERENCE && $resourceProperty->getKind() != ResourcePropertyKind::RESOURCESET_REFERENCE) { throw ODataException::createInternalServerError(Messages::expandProjectionParserUnexpectedPropertyType()); } } } } } $node = $currentNode->findNode($selectSubPathSegment); if (is_null($node)) { if (!$isLastSegment) { throw ODataException::createBadRequestError(Messages::expandProjectionParserPropertyWithoutMatchingExpand($selectSubPathSegment)); } $node = new ProjectionNode($selectSubPathSegment, $resourceProperty); $currentNode->addNode($node); } $currentNode = $node; if ($currentNode instanceof ExpandedProjectionNode && $isLastSegment) { $currentNode->setSelectionFound(); $currentNode->markSubtreeAsSelected(); } } } }