private static string GetRootElementName(ODataPath path)
        {
            if (path != null)
            {
                ODataPathSegment lastSegment = path.Segments.LastOrDefault();
                if (lastSegment != null)
                {
                    OperationSegment actionSegment = lastSegment as OperationSegment;

                    if (actionSegment != null)
                    {
                        IEdmAction action = actionSegment.Operations.Single() as IEdmAction;
                        if (action != null)
                        {
                            return(action.Name);
                        }
                    }

                    PropertySegment propertyAccessSegment = lastSegment as PropertySegment;
                    if (propertyAccessSegment != null)
                    {
                        return(propertyAccessSegment.Property.Name);
                    }
                }
            }

            return(null);
        }
        // Determine action from readContext
        // This function is copied from default ODataActionPayloadDeserializer because it cannot be inherited
        private static IEdmAction GetAction(ODataDeserializerContext readContext)
        {
            if (readContext == null)
            {
                throw new ArgumentNullException(nameof(readContext));
            }

            ODataPath path = readContext.Path;

            if (path == null || path.Segments.Count == 0)
            {
                throw new SerializationException("OData Path is missing");
            }

            IEdmAction action = null;

            if (path.PathTemplate == "~/unboundaction")
            {
                // only one segment, it may be an unbound action
                OperationImportSegment unboundActionSegment = path.Segments.Last() as OperationImportSegment;
                if (unboundActionSegment != null)
                {
                    IEdmActionImport actionImport = unboundActionSegment.OperationImports.First() as IEdmActionImport;
                    if (actionImport != null)
                    {
                        action = actionImport.Action;
                    }
                }
            }
            else
            {
                // otherwise, it may be a bound action
                OperationSegment actionSegment = path.Segments.Last() as OperationSegment;
                if (actionSegment != null)
                {
                    action = actionSegment.Operations.First() as IEdmAction;
                }
            }

            if (action == null)
            {
                throw new SerializationException("Request is not an action invocation");
            }

            return(action);
        }
        // This function is used to determine whether an OData path includes operation (import) path segments.
        // We use this function to make sure the value of ODataUri.Path in ODataMessageWriterSettings is null
        // when any path segment is an operation. ODL will try to calculate the context URL if the ODataUri.Path
        // equals to null.
        private static bool IsOperationPath(ODataPath path)
        {
            if (path == null)
            {
                return(false);
            }

            foreach (ODataPathSegment segment in path.Segments)
            {
                if (segment is OperationSegment ||
                    segment is OperationImportSegment)
                {
                    return(true);
                }
            }

            return(false);
        }
        static bool IsNextSegmentOfType <T>(AspNet.OData.Routing.ODataPath path, int currentPos)
        {
            var maxPos = path.Segments.Count - 1;

            if (maxPos <= currentPos)
            {
                return(false);
            }

            var nextSegment = path.Segments[currentPos + 1];

            if (nextSegment is T)
            {
                return(true);
            }

            if (nextSegment is TypeSegment && maxPos >= currentPos + 2 && path.Segments[currentPos + 2] is T)
            {
                return(true);
            }

            return(false);
        }
 static bool IsNextSegmentKey(AspNet.OData.Routing.ODataPath path, int currentPos)
 {
     return(IsNextSegmentOfType <KeySegment>(path, currentPos));
 }
        internal static IScopesEvaluator ExtractPermissionsForRequest(this IEdmModel model, string method, AspNet.OData.Routing.ODataPath odataPath)
        {
            var template = odataPath.PathTemplate;
            ODataPathSegment prevSegment = null;

            var segments = new List <ODataPathSegment>();

            // this combines the permission scopes across path segments
            // with a logical AND
            var permissionsChain = new WithAndScopesCombiner();

            var lastSegmentIndex = odataPath.Segments.Count - 1;

            if (template.EndsWith("$ref", StringComparison.OrdinalIgnoreCase))
            {
                // for ref segments, we apply the permission of the entity that contains the navigation property
                // e.g. for GET Customers(10)/Products/$ref, we apply the read key permissions of Customers
                // for GET TopCustomer/Products/$ref, we apply the read permissions of TopCustomer
                // for DELETE Customers(10)/Products(10)/$ref we apply the update permissions of Customers
                lastSegmentIndex = odataPath.Segments.Count - 2;
                while (!(odataPath.Segments[lastSegmentIndex] is KeySegment || odataPath.Segments[lastSegmentIndex] is SingletonSegment || odataPath.Segments[lastSegmentIndex] is NavigationPropertySegment) &&
                       lastSegmentIndex > 0)
                {
                    lastSegmentIndex--;
                }
            }

            for (int i = 0; i <= lastSegmentIndex; i++)
            {
                var segment = odataPath.Segments[i];

                if (segment is EntitySetSegment ||
                    segment is SingletonSegment ||
                    segment is NavigationPropertySegment ||
                    segment is OperationSegment ||
                    segment is OperationImportSegment ||
                    segment is KeySegment ||
                    segment is PropertySegment)
                {
                    var parent           = prevSegment;
                    var isPropertyAccess = IsNextSegmentOfType <PropertySegment>(odataPath, i) ||
                                           IsNextSegmentOfType <NavigationPropertyLinkSegment>(odataPath, i) ||
                                           IsNextSegmentOfType <NavigationPropertySegment>(odataPath, i);
                    prevSegment = segment;
                    segments.Add(segment);

                    // if nested segment, extract navigation restrictions of root

                    // else extract entity/set  restrictions
                    if (segment is EntitySetSegment entitySetSegment)
                    {
                        // if Customers(key), then we'll handle it when we reach the key segment
                        // so that we can properly handle ReadByKeyRestrictions
                        if (IsNextSegmentKey(odataPath, i))
                        {
                            continue;
                        }

                        // if Customers/UnboundFunction, then we'll handle it when we reach the operation segment
                        if (IsNextSegmentOfType <OperationSegment>(odataPath, i))
                        {
                            continue;
                        }

                        IScopesEvaluator permissions;

                        permissions = GetNavigationPropertyCrudPermisions(
                            segments,
                            false,
                            model,
                            method);

                        if (permissions is DefaultScopesEvaluator)
                        {
                            permissions = GetNavigationSourceCrudPermissions(entitySetSegment.EntitySet, model, method);
                        }

                        var handler = new WithOrScopesCombiner(permissions);
                        permissionsChain.Add(handler);
                    }
                    else if (segment is SingletonSegment singletonSegment)
                    {
                        // if Customers/UnboundFunction, then we'll handle it when we reach the operation segment
                        if (IsNextSegmentOfType <OperationSegment>(odataPath, i))
                        {
                            continue;
                        }

                        if (isPropertyAccess)
                        {
                            var propertyPermissions = GetSingletonPropertyOperationPermissions(singletonSegment.Singleton, model, method);
                            permissionsChain.Add(new WithOrScopesCombiner(propertyPermissions));
                        }
                        else
                        {
                            var permissions = GetNavigationSourceCrudPermissions(singletonSegment.Singleton, model, method);
                            permissionsChain.Add(new WithOrScopesCombiner(permissions));
                        }
                    }
                    else if (segment is KeySegment keySegment)
                    {
                        // if Customers/UnboundFunction, then we'll handle it when we reach the operation segment
                        if (IsNextSegmentOfType <OperationSegment>(odataPath, i))
                        {
                            continue;
                        }

                        var entitySet   = keySegment.NavigationSource as IEdmEntitySet;
                        var permissions = isPropertyAccess ?
                                          GetEntityPropertyOperationPermissions(entitySet, model, method) :
                                          GetEntityCrudPermissions(entitySet, model, method);

                        var evaluator = new WithOrScopesCombiner(permissions);


                        if (parent is NavigationPropertySegment)
                        {
                            var nestedPermissions = isPropertyAccess ?
                                                    GetNavigationPropertyPropertyOperationPermisions(segments, isTargetByKey: true, model, method) :
                                                    GetNavigationPropertyCrudPermisions(segments, isTargetByKey: true, model, method);

                            evaluator.Add(nestedPermissions);
                        }


                        permissionsChain.Add(evaluator);
                    }
                    else if (segment is NavigationPropertySegment navSegment)
                    {
                        // if Customers/UnboundFunction, then we'll handle it when we reach there
                        if (IsNextSegmentOfType <OperationSegment>(odataPath, i))
                        {
                            continue;
                        }


                        // if Customers(key), then we'll handle it when we reach the key segment
                        // so that we can properly handle ReadByKeyRestrictions
                        if (IsNextSegmentKey(odataPath, i))
                        {
                            continue;
                        }

                        var topLevelPermissions = GetNavigationSourceCrudPermissions(navSegment.NavigationSource as IEdmVocabularyAnnotatable, model, method);
                        var segmentEvaluator    = new WithOrScopesCombiner(topLevelPermissions);

                        var nestedPermissions = GetNavigationPropertyCrudPermisions(
                            segments,
                            isTargetByKey: false,
                            model,
                            method);


                        segmentEvaluator.Add(nestedPermissions);
                        permissionsChain.Add(segmentEvaluator);
                    }
                    else if (segment is OperationImportSegment operationImportSegment)
                    {
                        var annotations = operationImportSegment.OperationImports.First().Operation.VocabularyAnnotations(model);
                        var permissions = GetOperationPermissions(annotations);
                        permissionsChain.Add(new WithOrScopesCombiner(permissions));
                    }
                    else if (segment is OperationSegment operationSegment)
                    {
                        var annotations          = operationSegment.Operations.First().VocabularyAnnotations(model);
                        var operationPermissions = GetOperationPermissions(annotations);
                        permissionsChain.Add(new WithOrScopesCombiner(operationPermissions));
                    }
                }
            }

            return(permissionsChain);
        }
        /// <summary>
        /// Read and serialize outgoing object to HTTP request stream.
        /// </summary>
        internal static void WriteToStream(
            Type type,
            object value,
            IEdmModel model,
            Uri baseAddress,
            MediaTypeHeaderValue contentType,
            IUrlHelper internalUrlHelper,
            HttpRequest internalRequest,
            IHeaderDictionary internalRequestHeaders,
            Func <IServiceProvider, ODataMigrationMessageWrapper> getODataMessageWrapper,
            Func <IEdmTypeReference, ODataSerializer> getEdmTypeSerializer,
            Func <Type, ODataSerializer> getODataPayloadSerializer,
            Func <ODataSerializerContext> getODataSerializerContext)
        {
            if (model == null)
            {
                throw new InvalidOperationException("Request must have model");
            }

            ODataSerializer serializer = GetSerializer(type, value, internalRequest, getEdmTypeSerializer, getODataPayloadSerializer);

            // special case: if the top level serializer is an ODataPrimitiveSerializer then swap it out for an ODataMigrationPrimitiveSerializer
            // This only applies to the top level because inline primitives are translated but top level primitives are not, unless we use a customized serializer.
            if (serializer is ODataPrimitiveSerializer)
            {
                serializer = new ODataMigrationPrimitiveSerializer();
            }

            ODataPath            path = internalRequest.ODataFeature().Path;
            IEdmNavigationSource targetNavigationSource = path == null ? null : path.NavigationSource;

            // serialize a response
            string preferHeader     = GetRequestPreferHeader(internalRequestHeaders);
            string annotationFilter = null;

            if (!String.IsNullOrEmpty(preferHeader))
            {
                ODataMigrationMessageWrapper messageWrapper = getODataMessageWrapper(null);
                messageWrapper.SetHeader("Prefer", preferHeader);
                annotationFilter = messageWrapper.PreferHeader().AnnotationFilter;
            }

            ODataMigrationMessageWrapper responseMessageWrapper = getODataMessageWrapper(internalRequest.GetRequestContainer());
            IODataResponseMessage        responseMessage        = responseMessageWrapper;

            if (annotationFilter != null)
            {
                responseMessage.PreferenceAppliedHeader().AnnotationFilter = annotationFilter;
            }

            ODataMessageWriterSettings writerSettings = internalRequest.GetWriterSettings();

            writerSettings.BaseUri     = baseAddress;
            writerSettings.Version     = ODataVersion.V4; // Todo how to specify v3?  Maybe don't because reading as v4
            writerSettings.Validations = writerSettings.Validations & ~ValidationKinds.ThrowOnUndeclaredPropertyForNonOpenType;

            string metadataLink = internalUrlHelper.CreateODataLink(MetadataSegment.Instance);

            if (metadataLink == null)
            {
                throw new SerializationException("Unable to determine metadata url");
            }

            writerSettings.ODataUri = new ODataUri
            {
                ServiceRoot = baseAddress,

                SelectAndExpand = internalRequest.ODataFeature()?.SelectExpandClause,
                Apply           = internalRequest.ODataFeature().ApplyClause,
                Path            = (path == null || IsOperationPath(path)) ? null : path.Path,
            };

            ODataMetadataLevel metadataLevel = ODataMetadataLevel.MinimalMetadata;

            if (contentType != null)
            {
                IEnumerable <KeyValuePair <string, string> > parameters =
                    contentType.Parameters.Select(val => new KeyValuePair <string, string>(val.Name.Value, val.Value.Value));
                metadataLevel = ODataMediaTypes.GetMetadataLevel(contentType.MediaType.Value, parameters);
            }

            using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, writerSettings, model))
            {
                ODataSerializerContext writeContext = getODataSerializerContext();
                writeContext.NavigationSource = targetNavigationSource;
                writeContext.Model            = model;
                writeContext.RootElementName  = GetRootElementName(path) ?? "root";
                writeContext.SkipExpensiveAvailabilityChecks = serializer.ODataPayloadKind == ODataPayloadKind.ResourceSet;
                writeContext.Path = path;
                writeContext.SelectExpandClause = internalRequest.ODataFeature()?.SelectExpandClause;
                writeContext.MetadataLevel      = metadataLevel;

                // Substitute stream to swap @odata.context
                Stream substituteStream = new MemoryStream();
                Stream originalStream   = messageWriter.SubstituteResponseStream(substituteStream);

                serializer.WriteObject(value, type, messageWriter, writeContext);
                StreamReader reader = new StreamReader(substituteStream);
                substituteStream.Seek(0, SeekOrigin.Begin);
                JToken responsePayload = JToken.Parse(reader.ReadToEnd());

                // If odata context is present, replace with odata metadata
                if (responsePayload["@odata.context"] != null)
                {
                    responsePayload["odata.metadata"] = responsePayload["@odata.context"].ToString().Replace("$entity", "@Element");
                    ((JObject)responsePayload).Property("@odata.context").Remove();
                }

                // Write to actual stream
                // We cannot dispose of the stream because this method does not own the stream (subsequent methods will close the streamwriter)
                StreamWriter   streamWriter   = new StreamWriter(originalStream);
                JsonTextWriter writer         = new JsonTextWriter(streamWriter);
                JsonSerializer jsonSerializer = new JsonSerializer();
                jsonSerializer.Serialize(writer, responsePayload);
                writer.Flush();
                messageWriter.SubstituteResponseStream(originalStream);
            }
        }