diff --git a/README.md b/README.md index e645d8b..a5e69f6 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ dotnet add package JsonApiDotNetCore.MongoDb #nullable enable [Resource] -public class Book : MongoIdentifiable +public class Book : HexStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; @@ -70,9 +70,19 @@ builder.Services.AddJsonApiMongoDb(); builder.Services.AddScoped(typeof(IResourceReadRepository<,>), typeof(MongoRepository<,>)); builder.Services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(MongoRepository<,>)); builder.Services.AddScoped(typeof(IResourceRepository<,>), typeof(MongoRepository<,>)); - ``` +## Using client-generated IDs +Resources that inherit from `HexStringMongoIdentifiable` use auto-generated (performant) 12-byte hexadecimal +[Object IDs](https://docs.mongodb.com/manual/reference/bson-types/#objectid). +You can assign an ID manually, but it must match the 12-byte hexadecimal pattern. + +To assign free-format string IDs manually, make your resources inherit from `FreeStringMongoIdentifiable` instead. +When creating a resource without assigning an ID, a 12-byte hexadecimal ID will be auto-generated. + +Set `options.AllowClientGeneratedIds` to `true` in Program.cs to allow API clients to assign IDs. This can be combined +with both base classes, but `FreeStringMongoIdentifiable` probably makes the most sense. + ## Limitations - JSON:API relationships are currently not supported. You can use complex object graphs though, which are stored in a single document. diff --git a/src/Examples/GettingStarted/Models/Book.cs b/src/Examples/GettingStarted/Models/Book.cs index b9853d1..49310bd 100644 --- a/src/Examples/GettingStarted/Models/Book.cs +++ b/src/Examples/GettingStarted/Models/Book.cs @@ -6,7 +6,7 @@ namespace GettingStarted.Models; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource] -public sealed class Book : MongoIdentifiable +public sealed class Book : HexStringMongoIdentifiable { [Attr] public string Title { get; set; } = null!; diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs index e2adddd..7dc654c 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Models/TodoItem.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbExample.Models; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource] -public sealed class TodoItem : MongoIdentifiable +public sealed class TodoItem : HexStringMongoIdentifiable { [Attr] public string Description { get; set; } = null!; diff --git a/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs b/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs index 414b5f7..be71664 100644 --- a/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs +++ b/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.MongoDb.Resources; using JsonApiDotNetCore.Queries; using JsonApiDotNetCore.Queries.Internal; using JsonApiDotNetCore.Resources; @@ -38,6 +39,11 @@ public IImmutableSet GetSparseFieldSetForSerializer(Reso { IImmutableSet fieldSet = _innerCache.GetSparseFieldSetForSerializer(resourceType); + return resourceType.ClrType.IsAssignableTo(typeof(IMongoIdentifiable)) ? RemoveRelationships(fieldSet) : fieldSet; + } + + private static IImmutableSet RemoveRelationships(IImmutableSet fieldSet) + { ResourceFieldAttribute[] relationships = fieldSet.Where(field => field is RelationshipAttribute).ToArray(); return fieldSet.Except(relationships); } diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs index 07a6030..de3d9e0 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.MongoDb.Errors; using JsonApiDotNetCore.MongoDb.Queries.Internal.QueryableBuilding; +using JsonApiDotNetCore.MongoDb.Resources; using JsonApiDotNetCore.Queries; using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Queries.Internal.QueryableBuilding; @@ -52,9 +53,9 @@ public MongoRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete _constraintProviders = constraintProviders; _resourceDefinitionAccessor = resourceDefinitionAccessor; - if (typeof(TId) != typeof(string)) + if (!typeof(TResource).IsAssignableTo(typeof(IMongoIdentifiable))) { - throw new InvalidConfigurationException("MongoDB can only be used for resources with an 'Id' property of type 'string'."); + throw new InvalidConfigurationException("MongoDB can only be used with resources that implement 'IMongoIdentifiable'."); } } diff --git a/src/JsonApiDotNetCore.MongoDb/Resources/FreeStringMongoIdentifiable.cs b/src/JsonApiDotNetCore.MongoDb/Resources/FreeStringMongoIdentifiable.cs new file mode 100644 index 0000000..ca19d2e --- /dev/null +++ b/src/JsonApiDotNetCore.MongoDb/Resources/FreeStringMongoIdentifiable.cs @@ -0,0 +1,27 @@ +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.IdGenerators; + +namespace JsonApiDotNetCore.MongoDb.Resources; + +/// +/// Basic implementation of a JSON:API resource whose Id is stored as a free-format string in MongoDB. Useful for resources that are created using +/// client-generated IDs. +/// +public abstract class FreeStringMongoIdentifiable : IMongoIdentifiable +{ + /// + [BsonId(IdGenerator = typeof(StringObjectIdGenerator))] + public virtual string? Id { get; set; } + + /// + [BsonIgnore] + public string? StringId + { + get => Id; + set => Id = value; + } + + /// + [BsonIgnore] + public string? LocalId { get; set; } +} diff --git a/src/JsonApiDotNetCore.MongoDb/Resources/MongoIdentifiable.cs b/src/JsonApiDotNetCore.MongoDb/Resources/HexStringMongoIdentifiable.cs similarity index 70% rename from src/JsonApiDotNetCore.MongoDb/Resources/MongoIdentifiable.cs rename to src/JsonApiDotNetCore.MongoDb/Resources/HexStringMongoIdentifiable.cs index b8b4104..e4049f9 100644 --- a/src/JsonApiDotNetCore.MongoDb/Resources/MongoIdentifiable.cs +++ b/src/JsonApiDotNetCore.MongoDb/Resources/HexStringMongoIdentifiable.cs @@ -1,13 +1,12 @@ -using JsonApiDotNetCore.Resources; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace JsonApiDotNetCore.MongoDb.Resources; /// -/// A convenient basic implementation of for use with MongoDB models. +/// Basic implementation of a JSON:API resource whose Id is stored as a 12-byte hexadecimal ObjectId in MongoDB. /// -public abstract class MongoIdentifiable : IIdentifiable +public abstract class HexStringMongoIdentifiable : IMongoIdentifiable { /// [BsonId] diff --git a/src/JsonApiDotNetCore.MongoDb/Resources/IMongoIdentifiable.cs b/src/JsonApiDotNetCore.MongoDb/Resources/IMongoIdentifiable.cs new file mode 100644 index 0000000..11dbfa2 --- /dev/null +++ b/src/JsonApiDotNetCore.MongoDb/Resources/IMongoIdentifiable.cs @@ -0,0 +1,10 @@ +using JsonApiDotNetCore.Resources; + +namespace JsonApiDotNetCore.MongoDb.Resources; + +/// +/// Marker interface to indicate a resource that is stored in MongoDB. +/// +public interface IMongoIdentifiable : IIdentifiable +{ +} diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs index f0671b3..40ebec1 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs @@ -3,7 +3,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; -using MongoDB.Bson; using TestBuildingBlocks; using Xunit; @@ -28,7 +27,7 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_sid { // Arrange TextLanguage newLanguage = _fakers.TextLanguage.Generate(); - newLanguage.Id = ObjectId.GenerateNewId().ToString(); + newLanguage.Id = "free-format-client-generated-id"; var requestBody = new { @@ -82,8 +81,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_create_resource_with_client_generated_string_ID_having_no_side_effects() { // Arrange - MusicTrack newTrack = _fakers.MusicTrack.Generate(); - newTrack.Id = ObjectId.GenerateNewId().ToString(); + Playlist newPlaylist = _fakers.Playlist.Generate(); + newPlaylist.Id = "free-format-client-generated-id"; var requestBody = new { @@ -94,13 +93,11 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_no_ op = "add", data = new { - type = "musicTracks", - id = newTrack.StringId, + type = "playlists", + id = newPlaylist.StringId, attributes = new { - title = newTrack.Title, - lengthInSeconds = newTrack.LengthInSeconds, - releasedAt = newTrack.ReleasedAt + name = newPlaylist.Name } } } @@ -119,10 +116,9 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_no_ await _testContext.RunOnDatabaseAsync(async dbContext => { - MusicTrack trackInDatabase = await dbContext.MusicTracks.FirstWithIdAsync(newTrack.Id); + Playlist playlistInDatabase = await dbContext.Playlists.FirstWithIdAsync(newPlaylist.Id); - trackInDatabase.Title.Should().Be(newTrack.Title); - trackInDatabase.LengthInSeconds.Should().BeApproximately(newTrack.LengthInSeconds); + playlistInDatabase.Name.Should().Be(newPlaylist.Name); }); } @@ -131,7 +127,7 @@ public async Task Cannot_create_resource_for_existing_client_generated_ID() { // Arrange TextLanguage existingLanguage = _fakers.TextLanguage.Generate(); - existingLanguage.Id = ObjectId.GenerateNewId().ToString(); + existingLanguage.Id = "existing-free-format-client-generated-id"; string newIsoCode = _fakers.TextLanguage.Generate().IsoCode!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Lyric.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Lyric.cs index 3618a77..674e682 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Lyric.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Lyric.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations")] -public sealed class Lyric : MongoIdentifiable +public sealed class Lyric : HexStringMongoIdentifiable { [Attr] public string? Format { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/MusicTrack.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/MusicTrack.cs index 2f0fb01..2db3dfa 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/MusicTrack.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/MusicTrack.cs @@ -8,7 +8,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations")] -public sealed class MusicTrack : MongoIdentifiable +public sealed class MusicTrack : HexStringMongoIdentifiable { [RegularExpression(@"^[a-fA-F\d]{24}$")] public override string? Id { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Performer.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Performer.cs index 346b7a3..5087bfd 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Performer.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Performer.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations")] -public sealed class Performer : MongoIdentifiable +public sealed class Performer : HexStringMongoIdentifiable { [Attr] public string? ArtistName { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Playlist.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Playlist.cs index ea1d8af..876fe36 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Playlist.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Playlist.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations")] -public sealed class Playlist : MongoIdentifiable +public sealed class Playlist : FreeStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/RecordCompany.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/RecordCompany.cs index b50ff94..761c1e1 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/RecordCompany.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/RecordCompany.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations")] -public sealed class RecordCompany : MongoIdentifiable +public sealed class RecordCompany : HexStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/TextLanguage.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/TextLanguage.cs index 8c621b8..08a3d4f 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/TextLanguage.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/TextLanguage.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations")] -public sealed class TextLanguage : MongoIdentifiable +public sealed class TextLanguage : FreeStringMongoIdentifiable { [Attr] public string? IsoCode { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicket.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicket.cs index e382405..098de4e 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicket.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicket.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.Meta; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.Meta")] -public sealed class SupportTicket : MongoIdentifiable +public sealed class SupportTicket : HexStringMongoIdentifiable { [Attr] public string Description { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs index f4b3858..0cd5aa2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/AccountPreferences.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class AccountPreferences : MongoIdentifiable +public sealed class AccountPreferences : HexStringMongoIdentifiable { [Attr] public bool UseDarkTheme { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Blog.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Blog.cs index 93dfabc..bc0530f 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Blog.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Blog.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] -public sealed class Blog : MongoIdentifiable +public sealed class Blog : HexStringMongoIdentifiable { [Attr] public string Title { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/BlogPost.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/BlogPost.cs index 85d366e..4508e2f 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/BlogPost.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/BlogPost.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] -public sealed class BlogPost : MongoIdentifiable +public sealed class BlogPost : HexStringMongoIdentifiable { [Attr] public string Caption { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Comment.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Comment.cs index 6c79972..38a3ebd 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Comment.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Comment.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] -public sealed class Comment : MongoIdentifiable +public sealed class Comment : HexStringMongoIdentifiable { [Attr] public string Text { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs index c82fcd9..e474854 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.Filtering; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.Filtering")] -public sealed class FilterableResource : MongoIdentifiable +public sealed class FilterableResource : HexStringMongoIdentifiable { [Attr] public string SomeString { get; set; } = string.Empty; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs index 9c29c87..5c267fb 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Label.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class Label : MongoIdentifiable +public sealed class Label : HexStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs index 51bbf50..5e06cea 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/LoginAttempt.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class LoginAttempt : MongoIdentifiable +public sealed class LoginAttempt : HexStringMongoIdentifiable { [Attr] public DateTimeOffset TriedAt { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs index 28f59c3..de3c673 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings")] -public sealed class WebAccount : MongoIdentifiable +public sealed class WebAccount : HexStringMongoIdentifiable { [Attr] public string UserName { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index fdc80bf..35c873f 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -98,7 +98,7 @@ public async Task Cannot_create_resource_with_int_ID() ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.InternalServerError); error.Title.Should().Be("An unhandled error occurred while processing this request."); - error.Detail.Should().Be("MongoDB can only be used for resources with an 'Id' property of type 'string'."); + error.Detail.Should().Be("MongoDB can only be used with resources that implement 'IMongoIdentifiable'."); } [Fact] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index 2fb07c0..e22f6cf 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -3,7 +3,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; -using MongoDB.Bson; using TestBuildingBlocks; using Xunit; @@ -35,7 +34,7 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_sid { // Arrange WorkItemGroup newGroup = _fakers.WorkItemGroup.Generate(); - newGroup.Id = ObjectId.GenerateNewId().ToString(); + newGroup.Id = "free-format-client-generated-id-1"; var requestBody = new { @@ -79,7 +78,7 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_sid { // Arrange WorkItemGroup newGroup = _fakers.WorkItemGroup.Generate(); - newGroup.Id = ObjectId.GenerateNewId().ToString(); + newGroup.Id = "free-format-client-generated-id-2"; var requestBody = new { @@ -124,7 +123,7 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_no_ { // Arrange RgbColor newColor = _fakers.RgbColor.Generate(); - newColor.Id = ObjectId.GenerateNewId().ToString(); + newColor.Id = "free-format-client-generated-id-3"; var requestBody = new { @@ -162,7 +161,7 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_no_ { // Arrange RgbColor newColor = _fakers.RgbColor.Generate(); - newColor.Id = ObjectId.GenerateNewId().ToString(); + newColor.Id = "free-format-client-generated-id-4"; var requestBody = new { @@ -200,7 +199,7 @@ public async Task Cannot_create_resource_for_existing_client_generated_ID() { // Arrange RgbColor existingColor = _fakers.RgbColor.Generate(); - existingColor.Id = ObjectId.GenerateNewId().ToString(); + existingColor.Id = "free-format-client-generated-id-5"; string newDisplayName = _fakers.RgbColor.Generate().DisplayName; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/RgbColor.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/RgbColor.cs index 7d95c46..a86f1c2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/RgbColor.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/RgbColor.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite")] -public sealed class RgbColor : MongoIdentifiable +public sealed class RgbColor : FreeStringMongoIdentifiable { [Attr] public string DisplayName { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/UserAccount.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/UserAccount.cs index a024856..53ae918 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/UserAccount.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/UserAccount.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite")] -public sealed class UserAccount : MongoIdentifiable +public sealed class UserAccount : HexStringMongoIdentifiable { [Attr] public string FirstName { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs index 925ed77..4eecb06 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite")] -public sealed class WorkItem : MongoIdentifiable +public sealed class WorkItem : HexStringMongoIdentifiable { [Attr] public string? Description { get; set; } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItemGroup.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItemGroup.cs index f46a306..6dd08e4 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItemGroup.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItemGroup.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite")] -public sealed class WorkItemGroup : MongoIdentifiable +public sealed class WorkItemGroup : FreeStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs index 01a061d..48de6cf 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkTag.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class WorkTag : MongoIdentifiable +public sealed class WorkTag : HexStringMongoIdentifiable { [Attr] public string Text { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs index bf5615f..603a755 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Rea [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading")] -public sealed class Moon : MongoIdentifiable +public sealed class Moon : HexStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs index f84d013..6b94174 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Rea [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading")] -public sealed class Planet : MongoIdentifiable +public sealed class Planet : HexStringMongoIdentifiable { [Attr] public string PublicName { get; set; } = null!; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs index 3c30784..12ede8b 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Rea [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading")] -public sealed class Star : MongoIdentifiable +public sealed class Star : HexStringMongoIdentifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/TestBuildingBlocks/MongoDbContextShim.cs b/test/TestBuildingBlocks/MongoDbContextShim.cs index 9c07d9f..323af40 100644 --- a/test/TestBuildingBlocks/MongoDbContextShim.cs +++ b/test/TestBuildingBlocks/MongoDbContextShim.cs @@ -18,7 +18,7 @@ protected MongoDbContextShim(IMongoDatabase database) } protected MongoDbSetShim Set() - where TEntity : MongoIdentifiable + where TEntity : IMongoIdentifiable { IMongoCollection collection = _database.GetCollection(typeof(TEntity).Name); var dbSetShim = new MongoDbSetShim(collection); @@ -28,7 +28,7 @@ protected MongoDbSetShim Set() } public async Task ClearTableAsync() - where TEntity : MongoIdentifiable + where TEntity : IMongoIdentifiable { await _database.DropCollectionAsync(typeof(TEntity).Name); } diff --git a/test/TestBuildingBlocks/MongoDbSetShim.cs b/test/TestBuildingBlocks/MongoDbSetShim.cs index af1bf8e..7e58223 100644 --- a/test/TestBuildingBlocks/MongoDbSetShim.cs +++ b/test/TestBuildingBlocks/MongoDbSetShim.cs @@ -15,7 +15,7 @@ public abstract class MongoDbSetShim /// repository. /// public sealed class MongoDbSetShim : MongoDbSetShim - where TEntity : MongoIdentifiable + where TEntity : IMongoIdentifiable { private readonly IMongoCollection _collection; private readonly List _entitiesToInsert = new(); @@ -64,21 +64,19 @@ public async Task ExecuteAsync(Func, Task> action) public async Task FirstWithIdAsync(string? id, CancellationToken cancellationToken = default) { - MongoIdentifiable? firstOrDefault = await _collection.AsQueryable().FirstOrDefaultAsync(document => Equals(document.Id, id), cancellationToken); + TEntity entity = await _collection.AsQueryable().FirstOrDefaultAsync(document => Equals(document.Id, id), cancellationToken); - if (Equals(firstOrDefault, default(MongoIdentifiable))) + if (entity is null) { throw new InvalidOperationException($"Resource with ID '{id}' was not found."); } - return (TEntity)firstOrDefault; + return entity; } public async Task FirstWithIdOrDefaultAsync(string? id, CancellationToken cancellationToken = default) { - MongoIdentifiable? entity = await _collection.AsQueryable().FirstOrDefaultAsync(document => Equals(document.Id, id), cancellationToken); - - return (TEntity?)entity; + return await _collection.AsQueryable().FirstOrDefaultAsync(document => Equals(document.Id, id), cancellationToken); } public async Task> ToListAsync(CancellationToken cancellationToken = default)