/// <summary>
        /// Additional processing required for server deserialization. Flags a processed attribute or relationship as updated using
        /// <see cref="ITargetedFields" />.
        /// </summary>
        /// <param name="resource">
        /// The resource that was constructed from the document's body.
        /// </param>
        /// <param name="field">
        /// The metadata for the exposed field.
        /// </param>
        /// <param name="data">
        /// Relationship data for <paramref name="resource" />. Is null when <paramref name="field" /> is not a <see cref="RelationshipAttribute" />.
        /// </param>
        protected override void AfterProcessField(IIdentifiable resource, ResourceFieldAttribute field, RelationshipEntry data = null)
        {
            bool isCreatingResource = IsCreatingResource();
            bool isUpdatingResource = IsUpdatingResource();

            if (field is AttrAttribute attr)
            {
                if (isCreatingResource && !attr.Capabilities.HasFlag(AttrCapabilities.AllowCreate))
                {
                    throw new JsonApiSerializationException("Setting the initial value of the requested attribute is not allowed.",
                                                            $"Setting the initial value of '{attr.PublicName}' is not allowed.", atomicOperationIndex: AtomicOperationIndex);
                }

                if (isUpdatingResource && !attr.Capabilities.HasFlag(AttrCapabilities.AllowChange))
                {
                    throw new JsonApiSerializationException("Changing the value of the requested attribute is not allowed.",
                                                            $"Changing the value of '{attr.PublicName}' is not allowed.", atomicOperationIndex: AtomicOperationIndex);
                }

                _targetedFields.Attributes.Add(attr);
            }
            else if (field is RelationshipAttribute relationship)
            {
                _targetedFields.Relationships.Add(relationship);
            }
        }
        /// <summary>
        /// Additional processing required for client deserialization, responsible
        /// for parsing the <see cref="Document.Included"/> property. When a relationship value is parsed,
        /// it goes through the included list to set its attributes and relationships.
        /// </summary>
        /// <param name="resource">The resource that was constructed from the document's body</param>
        /// <param name="field">The metadata for the exposed field</param>
        /// <param name="data">Relationship data for <paramref name="resource"/>. Is null when <paramref name="field"/> is not a <see cref="RelationshipAttribute"/></param>
        protected override void AfterProcessField(IIdentifiable resource, ResourceFieldAttribute field, RelationshipEntry data = null)
        {
            // Client deserializers do not need additional processing for attributes.
            if (field is AttrAttribute)
            {
                return;
            }

            // if the included property is empty or absent, there is no additional data to be parsed.
            if (_document.Included == null || _document.Included.Count == 0)
            {
                return;
            }

            if (field is HasOneAttribute hasOneAttr)
            {
                // add attributes and relationships of a parsed HasOne relationship
                var rio = data.SingleData;
                hasOneAttr.SetValue(resource, rio == null ? null : ParseIncludedRelationship(hasOneAttr, rio), _resourceFactory);
            }
            else if (field is HasManyAttribute hasManyAttr)
            {  // add attributes and relationships of a parsed HasMany relationship
                var items  = data.ManyData.Select(rio => ParseIncludedRelationship(hasManyAttr, rio));
                var values = items.CopyToTypedCollection(hasManyAttr.Property.PropertyType);
                hasManyAttr.SetValue(resource, values, _resourceFactory);
            }
        }
示例#3
0
        /// <summary>
        /// Additional processing required for server deserialization. Flags a
        /// processed attribute or relationship as updated using <see cref="ITargetedFields"/>.
        /// </summary>
        /// <param name="resource">The resource that was constructed from the document's body.</param>
        /// <param name="field">The metadata for the exposed field.</param>
        /// <param name="data">Relationship data for <paramref name="resource"/>. Is null when <paramref name="field"/> is not a <see cref="RelationshipAttribute"/>.</param>
        protected override void AfterProcessField(IIdentifiable resource, ResourceFieldAttribute field, RelationshipEntry data = null)
        {
            if (field is AttrAttribute attr)
            {
                if (_httpContextAccessor.HttpContext.Request.Method == HttpMethod.Post.Method &&
                    !attr.Capabilities.HasFlag(AttrCapabilities.AllowCreate))
                {
                    throw new JsonApiSerializationException(
                              "Setting the initial value of the requested attribute is not allowed.",
                              $"Setting the initial value of '{attr.PublicName}' is not allowed.");
                }

                if (_httpContextAccessor.HttpContext.Request.Method == HttpMethod.Patch.Method &&
                    !attr.Capabilities.HasFlag(AttrCapabilities.AllowChange))
                {
                    throw new JsonApiSerializationException(
                              "Changing the value of the requested attribute is not allowed.",
                              $"Changing the value of '{attr.PublicName}' is not allowed.");
                }

                _targetedFields.Attributes.Add(attr);
            }
            else if (field is RelationshipAttribute relationship)
            {
                _targetedFields.Relationships.Add(relationship);
            }
        }
示例#4
0
        /// <summary>
        /// Additional processing required for client deserialization, responsible for parsing the <see cref="Document.Included" /> property. When a relationship
        /// value is parsed, it goes through the included list to set its attributes and relationships.
        /// </summary>
        /// <param name="resource">
        /// The resource that was constructed from the document's body.
        /// </param>
        /// <param name="field">
        /// The metadata for the exposed field.
        /// </param>
        /// <param name="data">
        /// Relationship data for <paramref name="resource" />. Is null when <paramref name="field" /> is not a <see cref="RelationshipAttribute" />.
        /// </param>
        protected override void AfterProcessField(IIdentifiable resource, ResourceFieldAttribute field, RelationshipEntry data = null)
        {
            ArgumentGuard.NotNull(resource, nameof(resource));
            ArgumentGuard.NotNull(field, nameof(field));

            // Client deserializers do not need additional processing for attributes.
            if (field is AttrAttribute)
            {
                return;
            }

            // if the included property is empty or absent, there is no additional data to be parsed.
            if (Document.Included.IsNullOrEmpty())
            {
                return;
            }

            if (data != null)
            {
                if (field is HasOneAttribute hasOneAttr)
                {
                    // add attributes and relationships of a parsed HasOne relationship
                    ResourceIdentifierObject rio = data.SingleData;
                    hasOneAttr.SetValue(resource, rio == null ? null : ParseIncludedRelationship(rio));
                }
                else if (field is HasManyAttribute hasManyAttr)
                {
                    // add attributes and relationships of a parsed HasMany relationship
                    IEnumerable <IIdentifiable> items = data.ManyData.Select(ParseIncludedRelationship);
                    IEnumerable values = CollectionConverter.CopyToTypedCollection(items, hasManyAttr.Property.PropertyType);
                    hasManyAttr.SetValue(resource, values);
                }
            }
        }
            public PropertySelector(ResourceFieldAttribute field, QueryLayer nextLayer = null)
            {
                ArgumentGuard.NotNull(field, nameof(field));

                OriginatingField = field;
                NextLayer        = nextLayer;
                Property         = field is HasManyThroughAttribute hasManyThrough ? hasManyThrough.ThroughProperty : field.Property;
            }
        protected override IReadOnlyCollection <ResourceFieldAttribute> OnResolveFieldChain(string path, FieldChainRequirements chainRequirements)
        {
            ResourceFieldAttribute field = ChainResolver.GetField(path, _resourceContext, path);

            _validateSingleFieldCallback?.Invoke(field, _resourceContext, path);

            return(field.AsArray());
        }
 protected void ValidateSingleField(ResourceFieldAttribute field, ResourceContext resourceContext, string path)
 {
     if (field is AttrAttribute attribute && !attribute.Capabilities.HasFlag(AttrCapabilities.AllowSort))
     {
         throw new InvalidQueryStringParameterException(_lastParameterName, "Sorting on the requested attribute is not allowed.",
                                                        $"Sorting on attribute '{attribute.PublicName}' is not allowed.");
     }
 }
            public PropertySelector(ResourceFieldAttribute field, QueryLayer nextLayer = null)
            {
                OriginatingField = field ?? throw new ArgumentNullException(nameof(field));
                NextLayer        = nextLayer;

                Property = field is HasManyThroughAttribute hasManyThrough
                    ? hasManyThrough.ThroughProperty
                    : field.Property;
            }
        public ResourceFieldChainExpression(ResourceFieldAttribute field)
        {
            if (field == null)
            {
                throw new ArgumentNullException(nameof(field));
            }

            Fields = new[] { field };
        }
        protected ResourceContext GetResourceContextForScope(ResourceFieldChainExpression scope)
        {
            if (scope == null)
            {
                return(RequestResource);
            }

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

            return(_resourceContextProvider.GetResourceContext(type));
        }
        protected SparseFieldSetExpression ParseSparseFieldSet()
        {
            var fields = new Dictionary <string, ResourceFieldAttribute>();

            while (TokenStack.Any())
            {
                if (fields.Count > 0)
                {
                    EatSingleCharacterToken(TokenKind.Comma);
                }

                ResourceFieldChainExpression nextChain = ParseFieldChain(FieldChainRequirements.EndsInAttribute, "Field name expected.");
                ResourceFieldAttribute       nextField = nextChain.Fields.Single();
                fields[nextField.PublicName] = nextField;
            }

            return(fields.Any() ? new SparseFieldSetExpression(fields.Values) : null);
        }
 /// <summary>
 /// Additional processing required for server deserialization. Flags a
 /// processed attribute or relationship as updated using <see cref="ITargetedFields"/>.
 /// </summary>
 /// <param name="resource">The resource that was constructed from the document's body.</param>
 /// <param name="field">The metadata for the exposed field.</param>
 /// <param name="data">Relationship data for <paramref name="resource"/>. Is null when <paramref name="field"/> is not a <see cref="RelationshipAttribute"/>.</param>
 protected override void AfterProcessField(IIdentifiable resource, ResourceFieldAttribute field, RelationshipEntry data = null)
 {
     if (field is AttrAttribute attr)
     {
         if (attr.Capabilities.HasFlag(AttrCapabilities.AllowChange))
         {
             _targetedFields.Attributes.Add(attr);
         }
         else
         {
             throw new InvalidRequestBodyException(
                       "Changing the value of the requested attribute is not allowed.",
                       $"Changing the value of '{attr.PublicName}' is not allowed.", null);
         }
     }
     else if (field is RelationshipAttribute relationship)
     {
         _targetedFields.Relationships.Add(relationship);
     }
 }
示例#13
0
 private static string GetPropertyName(ResourceFieldAttribute field)
 {
     return(field is HasManyThroughAttribute hasManyThrough ? hasManyThrough.ThroughProperty.Name : field.Property.Name);
 }
示例#14
0
 private static string GetPropertyName(ResourceFieldAttribute field)
 {
     // In case of a HasManyThrough access (from count() or has() function), we only need to look at the number of entries in the join table.
     return(field is HasManyThroughAttribute hasManyThrough ? hasManyThrough.ThroughProperty.Name : field.Property.Name);
 }
示例#15
0
        private static SparseFieldSetExpression IncludeField(SparseFieldSetExpression sparseFieldSet, ResourceFieldAttribute fieldToInclude)
        {
            if (sparseFieldSet == null || sparseFieldSet.Fields.Contains(fieldToInclude))
            {
                return(sparseFieldSet);
            }

            HashSet <ResourceFieldAttribute> fieldSet = sparseFieldSet.Fields.ToHashSet();

            fieldSet.Add(fieldToInclude);
            return(new SparseFieldSetExpression(fieldSet));
        }
示例#16
0
        public ResourceFieldChainExpression(ResourceFieldAttribute field)
        {
            ArgumentGuard.NotNull(field, nameof(field));

            Fields = field.AsArray();
        }
示例#17
0
 protected override void AfterProcessField(IIdentifiable resource, ResourceFieldAttribute field, RelationshipEntry data = null)
 {
 }
示例#18
0
        private static SparseFieldSetExpression ExcludeField(SparseFieldSetExpression sparseFieldSet, ResourceFieldAttribute fieldToExclude)
        {
            // Design tradeoff: When the sparse fieldset is empty, it means all fields will be selected.
            // Adding an exclusion in that case is a no-op, which results in still retrieving the excluded field from data store.
            // But later, when serializing the response, the sparse fieldset is first populated with all fields,
            // so then the exclusion will actually be applied and the excluded field is not returned to the client.

            if (sparseFieldSet == null || !sparseFieldSet.Fields.Contains(fieldToExclude))
            {
                return(sparseFieldSet);
            }

            HashSet <ResourceFieldAttribute> fieldSet = sparseFieldSet.Fields.ToHashSet();

            fieldSet.Remove(fieldToExclude);
            return(new SparseFieldSetExpression(fieldSet));
        }