From 8788d9ba4ad43bd8ac06250e9465415c0205e952 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Jul 2024 07:06:38 +0200 Subject: [PATCH] Add AdminPath to WireMockServerSettings (#1130) * Make admin endpoint configurable * Add AdminPath to WireMockServerSettings * sealed * foo * WireMockServer_CreateClient_And_CallAdminSettingsEndpoint --- .../Server/WireMockServer.Admin.cs | 121 ++++++++++-------- .../Server/WireMockServer.AdminFiles.cs | 6 +- .../Settings/WireMockServerSettings.cs | 25 ++-- .../Settings/WireMockServerSettingsParser.cs | 1 + .../AdminApi/WireMockAdminApiTests.cs | 22 ++++ .../WireMockServerSettingsParserTests.cs | 35 +++++ .../WireMockServer.Admin.cs | 54 +++++--- 7 files changed, 182 insertions(+), 82 deletions(-) create mode 100644 test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 0656efa4..c52ab499 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -19,6 +19,7 @@ using WireMock.Matchers.Request; using WireMock.RequestBuilders; using WireMock.ResponseProviders; using WireMock.Serialization; +using WireMock.Settings; using WireMock.Types; using WireMock.Util; @@ -30,100 +31,116 @@ namespace WireMock.Server; public partial class WireMockServer { private const int EnhancedFileSystemWatcherTimeoutMs = 1000; - private const string AdminFiles = "/__admin/files"; - private const string AdminHealth = "/__admin/health"; - private const string AdminMappings = "/__admin/mappings"; - private const string AdminMappingsCode = "/__admin/mappings/code"; - private const string AdminMappingsWireMockOrg = "/__admin/mappings/wiremock.org"; - private const string AdminRequests = "/__admin/requests"; - private const string AdminSettings = "/__admin/settings"; - private const string AdminScenarios = "/__admin/scenarios"; - private const string AdminOpenApi = "/__admin/openapi"; - + private const string DefaultAdminPathPrefix = "/__admin"; private const string QueryParamReloadStaticMappings = "reloadStaticMappings"; - private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567"); private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true); - private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); - private static readonly RegexMatcher AdminMappingsCodeGuidPathMatcher = new(@"^\/__admin\/mappings\/code\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); - private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); - private static readonly RegexMatcher AdminScenariosNameMatcher = new(@"^\/__admin\/scenarios\/.+$"); - private static readonly RegexMatcher AdminScenariosNameWithResetMatcher = new(@"^\/__admin\/scenarios\/.+\/reset$"); - private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher; + private AdminPaths? _adminPaths; + + private sealed class AdminPaths + { + private readonly string _prefix; + private readonly string _prefixEscaped; + + public AdminPaths(WireMockServerSettings settings) + { + _prefix = settings.AdminPath ?? DefaultAdminPathPrefix; + _prefixEscaped = _prefix.Replace("/", "\\/"); + } + + public string Files => $"{_prefix}/files"; + public string Health => $"{_prefix}/health"; + public string Mappings => $"{_prefix}/mappings"; + public string MappingsCode => $"{_prefix}/mappings/code"; + public string MappingsWireMockOrg => $"{_prefix}mappings/wiremock.org"; + public string Requests => $"{_prefix}/requests"; + public string Settings => $"{_prefix}/settings"; + public string Scenarios => $"{_prefix}/scenarios"; + public string OpenApi => $"{_prefix}/openapi"; + + public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$"); + public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/code\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$"); + public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/requests\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$"); + public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$"); + public RegexMatcher ScenariosNameWithResetMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+\\/reset$"); + public RegexMatcher FilesFilenamePathMatcher => new($"^{_prefixEscaped}\\/files\\/.+$"); + } #region InitAdmin private void InitAdmin() { + _adminPaths = new AdminPaths(_settings); + // __admin/health - Given(Request.Create().WithPath(AdminHealth).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(HealthGet)); + Given(Request.Create().WithPath(_adminPaths.Health).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(HealthGet)); // __admin/settings - Given(Request.Create().WithPath(AdminSettings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsGet)); - Given(Request.Create().WithPath(AdminSettings).UsingMethod("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsUpdate)); + Given(Request.Create().WithPath(_adminPaths.Settings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsGet)); + Given(Request.Create().WithPath(_adminPaths.Settings).UsingMethod("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsUpdate)); // __admin/mappings - Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet)); - Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost)); - Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete)); + Given(Request.Create().WithPath(_adminPaths.Mappings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet)); + Given(Request.Create().WithPath(_adminPaths.Mappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost)); + Given(Request.Create().WithPath(_adminPaths.Mappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete)); // __admin/mappings/code - Given(Request.Create().WithPath(AdminMappingsCode).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsCodeGet)); + Given(Request.Create().WithPath(_adminPaths.MappingsCode).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsCodeGet)); // __admin/mappings/wiremock.org - Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg)); + Given(Request.Create().WithPath(_adminPaths.MappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg)); // __admin/mappings/reset - Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset)); + Given(Request.Create().WithPath(_adminPaths.Mappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset)); // __admin/mappings/{guid} - Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingGet)); - Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut)); - Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete)); + Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingGet)); + Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut)); + Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete)); // __admin/mappings/code/{guid} - Given(Request.Create().WithPath(AdminMappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet)); + Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet)); // __admin/mappings/save - Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave)); + Given(Request.Create().WithPath($"{_adminPaths.Mappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave)); // __admin/mappings/swagger - Given(Request.Create().WithPath($"{AdminMappings}/swagger").UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SwaggerGet)); + Given(Request.Create().WithPath($"{_adminPaths.Mappings}/swagger").UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SwaggerGet)); // __admin/requests - Given(Request.Create().WithPath(AdminRequests).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsGet)); - Given(Request.Create().WithPath(AdminRequests).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete)); + Given(Request.Create().WithPath(_adminPaths.Requests).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsGet)); + Given(Request.Create().WithPath(_adminPaths.Requests).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete)); // __admin/requests/reset - Given(Request.Create().WithPath(AdminRequests + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete)); + Given(Request.Create().WithPath(_adminPaths.Requests + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete)); // __admin/request/{guid} - Given(Request.Create().WithPath(AdminRequestsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestGet)); - Given(Request.Create().WithPath(AdminRequestsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestDelete)); + Given(Request.Create().WithPath(_adminPaths.RequestsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestGet)); + Given(Request.Create().WithPath(_adminPaths.RequestsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestDelete)); // __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(RequestsFindByMappingGuid)); + Given(Request.Create().WithPath(_adminPaths.Requests + "/find").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsFind)); + Given(Request.Create().WithPath(_adminPaths.Requests + "/find").UsingGet().WithParam("mappingGuid", new NotNullOrEmptyMatcher())).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsFindByMappingGuid)); // __admin/scenarios - Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet)); - Given(Request.Create().WithPath(AdminScenarios).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); - Given(Request.Create().WithPath(AdminScenariosNameMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset)); + Given(Request.Create().WithPath(_adminPaths.Scenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet)); + Given(Request.Create().WithPath(_adminPaths.Scenarios).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); + Given(Request.Create().WithPath(_adminPaths.ScenariosNameMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset)); // __admin/scenarios/reset - Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); - Given(Request.Create().WithPath(AdminScenariosNameWithResetMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset)); + Given(Request.Create().WithPath(_adminPaths.Scenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); + Given(Request.Create().WithPath(_adminPaths.ScenariosNameWithResetMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset)); // __admin/files/{filename} - Given(Request.Create().WithPath(AdminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost)); - Given(Request.Create().WithPath(AdminFilesFilenamePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePut)); - Given(Request.Create().WithPath(AdminFilesFilenamePathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileGet)); - Given(Request.Create().WithPath(AdminFilesFilenamePathMatcher).UsingHead()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileHead)); - Given(Request.Create().WithPath(AdminFilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete)); + Given(Request.Create().WithPath(_adminPaths.FilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost)); + Given(Request.Create().WithPath(_adminPaths.FilesFilenamePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePut)); + Given(Request.Create().WithPath(_adminPaths.FilesFilenamePathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileGet)); + Given(Request.Create().WithPath(_adminPaths.FilesFilenamePathMatcher).UsingHead()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileHead)); + Given(Request.Create().WithPath(_adminPaths.FilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete)); // __admin/openapi - Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiConvertToMappings)); - Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiSaveToMappings)); + Given(Request.Create().WithPath($"{_adminPaths.OpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiConvertToMappings)); + Given(Request.Create().WithPath($"{_adminPaths.OpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiSaveToMappings)); } #endregion @@ -665,7 +682,7 @@ public partial class WireMockServer private IResponseMessage ScenarioReset(IRequestMessage requestMessage) { var name = string.Equals(HttpRequestMethod.DELETE, requestMessage.Method, StringComparison.OrdinalIgnoreCase) ? - requestMessage.Path.Substring(AdminScenarios.Length + 1) : + requestMessage.Path.Substring(_adminPaths!.Scenarios.Length + 1) : requestMessage.Path.Split('/').Reverse().Skip(1).First(); return ResetScenario(name) ? diff --git a/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs b/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs index 508e5fe9..6a374ec0 100644 --- a/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs +++ b/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using System.Net; using System.Text; -using WireMock.Matchers; using WireMock.Types; using WireMock.Util; @@ -10,7 +9,6 @@ namespace WireMock.Server; public partial class WireMockServer { - private static readonly RegexMatcher AdminFilesFilenamePathMatcher = new(@"^\/__admin\/files\/.*$"); private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII }; #region Files/{filename} @@ -117,9 +115,9 @@ public partial class WireMockServer return ResponseMessageBuilder.Create(HttpStatusCode.OK, "File deleted."); } - private static string GetFileNameFromRequestMessage(IRequestMessage requestMessage) + private string GetFileNameFromRequestMessage(IRequestMessage requestMessage) { - return Path.GetFileName(requestMessage.Path.Substring(AdminFiles.Length + 1)); + return Path.GetFileName(requestMessage.Path.Substring(_adminPaths!.Files.Length + 1)); } #endregion } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/WireMockServerSettings.cs b/src/WireMock.Net/Settings/WireMockServerSettings.cs index 4bfdd045..b8abedcb 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettings.cs @@ -245,16 +245,16 @@ public class WireMockServerSettings public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true; #if USE_ASPNETCORE - /// - /// Client certificate mode for the server - /// - [PublicAPI] - public ClientCertificateMode ClientCertificateMode { get; set; } + /// + /// Client certificate mode for the server + /// + [PublicAPI] + public ClientCertificateMode ClientCertificateMode { get; set; } - /// - /// Whether to accept any client certificate - /// - public bool AcceptAnyClientCertificate { get; set; } + /// + /// Whether to accept any client certificate + /// + public bool AcceptAnyClientCertificate { get; set; } #endif /// @@ -320,4 +320,11 @@ public class WireMockServerSettings /// [PublicAPI] public Dictionary? GraphQLSchemas { get; set; } + + /// + /// The admin path to use for accessing the Admin REST interface. + /// If not set __/admin is used. + /// + [PublicAPI] + public string? AdminPath { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs index 707aef10..1e5d179e 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs @@ -45,6 +45,7 @@ public static class WireMockServerSettingsParser AdminAzureADTenant = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADTenant)), AdminPassword = parser.GetStringValue(nameof(WireMockServerSettings.AdminPassword)), AdminUsername = parser.GetStringValue(nameof(WireMockServerSettings.AdminUsername)), + AdminPath = parser.GetStringValue(nameof(WireMockServerSettings.AdminPath), "/__admin"), AllowBodyForAllHttpMethods = parser.GetBoolValue(nameof(WireMockServerSettings.AllowBodyForAllHttpMethods)), AllowCSharpCodeMatcher = parser.GetBoolValue(nameof(WireMockServerSettings.AllowCSharpCodeMatcher)), AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue(nameof(WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse)), diff --git a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs index cc22ae77..80896323 100644 --- a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs +++ b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs @@ -95,6 +95,28 @@ public partial class WireMockAdminApiTests Check.That(settings).IsNotNull(); } + [Fact] + public async Task IWireMockAdminApi_GetSettingsAsync_ForDifferentAdminPath() + { + // Arrange + var server = WireMockServer.Start(w => + { + w.StartAdminInterface = true; + w.AdminPath = "/foo/__admin"; + }); + var api = RestClient.For(server.Urls[0] + "/foo"); + + // Act + var settings = await api.GetSettingsAsync().ConfigureAwait(false); + + // Assert + Check.That(settings).IsNotNull(); + + // Cleanup + server.Stop(); + server.Dispose(); + } + [Fact] public async Task IWireMockAdminApi_PostSettingsAsync() { diff --git a/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs b/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs new file mode 100644 index 00000000..492d8e5b --- /dev/null +++ b/test/WireMock.Net.Tests/Settings/WireMockServerSettingsParserTests.cs @@ -0,0 +1,35 @@ +using FluentAssertions; +using WireMock.Settings; +using Xunit; + +namespace WireMock.Net.Tests.Settings; + +public class WireMockServerSettingsParserTests +{ + [Fact] + public void TryParseArguments_With_Args() + { + // Act + var result = WireMockServerSettingsParser.TryParseArguments(new[] + { + "--adminPath", "ap" + }, null, out var settings); + + // Assert + result.Should().BeTrue(); + settings.Should().NotBeNull(); + settings!.AdminPath.Should().Be("ap"); + } + + [Fact] + public void TryParseArguments_Without_Args() + { + // Act + var result = WireMockServerSettingsParser.TryParseArguments(new string[] { }, null, out var settings); + + // Assert + result.Should().BeTrue(); + settings.Should().NotBeNull(); + settings!.AdminPath.Should().Be("/__admin"); + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMockServer.Admin.cs b/test/WireMock.Net.Tests/WireMockServer.Admin.cs index df376b5a..9b076a33 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Admin.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Admin.cs @@ -9,6 +9,7 @@ using FluentAssertions; using Moq; using Newtonsoft.Json; using NFluent; +using WireMock.Admin.Settings; using WireMock.Handlers; using WireMock.Logging; using WireMock.RequestBuilders; @@ -22,13 +23,9 @@ namespace WireMock.Net.Tests; public class WireMockServerAdminTests { // For for AppVeyor + OpenCover - private string GetCurrentFolder() + private static string GetCurrentFolder() { - string current = Directory.GetCurrentDirectory(); - //if (!current.EndsWith("WireMock.Net.Tests")) - // return Path.Combine(current, "test", "WireMock.Net.Tests"); - - return current; + return Directory.GetCurrentDirectory(); } [Fact] @@ -468,9 +465,9 @@ public class WireMockServerAdminTests Check.That(server.MappingModels.Count()).Equals(3); - Guid? guid1 = server.MappingModels.ElementAt(0).Guid; - Guid? guid2 = server.MappingModels.ElementAt(1).Guid; - Guid? guid3 = server.MappingModels.ElementAt(2).Guid; + var guid1 = server.MappingModels.ElementAt(0).Guid; + var guid2 = server.MappingModels.ElementAt(1).Guid; + var guid3 = server.MappingModels.ElementAt(2).Guid; Check.That(guid1).IsNotNull(); Check.That(guid2).IsNotNull(); @@ -482,7 +479,7 @@ public class WireMockServerAdminTests $"]"; // Act - var request = new HttpRequestMessage() + var request = new HttpRequestMessage { Method = HttpMethod.Delete, RequestUri = new Uri($"http://localhost:{server.Ports[0]}/__admin/mappings"), @@ -501,22 +498,45 @@ public class WireMockServerAdminTests } [Fact] - public async Task WireMockServer_Admin_() + public async Task WireMockServer_CreateClient_And_CallEndpoint() { - // given + // Arrange var server = WireMockServer.Start(); + var client = server.CreateClient(); - server.CreateClient(); + // Act + await client.GetAsync($"{server.Url}/foo").ConfigureAwait(false); - // when - await new HttpClient().GetAsync("http://localhost:" + server.Ports[0] + "/foo").ConfigureAwait(false); - - // then + // Assert Check.That(server.LogEntries).HasSize(1); var requestLogged = server.LogEntries.First(); Check.That(requestLogged.RequestMessage.Method).IsEqualTo("GET"); Check.That(requestLogged.RequestMessage.BodyData).IsNull(); + // Cleanup server.Stop(); + server.Dispose(); + } + + [Fact] + public async Task WireMockServer_CreateClient_And_CallAdminSettingsEndpoint() + { + // Arrange + var server = WireMockServer.Start(w => + { + w.StartAdminInterface = true; + w.AdminPath = "/adm"; + }); + var client = server.CreateClient(); + + // Act + var settings = await client.GetStringAsync($"{server.Url}/adm/settings").ConfigureAwait(false); + + // Assert + settings.Should().NotBeNull(); + + // Cleanup + server.Stop(); + server.Dispose(); } } \ No newline at end of file