/// <summary> /// Creates the wrapper from the given service operation. /// </summary> /// <param name="serviceOperation">Service operation instance whose wrapper needs to get created.</param> /// <param name="resourceSetValidator">Resource set validator.</param> /// <param name="resourceTypeValidator">Resource type validator.</param> /// <returns>Wrapper for the given service operation.</returns> internal static ServiceOperationWrapper CreateServiceOperationWrapper( ServiceOperation serviceOperation, Func <ResourceSet, ResourceSetWrapper> resourceSetValidator, Func <ResourceType, ResourceType> resourceTypeValidator) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(serviceOperation != null, "serviceOperation != null"); Debug.Assert(serviceOperation.IsReadOnly, "The serviceOperation must be read-only by now."); Debug.Assert(resourceSetValidator != null, "resourceSetValidator != null"); Debug.Assert(resourceTypeValidator != null, "resourceTypeValidator != null"); ServiceOperationWrapper serviceOperationWrapper = new ServiceOperationWrapper(serviceOperation); #if DEBUG serviceOperationWrapper.isReadOnly = true; #endif serviceOperationWrapper.resourceSet = resourceSetValidator(serviceOperation.ResourceSet); ResourceType resultType = serviceOperation.ResultType; if (resultType == null || resultType.ResourceTypeKind == ResourceTypeKind.Primitive) { // No need to validate primitive resource types serviceOperationWrapper.resultType = resultType; } else { // Validate the resource type of the result serviceOperationWrapper.resultType = resourceTypeValidator(resultType); } return(serviceOperationWrapper); }
/// <summary> /// Validates if the service operation should be visible and is read only. If the service operation /// rights are set to None the service operation should not be visible. /// </summary> /// <param name="serviceOperation">Service operation to be validated.</param> /// <returns>Validated service operation, null if the service operation is not supposed to be visible.</returns> internal ServiceOperationWrapper ValidateServiceOperation(ServiceOperation serviceOperation) { DebugUtils.CheckNoExternalCallers(); ServiceOperationWrapper serviceOperationWrapper = null; if (serviceOperation != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. if (!this.serviceOperationCache.TryGetValue(serviceOperation.Name, out serviceOperationWrapper)) { ValidateServiceOperationReadOnly(serviceOperation); serviceOperationWrapper = ServiceOperationWrapper.CreateServiceOperationWrapper(serviceOperation, this.ValidateResourceSet, this.ValidateResourceType); this.serviceOperationCache[serviceOperation.Name] = serviceOperationWrapper; } } return(serviceOperationWrapper); }
/// <summary> /// Binds the service operation parameters from query options. /// </summary> /// <param name="serviceOperation">The service operation to bind the parameters for.</param> /// <returns>Enumeration of parameter values for the service operation.</returns> private IEnumerable<QueryNode> BindServiceOperationParameters(ServiceOperationWrapper serviceOperation) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); //// This is a copy of RequestUriProcessor.ReadOperationParameters if (serviceOperation.Parameters.Count == 0) { return null; } List<QueryNode> parameters = new List<QueryNode>(serviceOperation.Parameters.Count); for (int i = 0; i < serviceOperation.Parameters.Count; i++) { ServiceOperationParameter serviceOperationParameter = serviceOperation.Parameters[i]; Type parameterType = serviceOperationParameter.ParameterType.InstanceType; Type nonNullableParameterType = Nullable.GetUnderlyingType(parameterType) ?? parameterType; string parameterTextValue = this.ConsumeQueryOption(serviceOperationParameter.Name); object parameterValue; if (string.IsNullOrEmpty(parameterTextValue)) { if (!parameterType.IsClass && (parameterType == nonNullableParameterType)) { // The target parameter type is non-nullable, but we found null value, this usually means that the parameter is missing throw new ODataException(Strings.MetadataBinder_ServiceOperationParameterMissing(serviceOperation.Name, serviceOperationParameter.Name)); } parameterValue = null; } else { // We choose to be a little more flexible than with keys and // allow surrounding whitespace (which is never significant). parameterTextValue = parameterTextValue.Trim(); if (!UriPrimitiveTypeParser.TryUriStringToPrimitive(parameterTextValue, parameterType, out parameterValue)) { throw new ODataException(Strings.MetadataBinder_ServiceOperationParameterInvalidType(serviceOperationParameter.Name, parameterTextValue, serviceOperation.Name, serviceOperationParameter.ParameterType.FullName)); } } parameters.Add(new ConstantQueryNode() { Value = parameterValue }); } return new ReadOnlyCollection<QueryNode>(parameters); }
/// <summary> /// Binds a service operation segment. /// </summary> /// <param name="segmentToken">The segment which represents a service operation.</param> /// <param name="serviceOperation">The service operation to bind.</param> /// <returns>The bound node.</returns> private QueryNode BindServiceOperation(SegmentQueryToken segmentToken, ServiceOperationWrapper serviceOperation) { Debug.Assert(segmentToken != null, "segmentToken != null"); Debug.Assert(serviceOperation != null, "serviceOperation != null"); Debug.Assert(segmentToken.Name == serviceOperation.Name, "The segment represents a different service operation."); //// This is a metadata copy of the RequestUriProcessor.CreateSegmentForServiceOperation //// The WCF DS checks the verb in this place, we can't do that here // All service operations other than those returning IQueryable MUST NOT have () appended to the URI // V1/V2 behavior: if it's IEnumerable<T>, we do not allow () either, because it's not further composable. if (serviceOperation.ResultKind != ServiceOperationResultKind.QueryWithMultipleResults && segmentToken.NamedValues != null) { throw new ODataException(Strings.MetadataBinder_NonQueryableServiceOperationWithKeyLookup(segmentToken.Name)); } IEnumerable<QueryNode> serviceOperationParameters = this.BindServiceOperationParameters(serviceOperation); switch (serviceOperation.ResultKind) { case ServiceOperationResultKind.QueryWithMultipleResults: if (serviceOperation.ResultType.ResourceTypeKind != ResourceTypeKind.EntityType) { throw new ODataException(Strings.MetadataBinder_QueryServiceOperationOfNonEntityType(serviceOperation.Name, serviceOperation.ResultKind.ToString(), serviceOperation.ResultType.FullName)); } CollectionServiceOperationQueryNode collectionServiceOperationQueryNode = new CollectionServiceOperationQueryNode() { ServiceOperation = serviceOperation.ServiceOperation, Parameters = serviceOperationParameters }; if (segmentToken.NamedValues != null) { return this.BindKeyValues(collectionServiceOperationQueryNode, segmentToken.NamedValues); } else { return collectionServiceOperationQueryNode; } case ServiceOperationResultKind.QueryWithSingleResult: if (serviceOperation.ResultType.ResourceTypeKind != ResourceTypeKind.EntityType) { throw new ODataException(Strings.MetadataBinder_QueryServiceOperationOfNonEntityType(serviceOperation.Name, serviceOperation.ResultKind.ToString(), serviceOperation.ResultType.FullName)); } return new SingleValueServiceOperationQueryNode() { ServiceOperation = serviceOperation.ServiceOperation, Parameters = serviceOperationParameters }; case ServiceOperationResultKind.DirectValue: if (serviceOperation.ResultType.ResourceTypeKind == ResourceTypeKind.Primitive) { // Direct primitive values are composable, $value is allowed on them. return new SingleValueServiceOperationQueryNode() { ServiceOperation = serviceOperation.ServiceOperation, Parameters = serviceOperationParameters }; } else { // Direct non-primitive values are not composable at all return new UncomposableServiceOperationQueryNode() { ServiceOperation = serviceOperation.ServiceOperation, Parameters = serviceOperationParameters }; } case ServiceOperationResultKind.Enumeration: case ServiceOperationResultKind.Void: // Enumeration and void service operations are not composable return new UncomposableServiceOperationQueryNode() { ServiceOperation = serviceOperation.ServiceOperation, Parameters = serviceOperationParameters }; default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.MetadataBinder_BindServiceOperation)); } }
/// <summary>Checks whether this request has the specified rights.</summary> /// <param name="operation">Operation to check.</param> /// <param name="singleResult">Whether a single or multiple resources are requested.</param> /// <exception cref="DataServiceException">Thrown if <paramref name="singleResult"/> aren't available.</exception> internal static void CheckServiceRights(ServiceOperationWrapper operation, bool singleResult) { Debug.Assert(operation != null, "operation != null"); if (operation.ResultKind != ServiceOperationResultKind.Void) { ServiceOperationRights requiredRights = singleResult ? ServiceOperationRights.ReadSingle : ServiceOperationRights.ReadMultiple; CheckServiceRights(operation, requiredRights); } }
/// <summary>Checks whether this request has the specified rights.</summary> /// <param name="operation">Operation to check.</param> /// <param name="requiredRights">Required rights.</param> /// <exception cref="DataServiceException">Thrown if <paramref name="requiredRights"/> aren't available.</exception> internal static void CheckServiceRights(ServiceOperationWrapper operation, ServiceOperationRights requiredRights) { Debug.Assert(operation != null, "operation != null"); Debug.Assert(requiredRights != ServiceOperationRights.None, "requiredRights != EntitySetRights.None"); ServiceOperationRights effectiveRights = operation.Rights; if ((requiredRights & effectiveRights) == 0) { throw DataServiceException.CreateForbidden(); } }
/// <summary>Populate resource types returned by the given service operation.</summary> /// <param name="serviceOperation">Service operation to inspect</param> private void PopulateTypeForServiceOperation(ServiceOperationWrapper serviceOperation) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); ResourceType resultType = serviceOperation.ResultType; if (resultType != null && resultType.ResourceTypeKind == ResourceTypeKind.ComplexType) { this.AddVisibleResourceType(resultType); this.AddComplexPropertTypes(resultType); } Debug.Assert( resultType == null || resultType.ResourceTypeKind != ResourceTypeKind.EntityType || (this.resourceTypes.ContainsKey(resultType.Namespace) && this.resourceTypes[resultType.Namespace].Contains(resultType)), "If a result type is an entity type, it must be visible through an entity set."); }
/// <summary> /// Creates the wrapper from the given service operation. /// </summary> /// <param name="serviceOperation">Service operation instance whose wrapper needs to get created.</param> /// <param name="resourceSetValidator">Resource set validator.</param> /// <param name="resourceTypeValidator">Resource type validator.</param> /// <returns>Wrapper for the given service operation.</returns> public static ServiceOperationWrapper CreateServiceOperationWrapper( ServiceOperation serviceOperation, Func<ResourceSet, ResourceSetWrapper> resourceSetValidator, Func<ResourceType, ResourceType> resourceTypeValidator) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(serviceOperation != null, "serviceOperation != null"); Debug.Assert(serviceOperation.IsReadOnly, "The serviceOperation must be read-only by now."); Debug.Assert(resourceSetValidator != null, "resourceSetValidator != null"); Debug.Assert(resourceTypeValidator != null, "resourceTypeValidator != null"); ServiceOperationWrapper serviceOperationWrapper = new ServiceOperationWrapper(serviceOperation); #if DEBUG serviceOperationWrapper.isReadOnly = true; #endif serviceOperationWrapper.resourceSet = resourceSetValidator(serviceOperation.ResourceSet); ResourceType resultType = serviceOperation.ResultType; if (resultType == null || resultType.ResourceTypeKind == ResourceTypeKind.Primitive) { // No need to validate primitive resource types serviceOperationWrapper.resultType = resultType; } else { // Validate the resource type of the result serviceOperationWrapper.resultType = resourceTypeValidator(resultType); } return serviceOperationWrapper; }