Example #1
0
        private bool InitializeFunctionMetadata(Function function, EntitySet previousEntitySet, out EntitySet returningEntitySet)
        {
            // Push the function as the source of the response, and then
            // push further metadata describing the expected response, if appropriate
            this.MetadataStack.Push(function);

            EntitySet entitySet = null;
            bool      foundExpectedEntitySet;
            ServiceOperationAnnotation serviceOperationAnnotation = function.Annotations.OfType <ServiceOperationAnnotation>().SingleOrDefault();

            if (serviceOperationAnnotation == null)
            {
                foundExpectedEntitySet = function.TryGetExpectedServiceOperationEntitySet(out entitySet);
            }
            else
            {
                foundExpectedEntitySet = function.TryGetExpectedActionEntitySet(previousEntitySet, out entitySet);
            }

            if (foundExpectedEntitySet)
            {
                this.MetadataStack.Push(entitySet);
                returningEntitySet = entitySet;
                return(true);
            }

            if (function.ReturnType != null)
            {
                this.MetadataStack.Push(function.ReturnType);
            }

            returningEntitySet = null;
            return(false);
        }
Example #2
0
            /// <summary>
            /// Find out whether it is an action or WebInvoke request.
            /// </summary>
            /// <param name="uriString">The request uri string</param>
            /// <returns>Whether it is an action or WebInvoke request</returns>
            internal bool IsInvokeOperationRequest(string uriString)
            {
                int operationNameStartIndex = uriString.LastIndexOf('/');

                if (operationNameStartIndex > 0)
                {
                    string functionName          = uriString.Substring(operationNameStartIndex + 1);
                    int    queryOptionStartIndex = functionName.IndexOf('?');
                    if (queryOptionStartIndex > 0)
                    {
                        functionName = functionName.Substring(0, queryOptionStartIndex);
                    }

                    // find out if the function is an action or WebInvoke
                    Function function = this.model.Functions.Where(f => f.Name == functionName).SingleOrDefault();
                    if (function != null)
                    {
                        ServiceOperationAnnotation serviceOperationAnnotation = function.Annotations.OfType <ServiceOperationAnnotation>().SingleOrDefault();
                        if (serviceOperationAnnotation != null)
                        {
                            return(serviceOperationAnnotation.IsAction);
                        }

                        LegacyServiceOperationAnnotation legacyServiceOperationAnnotation = function.Annotations.OfType <LegacyServiceOperationAnnotation>().SingleOrDefault();
                        if (legacyServiceOperationAnnotation != null)
                        {
                            return(legacyServiceOperationAnnotation.Method == HttpVerb.Post);
                        }
                    }
                }

                return(false);
            }
Example #3
0
        /// <summary>
        /// Determines whether an entity set is expected to be returned from a function call
        /// </summary>
        /// <param name="function">the function being called</param>
        /// <param name="previousEntitySet">the binding entity set</param>
        /// <param name="returningEntitySet">the expected entity set, if appropriate</param>
        /// <returns>whether or not an entity set is expected</returns>
        public static bool TryGetExpectedActionEntitySet(this Function function, EntitySet previousEntitySet, out EntitySet returningEntitySet)
        {
            ExceptionUtilities.CheckArgumentNotNull(function, "function");

            ServiceOperationAnnotation serviceOperationAnnotation = function.Annotations.OfType <ServiceOperationAnnotation>().Single();
            EntityDataType             entityDataType             = function.ReturnType as EntityDataType;
            CollectionDataType         collectionDataType         = function.ReturnType as CollectionDataType;

            if (collectionDataType != null)
            {
                entityDataType = collectionDataType.ElementDataType as EntityDataType;
            }

            if (entityDataType != null)
            {
                if (serviceOperationAnnotation.EntitySetPath == null)
                {
                    returningEntitySet = function.Model.GetDefaultEntityContainer().EntitySets.Single(es => es.EntityType == entityDataType.Definition);
                }
                else
                {
                    string             navigationPropertyName = serviceOperationAnnotation.EntitySetPath.Substring(serviceOperationAnnotation.EntitySetPath.LastIndexOf('/') + 1);
                    NavigationProperty navigationProperty     = previousEntitySet.EntityType.AllNavigationProperties.Single(np => np.Name == navigationPropertyName);
                    var            associationSets            = function.Model.GetDefaultEntityContainer().AssociationSets.Where(a => a.AssociationType == navigationProperty.Association);
                    AssociationSet associationSet             = associationSets.Single(a => a.Ends.Any(e => e.EntitySet == previousEntitySet));
                    returningEntitySet = associationSet.Ends.Single(es => es.EntitySet != previousEntitySet).EntitySet;
                }

                return(true);
            }

            returningEntitySet = null;
            return(false);
        }
Example #4
0
        internal static ODataUri ConstructODataUriWithoutActionInformation(IEnumerable <EntitySet> entitySets, ODataUri uri)
        {
            var segmentsToInclude = new List <ODataUriSegment>();
            ServiceOperationAnnotation serviceOperationAnnotation = null;
            string setName = null;

            foreach (var segment in uri.Segments)
            {
                var functionSegment = segment as FunctionSegment;
                if (functionSegment != null && functionSegment.Function.IsAction())
                {
                    serviceOperationAnnotation = functionSegment.Function.Annotations.OfType <ServiceOperationAnnotation>().Single();
                    var toggleBooleanAnnotation = functionSegment.Function.Annotations.OfType <ToggleBoolPropertyValueActionAnnotation>().SingleOrDefault();

                    if (toggleBooleanAnnotation != null)
                    {
                        setName = toggleBooleanAnnotation.SourceEntitySet;
                    }

                    break;
                }

                segmentsToInclude.Add(segment);
            }

            if (!serviceOperationAnnotation.BindingKind.IsBound())
            {
                ExceptionUtilities.CheckObjectNotNull(setName, "Cannot find the set name that the action starts from");
                var sourceEntitySet = entitySets.Single(es => es.Name == setName);
                segmentsToInclude.Add(new EntitySetSegment(sourceEntitySet));
            }

            var actionlessODataUri = new ODataUri(segmentsToInclude);

            actionlessODataUri.CustomQueryOptions = uri.CustomQueryOptions;
            actionlessODataUri.Filter             = uri.Filter;
            actionlessODataUri.InlineCount        = uri.InlineCount;
            actionlessODataUri.OrderBy            = uri.OrderBy;
            actionlessODataUri.Skip      = uri.Skip;
            actionlessODataUri.SkipToken = uri.SkipToken;
            actionlessODataUri.Top       = uri.Top;
            EntitySet expectedEntitySet = null;

            ExceptionUtilities.Assert(actionlessODataUri.TryGetExpectedEntitySet(out expectedEntitySet), "Expected entity set not found");

            // expand all navigations for actionlessODataUri so that we do not need to send additional request when calculating expected action result
            foreach (NavigationProperty navigation in expectedEntitySet.EntityType.NavigationProperties)
            {
                actionlessODataUri.ExpandSegments.Add(new List <ODataUriSegment>()
                {
                    new NavigationSegment(navigation)
                });
            }

            return(actionlessODataUri);
        }
Example #5
0
        /// <summary>
        /// Declare the extension method given the function container class.
        /// </summary>
        /// <param name="functionExtensionClass">Function container class</param>
        /// <param name="func">Function to add</param>
        protected virtual void DeclareExtensionMethod(CodeTypeDeclaration functionExtensionClass, Function func)
        {
            // retrieve parameters and add the extension method
            ServiceOperationAnnotation actionAnnotation = func.Annotations.OfType <ServiceOperationAnnotation>().Single();

            ExceptionUtilities.Assert(actionAnnotation.BindingKind.IsBound(), "Action function cannot generate extension method with out function having a binding");
            FunctionParameter bindingTypeParameter = func.Parameters[0];

            CodeMemberMethod method = functionExtensionClass.AddExtensionMethod(
                func.Name,
                new CodeParameterDeclarationExpression(
                    this.GetParameterTypeOrFunctionReturnTypeReference(bindingTypeParameter.Annotations, bindingTypeParameter.DataType),
                    bindingTypeParameter.Name));

            // add non-binding type parameters
            foreach (var parameter in func.Parameters.Where(p => p.Name != bindingTypeParameter.Name))
            {
                method.Parameters.Add(
                    new CodeParameterDeclarationExpression(
                        this.GetParameterTypeOrFunctionReturnTypeReference(parameter.Annotations, parameter.DataType),
                        parameter.Name));
            }

            // throw exception if the client code method is called
            method.Statements.Add(
                new CodeThrowExceptionStatement(
                    new CodeObjectCreateExpression(
                        new CodeTypeReference(typeof(InvalidOperationException)),
                        new CodeExpression[] { })));

            // add method return type
            if (func.ReturnType != null)
            {
                method.ReturnType = this.GetParameterTypeOrFunctionReturnTypeReference(func.Annotations, func.ReturnType);
            }
        }
        private void VerifyEntityOperations(ODataRequest request, ODataResponse response, List <EntityInstance> entities, bool isTopLevelElement)
        {
            foreach (var entity in entities.Where(et => !et.IsNull))
            {
                var instanceEntityType = this.model.EntityTypes.Single(et => et.FullName == entity.FullTypeName);

                // look at each action/function bound to the same type as entity, ensure it is advertised correctly
                int numberOfDescriptorsVerified = 0;
                var functionsWithBindings       = this.model.Functions.Where(f => f.Annotations.OfType <ServiceOperationAnnotation>().Any(s => s.BindingKind.IsBound())).ToArray();
                foreach (Function function in functionsWithBindings)
                {
                    ServiceOperationAnnotation serviceOperationAnnotation = function.Annotations.OfType <ServiceOperationAnnotation>().SingleOrDefault();
                    if (serviceOperationAnnotation != null)
                    {
                        DataType       bindingParameterDataType = function.Parameters[0].DataType;
                        EntityDataType bindingEntityDataType    = bindingParameterDataType as EntityDataType;
                        if (bindingEntityDataType == null)
                        {
                            ExceptionUtilities.Assert(bindingParameterDataType is CollectionDataType, "Unsupported binding parameter data type.");
                            ExceptionUtilities.Assert(entity.ServiceOperationDescriptors.SingleOrDefault(d => d.Title == function.Name) == null, "Unexpected feed-bound action advertised in entry.");
                            continue;
                        }

                        // Check the base type as well as derived types
                        bool beginServiceOpMatchMatching = bindingEntityDataType.Definition.Model.EntityTypes.Where(t => t.IsKindOf(bindingEntityDataType.Definition)).Where(a => a.FullName.Equals(entity.FullTypeName)).Any();

                        if (beginServiceOpMatchMatching)
                        {
                            // find ServiceOperationDescriptor that matches the function
                            ServiceOperationDescriptor descriptor = entity.ServiceOperationDescriptors.SingleOrDefault(d => ExtractSimpleActionName(d.Title) == function.Name);
                            bool expectActionDescriptor           = true;
                            if (request.Uri.SelectSegments.Count > 0)
                            {
                                expectActionDescriptor = this.ExpectActionWithProjection(request.Uri, function, isTopLevelElement);
                            }

                            if (descriptor == null)
                            {
                                ExceptionUtilities.Assert(!expectActionDescriptor, "Missing service operation descriptor for " + function.Name);
                            }
                            else
                            {
                                expectActionDescriptor = this.VerifyIfOpenType(function, bindingEntityDataType, expectActionDescriptor);

                                ExceptionUtilities.Assert(expectActionDescriptor, "Unexpected service operation descriptor for " + function.Name);

                                // JSONLight will always add the type segment if its a derived type now
                                string derivedTypeFullName = string.Empty;
                                var    acceptHeaderValue   = response.Headers[HttpHeaders.ContentType];
                                if (bindingEntityDataType.Definition.BaseType != null)
                                {
                                    derivedTypeFullName = string.Concat(bindingEntityDataType.Definition.FullName, "/");
                                }

                                if (bindingEntityDataType.Definition.FullName != instanceEntityType.FullName)
                                {
                                    derivedTypeFullName = string.Concat(instanceEntityType.FullName, "/");
                                }

                                // check if there is a possible name collision between a property and an action, if so add the Entity Container name to the expected result
                                string containerName = this.BuildExpectedContainerName(function, bindingEntityDataType, instanceEntityType);
                                string expectedMetadataContainerName = string.Concat(function.Model.GetDefaultEntityContainer().Name, ".");

                                // When running in JSONLight descriptor returns full name, not partial
                                if (IsJsonLightResponse(acceptHeaderValue))
                                {
                                    containerName = string.Concat(function.Model.GetDefaultEntityContainer().Name, ".");

                                    expectedMetadataContainerName = containerName;
                                }

                                string expectedTarget   = string.Concat(entity.Id.TrimEnd('/'), "/", derivedTypeFullName, containerName, function.Name);
                                string expectedMetadata = string.Concat(((ServiceRootSegment)request.Uri.RootSegment).Uri.AbsoluteUri.TrimEnd('/'), "/", Endpoints.Metadata, "#", expectedMetadataContainerName, function.Name);

                                this.Assert(descriptor.Target == expectedTarget, "Expected target " + expectedTarget + " Actual " + descriptor.Target, request, response);
                                this.Assert(descriptor.Metadata == expectedMetadata, "Expected Metadata " + expectedMetadata + " Actual " + descriptor.Metadata, request, response);

                                // verify IsAction flag
                                this.Assert(serviceOperationAnnotation.IsAction == descriptor.IsAction, "Expected action " + serviceOperationAnnotation.IsAction + " Actual " + descriptor.IsAction, request, response);
                                numberOfDescriptorsVerified++;
                            }
                        }
                    }
                }

                this.Assert(numberOfDescriptorsVerified == entity.ServiceOperationDescriptors.Count, "Wrong number of action/function.", request, response);
                this.VerifyExpandedEntityOperations(entity, request, response);
            }
        }
Example #7
0
        /// <summary>
        /// Builds the statements to call execute on the client
        /// </summary>
        /// <param name="continuationExpression">continuation variable</param>
        /// <param name="functionExpression">functionexpression to build</param>
        /// <param name="httpMethod">Method to execute</param>
        /// <returns>List of code statements</returns>
        protected internal virtual IList <CodeStatement> BuildExecuteUriCall(CodeExpression continuationExpression, QueryCustomFunctionCallExpression functionExpression, HttpVerb httpMethod)
        {
            List <CodeStatement>  codeStatements           = new List <CodeStatement>();
            List <CodeExpression> inputParametersVariables = new List <CodeExpression>();

            ExceptionUtilities.Assert(functionExpression.Arguments.Count == functionExpression.Function.Parameters.Count, "FunctionExpression Argument must have the same number of parameters as Function");

            bool buildFirstParameter = true;

            if (functionExpression.Function.IsAction())
            {
                ServiceOperationAnnotation serviceOperationAnnotation = functionExpression.Function.Annotations.OfType <ServiceOperationAnnotation>().Single();
                if (serviceOperationAnnotation.BindingKind.IsBound())
                {
                    // Skip the first argument for bound action as this is the bound parameter
                    buildFirstParameter = false;

                    // build service operation parameters if calling action on servic operation results
                    QueryCustomFunctionCallExpression serviceOperationExpression = functionExpression.Arguments[0] as QueryCustomFunctionCallExpression;
                    if (serviceOperationExpression != null)
                    {
                        this.AppendFunctionParametersForExecuteUriCall(true, codeStatements, inputParametersVariables, serviceOperationExpression);
                    }
                }
            }

            this.AppendFunctionParametersForExecuteUriCall(buildFirstParameter, codeStatements, inputParametersVariables, functionExpression);

            codeStatements.Add(Code.DeclareVariable("uri", Code.Primitive(this.Uri)));

            if (functionExpression.Function.ReturnType != null)
            {
                var queryClrType = functionExpression.ExpressionType as IQueryClrType;

                var  collectionDataType = functionExpression.ExpressionType as QueryCollectionType;
                bool isSingleResult     = true;
                if (collectionDataType != null)
                {
                    queryClrType   = collectionDataType.ElementType as IQueryClrType;
                    isSingleResult = false;
                }

                ExceptionUtilities.CheckObjectNotNull(queryClrType, "Need to know the CLR Type to query it");

                // (IAsyncContinuation continuation, bool isAsync, string uriString, HttpMethod method, object[] inputParameters, DataServiceContext dataContext, ExpectedClientErrorBaseline clientExpectedError)
                codeStatements.Add(new CodeExpressionStatement(this.ResultComparerVariable.Call(
                                                                   "ExecuteUriAndCompare",
                                                                   new CodeTypeReference[] { Code.TypeRef(queryClrType.ClrType) },
                                                                   continuationExpression,
                                                                   Code.Primitive(this.IsAsync),
                                                                   Code.Variable("uri"),
                                                                   Code.ObjectValue(httpMethod),
                                                                   new CodeArrayCreateExpression(Code.TypeRef("Microsoft.OData.Client.OperationParameter"), inputParametersVariables.ToArray()),
                                                                   Code.Primitive(isSingleResult),
                                                                   this.ContextVariable,
                                                                   this.ExpectedClientErrorValue)));
            }
            else
            {
                // (IAsyncContinuation continuation, bool isAsync, string uriString, HttpMethod method, object[] inputParameters, DataServiceContext dataContext, ExpectedClientErrorBaseline clientExpectedError)
                codeStatements.Add(new CodeExpressionStatement(this.ResultComparerVariable.Call(
                                                                   "ExecuteUriAndCompare",
                                                                   continuationExpression,
                                                                   Code.Primitive(this.IsAsync),
                                                                   Code.Variable("uri"),
                                                                   Code.ObjectValue(httpMethod),
                                                                   new CodeArrayCreateExpression(Code.TypeRef("Microsoft.OData.Client.OperationParameter"), inputParametersVariables.ToArray()),
                                                                   this.ContextVariable,
                                                                   this.ExpectedClientErrorValue)));
            }

            return(codeStatements);
        }
        internal static IList <KeyValuePair <string, ITypedValue> > ConvertActionArgumentsToTypedValues(this Function function, ServiceOperationAnnotation actionAnnotation, IList <KeyValuePair <string, QueryExpression> > parameters)
        {
            var parameterValues = new List <KeyValuePair <string, ITypedValue> >();

            FunctionParameter boundParameter = null;

            if (actionAnnotation.BindingKind.IsBound())
            {
                boundParameter = function.Parameters[0];
            }

            foreach (var parameterPair in parameters)
            {
                var edmFunctionParameterDefinition = function.Parameters.Where(p => p.Name == parameterPair.Key).SingleOrDefault();
                ExceptionUtilities.CheckObjectNotNull(edmFunctionParameterDefinition, "Cannot find parameter '{0}' defined in function '{1}'", parameterPair.Key, function.Name);
                ExceptionUtilities.Assert(edmFunctionParameterDefinition != boundParameter, "Bound parameters MUST not be passed in as they will have already been built for the ODataUri was built");

                var primitiveDataType  = edmFunctionParameterDefinition.DataType as PrimitiveDataType;
                var collectionDataType = edmFunctionParameterDefinition.DataType as CollectionDataType;

                if (primitiveDataType != null)
                {
                    ExceptionUtilities.CheckObjectNotNull(primitiveDataType, "Expected a primitive data type not '{0}'", primitiveDataType);
                    var primitiveValue = ConvertToPrimitiveValue(parameterPair.Value, primitiveDataType);
                    parameterValues.Add(new KeyValuePair <string, ITypedValue>(edmFunctionParameterDefinition.Name, primitiveValue));
                }
                else if (collectionDataType != null)
                {
                    ExceptionUtilities.CheckObjectNotNull(collectionDataType, "expected a collection data type, actual '{0}", collectionDataType);
                    var collectionValue = parameterPair.Value.ConvertToMultiValue(collectionDataType.ElementDataType);
                    parameterValues.Add(new KeyValuePair <string, ITypedValue>(edmFunctionParameterDefinition.Name, collectionValue));
                }
                else
                {
                    var complexArgumentValue = parameterPair.Value;
                    var complexInstance      = complexArgumentValue.ConvertToComplexInstance();
                    parameterValues.Add(new KeyValuePair <string, ITypedValue>(edmFunctionParameterDefinition.Name, complexInstance));
                }
            }

            return(parameterValues);
        }