/// <inheritdoc />
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            Debug.Assert(context.Controller != null);
            Debug.Assert(context.Action != null);
            ActionModel action     = context.Action;
            string      actionName = action.ActionMethod.Name;

            // for ~$metadata
            if (actionName == "GetMetadata")
            {
                ODataPathTemplate template = new ODataPathTemplate(MetadataSegmentTemplate.Instance);
                action.AddSelector("Get", context.Prefix, context.Model, template, context.RouteOptions);
                return(true);
            }

            // for ~/
            if (actionName == "GetServiceDocument")
            {
                ODataPathTemplate template = new ODataPathTemplate();
                action.AddSelector("Get", context.Prefix, context.Model, template, context.RouteOptions);
                return(true);
            }

            return(false);
        }
        private static IEdmOperation[] FindCandidates(ODataControllerActionContext context, IEdmEntityType entityType, string actionName,
                                                      out IEdmEntityType castTypeFromActionName, out bool isOnCollection)
        {
            // OperationNameOnCollectionOfEntityType
            StringComparison caseComparision = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ?
                                               StringComparison.OrdinalIgnoreCase :
                                               StringComparison.Ordinal;

            string operationName = SplitActionName(actionName, out string cast, out isOnCollection, caseComparision);

            castTypeFromActionName = null;
            if (cast != null)
            {
                if (cast.Length == 0)
                {
                    // Early return for the following cases:
                    // - {OperationName}On
                    // - {OperationName}OnCollectionOf
                    return(Array.Empty <IEdmOperation>());
                }

                castTypeFromActionName = entityType.FindTypeInInheritance(context.Model, cast, context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true) as IEdmEntityType;
                if (castTypeFromActionName == null)
                {
                    return(Array.Empty <IEdmOperation>());
                }
            }

            return(FindCandidates(context, operationName));
        }
Esempio n. 3
0
        /// <inheritdoc />
        public override bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            IEdmNavigationSource navigationSource = context.EntitySet == null ?
                                                    (IEdmNavigationSource)context.Singleton :
                                                    (IEdmNavigationSource)context.EntitySet;

            IEdmEntityType entityType = navigationSource.EntityType();

            // function should have the [HttpPost]
            if (!context.Action.Attributes.Any(a => a is HttpPostAttribute))
            {
                return(false);
            }

            // action overload on binding type, only one action overload on the same binding type.
            // however, it supports the bound action on derived type.
            ProcessOperations(context, entityType, navigationSource);

            // in OData operationImport routing convention, all action are processed by default
            // even it's not a really edm operation import call.
            return(false);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            Debug.Assert(context.Controller != null);
            Debug.Assert(context.Action != null);
            ActionModel action = context.Action;

            if (action.Controller.ControllerType != typeof(MetadataController).GetTypeInfo())
            {
                return(false);
            }

            if (action.ActionMethod.Name == "GetMetadata")
            {
                ODataPathTemplate template = new ODataPathTemplate(MetadataSegmentTemplate.Instance);
                action.AddSelector(context.Prefix, context.Model, template);

                return(true);
            }

            if (action.ActionMethod.Name == "GetServiceDocument")
            {
                ODataPathTemplate template = new ODataPathTemplate();
                action.AddSelector(context.Prefix, context.Model, template);

                return(true);
            }

            return(false);
        }
        private void ProcessAttributeModel(AttributeRouteModel attributeRouteModel, IEnumerable <string> prefixes,
                                           ODataControllerActionContext context, SelectorModel actionSelector, ActionModel actionModel, ControllerModel controllerModel,
                                           IDictionary <SelectorModel, IList <SelectorModel> > updatedSelectors)
        {
            if (attributeRouteModel == null)
            {
                // not an attribute routing, skip it.
                return;
            }

            string prefix = FindRelatedODataPrefix(attributeRouteModel.Template, prefixes, out string newRouteTemplate);

            if (prefix == null)
            {
                return;
            }

            IEdmModel        model = context.Options.RouteComponents[prefix].EdmModel;
            IServiceProvider sp    = context.Options.RouteComponents[prefix].ServiceProvider;

            SelectorModel newSelectorModel = CreateActionSelectorModel(prefix, model, sp, newRouteTemplate, actionSelector,
                                                                       attributeRouteModel.Template, actionModel.ActionName, controllerModel.ControllerName);

            if (newSelectorModel != null)
            {
                IList <SelectorModel> selectors;
                if (!updatedSelectors.TryGetValue(actionSelector, out selectors))
                {
                    selectors = new List <SelectorModel>();
                    updatedSelectors[actionSelector] = selectors;
                }

                selectors.Add(newSelectorModel);
            }
        }
        /// <inheritdoc />
        public bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            Debug.Assert(context.Singleton != null);
            Debug.Assert(context.Action != null);

            ActionModel action        = context.Action;
            string      singletonName = context.Singleton.Name;

            string actionMethodName = action.ActionName;

            if (IsSupportedActionName(actionMethodName, singletonName, out string httpMethod))
            {
                // ~/Me
                ODataPathTemplate template = new ODataPathTemplate(new SingletonSegmentTemplate(context.Singleton));
                action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions);

                // processed
                return(true);
            }

            // type cast
            // GetFrom{EntityTypeName} or Get{SingletonName}From{EntityTypeName}
            int index = actionMethodName.IndexOf("From", StringComparison.Ordinal);

            if (index == -1)
            {
                return(false);
            }

            string actionPrefix = actionMethodName.Substring(0, index);

            if (IsSupportedActionName(actionPrefix, singletonName, out httpMethod))
            {
                string         castTypeName = actionMethodName.Substring(index + 4);
                IEdmEntityType entityType   = context.Singleton.EntityType();

                // Shall we cast to base type and the type itself? I think yes.
                IEdmStructuredType castType = entityType.FindTypeInInheritance(context.Model, castTypeName);
                if (castType != null)
                {
                    // ~/Me/Namespace.TypeCast
                    ODataPathTemplate template = new ODataPathTemplate(
                        new SingletonSegmentTemplate(context.Singleton),
                        new CastSegmentTemplate(castType, entityType, context.Singleton));

                    action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions);
                    return(true);
                }
            }

            return(false);
        }
Esempio n. 7
0
        internal static bool ProcessNonNavigationProperty(string httpMethod, ODataControllerActionContext context,
                                                          ActionModel action,
                                                          IEdmNavigationSource navigationSource,
                                                          IEdmEntityType entityType, IEdmStructuredType castType)
        {
            // Action parameter should have a (string navigationProperty) parameter
            if (!action.HasParameter <string>("navigationProperty"))
            {
                return(false);
            }

            // Let's only handle single-key convention, for composite key, use attribute routing or non-generic navigation.
            bool hasRelatedKey = action.Parameters.Any(p => p.Name == "relatedKey"); // case sensitive?
            bool hasRelatedId  = action.Parameters.Any(p => p.Name == "relatedId");

            IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>();

            if (context.EntitySet != null)
            {
                segments.Add(new EntitySetSegmentTemplate(context.EntitySet));
                segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, context.EntitySet));
            }
            else
            {
                segments.Add(new SingletonSegmentTemplate(context.Singleton));
            }

            if (entityType != castType)
            {
                segments.Add(new CastSegmentTemplate(castType, entityType, navigationSource));
            }

            if (hasRelatedKey)
            {
                segments.Add(new NavigationLinkTemplateSegmentTemplate(entityType, navigationSource)
                {
                    RelatedKey = "relatedKey"
                });
            }
            else if (hasRelatedId)
            {
                segments.Add(new NavigationLinkTemplateSegmentTemplate(entityType, navigationSource)
                {
                    RelatedKey = "relatedId"
                });
            }
            else
            {
                segments.Add(new NavigationLinkTemplateSegmentTemplate(entityType, navigationSource));
            }

            ODataPathTemplate template = new ODataPathTemplate(segments);

            action.AddSelector(httpMethod, context.Prefix, context.Model, template, context.Options?.RouteOptions);

            return(true);
        }
Esempio n. 8
0
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            return(context.Singleton != null);
        }
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            return(context.EntitySet != null);
        }
Esempio n. 10
0
        /// <inheritdoc />
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            ActionModel    action     = context.Action;
            IEdmEntitySet  entitySet  = context.EntitySet;
            IEdmEntityType entityType = entitySet.EntityType();

            // if the action has key parameter, skip it.
            if (action.HasODataKeyParameter(entityType, context.Options?.RouteOptions?.EnablePropertyNameCaseInsensitive ?? false))
            {
                return(false);
            }

            string actionName = action.ActionName;

            // 1. Without type case
            if (ProcessEntitySetAction(actionName, entitySet, null, context, action))
            {
                return(true);
            }

            // 2. process the derived type (cast) by searching all derived types
            // GetFrom{EntityTypeName} or Get{EntitySet}From{EntityTypeName}
            int index = actionName.IndexOf("From", StringComparison.Ordinal);

            if (index == -1)
            {
                return(false);
            }

            string castTypeName = actionName.Substring(index + 4); // + 4 means to skip the "From"

            if (castTypeName.Length == 0)
            {
                // Early return for the following cases:
                // - Get|Post|PatchFrom
                // - Get|Patch{EntitySet}From
                // - Post{EntityType}From
                return(false);
            }

            IEdmStructuredType castType = entityType.FindTypeInInheritance(context.Model, castTypeName);

            if (castType == null)
            {
                return(false);
            }

            string actionPrefix = actionName.Substring(0, index);

            return(ProcessEntitySetAction(actionPrefix, entitySet, castType, context, action));
        }
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            // This convention only applies to "MetadataController".
            return(context.Controller.ControllerType == metadataTypeInfo);
        }
Esempio n. 12
0
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            // bound operation supports for entity set and singleton
            return(context.NavigationSource != null);
        }
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            // structural property supports for entity set and singleton
            return(context.EntitySet != null || context.Singleton != null);
        }
Esempio n. 14
0
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            // By convention, we look for the controller name as "ODataOperationImportController"
            // Each operation import will be handled by the same action name in this controller.
            return(context.Controller.ControllerName == "ODataOperationImport");
        }
Esempio n. 15
0
        private static void AddSelector(string httpMethod, ODataControllerActionContext context, ActionModel action,
                                        IEdmNavigationSource navigationSource,
                                        IEdmStructuralProperty edmProperty,
                                        IEdmType cast, IEdmEntityType declaringType, bool dollarValue, bool dollarCount)
        {
            IEdmEntitySet  entitySet  = navigationSource as IEdmEntitySet;
            IEdmEntityType entityType = navigationSource.EntityType();

            IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>();

            if (entitySet != null)
            {
                segments.Add(new EntitySetSegmentTemplate(entitySet));
                segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, navigationSource));
            }
            else
            {
                segments.Add(new SingletonSegmentTemplate(navigationSource as IEdmSingleton));
            }

            if (declaringType != null && declaringType != entityType)
            {
                segments.Add(new CastSegmentTemplate(declaringType, entityType, navigationSource));
            }

            segments.Add(new PropertySegmentTemplate(edmProperty));

            if (cast != null)
            {
                if (edmProperty.Type.IsCollection())
                {
                    cast = new EdmCollectionType(cast.ToEdmTypeReference(edmProperty.Type.IsNullable));
                }

                // TODO: maybe create the collection type for the collection????
                segments.Add(new CastSegmentTemplate(cast, edmProperty.Type.Definition, navigationSource));
            }

            if (dollarValue)
            {
                segments.Add(new ValueSegmentTemplate(edmProperty.Type.Definition));
            }

            if (dollarCount)
            {
                segments.Add(CountSegmentTemplate.Instance);
            }

            ODataPathTemplate template = new ODataPathTemplate(segments);

            action.AddSelector(httpMethod.NormalizeHttpMethod(), context.Prefix, context.Model, template, context.Options?.RouteOptions);
        }
        /// <inheritdoc />
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            // It allows to use attribute routing without ODataRoutePrefixAttribute.
            // In this case, we only use the ODataRouteAttrbute to construct the route template.
            // Otherwise, we combine each route prefix with each route attribute to construct the route template.
            foreach (var pathTemplatePrefix in GetODataPathTemplatePrefixes(context.Prefix, context.Controller))
            {
                foreach (var action in context.Controller.Actions)
                {
                    var routeAttributes = action.Attributes.OfType <ODataRouteAttribute>();

                    foreach (ODataRouteAttribute routeAttribute in routeAttributes)
                    {
                        // If we have the route prefix name setting, make sure we only let the attribute with the same route prefx to pass.
                        if (routeAttribute.RoutePrefix != null &&
                            !string.Equals(routeAttribute.RoutePrefix, context.Prefix, StringComparison.OrdinalIgnoreCase))
                        {
                            continue;
                        }

                        try
                        {
                            string routeTemplate = GetODataPathTemplateString(pathTemplatePrefix, routeAttribute.PathTemplate);

                            ODataPathTemplate pathTemplate = _templateParser.Parse(context.Model, routeTemplate, context.ServiceProvider);

                            // Add the httpMethod?
                            action.AddSelector(null, context.Prefix, context.Model, pathTemplate, context.RouteOptions);
                        }
                        catch (ODataException ex)
                        {
                            // use the logger to log the wrong odata attribute template. Shall we log the others?
                            string warning = string.Format(CultureInfo.CurrentCulture, SRResources.InvalidODataRouteOnAction,
                                                           routeAttribute.PathTemplate, action.ActionMethod.Name, context.Controller.ControllerName, ex.Message);

                            _logger.LogWarning(warning);
                        }
                    }
                }
            }

            // We execute this convention on all actions in the controller level.
            // So, returns false to make sure we don't want to call the AppliesToAction for this convention.
            return(false);
        }
        private static IEdmOperation[] FindCandidates(ODataControllerActionContext context, string operationName)
        {
            // TODO: refactor here
            // If we have multiple same function defined, we should match the best one?

            StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ?
                                                    StringComparison.OrdinalIgnoreCase :
                                                    StringComparison.Ordinal;

            return(context.Model.SchemaElements
                   .OfType <IEdmOperation>()
                   .Where(f => f.IsBound && f.Name.Equals(operationName, actionNameComparison))
                   .ToArray());
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ActionModel action = context.Action;

            if (context.EntitySet == null)
            {
                return(false);
            }

            IEdmEntitySet entitySet = context.EntitySet;

            if (action.Parameters.Count != 0)
            {
                // TODO: improve here to accept other parameters, for example ODataQueryOptions<T>
                return(false);
            }

            string actionName = action.ActionMethod.Name;

            if (actionName == "Get" ||
                actionName == $"Get{entitySet.Name}")
            {
                ODataPathTemplate template = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet));
                action.AddSelector(context.Prefix, context.Model, template);

                // $count
                template = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet), CountSegmentTemplate.Instance);
                action.AddSelector(context.Prefix, context.Model, template);
                return(true);
            }
            else if (actionName == "Post" ||
                     actionName == $"Post{entitySet.EntityType().Name}")
            {
                ODataPathTemplate template = new ODataPathTemplate(new EntitySetSegmentTemplate(entitySet));
                action.AddSelector(context.Prefix, context.Model, template);
                return(true);
            }
            else
            {
                // process the derive type (cast)
                // search all derived types
            }

            return(false);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ActionModel action = context.Action;

            if (context.EntitySet == null || action.Parameters.Count < 1)
            {
                // At lease one parameter for the key.
                return(false);
            }

            IEdmEntitySet entitySet      = context.EntitySet;
            var           entityType     = entitySet.EntityType();
            var           entityTypeName = entitySet.EntityType().Name;
            var           keys           = entitySet.EntityType().Key().ToArray();

            string actionName = action.ActionMethod.Name;

            if ((actionName == "Get" ||
                 actionName == $"Get{entityTypeName}" ||
                 actionName == "Put" ||
                 actionName == $"Put{entityTypeName}" ||
                 actionName == "Patch" ||
                 actionName == $"Patch{entityTypeName}" ||
                 actionName == "Delete" ||
                 actionName == $"Delete{entityTypeName}") &&
                keys.Length == action.Parameters.Count)
            {
                ODataPathTemplate template = new ODataPathTemplate(
                    new EntitySetSegmentTemplate(entitySet),
                    new KeySegmentTemplate(entityType)
                    );

                // support key in parenthesis
                action.AddSelector(context.Prefix, context.Model, template);

                // support key as segment
                ODataPathTemplate newTemplate = template.Clone();
                newTemplate.KeyAsSegment = true;
                action.AddSelector(context.Prefix, context.Model, newTemplate);
                return(true);
            }

            return(false);
        }
Esempio n. 20
0
        private void AddSelector(string httpMethod, ODataControllerActionContext context, ActionModel action,
                                 IEdmNavigationSource navigationSource, string declared, IEdmEntityType declaringEntityType,
                                 IEdmNavigationProperty navigationProperty, bool hasKey, bool dollarCount)
        {
            IEdmEntitySet  entitySet  = navigationSource as IEdmEntitySet;
            IEdmEntityType entityType = navigationSource.EntityType();

            // Starts the routing template
            IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>();

            if (entitySet != null)
            {
                segments.Add(new EntitySetSegmentTemplate(entitySet));
            }
            else
            {
                segments.Add(new SingletonSegmentTemplate(navigationSource as IEdmSingleton));
            }

            if (hasKey)
            {
                segments.Add(KeySegmentTemplate.CreateKeySegment(entityType, navigationSource));
            }

            if (declared != null)
            {
                // It should be always single type
                if (entityType != declaringEntityType)
                {
                    segments.Add(new CastSegmentTemplate(declaringEntityType, entityType, navigationSource));
                }
            }

            IEdmNavigationSource targetNavigationSource = navigationSource.FindNavigationTarget(navigationProperty, segments, out _);

            segments.Add(new NavigationSegmentTemplate(navigationProperty, targetNavigationSource));

            if (dollarCount)
            {
                segments.Add(CountSegmentTemplate.Instance);
            }

            ODataPathTemplate template = new ODataPathTemplate(segments);

            action.AddSelector(httpMethod.NormalizeHttpMethod(), context.Prefix, context.Model, template, context.RouteOptions);

            Log.AddedODataSelector(_logger, action, template);
        }
Esempio n. 21
0
        /// <inheritdoc />
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            ActionModel    action     = context.Action;
            IEdmEntitySet  entitySet  = context.EntitySet;
            IEdmEntityType entityType = entitySet.EntityType();

            // if the action has key parameter, skip it.
            if (action.HasODataKeyParameter(entityType))
            {
                return(false);
            }

            string actionName = action.ActionMethod.Name;

            // 1. Without type case
            if (ProcessEntitySetAction(actionName, entitySet, null, context, action))
            {
                return(true);
            }

            // 2. process the derive type (cast) by searching all derived types
            // GetFrom{EntityTypeName} or Get{EntitySet}From{EntityTypeName}
            int index = actionName.IndexOf("From", StringComparison.Ordinal);

            if (index == -1)
            {
                return(false);
            }

            string             castTypeName = actionName.Substring(index + 4); // + 4 means to skip the "From"
            IEdmStructuredType castType     = entityType.FindTypeInInheritance(context.Model, castTypeName);

            if (castType == null)
            {
                return(false);
            }

            string actionPrefix = actionName.Substring(0, index);

            return(ProcessEntitySetAction(actionPrefix, entitySet, castType, context, action));
        }
Esempio n. 22
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ActionModel         action    = context.Action;
            ODataRouteAttribute routeAttr = action.GetAttribute <ODataRouteAttribute>();

            if (routeAttr == null)
            {
                return(false);
            }
            string    prefix = context.Prefix;
            IEdmModel model  = context.Model;

            string routeTemplate = "";
            ODataRoutePrefixAttribute prefixAttr = action.Controller.GetAttribute <ODataRoutePrefixAttribute>();

            if (prefixAttr != null)
            {
                routeTemplate = prefixAttr.Prefix + "/";
            }
            routeTemplate += routeAttr.PathTemplate;

            SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);

            if (selectorModel == null)
            {
                selectorModel = new SelectorModel();
                action.Selectors.Add(selectorModel);
            }

            string templateStr = string.IsNullOrEmpty(prefix) ? routeTemplate : $"{prefix}/{routeTemplate}";

            selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
            {
                Name = templateStr
            });
            selectorModel.EndpointMetadata.Add(new ODataEndpointMetadata(prefix, model, templateStr));

            return(true);
        }
Esempio n. 23
0
        /// <inheritdoc />
        public override bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            IEdmNavigationSource navigationSource = context.NavigationSource;
            IEdmEntityType       entityType       = navigationSource.EntityType();

            // function should have the [HttpGet]
            if (!context.Action.Attributes.Any(a => a is HttpGetAttribute))
            {
                return(false);
            }

            ProcessOperations(context, entityType, navigationSource);
            return(false);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        public bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ActionModel action = context.Action;

            if (action.Controller.ControllerName != "ODataOperationImport")
            {
                return(false);
            }

            IEdmModel model = context.Model;

            // By convention, we use the operation name as the action name in the controller
            string actionMethodName    = action.ActionMethod.Name;
            var    edmOperationImports = model.EntityContainer.FindOperationImports(actionMethodName);

            foreach (var edmOperationImport in edmOperationImports)
            {
                IEdmEntitySetBase targetSet = null;
                edmOperationImport.TryGetStaticEntitySet(model, out targetSet);

                if (edmOperationImport.IsActionImport())
                {
                    ODataPathTemplate template = new ODataPathTemplate(new ActionImportSegmentTemplate((IEdmActionImport)edmOperationImport));
                    action.AddSelector(context.Prefix, context.Model, template);
                }
                else
                {
                    IEdmFunctionImport functionImport = (IEdmFunctionImport)edmOperationImport;
                    ODataPathTemplate  template       = new ODataPathTemplate(new FunctionImportSegmentTemplate(functionImport));
                    action.AddSelector(context.Prefix, context.Model, template);
                }
            }

            // in OData operationImport routing convention, all action are processed by default
            // even it's not a really edm operation import call.
            return(true);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public virtual bool AppliesToController(ODataControllerActionContext context)
        {
            //if (model == null)
            //{
            //    throw new ArgumentNullException(nameof(model));
            //}

            //if (controller == null)
            //{
            //    throw new ArgumentNullException(nameof(controller));
            //}

            //string controllerName = controller.ControllerName;
            //NavigationSource = model.EntityContainer?.FindEntitySet(controllerName);

            //// Cached the singleton, because we call this method first, then AppliesToAction
            //// FindSingleton maybe time consuming.
            //return NavigationSource != null;
            return(true);
        }
        /// <inheritdoc />
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            ActionModel    action     = context.Action;
            IEdmEntitySet  entitySet  = context.EntitySet;
            IEdmEntityType entityType = entitySet.EntityType();

            // if the action has no key parameter, skip it.
            if (!action.HasODataKeyParameter(entityType))
            {
                return(false);
            }

            string actionName = action.ActionMethod.Name;

            // We care about the action in this pattern: {HttpMethod}{EntityTypeName}
            (string httpMethod, string castTypeName) = Split(actionName);
            if (httpMethod == null)
            {
                return(false);
            }

            IEdmStructuredType castType = null;

            if (castTypeName != null)
            {
                castType = entityType.FindTypeInInheritance(context.Model, castTypeName);
                if (castType == null)
                {
                    return(false);
                }
            }

            AddSelector(entitySet, entityType, castType, context.Prefix, context.Model, action, httpMethod);
            return(true);
        }
        private static bool IsSupportedActionName(ODataControllerActionContext context, string actionName, string singletonName, out string httpMethod)
        {
            StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

            if (actionName.Equals("Get", actionNameComparison) || actionName.Equals($"Get{singletonName}", actionNameComparison))
            {
                httpMethod = "Get";
                return(true);
            }
            else if (actionName.Equals("Put", actionNameComparison) || actionName.Equals($"Put{singletonName}", actionNameComparison))
            {
                httpMethod = "Put";
                return(true);
            }
            else if (actionName.Equals("Patch", actionNameComparison) || actionName.Equals($"Patch{singletonName}", actionNameComparison))
            {
                httpMethod = "Patch";
                return(true);
            }

            httpMethod = "";
            return(false);
        }
Esempio n. 28
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="context"></param>
 /// <returns></returns>
 public virtual bool AppliesToController(ODataControllerActionContext context)
 {
     // Apply to all controllers
     return(true);
 }
Esempio n. 29
0
        /// <inheritdoc />
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw Error.ArgumentNull(nameof(context));
            }

            ActionModel action = context.Action;
            IEdmModel   model  = context.Model;

            // By convention, we use the operation import name as the action name in the controller
            string actionMethodName = action.ActionName;

            var edmOperationImports = model.ResolveOperationImports(actionMethodName, enableCaseInsensitive: true);

            if (!edmOperationImports.Any())
            {
                return(true);
            }

            (var actionImports, var functionImports) = edmOperationImports.SplitOperationImports();

            // It's not allowed to have an action import and function import with the same name.
            if (actionImports.Count > 0 && functionImports.Count > 0)
            {
                throw new ODataException(Error.Format(SRResources.OperationMustBeUniqueInEntitySetContainer, actionMethodName));
            }
            else if (actionImports.Count > 0 && context.Action.Attributes.Any(a => a is HttpPostAttribute))
            {
                if (actionImports.Count != 1)
                {
                    throw new ODataException(Error.Format(SRResources.MultipleActionImportFound, actionMethodName));
                }

                IEdmActionImport actionImport = actionImports[0];

                IEdmEntitySetBase targetEntitySet;
                actionImport.TryGetStaticEntitySet(model, out targetEntitySet);

                // TODO:
                // 1. shall we check the [HttpPost] attribute, or does the ASP.NET Core have the default?
                // 2) shall we check the action has "ODataActionParameters" parameter type?
                ODataPathTemplate template = new ODataPathTemplate(new ActionImportSegmentTemplate(actionImport, targetEntitySet));
                action.AddSelector("Post", context.Prefix, context.Model, template, context.Options?.RouteOptions);
                return(true);
            }
            else if (functionImports.Count > 0 && context.Action.Attributes.Any(a => a is HttpGetAttribute))
            {
                IEdmFunctionImport functionImport = FindFunctionImport(functionImports, action);
                if (functionImport == null)
                {
                    return(false);
                }

                IEdmEntitySetBase targetSet;
                functionImport.TryGetStaticEntitySet(model, out targetSet);

                // TODO:
                // 1) shall we check the [HttpGet] attribute, or does the ASP.NET Core have the default?
                ODataPathTemplate template = new ODataPathTemplate(new FunctionImportSegmentTemplate(functionImport, targetSet));
                action.AddSelector("Get", context.Prefix, context.Model, template, context.Options?.RouteOptions);
                return(true);
            }
            else
            {
                // doesn't find an operation, return true means to skip the remaining conventions.
                return(false);
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        public virtual bool AppliesToAction(ODataControllerActionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ActionModel action = context.Action;

            if (context.EntitySet == null && context.Singleton == null)
            {
                return(false);
            }
            IEdmNavigationSource navigationSource = context.EntitySet == null ?
                                                    (IEdmNavigationSource)context.Singleton :
                                                    (IEdmNavigationSource)context.EntitySet;

            string actionName = action.ActionMethod.Name;

            string method = Split(actionName, out string property, out string cast, out string declared);

            if (method == null || string.IsNullOrEmpty(property))
            {
                return(false);
            }

            IEdmEntityType entityType         = navigationSource.EntityType();
            IEdmModel      model              = context.Model;
            string         prefix             = context.Prefix;
            IEdmEntityType declaredEntityType = null;

            if (declared != null)
            {
                declaredEntityType = entityType.FindTypeInInheritance(model, declared) as IEdmEntityType;
                if (declaredEntityType == null)
                {
                    return(false);
                }

                if (declaredEntityType == entityType)
                {
                    declaredEntityType = null;
                }
            }

            bool          hasKeyParameter = HasKeyParameter(entityType, action);
            IEdmSingleton singleton       = navigationSource as IEdmSingleton;

            if (singleton != null && hasKeyParameter)
            {
                // Singleton, doesn't allow to query property with key
                return(false);
            }

            if (singleton == null && !hasKeyParameter)
            {
                // in entityset, doesn't allow for non-key to query property
                return(false);
            }

            IEdmProperty edmProperty = entityType.FindProperty(property);

            if (edmProperty != null && edmProperty.PropertyKind == EdmPropertyKind.Structural)
            {
                // only process structural property
                IEdmStructuredType castComplexType = null;
                if (cast != null)
                {
                    IEdmTypeReference propertyType = edmProperty.Type;
                    if (propertyType.IsCollection())
                    {
                        propertyType = propertyType.AsCollection().ElementType();
                    }
                    if (!propertyType.IsComplex())
                    {
                        return(false);
                    }

                    castComplexType = propertyType.ToStructuredType().FindTypeInInheritance(model, cast);
                    if (castComplexType == null)
                    {
                        return(false);
                    }
                }

                IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>();

                if (context.EntitySet != null)
                {
                    segments.Add(new EntitySetSegmentTemplate(context.EntitySet));
                }
                else
                {
                    segments.Add(new SingletonSegmentTemplate(context.Singleton));
                }

                if (hasKeyParameter)
                {
                    segments.Add(new KeySegmentTemplate(entityType));
                }
                if (declaredEntityType != null && declaredEntityType != entityType)
                {
                    segments.Add(new CastSegmentTemplate(declaredEntityType));
                }

                segments.Add(new PropertySegmentTemplate((IEdmStructuralProperty)edmProperty));

                ODataPathTemplate template = new ODataPathTemplate(segments);
                action.AddSelector(prefix, model, template);
                return(true);
            }
            else
            {
                // map to a static action like:  <method>Property(int key, string property)From<...>
                if (property == "Property" && cast == null)
                {
                    if (action.Parameters.Any(p => p.ParameterInfo.Name == "property" && p.ParameterType == typeof(string)))
                    {
                        // we find a static method mapping for all property
                        // we find a action route
                        IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>();

                        if (context.EntitySet != null)
                        {
                            segments.Add(new EntitySetSegmentTemplate(context.EntitySet));
                        }
                        else
                        {
                            segments.Add(new SingletonSegmentTemplate(context.Singleton));
                        }

                        if (hasKeyParameter)
                        {
                            segments.Add(new KeySegmentTemplate(entityType));
                        }
                        if (declaredEntityType != null)
                        {
                            segments.Add(new CastSegmentTemplate(declaredEntityType));
                        }

                        segments.Add(new PropertySegmentTemplate((string)null /*entityType*/));

                        ODataPathTemplate template = new ODataPathTemplate(segments);
                        action.AddSelector(prefix, model, template);
                        return(true);
                    }
                }
            }

            return(false);
        }