// Copyright © WireMock.Net using System.Diagnostics.CodeAnalysis; using Stef.Validation; using WireMock.Client.Builders; using WireMock.Util; // ReSharper disable once CheckNamespace namespace Aspire.Hosting; /// /// Represents the arguments required to configure and start a WireMock.Net Server. /// public class WireMockServerArguments { internal const int HttpContainerPort = 80; /// /// The default HTTP port where WireMock.Net is listening. /// public const int DefaultPort = 9091; private const string DefaultLogger = "WireMockConsoleLogger"; /// /// The HTTP ports where WireMock.Net is listening on. /// If not defined, .NET Aspire automatically assigns a random port. /// public List HttpPorts { get; set; } = []; /// /// Additional Urls on which WireMock listens. /// public List AdditionalUrls { get; set; } = []; /// /// The admin username. /// [MemberNotNullWhen(true, nameof(HasBasicAuthentication))] public string? AdminUsername { get; set; } /// /// The admin password. /// [MemberNotNullWhen(true, nameof(HasBasicAuthentication))] public string? AdminPassword { get; set; } /// /// Defines if the static mappings should be read at startup. /// /// Default value is false. /// public bool ReadStaticMappings { get; set; } /// /// Watch the static mapping files + folder for changes when running. /// /// Default value is false. /// public bool WatchStaticMappings { get; set; } /// /// Specifies the path for the (static) mapping json files. /// public string? MappingsPath { get; set; } /// /// Indicates whether the admin interface has Basic Authentication. /// public bool HasBasicAuthentication => !string.IsNullOrEmpty(AdminUsername) && !string.IsNullOrEmpty(AdminPassword); /// /// Optional delegate that will be invoked to configure the WireMock.Net resource using the . /// public Func? ApiMappingBuilder { get; set; } /// /// Grpc ProtoDefinitions. /// public Dictionary ProtoDefinitions { get; set; } = []; /// /// Gets or sets a value indicating whether OpenTelemetry tracing is enabled. /// When enabled, WireMock.Net will emit distributed traces for request processing. /// Default value is false. /// public bool OpenTelemetryEnabled { get; set; } /// /// Gets or sets the OTLP exporter endpoint URL. /// When set, traces will be exported to this endpoint using the OTLP protocol. /// Example: "http://localhost:4317" for gRPC or "http://localhost:4318" for HTTP. /// If not set, the OTLP exporter will use the OTEL_EXPORTER_OTLP_ENDPOINT environment variable, /// or fall back to the default endpoint (http://localhost:4317 for gRPC). /// public string? OpenTelemetryOtlpExporterEndpoint { get; set; } /// /// Add an additional Urls on which WireMock should listen. /// /// The additional urls which the WireMock Server should listen on. public void WithAdditionalUrls(params string[] additionalUrls) { foreach (var url in additionalUrls) { if (!PortUtils.TryExtract(Guard.NotNullOrEmpty(url), out _, out _, out _, out _, out var port)) { throw new ArgumentException($"The URL '{url}' is not valid."); } AdditionalUrls.Add(Guard.NotNullOrWhiteSpace(url)); HttpPorts.Add(port); } } /// /// Add a Grpc ProtoDefinition at server-level. /// /// Unique identifier for the ProtoDefinition. /// The ProtoDefinition as text. public void WithProtoDefinition(string id, params string[] protoDefinitions) { Guard.NotNullOrWhiteSpace(id); Guard.NotNullOrEmpty(protoDefinitions); ProtoDefinitions[id] = protoDefinitions; } /// /// Converts the current instance's properties to an array of command-line arguments for starting the WireMock.Net server. /// /// An array of strings representing the command-line arguments. public string[] GetArgs() { var args = new Dictionary(); Add(args, "--WireMockLogger", DefaultLogger); if (HasBasicAuthentication) { Add(args, "--AdminUserName", AdminUsername!); Add(args, "--AdminPassword", AdminPassword!); } if (ReadStaticMappings) { Add(args, "--ReadStaticMappings", "true"); } if (WatchStaticMappings) { Add(args, "--ReadStaticMappings", "true"); Add(args, "--WatchStaticMappings", "true"); Add(args, "--WatchStaticMappingsInSubdirectories", "true"); } if (OpenTelemetryEnabled) { // Enable activity tracing (creates System.Diagnostics.Activity objects) Add(args, "--ActivityTracingEnabled", "true"); // Enable OpenTelemetry exporter Add(args, "--OpenTelemetryEnabled", "true"); if (!string.IsNullOrEmpty(OpenTelemetryOtlpExporterEndpoint)) { Add(args, "--OpenTelemetryOtlpExporterEndpoint", OpenTelemetryOtlpExporterEndpoint); } } if (AdditionalUrls.Count > 0) { Add(args, "--Urls", $"http://*:{HttpContainerPort} {string.Join(' ', AdditionalUrls)}"); } return args .SelectMany(k => new[] { k.Key, k.Value }) .ToArray(); } private static void Add(IDictionary args, string argument, string value) { args[argument] = value; } private static void Add(IDictionary args, string argument, Func action) { args[argument] = action(); } }