public void OnActionExecuting(ActionExecutingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!context.HttpContext.IsJsonApiRequest())
            {
                return;
            }

            var request = context.HttpContext.Request;

            if (request.Method == "PATCH" || request.Method == "POST")
            {
                var deserializedType = context.ActionArguments.FirstOrDefault().Value?.GetType();
                var targetType       = context.ActionDescriptor.Parameters.FirstOrDefault()?.ParameterType;

                if (deserializedType != null && targetType != null && deserializedType != targetType)
                {
                    ResourceContext resourceFromEndpoint = _provider.GetResourceContext(targetType);
                    ResourceContext resourceFromBody     = _provider.GetResourceContext(deserializedType);

                    throw new ResourceTypeMismatchException(new HttpMethod(request.Method), request.Path, resourceFromEndpoint, resourceFromBody);
                }
            }
        }
예제 #2
0
        /// <inheritdoc />
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            if (context.HttpContext.IsJsonApiRequest() && IsPatchOrPostRequest(context.HttpContext.Request))
            {
                var deserializedType = context.ActionArguments.FirstOrDefault().Value?.GetType();
                var targetType       = context.ActionDescriptor.Parameters.FirstOrDefault()?.ParameterType;

                if (deserializedType != null && targetType != null && deserializedType != targetType)
                {
                    var resourceFromEndpoint = _provider.GetResourceContext(targetType);
                    var resourceFromBody     = _provider.GetResourceContext(deserializedType);

                    throw new ResourceTypeMismatchException(new HttpMethod(context.HttpContext.Request.Method), context.HttpContext.Request.Path,
                                                            resourceFromEndpoint, resourceFromBody);
                }
            }

            await next();
        }
        private void ValidateIncomingResourceType(InputFormatterContext context, object model)
        {
            if (context.HttpContext.IsJsonApiRequest() && IsPatchOrPostRequest(context.HttpContext.Request))
            {
                var endpointResourceType = GetEndpointResourceType();
                if (endpointResourceType == null)
                {
                    return;
                }

                var bodyResourceTypes = GetBodyResourceTypes(model);
                foreach (var bodyResourceType in bodyResourceTypes)
                {
                    if (!endpointResourceType.IsAssignableFrom(bodyResourceType))
                    {
                        var resourceFromEndpoint = _resourceContextProvider.GetResourceContext(endpointResourceType);
                        var resourceFromBody     = _resourceContextProvider.GetResourceContext(bodyResourceType);

                        throw new ResourceTypeMismatchException(new HttpMethod(context.HttpContext.Request.Method),
                                                                context.HttpContext.Request.Path,
                                                                resourceFromEndpoint, resourceFromBody);
                    }
                }
            }
        }
예제 #4
0
        private IReadOnlyCollection <IncludeElementExpression> ProcessIncludeSet(IReadOnlyCollection <IncludeElementExpression> includeElements,
                                                                                 QueryLayer parentLayer, ICollection <RelationshipAttribute> parentRelationshipChain, ICollection <ExpressionInScope> constraints)
        {
            var includeElementsEvaluated = GetIncludeElements(includeElements, parentLayer.ResourceContext) ?? Array.Empty <IncludeElementExpression>();

            var updatesInChildren = new Dictionary <IncludeElementExpression, IReadOnlyCollection <IncludeElementExpression> >();

            foreach (var includeElement in includeElementsEvaluated)
            {
                parentLayer.Projection ??= new Dictionary <ResourceFieldAttribute, QueryLayer>();

                if (!parentLayer.Projection.ContainsKey(includeElement.Relationship))
                {
                    var relationshipChain = new List <RelationshipAttribute>(parentRelationshipChain)
                    {
                        includeElement.Relationship
                    };

                    // @formatter:wrap_chained_method_calls chop_always
                    // @formatter:keep_existing_linebreaks true

                    var expressionsInCurrentScope = constraints
                                                    .Where(constraint =>
                                                           constraint.Scope != null && constraint.Scope.Fields.SequenceEqual(relationshipChain))
                                                    .Select(constraint => constraint.Expression)
                                                    .ToArray();

                    // @formatter:keep_existing_linebreaks restore
                    // @formatter:wrap_chained_method_calls restore

                    var resourceContext =
                        _resourceContextProvider.GetResourceContext(includeElement.Relationship.RightType);

                    var child = new QueryLayer(resourceContext)
                    {
                        Filter     = GetFilter(expressionsInCurrentScope, resourceContext),
                        Sort       = GetSort(expressionsInCurrentScope, resourceContext),
                        Pagination = ((JsonApiOptions)_options).DisableChildrenPagination
                            ? null
                            : GetPagination(expressionsInCurrentScope, resourceContext),
                        Projection = GetProjectionForSparseAttributeSet(resourceContext)
                    };

                    parentLayer.Projection.Add(includeElement.Relationship, child);

                    if (includeElement.Children.Any())
                    {
                        var updatedChildren = ProcessIncludeSet(includeElement.Children, child, relationshipChain, constraints);

                        if (!ReferenceEquals(includeElement.Children, updatedChildren))
                        {
                            updatesInChildren.Add(includeElement, updatedChildren);
                        }
                    }
                }
            }

            return(!updatesInChildren.Any() ? includeElementsEvaluated : ApplyIncludeElementUpdates(includeElementsEvaluated, updatesInChildren));
        }
        /// <inheritdoc />
        public void SetInitiallyStoredAttributeValues(TResource resource)
        {
            ArgumentGuard.NotNull(resource, nameof(resource));

            ResourceContext resourceContext = _resourceContextProvider.GetResourceContext <TResource>();

            _initiallyStoredAttributeValues = CreateAttributeDictionary(resource, resourceContext.Attributes);
        }
예제 #6
0
 private void DeclareLocalId(IIdentifiable resource)
 {
     if (resource.LocalId != null)
     {
         var resourceContext = _resourceContextProvider.GetResourceContext(resource.GetType());
         _localIdTracker.Declare(resource.LocalId, resourceContext.PublicName);
     }
 }
        /// <inheritdoc />
        public void SetInitiallyStoredAttributeValues(TResource resource)
        {
            if (resource == null)
            {
                throw new ArgumentNullException(nameof(resource));
            }

            var resourceContext = _resourceContextProvider.GetResourceContext <TResource>();

            _initiallyStoredAttributeValues = CreateAttributeDictionary(resource, resourceContext.Attributes);
        }
예제 #8
0
        /// <inheritdoc />
        public virtual Task <int> CountAsync(FilterExpression topFilter, CancellationToken cancellationToken)
        {
            var resourceContext = _resourceContextProvider.GetResourceContext <TResource>();
            var layer           = new QueryLayer(resourceContext)
            {
                Filter = topFilter
            };

            var query = ApplyQueryLayer(layer);

            return(query.CountAsync(cancellationToken));
        }
예제 #9
0
        /// <inheritdoc/>
        public ResourceLinks GetResourceLinks(string resourceName, string id)
        {
            var resourceContext = _provider.GetResourceContext(resourceName);

            if (ShouldAddResourceLink(resourceContext, Link.Self))
            {
                return(new ResourceLinks {
                    Self = GetSelfResourceLink(resourceName, id)
                });
            }

            return(null);
        }
예제 #10
0
        /// <inheritdoc />
        public IReadOnlyCollection <AttrAttribute> GetAttributes(Type resourceType)
        {
            ArgumentGuard.NotNull(resourceType, nameof(resourceType));

            if (_request.Kind == EndpointKind.Relationship)
            {
                return(Array.Empty <AttrAttribute>());
            }

            var resourceContext = _resourceContextProvider.GetResourceContext(resourceType);
            var fieldSet        = _sparseFieldSetCache.GetSparseFieldSetForSerializer(resourceContext);

            return(fieldSet.OfType <AttrAttribute>().ToArray());
        }
        /// <inheritdoc />
        public string GetControllerNameForResourceType(Type resourceType)
        {
            ArgumentGuard.NotNull(resourceType, nameof(resourceType));

            ResourceContext resourceContext = _resourceContextProvider.GetResourceContext(resourceType);

            if (_controllerPerResourceContextMap.TryGetValue(resourceContext, out ControllerModel controllerModel))

            {
                return(controllerModel.ControllerName);
            }

            return(null);
        }
        /// <inheritdoc />
        public IReadOnlyCollection <AttrAttribute> GetAttributes(Type resourceType)
        {
            ArgumentGuard.NotNull(resourceType, nameof(resourceType));

            if (!ShouldSerialize)
            {
                return(Array.Empty <AttrAttribute>());
            }

            ResourceContext resourceContext = _resourceContextProvider.GetResourceContext(resourceType);
            IReadOnlyCollection <ResourceFieldAttribute> fieldSet = _sparseFieldSetCache.GetSparseFieldSetForSerializer(resourceContext);

            return(fieldSet.OfType <AttrAttribute>().ToArray());
        }
예제 #13
0
        private IEnumerable <TResource> GetWhereWithInclude <TResource, TId>(IReadOnlyCollection <TId> ids, RelationshipAttribute[] relationshipsToNextLayer)
            where TResource : class, IIdentifiable <TId>
        {
            if (!ids.Any())
            {
                return(Array.Empty <TResource>());
            }

            ResourceContext  resourceContext  = _resourceContextProvider.GetResourceContext <TResource>();
            FilterExpression filterExpression = CreateFilterByIds(ids, resourceContext);

            var queryLayer = new QueryLayer(resourceContext)
            {
                Filter = filterExpression
            };

            List <ResourceFieldChainExpression> chains = relationshipsToNextLayer.Select(relationship => new ResourceFieldChainExpression(relationship))
                                                         .ToList();

            if (chains.Any())
            {
                queryLayer.Include = IncludeChainConverter.FromRelationshipChains(chains);
            }

            IResourceReadRepository <TResource, TId> repository = GetRepository <TResource, TId>();

            return(repository.GetAsync(queryLayer, CancellationToken.None).Result);
        }
예제 #14
0
        private static void SetupCurrentRequest(CurrentRequest currentRequest, ResourceContext primaryResourceContext,
                                                RouteValueDictionary routeValues, IJsonApiOptions options, IResourceContextProvider resourceContextProvider,
                                                HttpRequest httpRequest)
        {
            currentRequest.IsReadOnly      = httpRequest.Method == HttpMethod.Get.Method;
            currentRequest.Kind            = EndpointKind.Primary;
            currentRequest.PrimaryResource = primaryResourceContext;
            currentRequest.PrimaryId       = GetPrimaryRequestId(routeValues);
            currentRequest.BasePath        = GetBasePath(primaryResourceContext.ResourceName, options, httpRequest);

            var relationshipName = GetRelationshipNameForSecondaryRequest(routeValues);

            if (relationshipName != null)
            {
                currentRequest.Kind = IsRouteForRelationship(routeValues) ? EndpointKind.Relationship : EndpointKind.Secondary;

                var requestRelationship =
                    primaryResourceContext.Relationships.SingleOrDefault(relationship =>
                                                                         relationship.PublicName == relationshipName);

                if (requestRelationship != null)
                {
                    currentRequest.Relationship      = requestRelationship;
                    currentRequest.SecondaryResource = resourceContextProvider.GetResourceContext(requestRelationship.RightType);
                }
            }

            currentRequest.IsCollection = currentRequest.PrimaryId == null || currentRequest.Relationship is HasManyAttribute;
        }
예제 #15
0
        /// <inheritdoc />
        public ResourceLinks GetResourceLinks(string resourceName, string id)
        {
            ArgumentGuard.NotNull(resourceName, nameof(resourceName));
            ArgumentGuard.NotNull(id, nameof(id));

            var resourceContext = _provider.GetResourceContext(resourceName);

            if (ShouldAddResourceLink(resourceContext, LinkTypes.Self))
            {
                return(new ResourceLinks {
                    Self = GetSelfResourceLink(resourceName, id)
                });
            }

            return(null);
        }
예제 #16
0
        /// <summary>
        /// Creates an instance of the referenced type in <paramref name="data"/>
        /// and sets its attributes and relationships
        /// </summary>
        /// <param name="data"></param>
        /// <returns>The parsed entity</returns>
        private IIdentifiable ParseResourceObject(ResourceObject data)
        {
            var resourceContext = _provider.GetResourceContext(data.Type);

            if (resourceContext == null)
            {
                throw new JsonApiException(400,
                                           message: $"This API does not contain a json:api resource named '{data.Type}'.",
                                           detail: "This resource is not registered on the ResourceGraph. "
                                           + "If you are using Entity Framework, make sure the DbSet matches the expected resource name. "
                                           + "If you have manually registered the resource, check that the call to AddResource correctly sets the public name.");
            }

            var entity = (IIdentifiable)Activator.CreateInstance(resourceContext.ResourceType);

            entity = SetAttributes(entity, data.Attributes, resourceContext.Attributes);
            entity = SetRelationships(entity, data.Relationships, resourceContext.Relationships);

            if (data.Id != null)
            {
                entity.StringId = data.Id?.ToString();
            }

            return(entity);
        }
        private static void SetupResourceRequest(JsonApiRequest request, ResourceContext primaryResourceContext, RouteValueDictionary routeValues,
                                                 IJsonApiOptions options, IResourceContextProvider resourceContextProvider, HttpRequest httpRequest)
        {
            request.IsReadOnly      = httpRequest.Method == HttpMethod.Get.Method || httpRequest.Method == HttpMethod.Head.Method;
            request.Kind            = EndpointKind.Primary;
            request.PrimaryResource = primaryResourceContext;
            request.PrimaryId       = GetPrimaryRequestId(routeValues);
            request.BasePath        = GetBasePath(primaryResourceContext.PublicName, options, httpRequest);

            string relationshipName = GetRelationshipNameForSecondaryRequest(routeValues);

            if (relationshipName != null)
            {
                request.Kind = IsRouteForRelationship(routeValues) ? EndpointKind.Relationship : EndpointKind.Secondary;

                RelationshipAttribute requestRelationship =
                    primaryResourceContext.Relationships.SingleOrDefault(relationship => relationship.PublicName == relationshipName);

                if (requestRelationship != null)
                {
                    request.Relationship      = requestRelationship;
                    request.SecondaryResource = resourceContextProvider.GetResourceContext(requestRelationship.RightType);
                }
            }

            bool isGetAll = request.PrimaryId == null && request.IsReadOnly;

            request.IsCollection = isGetAll || request.Relationship is HasManyAttribute;
        }
예제 #18
0
 public DefaultResourceService(
     IFieldsToSerialize fieldsToSerialize,
     IEnumerable <IQueryParameterService> queryParameters,
     IJsonApiOptions options,
     ILoggerFactory loggerFactory,
     IResourceRepository <TResource, TId> repository,
     IResourceContextProvider provider,
     IResourceChangeTracker <TResource> resourceChangeTracker,
     IResourceFactory resourceFactory,
     IResourceHookExecutor hookExecutor = null)
 {
     _fieldsToSerialize   = fieldsToSerialize;
     _includeService      = queryParameters.FirstOrDefault <IIncludeService>();
     _sparseFieldsService = queryParameters.FirstOrDefault <ISparseFieldsService>();
     _pageService         = queryParameters.FirstOrDefault <IPageService>();
     _sortService         = queryParameters.FirstOrDefault <ISortService>();
     _filterService       = queryParameters.FirstOrDefault <IFilterService>();
     _options             = options;
     _logger                 = loggerFactory.CreateLogger <DefaultResourceService <TResource, TId> >();
     _repository             = repository;
     _resourceChangeTracker  = resourceChangeTracker;
     _resourceFactory        = resourceFactory;
     _hookExecutor           = hookExecutor;
     _currentRequestResource = provider.GetResourceContext <TResource>();
 }
예제 #19
0
        public CarExpressionRewriter(IResourceContextProvider resourceContextProvider)
        {
            ResourceContext carResourceContext = resourceContextProvider.GetResourceContext <Car>();

            _regionIdAttribute = carResourceContext.Attributes.Single(attribute => attribute.Property.Name == nameof(Car.RegionId));

            _licensePlateAttribute = carResourceContext.Attributes.Single(attribute => attribute.Property.Name == nameof(Car.LicensePlate));
        }
예제 #20
0
        protected virtual IOperationProcessor ResolveProcessor(OperationContainer operation)
        {
            Type            processorInterface = GetProcessorInterface(operation.Kind);
            ResourceContext resourceContext    = _resourceContextProvider.GetResourceContext(operation.Resource.GetType());

            Type processorType = processorInterface.MakeGenericType(resourceContext.ResourceType, resourceContext.IdentityType);

            return((IOperationProcessor)_serviceProvider.GetRequiredService(processorType));
        }
        protected virtual object ResolveReadRepository(Type resourceType)
        {
            var resourceContext = _resourceContextProvider.GetResourceContext(resourceType);

            if (resourceContext.IdentityType == typeof(int))
            {
                var intRepositoryType = typeof(IResourceReadRepository <>).MakeGenericType(resourceContext.ResourceType);
                var intRepository     = _serviceProvider.GetService(intRepositoryType);

                if (intRepository != null)
                {
                    return(intRepository);
                }
            }

            var resourceDefinitionType = typeof(IResourceReadRepository <,>).MakeGenericType(resourceContext.ResourceType, resourceContext.IdentityType);

            return(_serviceProvider.GetRequiredService(resourceDefinitionType));
        }
        private ResourceContext GetResourceContext(string resourceName)
        {
            var resourceContext = _resourceContextProvider.GetResourceContext(resourceName);

            if (resourceContext == null)
            {
                throw new QueryParseException($"Resource type '{resourceName}' does not exist.");
            }

            return(resourceContext);
        }
        protected ResourceContext GetResourceContextForScope(ResourceFieldChainExpression scope)
        {
            if (scope == null)
            {
                return(RequestResource);
            }

            var lastField = scope.Fields.Last();
            var type      = lastField is RelationshipAttribute relationship ? relationship.RightType : lastField.Property.PropertyType;

            return(_resourceContextProvider.GetResourceContext(type));
        }
        private IReadOnlyCollection <IncludeElementExpression> ProcessIncludeSet(IReadOnlyCollection <IncludeElementExpression> includeElements,
                                                                                 QueryLayer parentLayer, ICollection <RelationshipAttribute> parentRelationshipChain, ExpressionInScope[] constraints)
        {
            includeElements = GetIncludeElements(includeElements, parentLayer.ResourceContext) ?? Array.Empty <IncludeElementExpression>();

            var updatesInChildren = new Dictionary <IncludeElementExpression, IReadOnlyCollection <IncludeElementExpression> >();

            foreach (var includeElement in includeElements)
            {
                parentLayer.Projection ??= new Dictionary <ResourceFieldAttribute, QueryLayer>();

                if (!parentLayer.Projection.ContainsKey(includeElement.Relationship))
                {
                    var relationshipChain = new List <RelationshipAttribute>(parentRelationshipChain)
                    {
                        includeElement.Relationship
                    };

                    var expressionsInCurrentScope = constraints
                                                    .Where(c => c.Scope != null && c.Scope.Fields.SequenceEqual(relationshipChain))
                                                    .Select(expressionInScope => expressionInScope.Expression)
                                                    .ToArray();

                    var resourceContext =
                        _resourceContextProvider.GetResourceContext(includeElement.Relationship.RightType);

                    var child = new QueryLayer(resourceContext)
                    {
                        Filter     = GetFilter(expressionsInCurrentScope, resourceContext),
                        Sort       = GetSort(expressionsInCurrentScope, resourceContext),
                        Pagination = ((JsonApiOptions)_options).DisableChildrenPagination
                            ? null
                            : GetPagination(expressionsInCurrentScope, resourceContext),
                        Projection = GetSparseFieldSetProjection(expressionsInCurrentScope, resourceContext)
                    };

                    parentLayer.Projection.Add(includeElement.Relationship, child);

                    if (includeElement.Children.Any())
                    {
                        var updatedChildren = ProcessIncludeSet(includeElement.Children, child, relationshipChain, constraints);

                        if (!ReferenceEquals(includeElement.Children, updatedChildren))
                        {
                            updatesInChildren.Add(includeElement, updatedChildren);
                        }
                    }
                }
            }

            return(!updatesInChildren.Any() ? includeElements : ApplyIncludeElementUpdates(includeElements, updatesInChildren));
        }
예제 #25
0
        /// <inheritdoc />
        public ResourceLinks GetResourceLinks(string resourceName, string id)
        {
            if (resourceName == null)
            {
                throw new ArgumentNullException(nameof(resourceName));
            }
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            var resourceContext = _provider.GetResourceContext(resourceName);

            if (ShouldAddResourceLink(resourceContext, LinkTypes.Self))
            {
                return(new ResourceLinks {
                    Self = GetSelfResourceLink(resourceName, id)
                });
            }

            return(null);
        }
예제 #26
0
        private void ValidateIncomingResourceType(object model, HttpRequest httpRequest)
        {
            Type endpointResourceType = GetResourceTypeFromEndpoint();

            if (endpointResourceType == null)
            {
                return;
            }

            IEnumerable <Type> bodyResourceTypes = GetResourceTypesFromRequestBody(model);

            foreach (Type bodyResourceType in bodyResourceTypes)
            {
                if (!endpointResourceType.IsAssignableFrom(bodyResourceType))
                {
                    ResourceContext resourceFromEndpoint = _resourceContextProvider.GetResourceContext(endpointResourceType);
                    ResourceContext resourceFromBody     = _resourceContextProvider.GetResourceContext(bodyResourceType);

                    throw new ResourceTypeMismatchException(new HttpMethod(httpRequest.Method), httpRequest.Path, resourceFromEndpoint, resourceFromBody);
                }
            }
        }
예제 #27
0
        private FilterExpression ParseFilterInHas(HasManyAttribute hasManyRelationship)
        {
            ResourceContext outerScopeBackup = _resourceContextInScope;

            Type innerResourceType = hasManyRelationship.RightType;

            _resourceContextInScope = _resourceContextProvider.GetResourceContext(innerResourceType);

            FilterExpression filter = ParseFilter();

            _resourceContextInScope = outerScopeBackup;
            return(filter);
        }
예제 #28
0
        /// <inheritdoc/>
        public ResourceObject Build(IIdentifiable entity, IEnumerable <AttrAttribute> attributes = null, IEnumerable <RelationshipAttribute> relationships = null)
        {
            var resourceContext = _provider.GetResourceContext(entity.GetType());

            // populating the top-level "type" and "id" members.
            var ro = new ResourceObject {
                Type = resourceContext.ResourceName, Id = entity.StringId.NullIfEmpty()
            };

            // populating the top-level "attribute" member of a resource object. never include "id" as an attribute
            if (attributes != null && (attributes = attributes.Where(attr => attr.PropertyInfo.Name != _identifiablePropertyName)).Any())
            {
                ProcessAttributes(entity, attributes, ro);
            }

            // populating the top-level "relationship" member of a resource object.
            if (relationships != null)
            {
                ProcessRelationships(entity, relationships, ro);
            }

            return(ro);
        }
        private static ResourceContext CreateResourceContext(RouteValueDictionary routeValues,
                                                             IControllerResourceMapping controllerResourceMapping, IResourceContextProvider resourceGraph)
        {
            var controllerName = (string)routeValues["controller"];

            if (controllerName == null)
            {
                return(null);
            }

            var resourceType = controllerResourceMapping.GetAssociatedResource(controllerName);

            return(resourceGraph.GetResourceContext(resourceType));
        }
        private Expression ProcessRelationshipChain(ResourceFieldChainExpression chain, Expression source)
        {
            string     path   = null;
            Expression result = source;

            foreach (RelationshipAttribute relationship in chain.Fields.Cast <RelationshipAttribute>())
            {
                path = path == null ? relationship.RelationshipPath : path + "." + relationship.RelationshipPath;

                ResourceContext resourceContext = _resourceContextProvider.GetResourceContext(relationship.RightType);
                result = ApplyEagerLoads(result, resourceContext.EagerLoads, path);
            }

            return(IncludeExtensionMethodCall(result, path));
        }