diff --git a/src/WireMock.Net.Abstractions/Admin/Requests/LogEntryModel.cs b/src/WireMock.Net.Abstractions/Admin/Requests/LogEntryModel.cs index 9c4ffefa0..aa895697e 100644 --- a/src/WireMock.Net.Abstractions/Admin/Requests/LogEntryModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Requests/LogEntryModel.cs @@ -15,12 +15,12 @@ public class LogEntryModel /// /// The request. /// - public LogRequestModel Request { get; set; } + public required LogRequestModel Request { get; init; } /// /// The response. /// - public LogResponseModel Response { get; set; } + public required LogResponseModel Response { get; init; } /// /// The mapping unique identifier. diff --git a/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj b/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj index f6c9f501b..29aa28054 100644 --- a/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj +++ b/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj @@ -43,6 +43,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs index 92fdcb709..e8f07c2e0 100644 --- a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs +++ b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs @@ -178,7 +178,7 @@ public interface IWireMockAdminApi /// Get a request based on the guid /// /// The Guid - /// MappingModel + /// LogEntryModel /// The optional cancellationToken. [Get("requests/{guid}")] Task GetRequestAsync([Path] Guid guid, CancellationToken cancellationToken = default); @@ -192,7 +192,7 @@ public interface IWireMockAdminApi Task DeleteRequestAsync([Path] Guid guid, CancellationToken cancellationToken = default); /// - /// Find a request based on the criteria + /// Find requests based on the criteria () /// /// The RequestModel /// The optional cancellationToken. @@ -200,6 +200,14 @@ public interface IWireMockAdminApi [Header("Content-Type", "application/json")] Task> FindRequestsAsync([Body] RequestModel model, CancellationToken cancellationToken = default); + /// + /// Find a request based on the Mapping Guid. + /// + /// The Mapping Guid + /// The optional cancellationToken. + [Get("requests/find")] + Task FindRequestByMappingGuidAsync([Query] Guid mappingGuid, CancellationToken cancellationToken = default); + /// /// Get all scenarios /// diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index a0fce63e8..15c225d7f 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -99,6 +99,7 @@ private void InitAdmin() // __admin/requests/find Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsFind)); + Given(Request.Create().WithPath(AdminRequests + "/find").UsingGet().WithParam("mappingGuid", new NotNullOrEmptyMatcher())).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestFindByMappingGuid)); // __admin/scenarios Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet)); @@ -436,7 +437,7 @@ private IResponseMessage MappingsPost(IRequestMessage requestMessage) var mappingModels = DeserializeRequestMessageToArray(requestMessage); if (mappingModels.Length == 1) { - Guid? guid = ConvertMappingAndRegisterAsRespondProvider(mappingModels[0]); + var guid = ConvertMappingAndRegisterAsRespondProvider(mappingModels[0]); return ResponseMessageBuilder.Create(201, "Mapping added", guid); } @@ -535,7 +536,7 @@ private IResponseMessage RequestGet(IRequestMessage requestMessage) { if (TryParseGuidFromRequestMessage(requestMessage, out var guid)) { - var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid); + var entry = LogEntries.SingleOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid); if (entry is { }) { var model = new LogEntryMapper(_options).Map(entry); @@ -600,6 +601,26 @@ private IResponseMessage RequestsFind(IRequestMessage requestMessage) return ToJson(result); } + + private IResponseMessage RequestFindByMappingGuid(IRequestMessage requestMessage) + { + if (requestMessage.Query != null && + requestMessage.Query.TryGetValue("mappingGuid", out var value) && + Guid.TryParse(value.ToString(), out var mappingGuid) + ) + { + var logEntry = LogEntries.SingleOrDefault(le => !le.RequestMessage.Path.StartsWith("/__admin/") && le.MappingGuid == mappingGuid); + if (logEntry != null) + { + var logEntryMapper = new LogEntryMapper(_options); + return ToJson(logEntryMapper.Map(logEntry)); + } + + return ResponseMessageBuilder.Create(HttpStatusCode.OK); + } + + return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest); + } #endregion Requests/find #region Scenarios diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 895c5acf4..bcb52ca51 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -72,13 +72,6 @@ - - - all - runtime; build; native; contentfiles; analyzers - - - diff --git a/test/WireMock.Net.Tests/WireMockAdminApiTests.cs b/test/WireMock.Net.Tests/WireMockAdminApiTests.cs index 43f83a8e4..b25018321 100644 --- a/test/WireMock.Net.Tests/WireMockAdminApiTests.cs +++ b/test/WireMock.Net.Tests/WireMockAdminApiTests.cs @@ -234,6 +234,10 @@ public async Task IWireMockAdminApi_FindRequestsAsync() StartAdminInterface = true, Logger = new WireMockNullLogger() }); + server + .Given(Request.Create().WithPath("/foo").UsingGet()) + .RespondWith(Response.Create()); + var serverUrl = "http://localhost:" + server.Ports[0]; await new HttpClient().GetAsync(serverUrl + "/foo").ConfigureAwait(false); var api = RestClient.For(serverUrl); @@ -242,11 +246,82 @@ public async Task IWireMockAdminApi_FindRequestsAsync() var requests = await api.FindRequestsAsync(new RequestModel { Methods = new[] { "GET" } }).ConfigureAwait(false); // Assert - Check.That(requests).HasSize(1); + requests.Should().HaveCount(1); var requestLogged = requests.First(); - Check.That(requestLogged.Request.Method).IsEqualTo("GET"); - Check.That(requestLogged.Request.Body).IsNull(); - Check.That(requestLogged.Request.Path).IsEqualTo("/foo"); + requestLogged.Request.Method.Should().Be("GET"); + requestLogged.Request.Body.Should().BeNull(); + requestLogged.Request.Path.Should().Be("/foo"); + } + + [Fact] + public async Task IWireMockAdminApi_FindRequestByMappingGuidAsync_Found() + { + // Arrange + var mappingGuid = Guid.NewGuid(); + var server = WireMockServer.Start(new WireMockServerSettings + { + StartAdminInterface = true, + Logger = new WireMockNullLogger() + }); + server + .Given(Request.Create().WithPath("/foo").UsingGet()) + .WithGuid(mappingGuid) + .RespondWith(Response.Create()); + + var serverUrl = "http://localhost:" + server.Ports[0]; + await new HttpClient().GetAsync(serverUrl + "/foo").ConfigureAwait(false); + var api = RestClient.For(serverUrl); + + // Act + var logEntryModel = await api.FindRequestByMappingGuidAsync(mappingGuid).ConfigureAwait(false); + + // Assert + logEntryModel.Should().NotBeNull(); + logEntryModel!.Request.Method.Should().Be("GET"); + logEntryModel!.Request.Body.Should().BeNull(); + logEntryModel!.Request.Path.Should().Be("/foo"); + } + + [Fact] + public async Task IWireMockAdminApi_FindRequestByMappingGuidAsync_NotFound() + { + // Arrange + var server = WireMockServer.Start(new WireMockServerSettings + { + StartAdminInterface = true, + Logger = new WireMockNullLogger() + }); + server + .Given(Request.Create().WithPath("/foo").UsingGet()) + .WithGuid(Guid.NewGuid()) + .RespondWith(Response.Create()); + + var serverUrl = "http://localhost:" + server.Ports[0]; + await new HttpClient().GetAsync(serverUrl + "/foo").ConfigureAwait(false); + var api = RestClient.For(serverUrl); + + // Act + var logEntryModel = await api.FindRequestByMappingGuidAsync(Guid.NewGuid()).ConfigureAwait(false); + + // Assert + logEntryModel.Should().BeNull(); + } + + [Fact] + public async Task IWireMockAdminApi_FindRequestByMappingGuidAsync_Invalid_ShouldReturnBadRequest() + { + // Arrange + var server = WireMockServer.Start(new WireMockServerSettings + { + StartAdminInterface = true, + Logger = new WireMockNullLogger() + }); + + // Act + var result = await server.CreateClient().GetAsync("/__admin/requests/find?mappingGuid=x"); + + // Assert + result.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] @@ -815,7 +890,7 @@ public async Task IWireMockAdminApi_GetMappingsCode() .RespondWith( Response.Create() .WithStatusCode(HttpStatusCode.AlreadyReported) - .WithBodyAsJson(new { @as = 1, b=1.2, d=true, e=false, f=new[]{1,2,3,4}, g= new{z1=1, z2=2, z3=new []{"a","b","c"}, z4=new[]{new {a=1, b=2},new {a=2, b=3}}}, date_field = new DateTime(2023,05,08,11,20,19), string_field_with_date="2021-03-13T21:04:00Z", multiline_text= @"This + .WithBodyAsJson(new { @as = 1, b = 1.2, d = true, e = false, f = new[] { 1, 2, 3, 4 }, g = new { z1 = 1, z2 = 2, z3 = new[] { "a", "b", "c" }, z4 = new[] { new { a = 1, b = 2 }, new { a = 2, b = 3 } } }, date_field = new DateTime(2023, 05, 08, 11, 20, 19), string_field_with_date = "2021-03-13T21:04:00Z", multiline_text = @"This is multiline text @@ -826,7 +901,7 @@ public async Task IWireMockAdminApi_GetMappingsCode() .Given( Request.Create() .WithPath("/foo3") - .WithBody(new JsonPartialMatcher(new { a=1, b=2})) + .WithBody(new JsonPartialMatcher(new { a = 1, b = 2 })) .UsingPost() ) .WithGuid(guid4) diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs index 1d7240eca..1504b6369 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs @@ -117,7 +117,7 @@ public async Task WireMockServer_Proxy_AdminTrue_With_SaveMapping_Is_True_And_Sa } // Assert - server.Mappings.Should().HaveCount(34); + server.Mappings.Should().HaveCount(35); } [Fact] diff --git a/test/WireMock.Net.Tests/WireMockServer.Settings.cs b/test/WireMock.Net.Tests/WireMockServer.Settings.cs index e0974cb25..d1e8e9c94 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Settings.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Settings.cs @@ -81,7 +81,7 @@ public void WireMockServer_WireMockServerSettings_PriorityFromAllAdminMappingsIs // Assert server.Mappings.Should().NotBeNull(); - server.Mappings.Should().HaveCount(32); + server.Mappings.Should().HaveCount(33); server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue(); } @@ -100,9 +100,9 @@ public void WireMockServer_WireMockServerSettings_ProxyAndRecordSettings_ProxyPr // Assert server.Mappings.Should().NotBeNull(); - server.Mappings.Should().HaveCount(33); + server.Mappings.Should().HaveCount(34); - server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(32); + server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(33); server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1); }