-
-
Notifications
You must be signed in to change notification settings - Fork 5
Add example projects and tests #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Please merge in latest master, I've setup cibuild there. And I created a v4-alpha1 release on GitHub, to test that it automatically publishes to NuGet. I understand you don't want to make too many changes in this PR. The way I'm going to review the tests in this PR, is by running a file-by-file diff with the main repo. For each test, I'm going to ask myself questions like: Why are the assertions different here? Why was this test removed? What other cases should be added? Is this a duplicate? Is this testing serializer behavior, so it should be left out? My point is that reviewing a large set with gaps (future work) is a lot harder than reviewing a set that is supposed to be complete. And once the set is complete, I'm going to ask you to remove several models, resource hooks etc, because they are unneeded. I don't think this needs to result in a lot of changes on your side. For example, we have many tests for usage of the |
I've just added error messages for when the user attempts to fetch a related resource (either via I also went back to the tests I'd said I was failing in #2 and I have some things to comment:
As for resource updates I couldn't figure out what it would be like to test that because since you can't create how do you even think of updating, right? I understand we will most likely want to test this a bit anyway so some suggestions would come in handy! |
To make the original test work, you just need to feed it the info MongoDbModel source code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JsonApiDotNetCore.Configuration;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
namespace JsonApiDotNetCore.MongoDb
{
internal sealed class MongoDbModel : IModel
{
private readonly IResourceContextProvider _resourceContextProvider;
public object this[string name] => throw new NotImplementedException();
public MongoDbModel(IResourceContextProvider resourceContextProvider)
{
_resourceContextProvider = resourceContextProvider ?? throw new ArgumentNullException(nameof(resourceContextProvider));
}
public IEnumerable<IEntityType> GetEntityTypes()
{
var resourceContexts = _resourceContextProvider.GetResourceContexts();
return resourceContexts.Select(resourceContext => new MongoEntityType(resourceContext, this));
}
public IAnnotation FindAnnotation(string name) => throw new NotImplementedException();
public IEnumerable<IAnnotation> GetAnnotations() => throw new NotImplementedException();
public IEntityType FindEntityType(string name) => throw new NotImplementedException();
public IEntityType FindEntityType(string name, string definingNavigationName, IEntityType definingEntityType) => throw new NotImplementedException();
}
internal sealed class MongoEntityType : IEntityType
{
private readonly ResourceContext _resourceContext;
public IModel Model { get; }
public Type ClrType => _resourceContext.ResourceType;
public string Name => throw new NotImplementedException();
public IEntityType BaseType => throw new NotImplementedException();
public string DefiningNavigationName => throw new NotImplementedException();
public IEntityType DefiningEntityType => throw new NotImplementedException();
public object this[string name] => throw new NotImplementedException();
public MongoEntityType(ResourceContext resourceContext, MongoDbModel owner)
{
_resourceContext = resourceContext ?? throw new ArgumentNullException(nameof(resourceContext));
Model = owner ?? throw new ArgumentNullException(nameof(owner));
}
public IEnumerable<IProperty> GetProperties()
{
return _resourceContext.Attributes.Select(attr => new MongoDbProperty(attr.Property, this));
}
public IAnnotation FindAnnotation(string name) => throw new NotImplementedException();
public IEnumerable<IAnnotation> GetAnnotations() => throw new NotImplementedException();
public IKey FindPrimaryKey() => throw new NotImplementedException();
public IKey FindKey(IReadOnlyList<IProperty> properties) => throw new NotImplementedException();
public IEnumerable<IKey> GetKeys() => throw new NotImplementedException();
public IForeignKey FindForeignKey(IReadOnlyList<IProperty> properties, IKey principalKey, IEntityType principalEntityType) => throw new NotImplementedException();
public IEnumerable<IForeignKey> GetForeignKeys() => throw new NotImplementedException();
public IIndex FindIndex(IReadOnlyList<IProperty> properties) => throw new NotImplementedException();
public IEnumerable<IIndex> GetIndexes() => throw new NotImplementedException();
public IProperty FindProperty(string name) => throw new NotImplementedException();
public IServiceProperty FindServiceProperty(string name) => throw new NotImplementedException();
public IEnumerable<IServiceProperty> GetServiceProperties() => throw new NotImplementedException();
}
internal sealed class MongoDbProperty : IProperty
{
public IEntityType DeclaringEntityType { get; }
public PropertyInfo PropertyInfo { get; }
public string Name => throw new NotImplementedException();
public Type ClrType => throw new NotImplementedException();
public FieldInfo FieldInfo => throw new NotImplementedException();
public ITypeBase DeclaringType => throw new NotImplementedException();
public bool IsNullable => throw new NotImplementedException();
public ValueGenerated ValueGenerated => throw new NotImplementedException();
public bool IsConcurrencyToken => throw new NotImplementedException();
public object this[string name] => throw new NotImplementedException();
public MongoDbProperty(PropertyInfo propertyInfo, MongoEntityType owner)
{
DeclaringEntityType = owner ?? throw new ArgumentNullException(nameof(owner));
PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
}
public IAnnotation FindAnnotation(string name) => throw new NotImplementedException();
public IEnumerable<IAnnotation> GetAnnotations() => throw new NotImplementedException();
}
} Please put each of these classes in its own file. We prefer multiple types in a single file only when nested or only their generic arguments vary. |
The use case is that an API developer can define resources with My first thought to block relationships was to just forbid using these attributes in resources. This repo could add a post-processor step in the resource graph building process that throws when relationships are modelled. But that is too restrictive, because an API may serve both EF Core and MongoDB resources at the same time. The only place where we run into trouble, is when the code enters So to answer your question: A test that shows PATCH fails when relationships are being sent in the request body would not actually need to create a resource with a relationship in the test-database upfront (there is currently no way to do that). Not a problem, because the API code never gets there. But the resource class being used in the test does need to define a relationship. Then the code passes through query string parsing without errors, ending up in the repo. Clarification: With "the resource class being used in the test does need to define a relationship" I mean it needs to contain a HasOne/HasMany/HasManyThrough attribute. There is no need to add MongoDB-specific attributes to make it persist as a relationship. For all I know, relationships do not exist in MongoDB, right? :) |
I've taken a peek at your changes in MongoDbQueryExpressionValidator source code
internal sealed class MongoDbQueryExpressionValidator : QueryExpressionRewriter<object>
{
public void Validate(QueryLayer layer)
{
if (layer == null) throw new ArgumentNullException(nameof(layer));
bool hasIncludes = layer.Include?.Elements.Any() == true;
var hasSparseRelationshipSets = layer.Projection?.Any(pair => pair.Key is RelationshipAttribute) == true;
if (hasIncludes || hasSparseRelationshipSets)
{
throw CreateErrorForRelationshipUsage();
}
ValidateExpression(layer.Filter);
ValidateExpression(layer.Sort);
ValidateExpression(layer.Pagination);
}
private static JsonApiException CreateErrorForRelationshipUsage()
{
return new JsonApiException(new Error(HttpStatusCode.BadRequest)
{
Title = "Relationships are not supported when using MongoDB."
});
}
private void ValidateExpression(QueryExpression expression)
{
if (expression != null)
{
Visit(expression, null);
}
}
public override QueryExpression VisitResourceFieldChain(ResourceFieldChainExpression expression, object argument)
{
if (expression != null)
{
if (expression.Fields.Count > 1 || expression.Fields.First() is RelationshipAttribute)
{
throw CreateErrorForRelationshipUsage();
}
}
return base.VisitResourceFieldChain(expression, argument);
}
public override QueryExpression VisitComparison(ComparisonExpression expression, object argument)
{
if (expression?.Left is ResourceFieldChainExpression && expression.Right is ResourceFieldChainExpression)
{
// https://jira.mongodb.org/browse/CSHARP-1592
throw new JsonApiException(new Error(HttpStatusCode.BadRequest)
{
Title = "Comparing attributes against each other is not supported when using MongoDB."
});
}
return base.VisitComparison(expression, argument);
}
} You can call I noticed you were adding the "include" parameter to the exception, which is fine for include, but hard to reconstruct for other query string parameters such as fields[]. So I took that out, it is not important enough to do extra work for it. Also I created a private method to produce the exception when a relationship is encountered. You'll likely also need that from within |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review of your last commit only. Please let me know when you're ready for review of the rest.
src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedComparisonExpressionException.cs
Outdated
Show resolved
Hide resolved
src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs
Show resolved
Hide resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Filtering/FilterOperatorTests.cs
Outdated
Show resolved
Hide resolved
Please don't hold back on my account! Thank you so much for those snippets, they worked great! Knowing the codebase does help when coming up with solutions to these problems hehe. Anyway, I just added a test for updating relationships, cleaned up the models that had to have relationships and addressed the comments of your review. I think I'm ready for a review of the entire PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed until Meta tests, will look at the rest later.
src/JsonApiDotNetCore.MongoDb.GettingStarted/Controllers/BooksController.cs
Outdated
Show resolved
Hide resolved
src/JsonApiDotNetCore.MongoDb.GettingStarted/Properties/launchSettings.json
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Filtering/FilterOperatorTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Filtering/FilterDepthTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Filtering/FilterDataTypeTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Includes/IncludeTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Meta/TopLevelCountTests.cs
Outdated
Show resolved
Hide resolved
Yes, I'd forgotten about that... The test I was failing was
Basically, the metadata just isn't added to the response object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone over the open feedback items today, and noticed that some of them aren't yet addressed, others only partly.
So I'd like to structure our way of working a bit, as it is becoming unclear to me what is going on. Let's follow these steps:
- When you're ready for another review round, use the GitHub button to re-request review
- While reviewing, I'm adding new feedback items
- Once you've addressed a feedback item, mark it with a thumbs-up -or- comment so we can discuss
- I'll verify and mark addressed feedback items as resolved
test/JsonApiDotNetCore.MongoDb.Example.Tests/IntegrationTests/Meta/TopLevelCountTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Filtering/FilterOperatorTests.cs
Outdated
Show resolved
Hide resolved
Sorry things got a bit confusing... I think I did get all your comments sorted out this time, if not I did leave comments. You'll notice I'm a bit lost in terms of how to proceed in terms of the |
Before this, the tests would succeed only in time zones with zero or a negative offset (Uruguay = UTC−3), but fail in zones with a positive offset (Netherlands = UTC+1 or +2, depending on DST). So cutting off the time part was not the right solution.
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Sorting/SortTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Sorting/SortTests.cs
Outdated
Show resolved
Hide resolved
Left the new tests for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gone over open items, think I addressed all your questions.
...piDotNetCoreMongoDbExampleTests/IntegrationTests/Pagination/PaginationWithTotalCountTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Sorting/SortTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Sorting/SortTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Sorting/SortTests.cs
Outdated
Show resolved
Hide resolved
test/JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/Filtering/FilterDataTypeTests.cs
Outdated
Show resolved
Hide resolved
...JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs
Outdated
Show resolved
Hide resolved
...JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs
Outdated
Show resolved
Hide resolved
...JsonApiDotNetCoreMongoDbExampleTests/IntegrationTests/SparseFieldSets/SparseFieldSetTests.cs
Outdated
Show resolved
Hide resolved
...nApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skimmed over the ReadWrite tests quickly, I'll take a closer look once you're done with them.
...goDbExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithRelationshipsTests.cs
Outdated
Show resolved
Hide resolved
...goDbExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithRelationshipsTests.cs
Outdated
Show resolved
Hide resolved
...onApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs
Outdated
Show resolved
Hide resolved
...onApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs
Outdated
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Outdated
Show resolved
Hide resolved
I've just finished adding all the tests for |
As the number of tests increases, I'm experiencing timeouts when running all tests, because they run in parallel and my docker container seems unable to handle so many connections causing timeouts. I've added an xunit configuration file to remedy this. Also added PowerShell script from main repo that makes it quick to restart the container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone over all ReadWrite tests now.
...onApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs
Outdated
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Outdated
Show resolved
Hide resolved
} | ||
|
||
[Fact] | ||
public async Task Can_create_resource_with_ID() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be named Can_create_resource_with_string_ID. Assuming MongoDB only works with string IDs, there should also be a test like Cannot_create_resource_with_int_ID.
...nApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs
Show resolved
Hide resolved
...nApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Outdated
Show resolved
Hide resolved
...DbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs
Outdated
Show resolved
Hide resolved
Addressed all comments but two for which I left a question |
Something else I forgot to mention, in adding the CreateResourceWithClientGeneratedIdTests class I saw I hadn't handled duplicate ID errors. I saw in the main repo that exception is thrown from the service only and not from the repo, which I think throws a DataStoreUpdateException. Must have been my mistake but I couldn't make that work today... Did I overlook some key detail? |
…ed, so exceptions could not be caught (see https://stackoverflow.com/questions/30102651/mongodb-server-v-2-6-7-with-c-sharp-driver-2-0-how-to-get-the-result-from-ins). This commit corrects detection of Create for an ID that already exists. For performance, the service/repo does not check upfront, but catches the error. On error, it executes an additional query to determine if the record already existed and returns a matching error response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed changes and added comments. Pushed commits to fix relationship links and duplicate IDs. The service catches DataStoreUpdateException
when a write fails, so the repository must throw that. By using this exception, the service does not need to know about EF Core- or MongoDB-specific exception types.
...DbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs
Outdated
Show resolved
Hide resolved
...DbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs
Outdated
Show resolved
Hide resolved
...nApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs
Show resolved
Hide resolved
...ExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs
Outdated
Show resolved
Hide resolved
...onApiDotNetCoreMongoDbExampleTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs
Outdated
Show resolved
Hide resolved
...ExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs
Outdated
Show resolved
Hide resolved
...ExampleTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs
Outdated
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Outdated
Show resolved
Hide resolved
...ExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs
Outdated
Show resolved
Hide resolved
...CoreMongoDbExampleTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs
Show resolved
Hide resolved
…used with MongoDB. Adding MongoDbRepository with only TResource (implicitly using string for TId) was a bad idea. It breaks with assumptions in ResourceRepositoryAccessor that a single type parameter must always use int for TId (like the IResourceRepository interface itself). Also added extension method, so when we need to change registrations we can do that on the inside (without requiring changes from the api-developer each time).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is ready for merge now. Will hold off until I hear from you, in case I missed something. After merge, I intend to publish another alpha to NuGet.
I'm just taking the liberty to rename Other than that I'm happy to have this merged :) |
…ollectionExtensions.cs
Ah, good catch! Thanks a lot for your continued efforts working on this! |
This PR is what @bart-degreed had suggested in #2 , let me quote for reference:
Closes #1.