/// <summary> /// Extracts the resources for the current layer by going through all populated relationships /// of the (left resources of the previous layer. /// </summary> private (Dictionary <RelationshipProxy, List <IIdentifiable> >, Dictionary <RelationshipProxy, List <IIdentifiable> >) ExtractResources(IEnumerable <IResourceNode> leftNodes) { // RelationshipAttr_prevLayer->currentLayer => prevLayerResources var leftResourcesGrouped = new Dictionary <RelationshipProxy, List <IIdentifiable> >(); // RelationshipAttr_prevLayer->currentLayer => currentLayerResources var rightResourcesGrouped = new Dictionary <RelationshipProxy, List <IIdentifiable> >(); foreach (var node in leftNodes) { var leftResources = node.UniqueResources; var relationships = node.RelationshipsToNextLayer; ExtractLeftResources(leftResources, relationships, rightResourcesGrouped, leftResourcesGrouped); } var processResourcesMethod = GetType().GetMethod(nameof(ProcessResources), BindingFlags.NonPublic | BindingFlags.Instance); foreach (var kvp in rightResourcesGrouped) { var type = kvp.Key.RightType; var list = TypeHelper.CopyToList(kvp.Value, type); processResourcesMethod !.MakeGenericMethod(type).Invoke(this, ArrayFactory.Create <object>(list)); } return(leftResourcesGrouped, rightResourcesGrouped); }
private Expression ExtensionMethodCall(Expression source, string operationName, Type keyType, LambdaExpression keySelector) { var typeArguments = ArrayFactory.Create(LambdaScope.Parameter.Type, keyType); return(Expression.Call(_extensionType, operationName, typeArguments, source, keySelector)); }
private (List <Article>, List <Tag>) CreateDummyData() { var tagsSubset = TagFaker.Generate(3); var joinsSubSet = ArticleTagFaker.Generate(3); var articleTagsSubset = ArticleFaker.Generate(); articleTagsSubset.ArticleTags = joinsSubSet.ToHashSet(); for (int i = 0; i < 3; i++) { joinsSubSet[i].Article = articleTagsSubset; joinsSubSet[i].Tag = tagsSubset[i]; } var allTags = TagFaker.Generate(3).Concat(tagsSubset).ToList(); var completeJoin = ArticleTagFaker.Generate(6); var articleWithAllTags = ArticleFaker.Generate(); articleWithAllTags.ArticleTags = completeJoin.ToHashSet(); for (int i = 0; i < 6; i++) { completeJoin[i].Article = articleWithAllTags; completeJoin[i].Tag = allTags[i]; } var articles = ArrayFactory.Create(articleTagsSubset, articleWithAllTags).ToList(); return(articles, allTags); }
/// <summary> /// Given a source of resources, gets the implicitly affected resources from the database and calls the BeforeImplicitUpdateRelationship hook. /// </summary> private void FireForAffectedImplicits(Type resourceTypeToInclude, IDictionary <RelationshipAttribute, IEnumerable> implicitsTarget, ResourcePipeline pipeline, IEnumerable existingImplicitResources = null) { IResourceHookContainer container = _containerProvider.GetResourceHookContainer(resourceTypeToInclude, ResourceHook.BeforeImplicitUpdateRelationship); if (container == null) { return; } IDictionary <RelationshipAttribute, IEnumerable> implicitAffected = _containerProvider.LoadImplicitlyAffected(implicitsTarget, existingImplicitResources); if (!implicitAffected.Any()) { return; } Dictionary <RelationshipAttribute, IEnumerable> inverse = new Dictionary <RelationshipAttribute, IEnumerable>(); foreach (KeyValuePair <RelationshipAttribute, IEnumerable> pair in implicitAffected) { var inverseRelationship = _resourceGraph.GetInverseRelationship(pair.Key); if (inverseRelationship != null) { inverse.Add(inverseRelationship, pair.Value); } } IRelationshipsDictionary resourcesByRelationship = CreateRelationshipHelper(resourceTypeToInclude, inverse); CallHook(container, ResourceHook.BeforeImplicitUpdateRelationship, ArrayFactory.Create <object>(resourcesByRelationship, pipeline)); }
protected ManyToManyTestData CreateIdentifiableManyToManyData() { List <Tag> tagsSubset = TagFaker.Generate(3); List <IdentifiableArticleTag> joinsSubSet = _identifiableArticleTagFaker.Generate(3); Article articleTagsSubset = ArticleFaker.Generate(); articleTagsSubset.IdentifiableArticleTags = joinsSubSet.ToHashSet(); for (int index = 0; index < 3; index++) { joinsSubSet[index].Article = articleTagsSubset; joinsSubSet[index].Tag = tagsSubset[index]; } List <Tag> allTags = TagFaker.Generate(3).Concat(tagsSubset).ToList(); List <IdentifiableArticleTag> completeJoin = _identifiableArticleTagFaker.Generate(6); Article articleWithAllTags = ArticleFaker.Generate(); articleWithAllTags.IdentifiableArticleTags = joinsSubSet.ToHashSet(); for (int index = 0; index < 6; index++) { completeJoin[index].Article = articleWithAllTags; completeJoin[index].Tag = allTags[index]; } List <IdentifiableArticleTag> allJoins = joinsSubSet.Concat(completeJoin).ToList(); List <Article> articles = ArrayFactory.Create(articleTagsSubset, articleWithAllTags).ToList(); return(new ManyToManyTestData(articles, allJoins, allTags)); }
protected (List <Article>, List <IdentifiableArticleTag>, List <Tag>) CreateIdentifiableManyToManyData() { var tagsSubset = TagFaker.Generate(3); var joinsSubSet = _identifiableArticleTagFaker.Generate(3); var articleTagsSubset = ArticleFaker.Generate(); articleTagsSubset.IdentifiableArticleTags = joinsSubSet.ToHashSet(); for (int i = 0; i < 3; i++) { joinsSubSet[i].Article = articleTagsSubset; joinsSubSet[i].Tag = tagsSubset[i]; } var allTags = TagFaker.Generate(3).Concat(tagsSubset).ToList(); var completeJoin = _identifiableArticleTagFaker.Generate(6); var articleWithAllTags = ArticleFaker.Generate(); articleWithAllTags.IdentifiableArticleTags = joinsSubSet.ToHashSet(); for (int i = 0; i < 6; i++) { completeJoin[i].Article = articleWithAllTags; completeJoin[i].Tag = allTags[i]; } var allJoins = joinsSubSet.Concat(completeJoin).ToList(); var articles = ArrayFactory.Create(articleTagsSubset, articleWithAllTags).ToList(); return(articles, allJoins, allTags); }
/// <inheritdoc /> public EntityTagHeaderValue Generate(string requestUrl, string responseBody) { string fingerprint = _fingerprintGenerator.Generate(ArrayFactory.Create(requestUrl, responseBody)); string eTagValue = "\"" + fingerprint + "\""; return(EntityTagHeaderValue.Parse(eTagValue)); }
private (List <Article>, List <Tag>) CreateDummyData() { List <Tag> tagsSubset = TagFaker.Generate(3); List <ArticleTag> joinsSubSet = ArticleTagFaker.Generate(3); Article articleTagsSubset = ArticleFaker.Generate(); articleTagsSubset.ArticleTags = joinsSubSet.ToHashSet(); for (int index = 0; index < 3; index++) { joinsSubSet[index].Article = articleTagsSubset; joinsSubSet[index].Tag = tagsSubset[index]; } List <Tag> allTags = TagFaker.Generate(3).Concat(tagsSubset).ToList(); List <ArticleTag> completeJoin = ArticleTagFaker.Generate(6); Article articleWithAllTags = ArticleFaker.Generate(); articleWithAllTags.ArticleTags = completeJoin.ToHashSet(); for (int index = 0; index < 6; index++) { completeJoin[index].Article = articleWithAllTags; completeJoin[index].Tag = allTags[index]; } List <Article> articles = ArrayFactory.Create(articleTagsSubset, articleWithAllTags).ToList(); return(articles, allTags); }
private FilterExpression CreateFilterByIds <TId>(ICollection <TId> ids, AttrAttribute idAttribute, FilterExpression existingFilter) { var idChain = new ResourceFieldChainExpression(idAttribute); FilterExpression filter = null; if (ids.Count == 1) { var constant = new LiteralConstantExpression(ids.Single().ToString()); filter = new ComparisonExpression(ComparisonOperator.Equals, idChain, constant); } else if (ids.Count > 1) { var constants = ids.Select(id => new LiteralConstantExpression(id.ToString())).ToList(); filter = new EqualsAnyOfExpression(idChain, constants); } // @formatter:keep_existing_linebreaks true return(filter == null ? existingFilter : existingFilter == null ? filter : new LogicalExpression(LogicalOperator.And, ArrayFactory.Create(filter, existingFilter))); // @formatter:keep_existing_linebreaks restore }
/// <summary> /// Extracts the resources for the current layer by going through all populated relationships of the (left resources of the previous layer. /// </summary> private (Dictionary <RelationshipProxy, List <IIdentifiable> >, Dictionary <RelationshipProxy, List <IIdentifiable> >) ExtractResources( IEnumerable <IResourceNode> leftNodes) { // RelationshipAttr_prevLayer->currentLayer => prevLayerResources var leftResourcesGrouped = new Dictionary <RelationshipProxy, List <IIdentifiable> >(); // RelationshipAttr_prevLayer->currentLayer => currentLayerResources var rightResourcesGrouped = new Dictionary <RelationshipProxy, List <IIdentifiable> >(); foreach (IResourceNode node in leftNodes) { IEnumerable leftResources = node.UniqueResources; IReadOnlyCollection <RelationshipProxy> relationships = node.RelationshipsToNextLayer; ExtractLeftResources(leftResources, relationships, rightResourcesGrouped, leftResourcesGrouped); } MethodInfo processResourcesMethod = GetType().GetMethod(nameof(ProcessResources), BindingFlags.NonPublic | BindingFlags.Instance); foreach (KeyValuePair <RelationshipProxy, List <IIdentifiable> > pair in rightResourcesGrouped) { RightType type = pair.Key.RightType; IList list = CollectionConverter.CopyToList(pair.Value, type); processResourcesMethod !.MakeGenericMethod(type).Invoke(this, ArrayFactory.Create <object>(list)); } return(leftResourcesGrouped, rightResourcesGrouped); }
/// <summary> /// Fires the AfterUpdateRelationship hook /// </summary> private void FireAfterUpdateRelationship(IResourceHookContainer container, IResourceNode node, ResourcePipeline pipeline) { Dictionary <RelationshipAttribute, IEnumerable> currentResourcesGrouped = node.RelationshipsFromPreviousLayer.GetRightResources(); // the relationships attributes in currentResourcesGrouped will be pointing from a // resource in the previous layer to a resource in the current (nested) layer. // For the nested hook we need to replace these attributes with their inverse. // See the FireNestedBeforeUpdateHooks method for a more detailed example. var resourcesByRelationship = CreateRelationshipHelper(node.ResourceType, ReplaceKeysWithInverseRelationships(currentResourcesGrouped)); CallHook(container, ResourceHook.AfterUpdateRelationship, ArrayFactory.Create <object>(resourcesByRelationship, pipeline)); }
/// <inheritdoc /> public void AfterRead <TResource>(IEnumerable <TResource> resources, ResourcePipeline pipeline) where TResource : class, IIdentifiable { if (GetHook(ResourceHook.AfterRead, resources, out var container, out var node)) { container.AfterRead((HashSet <TResource>)node.UniqueResources, pipeline); } Traverse(_traversalHelper.CreateNextLayer(node), ResourceHook.AfterRead, (nextContainer, nextNode) => { CallHook(nextContainer, ResourceHook.AfterRead, ArrayFactory.Create <object>(nextNode.UniqueResources, pipeline, true)); }); }
public override FilterExpression OnApplyFilter(FilterExpression existingFilter) { var resourceContext = _resourceGraph.GetResourceContext <TResource>(); var isSoftDeletedAttribute = resourceContext.Attributes.Single(attribute => attribute.Property.Name == nameof(ISoftDeletable.IsSoftDeleted)); var isNotSoftDeleted = new ComparisonExpression(ComparisonOperator.Equals, new ResourceFieldChainExpression(isSoftDeletedAttribute), new LiteralConstantExpression("false")); return(existingFilter == null ? (FilterExpression)isNotSoftDeleted : new LogicalExpression(LogicalOperator.And, ArrayFactory.Create(isNotSoftDeleted, existingFilter))); }
public void ResourceToDocument_ResourceList_CanBuild() { // Arrange DummyResource[] resources = ArrayFactory.Create(new DummyResource(), new DummyResource()); // Act Document document = _builder.PublicBuild(resources); var data = (List <ResourceObject>)document.Data; // Assert Assert.Equal(2, data.Count); }
private SparseFieldSetExpression GetSparseFieldSet(string parameterValue, ResourceContext resourceContext) { SparseFieldSetExpression sparseFieldSet = _sparseFieldSetParser.Parse(parameterValue, resourceContext); if (sparseFieldSet == null) { // We add ID on an incoming empty fieldset, so that callers can distinguish between no fieldset and an empty one. AttrAttribute idAttribute = resourceContext.Attributes.Single(attribute => attribute.Property.Name == nameof(Identifiable.Id)); return(new SparseFieldSetExpression(ArrayFactory.Create(idAttribute))); } return(sparseFieldSet); }
private void RegisterImplementations(Assembly assembly, Type interfaceType, ResourceDescriptor resourceDescriptor) { var genericArguments = interfaceType.GetTypeInfo().GenericTypeParameters.Length == 2 ? ArrayFactory.Create(resourceDescriptor.ResourceType, resourceDescriptor.IdType) : ArrayFactory.Create(resourceDescriptor.ResourceType); var result = TypeLocator.GetGenericInterfaceImplementation(assembly, interfaceType, genericArguments); if (result != null) { var(implementation, registrationInterface) = result.Value; _services.AddScoped(registrationInterface, implementation); } }
public override FilterExpression OnApplyFilter(FilterExpression existingFilter) { // Use case: automatically exclude deleted resources for all requests. ResourceContext resourceContext = ResourceGraph.GetResourceContext <CallableResource>(); AttrAttribute isDeletedAttribute = resourceContext.Attributes.Single(attribute => attribute.Property.Name == nameof(CallableResource.IsDeleted)); var isNotDeleted = new ComparisonExpression(ComparisonOperator.Equals, new ResourceFieldChainExpression(isDeletedAttribute), new LiteralConstantExpression(bool.FalseString)); return(existingFilter == null ? (FilterExpression)isNotDeleted : new LogicalExpression(LogicalOperator.And, ArrayFactory.Create(isNotDeleted, existingFilter))); }
public IEnumerable LoadDbValues(LeftType resourceTypeForRepository, IEnumerable resources, params RelationshipAttribute[] relationshipsToNextLayer) { LeftType idType = ObjectFactory.GetIdType(resourceTypeForRepository); MethodInfo parameterizedGetWhere = GetType().GetMethod(nameof(GetWhereWithInclude), BindingFlags.NonPublic | BindingFlags.Instance) !.MakeGenericMethod(resourceTypeForRepository, idType); IEnumerable <object> resourceIds = ((IEnumerable <object>)resources).Cast <IIdentifiable>().Select(resource => resource.GetTypedId()); IList idsAsList = CollectionConverter.CopyToList(resourceIds, idType); var values = (IEnumerable)parameterizedGetWhere.Invoke(this, ArrayFactory.Create <object>(idsAsList, relationshipsToNextLayer)); return(values == null ? null : CollectionConverter.CopyToHashSet(values, resourceTypeForRepository)); }
/// <inheritdoc /> public void AfterRead <TResource>(IEnumerable <TResource> resources, ResourcePipeline pipeline) where TResource : class, IIdentifiable { GetHookResult <TResource> result = GetHook(ResourceHook.AfterRead, resources); if (result.Succeeded) { result.Container.AfterRead((HashSet <TResource>)result.Node.UniqueResources, pipeline); } TraverseNodesInLayer(_nodeNavigator.CreateNextLayer(result.Node), ResourceHook.AfterRead, (nextContainer, nextNode) => { CallHook(nextContainer, ResourceHook.AfterRead, ArrayFactory.Create <object>(nextNode.UniqueResources, pipeline, true)); }); }
private static QueryStringReader CreateQueryParameterDiscoveryForAll(IResourceGraph resourceGraph, JsonApiRequest request, IJsonApiOptions options, FakeRequestQueryStringAccessor queryStringAccessor) { var resourceFactory = new ResourceFactory(new ServiceContainer()); var includeReader = new IncludeQueryStringParameterReader(request, resourceGraph, options); var filterReader = new FilterQueryStringParameterReader(request, resourceGraph, resourceFactory, options); var sortReader = new SortQueryStringParameterReader(request, resourceGraph); var sparseFieldSetReader = new SparseFieldSetQueryStringParameterReader(request, resourceGraph); var paginationReader = new PaginationQueryStringParameterReader(request, resourceGraph, options); var defaultsReader = new DefaultsQueryStringParameterReader(options); var nullsReader = new NullsQueryStringParameterReader(options); var readers = ArrayFactory.Create <IQueryStringParameterReader>(includeReader, filterReader, sortReader, sparseFieldSetReader, paginationReader, defaultsReader, nullsReader); return(new QueryStringReader(options, queryStringAccessor, readers, NullLoggerFactory.Instance)); }
public IEnumerable LoadDbValues(LeftType resourceTypeForRepository, IEnumerable resources, ResourceHook hook, params RelationshipAttribute[] relationshipsToNextLayer) { var idType = TypeHelper.GetIdType(resourceTypeForRepository); var parameterizedGetWhere = GetType() .GetMethod(nameof(GetWhereAndInclude), BindingFlags.NonPublic | BindingFlags.Instance) ! .MakeGenericMethod(resourceTypeForRepository, idType); var cast = ((IEnumerable <object>)resources).Cast <IIdentifiable>(); var ids = TypeHelper.CopyToList(cast.Select(i => i.GetTypedId()), idType); var values = (IEnumerable)parameterizedGetWhere.Invoke(this, ArrayFactory.Create <object>(ids, relationshipsToNextLayer)); if (values == null) { return(null); } return((IEnumerable)Activator.CreateInstance(typeof(HashSet <>).MakeGenericType(resourceTypeForRepository), TypeHelper.CopyToList(values, resourceTypeForRepository))); }
public override FilterExpression OnApplyFilter(FilterExpression existingFilter) { _hitCounter.TrackInvocation <Planet>(ResourceDefinitionHitCounter.ExtensibilityPoint.OnApplyFilter); if (_clientSettingsProvider.ArePlanetsWithPrivateNameHidden) { AttrAttribute privateNameAttribute = ResourceContext.Attributes.Single(attribute => attribute.Property.Name == nameof(Planet.PrivateName)); FilterExpression hasNoPrivateName = new ComparisonExpression(ComparisonOperator.Equals, new ResourceFieldChainExpression(privateNameAttribute), new NullConstantExpression()); return(existingFilter == null ? hasNoPrivateName : new LogicalExpression(LogicalOperator.And, ArrayFactory.Create(hasNoPrivateName, existingFilter))); } return(existingFilter); }
private void CalculateComponents() { for (var y = 0; y < NodeSpace.MaxY; y++) { for (var x = 0; x < NodeSpace.MaxX; x++) { for (var a = 0; a < NodeSpace.LatticeVectors.Count; a++) { _fPrevious[a, x, y] = _fCurrent[a, x, y]; } } } var sumRho = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, 0.0); var sumU = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, 0.0); var sumV = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, 0.0); foreach (var node in _nodes) { var x = node.X ?? -1; var y = node.Y ?? -1; for (var a = 0; a < NodeSpace.LatticeVectors.Count; a++) { var value = node.NodeType != NodeType.Liquid ? 0 : _fCurrent[a, x, y]; sumRho[x, y] += value; sumU[x, y] += value * NodeSpace.LatticeVectors[a].Dx; sumV[x, y] += value * NodeSpace.LatticeVectors[a].Dy; } } _rho = sumRho; for (var y = 0; y < NodeSpace.MaxY; y++) { for (var x = 0; x < NodeSpace.MaxX; x++) { _u[x, y] = sumU[x, y] / _rho[x, y]; _v[x, y] = sumV[x, y] / _rho[x, y]; } } }
/// <summary> /// Given a source of resources, gets the implicitly affected resources /// from the database and calls the BeforeImplicitUpdateRelationship hook. /// </summary> private void FireForAffectedImplicits(Type resourceTypeToInclude, Dictionary <RelationshipAttribute, IEnumerable> implicitsTarget, ResourcePipeline pipeline, IEnumerable existingImplicitResources = null) { var container = _executorHelper.GetResourceHookContainer(resourceTypeToInclude, ResourceHook.BeforeImplicitUpdateRelationship); if (container == null) { return; } var implicitAffected = _executorHelper.LoadImplicitlyAffected(implicitsTarget, existingImplicitResources); if (!implicitAffected.Any()) { return; } var inverse = implicitAffected.ToDictionary(kvp => _resourceGraph.GetInverseRelationship(kvp.Key), kvp => kvp.Value); var resourcesByRelationship = CreateRelationshipHelper(resourceTypeToInclude, inverse); CallHook(container, ResourceHook.BeforeImplicitUpdateRelationship, ArrayFactory.Create <object>(resourcesByRelationship, pipeline)); }
public override FilterExpression OnApplyFilter(FilterExpression existingFilter) { if (_request.IsReadOnly) { // Rule: hide archived broadcasts in collections, unless a filter is specified. if (IsReturningCollectionOfTelevisionBroadcasts() && !HasFilterOnArchivedAt(existingFilter)) { AttrAttribute archivedAtAttribute = ResourceContext.Attributes.Single(attr => attr.Property.Name == nameof(TelevisionBroadcast.ArchivedAt)); var archivedAtChain = new ResourceFieldChainExpression(archivedAtAttribute); FilterExpression isUnarchived = new ComparisonExpression(ComparisonOperator.Equals, archivedAtChain, new NullConstantExpression()); return(existingFilter == null ? isUnarchived : new LogicalExpression(LogicalOperator.And, ArrayFactory.Create(existingFilter, isUnarchived))); } } return(base.OnApplyFilter(existingFilter)); }
private void Initialise() { // Calculated at the start of each iteration. The equilibrium function. _fEquilibrium = ArrayFactory.Create(9, NodeSpace.MaxX, NodeSpace.MaxY, 0.0); // Built up each iteration. The 'new' values. _fCurrent = ArrayFactory.Create(9, NodeSpace.MaxX, NodeSpace.MaxY, 0.0); // Copied from _fCurrent at the end of each iteration. The 'previous' values. _fPrevious = ArrayFactory.Create(9, NodeSpace.MaxX, NodeSpace.MaxY, 0.0); _rho = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, ModelParameters.Rho0); _rhoOld = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, ModelParameters.Rho0); _u = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, 0.0); _v = ArrayFactory.Create(NodeSpace.MaxX, NodeSpace.MaxY, 0.0); var validNodeTypes = new[] { NodeType.Boundary, NodeType.Liquid }; for (var y = 0; y < NodeSpace.MaxY; y++) { for (var x = 0; x < NodeSpace.MaxX; x++) { if (!validNodeTypes.Contains(_nodes[x, y].NodeType)) { continue; } _u[x, y] = ModelParameters.U0; _v[x, y] = ModelParameters.V0; } } CurrentIteration = 0; IsInitialised = true; }
public async Task Cannot_remove_for_unknown_IDs_in_data() { // Arrange RecordCompany existingCompany = _fakers.RecordCompany.Generate(); Guid[] trackIds = ArrayFactory.Create(Guid.NewGuid(), Guid.NewGuid()); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RecordCompanies.Add(existingCompany); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "remove", @ref = new { type = "recordCompanies", id = existingCompany.StringId, relationship = "tracks" }, data = new[] { new { type = "musicTracks", id = trackIds[0].ToString() }, new { type = "musicTracks", id = trackIds[1].ToString() } } } } }; const string route = "/operations"; // Act (HttpResponseMessage httpResponse, ErrorDocument responseDocument) = await _testContext.ExecutePostAtomicAsync <ErrorDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); responseDocument.Errors.Should().HaveCount(2); Error error1 = responseDocument.Errors[0]; error1.StatusCode.Should().Be(HttpStatusCode.NotFound); error1.Title.Should().Be("A related resource does not exist."); error1.Detail.Should().Be($"Related resource of type 'musicTracks' with ID '{trackIds[0]}' in relationship 'tracks' does not exist."); error1.Source.Pointer.Should().Be("/atomic:operations[0]"); Error error2 = responseDocument.Errors[1]; error2.StatusCode.Should().Be(HttpStatusCode.NotFound); error2.Title.Should().Be("A related resource does not exist."); error2.Detail.Should().Be($"Related resource of type 'musicTracks' with ID '{trackIds[1]}' in relationship 'tracks' does not exist."); error2.Source.Pointer.Should().Be("/atomic:operations[0]"); }
/// <summary> /// Fires the nested before hooks for resources in the current <paramref name="layer" /> /// </summary> /// <remarks> /// For example: consider the case when the owner of article1 (one-to-one) is being updated from owner_old to owner_new, where owner_new is currently /// already related to article2. Then, the following nested hooks need to be fired in the following order. First the BeforeUpdateRelationship should be /// for owner1, then the BeforeImplicitUpdateRelationship hook should be fired for owner2, and lastly the BeforeImplicitUpdateRelationship for article2. /// </remarks> private void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, IEnumerable <IResourceNode> layer) { foreach (IResourceNode node in layer) { IResourceHookContainer nestedHookContainer = _containerProvider.GetResourceHookContainer(node.ResourceType, ResourceHook.BeforeUpdateRelationship); IEnumerable uniqueResources = node.UniqueResources; RightType resourceType = node.ResourceType; IDictionary <RelationshipAttribute, IEnumerable> currentResourcesGrouped; IDictionary <RelationshipAttribute, IEnumerable> currentResourcesGroupedInverse; // fire the BeforeUpdateRelationship hook for owner_new if (nestedHookContainer != null) { if (uniqueResources.Cast <IIdentifiable>().Any()) { RelationshipAttribute[] relationships = node.RelationshipsToNextLayer.Select(proxy => proxy.Attribute).ToArray(); IEnumerable dbValues = LoadDbValues(resourceType, uniqueResources, ResourceHook.BeforeUpdateRelationship, relationships); // these are the resources of the current node grouped by // RelationshipAttributes that occurred in the previous layer // so it looks like { HasOneAttribute:owner => owner_new }. // Note that in the BeforeUpdateRelationship hook of Person, // we want want inverse relationship attribute: // we now have the one pointing from article -> person, ] // but we require the the one that points from person -> article currentResourcesGrouped = node.RelationshipsFromPreviousLayer.GetRightResources(); currentResourcesGroupedInverse = ReplaceKeysWithInverseRelationships(currentResourcesGrouped); IRelationshipsDictionary resourcesByRelationship = CreateRelationshipHelper(resourceType, currentResourcesGroupedInverse, dbValues); IEnumerable <string> allowedIds = CallHook(nestedHookContainer, ResourceHook.BeforeUpdateRelationship, ArrayFactory.Create <object>(GetIds(uniqueResources), resourcesByRelationship, pipeline)).Cast <string>(); ISet <IIdentifiable> updated = GetAllowedResources(uniqueResources, allowedIds); node.UpdateUnique(updated); node.Reassign(); } } // Fire the BeforeImplicitUpdateRelationship hook for owner_old. // Note: if the pipeline is Post it means we just created article1, // which means we are sure that it isn't related to any other resources yet. if (pipeline != ResourcePipeline.Post) { // To fire a hook for owner_old, we need to first get a reference to it. // For this, we need to query the database for the HasOneAttribute:owner // relationship of article1, which is referred to as the // left side of the HasOneAttribute:owner relationship. IDictionary <RelationshipAttribute, IEnumerable> leftResources = node.RelationshipsFromPreviousLayer.GetLeftResources(); if (leftResources.Any()) { // owner_old is loaded, which is an "implicitly affected resource" FireForAffectedImplicits(resourceType, leftResources, pipeline, uniqueResources); } } // Fire the BeforeImplicitUpdateRelationship hook for article2 // For this, we need to query the database for the current owner // relationship value of owner_new. currentResourcesGrouped = node.RelationshipsFromPreviousLayer.GetRightResources(); if (currentResourcesGrouped.Any()) { // rightResources is grouped by relationships from previous // layer, ie { HasOneAttribute:owner => owner_new }. But // to load article2 onto owner_new, we need to have the // RelationshipAttribute from owner to article, which is the // inverse of HasOneAttribute:owner currentResourcesGroupedInverse = ReplaceKeysWithInverseRelationships(currentResourcesGrouped); // Note that currently in the JsonApiDotNetCore implementation of hooks, // the root layer is ALWAYS homogenous, so we safely assume // that for every relationship to the previous layer, the // left type is the same. LeftType leftType = currentResourcesGrouped.First().Key.LeftType; FireForAffectedImplicits(leftType, currentResourcesGroupedInverse, pipeline); } } }
/// <inheritdoc /> public IEnumerable <TResource> OnReturn <TResource>(IEnumerable <TResource> resources, ResourcePipeline pipeline) where TResource : class, IIdentifiable { GetHookResult <TResource> result = GetHook(ResourceHook.OnReturn, resources); if (result.Succeeded) { IEnumerable <TResource> updated = result.Container.OnReturn((HashSet <TResource>)result.Node.UniqueResources, pipeline); ValidateHookResponse(updated); result.Node.UpdateUnique(updated); result.Node.Reassign(resources); } TraverseNodesInLayer(_nodeNavigator.CreateNextLayer(result.Node), ResourceHook.OnReturn, (nextContainer, nextNode) => { IEnumerable filteredUniqueSet = CallHook(nextContainer, ResourceHook.OnReturn, ArrayFactory.Create <object>(nextNode.UniqueResources, pipeline)); nextNode.UpdateUnique(filteredUniqueSet); nextNode.Reassign(); }); return(resources); }
private Expression SelectExtensionMethodCall(Expression source, Type elementType, Expression selectorBody) { Type[] typeArguments = ArrayFactory.Create(elementType, elementType); return(Expression.Call(_extensionType, "Select", typeArguments, source, selectorBody)); }