private ResourceObject TryGetBuiltResourceObjectFor(IIdentifiable resource)
        {
            Type            resourceType    = resource.GetType();
            ResourceContext resourceContext = ResourceContextProvider.GetResourceContext(resourceType);

            return(_included.SingleOrDefault(resourceObject => resourceObject.Type == resourceContext.PublicName && resourceObject.Id == resource.StringId));
        }
        /// <inheritdoc />
        public IList <ResourceObject> Build()
        {
            if (_included.Any())
            {
                // cleans relationship dictionaries and adds links of resources.
                foreach (var resourceObject in _included)
                {
                    if (resourceObject.Relationships != null)
                    {
                        foreach (var relationshipName in resourceObject.Relationships.Keys.ToArray())
                        {
                            var resourceContext = ResourceContextProvider.GetResourceContext(resourceObject.Type);
                            var relationship    = resourceContext.Relationships.Single(rel => rel.PublicName == relationshipName);

                            if (!IsRelationshipInSparseFieldSet(relationship))
                            {
                                resourceObject.Relationships.Remove(relationshipName);
                            }
                        }

                        // removes relationship entries (<see cref="RelationshipEntry"/>s) if they're completely empty.
                        var pruned = resourceObject.Relationships.Where(p => p.Value.IsPopulated || p.Value.Links != null).ToDictionary(p => p.Key, p => p.Value);
                        if (!pruned.Any())
                        {
                            pruned = null;
                        }
                        resourceObject.Relationships = pruned;
                    }
                    resourceObject.Links = _linkBuilder.GetResourceLinks(resourceObject.Type, resourceObject.Id);
                }
                return(_included.ToArray());
            }
            return(null);
        }
Example #3
0
        /// <summary>
        /// Sets a HasOne relationship on a parsed resource. If present, also
        /// populates the foreign key.
        /// </summary>
        private void SetHasOneRelationship(IIdentifiable resource,
                                           PropertyInfo[] resourceProperties,
                                           HasOneAttribute attr,
                                           RelationshipEntry relationshipData)
        {
            var rio       = (ResourceIdentifierObject)relationshipData.Data;
            var relatedId = rio?.Id;

            var relationshipType = relationshipData.SingleData == null
                ? attr.RightType
                : ResourceContextProvider.GetResourceContext(relationshipData.SingleData.Type).ResourceType;

            // this does not make sense in the following case: if we're setting the dependent of a one-to-one relationship, IdentifiablePropertyName should be null.
            var foreignKeyProperty = resourceProperties.FirstOrDefault(p => p.Name == attr.IdentifiablePropertyName);

            if (foreignKeyProperty != null)
            {
                // there is a FK from the current resource pointing to the related object,
                // i.e. we're populating the relationship from the dependent side.
                SetForeignKey(resource, foreignKeyProperty, attr, relatedId, relationshipType);
            }

            SetNavigation(resource, attr, relatedId, relationshipType);

            // depending on if this base parser is used client-side or server-side,
            // different additional processing per field needs to be executed.
            AfterProcessField(resource, attr, relationshipData);
        }
Example #4
0
        private bool IsRelationshipInSparseFieldSet(RelationshipAttribute relationship)
        {
            ResourceContext resourceContext = ResourceContextProvider.GetResourceContext(relationship.LeftType);

            IReadOnlyCollection <ResourceFieldAttribute> fieldSet = _sparseFieldSetCache.GetSparseFieldSetForSerializer(resourceContext);

            return(fieldSet.Contains(relationship));
        }
Example #5
0
        private bool IsRelationshipInSparseFieldSet(RelationshipAttribute relationship)
        {
            var resourceContext = ResourceContextProvider.GetResourceContext(relationship.LeftType);

            var fieldSet = _sparseFieldSetCache.GetSparseFieldSetForSerializer(resourceContext);

            return(fieldSet.Contains(relationship));
        }
Example #6
0
        protected ResourceContext GetExistingResourceContext(string publicName)
        {
            var resourceContext = ResourceContextProvider.GetResourceContext(publicName);

            if (resourceContext == null)
            {
                throw new JsonApiSerializationException("Request body includes unknown resource type.",
                                                        $"Resource type '{publicName}' does not exist.", atomicOperationIndex: AtomicOperationIndex);
            }

            return(resourceContext);
        }
Example #7
0
        private OperationContainer ParseForRelationshipOperation(AtomicOperationObject operation, OperationKind kind,
                                                                 bool requireToMany)
        {
            AssertElementHasType(operation.Ref, "ref");
            AssertElementHasIdOrLid(operation.Ref, "ref", true);

            var primaryResourceContext = GetExistingResourceContext(operation.Ref.Type);

            AssertCompatibleId(operation.Ref, primaryResourceContext.IdentityType);

            var primaryResource = ResourceFactory.CreateInstance(primaryResourceContext.ResourceType);

            primaryResource.StringId = operation.Ref.Id;
            primaryResource.LocalId  = operation.Ref.Lid;

            var relationship = GetExistingRelationship(operation.Ref, primaryResourceContext);

            if (requireToMany && relationship is HasOneAttribute)
            {
                throw new JsonApiSerializationException(
                          $"Only to-many relationships can be targeted in '{operation.Code.ToString().Camelize()}' operations.",
                          $"Relationship '{operation.Ref.Relationship}' must be a to-many relationship.",
                          atomicOperationIndex: AtomicOperationIndex);
            }

            var secondaryResourceContext = ResourceContextProvider.GetResourceContext(relationship.RightType);

            var request = new JsonApiRequest
            {
                Kind              = EndpointKind.AtomicOperations,
                BasePath          = _request.BasePath,
                PrimaryId         = primaryResource.StringId,
                PrimaryResource   = primaryResourceContext,
                SecondaryResource = secondaryResourceContext,
                Relationship      = relationship,
                IsCollection      = relationship is HasManyAttribute,
                OperationKind     = kind
            };

            _request.CopyFrom(request);

            _targetedFields.Relationships.Add(relationship);

            ParseDataForRelationship(relationship, secondaryResourceContext, operation, primaryResource);

            var targetedFields = new TargetedFields
            {
                Attributes    = _targetedFields.Attributes.ToHashSet(),
                Relationships = _targetedFields.Relationships.ToHashSet()
            };

            return(new OperationContainer(kind, primaryResource, targetedFields, request));
        }
        /// <summary>
        /// Gets the resource object for <paramref name="parent"/> by searching the included list.
        /// If it was not already built, it is constructed and added to the inclusion list.
        /// </summary>
        private ResourceObject GetOrBuildResourceObject(IIdentifiable parent, RelationshipAttribute relationship)
        {
            var type         = parent.GetType();
            var resourceName = ResourceContextProvider.GetResourceContext(type).PublicName;
            var entry        = _included.SingleOrDefault(ro => ro.Type == resourceName && ro.Id == parent.StringId);

            if (entry == null)
            {
                entry = Build(parent, _fieldsToSerialize.GetAttributes(type, relationship), _fieldsToSerialize.GetRelationships(type));
                _included.Add(entry);
            }
            return(entry);
        }
        private void UpdateRelationships(ResourceObject resourceObject)
        {
            foreach (string relationshipName in resourceObject.Relationships.Keys.ToArray())
            {
                ResourceContext       resourceContext = ResourceContextProvider.GetResourceContext(resourceObject.Type);
                RelationshipAttribute relationship    = resourceContext.Relationships.Single(rel => rel.PublicName == relationshipName);

                if (!IsRelationshipInSparseFieldSet(relationship))
                {
                    resourceObject.Relationships.Remove(relationshipName);
                }
            }

            resourceObject.Relationships = PruneRelationshipEntries(resourceObject);
        }
Example #10
0
        /// <summary>
        /// Sets a HasMany relationship.
        /// </summary>
        private void SetHasManyRelationship(
            IIdentifiable resource,
            HasManyAttribute attr,
            RelationshipEntry relationshipData)
        {
            if (relationshipData.Data != null)
            {   // if the relationship is set to null, no need to set the navigation property to null: this is the default value.
                var relatedResources = relationshipData.ManyData.Select(rio =>
                {
                    var relationshipType     = ResourceContextProvider.GetResourceContext(rio.Type).ResourceType;
                    var relatedInstance      = (IIdentifiable)ResourceFactory.CreateInstance(relationshipType);
                    relatedInstance.StringId = rio.Id;

                    return(relatedInstance);
                });

                var convertedCollection = TypeHelper.CopyToTypedCollection(relatedResources, attr.Property.PropertyType);
                attr.SetValue(resource, convertedCollection, ResourceFactory);
            }

            AfterProcessField(resource, attr, relationshipData);
        }
        /// <summary>
        /// Searches for and parses the included relationship.
        /// </summary>
        private IIdentifiable ParseIncludedRelationship(ResourceIdentifierObject relatedResourceIdentifier)
        {
            var relatedResourceContext = ResourceContextProvider.GetResourceContext(relatedResourceIdentifier.Type);

            if (relatedResourceContext == null)
            {
                throw new InvalidOperationException($"Included type '{relatedResourceIdentifier.Type}' is not a registered JSON:API resource.");
            }

            var relatedInstance = ResourceFactory.CreateInstance(relatedResourceContext.ResourceType);

            relatedInstance.StringId = relatedResourceIdentifier.Id;

            var includedResource = GetLinkedResource(relatedResourceIdentifier);

            if (includedResource != null)
            {
                SetAttributes(relatedInstance, includedResource.Attributes, relatedResourceContext.Attributes);
                SetRelationships(relatedInstance, includedResource.Relationships, relatedResourceContext.Relationships);
            }

            return(relatedInstance);
        }
Example #12
0
        /// <summary>
        /// Creates an instance of the referenced type in <paramref name="data"/>
        /// and sets its attributes and relationships.
        /// </summary>
        /// <returns>The parsed resource.</returns>
        private IIdentifiable ParseResourceObject(ResourceObject data)
        {
            var resourceContext = ResourceContextProvider.GetResourceContext(data.Type);

            if (resourceContext == null)
            {
                throw new InvalidRequestBodyException("Payload includes unknown resource type.",
                                                      $"The resource '{data.Type}' is not registered on the resource graph. " +
                                                      "If you are using Entity Framework Core, make sure the DbSet matches the expected resource name. " +
                                                      "If you have manually registered the resource, check that the call to Add correctly sets the public name.", null);
            }

            var resource = (IIdentifiable)ResourceFactory.CreateInstance(resourceContext.ResourceType);

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

            if (data.Id != null)
            {
                resource.StringId = data.Id;
            }

            return(resource);
        }