From bc7f3eab792be71c0393bfaada880978e86016de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Ol=C4=99cki?= Date: Fri, 29 Dec 2023 14:59:03 +0100 Subject: [PATCH] Replace Device model with MyThing model The commit represents a significant refactoring in favor of an abstracted 'MyThing' model replacing the previous 'Device' model. All instances and usages of 'Device' have been removed and replaced with 'MyThing', going as far as to change endpoint routes and update tests to match the changes. Events and handlers have also been updated to match MyThing's functionality. --- SimpleWebApi8.Tests/WebApiTests.cs | 55 +++++++++++++++++-- SimpleWebApi8/DeviceHandlers.cs | 31 ----------- SimpleWebApi8/EventSourcing.cs | 84 ++++++++++++++++-------------- SimpleWebApi8/Program.cs | 6 +-- SimpleWebApi8/ThingHandlers.cs | 32 ++++++++++++ 5 files changed, 130 insertions(+), 78 deletions(-) delete mode 100644 SimpleWebApi8/DeviceHandlers.cs create mode 100644 SimpleWebApi8/ThingHandlers.cs diff --git a/SimpleWebApi8.Tests/WebApiTests.cs b/SimpleWebApi8.Tests/WebApiTests.cs index 40ee36a..5ea3f2f 100644 --- a/SimpleWebApi8.Tests/WebApiTests.cs +++ b/SimpleWebApi8.Tests/WebApiTests.cs @@ -12,10 +12,10 @@ public WebAppTest(CustomWebAppFactory factory) } [Fact] - public async Task TestCreateDeviceEndpoint() + public async Task TestCreateMyThingEndpoint() { // Arrange - var httpRequest = new HttpRequestMessage(HttpMethod.Get, "/createDevice"); + var httpRequest = new HttpRequestMessage(HttpMethod.Get, "/createMyThing"); // Act var httpResponse = await _client.SendAsync(httpRequest); @@ -23,8 +23,55 @@ public async Task TestCreateDeviceEndpoint() // Assert httpResponse.EnsureSuccessStatusCode(); // fails the test if the status code is not a success status code (2xx) var responseContent = await httpResponse.Content.ReadAsStringAsync(); - var device = JsonConvert.DeserializeObject(responseContent); + var myThing = JsonConvert.DeserializeObject(responseContent); - Assert.Equal("My Device", device?.Name); + Assert.Equal("My Thing", myThing?.Name); } + + [Fact] +public async Task TestCreateAndRetrieveMyThingEndpoint() +{ + // Arrange + var createRequest = new HttpRequestMessage(HttpMethod.Get, $"/createMyThing"); + + // Act + var createResponse = await _client.SendAsync(createRequest); + + // Assert + createResponse.EnsureSuccessStatusCode(); // fails the test if the status code is not a success status code (2xx) + var createResponseContent = await createResponse.Content.ReadAsStringAsync(); + var createdMyThing = JsonConvert.DeserializeObject(createResponseContent); + + var myThingId = createdMyThing?.Id ?? Guid.Empty; + var myThing = new MyThing("My Thing", myThingId) + { + Description = "This is my thing", + Tags = ["tag1", "tag2"], + Picture = [], // replace with your picture bytes + Place = "My place" + }; + + Assert.Equal(myThing.Name, createdMyThing?.Name); + Assert.Equal(myThing.Description, createdMyThing?.Description); + Assert.Equal(myThing.Tags, createdMyThing?.Tags); + Assert.Equal(myThing.Picture, createdMyThing?.Picture); + Assert.Equal(myThing.Place, createdMyThing?.Place); + + // Arrange + var retrieveRequest = new HttpRequestMessage(HttpMethod.Get, $"/myThing/{myThingId}"); + + // Act + var retrieveResponse = await _client.SendAsync(retrieveRequest); + + // Assert + retrieveResponse.EnsureSuccessStatusCode(); // fails the test if the status code is not a success status code (2xx) + var retrieveResponseContent = await retrieveResponse.Content.ReadAsStringAsync(); + var retrievedMyThing = JsonConvert.DeserializeObject(retrieveResponseContent); + + Assert.Equal(myThing.Name, retrievedMyThing?.Name); + Assert.Equal(myThing.Description, retrievedMyThing?.Description); + Assert.Equal(myThing.Tags, retrievedMyThing?.Tags); + Assert.Equal(myThing.Picture, retrievedMyThing?.Picture); + Assert.Equal(myThing.Place, retrievedMyThing?.Place); +} } \ No newline at end of file diff --git a/SimpleWebApi8/DeviceHandlers.cs b/SimpleWebApi8/DeviceHandlers.cs deleted file mode 100644 index 28f3157..0000000 --- a/SimpleWebApi8/DeviceHandlers.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Marten; - -namespace SimpleWebApi8; - -public static class DeviceHandlers -{ - public static async Task CreateDevice(DocumentStore store) - { - var deviceId = Guid.NewGuid(); - - await using var session = store.LightweightSession(); - var deviceInitialized = new DeviceInitialized("My Device") - { - Volume = 50, - Muted = false - }; - var volumeChanged = new VolumeChanged { Volume = 75 }; - - session.Events.StartStream(deviceId, deviceInitialized, volumeChanged); - await session.SaveChangesAsync(); - - var test = session.Events.AggregateStream(deviceId); - return session.Events.AggregateStream(deviceId); - } - - public static async Task GetDeviceById(Guid deviceId, DocumentStore store) - { - await using var session = store.LightweightSession(); - return session.Events.AggregateStream(deviceId); - } -} \ No newline at end of file diff --git a/SimpleWebApi8/EventSourcing.cs b/SimpleWebApi8/EventSourcing.cs index 792a14b..96d143b 100644 --- a/SimpleWebApi8/EventSourcing.cs +++ b/SimpleWebApi8/EventSourcing.cs @@ -2,76 +2,80 @@ namespace SimpleWebApi8; -public class VolumeChanged +public abstract class NameChanged(string name) { - public int Volume { get; init; } + public string Name { get; set; } = name; public override string ToString() { - return $"New volume: {Volume} "; + return $"New name: {Name} "; } } -public abstract class MuteToggled(bool muted) +public class DescriptionChanged { - public bool Muted { get; set; } = muted; - - public override string ToString() - { - return $"Muted: {Muted} "; - } + public required string Description { get; set; } } -public abstract class NameChanged(string name) +public class TagsChanged { - public string Name { get; set; } = name; + public required string[] Tags { get; set; } +} - public override string ToString() - { - return $"New name: {Name} "; - } +public class PictureChanged +{ + public required byte[] Picture { get; set; } } -public class DeviceInitialized(string name) +public class PlaceChanged { - public string Name { get; init; } = name; - public int Volume { get; init; } - public bool Muted { get; init; } + public required string Place { get; set; } +} - public override string ToString() - { - return $"Initialized with name: {Name}, volume: {Volume}, muted: {Muted} "; - } +public class MyThingInitialized +{ + public required string Name { get; set; } + public required string Description { get; set; } + public required string[] Tags { get; set; } + public required byte[] Picture { get; set; } + public required string Place { get; set; } } -public class Device(string name, Guid id) +public class MyThing(string name, Guid id) { public Guid Id { get; set; } = id; public string Name { get; private set; } = name; - public int Volume { get; set; } - public bool Muted { get; set; } + public required string Description { get; set; } + public required string[] Tags { get; set; } + public required byte[] Picture { get; set; } + public required string Place { get; set; } - public void Apply(VolumeChanged newVolume) => Volume = newVolume.Volume; - public void Apply(MuteToggled muteToggled) => Muted = muteToggled.Muted; - public void Apply(NameChanged newName) => Name = newName.Name; - public void Apply(DeviceInitialized deviceInitialized) + public void Apply(DescriptionChanged newDescription) => Description = newDescription.Description; + public void Apply(TagsChanged newTags) => Tags = newTags.Tags; + public void Apply(PictureChanged newPicture) => Picture = newPicture.Picture; + public void Apply(PlaceChanged newPlace) => Place = newPlace.Place; + public void Apply(MyThingInitialized myThingInitialized) { - Name = deviceInitialized.Name; - Volume = deviceInitialized.Volume; - Muted = deviceInitialized.Muted; + Name = myThingInitialized.Name; + Description = myThingInitialized.Description; + Tags = myThingInitialized.Tags; + Picture = myThingInitialized.Picture; + Place = myThingInitialized.Place; } public override string ToString() { - return $"Device {Id} with name: {Name}, volume: {Volume}, muted: {Muted} "; + return $"MyThing {Id} with name: {Name}, description: {Description}, tags: {string.Join(", ", Tags)}, place: {Place}"; } - - public static Device Create(IEvent @event) + + public static MyThing Create(IEvent @event) { - var newDevice = new Device(@event.Data.Name, @event.StreamId) + var newDevice = new MyThing(@event.Data.Name, @event.StreamId) { - Muted = @event.Data.Muted, - Volume = @event.Data.Volume + Description = @event.Data.Description, + Tags = @event.Data.Tags, + Picture = @event.Data.Picture, + Place = @event.Data.Place }; return newDevice; diff --git a/SimpleWebApi8/Program.cs b/SimpleWebApi8/Program.cs index 1c9502e..58ca0e0 100644 --- a/SimpleWebApi8/Program.cs +++ b/SimpleWebApi8/Program.cs @@ -23,8 +23,8 @@ app.MapGet("/", () => "Hello World!"); app.MapGet("/random", () => new Random().Next(1, 100).ToString()); -app.MapGet("createDevice", DeviceHandlers.CreateDevice); -app.MapGet("device/{deviceId:guid}", DeviceHandlers.GetDeviceById); +app.MapGet("createMyThing", ThingHandlers.CreateMyThing); +app.MapGet("myThing/{myThingId:guid}", ThingHandlers.GetMyThingById); app.Run(); return; @@ -33,7 +33,7 @@ DocumentStore GetDeviceStore(IServiceProvider serviceProvider) var documentStore = DocumentStore.For(options => { if (connectionString != null) options.Connection(connectionString); - options.Projections.Snapshot(SnapshotLifecycle.Inline); + options.Projections.Snapshot(SnapshotLifecycle.Inline); }); return documentStore; } diff --git a/SimpleWebApi8/ThingHandlers.cs b/SimpleWebApi8/ThingHandlers.cs new file mode 100644 index 0000000..4241387 --- /dev/null +++ b/SimpleWebApi8/ThingHandlers.cs @@ -0,0 +1,32 @@ +using Marten; + +namespace SimpleWebApi8; + +public static class ThingHandlers +{ + public static async Task CreateMyThing(DocumentStore store) +{ + var thingId = Guid.NewGuid(); + + await using var session = store.LightweightSession(); + var myThingInitialized = new MyThingInitialized + { + Name = "My Thing", + Description = "This is my thing", + Tags = ["tag1", "tag2"], + Picture = [], // replace with your picture bytes + Place = "My place" + }; + + session.Events.StartStream(thingId, myThingInitialized); + await session.SaveChangesAsync(); + + return session.Events.AggregateStream(thingId); +} + + public static async Task GetMyThingById(Guid myThingId, DocumentStore store) +{ + await using var session = store.LightweightSession(); + return session.Events.AggregateStream(myThingId); +} +} \ No newline at end of file