Пример #1
0
        private void AssertAttribute <TPropertyType, TTokenType>(IResourceTypeRegistration reg, string attributeName,
                                                                 JToken tokenToSet, TPropertyType expectedPropertyValue, TTokenType expectedTokenAfterSet, Func <AttributeGrabBag, TPropertyType> getPropertyFunc)
        {
            var grabBag = InitializeGrabBag();

            var field     = reg.GetFieldByName(attributeName);
            var attribute = (ResourceTypeAttribute)field;

            attribute.JsonKey.Should().Be(attributeName);

            attribute.SetValue(grabBag, tokenToSet);
            var propertyValueAfterSet = getPropertyFunc(grabBag);

            propertyValueAfterSet.Should().Be(expectedPropertyValue);

            var convertedToken = attribute.GetValue(grabBag);

            if (expectedTokenAfterSet == null)
            {
                convertedToken.Should().BeNull();
            }
            else
            {
                var convertedTokenValue = convertedToken.Value <TTokenType>();
                convertedTokenValue.Should().Be(expectedTokenAfterSet);
            }
        }
Пример #2
0
        public void AddRegistration(IResourceTypeRegistration registration)
        {
            lock (_registrationsByType)
            {
                lock (_registrationsByName)
                {
                    if (_registrationsByType.ContainsKey(registration.Type))
                    {
                        throw new InvalidOperationException(String.Format("The type `{0}` has already been registered.",
                                                                          registration.Type.FullName));
                    }

                    IResourceTypeRegistration existingRegistration;
                    if (_registrationsByName.TryGetValue(registration.ResourceTypeName, out existingRegistration))
                    {
                        throw new InvalidOperationException(
                                  String.Format("Could not register `{0} under resource type name `{1}` because `{1}` has already been registered by `{2}`.",
                                                registration.Type.FullName,
                                                registration.ResourceTypeName,
                                                existingRegistration.Type.FullName));
                    }

                    _registrationsByType.Add(registration.Type, registration);
                    _registrationsByName.Add(registration.ResourceTypeName, registration);
                }
            }
        }
Пример #3
0
        /// <inheritdoc />
        public object CreateEphemeralResource(IResourceTypeRegistration resourceTypeRegistration, string id)
        {
            var obj = Activator.CreateInstance(resourceTypeRegistration.Type);

            resourceTypeRegistration.SetIdForResource(obj, id);
            return(obj);
        }
 public StarshipOfficersRelatedResourceMaterializer(ResourceTypeRelationship relationship, DbContext dbContext,
                                                    IQueryableResourceCollectionDocumentBuilder queryableResourceCollectionDocumentBuilder,
                                                    ISortExpressionExtractor sortExpressionExtractor,
                                                    IResourceTypeRegistration primaryTypeRegistration)
     : base(relationship, dbContext, queryableResourceCollectionDocumentBuilder, sortExpressionExtractor, primaryTypeRegistration)
 {
     _dbContext = dbContext;
 }
        /// <summary>
        /// Gets an existing record from the store by ID, if it exists
        /// </summary>
        /// <returns></returns>
        protected virtual async Task <object> GetExistingRecord(IResourceTypeRegistration registration, string id,
                                                                ResourceTypeRelationship[] relationshipsToInclude, CancellationToken cancellationToken)
        {
            var method = _openGetExistingRecordGenericMethod.MakeGenericMethod(registration.Type);
            var result = (dynamic)method.Invoke(this, new object[] { registration, id, relationshipsToInclude, cancellationToken });

            return(await result);
        }
Пример #6
0
 public StarshipShipCounselorRelatedResourceMaterializer(
     ISingleResourceDocumentBuilder singleResourceDocumentBuilder, IBaseUrlService baseUrlService,
     IResourceTypeRegistration primaryTypeRegistration, ResourceTypeRelationship relationship,
     DbContext dbContext)
     : base(singleResourceDocumentBuilder, baseUrlService, primaryTypeRegistration, relationship, dbContext)
 {
     _dbContext = dbContext;
 }
Пример #7
0
        private IQueryable <TResource> FilterById <TResource>(string id, IResourceTypeRegistration resourceTypeRegistration,
                                                              params Expression <Func <TResource, object> >[] includes) where TResource : class
        {
            var param = Expression.Parameter(typeof(TResource));
            var filterByIdExpression = resourceTypeRegistration.GetFilterByIdExpression(param, id);
            var predicate            = Expression.Lambda <Func <TResource, bool> >(filterByIdExpression, param);

            return(Filter(predicate, includes));
        }
Пример #8
0
        private IQueryable <TResource> FilterById <TResource>(string id,
                                                              IResourceTypeRegistration resourceTypeRegistration) where TResource : class
        {
            var param = Expression.Parameter(typeof(TResource));
            var filterByIdExpression     = resourceTypeRegistration.GetFilterByIdExpression(param, id);
            var predicate                = Expression.Lambda <Func <TResource, bool> >(filterByIdExpression, param);
            IQueryable <TResource> query = _dbContext.Set <TResource>();

            return(query.Where(predicate));
        }
 /// <summary>
 /// Builds a new EntityFrameworkToOneRelatedResourceDocumentMaterializer
 /// </summary>
 public EntityFrameworkToOneRelatedResourceDocumentMaterializer(
     ISingleResourceDocumentBuilder singleResourceDocumentBuilder, IBaseUrlService baseUrlService,
     IResourceTypeRegistration primaryTypeRegistration, ResourceTypeRelationship relationship,
     DbContext dbContext)
     : base(singleResourceDocumentBuilder, baseUrlService)
 {
     _primaryTypeRegistration = primaryTypeRegistration;
     _relationship            = relationship;
     _dbContext = dbContext;
 }
        protected override Task SetIdForNewResource(IResourceObject resourceObject, object newObject, IResourceTypeRegistration typeRegistration)
        {
            // This is to facilitate testing creation of a resource with a server-provided ID
            if (typeRegistration.Type == typeof (Post) && String.IsNullOrEmpty(resourceObject.Id))
            {
                ((Post) newObject).Id = "230";
                return Task.FromResult(0);
            }

            return base.SetIdForNewResource(resourceObject, newObject, typeRegistration);
        }
 /// <summary>
 /// Builds a new EntityFrameworkToManyRelatedResourceDocumentMaterializer.
 /// </summary>
 public EntityFrameworkToManyRelatedResourceDocumentMaterializer(
     ResourceTypeRelationship relationship,
     DbContext dbContext,
     IQueryableResourceCollectionDocumentBuilder queryableResourceCollectionDocumentBuilder,
     ISortExpressionExtractor sortExpressionExtractor,
     IResourceTypeRegistration primaryTypeRegistration)
     : base(queryableResourceCollectionDocumentBuilder, sortExpressionExtractor)
 {
     _relationship            = relationship;
     _dbContext               = dbContext;
     _primaryTypeRegistration = primaryTypeRegistration;
 }
Пример #12
0
 /// <summary>
 /// Creates a new EntityFrameworkDocumentMaterializer
 /// </summary>
 public EntityFrameworkDocumentMaterializer(
     DbContext dbContext,
     IResourceTypeRegistration resourceTypeRegistration,
     IQueryableResourceCollectionDocumentBuilder queryableResourceCollectionDocumentBuilder,
     ISingleResourceDocumentBuilder singleResourceDocumentBuilder,
     IEntityFrameworkResourceObjectMaterializer entityFrameworkResourceObjectMaterializer,
     ISortExpressionExtractor sortExpressionExtractor,
     IBaseUrlService baseUrlService)
 {
     _dbContext = dbContext;
     _resourceTypeRegistration = resourceTypeRegistration;
     _queryableResourceCollectionDocumentBuilder = queryableResourceCollectionDocumentBuilder;
     _singleResourceDocumentBuilder             = singleResourceDocumentBuilder;
     _entityFrameworkResourceObjectMaterializer = entityFrameworkResourceObjectMaterializer;
     _sortExpressionExtractor = sortExpressionExtractor;
     _baseUrlService          = baseUrlService;
 }
        /// <summary>
        /// Gets a record by ID
        /// </summary>
        protected async Task <TRecord> GetExistingRecordGeneric <TRecord>(IResourceTypeRegistration registration,
                                                                          string id, ResourceTypeRelationship[] relationshipsToInclude, CancellationToken cancellationToken) where TRecord : class
        {
            var param            = Expression.Parameter(registration.Type);
            var filterExpression = registration.GetFilterByIdExpression(param, id);
            var lambda           = Expression.Lambda <Func <TRecord, bool> >(filterExpression, param);
            var query            = _dbContext.Set <TRecord>().AsQueryable()
                                   .Where(lambda);

            if (relationshipsToInclude != null)
            {
                query = relationshipsToInclude.Aggregate(query,
                                                         (current, resourceTypeRelationship) => current.Include(resourceTypeRelationship.Property.Name));
            }

            return(await query.FirstOrDefaultAsync(cancellationToken));
        }
Пример #14
0
        private void AssertAttributeHelper(IResourceTypeRegistration reg, string attributeName,
                                           JToken tokenToSet, Action <AttributeGrabBag> testPropertyValueAfterSet,
                                           Action <JToken> testTokenAfterSetAndGet)
        {
            var grabBag = InitializeGrabBag();

            var field     = reg.GetFieldByName(attributeName);
            var attribute = (ResourceTypeAttribute)field;

            attribute.JsonKey.Should().Be(attributeName);

            attribute.SetValue(grabBag, tokenToSet);
            testPropertyValueAfterSet(grabBag);

            var convertedToken = attribute.GetValue(grabBag);

            testTokenAfterSetAndGet(convertedToken);
        }
Пример #15
0
        public void AddRegistration(IResourceTypeRegistration registration)
        {
            lock (_registrationsByType)
            {
                lock (_registrationsByName)
                {
                    if (_registrationsByType.ContainsKey(registration.Type))
                        throw new InvalidOperationException(String.Format("The type `{0}` has already been registered.",
                            registration.Type.FullName));

                    if (_registrationsByName.ContainsKey(registration.ResourceTypeName))
                        throw new InvalidOperationException(
                            String.Format("The resource type name `{0}` has already been registered.",
                                registration.ResourceTypeName));

                    _registrationsByType.Add(registration.Type, registration);
                    _registrationsByName.Add(registration.ResourceTypeName, registration);
                }
            }
        }
Пример #16
0
 private void AssertAttribute <TPropertyType, TTokenType>(IResourceTypeRegistration reg, string attributeName,
                                                          JToken tokenToSet, TPropertyType expectedPropertyValue, TTokenType expectedTokenAfterSet, Func <AttributeGrabBag, TPropertyType> getPropertyFunc)
 {
     AssertAttributeHelper(reg, attributeName, tokenToSet, grabBag =>
     {
         var propertyValueAfterSet = getPropertyFunc(grabBag);
         propertyValueAfterSet.Should().Be(expectedPropertyValue);
     }, token =>
     {
         if (expectedTokenAfterSet == null)
         {
             token.Should().BeNull();
         }
         else
         {
             var convertedTokenValue = token.Value <TTokenType>();
             convertedTokenValue.Should().Be(expectedTokenAfterSet);
         }
     });
 }
Пример #17
0
        private Expression GetPredicate(string filterField, IResourceTypeRegistration registration, ParameterExpression param, string queryValue)
        {
            if (filterField == "id")
            {
                return(GetPredicateBodyForProperty(registration.IdProperty, queryValue, param));
            }

            var resourceTypeField = registration.GetFieldByName(filterField);

            if (resourceTypeField == null)
            {
                throw JsonApiException.CreateForBadRequest(
                          string.Format("No attribute {0} exists on the specified type.", filterField));
            }

            if (string.IsNullOrWhiteSpace(queryValue))
            {
                queryValue = null;
            }

            // See if it is a field property
            var fieldModelProperty = resourceTypeField as ResourceTypeAttribute;

            if (fieldModelProperty != null)
            {
                return(GetPredicateBodyForField(fieldModelProperty, queryValue, param));
            }

            // See if it is a relationship property
            var relationshipModelProperty = resourceTypeField as ResourceTypeRelationship;

            if (relationshipModelProperty != null)
            {
                return(GetPredicateBodyForRelationship(relationshipModelProperty, queryValue, param));
            }

            throw JsonApiException.CreateForBadRequest(
                      string.Format("The attribute {0} is unsupported for filtering.", filterField));
        }
Пример #18
0
 /// <summary>
 /// Allows implementers to control how a new resource's ID should be set.
 /// </summary>
 protected virtual Task SetIdForNewResource(IResourceObject resourceObject, object newObject, IResourceTypeRegistration typeRegistration)
 {
     if (resourceObject.Id != null)
     {
         typeRegistration.IdProperty.SetValue(newObject, Convert.ChangeType(resourceObject.Id, typeRegistration.IdProperty.PropertyType));
     }
     return(Task.FromResult(0));
 }
        /// <summary>
        /// Merges the field values of the given resource object into the materialized object
        /// </summary>
        /// <param name="resourceObject"></param>
        /// <param name="material"></param>
        /// <param name="registration"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="DeserializationException">Thrown when a semantically incorrect part of the document is encountered</exception>
        protected virtual async Task MergeFieldsIntoProperties(IResourceObject resourceObject, object material,
                                                               IResourceTypeRegistration registration, CancellationToken cancellationToken)
        {
            foreach (var attributeValue in resourceObject.Attributes)
            {
                var attribute = registration.GetFieldByName(attributeValue.Key) as ResourceTypeAttribute;
                if (attribute == null)
                {
                    continue;
                }
                attribute.SetValue(material, attributeValue.Value);
            }

            foreach (var relationshipValue in resourceObject.Relationships)
            {
                var linkage = relationshipValue.Value.Linkage;

                var typeRelationship = registration.GetFieldByName(relationshipValue.Key) as ResourceTypeRelationship;
                if (typeRelationship == null)
                {
                    continue;
                }

                if (typeRelationship.IsToMany)
                {
                    if (linkage == null)
                    {
                        throw new DeserializationException("Missing linkage for to-many relationship",
                                                           "Expected an array for to-many linkage, but no linkage was specified.", "/data/relationships/" + relationshipValue.Key);
                    }

                    if (!linkage.IsToMany)
                    {
                        throw new DeserializationException("Invalid linkage for to-many relationship",
                                                           "Expected an array for to-many linkage.",
                                                           "/data/relationships/" + relationshipValue.Key + "/data");
                    }

                    // TODO: One query per related object is going to be slow. At the very least, we should be able to group the queries by type
                    var newCollection = new List <object>();
                    foreach (var resourceIdentifier in linkage.Identifiers)
                    {
                        var relatedObjectRegistration = _registry.GetRegistrationForResourceTypeName(resourceIdentifier.Type);
                        var relatedObject             = await GetExistingRecord(relatedObjectRegistration, resourceIdentifier.Id, null, cancellationToken);

                        newCollection.Add(relatedObject);
                    }

                    var method = _openSetToManyRelationshipValueMethod.MakeGenericMethod(typeRelationship.RelatedType);
                    method.Invoke(this, new[] { material, newCollection, typeRelationship });
                }
                else
                {
                    if (linkage == null)
                    {
                        throw new DeserializationException("Missing linkage for to-one relationship",
                                                           "Expected an object for to-one linkage, but no linkage was specified.", "/data/relationships/" + relationshipValue.Key);
                    }

                    if (linkage.IsToMany)
                    {
                        throw new DeserializationException("Invalid linkage for to-one relationship",
                                                           "Expected an object or null for to-one linkage",
                                                           "/data/relationships/" + relationshipValue.Key + "/data");
                    }

                    var identifier = linkage.Identifiers.FirstOrDefault();
                    if (identifier == null)
                    {
                        typeRelationship.Property.SetValue(material, null);
                    }
                    else
                    {
                        var relatedObjectRegistration = _registry.GetRegistrationForResourceTypeName(identifier.Type);
                        var relatedObject             =
                            await GetExistingRecord(relatedObjectRegistration, identifier.Id, null, cancellationToken);

                        typeRelationship.Property.SetValue(material, relatedObject);
                    }
                }
            }
        }
        /// <summary>
        /// Allows implementers to control how a new resource's ID should be set.
        /// </summary>
        protected virtual Task SetIdForNewResource(IResourceObject resourceObject, object newObject, IResourceTypeRegistration typeRegistration)
        {
            typeRegistration.IdProperty.SetValue(newObject, resourceObject.Id);

            return(Task.FromResult(0));
        }
        protected override Task SetIdForNewResource(IResourceObject resourceObject, object newObject, IResourceTypeRegistration typeRegistration)
        {
            // This is to facilitate testing creation of a resource with a server-provided ID
            if (typeRegistration.Type == typeof(Post) && String.IsNullOrEmpty(resourceObject.Id))
            {
                ((Post)newObject).Id = "230";
                return(Task.FromResult(0));
            }

            return(base.SetIdForNewResource(resourceObject, newObject, typeRegistration));
        }