diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5b095e..3ad45b0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,432 +1,212 @@ +# 1.7.5-preview-02 (03 April 2025) +- [#1265](https://github.com/WireMock-Net/WireMock.Net/pull/1265) - Fix construction of path in OpenApiParser [bug] contributed by [StefH](https://github.com/StefH) +- [#1269](https://github.com/WireMock-Net/WireMock.Net/pull/1269) - Server-Sent Events [feature] contributed by [StefH](https://github.com/StefH) +- [#1264](https://github.com/WireMock-Net/WireMock.Net/issues/1264) - OpenApiParser - Construction of path possibly incorrect [bug] + # 1.7.4 (27 February 2025) -- [#1256](https://github.com/WireMock-Net/WireMock.Net/pull/1256) - Add ToArray() to ConcurrentObservableCollection [bug] contributed by [StefH](https://github.com/StefH) - [#1254](https://github.com/WireMock-Net/WireMock.Net/issues/1254) - FindLogEntries exception 'Destination array was not long enough' [bug] # 1.7.3 (24 February 2025) -- [#1253](https://github.com/WireMock-Net/WireMock.Net/pull/1253) - Update QueryStringParser to support param with equal but no value [bug] contributed by [StefH](https://github.com/StefH) - [#1247](https://github.com/WireMock-Net/WireMock.Net/issues/1247) - API call isn't matched when using an empty query string parameter [bug] # 1.7.2 (12 February 2025) -- [#1246](https://github.com/WireMock-Net/WireMock.Net/pull/1246) - Add "AddUrl" to WireMockContainerBuilder to support grpc [feature] contributed by [StefH](https://github.com/StefH) -- [#1248](https://github.com/WireMock-Net/WireMock.Net/pull/1248) - Add exception message to logging when mapping fails due to an exception. contributed by [JvE-iO](https://github.com/JvE-iO) -- [#1250](https://github.com/WireMock-Net/WireMock.Net/pull/1250) - Add ProtoDefinition to WireMockContainer [feature] contributed by [StefH](https://github.com/StefH) - [#1239](https://github.com/WireMock-Net/WireMock.Net/issues/1239) - How to use WiremockContainerBuilder for grpc using http2 [feature] - [#1249](https://github.com/WireMock-Net/WireMock.Net/issues/1249) - Add protodefinition and refer it from mapping [feature] # 1.7.1 (26 January 2025) -- [#1236](https://github.com/WireMock-Net/WireMock.Net/pull/1236) - Fix ProtoBuf mapping.json [bug] contributed by [StefH](https://github.com/StefH) -- [#1245](https://github.com/WireMock-Net/WireMock.Net/pull/1245) - Use Handlebars.Net.Helpers to version 2.4.10 [feature] contributed by [StefH](https://github.com/StefH) - [#1233](https://github.com/WireMock-Net/WireMock.Net/issues/1233) - GRPC mappings are not created correctly when created through Admin API [bug] -# 1.7.0 (22 January 2025) -- [#1242](https://github.com/WireMock-Net/WireMock.Net/pull/1242) - Disable DynamicLinq to fix CVE [bug] contributed by [StefH](https://github.com/StefH) - # 1.6.12 (21 January 2025) -- [#1231](https://github.com/WireMock-Net/WireMock.Net/pull/1231) - Fix google protobuf WellKnownTypes: Empty, Duration and Timestamp [bug] contributed by [StefH](https://github.com/StefH) -- [#1235](https://github.com/WireMock-Net/WireMock.Net/pull/1235) - Fix ArgumentException in FindLogEntries [bug] contributed by [StefH](https://github.com/StefH) -- [#1241](https://github.com/WireMock-Net/WireMock.Net/pull/1241) - Upgrade to Handlebars.Net.Helpers 2.4.9 [bug] contributed by [StefH](https://github.com/StefH) - [#1227](https://github.com/WireMock-Net/WireMock.Net/issues/1227) - unable to call grpc method with namespace [bug] - [#1228](https://github.com/WireMock-Net/WireMock.Net/issues/1228) - how to set datetime for grpc field [bug] - [#1234](https://github.com/WireMock-Net/WireMock.Net/issues/1234) - FindLogEntries regression [bug] - [#1240](https://github.com/WireMock-Net/WireMock.Net/issues/1240) - Method 'get_Category' in type 'WireMock.Transformers.Handlebars.FileHelpers' [bug] # 1.6.11 (02 January 2025) -- [#1221](https://github.com/WireMock-Net/WireMock.Net/pull/1221) - Add overloads to AtUrl and AtAbsoluteUrl which can use a IStringMatcher [feature] contributed by [StefH](https://github.com/StefH) -- [#1222](https://github.com/WireMock-Net/WireMock.Net/pull/1222) - Fix WireMockContainerBuilder (duplicate entries) [bug] contributed by [StefH](https://github.com/StefH) -- [#1223](https://github.com/WireMock-Net/WireMock.Net/pull/1223) - Add functionality to call a PostTransform method after the Webhook request has been transformed [feature] contributed by [StefH](https://github.com/StefH) -- [#1224](https://github.com/WireMock-Net/WireMock.Net/pull/1224) - Add FindLogEntries to IWireMockServer [feature] contributed by [StefH](https://github.com/StefH) - [#1092](https://github.com/WireMock-Net/WireMock.Net/issues/1092) - FindLogEntries present in WireMockServer but not IWireMockServer [feature] - [#1192](https://github.com/WireMock-Net/WireMock.Net/issues/1192) - Feature: add URL assertion excluding query parameters [feature] - [#1204](https://github.com/WireMock-Net/WireMock.Net/issues/1204) - Supplying Values From Request for Webhook Body and Headers [feature] - [#1217](https://github.com/WireMock-Net/WireMock.Net/issues/1217) - Order of WireMockContainerBuilder WithX calls caused duplicate Networks in Configuration [bug] # 1.6.10 (15 December 2024) -- [#1189](https://github.com/WireMock-Net/WireMock.Net/pull/1189) - WireMock.Net.Testcontainers: implement watching the static mapping folder for changes [bug] contributed by [StefH](https://github.com/StefH) - [#1188](https://github.com/WireMock-Net/WireMock.Net/issues/1188) - WithWatchStaticMappings doesn't respect new files [bug] # 1.6.9 (06 December 2024) -- [#1216](https://github.com/WireMock-Net/WireMock.Net/pull/1216) - Fix JsonPartialMatcher when using property names with dot [bug] contributed by [StefH](https://github.com/StefH) - [#1210](https://github.com/WireMock-Net/WireMock.Net/issues/1210) - JsonPartialMatcher fails to match on property name that JsonMatcher matches [bug] # 1.6.8 (24 November 2024) -- [#1202](https://github.com/WireMock-Net/WireMock.Net/pull/1202) - Log exception when (static) mapping file cannot be read [feature] contributed by [StefH](https://github.com/StefH) -- [#1206](https://github.com/WireMock-Net/WireMock.Net/pull/1206) - Fix security issues [bug] contributed by [StefH](https://github.com/StefH) -- [#1211](https://github.com/WireMock-Net/WireMock.Net/pull/1211) - Use GraphQL 8.2.1 [feature] contributed by [StefH](https://github.com/StefH) -- [#1213](https://github.com/WireMock-Net/WireMock.Net/pull/1213) - Fix HandlebarsContext ParseAndEvaluate method [bug] contributed by [StefH](https://github.com/StefH) - [#1201](https://github.com/WireMock-Net/WireMock.Net/issues/1201) - Mapping file parse errors are not logged to the console [feature] - [#1209](https://github.com/WireMock-Net/WireMock.Net/issues/1209) - Upgrade of GraphQL libs to the latest [feature] - [#1212](https://github.com/WireMock-Net/WireMock.Net/issues/1212) - Response Body Does Not Include Text After Path Segment [bug] # 1.6.7 (17 October 2024) -- [#1161](https://github.com/WireMock-Net/WireMock.Net/pull/1161) - Use latest ProtoBufJsonConverter to support WellKnownTypes [bug] contributed by [StefH](https://github.com/StefH) -- [#1190](https://github.com/WireMock-Net/WireMock.Net/pull/1190) - Bump System.Text.Json from 8.0.4 to 8.0.5 in /examples/WireMock.Net.Console.Net472.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#1194](https://github.com/WireMock-Net/WireMock.Net/pull/1194) - Upgrade System.Text.RegularExpressions to 4.3.1 to solve CVE-2019-0820 [bug] contributed by [StefH](https://github.com/StefH) -- [#1197](https://github.com/WireMock-Net/WireMock.Net/pull/1197) - Bump System.Text.Json from 8.0.4 to 8.0.5 in /src/dotnet-WireMock.Net [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#1198](https://github.com/WireMock-Net/WireMock.Net/pull/1198) - Fix Google.Protobuf.WellKnownTypes.Value [bug] contributed by [StefH](https://github.com/StefH) - [#1144](https://github.com/WireMock-Net/WireMock.Net/issues/1144) - Using google.protobuf.Empty as response results in a bad gRPC response [bug] - [#1153](https://github.com/WireMock-Net/WireMock.Net/issues/1153) - Grpc support for multiple proto files [feature] - [#1193](https://github.com/WireMock-Net/WireMock.Net/issues/1193) - Snyk issue : Regular Expression Denial of Service [bug] # 1.6.6 (01 October 2024) -- [#1185](https://github.com/WireMock-Net/WireMock.Net/pull/1185) - Throw exception in case WithTransformer is used after WithBodyFromFile [bug] contributed by [StefH](https://github.com/StefH) -- [#1187](https://github.com/WireMock-Net/WireMock.Net/pull/1187) - Fix StaticMappingsPath in WireMockContainerBuilder [bug] contributed by [StefH](https://github.com/StefH) - [#1184](https://github.com/WireMock-Net/WireMock.Net/issues/1184) - .WithBodyFromFile() + .WithTransformer(transformContentFromBodyAsFile: true) = empty string [bug] - [#1186](https://github.com/WireMock-Net/WireMock.Net/issues/1186) - WithMappings path is null on Build() call [bug] -# 1.6.5 (28 September 2024) -- [#1175](https://github.com/WireMock-Net/WireMock.Net/pull/1175) - Add WireMock.Net.AspNetCore.Middleware [feature] contributed by [StefH](https://github.com/StefH) -- [#1181](https://github.com/WireMock-Net/WireMock.Net/pull/1181) - WireMock.Net.Testcontainers: Use 'sheyenrath/wiremock.net-alpine' image as default for Linux [feature] contributed by [StefH](https://github.com/StefH) -- [#1182](https://github.com/WireMock-Net/WireMock.Net/pull/1182) - pass in the request when no matching is found to the warn logger [feature] contributed by [JasonLandbridge](https://github.com/JasonLandbridge) - # 1.6.4 (25 September 2024) -- [#1169](https://github.com/WireMock-Net/WireMock.Net/pull/1169) - Allow mapping without Path or Url [bug] contributed by [StefH](https://github.com/StefH) -- [#1170](https://github.com/WireMock-Net/WireMock.Net/pull/1170) - Update the .NET Aspire tests [feature] contributed by [StefH](https://github.com/StefH) -- [#1172](https://github.com/WireMock-Net/WireMock.Net/pull/1172) - Fix JSON parsing of text/plain content type [bug] contributed by [ruxo](https://github.com/ruxo) -- [#1177](https://github.com/WireMock-Net/WireMock.Net/pull/1177) - Unpin Testcontainers version and upgrade to version 3.10.0 [bug] contributed by [StefH](https://github.com/StefH) -- [#1178](https://github.com/WireMock-Net/WireMock.Net/pull/1178) - Upgrade CS-Script to version 4.8.17 [feature] contributed by [StefH](https://github.com/StefH) -- [#1179](https://github.com/WireMock-Net/WireMock.Net/pull/1179) - Add WireMock.Net.TUnit project [feature] contributed by [StefH](https://github.com/StefH) - [#1146](https://github.com/WireMock-Net/WireMock.Net/issues/1146) - Bump Request CS-Script 4.8.13 to 4.8.17 [feature] - [#1167](https://github.com/WireMock-Net/WireMock.Net/issues/1167) - Admin API fails to create a mapping with Request Header matching using WildCardMatcher [bug] - [#1168](https://github.com/WireMock-Net/WireMock.Net/issues/1168) - Numbers in text/plain content is parsed as JSON. [bug] - [#1176](https://github.com/WireMock-Net/WireMock.Net/issues/1176) - WireMock.NET TestContainer Dependency Constraint Issue [bug] # 1.6.3 (07 September 2024) -- [#1165](https://github.com/WireMock-Net/WireMock.Net/pull/1165) - Fix listen on AnyIP for url 0.0.0.0 contributed by [cocoon](https://github.com/cocoon) - [#1154](https://github.com/WireMock-Net/WireMock.Net/issues/1154) - Listen on all ips [bug] # 1.6.2 (04 September 2024) -- [#1152](https://github.com/WireMock-Net/WireMock.Net/pull/1152) - Update MappingConverter to correctly write the Matcher as C# code [bug] contributed by [StefH](https://github.com/StefH) -- [#1163](https://github.com/WireMock-Net/WireMock.Net/pull/1163) - Upgrade Aspire to version 8.2.0 [feature] contributed by [StefH](https://github.com/StefH) -- [#1166](https://github.com/WireMock-Net/WireMock.Net/pull/1166) - Also update IWireMockMiddlewareOptions when settings are updated via admin interface [bug] contributed by [StefH](https://github.com/StefH) - [#1151](https://github.com/WireMock-Net/WireMock.Net/issues/1151) - MappingsToCSharpCode should use RegexMatcher when specified [bug] - [#1164](https://github.com/WireMock-Net/WireMock.Net/issues/1164) - WithParam not working. [bug] # 1.6.1 (22 August 2024) -- [#1160](https://github.com/WireMock-Net/WireMock.Net/pull/1160) - Use default timeout for Regex [bug] contributed by [StefH](https://github.com/StefH) - [#1159](https://github.com/WireMock-Net/WireMock.Net/issues/1159) - RegexMatchTimeoutException when trying to parse HTTP version [bug] # 1.6.0 (16 August 2024) -- [#1042](https://github.com/WireMock-Net/WireMock.Net/pull/1042) - Update + add fluent builder methods [feature] contributed by [StefH](https://github.com/StefH) -- [#1109](https://github.com/WireMock-Net/WireMock.Net/pull/1109) - Add Aspire Extension [feature] contributed by [StefH](https://github.com/StefH) -- [#1148](https://github.com/WireMock-Net/WireMock.Net/pull/1148) - Use Guid.TryParseExact with format "D" contributed by [StefH](https://github.com/StefH) -- [#1157](https://github.com/WireMock-Net/WireMock.Net/pull/1157) - Fix FormUrlEncodedMatcher (MatchOperator.And) [bug] contributed by [StefH](https://github.com/StefH) -- [#1158](https://github.com/WireMock-Net/WireMock.Net/pull/1158) - Allow setting Content-Length header on the response [feature] contributed by [StefH](https://github.com/StefH) - [#720](https://github.com/WireMock-Net/WireMock.Net/issues/720) - Response Header Content-Length not available when call HEAD Method [feature] - [#1145](https://github.com/WireMock-Net/WireMock.Net/issues/1145) - Response is auto converting string to guid [bug] - [#1156](https://github.com/WireMock-Net/WireMock.Net/issues/1156) - FormUrlEncodedMatcher is not requiring to match all properties when MatchOperator.And [bug] # 1.5.62 (27 July 2024) -- [#1147](https://github.com/WireMock-Net/WireMock.Net/pull/1147) - Add FormUrlEncodedMatcher [feature] contributed by [StefH](https://github.com/StefH) - [#1143](https://github.com/WireMock-Net/WireMock.Net/issues/1143) - FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug] # 1.5.61 (22 July 2024) -- [#1122](https://github.com/WireMock-Net/WireMock.Net/pull/1122) - Fix OpenApiPathsMapper [bug] contributed by [StefH](https://github.com/StefH) -- [#1135](https://github.com/WireMock-Net/WireMock.Net/pull/1135) - Bump System.Text.Json from 4.7.2 to 8.0.4 in /examples/WireMock.Net.Console.Net472.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#1136](https://github.com/WireMock-Net/WireMock.Net/pull/1136) - Bump System.Text.Json from 8.0.0 to 8.0.4 in /src/dotnet-WireMock.Net [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#1137](https://github.com/WireMock-Net/WireMock.Net/pull/1137) - Add link to TIOBE Index on main page + fix issues [refactor] contributed by [StefH](https://github.com/StefH) -- [#1138](https://github.com/WireMock-Net/WireMock.Net/pull/1138) - Fix some SonarCloud warnings [refactor] contributed by [StefH](https://github.com/StefH) -- [#1141](https://github.com/WireMock-Net/WireMock.Net/pull/1141) - Update WireMockContainerBuilder.WithMappings for "includeSubDirectories" [feature] contributed by [StefH](https://github.com/StefH) -- [#1142](https://github.com/WireMock-Net/WireMock.Net/pull/1142) - Make property FromConfiguredStub nullable (for WireMock.Org) [bug] contributed by [StefH](https://github.com/StefH) - [#1118](https://github.com/WireMock-Net/WireMock.Net/issues/1118) - Generating mappings from Payroc open-api file gives ArgumentException: Property with the same name already exists on object [bug] - [#1139](https://github.com/WireMock-Net/WireMock.Net/issues/1139) - Allow WithMappings to support scanning SubDirectories when building a WireMockContainer [feature] - [#1140](https://github.com/WireMock-Net/WireMock.Net/issues/1140) - WireMock.Org nullable properties and defaults [bug] # 1.5.60 (09 July 2024) -- [#1128](https://github.com/WireMock-Net/WireMock.Net/pull/1128) - Add Handlebars.Net.Helpers.Xslt [feature] contributed by [StefH](https://github.com/StefH) -- [#1130](https://github.com/WireMock-Net/WireMock.Net/pull/1130) - Add AdminPath to WireMockServerSettings [feature] contributed by [StefH](https://github.com/StefH) -- [#1132](https://github.com/WireMock-Net/WireMock.Net/pull/1132) - Multipart Matcher Fix [bug] contributed by [rmeshksar](https://github.com/rmeshksar) -- [#1134](https://github.com/WireMock-Net/WireMock.Net/pull/1134) - Remove some files and folders [refactor] contributed by [StefH](https://github.com/StefH) - [#1119](https://github.com/WireMock-Net/WireMock.Net/issues/1119) - Error in RequestMessageMultiPartMatcher [bug] - [#1121](https://github.com/WireMock-Net/WireMock.Net/issues/1121) - XML transformation [feature] -# 1.5.59 (26 June 2024) -- [#1127](https://github.com/WireMock-Net/WireMock.Net/pull/1127) - Made changes to accommodate breaking change in testcontainers-dotnet 3.9 [feature] contributed by [epDugas](https://github.com/epDugas) - # 1.5.58 (08 June 2024) -- [#1116](https://github.com/WireMock-Net/WireMock.Net/pull/1116) - Add some methods to the BodyModelBuilder [feature] contributed by [StefH](https://github.com/StefH) - [#1117](https://github.com/WireMock-Net/WireMock.Net/issues/1117) - AbstractJsonPartialMatcher: Regex Value is Uppercased when IgnoreCase is set to true [bug] -# 1.5.57 (04 June 2024) -- [#1113](https://github.com/WireMock-Net/WireMock.Net/pull/1113) - Add some Extension methods to IWireMockAdminApi [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.56 (03 June 2024) -- [#1111](https://github.com/WireMock-Net/WireMock.Net/pull/1111) - Fix Request.Create().WithBodyAsJson(...) [bug] contributed by [StefH](https://github.com/StefH) -- [#1112](https://github.com/WireMock-Net/WireMock.Net/pull/1112) - Add "/__admin/health" endpoint [feature] contributed by [StefH](https://github.com/StefH) - [#1110](https://github.com/WireMock-Net/WireMock.Net/issues/1110) - Connection prematurely closed BEFORE response [bug] -# 1.5.55 (22 May 2024) -- [#1107](https://github.com/WireMock-Net/WireMock.Net/pull/1107) - When only Port is provided, bind to * (Fixes #1100) [bug] contributed by [StefH](https://github.com/StefH) - -# 1.5.54 (18 May 2024) -- [#1100](https://github.com/WireMock-Net/WireMock.Net/pull/1100) - Add support to bind to ip-address instead of only localhost [feature] contributed by [StefH](https://github.com/StefH) -- [#1104](https://github.com/WireMock-Net/WireMock.Net/pull/1104) - Use try..catch to set encoding in WireMockConsoleLogger [feature] contributed by [asherber](https://github.com/asherber) - # 1.5.53 (08 May 2024) -- [#1093](https://github.com/WireMock-Net/WireMock.Net/pull/1093) - Update Handlebars.Net [feature] contributed by [StefH](https://github.com/StefH) -- [#1101](https://github.com/WireMock-Net/WireMock.Net/pull/1101) - Fix MappingConverter to support Body with JsonMatcher [bug] contributed by [StefH](https://github.com/StefH) - [#1095](https://github.com/WireMock-Net/WireMock.Net/issues/1095) - When using C# code generation WithBody() matcher is not generated for POST Request [bug] # 1.5.52 (06 April 2024) -- [#1091](https://github.com/WireMock-Net/WireMock.Net/pull/1091) - Add RegEx support to JsonMatcher [feature] contributed by [StefH](https://github.com/StefH) - [#1088](https://github.com/WireMock-Net/WireMock.Net/issues/1088) - Regex support for JsonMatcher [feature] # 1.5.51 (20 March 2024) -- [#1085](https://github.com/WireMock-Net/WireMock.Net/pull/1085) - Fix FluentAssertions (actual body is not displayed in error message) [bug] contributed by [StefH](https://github.com/StefH) - [#1084](https://github.com/WireMock-Net/WireMock.Net/issues/1084) - FluentAssertions - Actual body is not displayed in error message when using Json Body [bug] # 1.5.50 (12 March 2024) -- [#1080](https://github.com/WireMock-Net/WireMock.Net/pull/1080) - Fix FluentAssertions on Header(s) [bug] contributed by [StefH](https://github.com/StefH) -- [#1082](https://github.com/WireMock-Net/WireMock.Net/pull/1082) - Make WireMockAssertions extendable [feature] contributed by [StefH](https://github.com/StefH) - [#1074](https://github.com/WireMock-Net/WireMock.Net/issues/1074) - FluentAssertions extensions do not filter headers correctly [bug] - [#1075](https://github.com/WireMock-Net/WireMock.Net/issues/1075) - FluentAssertions extensions are not open for extension [feature] # 1.5.49 (06 March 2024) -- [#1069](https://github.com/WireMock-Net/WireMock.Net/pull/1069) - Extend TypeLoader [feature] contributed by [StefH](https://github.com/StefH) -- [#1078](https://github.com/WireMock-Net/WireMock.Net/pull/1078) - Upgrade ProtoBufJsonConverter to fix issue with dot(s) in package name [bug] contributed by [StefH](https://github.com/StefH) - [#1077](https://github.com/WireMock-Net/WireMock.Net/issues/1077) - ProtoBufMatcher not working when proto package name contains dots [bug] -# 1.5.48 (22 February 2024) -- [#1047](https://github.com/WireMock-Net/WireMock.Net/pull/1047) - Add Grpc ProtoBuf support (request-response) [feature] contributed by [StefH](https://github.com/StefH) -- [#1058](https://github.com/WireMock-Net/WireMock.Net/pull/1058) - Fix some SonarCloud issues [refactor] contributed by [StefH](https://github.com/StefH) - # 1.5.47 (25 January 2024) -- [#1049](https://github.com/WireMock-Net/WireMock.Net/pull/1049) - Add WithoutHeader to WireMock.FluentAssertions [feature] contributed by [StefH](https://github.com/StefH) -- [#1053](https://github.com/WireMock-Net/WireMock.Net/pull/1053) - [Snyk] Security upgrade Microsoft.IdentityModel.Protocols.OpenIdConnect from 6.12.2 to 6.34.0 [security] contributed by [StefH](https://github.com/StefH) -- [#1057](https://github.com/WireMock-Net/WireMock.Net/pull/1057) - Pin the version from Testcontainers to 3.7.0 in WireMock.Net.Testcontainers [bug] contributed by [StefH](https://github.com/StefH) - [#1048](https://github.com/WireMock-Net/WireMock.Net/issues/1048) - WithoutHeader fluent assertion [feature] - [#1054](https://github.com/WireMock-Net/WireMock.Net/issues/1054) - WireMock.Net 1.5.46 is incompatible with TestContainers 3.7.0 (issue 1) [bug] - [#1059](https://github.com/WireMock-Net/WireMock.Net/issues/1059) - WireMock.Net 1.5.46 is incompatible with TestContainers 3.7.0 (issue 2) [bug] -# 1.5.46 (23 December 2023) -- [#1044](https://github.com/WireMock-Net/WireMock.Net/pull/1044) - WireMockServerSettingsParser [refactor] contributed by [StefH](https://github.com/StefH) -- [#1046](https://github.com/WireMock-Net/WireMock.Net/pull/1046) - Change FindRequestByMappingGuidAsync to return a collection of entries contributed by [tlevesque-ueat](https://github.com/tlevesque-ueat) - # 1.5.45 (21 December 2023) -- [#1036](https://github.com/WireMock-Net/WireMock.Net/pull/1036) - Update Handlebars Transformer logic (ReplaceNodeOptions) [feature] contributed by [StefH](https://github.com/StefH) -- [#1043](https://github.com/WireMock-Net/WireMock.Net/pull/1043) - FindRequestByMappingGuidAsync [feature] contributed by [StefH](https://github.com/StefH) - [#1039](https://github.com/WireMock-Net/WireMock.Net/issues/1039) - [Admin API] Find a request that matched a given mapping [feature] # 1.5.44 (14 December 2023) -- [#1040](https://github.com/WireMock-Net/WireMock.Net/pull/1040) - Implement prefix for saved mapping file [feature] contributed by [MindaugasLaganeckas](https://github.com/MindaugasLaganeckas) - [#1033](https://github.com/WireMock-Net/WireMock.Net/issues/1033) - How to get a Random Long? [bug] - [#1037](https://github.com/WireMock-Net/WireMock.Net/issues/1037) - Make mapping filenames more user friendly [feature] # 1.5.43 (11 December 2023) -- [#1026](https://github.com/WireMock-Net/WireMock.Net/pull/1026) - Add ProxyUrlReplaceSettings to Response [feature] contributed by [StefH](https://github.com/StefH) -- [#1038](https://github.com/WireMock-Net/WireMock.Net/pull/1038) - Proxy all requests - even a repeated one [feature] contributed by [sameena-ops](https://github.com/sameena-ops) - [#592](https://github.com/WireMock-Net/WireMock.Net/issues/592) - Proxy all requests - even a repeated one [feature] - [#1024](https://github.com/WireMock-Net/WireMock.Net/issues/1024) - Scenario with proxy not removing route prefix [feature] # 1.5.42 (09 December 2023) -- [#1023](https://github.com/WireMock-Net/WireMock.Net/pull/1023) - Fix Mapping[] for WireMock.Org REST API [bug] contributed by [StefH](https://github.com/StefH) -- [#1031](https://github.com/WireMock-Net/WireMock.Net/pull/1031) - Calling Reset also resets the scenarios [bug] contributed by [StefH](https://github.com/StefH) -- [#1034](https://github.com/WireMock-Net/WireMock.Net/pull/1034) - Workaround for: Random.Generate Type="Long" [bug] contributed by [StefH](https://github.com/StefH) - [#1021](https://github.com/WireMock-Net/WireMock.Net/issues/1021) - GetAdminMappingsResult in WireMock.Org.Abstractions should contain list of mappings [bug] - [#1030](https://github.com/WireMock-Net/WireMock.Net/issues/1030) - Reset resets only mappings and logentries, not scenarios. [bug] # 1.5.41 (04 December 2023) -- [#1012](https://github.com/WireMock-Net/WireMock.Net/pull/1012) - GraphQL - custom scalar support [feature] contributed by [StefH](https://github.com/StefH) -- [#1018](https://github.com/WireMock-Net/WireMock.Net/pull/1018) - Add .NET 8 [feature] contributed by [StefH](https://github.com/StefH) - [#984](https://github.com/WireMock-Net/WireMock.Net/issues/984) - GraphQL Schema validation with custom scalars [feature] -# 1.5.40 (07 November 2023) -- [#1011](https://github.com/WireMock-Net/WireMock.Net/pull/1011) - GraphQL - add support for standard scalar types in the schema [feature] contributed by [StefH](https://github.com/StefH) -- [#1014](https://github.com/WireMock-Net/WireMock.Net/pull/1014) - FluentAssertions - WithBody and WithBodyAsJson and WithBodyAsBytes contributed by [StefH](https://github.com/StefH) - # 1.5.39 (09 October 2023) -- [#1006](https://github.com/WireMock-Net/WireMock.Net/pull/1006) - Fix RequestMessageParamMatcher : RejectOnMatch [bug] contributed by [StefH](https://github.com/StefH) - [#997](https://github.com/WireMock-Net/WireMock.Net/issues/997) - JmesPathMatcher or and MatchOperator working in version 1.5.34 but not 1.5.35 [bug] -# 1.5.38 (02 October 2023) -- [#1005](https://github.com/WireMock-Net/WireMock.Net/pull/1005) - Support for xml namespaces in XPathMatcher [feature] contributed by [cal-schleupen](https://github.com/cal-schleupen) - # 1.5.37 (27 September 2023) -- [#1004](https://github.com/WireMock-Net/WireMock.Net/pull/1004) - Fix MappingModel to map IgnoreCase and RejectOnMatch for Headers, Cookies and Parameters [bug] contributed by [StefH](https://github.com/StefH) - [#1003](https://github.com/WireMock-Net/WireMock.Net/issues/1003) - Store Mapping per POST request ignores "IgnoreCase" property of HeaderModel [bug] # 1.5.36 (21 September 2023) -- [#986](https://github.com/WireMock-Net/WireMock.Net/pull/986) - Write logging in case a Matcher throws an exception [feature] contributed by [StefH](https://github.com/StefH) -- [#996](https://github.com/WireMock-Net/WireMock.Net/pull/996) - Remove dependency on Microsoft.AspNet.WebApi.Client [feature] contributed by [StefH](https://github.com/StefH) -- [#1002](https://github.com/WireMock-Net/WireMock.Net/pull/1002) - Fixed logic for SaveUnmatchedRequests [bug] contributed by [StefH](https://github.com/StefH) - [#974](https://github.com/WireMock-Net/WireMock.Net/issues/974) - HttpClient extension methods causes ambiguous invocations in .NET 7 [bug] - [#1001](https://github.com/WireMock-Net/WireMock.Net/issues/1001) - SaveUnmatchedRequests stopped working [bug] -# 1.5.35 (19 August 2023) -- [#993](https://github.com/WireMock-Net/WireMock.Net/pull/993) - Update JSONPathMatcher.cs to cover the string path selection to a child contributed by [DayLightDancer](https://github.com/DayLightDancer) - # 1.5.34 (04 August 2023) -- [#989](https://github.com/WireMock-Net/WireMock.Net/pull/989) - Fix MimeKitLite NuGet include [bug] contributed by [StefH](https://github.com/StefH) - [#988](https://github.com/WireMock-Net/WireMock.Net/issues/988) - v1.5.33 Returns always StatusCode 500 [bug] # 1.5.33 (03 August 2023) -- [#972](https://github.com/WireMock-Net/WireMock.Net/pull/972) - JsonPartialMatcher - match guid and string contributed by [timurnes](https://github.com/timurnes) -- [#976](https://github.com/WireMock-Net/WireMock.Net/pull/976) - Upgrade to Handlebars.Net.Helpers 2.4.0 to update XPath.SelectTokens and XPath.EvaluateToString [feature] contributed by [StefH](https://github.com/StefH) -- [#981](https://github.com/WireMock-Net/WireMock.Net/pull/981) - Add MultiPart/MimePart Request Matcher [feature] contributed by [StefH](https://github.com/StefH) - [#968](https://github.com/WireMock-Net/WireMock.Net/issues/968) - Using request multipart in response template [feature] - [#969](https://github.com/WireMock-Net/WireMock.Net/issues/969) - Multipart validation [feature] - [#970](https://github.com/WireMock-Net/WireMock.Net/issues/970) - Loop through xml elements in handlebars template [feature] - [#971](https://github.com/WireMock-Net/WireMock.Net/issues/971) - JsonPartialMatcher - match guid and string [feature] # 1.5.32 (15 July 2023) -- [#966](https://github.com/WireMock-Net/WireMock.Net/pull/966) - Fixed JsonPathMatcher to match nested objects [bug] contributed by [StefH](https://github.com/StefH) - [#965](https://github.com/WireMock-Net/WireMock.Net/issues/965) - JsonPathMatcher does not match json body nested objects [bug] - [#967](https://github.com/WireMock-Net/WireMock.Net/issues/967) - ⭐10 million downloads ! ⭐ [feature] -# 1.5.31 (08 July 2023) -- [#964](https://github.com/WireMock-Net/WireMock.Net/pull/964) - Add GraphQL Schema matching [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.30 (28 June 2023) -- [#959](https://github.com/WireMock-Net/WireMock.Net/pull/959) - Fixed logic for FluentAssertions WithHeader [bug] contributed by [StefH](https://github.com/StefH) -- [#962](https://github.com/WireMock-Net/WireMock.Net/pull/962) - Bump System.Linq.Dynamic.Core from 1.2.23 to 1.3.0 in /examples/WireMock.Net.Console.Net472.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#963](https://github.com/WireMock-Net/WireMock.Net/pull/963) - Bump System.Linq.Dynamic.Core from 1.2.23 to 1.3.0 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) - [#958](https://github.com/WireMock-Net/WireMock.Net/issues/958) - [FluentAssertions] Should().HaveReceivedACall().WithHeader() only checks the first header with the matching key. [bug] # 1.5.29 (22 June 2023) -- [#954](https://github.com/WireMock-Net/WireMock.Net/pull/954) - Support setting WireMockServerSettings via Environment [feature] contributed by [StefH](https://github.com/StefH) -- [#955](https://github.com/WireMock-Net/WireMock.Net/pull/955) - Fix some SonarCloud issues [refactor] contributed by [StefH](https://github.com/StefH) - [#953](https://github.com/WireMock-Net/WireMock.Net/issues/953) - How to use environment variable [feature] -# 1.5.28 (11 June 2023) -- [#948](https://github.com/WireMock-Net/WireMock.Net/pull/948) - WireMock.Net.Testcontainers [feature] contributed by [StefH](https://github.com/StefH) -- [#951](https://github.com/WireMock-Net/WireMock.Net/pull/951) - Allow setting the Content-Length header for a HTTP method HEAD [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.27 (03 June 2023) -- [#946](https://github.com/WireMock-Net/WireMock.Net/pull/946) - Add warning logging when sending a request to a Webhook does not return status 200 [feature] contributed by [StefH](https://github.com/StefH) -- [#949](https://github.com/WireMock-Net/WireMock.Net/pull/949) - Add ".NET Framework 4.7" to WireMock.Net.FluentAssertions [feature] contributed by [StefH](https://github.com/StefH) - [#928](https://github.com/WireMock-Net/WireMock.Net/issues/928) - TypeLoadException when using WithHeader method. [bug] - [#945](https://github.com/WireMock-Net/WireMock.Net/issues/945) - Webhook logging [feature] # 1.5.26 (25 May 2023) -- [#939](https://github.com/WireMock-Net/WireMock.Net/pull/939) - WireMockMiddleware should use HandleRequestsSynchronously correctly [bug] contributed by [StefH](https://github.com/StefH) -- [#940](https://github.com/WireMock-Net/WireMock.Net/pull/940) - Code generator improvements contributed by [cezarypiatek](https://github.com/cezarypiatek) -- [#942](https://github.com/WireMock-Net/WireMock.Net/pull/942) - Add GetParameter method to IRequestMessage [feature] contributed by [StefH](https://github.com/StefH) - [#941](https://github.com/WireMock-Net/WireMock.Net/issues/941) - RequestMessage.GetParameter method missing from IRequestMessage interface [feature] -# 1.5.25 (13 May 2023) -- [#934](https://github.com/WireMock-Net/WireMock.Net/pull/934) - Code generator improvements [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek) - -# 1.5.24 (07 May 2023) -- [#926](https://github.com/WireMock-Net/WireMock.Net/pull/926) - Fix C# mapping code generator for header names [bug] contributed by [cezarypiatek](https://github.com/cezarypiatek) -- [#927](https://github.com/WireMock-Net/WireMock.Net/pull/927) - Enrich generated code with status code [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek) -- [#930](https://github.com/WireMock-Net/WireMock.Net/pull/930) - Update C# mapping code generator for WithStatusCode [feature] contributed by [StefH](https://github.com/StefH) -- [#931](https://github.com/WireMock-Net/WireMock.Net/pull/931) - Add property 'IsStartedWithAdminInterface' to 'IWireMockServer' [feature] contributed by [StefH](https://github.com/StefH) -- [#933](https://github.com/WireMock-Net/WireMock.Net/pull/933) - C# code generator improvements [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek) - -# 1.5.23 (23 April 2023) -- [#922](https://github.com/WireMock-Net/WireMock.Net/pull/922) - Add WithProbability [feature] contributed by [StefH](https://github.com/StefH) -- [#924](https://github.com/WireMock-Net/WireMock.Net/pull/924) - Allow removal of prefix when proxying to another server (#630) [feature] contributed by [nudejustin](https://github.com/nudejustin) -- [#925](https://github.com/WireMock-Net/WireMock.Net/pull/925) - Add IgnoreCase option to ProxyUrlReplaceSettings [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.22 (08 April 2023) -- [#914](https://github.com/WireMock-Net/WireMock.Net/pull/914) - #912 add excluded params to proxy mapping [feature] contributed by [walidhaidarii](https://github.com/walidhaidarii) -- [#916](https://github.com/WireMock-Net/WireMock.Net/pull/916) - Include WireMockOpenApiParser project [feature] contributed by [StefH](https://github.com/StefH) - [#912](https://github.com/WireMock-Net/WireMock.Net/issues/912) - Feature: adding excluded params to proxy and records settings [feature] # 1.5.21 (22 March 2023) -- [#908](https://github.com/WireMock-Net/WireMock.Net/pull/908) - RequestBuilder : add WithBodyAsJson and WithBody (with IJsonConverter) [feature] contributed by [StefH](https://github.com/StefH) -- [#911](https://github.com/WireMock-Net/WireMock.Net/pull/911) - Fixed QueryStringParser for UrlEncoded values [bug] contributed by [StefH](https://github.com/StefH) - [#901](https://github.com/WireMock-Net/WireMock.Net/issues/901) - Matching one form-urlencoded value [feature] # 1.5.20 (19 March 2023) -- [#905](https://github.com/WireMock-Net/WireMock.Net/pull/905) - Add DeserializeFormUrl Encoded to the settings [feature] contributed by [StefH](https://github.com/StefH) -- [#907](https://github.com/WireMock-Net/WireMock.Net/pull/907) - Fix issue with application/x-www-form-urlencoded and ExactMatcher [bug] contributed by [StefH](https://github.com/StefH) - [#906](https://github.com/WireMock-Net/WireMock.Net/issues/906) - Upgrade to 1.5.19 breaks a form data test [bug] -# 1.5.19 (17 March 2023) -- [#903](https://github.com/WireMock-Net/WireMock.Net/pull/903) - Add WithBody with IDictionary (form-urlencoded values) [feature] contributed by [StefH](https://github.com/StefH) -- [#904](https://github.com/WireMock-Net/WireMock.Net/pull/904) - Update Handlebars.Net.Helpers to 2.3.15 [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.18 (09 March 2023) -- [#893](https://github.com/WireMock-Net/WireMock.Net/pull/893) - Add 'Data' to response which can be used during transforming the response [feature] contributed by [StefH](https://github.com/StefH) -- [#896](https://github.com/WireMock-Net/WireMock.Net/pull/896) - Bump Microsoft.Owin from 2.0.2 to 4.2.2 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#900](https://github.com/WireMock-Net/WireMock.Net/pull/900) - ProxySettings : Add logic to not save some requests depending on HttpMethods [feature] contributed by [StefH](https://github.com/StefH) - [#897](https://github.com/WireMock-Net/WireMock.Net/issues/897) - WebHostBuilder.ConfigureServices method not found when using nunit3testadapter 4.4.0 [bug] - [#899](https://github.com/WireMock-Net/WireMock.Net/issues/899) - Ignore OPTIONS request when using proxyandrecord [feature] -# 1.5.17 (25 February 2023) -- [#881](https://github.com/WireMock-Net/WireMock.Net/pull/881) - Add WithBodyAsJson builder method with accepts a Func [feature] contributed by [StefH](https://github.com/StefH) -- [#890](https://github.com/WireMock-Net/WireMock.Net/pull/890) - AdminApiMappingBuilder [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.16 (01 February 2023) -- [#880](https://github.com/WireMock-Net/WireMock.Net/pull/880) - Add `WithProxy(string proxyUrl, X509Certificate2 certificate)` [feature] contributed by [StefH](https://github.com/StefH) - [#879](https://github.com/WireMock-Net/WireMock.Net/issues/879) - Possibility to pass a X509Certificate2 to WithProxy() or specifiy certificate loading options [feature] -# 1.5.15 (29 January 2023) -- [#878](https://github.com/WireMock-Net/WireMock.Net/pull/878) - Update REST Admin interface to support "Get Mapping(s) as C# Code" [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.14 (24 January 2023) -- [#842](https://github.com/WireMock-Net/WireMock.Net/pull/842) - Generate C# code from Mapping [feature] contributed by [StefH](https://github.com/StefH) -- [#869](https://github.com/WireMock-Net/WireMock.Net/pull/869) - Add MappingBuilder to build mappings in code and export to Models or JSON [feature] contributed by [StefH](https://github.com/StefH) -- [#871](https://github.com/WireMock-Net/WireMock.Net/pull/871) - Add UseWebhooksFireAndForget to Server ConvertMapping [bug] contributed by [ggradnig](https://github.com/ggradnig) -- [#872](https://github.com/WireMock-Net/WireMock.Net/pull/872) - Fix unsubscribe from LogEntriesChanged event handler [bug] contributed by [StefH](https://github.com/StefH) -- [#875](https://github.com/WireMock-Net/WireMock.Net/pull/875) - Fix Self referencing loop detected for property [bug] contributed by [eseneckiy](https://github.com/eseneckiy) - [#701](https://github.com/WireMock-Net/WireMock.Net/issues/701) - Allow to create MappingModel from c# to be able to configure local and remote mocks similarly [feature] - [#867](https://github.com/WireMock-Net/WireMock.Net/issues/867) - Can I build mappings with code and save them to JSON-file without starting server [feature] - [#870](https://github.com/WireMock-Net/WireMock.Net/issues/870) - Can not unsubscribe from LogEntriesChanged event. [bug] # 1.5.13 (11 December 2022) -- [#858](https://github.com/WireMock-Net/WireMock.Net/pull/858) - Update Transformer functionality to return value instead of string [feature] contributed by [StefH](https://github.com/StefH) -- [#859](https://github.com/WireMock-Net/WireMock.Net/pull/859) - Add UpdatedAt property to Mapping [feature] contributed by [StefH](https://github.com/StefH) -- [#862](https://github.com/WireMock-Net/WireMock.Net/pull/862) - Add client certificate support [feature] contributed by [billybraga](https://github.com/billybraga) -- [#863](https://github.com/WireMock-Net/WireMock.Net/pull/863) - Update WireMockServer.CreateClient/CreateClients to include handlers [feature] contributed by [StefH](https://github.com/StefH) - [#856](https://github.com/WireMock-Net/WireMock.Net/issues/856) - Inconsistent result with overlapping (duplicate) request [bug] -# 1.5.12 (03 December 2022) -- [#851](https://github.com/WireMock-Net/WireMock.Net/pull/851) - Fix Linux CI build + Fix opencover [feature] contributed by [StefH](https://github.com/StefH) -- [#853](https://github.com/WireMock-Net/WireMock.Net/pull/853) - Add .Net 7 [feature] contributed by [StefH](https://github.com/StefH) -- [#854](https://github.com/WireMock-Net/WireMock.Net/pull/854) - Fix logic for QueryParameterMultipleValueSupport [bug] contributed by [StefH](https://github.com/StefH) -- [#857](https://github.com/WireMock-Net/WireMock.Net/pull/857) - Update some dependencies [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.11 (24 November 2022) -- [#836](https://github.com/WireMock-Net/WireMock.Net/pull/836) - Add Settings.QueryParameterMultipleValueSupport [feature] contributed by [StefH](https://github.com/StefH) -- [#848](https://github.com/WireMock-Net/WireMock.Net/pull/848) - Use try-catch when adding or removing logEntry [bug] contributed by [StefH](https://github.com/StefH) - [#846](https://github.com/WireMock-Net/WireMock.Net/issues/846) - Exception ArgumentOutOfRangeException [bug] -# 1.5.10 (06 November 2022) -- [#843](https://github.com/WireMock-Net/WireMock.Net/pull/843) - Webhook Templating: Use the transformed URL to create the HttpRequestMessage contributed by [ggradnig](https://github.com/ggradnig) -- [#845](https://github.com/WireMock-Net/WireMock.Net/pull/845) - Add WireMockNullLogger as valid commandline logger option [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.9 (29 October 2022) -- [#828](https://github.com/WireMock-Net/WireMock.Net/pull/828) - Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature] contributed by [StefH](https://github.com/StefH) -- [#832](https://github.com/WireMock-Net/WireMock.Net/pull/832) - Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature] contributed by [StefH](https://github.com/StefH) -- [#834](https://github.com/WireMock-Net/WireMock.Net/pull/834) - Support deleting / resetting a single scenario [feature] contributed by [StefH](https://github.com/StefH) -- [#837](https://github.com/WireMock-Net/WireMock.Net/pull/837) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.7 to 2.1.25 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#838](https://github.com/WireMock-Net/WireMock.Net/pull/838) - Add option to ProxySettings to append guid to mapping file contributed by [StefH](https://github.com/StefH) - [#826](https://github.com/WireMock-Net/WireMock.Net/issues/826) - Dynamic Body not to be cached when a Func is used to created the body [feature] # 1.5.8 (16 October 2022) -- [#816](https://github.com/WireMock-Net/WireMock.Net/pull/816) - Some fixes to WireMock.Net.Assertions [feature] contributed by [StefH](https://github.com/StefH) -- [#817](https://github.com/WireMock-Net/WireMock.Net/pull/817) - ExactMatcher : IgnoreCase [feature] contributed by [StefH](https://github.com/StefH) -- [#824](https://github.com/WireMock-Net/WireMock.Net/pull/824) - WebHook - Transform Url [feature] contributed by [StefH](https://github.com/StefH) - [#814](https://github.com/WireMock-Net/WireMock.Net/issues/814) - WithHeader cannot handle multiple requests with the same header key values [bug] - [#815](https://github.com/WireMock-Net/WireMock.Net/issues/815) - Why does UsingMethod check _callscount? [bug] - [#822](https://github.com/WireMock-Net/WireMock.Net/issues/822) - Webhook with generic url, body and custom header values [feature] # 1.5.7 (11 October 2022) -- [#818](https://github.com/WireMock-Net/WireMock.Net/pull/818) - Add option to run the server on http & https [feature] contributed by [StefH](https://github.com/StefH) -- [#821](https://github.com/WireMock-Net/WireMock.Net/pull/821) - Add UseDefinedRequestMatchers to ProxyAndRecordSettings [feature] contributed by [StefH](https://github.com/StefH) -- [#823](https://github.com/WireMock-Net/WireMock.Net/pull/823) - Add implicit operators to WireMockList contributed by [StefH](https://github.com/StefH) - [#819](https://github.com/WireMock-Net/WireMock.Net/issues/819) - Can I preserve Mapping title and matchers for proxy response? [feature] # 1.5.6 (12 September 2022) -- [#803](https://github.com/WireMock-Net/WireMock.Net/pull/803) - WebHook : UseFireAndForget + Delay [feature] contributed by [StefH](https://github.com/StefH) - [#801](https://github.com/WireMock-Net/WireMock.Net/issues/801) - Webhook Delays [feature] # 1.5.5 (03 September 2022) -- [#798](https://github.com/WireMock-Net/WireMock.Net/pull/798) - Add support to use 'mapping' object in in reponse templating [feature] contributed by [StefH](https://github.com/StefH) -- [#800](https://github.com/WireMock-Net/WireMock.Net/pull/800) - Bump Microsoft.Owin from 4.1.1 to 4.2.2 in /src/WireMock.Net (net46) [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#802](https://github.com/WireMock-Net/WireMock.Net/pull/802) - Add assertions for request methods contributed by [rafaelmfonseca](https://github.com/rafaelmfonseca) - [#772](https://github.com/WireMock-Net/WireMock.Net/issues/772) - How to get matched mapping by HttpRequest or HttpRequestMessage [feature] # 1.5.4 (24 August 2022) -- [#778](https://github.com/WireMock-Net/WireMock.Net/pull/778) - Fix Proxying when StartAdminInterface=true [bug] contributed by [StefH](https://github.com/StefH) -- [#781](https://github.com/WireMock-Net/WireMock.Net/pull/781) - Update some NuGet packages [feature] contributed by [StefH](https://github.com/StefH) -- [#783](https://github.com/WireMock-Net/WireMock.Net/pull/783) - Fix WithBody when using Pact and added more nullable annotations [feature] contributed by [StefH](https://github.com/StefH) -- [#787](https://github.com/WireMock-Net/WireMock.Net/pull/787) - Add support for PEM certificates contributed by [StefH](https://github.com/StefH) -- [#789](https://github.com/WireMock-Net/WireMock.Net/pull/789) - Add support for Matcher.Pattern in Pact Body mapping [feature] contributed by [StefH](https://github.com/StefH) -- [#790](https://github.com/WireMock-Net/WireMock.Net/pull/790) - Add Response.WithBody with IJsonConverter [feature] contributed by [StefH](https://github.com/StefH) -- [#795](https://github.com/WireMock-Net/WireMock.Net/pull/795) - Add check for duplicate Guids when posting multiple mappings in one request contributed by [StefH](https://github.com/StefH) -- [#797](https://github.com/WireMock-Net/WireMock.Net/pull/797) - Fix WithHeader when using RejectOnMatch [bug] contributed by [flts](https://github.com/flts) - [#775](https://github.com/WireMock-Net/WireMock.Net/issues/775) - When "StartAdminInterface" is true then each time is generated new mapping from the proxy [bug] - [#784](https://github.com/WireMock-Net/WireMock.Net/issues/784) - Response body is missing in generated pact file when IBodyResponseBuilder.WithBody is used [bug] - [#785](https://github.com/WireMock-Net/WireMock.Net/issues/785) - Support for PEM certificates when using ssl [feature] @@ -434,469 +214,210 @@ - [#796](https://github.com/WireMock-Net/WireMock.Net/issues/796) - RequestMessageHeaderMatcher with MatchBehaviour.RejectOnMatch reverses match results twice [bug] # 1.5.3 (29 July 2022) -- [#777](https://github.com/WireMock-Net/WireMock.Net/pull/777) - Update Scriban.Signed to version 5.5.0 [feature] contributed by [StefH](https://github.com/StefH) - [#776](https://github.com/WireMock-Net/WireMock.Net/issues/776) - Update Scriban.Signed to support more functions, e.g math.random [feature] -# 1.5.2 (24 July 2022) -- [#769](https://github.com/WireMock-Net/WireMock.Net/pull/769) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.3 to 2.1.7 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#771](https://github.com/WireMock-Net/WireMock.Net/pull/771) - JsonPartialMatcher - support Regex [feature] contributed by [StefH](https://github.com/StefH) - # 1.5.1 (08 July 2022) -- [#762](https://github.com/WireMock-Net/WireMock.Net/pull/762) - Bump Newtonsoft.Json from 11.0.2 to 13.0.1 in /examples/WireMock.Net.WebApplication.NETCore2 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#763](https://github.com/WireMock-Net/WireMock.Net/pull/763) - Bump Newtonsoft.Json from 6.0.1 to 13.0.1 in /examples/WireMock.Net.Client.Net472 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#765](https://github.com/WireMock-Net/WireMock.Net/pull/765) - Update WireMock.Org.Abstractions and WireMock.Org.RestClient [feature] contributed by [StefH](https://github.com/StefH) -- [#766](https://github.com/WireMock-Net/WireMock.Net/pull/766) - Bump Microsoft.AspNetCore.Http from 2.1.1 to 2.1.22 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#767](https://github.com/WireMock-Net/WireMock.Net/pull/767) - Rename (WireMock.Pact.Models.V2)-Request to PactRequest and -Response to PactResponse [feature] contributed by [StefH](https://github.com/StefH) - [#764](https://github.com/WireMock-Net/WireMock.Net/issues/764) - Wrong mapping of method GetAdminMappingsAsync from IWireMockOrgApi [bug] -# 1.5.0 (09 June 2022) -- [#755](https://github.com/WireMock-Net/WireMock.Net/pull/755) - Add MatchOperator "Or", "And" and "Average" for patterns [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.43 (21 May 2022) -- [#757](https://github.com/WireMock-Net/WireMock.Net/pull/757) - Log correct exception message when handling aggregate exceptions contributed by [siewers](https://github.com/siewers) -- [#759](https://github.com/WireMock-Net/WireMock.Net/pull/759) - Add WireMock.Net.xUnit project [feature] contributed by [StefH](https://github.com/StefH) - [#756](https://github.com/WireMock-Net/WireMock.Net/issues/756) - WireMockConsoleLogger aggregate exception handling bug? [bug] - [#758](https://github.com/WireMock-Net/WireMock.Net/issues/758) - Add support for logging to an xUnit ITestOutputHelper [feature] # 1.4.42 (13 May 2022) -- [#748](https://github.com/WireMock-Net/WireMock.Net/pull/748) - Initial support for converting the mappings to a Pact(flow) json file [feature] contributed by [StefH](https://github.com/StefH) -- [#749](https://github.com/WireMock-Net/WireMock.Net/pull/749) - Swagger support [feature] contributed by [StefH](https://github.com/StefH) -- [#750](https://github.com/WireMock-Net/WireMock.Net/pull/750) - [Snyk] Security upgrade Newtonsoft.Json from 11.0.2 to 13.0.1 contributed by [snyk-bot](https://github.com/snyk-bot) -- [#751](https://github.com/WireMock-Net/WireMock.Net/pull/751) - Update NuGets packages [feature] contributed by [StefH](https://github.com/StefH) - [#741](https://github.com/WireMock-Net/WireMock.Net/issues/741) - Integrate with Pact [feature] - [#753](https://github.com/WireMock-Net/WireMock.Net/issues/753) - FluentAssertions - assert the server has not received a call [feature] # 1.4.41 (21 April 2022) -- [#746](https://github.com/WireMock-Net/WireMock.Net/pull/746) - Allow Timeout.InfiniteTimeSpan for WithDelay [feature] contributed by [StefH](https://github.com/StefH) -- [#747](https://github.com/WireMock-Net/WireMock.Net/pull/747) - Update the logic for ProxyAndRecord contributed by [StefH](https://github.com/StefH) - [#744](https://github.com/WireMock-Net/WireMock.Net/issues/744) - System.ArgumentOutOfRangeException when Timeout.InfiniteTimeSpan used as an argument for IResponseBuilder.WithDelay() [bug] -# 1.4.40 (26 March 2022) -- [#740](https://github.com/WireMock-Net/WireMock.Net/pull/740) - Add Port and Url property to WireMockServer + upgrade System.Linq.Dynamic.Core [feature] contributed by [StefH](https://github.com/StefH) - -# 1.4.39 (25 March 2022) -- [#739](https://github.com/WireMock-Net/WireMock.Net/pull/739) - Upgrade NuGet for RandomDataGenerator.Net to 1.0.14 contributed by [StefH](https://github.com/StefH) - -# 1.4.38 (11 March 2022) -- [#736](https://github.com/WireMock-Net/WireMock.Net/pull/736) - Remove interface for all Setting classes [feature] contributed by [StefH](https://github.com/StefH) -- [#737](https://github.com/WireMock-Net/WireMock.Net/pull/737) - Add WireMock.Net.WebApplication.NET6 example contributed by [StefH](https://github.com/StefH) - # 1.4.37 (02 March 2022) -- [#730](https://github.com/WireMock-Net/WireMock.Net/pull/730) - Fixed bug "dotnet nuget push -n" [bug] contributed by [StefH](https://github.com/StefH) -- [#732](https://github.com/WireMock-Net/WireMock.Net/pull/732) - Make X509CertificatePassword optional [feature] contributed by [StefH](https://github.com/StefH) -- [#733](https://github.com/WireMock-Net/WireMock.Net/pull/733) - Fix FileSystemWatcher [bug] contributed by [StefH](https://github.com/StefH) - [#726](https://github.com/WireMock-Net/WireMock.Net/issues/726) - Wiremock - WatchStaticMappings only works until the first request is made [bug] -# 1.4.36 (25 February 2022) -- [#728](https://github.com/WireMock-Net/WireMock.Net/pull/728) - Update NuGet packages [feature] contributed by [StefH](https://github.com/StefH) -- [#729](https://github.com/WireMock-Net/WireMock.Net/pull/729) - BodyAsFile should use BodyAsFileIsCached value [bug] contributed by [StefH](https://github.com/StefH) - # 1.4.35 (09 February 2022) -- [#722](https://github.com/WireMock-Net/WireMock.Net/pull/722) - Fixed 'Response BodyAsJson with JArray does not work' [bug] contributed by [StefH](https://github.com/StefH) - [#721](https://github.com/WireMock-Net/WireMock.Net/issues/721) - Response BodyAsJson with array does not work [bug] # 1.4.34 (27 January 2022) -- [#716](https://github.com/WireMock-Net/WireMock.Net/pull/716) - MatcherMapper : Always use Pattern [bug] contributed by [StefH](https://github.com/StefH) - [#715](https://github.com/WireMock-Net/WireMock.Net/issues/715) - Record request mapping outputs JsonMatcher with Patterns instead of Pattern [bug] -# 1.4.33 (24 January 2022) -- [#714](https://github.com/WireMock-Net/WireMock.Net/pull/714) - Add support for Cors [feature] contributed by [StefH](https://github.com/StefH) - -# 1.4.32 (17 January 2022) -- [#713](https://github.com/WireMock-Net/WireMock.Net/pull/713) - Added support of custom matchers in static mappings contributed by [levanoz](https://github.com/levanoz) - -# 1.4.31 (06 January 2022) -- [#706](https://github.com/WireMock-Net/WireMock.Net/pull/706) - Provide open api schema to dynamic examples generator so you can generate accurate data [feature] contributed by [brunotarghetta](https://github.com/brunotarghetta) -- [#707](https://github.com/WireMock-Net/WireMock.Net/pull/707) - Use NuGet "Stef.Validation" [feature] contributed by [StefH](https://github.com/StefH) -- [#710](https://github.com/WireMock-Net/WireMock.Net/pull/710) - Add ReplaceNodeOption flag [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.30 (25 December 2021) -- [#703](https://github.com/WireMock-Net/WireMock.Net/pull/703) - SaveUnmatchedRequests [feature] contributed by [StefH](https://github.com/StefH) -- [#704](https://github.com/WireMock-Net/WireMock.Net/pull/704) - Add .ConfigureAwait(false); to the await Task calls [bug] contributed by [StefH](https://github.com/StefH) - [#534](https://github.com/WireMock-Net/WireMock.Net/issues/534) - Mock server not answer if integrated in Xamarin UITest project [bug] - [#567](https://github.com/WireMock-Net/WireMock.Net/issues/567) - Can't start WireMock.Net server in Xamarin.UITest project (.NET Framework 4.7.2) on MacOS [bug] - [#685](https://github.com/WireMock-Net/WireMock.Net/issues/685) - GuidWildcardMatcher to match on GUIDs [feature] -# 1.4.29 (12 December 2021) -- [#699](https://github.com/WireMock-Net/WireMock.Net/pull/699) - GUID Pattern support in RegexMatcher contributed by [brogdogg](https://github.com/brogdogg) -- [#700](https://github.com/WireMock-Net/WireMock.Net/pull/700) - RegexExtended in settings [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.28 (01 December 2021) -- [#686](https://github.com/WireMock-Net/WireMock.Net/pull/686) - [Snyk] Security upgrade Microsoft.Owin from 4.0.0 to 4.1.1 [dependencies] contributed by [snyk-bot](https://github.com/snyk-bot) -- [#688](https://github.com/WireMock-Net/WireMock.Net/pull/688) - Bump System.Text.Encodings.Web from 4.5.0 to 4.5.1 in /examples/WireMock.Net.Console.Net472.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#689](https://github.com/WireMock-Net/WireMock.Net/pull/689) - Upgrade some NuGet's (Codecov, coverlet, Moq and NFluent) [dependencies] contributed by [StefH](https://github.com/StefH) -- [#691](https://github.com/WireMock-Net/WireMock.Net/pull/691) - Update the OpenApiPathsMapper to handle Value/Wildcard [feature] contributed by [StefH](https://github.com/StefH) -- [#694](https://github.com/WireMock-Net/WireMock.Net/pull/694) - RamlToOpenAPI updated to 0.5.0 [feature] contributed by [mcheguini](https://github.com/mcheguini) -- [#695](https://github.com/WireMock-Net/WireMock.Net/pull/695) - Allow configure IgnoreCase in settings [feature] contributed by [leolplex](https://github.com/leolplex) -- [#696](https://github.com/WireMock-Net/WireMock.Net/pull/696) - Filter required property in headers, query params, request body [feature] contributed by [leolplex](https://github.com/leolplex) - [#666](https://github.com/WireMock-Net/WireMock.Net/issues/666) - Example is not working as expected [bug] - [#692](https://github.com/WireMock-Net/WireMock.Net/issues/692) - Case insensitive and ignoring optional path and header parameters in OpenApiPathsMapper [feature] -# 1.4.27 (17 November 2021) -- [#678](https://github.com/WireMock-Net/WireMock.Net/pull/678) - Support RequestBody [feature] contributed by [leolplex](https://github.com/leolplex) -- [#680](https://github.com/WireMock-Net/WireMock.Net/pull/680) - Support examples in properties [feature] contributed by [leolplex](https://github.com/leolplex) -- [#681](https://github.com/WireMock-Net/WireMock.Net/pull/681) - Support enums in properties [feature] contributed by [leolplex](https://github.com/leolplex) - -# 1.4.26 (04 November 2021) -- [#670](https://github.com/WireMock-Net/WireMock.Net/pull/670) - Improve method MapSchemaToObject to support array and object [feature] contributed by [leolplex](https://github.com/leolplex) -- [#673](https://github.com/WireMock-Net/WireMock.Net/pull/673) - Support examples random data generation contributed by [leolplex](https://github.com/leolplex) -- [#675](https://github.com/WireMock-Net/WireMock.Net/pull/675) - Support basepath from servers contributed by [leolplex](https://github.com/leolplex) -- [#676](https://github.com/WireMock-Net/WireMock.Net/pull/676) - Fix random generate data in url no spaces [feature] contributed by [leolplex](https://github.com/leolplex) - -# 1.4.25 (27 October 2021) -- [#661](https://github.com/WireMock-Net/WireMock.Net/pull/661) - Add TimeSettings (Start, End and TTL) [feature] contributed by [StefH](https://github.com/StefH) -- [#664](https://github.com/WireMock-Net/WireMock.Net/pull/664) - Support Array in OpenApiParser [feature] contributed by [leolplex](https://github.com/leolplex) -- [#667](https://github.com/WireMock-Net/WireMock.Net/pull/667) - Add JsonPartialWildcardMatcher [feature] contributed by [StefH](https://github.com/StefH) -- [#669](https://github.com/WireMock-Net/WireMock.Net/pull/669) - Support Schema Example and Support AllOf in definitions [feature] contributed by [StefH](https://github.com/StefH) - -# 1.4.24 (20 October 2021) -- [#637](https://github.com/WireMock-Net/WireMock.Net/pull/637) - Add support for AzureAD authentication for REST admin interface [feature] contributed by [StefH](https://github.com/StefH) -- [#643](https://github.com/WireMock-Net/WireMock.Net/pull/643) - Support edge case: first object, next an array. [feature] contributed by [leolplex](https://github.com/leolplex) -- [#644](https://github.com/WireMock-Net/WireMock.Net/pull/644) - Mapping headers in OpenAPI [feature] contributed by [leolplex](https://github.com/leolplex) -- [#649](https://github.com/WireMock-Net/WireMock.Net/pull/649) - Refactor method name MapHeaders and httpStatusCode contributed by [leolplex](https://github.com/leolplex) -- [#651](https://github.com/WireMock-Net/WireMock.Net/pull/651) - Implement PatternAsFile for StringMatcher [feature] contributed by [StefH](https://github.com/StefH) -- [#654](https://github.com/WireMock-Net/WireMock.Net/pull/654) - Update NotNullOrEmptyMatcher to also implement IStringMatcher [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.23 (27 September 2021) -- [#635](https://github.com/WireMock-Net/WireMock.Net/pull/635) - WireMock.Net.FluentAssertions : upgrade to latest FluentAssertions [feature] contributed by [StefH](https://github.com/StefH) - [#634](https://github.com/WireMock-Net/WireMock.Net/issues/634) - Upgrade to latest FluentAssertions [bug] -# 1.4.22 (22 September 2021) -- [#633](https://github.com/WireMock-Net/WireMock.Net/pull/633) - Implement Random Delay [feature] contributed by [StefH](https://github.com/StefH) - -# 1.4.21 (16 September 2021) -- [#631](https://github.com/WireMock-Net/WireMock.Net/pull/631) - Add WireMock.org RestClient [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.20 (06 August 2021) -- [#628](https://github.com/WireMock-Net/WireMock.Net/pull/628) - Fix issue with FluentBuilder [bug] contributed by [StefH](https://github.com/StefH) - [#626](https://github.com/WireMock-Net/WireMock.Net/issues/626) - version 1.4.19 throws a lot of analyzer errors related to the BaseBuilder.cs [bug] # 1.4.19 (04 August 2021) -- [#622](https://github.com/WireMock-Net/WireMock.Net/pull/622) - Add FluentBuilder for client models [feature] contributed by [StefH](https://github.com/StefH) -- [#625](https://github.com/WireMock-Net/WireMock.Net/pull/625) - Add NotNullOrEmptyMatcher [feature] contributed by [StefH](https://github.com/StefH) - [#621](https://github.com/WireMock-Net/WireMock.Net/issues/621) - Fluent API for RestClient MappingModel creation [feature] - [#624](https://github.com/WireMock-Net/WireMock.Net/issues/624) - Post request with "BodyAsBytes" is not matched by RegexMatcher [bug] # 1.4.18 (10 July 2021) -- [#619](https://github.com/WireMock-Net/WireMock.Net/pull/619) - Update Handlebars.Net.Helpers.XPath to fix issue with 'xml version' contributed by [StefH](https://github.com/StefH) - [#618](https://github.com/WireMock-Net/WireMock.Net/issues/618) - Trying to use attribute of the request object while creating response while mocking a soap service [bug] -# 1.4.17 (07 July 2021) -- [#617](https://github.com/WireMock-Net/WireMock.Net/pull/617) - Add support for Handlebars.Net.Helpers.Humanizer [feature] contributed by [StefH](https://github.com/StefH) - -# 1.4.16 (05 June 2021) -- [#616](https://github.com/WireMock-Net/WireMock.Net/pull/616) - Upgrade Handlebars.Net.Helpers to 2.19 [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.15 (19 May 2021) -- [#615](https://github.com/WireMock-Net/WireMock.Net/pull/615) - Add support for multiple webhooks [feature] contributed by [StefH](https://github.com/StefH) - [#614](https://github.com/WireMock-Net/WireMock.Net/issues/614) - Is it possible to some how send multiple webhooks? [feature] -# 1.4.14 (11 May 2021) -- [#610](https://github.com/WireMock-Net/WireMock.Net/pull/610) - Fix some SonarCloud issues in UnitTests contributed by [StefH](https://github.com/StefH) -- [#611](https://github.com/WireMock-Net/WireMock.Net/pull/611) - Allow to add custom service registrations when using ASP.NET Core [feature] contributed by [starkpl](https://github.com/starkpl) -- [#612](https://github.com/WireMock-Net/WireMock.Net/pull/612) - Don't run SonarCloud tasks for PullRequests [feature] contributed by [StefH](https://github.com/StefH) - # 1.4.13 (26 April 2021) -- [#607](https://github.com/WireMock-Net/WireMock.Net/pull/607) - Bump System.Text.Encodings.Web from 4.5.0 to 4.5.1 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#609](https://github.com/WireMock-Net/WireMock.Net/pull/609) - Add possibility to use settings to generate MappingModel models with wildcard path parameters. [feature] contributed by [StefH](https://github.com/StefH) - [#608](https://github.com/WireMock-Net/WireMock.Net/issues/608) - Import from OpenApi generates model with path parameter narrowed in range (example value=42 instead of '*') [feature] -# 1.4.12 (22 April 2021) -- [#605](https://github.com/WireMock-Net/WireMock.Net/pull/605) - Bump System.Net.Http from 4.3.3 to 4.3.4 in /src/WireMock.Net [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#606](https://github.com/WireMock-Net/WireMock.Net/pull/606) - Bump System.Net.Http from 4.3.3 to 4.3.4 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) - # 1.4.11 (18 April 2021) -- [#604](https://github.com/WireMock-Net/WireMock.Net/pull/604) - Fix match logic for exact bytearray contributed by [StefH](https://github.com/StefH) - [#601](https://github.com/WireMock-Net/WireMock.Net/issues/601) - Exact byte array request matching fails on specific byte arrays [bug] # 1.4.10 (15 April 2021) -- [#603](https://github.com/WireMock-Net/WireMock.Net/pull/603) - Fix callback with Headers [bug] contributed by [StefH](https://github.com/StefH) - [#602](https://github.com/WireMock-Net/WireMock.Net/issues/602) - Header not being returned when set in WithCallback [bug] -# 1.4.9 (31 March 2021) -- [#600](https://github.com/WireMock-Net/WireMock.Net/pull/600) - WithProxy() should save the new mapping [bug] contributed by [StefH](https://github.com/StefH) - # 1.4.8 (24 March 2021) -- [#591](https://github.com/WireMock-Net/WireMock.Net/pull/591) - Webhook [feature] contributed by [StefH](https://github.com/StefH) - [#589](https://github.com/WireMock-Net/WireMock.Net/issues/589) - How to send a request to a specific URL after sending response [feature] -# 1.4.7 (21 March 2021) -- [#594](https://github.com/WireMock-Net/WireMock.Net/pull/594) - Add possibility to the WithBody() to use IBodyData [feature] contributed by [StefH](https://github.com/StefH) -- [#595](https://github.com/WireMock-Net/WireMock.Net/pull/595) - Use Handlebars.Net.Helpers Version="2.1.2" [feature] contributed by [StefH](https://github.com/StefH) -- [#597](https://github.com/WireMock-Net/WireMock.Net/pull/597) - Remove 2 second delay from first response and add IPv6 address support [bug, feature] contributed by [benagain](https://github.com/benagain) - # 1.4.6 (26 February 2021) -- [#587](https://github.com/WireMock-Net/WireMock.Net/pull/587) - Fix WithCallback logic when using other fluent builder statements [bug] contributed by [StefH](https://github.com/StefH) - [#569](https://github.com/WireMock-Net/WireMock.Net/issues/569) - WithCallback circumvent the rest of the builder [bug] -# 1.4.5 (11 February 2021) -- [#585](https://github.com/WireMock-Net/WireMock.Net/pull/585) - Fix response date header [bug] contributed by [wolf8196](https://github.com/wolf8196) - # 1.4.4 (09 February 2021) -- [#581](https://github.com/WireMock-Net/WireMock.Net/pull/581) - Use new Handlebars.Net.Helpers [feature] contributed by [StefH](https://github.com/StefH) -- [#582](https://github.com/WireMock-Net/WireMock.Net/pull/582) - Add Xamarin UI tests [feature] contributed by [StefH](https://github.com/StefH) - [#568](https://github.com/WireMock-Net/WireMock.Net/issues/568) - [Question] Dates in response templates [feature] # 1.4.3 (05 February 2021) -- [#570](https://github.com/WireMock-Net/WireMock.Net/pull/570) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.StandAlone.NETCoreApp [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#571](https://github.com/WireMock-Net/WireMock.Net/pull/571) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.NETCoreApp2 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#572](https://github.com/WireMock-Net/WireMock.Net/pull/572) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.NETCoreApp [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#573](https://github.com/WireMock-Net/WireMock.Net/pull/573) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.Net461.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#574](https://github.com/WireMock-Net/WireMock.Net/pull/574) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.Net452.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#575](https://github.com/WireMock-Net/WireMock.Net/pull/575) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.StandAlone.Net452 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#576](https://github.com/WireMock-Net/WireMock.Net/pull/576) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#579](https://github.com/WireMock-Net/WireMock.Net/pull/579) - Net5 issue [bug] contributed by [StefH](https://github.com/StefH) - [#577](https://github.com/WireMock-Net/WireMock.Net/issues/577) - WireMock.Net will not run with certain .net5 dependencies installed in the project [bug] # 1.4.2 (24 January 2021) -- [#566](https://github.com/WireMock-Net/WireMock.Net/pull/566) - Do not save Mappings when SaveMappingForStatusCodePattern does not match [bug] contributed by [StefH](https://github.com/StefH) - [#565](https://github.com/WireMock-Net/WireMock.Net/issues/565) - NullReferenceException [bug] # 1.4.1 (19 January 2021) -- [#562](https://github.com/WireMock-Net/WireMock.Net/pull/562) - Refactor Transformer (add Scriban) [feature] contributed by [StefH](https://github.com/StefH) - [#214](https://github.com/WireMock-Net/WireMock.Net/issues/214) - Feature: Add support for template language DotLiquid [feature] -# 1.4.0 (12 January 2021) -- [#548](https://github.com/WireMock-Net/WireMock.Net/pull/548) - Move CSharpCodeMatcher to a new project [feature] contributed by [StefH](https://github.com/StefH) - # 1.3.10 (23 December 2020) -- [#555](https://github.com/WireMock-Net/WireMock.Net/pull/555) - Add more tests for Proxy with Authorization [feature] contributed by [StefH](https://github.com/StefH) -- [#561](https://github.com/WireMock-Net/WireMock.Net/pull/561) - Do not save "admin" mappings when running in Proxy - mode contributed by [StefH](https://github.com/StefH) - [#559](https://github.com/WireMock-Net/WireMock.Net/issues/559) - WireMock Setting 'SaveMappingToFile' raising cast object to type error [bug] # 1.3.9 (08 December 2020) -- [#550](https://github.com/WireMock-Net/WireMock.Net/pull/550) - WithProxy(...) also use all proxy settings [bug] contributed by [StefH](https://github.com/StefH) -- [#551](https://github.com/WireMock-Net/WireMock.Net/pull/551) - Add obsolete warning: CSharpCodeMatcher will be moved to a separate NuGet package 'WireMock.Net.Matchers.CSharpCode' [feature] contributed by [StefH](https://github.com/StefH) - [#549](https://github.com/WireMock-Net/WireMock.Net/issues/549) - WithProxy(...) does not save the mappings to file [bug] # 1.3.8 (03 December 2020) -- [#539](https://github.com/WireMock-Net/WireMock.Net/pull/539) - Support for partial JSON matching contributed by [gleb-osokin](https://github.com/gleb-osokin) -- [#542](https://github.com/WireMock-Net/WireMock.Net/pull/542) - Create dotnet-wiremock tool [feature] contributed by [StefH](https://github.com/StefH) -- [#543](https://github.com/WireMock-Net/WireMock.Net/pull/543) - Add support for .NET 5 [feature] contributed by [StefH](https://github.com/StefH) -- [#544](https://github.com/WireMock-Net/WireMock.Net/pull/544) - Use Java 11 in Azure Pipelines (needed for SonarCloud) [feature] contributed by [StefH](https://github.com/StefH) -- [#545](https://github.com/WireMock-Net/WireMock.Net/pull/545) - Fix SonarCloud OpenCover (coverlet-coverage) [bug] contributed by [StefH](https://github.com/StefH) -- [#547](https://github.com/WireMock-Net/WireMock.Net/pull/547) - Fix Proxying with SSL and NetCoreApp3.1 [bug] contributed by [StefH](https://github.com/StefH) - [#524](https://github.com/WireMock-Net/WireMock.Net/issues/524) - Proxying with SSL Not Working in .NET Core 3.1 [bug] # 1.3.6 (10 November 2020) -- [#529](https://github.com/WireMock-Net/WireMock.Net/pull/529) - Add assertions for ClientIP, Url and ProxyUrl [feature] contributed by [akamud](https://github.com/akamud) -- [#535](https://github.com/WireMock-Net/WireMock.Net/pull/535) - WithCallback should use also use enum HttpStatusCode [bug] contributed by [StefH](https://github.com/StefH) -- [#537](https://github.com/WireMock-Net/WireMock.Net/pull/537) - Add Custom Certificate settings [feature] contributed by [StefH](https://github.com/StefH) - [#533](https://github.com/WireMock-Net/WireMock.Net/issues/533) - Stubbed response with only callback returns unexpected status code. [bug] - [#536](https://github.com/WireMock-Net/WireMock.Net/issues/536) - Overriding the default ssl certificate via file. [feature] -# 1.3.5 (04 November 2020) -- [#530](https://github.com/WireMock-Net/WireMock.Net/pull/530) - Fix dotnet-sonarscanner [bug] contributed by [StefH](https://github.com/StefH) -- [#531](https://github.com/WireMock-Net/WireMock.Net/pull/531) - Add WithCallback-Async [feature] contributed by [StefH](https://github.com/StefH) - -# 1.3.4 (17 October 2020) -- [#522](https://github.com/WireMock-Net/WireMock.Net/pull/522) - Add ContinuousIntegrationBuild property [feature] contributed by [StefH](https://github.com/StefH) -- [#525](https://github.com/WireMock-Net/WireMock.Net/pull/525) - Handlebars.Net.Helpers Version="1.1.0" [feature] contributed by [StefH](https://github.com/StefH) - # 1.3.3 (15 October 2020) -- [#520](https://github.com/WireMock-Net/WireMock.Net/pull/520) - Make kestrel limits configurable contributed by [eduherminio](https://github.com/eduherminio) - [#521](https://github.com/WireMock-Net/WireMock.Net/issues/521) - Make Kestrel limits configurable [feature] # 1.3.2 (14 October 2020) -- [#505](https://github.com/WireMock-Net/WireMock.Net/pull/505) - Fix reading JsonMatcher-mapping with object as pattern [bug] contributed by [StefH](https://github.com/StefH) -- [#514](https://github.com/WireMock-Net/WireMock.Net/pull/514) - Update .NET Core 3.1 example contributed by [Crossbow78](https://github.com/Crossbow78) - [#504](https://github.com/WireMock-Net/WireMock.Net/issues/504) - Loading mapping models with `JsonMatcher` is not working correctly [bug] - [#513](https://github.com/WireMock-Net/WireMock.Net/issues/513) - Static mapping break from 1.2.17 to 1.2.18 and higher [bug] -# 1.3.1 (30 September 2020) -- [#509](https://github.com/WireMock-Net/WireMock.Net/pull/509) - Adding netcoreapp3.1 as a target framework [feature] contributed by [APIWT](https://github.com/APIWT) - # 1.3.0 (29 September 2020) -- [#508](https://github.com/WireMock-Net/WireMock.Net/pull/508) - Fix vulnerability in NuGet dependencies contributed by [StefH](https://github.com/StefH) - [#327](https://github.com/WireMock-Net/WireMock.Net/issues/327) - Index must be within the bounds of the List - Bug [bug] - [#507](https://github.com/WireMock-Net/WireMock.Net/issues/507) - Fix vulnerability found in Microsoft.AspNetCore dependency [feature] # 1.2.18 (13 August 2020) -- [#496](https://github.com/WireMock-Net/WireMock.Net/pull/496) - Add setting to handle requests synchronously [feature] contributed by [StefH](https://github.com/StefH) -- [#500](https://github.com/WireMock-Net/WireMock.Net/pull/500) - Add ThrowExceptionWhenMatcherFails option to all Matchers [feature] contributed by [StefH](https://github.com/StefH) - [#478](https://github.com/WireMock-Net/WireMock.Net/issues/478) - Sometimes returns status code 0 in unit tests with xunit test fixture (flaky test) [bug] # 1.2.17 (01 August 2020) -- [#495](https://github.com/WireMock-Net/WireMock.Net/pull/495) - Scenario : stay on current state for a number of times contributed by [StefH](https://github.com/StefH) - [#494](https://github.com/WireMock-Net/WireMock.Net/issues/494) - Stay in Current State for specified number of requests [feature] # 1.2.16 (27 July 2020) -- [#492](https://github.com/WireMock-Net/WireMock.Net/pull/492) - Mark FluentMockServer, FluentMockServerSettings, BlacklistedHeaders and BlacklistedCookies as obsolete [feature] contributed by [StefH](https://github.com/StefH) - [#489](https://github.com/WireMock-Net/WireMock.Net/issues/489) - Change "blacklist" and "whitelist" terms [feature] # 1.2.14 (09 July 2020) -- [#479](https://github.com/WireMock-Net/WireMock.Net/pull/479) - An OpenApi (swagger) parser to generate MappingModel or mapping.json file [feature] contributed by [StefH](https://github.com/StefH) -- [#482](https://github.com/WireMock-Net/WireMock.Net/pull/482) - Add PartialMatch to logging / logentries [feature] contributed by [StefH](https://github.com/StefH) -- [#483](https://github.com/WireMock-Net/WireMock.Net/pull/483) - Bring in the WireMock.Net.FluentAssertions tests contributed by [akamud](https://github.com/akamud) -- [#484](https://github.com/WireMock-Net/WireMock.Net/pull/484) - Refactor: extract interfaces [feature] contributed by [StefH](https://github.com/StefH) -- [#487](https://github.com/WireMock-Net/WireMock.Net/pull/487) - Fixed MappingConverter when methods are null [bug] contributed by [StefH](https://github.com/StefH) - [#486](https://github.com/WireMock-Net/WireMock.Net/issues/486) - Admin API fails to create a mapping with Request Body matching [bug] # 1.2.13 (24 May 2020) -- [#475](https://github.com/WireMock-Net/WireMock.Net/pull/475) - Fix Limits.KeepAliveTimeout & Limits.RequestHeadersTimeout [bug] contributed by [StefH](https://github.com/StefH) - [#474](https://github.com/WireMock-Net/WireMock.Net/issues/474) - Performance issue with multiple httpclients (since version 1.2.10) [bug] # 1.2.12 (23 May 2020) -- [#472](https://github.com/WireMock-Net/WireMock.Net/pull/472) - Create new .sln contributed by [StefH](https://github.com/StefH) -- [#473](https://github.com/WireMock-Net/WireMock.Net/pull/473) - Fixed Proxy when using MultipartForm with byte[] [bug] contributed by [StefH](https://github.com/StefH) - [#468](https://github.com/WireMock-Net/WireMock.Net/issues/468) - Proxy mode: Incorrect handling of multipart requests [bug] # 1.2.11.0 (18 May 2020) -- [#469](https://github.com/WireMock-Net/WireMock.Net/pull/469) - Fix unhandled exception when target is unavailable [bug] contributed by [StefH](https://github.com/StefH) - [#467](https://github.com/WireMock-Net/WireMock.Net/issues/467) - Proxy mode: Unhandled exception when target is not working [bug] # 1.2.10 (17 May 2020) -- [#456](https://github.com/WireMock-Net/WireMock.Net/pull/456) - Include Handlebars.Net.Helpers project [feature] contributed by [StefH](https://github.com/StefH) -- [#457](https://github.com/WireMock-Net/WireMock.Net/pull/457) - Kestrel Options Limits [bug] contributed by [StefH](https://github.com/StefH) - [#455](https://github.com/WireMock-Net/WireMock.Net/issues/455) - There is no option to increase body size while proxying [bug] # 1.2.9.0 (14 May 2020) -- [#465](https://github.com/WireMock-Net/WireMock.Net/pull/465) - Fix method ResetMappingsAsync in the RestEase-AdminApi [bug] contributed by [StefH](https://github.com/StefH) - [#464](https://github.com/WireMock-Net/WireMock.Net/issues/464) - RestClient Admin API Metadata Base Path Duplication [bug] -# 1.2.8.0 (04 May 2020) -- [#463](https://github.com/WireMock-Net/WireMock.Net/pull/463) - GH161: Fix SourceLink support [bug] contributed by [gitfool](https://github.com/gitfool) - # 1.2.7.0 (30 April 2020) -- [#461](https://github.com/WireMock-Net/WireMock.Net/pull/461) - Support Path in ProxyUrl contributed by [StefH](https://github.com/StefH) - [#459](https://github.com/WireMock-Net/WireMock.Net/issues/459) - When respond with proxy requestMessage.Url is used, not AbsoluteUrl [bug] # 1.2.6.0 (29 April 2020) -- [#460](https://github.com/WireMock-Net/WireMock.Net/pull/460) - When using ResponseMessageTransformer : keep BodyEncoding [bug] contributed by [StefH](https://github.com/StefH) - [#458](https://github.com/WireMock-Net/WireMock.Net/issues/458) - Response BodyAsString loses BodyData.Encoding when UseTransformer = true [bug] # 1.2.5.0 (17 April 2020) -- [#454](https://github.com/WireMock-Net/WireMock.Net/pull/454) - Fix port = 0 for net452 [bug] contributed by [StefH](https://github.com/StefH) - [#453](https://github.com/WireMock-Net/WireMock.Net/issues/453) - MockServer not starting [bug] # 1.2.4.0 (10 April 2020) -- [#439](https://github.com/WireMock-Net/WireMock.Net/pull/439) - Add support for GZip and Deflate [feature] contributed by [StefH](https://github.com/StefH) -- [#444](https://github.com/WireMock-Net/WireMock.Net/pull/444) - Add readme.md + license from mock4net [feature] contributed by [StefH](https://github.com/StefH) -- [#451](https://github.com/WireMock-Net/WireMock.Net/pull/451) - Update NuGet dependencies (e.g. coverage related) to fix CI-build [feature] contributed by [StefH](https://github.com/StefH) -- [#452](https://github.com/WireMock-Net/WireMock.Net/pull/452) - Add ValidatedNotNullAttribute (for SonarQube) [refactor] contributed by [StefH](https://github.com/StefH) - [#426](https://github.com/WireMock-Net/WireMock.Net/issues/426) - Add support for compressed requests, such as GZIP or DEFLATE [feature] # 1.2.3.0 (01 April 2020) -- [#449](https://github.com/WireMock-Net/WireMock.Net/pull/449) - Netstandard21 [feature] contributed by [StefH](https://github.com/StefH) - [#447](https://github.com/WireMock-Net/WireMock.Net/issues/447) - Add support for .NET Standard 2.1 / .NET Core 3.1 [feature] - [#448](https://github.com/WireMock-Net/WireMock.Net/issues/448) - WireMock.Net is not compatible with Microsoft.VisualStudio.Web.CodeGeneration.Design 3.1.1 [bug] # 1.2.2.0 (25 March 2020) -- [#446](https://github.com/WireMock-Net/WireMock.Net/pull/446) - When port is provided: WireMockServer still takes a random port [bug] contributed by [StefH](https://github.com/StefH) - [#445](https://github.com/WireMock-Net/WireMock.Net/issues/445) - Port where WireMockServer listens to - v1.2x [bug] -# 1.2.1.0 (17 March 2020) -- [#442](https://github.com/WireMock-Net/WireMock.Net/pull/442) - Fix Null body in handlebars transformation [bug] contributed by [StefH](https://github.com/StefH) - # 1.2.0.0 (14 March 2020) -- [#417](https://github.com/WireMock-Net/WireMock.Net/pull/417) - Let the .NET core/standard WebHostBuilder use a random port [bug] contributed by [StefH](https://github.com/StefH) -- [#422](https://github.com/WireMock-Net/WireMock.Net/pull/422) - AllowOnlyDefinedHttpStatusCodeInResponse [bug] contributed by [StefH](https://github.com/StefH) - [#379](https://github.com/WireMock-Net/WireMock.Net/issues/379) - Trusting the self signed certificate to enable SSL on dotnet core [bug] - [#420](https://github.com/WireMock-Net/WireMock.Net/issues/420) - Updating to 1.1.6+ breaks tests because new AllowAnyHttpStatusCodeInResponse option defaults to false [bug] # 1.1.10 (05 March 2020) -- [#427](https://github.com/WireMock-Net/WireMock.Net/pull/427) - Add UsingOptions, UsingConnect and UsingTrace [feature] contributed by [StefH](https://github.com/StefH) -- [#434](https://github.com/WireMock-Net/WireMock.Net/pull/434) - Option to disable JSON deserialization [feature] contributed by [sebastianmattar](https://github.com/sebastianmattar) -- [#435](https://github.com/WireMock-Net/WireMock.Net/pull/435) - Also call HandlebarsRegistrationCallback when using WithCallback(..) [feature] contributed by [StefH](https://github.com/StefH) - [#408](https://github.com/WireMock-Net/WireMock.Net/issues/408) - Intermittent threading errors with FindLogEntries [bug] - [#433](https://github.com/WireMock-Net/WireMock.Net/issues/433) - HandlebarsRegistrationCallback not fired [feature] # 1.1.9.0 (25 February 2020) -- [#431](https://github.com/WireMock-Net/WireMock.Net/pull/431) - Fix LinqMatcher for JSON int64 [bug] contributed by [StefH](https://github.com/StefH) - [#425](https://github.com/WireMock-Net/WireMock.Net/issues/425) - Allow 64 bit numbers in JSON [bug] # 1.1.8.0 (22 February 2020) -- [#419](https://github.com/WireMock-Net/WireMock.Net/pull/419) - Support multi line wild card matching [bug] contributed by [NoahLerner](https://github.com/NoahLerner) -- [#421](https://github.com/WireMock-Net/WireMock.Net/pull/421) - Fix: do not return empty matchers array when Func has been used [bug] contributed by [StefH](https://github.com/StefH) -- [#423](https://github.com/WireMock-Net/WireMock.Net/pull/423) - Fixes for Cookie and Header Reject on Match [bug] contributed by [StefH](https://github.com/StefH) -- [#424](https://github.com/WireMock-Net/WireMock.Net/pull/424) - Don't return empty dictionary object for response headers in JSON mapping [feature] contributed by [StefH](https://github.com/StefH) - [#418](https://github.com/WireMock-Net/WireMock.Net/issues/418) - Body matching fails if body has newline [bug] # 1.1.7.0 (06 February 2020) -- [#409](https://github.com/WireMock-Net/WireMock.Net/pull/409) - Admin Delete with mappings in body [feature] contributed by [NoahLerner](https://github.com/NoahLerner) -- [#411](https://github.com/WireMock-Net/WireMock.Net/pull/411) - Improved relative path checking based on file existence [feature] contributed by [NoahLerner](https://github.com/NoahLerner) -- [#413](https://github.com/WireMock-Net/WireMock.Net/pull/413) - Fix new Delete with body missing from IWireMockAdminApi interface contributed by [NoahLerner](https://github.com/NoahLerner) -- [#414](https://github.com/WireMock-Net/WireMock.Net/pull/414) - Fix logger in StandAlone [bug] contributed by [StefH](https://github.com/StefH) - [#412](https://github.com/WireMock-Net/WireMock.Net/issues/412) - WireMock Standalone - null reference exception since settings.Logger [bug] -# 1.1.6.0 (27 January 2020) -- [#407](https://github.com/WireMock-Net/WireMock.Net/pull/407) - AllowAnyHttpStatusCodeInResponse [feature] contributed by [StefH](https://github.com/StefH) - -# 1.1.5.0 (25 January 2020) -- [#405](https://github.com/WireMock-Net/WireMock.Net/pull/405) - Fix logging an Exception Message (linux docker on azure) [bug] contributed by [StefH](https://github.com/StefH) -- [#406](https://github.com/WireMock-Net/WireMock.Net/pull/406) - Fixed StatusCode = null or < 0 [bug] contributed by [StefH](https://github.com/StefH) - # 1.1.3.0 (22 January 2020) -- [#403](https://github.com/WireMock-Net/WireMock.Net/pull/403) - Fix for invalid cast exception contributed by [kashifsoofi](https://github.com/kashifsoofi) - [#402](https://github.com/WireMock-Net/WireMock.Net/issues/402) - Invalid Cast Exception [bug] # 1.1.2.0 (09 January 2020) -- [#399](https://github.com/WireMock-Net/WireMock.Net/pull/399) - ResponseModel.StatusCode is deserialized as either string or long. [bug] contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak) - [#400](https://github.com/WireMock-Net/WireMock.Net/issues/400) - StatusCode not built correctly when loaded from mapping file. [bug] # 1.1.1.0 (09 January 2020) -- [#398](https://github.com/WireMock-Net/WireMock.Net/pull/398) - Feature/xpath transformer [feature] contributed by [kashifsoofi](https://github.com/kashifsoofi) - [#397](https://github.com/WireMock-Net/WireMock.Net/issues/397) - Question/Feature: Add support for selecting XPath in response template [feature] -# 1.1.0.0 (27 December 2019) -- [#363](https://github.com/WireMock-Net/WireMock.Net/pull/363) - WireMock.Net version 1.1.x contributed by [StefH](https://github.com/StefH) - # 1.0.43.0 (26 December 2019) -- [#385](https://github.com/WireMock-Net/WireMock.Net/pull/385) - StatusCode as string [feature] contributed by [StefH](https://github.com/StefH) - [#380](https://github.com/WireMock-Net/WireMock.Net/issues/380) - StatusCode is defined as integer (string is not possible) [bug] - [#382](https://github.com/WireMock-Net/WireMock.Net/issues/382) - Return same request body [feature] # 1.0.42.0 (15 December 2019) -- [#391](https://github.com/WireMock-Net/WireMock.Net/pull/391) - Correctly support DateTime pattern as string in ExactMatcher [bug] contributed by [StefH](https://github.com/StefH) - [#383](https://github.com/WireMock-Net/WireMock.Net/issues/383) - ExactMatcher does not accept ISO8601 DateTime? [bug] # 1.0.41.0 (14 December 2019) -- [#392](https://github.com/WireMock-Net/WireMock.Net/pull/392) - Fix array in JsonMatcher [bug] contributed by [StefH](https://github.com/StefH) - [#390](https://github.com/WireMock-Net/WireMock.Net/issues/390) - JsonMatcher does not match a body containing an array of strings [bug] # 1.0.40.0 (09 December 2019) -- [#389](https://github.com/WireMock-Net/WireMock.Net/pull/389) - Fix QueryStringParser [bug] contributed by [StefH](https://github.com/StefH) - [#387](https://github.com/WireMock-Net/WireMock.Net/issues/387) - Query string parameter value which contains %26 does not work with ExactMatcher [bug] # 1.0.39.0 (07 December 2019) -- [#370](https://github.com/WireMock-Net/WireMock.Net/pull/370) - Add WebProxySettings (use when proxying requests) [feature] contributed by [StefH](https://github.com/StefH) -- [#388](https://github.com/WireMock-Net/WireMock.Net/pull/388) - Transform body as file [bug] contributed by [StefH](https://github.com/StefH) - [#369](https://github.com/WireMock-Net/WireMock.Net/issues/369) - Question: Is there a way to provide a corporate proxy configuration? [feature] - [#375](https://github.com/WireMock-Net/WireMock.Net/issues/375) - Proxying does not follow redirects : make this configurable [feature] - [#386](https://github.com/WireMock-Net/WireMock.Net/issues/386) - Is transforming contents of XML file supported.? [bug] # 1.0.38.0 (30 November 2019) -- [#376](https://github.com/WireMock-Net/WireMock.Net/pull/376) - Support int values for states and scenario naming [feature] contributed by [NoahLerner](https://github.com/NoahLerner) -- [#378](https://github.com/WireMock-Net/WireMock.Net/pull/378) - Set handlebars dependency for .net 4.5.1 to fixed value [bug] contributed by [StefH](https://github.com/StefH) -- [#381](https://github.com/WireMock-Net/WireMock.Net/pull/381) - Use dotnet default development certificate for .NET Core 2.x [feature] contributed by [StefH](https://github.com/StefH) - [#377](https://github.com/WireMock-Net/WireMock.Net/issues/377) - Unable to build against .NET 4.5.1 because of Handlebars [bug] # 1.0.37.0 (08 November 2019) -- [#373](https://github.com/WireMock-Net/WireMock.Net/pull/373) - Make Sonar and WhiteSource optional in the Azure pipelines build [feature] contributed by [StefH](https://github.com/StefH) -- [#374](https://github.com/WireMock-Net/WireMock.Net/pull/374) - WatchStaticMappingsInSubdirectories [feature] contributed by [StefH](https://github.com/StefH) - [#372](https://github.com/WireMock-Net/WireMock.Net/issues/372) - Reset in WireMock admin API not working fine. [feature] # 1.0.36.0 (26 October 2019) -- [#360](https://github.com/WireMock-Net/WireMock.Net/pull/360) - Add support for Faults [feature] contributed by [StefH](https://github.com/StefH) - [#343](https://github.com/WireMock-Net/WireMock.Net/issues/343) - Feature: Please provide support for Bad responses. [feature] -# 1.0.35.0 (25 October 2019) -- [#367](https://github.com/WireMock-Net/WireMock.Net/pull/367) - No symbol NuGets [feature] contributed by [StefH](https://github.com/StefH) -- [#368](https://github.com/WireMock-Net/WireMock.Net/pull/368) - Remove Obsolete annotations [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.34.0 (22 October 2019) -- [#354](https://github.com/WireMock-Net/WireMock.Net/pull/354) - AllowBodyForAllHttpMethods [bug, feature] contributed by [StefH](https://github.com/StefH) -- [#365](https://github.com/WireMock-Net/WireMock.Net/pull/365) - Bump Microsoft.AspNetCore.All from 2.0.8 to 2.0.9 in /examples/WireMock.Net.WebApplication [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot) -- [#366](https://github.com/WireMock-Net/WireMock.Net/pull/366) - Update ObsoleteAnnotations [feature] contributed by [StefH](https://github.com/StefH) - [#352](https://github.com/WireMock-Net/WireMock.Net/issues/352) - DELETE request drops the body [feature] # 1.0.33.0 (12 October 2019) -- [#311](https://github.com/WireMock-Net/WireMock.Net/pull/311) - fix jsonpath matcher [bug] contributed by [StefH](https://github.com/StefH) -- [#324](https://github.com/WireMock-Net/WireMock.Net/pull/324) - Add CSharpCodeMatcher [feature] contributed by [StefH](https://github.com/StefH) -- [#353](https://github.com/WireMock-Net/WireMock.Net/pull/353) - Fixed failing admin requests when content type includes a charset (based on idea from Paul Roub) [bug] contributed by [StefH](https://github.com/StefH) -- [#355](https://github.com/WireMock-Net/WireMock.Net/pull/355) - Add Try-Catch to the event LogEntriesChanged [feature] contributed by [StefH](https://github.com/StefH) -- [#357](https://github.com/WireMock-Net/WireMock.Net/pull/357) - Add Proxy Setting for: SaveMappingForStatusCodePattern to only save the mapping when the status code matches the pattern [feature] contributed by [StefH](https://github.com/StefH) -- [#358](https://github.com/WireMock-Net/WireMock.Net/pull/358) - Fix JsonMatcher (parsing DateTimeOffset) contributed by [StefH](https://github.com/StefH) - [#306](https://github.com/WireMock-Net/WireMock.Net/issues/306) - Writing to the response body is invalid for responses with status code 204 [bug] - [#307](https://github.com/WireMock-Net/WireMock.Net/issues/307) - JsonPathMatcher always convert to JArray before matching [bug] - [#329](https://github.com/WireMock-Net/WireMock.Net/issues/329) - Feature: Add support for CSharpCodeMatcher [feature] @@ -904,150 +425,67 @@ - [#356](https://github.com/WireMock-Net/WireMock.Net/issues/356) - JsonMatcher not working when JSON contains a DateTimeOffset [bug] # 1.0.32.0 (20 September 2019) -- [#348](https://github.com/WireMock-Net/WireMock.Net/pull/348) - When posting new mapping, use DateParseHandling.None [bug] contributed by [StefH](https://github.com/StefH) - [#347](https://github.com/WireMock-Net/WireMock.Net/issues/347) - Query string match on DateTimeOffset is not working [bug] # 1.0.31.0 (19 September 2019) -- [#334](https://github.com/WireMock-Net/WireMock.Net/pull/334) - Fix issues with Proxy mode and Binary Request Bodies [bug] contributed by [andi0b](https://github.com/andi0b) -- [#338](https://github.com/WireMock-Net/WireMock.Net/pull/338) - Fix ContentType with parameters in Proxy Mode [bug] contributed by [StefH](https://github.com/StefH) -- [#339](https://github.com/WireMock-Net/WireMock.Net/pull/339) - Fix ConcurrentObservableCollection [bug] contributed by [StefH](https://github.com/StefH) -- [#345](https://github.com/WireMock-Net/WireMock.Net/pull/345) - Fix CompareTo in RequestMatchResult [bug] contributed by [StefH](https://github.com/StefH) -- [#346](https://github.com/WireMock-Net/WireMock.Net/pull/346) - Fix recorded requests skipped by request logger contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak) - [#337](https://github.com/WireMock-Net/WireMock.Net/issues/337) - Proxy Missing header Content-Type - tried with Recording [bug] - [#344](https://github.com/WireMock-Net/WireMock.Net/issues/344) - Mapping adding order matters for multiple mappings? [bug] # 1.0.29.0 (29 August 2019) -- [#328](https://github.com/WireMock-Net/WireMock.Net/pull/328) - Fix LogRequest : Index Out Of Bounds [bug] contributed by [StefH](https://github.com/StefH) -- [#331](https://github.com/WireMock-Net/WireMock.Net/pull/331) - Fix: Collection was modified exception [bug] contributed by [theramis](https://github.com/theramis) -- [#333](https://github.com/WireMock-Net/WireMock.Net/pull/333) - JsonMatcher support IgnoreCase [feature] contributed by [StefH](https://github.com/StefH) - [#332](https://github.com/WireMock-Net/WireMock.Net/issues/332) - Case sensitive true is ignored for JsonMatcher [feature] # 1.0.28.0 (20 August 2019) -- [#309](https://github.com/WireMock-Net/WireMock.Net/pull/309) - Fix LogEntries: collection was modified exception [bug] contributed by [StefH](https://github.com/StefH) -- [#314](https://github.com/WireMock-Net/WireMock.Net/pull/314) - RequestLogExpirationDuration : use DateTime.UtcNow [bug] contributed by [StefH](https://github.com/StefH) -- [#316](https://github.com/WireMock-Net/WireMock.Net/pull/316) - Handles case where parameter value contains == [feature] contributed by [lobsteropteryx](https://github.com/lobsteropteryx) -- [#317](https://github.com/WireMock-Net/WireMock.Net/pull/317) - Make SaveMapping and SaveMappingToFile settings independent. [feature] contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak) -- [#319](https://github.com/WireMock-Net/WireMock.Net/pull/319) - Add blacklist for Request Cookies. contributed by [vitaliydavydiak](https://github.com/vitaliydavydiak) -- [#322](https://github.com/WireMock-Net/WireMock.Net/pull/322) - Fix MappingMatcher in case of an exception in LinqMatcher. [bug] contributed by [StefH](https://github.com/StefH) -- [#323](https://github.com/WireMock-Net/WireMock.Net/pull/323) - Refactor MappingConverter & MatcherMapper [refactor] contributed by [StefH](https://github.com/StefH) -- [#326](https://github.com/WireMock-Net/WireMock.Net/pull/326) - Fix Parsing Guid in PUT Mapping [bug] contributed by [StefH](https://github.com/StefH) - [#252](https://github.com/WireMock-Net/WireMock.Net/issues/252) - Proxy with Transform - [#308](https://github.com/WireMock-Net/WireMock.Net/issues/308) - __admin/requests - "Collection was modified" exception [bug] - [#313](https://github.com/WireMock-Net/WireMock.Net/issues/313) - RequestLogExpirationDuration - bug [bug] - [#325](https://github.com/WireMock-Net/WireMock.Net/issues/325) - Admin API: PUT Mapping, FormatException because of wrong parsing of the Query [bug] -# 1.0.25.0 (23 July 2019) -- [#304](https://github.com/WireMock-Net/WireMock.Net/pull/304) - Support WithBody with multiple matchers [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.24.0 (22 July 2019) -- [#302](https://github.com/WireMock-Net/WireMock.Net/pull/302) - Fixed bug 301 by not setting BodyAsFile to null after first use [bug] contributed by [rwwilden](https://github.com/rwwilden) - [#301](https://github.com/WireMock-Net/WireMock.Net/issues/301) - Error thrown when calling mocked endpoint second time when using file-based response body [bug] -# 1.0.23.0 (16 July 2019) -- [#298](https://github.com/WireMock-Net/WireMock.Net/pull/298) - MappingModels [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.22.0 (15 July 2019) -- [#297](https://github.com/WireMock-Net/WireMock.Net/pull/297) - FixNullRef (#295) contributed by [StefH](https://github.com/StefH) - [#295](https://github.com/WireMock-Net/WireMock.Net/issues/295) - NullRef in 1.0.21 [bug] # 1.0.21.0 (03 July 2019) -- [#286](https://github.com/WireMock-Net/WireMock.Net/pull/286) - Handlebars Extension [feature] contributed by [StefH](https://github.com/StefH) -- [#293](https://github.com/WireMock-Net/WireMock.Net/pull/293) - workaround for AppContext.BaseDirectory being null on some platforms contributed by [eli-darkly](https://github.com/eli-darkly) -- [#294](https://github.com/WireMock-Net/WireMock.Net/pull/294) - don't strip request body if we don't recognize the request method contributed by [eli-darkly](https://github.com/eli-darkly) - [#289](https://github.com/WireMock-Net/WireMock.Net/issues/289) - Bug: When WatchStaticMappings=true throws exceptions on updating the mapping files [bug] - [#290](https://github.com/WireMock-Net/WireMock.Net/issues/290) - Request body is dropped if verb is REPORT [bug] - [#292](https://github.com/WireMock-Net/WireMock.Net/issues/292) - Can't start server in Xamarin Android [bug] -# 1.0.20.0 (17 June 2019) -- [#284](https://github.com/WireMock-Net/WireMock.Net/pull/284) - Add SaveToFile in the mapping [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.19.0 (15 June 2019) - [#283](https://github.com/WireMock-Net/WireMock.Net/issues/283) - Support equal-sign in query [bug] -# 1.0.18.0 (10 June 2019) -- [#282](https://github.com/WireMock-Net/WireMock.Net/pull/282) - WireMock.Net.Standalone : Add --WireMockLogger commandline argument [feature] contributed by [StefH](https://github.com/StefH) - -# 1.0.17.0 (05 June 2019) -- [#278](https://github.com/WireMock-Net/WireMock.Net/pull/278) - Add support for HandleBars File (to read a file) [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.16.0 (16 May 2019) -- [#274](https://github.com/WireMock-Net/WireMock.Net/pull/274) - Sign Assembly [feature] contributed by [StefH](https://github.com/StefH) - [#160](https://github.com/WireMock-Net/WireMock.Net/issues/160) - Feature: Sign 'WireMock.Net' [feature] - [#267](https://github.com/WireMock-Net/WireMock.Net/issues/267) - Assembly does not have strong name -# 1.0.15.0 (04 May 2019) -- [#271](https://github.com/WireMock-Net/WireMock.Net/pull/271) - Support Dynamic response files using Handlebars templating [bug, feature] contributed by [StefH](https://github.com/StefH) -- [#273](https://github.com/WireMock-Net/WireMock.Net/pull/273) - Dynamic response handlebars templating (2) [bug, feature] contributed by [StefH](https://github.com/StefH) - -# 1.0.14.0 (20 April 2019) -- [#269](https://github.com/WireMock-Net/WireMock.Net/pull/269) - Add JmesPath matcher [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.13.0 (11 April 2019) -- [#266](https://github.com/WireMock-Net/WireMock.Net/pull/266) - [265] Add file upload to allow mocking of file operations contributed by [JackCreativeCrew](https://github.com/JackCreativeCrew) - [#265](https://github.com/WireMock-Net/WireMock.Net/issues/265) - File Upload [feature] # 1.0.12.0 (05 April 2019) -- [#264](https://github.com/WireMock-Net/WireMock.Net/pull/264) - Proxy : also save multipart as string in mapping file contributed by [StefH](https://github.com/StefH) - [#263](https://github.com/WireMock-Net/WireMock.Net/issues/263) - Content-Type multipart/form-data is not serialized in proxy and recording mode [bug] -# 1.0.11.0 (30 March 2019) -- [#261](https://github.com/WireMock-Net/WireMock.Net/pull/261) - Fix BodyAsJson transform bug in ResponseMessageTransformer contributed by [ghost](https://github.com/ghost) -- [#262](https://github.com/WireMock-Net/WireMock.Net/pull/262) - Add ProvideResponse_WithJsonBodyAndTransform test contributed by [ghost](https://github.com/ghost) - -# 1.0.10.0 (27 March 2019) -- [#260](https://github.com/WireMock-Net/WireMock.Net/pull/260) - Fix Response.Delay property serialization [bug] contributed by [StefH](https://github.com/StefH) - # 1.0.9.0 (25 March 2019) -- [#256](https://github.com/WireMock-Net/WireMock.Net/pull/256) - Fixed Multi Param Match logic contributed by [StefH](https://github.com/StefH) - [#255](https://github.com/WireMock-Net/WireMock.Net/issues/255) - ExactMatcher with array pattern not working? [bug] # 1.0.8.0 (12 March 2019) -- [#254](https://github.com/WireMock-Net/WireMock.Net/pull/254) - RequestMessageParamMatcher supports Ignore Case for the key [feature] contributed by [StefH](https://github.com/StefH) - [#253](https://github.com/WireMock-Net/WireMock.Net/issues/253) - Request Path and query parameter keys are case-sensitive # 1.0.7.0 (19 January 2019) -- [#244](https://github.com/WireMock-Net/WireMock.Net/pull/244) - Fix BodyAsFile to also allow relative paths [feature] contributed by [StefH](https://github.com/StefH) - [#240](https://github.com/WireMock-Net/WireMock.Net/issues/240) - How to submit mappings for multiple request, responses [feature] - [#243](https://github.com/WireMock-Net/WireMock.Net/issues/243) - Not able to read response from file [bug] # 1.0.6.1 (10 January 2019) -- [#247](https://github.com/WireMock-Net/WireMock.Net/pull/247) - Issue 225 improve logging in example for wire mock as windows service contributed by [paulssn](https://github.com/paulssn) -- [#249](https://github.com/WireMock-Net/WireMock.Net/pull/249) - Fixed "Content-Type multipart/form-data" [bug] contributed by [StefH](https://github.com/StefH) - [#225](https://github.com/WireMock-Net/WireMock.Net/issues/225) - Feature: Improve logging in example for WireMock as Windows Service [feature] - [#248](https://github.com/WireMock-Net/WireMock.Net/issues/248) - Content-Type multipart/form-data is not seen as byte[] anymore -# 1.0.6 (15 December 2018) -- [#242](https://github.com/WireMock-Net/WireMock.Net/pull/242) - Post multiple Mappings [feature] contributed by [StefH](https://github.com/StefH) - -# 1.0.5 (07 December 2018) -- [#236](https://github.com/WireMock-Net/WireMock.Net/pull/236) - Add Random Regex (using Fare) [feature] contributed by [StefH](https://github.com/StefH) - # 1.0.4.21 (30 November 2018) -- [#221](https://github.com/WireMock-Net/WireMock.Net/pull/221) - Update dependencies [feature] contributed by [StefH](https://github.com/StefH) -- [#230](https://github.com/WireMock-Net/WireMock.Net/pull/230) - Add HandleBars Random functionality (#219) [feature] contributed by [StefH](https://github.com/StefH) -- [#231](https://github.com/WireMock-Net/WireMock.Net/pull/231) - Use RandomDataGenerator.Net 1.0.3.0 contributed by [StefH](https://github.com/StefH) -- [#232](https://github.com/WireMock-Net/WireMock.Net/pull/232) - Add SonarLint checks [feature] contributed by [StefH](https://github.com/StefH) -- [#233](https://github.com/WireMock-Net/WireMock.Net/pull/233) - RandomDataGenerator.Net 1.0.4 [feature] contributed by [StefH](https://github.com/StefH) -- [#235](https://github.com/WireMock-Net/WireMock.Net/pull/235) - Check aggregate exception during startup [bug] contributed by [StefH](https://github.com/StefH) - [#219](https://github.com/WireMock-Net/WireMock.Net/issues/219) - Feature: random value helper [feature] - [#234](https://github.com/WireMock-Net/WireMock.Net/issues/234) - Timeout Exception on VSTS Test Platform (Azure DevOps), with private build agent # 1.0.4.20 (07 November 2018) -- [#222](https://github.com/WireMock-Net/WireMock.Net/pull/222) - Codecov contributed by [StefH](https://github.com/StefH) -- [#224](https://github.com/WireMock-Net/WireMock.Net/pull/224) - Fixed issue 223: Example for WireMock as Windows Service throws Exception because of WireMockConsoleLogger contributed by [paulssn](https://github.com/paulssn) -- [#228](https://github.com/WireMock-Net/WireMock.Net/pull/228) - Fixed logic for IsRestrictedResponseHeader [bug] contributed by [StefH](https://github.com/StefH) - [#223](https://github.com/WireMock-Net/WireMock.Net/issues/223) - Bug: Example for WireMock as Windows Service throws Exception because of WireMockConsoleLogger [bug] -# 1.0.4.19 (31 October 2018) -- [#220](https://github.com/WireMock-Net/WireMock.Net/pull/220) - Update SimpleCommandLineParser to handle arguments with key and value contributed by [StefH](https://github.com/StefH) - # 1.0.4.18 (27 October 2018) -- [#207](https://github.com/WireMock-Net/WireMock.Net/pull/207) - Rewrite some unit-integration-tests to unit-tests (#206) contributed by [StefH](https://github.com/StefH) -- [#208](https://github.com/WireMock-Net/WireMock.Net/pull/208) - Refactor contributed by [StefH](https://github.com/StefH) -- [#209](https://github.com/WireMock-Net/WireMock.Net/pull/209) - NET Core 2.1 + support for Service Fabric commandline parameters contributed by [StefH](https://github.com/StefH) -- [#212](https://github.com/WireMock-Net/WireMock.Net/pull/212) - Update BodyParser logic contributed by [StefH](https://github.com/StefH) -- [#217](https://github.com/WireMock-Net/WireMock.Net/pull/217) - Enable Source Link contributed by [kashifsoofi](https://github.com/kashifsoofi) -- [#218](https://github.com/WireMock-Net/WireMock.Net/pull/218) - remove appveyor contributed by [StefH](https://github.com/StefH) - [#107](https://github.com/WireMock-Net/WireMock.Net/issues/107) - Feature: increase code coverage [feature] - [#161](https://github.com/WireMock-Net/WireMock.Net/issues/161) - Feature: Implement SourceLink [feature] - [#179](https://github.com/WireMock-Net/WireMock.Net/issues/179) - BodyAsFile .json files interferes with WatchStaticMappings @@ -1059,43 +497,31 @@ - [#215](https://github.com/WireMock-Net/WireMock.Net/issues/215) - Issue: upgrade Microsoft.AspNetCore / Microsoft.AspNetCore.All to 2.1.5 # 1.0.4.17 (22 September 2018) -- [#203](https://github.com/WireMock-Net/WireMock.Net/pull/203) - Set up CI with Azure Pipelines contributed by [azure-pipelines[bot]](https://github.com/apps/azure-pipelines) -- [#204](https://github.com/WireMock-Net/WireMock.Net/pull/204) - Lower priority from Proxy mappings in favor of Admin Mappings [feature] contributed by [StefH](https://github.com/StefH) - [#200](https://github.com/WireMock-Net/WireMock.Net/issues/200) - Issue: Incorrect port matching - [#205](https://github.com/WireMock-Net/WireMock.Net/issues/205) - Issue: DELETE method is proxied as lowercase [bug] # 1.0.4.16 (11 September 2018) -- [#202](https://github.com/WireMock-Net/WireMock.Net/pull/202) - Update handlebars code to support Regex.Match (#201) contributed by [StefH](https://github.com/StefH) - [#201](https://github.com/WireMock-Net/WireMock.Net/issues/201) - Question : Extracting text from a request.body that is not json # 1.0.4.15 (04 September 2018) -- [#199](https://github.com/WireMock-Net/WireMock.Net/pull/199) - Fix for .WithBody(Func<RequestMessage, string>...) contributed by [StefH](https://github.com/StefH) - [#198](https://github.com/WireMock-Net/WireMock.Net/issues/198) - Issue : creating response using .WithBody(Func<RequestMessage, string>...) and .WithStatusCode [bug] # 1.0.4.14 (02 September 2018) -- [#197](https://github.com/WireMock-Net/WireMock.Net/pull/197) - Set IsStarted = true in a IApplicationLifetime.ApplicationStarted listener [bug] contributed by [davide-romanini](https://github.com/davide-romanini) - [#196](https://github.com/WireMock-Net/WireMock.Net/issues/196) - Issue: AspNetCoreSelfHost.IsStarted set before the server actually started for real [bug] # 1.0.4.13 (31 August 2018) -- [#195](https://github.com/WireMock-Net/WireMock.Net/pull/195) - Add LinqMatcher contributed by [StefH](https://github.com/StefH) - [#192](https://github.com/WireMock-Net/WireMock.Net/issues/192) - Cannot upgrade from 1.0.4.10 to 1.0.4.12 without upgrading to .net core 2.1 [bug] # 1.0.4.12 (23 August 2018) -- [#190](https://github.com/WireMock-Net/WireMock.Net/pull/190) - Fix ResponseMessageTransformer (#188) contributed by [StefH](https://github.com/StefH) -- [#191](https://github.com/WireMock-Net/WireMock.Net/pull/191) - Fix ignore case logic for header-name and cookie-name contributed by [StefH](https://github.com/StefH) - [#188](https://github.com/WireMock-Net/WireMock.Net/issues/188) - Bug: ResponseMessageTransformer : - [#189](https://github.com/WireMock-Net/WireMock.Net/issues/189) - Issue: Case of header key/name not ignored in RequestBuilder when ignoreCase == true # 1.0.4.11 (20 August 2018) -- [#183](https://github.com/WireMock-Net/WireMock.Net/pull/183) - Set Content-Type header for PutMappingAsync in the client contributed by [seanamosw](https://github.com/seanamosw) -- [#185](https://github.com/WireMock-Net/WireMock.Net/pull/185) - Support Microsoft.AspNetCore for net 4.6.1 and up [feature] contributed by [StefH](https://github.com/StefH) -- [#186](https://github.com/WireMock-Net/WireMock.Net/pull/186) - ContentType "application/vnd.api+json" is not recognized as json contributed by [steveland83](https://github.com/steveland83) - [#182](https://github.com/WireMock-Net/WireMock.Net/issues/182) - Bug: IFluentMockServerAdmin::PutMappingAsync does not set Content-Type - [#184](https://github.com/WireMock-Net/WireMock.Net/issues/184) - Bug: Fix AppVeyor PR build process - [#187](https://github.com/WireMock-Net/WireMock.Net/issues/187) - Bug: Admin GetRequestAsync does not populate request body for JsonApi ("application/vnd.api+json") content # 1.0.4.10 (14 August 2018) -- [#180](https://github.com/WireMock-Net/WireMock.Net/pull/180) - Add IFileSystemHandler to support Azure for StaticMapping location contributed by [StefH](https://github.com/StefH) - [#173](https://github.com/WireMock-Net/WireMock.Net/issues/173) - Feature: Mapping files lost when restarting an Azure app service [feature] # 1.0.4.9 (08 August 2018) @@ -1106,21 +532,15 @@ - [#177](https://github.com/WireMock-Net/WireMock.Net/issues/177) - Feature: Skip invalid static mapping files [feature] # 1.0.4.8 (23 July 2018) -- [#170](https://github.com/WireMock-Net/WireMock.Net/pull/170) - Support json path in the response contributed by [StefH](https://github.com/StefH) - [#167](https://github.com/WireMock-Net/WireMock.Net/issues/167) - Feature: Support for JsonPath in the response (with HandleBars) [feature] # 1.0.4.7 (19 July 2018) -- [#169](https://github.com/WireMock-Net/WireMock.Net/pull/169) - Fix for Restricted Response headers [bug] contributed by [StefH](https://github.com/StefH) - [#148](https://github.com/WireMock-Net/WireMock.Net/issues/148) - Question: proxy passthrough when no match? # 1.0.4.6 (18 July 2018) -- [#168](https://github.com/WireMock-Net/WireMock.Net/pull/168) - Expose scenario states [feature] contributed by [StefH](https://github.com/StefH) - [#163](https://github.com/WireMock-Net/WireMock.Net/issues/163) - Feature: Expose scenario states # 1.0.4.5 (17 July 2018) -- [#164](https://github.com/WireMock-Net/WireMock.Net/pull/164) - Support running WireMock.Net as a sub-app in IIS [feature] contributed by [StefH](https://github.com/StefH) -- [#165](https://github.com/WireMock-Net/WireMock.Net/pull/165) - Add SonarCloud contributed by [StefH](https://github.com/StefH) -- [#166](https://github.com/WireMock-Net/WireMock.Net/pull/166) - Fix Sonar issues contributed by [StefH](https://github.com/StefH) - [#120](https://github.com/WireMock-Net/WireMock.Net/issues/120) - Question: JsonPathMatcher - not matching? Correct syntax? - [#123](https://github.com/WireMock-Net/WireMock.Net/issues/123) - Fix for DateTime Header causing null value in ResponseBuilder @@ -1131,7 +551,6 @@ - [#159](https://github.com/WireMock-Net/WireMock.Net/issues/159) - Bug: IRequestBuilder.WithParam broken for key-only matching [bug] # 1.0.4.2 (26 June 2018) -- [#157](https://github.com/WireMock-Net/WireMock.Net/pull/157) - Support for string and object in JsonMatcher. [feature] contributed by [StefH](https://github.com/StefH) - [#150](https://github.com/WireMock-Net/WireMock.Net/issues/150) - Add support for .NET Core 2.1 (.NET Core 2.0 will reach end of life on september 2018) - [#154](https://github.com/WireMock-Net/WireMock.Net/issues/154) - Feature: support BodyAsJson for Request in static mapping files. [feature] @@ -1144,16 +563,10 @@ - [#151](https://github.com/WireMock-Net/WireMock.Net/issues/151) - Feature: Add logging of incoming request and body for tracability # 1.0.3.20 (29 May 2018) -- [#147](https://github.com/WireMock-Net/WireMock.Net/pull/147) - Revert PortUtil.cs changes contributed by [StefH](https://github.com/StefH) - [#129](https://github.com/WireMock-Net/WireMock.Net/issues/129) - Random test failures between WireMock.Net 1.0.3.1 and 1.0.3.2 - [#146](https://github.com/WireMock-Net/WireMock.Net/issues/146) - Hang possibly due to Windows firewall prompt -# 1.0.3.19 (28 May 2018) -- [#144](https://github.com/WireMock-Net/WireMock.Net/pull/144) - Fix ConcurrentDictionary (#129) contributed by [StefH](https://github.com/StefH) -- [#145](https://github.com/WireMock-Net/WireMock.Net/pull/145) - Cancellation token not passed to server instance in .NET Core 2 [bug] contributed by [Bob11327](https://github.com/Bob11327) - # 1.0.3.18 (25 May 2018) -- [#142](https://github.com/WireMock-Net/WireMock.Net/pull/142) - Allow all headers to be set as Response headers contributed by [StefH](https://github.com/StefH) - [#122](https://github.com/WireMock-Net/WireMock.Net/issues/122) - WireMock.Net not responding in unit tests - same works in console application - [#126](https://github.com/WireMock-Net/WireMock.Net/issues/126) - Question: UsingHead always returns 0 for Content-Length header even when explicitly specified - [#132](https://github.com/WireMock-Net/WireMock.Net/issues/132) - LogEntries not being recorded on subsequent tests @@ -1162,19 +575,12 @@ - [#139](https://github.com/WireMock-Net/WireMock.Net/issues/139) - Wiki link https://github.com/StefH/WireMock.Net/wiki/Record-(via-proxy)-and-Save is dead # 1.0.3.17 (16 May 2018) -- [#134](https://github.com/WireMock-Net/WireMock.Net/pull/134) - Stef negate matcher contributed by [alastairtree](https://github.com/alastairtree) -- [#138](https://github.com/WireMock-Net/WireMock.Net/pull/138) - Added Negate matcher logic contributed by [StefH](https://github.com/StefH) - [#130](https://github.com/WireMock-Net/WireMock.Net/issues/130) - ... # 1.0.3.16 (17 April 2018) -- [#121](https://github.com/WireMock-Net/WireMock.Net/pull/121) - Fix for issue #118 [bug] contributed by [raghavendrabankapur](https://github.com/raghavendrabankapur) -- [#125](https://github.com/WireMock-Net/WireMock.Net/pull/125) - Change listen from loopback to any ip address for dotnetcore2.0 apps contributed by [SubjectiveReality](https://github.com/SubjectiveReality) - [#118](https://github.com/WireMock-Net/WireMock.Net/issues/118) - Not reading the response from a file when mappings are placed in json file - [#124](https://github.com/WireMock-Net/WireMock.Net/issues/124) - Issue: Unable to get host to listen on ips other than 127.0.0.1 using StandAloneApp -# 1.0.3.15 (05 April 2018) -- [#117](https://github.com/WireMock-Net/WireMock.Net/pull/117) - Respect start timeout setting and expose exception from server startup contributed by [evanlwj](https://github.com/evanlwj) - # 1.0.3.14 (01 April 2018) - [#111](https://github.com/WireMock-Net/WireMock.Net/issues/111) - Question: Adding wiki documentation on how to use WireMock.Net.WebApplication project - [#112](https://github.com/WireMock-Net/WireMock.Net/issues/112) - Question: Request.Create().WithBody() not able to match with custom class which implements IMatcher @@ -1194,25 +600,18 @@ - [#106](https://github.com/WireMock-Net/WireMock.Net/issues/106) - Issue: Params does not work, when there are multiple values for a key # 1.0.3.4 (04 March 2018) -- [#95](https://github.com/WireMock-Net/WireMock.Net/pull/95) - Unittest fix contributed by [StefH](https://github.com/StefH) -- [#96](https://github.com/WireMock-Net/WireMock.Net/pull/96) - Replace log4net by custom logger (#94) contributed by [StefH](https://github.com/StefH) -- [#101](https://github.com/WireMock-Net/WireMock.Net/pull/101) - ICallbackResponseBuilder + added more unit-tests [bug] contributed by [StefH](https://github.com/StefH) -- [#102](https://github.com/WireMock-Net/WireMock.Net/pull/102) - Feature: add WithBody(req => dostuff) style callback [feature] contributed by [alastairtree](https://github.com/alastairtree) - [#66](https://github.com/WireMock-Net/WireMock.Net/issues/66) - Interested in callbacks? - [#93](https://github.com/WireMock-Net/WireMock.Net/issues/93) - Bug: FluentMockServer IsStarted after calling Start() - [#94](https://github.com/WireMock-Net/WireMock.Net/issues/94) - Issue: Introduced dependency on log4net - [#98](https://github.com/WireMock-Net/WireMock.Net/issues/98) - IBodyResponseBuilder.WithBody* should receive the request as a parameter # 1.0.3.3 (24 February 2018) -- [#92](https://github.com/WireMock-Net/WireMock.Net/pull/92) - Json fixes (#91) contributed by [StefH](https://github.com/StefH) - [#91](https://github.com/WireMock-Net/WireMock.Net/issues/91) - Bug: WireMock.Net is not matching application/json http requests using JSONPathMatcher [bug] # 1.0.3.2 (14 February 2018) -- [#90](https://github.com/WireMock-Net/WireMock.Net/pull/90) - Concurrent issue (#88) contributed by [StefH](https://github.com/StefH) - [#88](https://github.com/WireMock-Net/WireMock.Net/issues/88) - Bug: Standalone server throws 500 error when receiving concurrent requests [bug] # 1.0.3.1 (14 February 2018) -- [#89](https://github.com/WireMock-Net/WireMock.Net/pull/89) - Add log4net logging contributed by [StefH](https://github.com/StefH) - [#87](https://github.com/WireMock-Net/WireMock.Net/issues/87) - Feature: Add logging # 1.0.3.0 (05 February 2018) @@ -1224,13 +623,10 @@ - [#86](https://github.com/WireMock-Net/WireMock.Net/issues/86) - Feature : Add FileSystemWatcher logic for watching static mapping files [feature] # 1.0.2.13 (23 January 2018) -- [#79](https://github.com/WireMock-Net/WireMock.Net/pull/79) - Fix missed content headers contributed by [volodymyr-fed](https://github.com/volodymyr-fed) - [#57](https://github.com/WireMock-Net/WireMock.Net/issues/57) - ProxyAndRecord does not save query-parameters, headers and body [bug] - [#78](https://github.com/WireMock-Net/WireMock.Net/issues/78) - WireMock not working when attempting to access from anything other than localhost. # 1.0.2.12 (16 January 2018) -- [#75](https://github.com/WireMock-Net/WireMock.Net/pull/75) - Add WireMock.Net.WebApplication example contributed by [StefH](https://github.com/StefH) -- [#77](https://github.com/WireMock-Net/WireMock.Net/pull/77) - Fixed issue #76 contributed by [StefH](https://github.com/StefH) - [#73](https://github.com/WireMock-Net/WireMock.Net/issues/73) - Updated mapping is not being picked and responded with the response - [#76](https://github.com/WireMock-Net/WireMock.Net/issues/76) - Bug: IFluentMockServerAdmin is missing content-type for some POST/PUT calls @@ -1241,42 +637,27 @@ - [#70](https://github.com/WireMock-Net/WireMock.Net/issues/70) - Proxy/Intercept pattern is throwing a keep alive header error with net461 # 1.0.2.9 (07 December 2017) -- [#71](https://github.com/WireMock-Net/WireMock.Net/pull/71) - Fixed restricted headers on response contributed by [StefH](https://github.com/StefH) - [#69](https://github.com/WireMock-Net/WireMock.Net/issues/69) - Instructions are incorrect (?) # 1.0.2.8 (23 November 2017) -- [#65](https://github.com/WireMock-Net/WireMock.Net/pull/65) - bug: Fix admin api client definition returning the wrong types contributed by [alastairtree](https://github.com/alastairtree) -- [#67](https://github.com/WireMock-Net/WireMock.Net/pull/67) - bug: fix supporting the Patch method and logging the body contributed by [alastairtree](https://github.com/alastairtree) - [#64](https://github.com/WireMock-Net/WireMock.Net/issues/64) - Pull Requests do not trigger test + codecoverage ? # 1.0.2.7 (18 November 2017) -- [#62](https://github.com/WireMock-Net/WireMock.Net/pull/62) - Add the Host, Protocol, Port and Origin to the Request message so they can be used in templating contributed by [alastairtree](https://github.com/alastairtree) -- [#63](https://github.com/WireMock-Net/WireMock.Net/pull/63) - Fix issue with concurrent logging contributed by [volodymyr-fed](https://github.com/volodymyr-fed) - [#27](https://github.com/WireMock-Net/WireMock.Net/issues/27) - New feature: Record and Save - [#42](https://github.com/WireMock-Net/WireMock.Net/issues/42) - Enhancement - Save/load request logs to/from disk [feature] - [#53](https://github.com/WireMock-Net/WireMock.Net/issues/53) - New feature request: Access to Owin pipeline # 1.0.2.6 (30 October 2017) -- [#59](https://github.com/WireMock-Net/WireMock.Net/pull/59) - Add ability to provide multiple values for headers in response contributed by [Dreamescaper](https://github.com/Dreamescaper) -- [#60](https://github.com/WireMock-Net/WireMock.Net/pull/60) - Fix proxy headers handling contributed by [Dreamescaper](https://github.com/Dreamescaper) - [#54](https://github.com/WireMock-Net/WireMock.Net/issues/54) - Proxy for AWS: Error unmarshalling response back from AWS [bug] - [#56](https://github.com/WireMock-Net/WireMock.Net/issues/56) - WithBodyFromFile Support [feature] - [#58](https://github.com/WireMock-Net/WireMock.Net/issues/58) - Multiple headers with same name [feature] # 1.0.2.5 (24 October 2017) -- [#55](https://github.com/WireMock-Net/WireMock.Net/pull/55) - Fix the problem with headers passthrough [bug] contributed by [dmtrrk](https://github.com/dmtrrk) - [#44](https://github.com/WireMock-Net/WireMock.Net/issues/44) - Bug: Server not listening after Start() returns (on macOS) [bug] - [#48](https://github.com/WireMock-Net/WireMock.Net/issues/48) - Stateful support [feature] - [#52](https://github.com/WireMock-Net/WireMock.Net/issues/52) - SimMetrics.NET error when trying to install NuGet Package # 1.0.2.4 (10 October 2017) -- [#32](https://github.com/WireMock-Net/WireMock.Net/pull/32) - [Feature] Add support for client certificate password and test with real services that require client certificate auth [feature] contributed by [phillee007](https://github.com/phillee007) -- [#35](https://github.com/WireMock-Net/WireMock.Net/pull/35) - Revert changes that were made by mistake in prior PR contributed by [phillee007](https://github.com/phillee007) -- [#39](https://github.com/WireMock-Net/WireMock.Net/pull/39) - Listen on http://*:9090 contributed by [StefH](https://github.com/StefH) -- [#40](https://github.com/WireMock-Net/WireMock.Net/pull/40) - Expose more settings to stand-alone app contributed by [StefH](https://github.com/StefH) -- [#41](https://github.com/WireMock-Net/WireMock.Net/pull/41) - Dotnet 20 preview final [feature] contributed by [StefH](https://github.com/StefH) -- [#45](https://github.com/WireMock-Net/WireMock.Net/pull/45) - Add RequestLogExpirationDuration and MaxRequestLogCount (#43) contributed by [StefH](https://github.com/StefH) -- [#51](https://github.com/WireMock-Net/WireMock.Net/pull/51) - Observable logs contributed by [dmtrrk](https://github.com/dmtrrk) - [#15](https://github.com/WireMock-Net/WireMock.Net/issues/15) - New feature: Proxying [feature] - [#20](https://github.com/WireMock-Net/WireMock.Net/issues/20) - Add client certificate authentication [feature] - [#31](https://github.com/WireMock-Net/WireMock.Net/issues/31) - Feature request: Nuget package for standalone version [feature] @@ -1291,13 +672,11 @@ - [#30](https://github.com/WireMock-Net/WireMock.Net/issues/30) - [Feature] Disable partial mappings by default in standalone version [bug, feature] # 1.0.2.0 (05 May 2017) -- [#26](https://github.com/WireMock-Net/WireMock.Net/pull/26) - merge netstandard into main contributed by [StefH](https://github.com/StefH) - [#21](https://github.com/WireMock-Net/WireMock.Net/issues/21) - Admin static json mappings [feature] - [#23](https://github.com/WireMock-Net/WireMock.Net/issues/23) - Consider port to .Net Core - [#25](https://github.com/WireMock-Net/WireMock.Net/issues/25) - Upgrade to vs2017 [feature] # 1.0.1.2 (27 February 2017) -- [#24](https://github.com/WireMock-Net/WireMock.Net/pull/24) - Body Encoding contributed by [sbebrys](https://github.com/sbebrys) - [#8](https://github.com/WireMock-Net/WireMock.Net/issues/8) - admin rest api - [#22](https://github.com/WireMock-Net/WireMock.Net/issues/22) - Add basic-authentication for accessing admin-interface [feature] diff --git a/Directory.Build.props b/Directory.Build.props index 1fb52791..b171c96a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ - 1.8.0-prview-01 + 1.7.5-preview-02 WireMock.Net-Logo.png https://github.com/WireMock-Net/WireMock.Net Apache-2.0 diff --git a/Generate-ReleaseNotes.cmd b/Generate-ReleaseNotes.cmd index 2d1a3761..998b0d48 100644 --- a/Generate-ReleaseNotes.cmd +++ b/Generate-ReleaseNotes.cmd @@ -1,6 +1,6 @@ rem https://github.com/StefH/GitHubReleaseNotes -SET version=1.8.0-prview-01 +SET version=1.7.5-preview-02 GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN% diff --git a/PackageReleaseNotes.txt b/PackageReleaseNotes.txt index a6878fbb..7893c0bf 100644 --- a/PackageReleaseNotes.txt +++ b/PackageReleaseNotes.txt @@ -1,5 +1,6 @@ -# 1.7.4 (27 February 2025) -- #1256 Add ToArray() to ConcurrentObservableCollection [bug] -- #1254 FindLogEntries exception 'Destination array was not long enough' [bug] +# 1.7.5-preview-02 (03 April 2025) +- #1265 Fix construction of path in OpenApiParser [bug] +- #1269 Server-Sent Events [feature] +- #1264 OpenApiParser - Construction of path possibly incorrect [bug] The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md \ No newline at end of file diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings index 8d0a52ef..4e64aaeb 100644 --- a/WireMock.Net Solution.sln.DotSettings +++ b/WireMock.Net Solution.sln.DotSettings @@ -35,6 +35,7 @@ True True True + True True True True diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs index 6bc1fd47..13046fd9 100644 --- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs +++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs @@ -97,6 +97,46 @@ message HelloReply { fullName:String }"; + private static void RunSse() + { + var server = WireMockServer.Start(new WireMockServerSettings + { + Port = 9091, + StartAdminInterface = true, + Logger = new WireMockConsoleLogger() + }); + server + .WhenRequest(r => r + .UsingGet() + .WithPath("/sse") + ) + .ThenRespondWith(r => r + .WithHeader("Content-Type", "text/event-stream") + .WithHeader("Cache-Control", "no-cache") + .WithHeader("Connection", "keep-alive") + .WithSseBody(async (_, q) => + { + for (var i = 0; i < 5; i++) + { + q.Write("test " + i + "\r\n"); + await Task.Delay(5000); + } + + q.Close(); + }) + ); + + server + .WhenRequest(r => r + .UsingGet() + ) + .ThenRespondWith(r => r + .WithBody("normal") + ); + + System.Console.ReadKey(); + } + private static void RunOnLocal() { try @@ -136,6 +176,7 @@ message HelloReply { public static void Run() { + RunSse(); RunOnLocal(); var mappingBuilder = new MappingBuilder(); diff --git a/src/WireMock.Net.Abstractions/Models/IBlockingQueue.cs b/src/WireMock.Net.Abstractions/Models/IBlockingQueue.cs new file mode 100644 index 00000000..054dfa1a --- /dev/null +++ b/src/WireMock.Net.Abstractions/Models/IBlockingQueue.cs @@ -0,0 +1,30 @@ +// Copyright © WireMock.Net + +using System.Diagnostics.CodeAnalysis; + +namespace WireMock.Models; + +/// +/// A simple implementation for a Blocking Queue. +/// +/// Specifies the type of elements in the queue. +public interface IBlockingQueue +{ + /// + /// Writes an item to the queue and signals that an item is available. + /// + /// The item to be added to the queue. + void Write(T item); + + /// + /// Tries to read an item from the queue. Waits until an item is available or the timeout occurs. + /// + /// The item read from the queue, or default if the timeout occurs. + /// True if an item was successfully read; otherwise, false. + bool TryRead([NotNullWhen(true)] out T? item); + + /// + /// Closes the queue and signals all waiting threads. + /// + public void Close(); +} \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Models/IBodyData.cs b/src/WireMock.Net.Abstractions/Models/IBodyData.cs index 7c9f35e6..79b737d5 100644 --- a/src/WireMock.Net.Abstractions/Models/IBodyData.cs +++ b/src/WireMock.Net.Abstractions/Models/IBodyData.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using WireMock.Models; using WireMock.Types; @@ -71,7 +72,7 @@ public interface IBodyData Encoding? Encoding { get; set; } /// - /// Defines if this BodyData is the result of a dynamically created response-string. ( + /// Defines if this BodyData is the result of a dynamically created response-string. /// public string? IsFuncUsed { get; set; } @@ -86,4 +87,14 @@ public interface IBodyData /// public string? ProtoBufMessageType { get; set; } #endregion + + /// + /// Defines the queue to use for Server-Sent Events (string). + /// + public IBlockingQueue? SseStringQueue { get; set; } + + /// + /// Defines if the body is using Server-Sent Events (string). + /// + public Task? BodyAsSseStringTask { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Types/BodyType.cs b/src/WireMock.Net.Abstractions/Types/BodyType.cs index 38c14a9d..6894c369 100644 --- a/src/WireMock.Net.Abstractions/Types/BodyType.cs +++ b/src/WireMock.Net.Abstractions/Types/BodyType.cs @@ -45,5 +45,10 @@ public enum BodyType /// /// Body is a ProtoBuf Byte array /// - ProtoBuf + ProtoBuf, + + /// + /// Use Server-Sent Events (string) + /// + SseString } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj b/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj index 85cf222e..4b010184 100644 --- a/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj +++ b/src/WireMock.Net.Abstractions/WireMock.Net.Abstractions.csproj @@ -61,4 +61,11 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs index 8034318e..a819a1f9 100644 --- a/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs +++ b/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs @@ -1,6 +1,6 @@ // Copyright © WireMock.Net -#if NET46 || NETSTANDARD2_0 +#if NET46 || NET47 || NETSTANDARD2_0 using System.Collections.Generic; namespace WireMock.Net.OpenApiParser.Extensions; diff --git a/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs index efd4bfcd..02f7a49b 100644 --- a/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs +++ b/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs @@ -1,60 +1,53 @@ // Copyright © WireMock.Net using System.Linq; +using System.Text.Json; using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using WireMock.Net.OpenApiParser.Types; namespace WireMock.Net.OpenApiParser.Extensions; internal static class OpenApiSchemaExtensions { - /// - /// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger - /// - public static bool TryGetXNullable(this OpenApiSchema schema, out bool value) + public static bool TryGetXNullable(this IOpenApiSchema schema, out bool value) { value = false; - if (schema.Extensions.TryGetValue("x-nullable", out var e) && e is OpenApiBoolean openApiBoolean) + if (schema.Extensions != null && schema.Extensions.TryGetValue(OpenApiConstants.NullableExtension, out var nullExtRawValue) && nullExtRawValue is OpenApiAny { Node: { } jsonNode }) { - value = openApiBoolean.Value; + value = jsonNode.GetValueKind() == JsonValueKind.True; return true; } return false; } - public static SchemaType GetSchemaType(this OpenApiSchema? schema) + public static JsonSchemaType? GetSchemaType(this IOpenApiSchema? schema, out bool isNullable) { + isNullable = false; + if (schema == null) { - return SchemaType.Unknown; + return null; } if (schema.Type == null) { - if (schema.AllOf.Any() || schema.AnyOf.Any()) + if (schema.AllOf?.Any() == true || schema.AnyOf?.Any() == true) { - return SchemaType.Object; + return JsonSchemaType.Object; } } - return schema.Type switch - { - "object" => SchemaType.Object, - "array" => SchemaType.Array, - "integer" => SchemaType.Integer, - "number" => SchemaType.Number, - "boolean" => SchemaType.Boolean, - "string" => SchemaType.String, - "file" => SchemaType.File, - _ => SchemaType.Unknown - }; + isNullable = (schema.Type | JsonSchemaType.Null) == JsonSchemaType.Null || (schema.TryGetXNullable(out var xNullable) && xNullable); + + // Removes the Null flag from the schema.Type, ensuring the returned value represents a non-nullable type. + return schema.Type & ~JsonSchemaType.Null; } - public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema) + public static SchemaFormat GetSchemaFormat(this IOpenApiSchema? schema) { switch (schema?.Format) { diff --git a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs index b2aadb06..bd20f521 100644 --- a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs +++ b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using JetBrains.Annotations; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi.Reader; using Stef.Validation; using WireMock.Net.OpenApiParser.Settings; using WireMock.Server; @@ -17,7 +17,7 @@ namespace WireMock.Net.OpenApiParser.Extensions; public static class WireMockServerExtensions { /// - /// Register the mappings via an OpenAPI (swagger) V2 or V3 file. + /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file. /// /// The WireMockServer instance /// Path containing OpenAPI file to parse and use the mappings. @@ -29,7 +29,7 @@ public static class WireMockServerExtensions } /// - /// Register the mappings via an OpenAPI (swagger) V2 or V3 file. + /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file. /// /// The WireMockServer instance /// Path containing OpenAPI file to parse and use the mappings. @@ -47,7 +47,7 @@ public static class WireMockServerExtensions } /// - /// Register the mappings via an OpenAPI (swagger) V2 or V3 stream. + /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream. /// /// The WireMockServer instance /// Stream containing OpenAPI description to parse and use the mappings. @@ -59,7 +59,7 @@ public static class WireMockServerExtensions } /// - /// Register the mappings via an OpenAPI (swagger) V2 or V3 stream. + /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream. /// /// The WireMockServer instance /// Stream containing OpenAPI description to parse and use the mappings. @@ -78,7 +78,7 @@ public static class WireMockServerExtensions } /// - /// Register the mappings via an OpenAPI (swagger) V2 or V3 document. + /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 document. /// /// The WireMockServer instance /// The OpenAPI document to use as mappings. diff --git a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs index e54ded9f..4118414c 100644 --- a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi.Reader; using WireMock.Admin.Mappings; using WireMock.Net.OpenApiParser.Settings; @@ -17,7 +17,7 @@ public interface IWireMockOpenApiParser /// /// Generate from a file-path. /// - /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. + /// The path to read the OpenApi/Swagger/V2/V3/V31 or Raml file. /// OpenApiDiagnostic output /// MappingModel IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic); @@ -25,7 +25,7 @@ public interface IWireMockOpenApiParser /// /// Generate from a file-path. /// - /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. + /// The path to read the OpenApi/Swagger/V2/V3/V31 or Raml file. /// Additional settings /// OpenApiDiagnostic output /// MappingModel diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs index a4167db7..e48766e0 100644 --- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs +++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs @@ -3,20 +3,19 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; -using Microsoft.OpenApi; -using Microsoft.OpenApi.Any; +using System.Text.Json; +using System.Text.Json.Nodes; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; +using Microsoft.OpenApi.Models.Interfaces; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Stef.Validation; using WireMock.Admin.Mappings; using WireMock.Net.OpenApiParser.Extensions; using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Utils; +using SystemTextJsonSerializer = System.Text.Json.JsonSerializer; namespace WireMock.Net.OpenApiParser.Mappers; @@ -39,56 +38,40 @@ internal class OpenApiPathsMapper .OrderBy(p => p.Key) .Select(p => MapPath(p.Key, p.Value, servers)) .SelectMany(x => x) - .ToArray() ?? - Array.Empty(); + .ToArray() ?? []; } - private IReadOnlyList MapPaths(OpenApiPaths? paths, IList servers) + private IReadOnlyList MapPath(string path, IOpenApiPathItem pathItem, IList servers) { - return paths? - .OrderBy(p => p.Key) - .Select(p => MapPath(p.Key, p.Value, servers)) - .SelectMany(x => x) - .ToArray() ?? - Array.Empty(); - } - - private IReadOnlyList MapPath(string path, OpenApiPathItem pathItem, IList servers) - { - return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray(); + return pathItem.Operations?.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray() ?? []; } private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList servers) { - var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query); - var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path); - var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header); + var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? []; + var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? []; + var headers = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? []; - var response = operation.Responses.FirstOrDefault(); + var response = operation?.Responses?.FirstOrDefault() ?? new KeyValuePair(); - TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType); + TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out var responseContentType); var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema; var responseExample = responseContent?.Example; var responseSchemaExample = responseContent?.Schema?.Example; - var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) : - responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) : - MapSchemaToObject(responseSchema); + var responseBody = responseExample ?? responseSchemaExample ?? MapSchemaToObject(responseSchema); var requestBodyModel = new BodyModel(); if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required) { var request = operation.RequestBody.Content; - TryGetContent(request, out OpenApiMediaType? requestContent, out _); + TryGetContent(request, out var requestContent, out _); var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema; var requestBodyExample = requestContent!.Example; var requestBodySchemaExample = requestContent.Schema?.Example; - var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) : - requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) : - MapSchemaToObject(requestBodySchema); - + var requestBodyMapped = requestBodyExample ?? requestBodySchemaExample ?? MapSchemaToObject(requestBodySchema); requestBodyModel = MapRequestBody(requestBodyMapped); } @@ -102,8 +85,8 @@ internal class OpenApiPathsMapper Guid = Guid.NewGuid(), Request = new RequestModel { - Methods = new[] { httpMethod }, - Path = MapBasePath(servers) + MapPathWithParameters(path, pathParameters), + Methods = [httpMethod], + Path = PathUtils.Combine(MapBasePath(servers), MapPathWithParameters(path, pathParameters)), Params = MapQueryParameters(queryParameters), Headers = MapRequestHeaders(headers), Body = requestBodyModel @@ -112,12 +95,12 @@ internal class OpenApiPathsMapper { StatusCode = httpStatusCode, Headers = MapHeaders(responseContentType, response.Value?.Headers), - BodyAsJson = body + BodyAsJson = responseBody != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(responseBody)) : null } }; } - private BodyModel? MapRequestBody(object? requestBody) + private BodyModel? MapRequestBody(JsonNode? requestBody) { if (requestBody == null) { @@ -129,7 +112,7 @@ internal class OpenApiPathsMapper Matcher = new MatcherModel { Name = "JsonMatcher", - Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented), + Pattern = SystemTextJsonSerializer.Serialize(requestBody, new JsonSerializerOptions { WriteIndented = true }), IgnoreCase = _settings.RequestBodyIgnoreCase } }; @@ -160,117 +143,103 @@ internal class OpenApiPathsMapper return true; } - private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null) + private JsonNode? MapSchemaToObject(IOpenApiSchema? schema) { if (schema == null) { return null; } - switch (schema.GetSchemaType()) + switch (schema.GetSchemaType(out _)) { - case SchemaType.Array: - var jArray = new JArray(); - for (int i = 0; i < _settings.NumberOfArrayItems; i++) + case JsonSchemaType.Array: + var array = new JsonArray(); + for (var i = 0; i < _settings.NumberOfArrayItems; i++) { - if (schema.Items.Properties.Count > 0) + if (schema.Items?.Properties?.Count > 0) { - var arrayItem = new JObject(); + var item = new JsonObject(); foreach (var property in schema.Items.Properties) { - var objectValue = MapSchemaToObject(property.Value, property.Key); - if (objectValue is JProperty jp) - { - arrayItem.Add(jp); - } - else - { - arrayItem.Add(new JProperty(property.Key, objectValue)); - } + item[property.Key] = MapSchemaToObject(property.Value); } - jArray.Add(arrayItem); + array.Add(item); } else { - var arrayItem = MapSchemaToObject(schema.Items, name: null); // Set name to null to force JObject instead of JProperty - jArray.Add(arrayItem); + var arrayItem = MapSchemaToObject(schema.Items); + array.Add(arrayItem); } } - if (schema.AllOf.Count > 0) + if (schema.AllOf?.Count > 0) { - jArray.Add(MapSchemaAllOfToObject(schema)); + array.Add(MapSchemaAllOfToObject(schema)); } - return jArray; + return array; - case SchemaType.Boolean: - case SchemaType.Integer: - case SchemaType.Number: - case SchemaType.String: + case JsonSchemaType.Boolean: + case JsonSchemaType.Integer: + case JsonSchemaType.Number: + case JsonSchemaType.String: return _exampleValueGenerator.GetExampleValue(schema); - case SchemaType.Object: - var propertyAsJObject = new JObject(); - foreach (var schemaProperty in schema.Properties) + case JsonSchemaType.Object: + var propertyAsJsonObject = new JsonObject(); + foreach (var schemaProperty in schema.Properties ?? new Dictionary()) { - propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key)); + propertyAsJsonObject[schemaProperty.Key] = MapPropertyAsJsonNode(schemaProperty.Value); } - if (schema.AllOf.Count > 0) + if (schema.AllOf?.Count > 0) { - foreach (var group in schema.AllOf.SelectMany(p => p.Properties).GroupBy(x => x.Key)) + foreach (var group in schema.AllOf.SelectMany(p => p.Properties ?? new Dictionary()).GroupBy(x => x.Key)) { - propertyAsJObject.Add(MapPropertyAsJObject(group.First().Value, group.Key)); + propertyAsJsonObject[group.Key] = MapPropertyAsJsonNode(group.First().Value); } } - return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject; + return propertyAsJsonObject; default: return null; } } - private JObject MapSchemaAllOfToObject(OpenApiSchema schema) + private JsonObject MapSchemaAllOfToObject(IOpenApiSchema schema) { - var arrayItem = new JObject(); - foreach (var property in schema.AllOf) + var arrayItem = new JsonObject(); + foreach (var property in schema.AllOf ?? []) { - foreach (var item in property.Properties) + foreach (var item in property.Properties ?? new Dictionary()) { - arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key)); + arrayItem[item.Key] = MapPropertyAsJsonNode(item.Value); } } return arrayItem; } - private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key) + private JsonNode? MapPropertyAsJsonNode(IOpenApiSchema openApiSchema) { - if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array) + var schemaType = openApiSchema.GetSchemaType(out _); + if (schemaType is JsonSchemaType.Object or JsonSchemaType.Array) { - var mapped = MapSchemaToObject(openApiSchema, key); - if (mapped is JProperty jp) - { - return jp; - } - - return new JProperty(key, mapped); + return MapSchemaToObject(openApiSchema); } - // bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x); - return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema)); + return _exampleValueGenerator.GetExampleValue(openApiSchema); } - private string MapPathWithParameters(string path, IEnumerable? parameters) + private string MapPathWithParameters(string path, IEnumerable? parameters) { if (parameters == null) { return path; } - string newPath = path; + var newPath = path; foreach (var parameter in parameters) { var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse); @@ -280,93 +249,56 @@ internal class OpenApiPathsMapper return newPath; } - private string MapBasePath(IList? servers) + private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers) { - if (servers == null || servers.Count == 0) - { - return string.Empty; - } - - OpenApiServer server = servers.First(); - if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult)) - { - return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString(); - } - - return string.Empty; - } - - private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any) - { - if (any == null) - { - return null; - } - - using var outputString = new StringWriter(); - var writer = new OpenApiJsonWriter(outputString); - any.Write(writer, OpenApiSpecVersion.OpenApi3_0); - - if (any.AnyType == AnyType.Array) - { - return JArray.Parse(outputString.ToString()); - } - - return JObject.Parse(outputString.ToString()); - } - - private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers) - { - var mappedHeaders = headers?.ToDictionary( - item => item.Key, - _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern! - ) ?? new Dictionary(); + var mappedHeaders = headers? + .ToDictionary(item => item.Key, _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!) ?? new Dictionary(); if (!string.IsNullOrEmpty(responseContentType)) { - mappedHeaders.TryAdd(HeaderContentType, responseContentType!); + mappedHeaders.TryAdd(HeaderContentType, responseContentType); } return mappedHeaders.Keys.Any() ? mappedHeaders : null; } - private IList? MapQueryParameters(IEnumerable queryParameters) + private IList? MapQueryParameters(IEnumerable queryParameters) { var list = queryParameters .Where(req => req.Required) .Select(qp => new ParamModel { - Name = qp.Name, + Name = qp.Name ?? string.Empty, IgnoreCase = _settings.QueryParameterPatternIgnoreCase, - Matchers = new[] - { + Matchers = + [ GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse) - } + ] }) .ToList(); return list.Any() ? list : null; } - private IList? MapRequestHeaders(IEnumerable headers) + private IList? MapRequestHeaders(IEnumerable headers) { var list = headers .Where(req => req.Required) .Select(qp => new HeaderModel { - Name = qp.Name, + Name = qp.Name ?? string.Empty, IgnoreCase = _settings.HeaderPatternIgnoreCase, - Matchers = new[] - { + Matchers = + [ GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse) - } + ] }) .ToList(); return list.Any() ? list : null; } - private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type) + private MatcherModel GetExampleMatcherModel(IOpenApiSchema? schema, ExampleValueType type) { return type switch { @@ -385,15 +317,31 @@ internal class OpenApiPathsMapper }; } - private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema) + private string GetExampleValueAsStringForSchemaType(IOpenApiSchema? schema) { var value = _exampleValueGenerator.GetExampleValue(schema); - return value switch + if (value.GetValueKind() == JsonValueKind.String) { - string valueAsString => valueAsString, + return value.GetValue(); + } - _ => value.ToString(), - }; + return value.ToString(); + } + + private static string MapBasePath(IList? servers) + { + var server = servers?.FirstOrDefault(); + if (server == null) + { + return string.Empty; + } + + if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out var uriResult)) + { + return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString(); + } + + return string.Empty; } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Properties/AssemblyInfo.cs b/src/WireMock.Net.OpenApiParser/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..fb354ea5 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +// Copyright © WireMock.Net + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")] \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs index 1d99b9ff..7c52339d 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs @@ -1,7 +1,7 @@ // Copyright © WireMock.Net using System; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; namespace WireMock.Net.OpenApiParser.Settings; @@ -26,9 +26,9 @@ public interface IWireMockOpenApiParserExampleValues float Float { get; } /// - /// An example value for a Double. + /// An example value for a Decimal. /// - double Double { get; } + decimal Decimal { get; } /// /// An example value for a Date. @@ -58,5 +58,5 @@ public interface IWireMockOpenApiParserExampleValues /// /// OpenApi Schema to generate dynamic examples more accurate /// - OpenApiSchema? Schema { get; set; } + IOpenApiSchema? Schema { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs index e179981c..c28e7016 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs @@ -1,7 +1,7 @@ // Copyright © WireMock.Net using System; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using RandomDataGenerator.FieldOptions; using RandomDataGenerator.Randomizers; @@ -22,7 +22,7 @@ public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserE public virtual float Float => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; /// - public virtual double Double => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; + public virtual decimal Decimal => SafeConvertFloatToDecimal(RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f); /// public virtual Func Date => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; @@ -40,5 +40,20 @@ public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserE public virtual string String => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; /// - public virtual OpenApiSchema? Schema { get; set; } + public virtual IOpenApiSchema? Schema { get; set; } + + /// + /// Safely converts a float to a decimal, ensuring the value stays within the bounds of a decimal. + /// + /// The float value to convert. + /// A decimal value within the valid range of a decimal. + private static decimal SafeConvertFloatToDecimal(float value) + { + return value switch + { + < (float)decimal.MinValue => decimal.MinValue, + > (float)decimal.MaxValue => decimal.MaxValue, + _ => (decimal)value + }; + } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs index f5e7f03e..4a43b424 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs @@ -2,6 +2,7 @@ using System; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; namespace WireMock.Net.OpenApiParser.Settings; @@ -20,7 +21,7 @@ public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleV public virtual float Float => 4.2f; /// - public virtual double Double => 4.2d; + public virtual decimal Decimal => 4.2m; /// public virtual Func Date { get; } = () => System.DateTime.UtcNow.Date; @@ -29,7 +30,7 @@ public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleV public virtual Func DateTime { get; } = () => System.DateTime.UtcNow; /// - public virtual byte[] Bytes { get; } = { 48, 49, 50 }; + public virtual byte[] Bytes { get; } = [48, 49, 50]; /// public virtual object Object => "example-object"; @@ -38,5 +39,5 @@ public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleV public virtual string String => "example-string"; /// - public virtual OpenApiSchema? Schema { get; set; } = new(); + public virtual IOpenApiSchema? Schema { get; set; } = new OpenApiSchema(); } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Types/SchemaType.cs b/src/WireMock.Net.OpenApiParser/Types/SchemaType.cs deleted file mode 100644 index 4b5df64d..00000000 --- a/src/WireMock.Net.OpenApiParser/Types/SchemaType.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright © WireMock.Net - -namespace WireMock.Net.OpenApiParser.Types; - -internal enum SchemaType -{ - Object, - - Array, - - String, - - Integer, - - Number, - - Boolean, - - File, - - Unknown -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs b/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs index c0108295..e3178549 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs @@ -7,13 +7,16 @@ namespace WireMock.Net.OpenApiParser.Utils; internal static class DateTimeUtils { + private const string DateFormat = "yyyy-MM-dd"; + private const string DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fffzzz"; + public static string ToRfc3339DateTime(DateTime dateTime) { - return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo); + return dateTime.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo); } public static string ToRfc3339Date(DateTime dateTime) { - return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); + return dateTime.ToString(DateFormat, DateTimeFormatInfo.InvariantInfo); } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs index 4039a358..153d4c67 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs @@ -1,8 +1,10 @@ // Copyright © WireMock.Net +using System; using System.Linq; -using Microsoft.OpenApi.Any; +using System.Text.Json.Nodes; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models.Interfaces; using Stef.Validation; using WireMock.Net.OpenApiParser.Extensions; using WireMock.Net.OpenApiParser.Settings; @@ -36,82 +38,66 @@ internal class ExampleValueGenerator } } - public object GetExampleValue(OpenApiSchema? schema) + public JsonNode GetExampleValue(IOpenApiSchema? schema) { var schemaExample = schema?.Example; var schemaEnum = schema?.Enum?.FirstOrDefault(); _exampleValues.Schema = schema; - switch (schema?.GetSchemaType()) + switch (schema?.GetSchemaType(out _)) { - case SchemaType.Boolean: - var exampleBoolean = schemaExample as OpenApiBoolean; - return exampleBoolean?.Value ?? _exampleValues.Boolean; + case JsonSchemaType.Boolean: + var exampleBoolean = schemaExample?.GetValue(); + return exampleBoolean ?? _exampleValues.Boolean; - case SchemaType.Integer: - switch (schema?.GetSchemaFormat()) - { - case SchemaFormat.Int64: - var exampleLong = schemaExample as OpenApiLong; - var enumLong = schemaEnum as OpenApiLong; - var valueLongEnumOrExample = enumLong?.Value ?? exampleLong?.Value; - return valueLongEnumOrExample ?? _exampleValues.Integer; + case JsonSchemaType.Integer: + var exampleInteger = schemaExample?.GetValue(); + var enumInteger = schemaEnum?.GetValue(); + var valueIntegerEnumOrExample = enumInteger ?? exampleInteger; + return valueIntegerEnumOrExample ?? _exampleValues.Integer; - default: - var exampleInteger = schemaExample as OpenApiInteger; - var enumInteger = schemaEnum as OpenApiInteger; - var valueIntegerEnumOrExample = enumInteger?.Value ?? exampleInteger?.Value; - return valueIntegerEnumOrExample ?? _exampleValues.Integer; - } - - case SchemaType.Number: - switch (schema?.GetSchemaFormat()) + case JsonSchemaType.Number: + switch (schema.GetSchemaFormat()) { case SchemaFormat.Float: - var exampleFloat = schemaExample as OpenApiFloat; - var enumFloat = schemaEnum as OpenApiFloat; - var valueFloatEnumOrExample = enumFloat?.Value ?? exampleFloat?.Value; + var exampleFloat = schemaExample?.GetValue(); + var enumFloat = schemaEnum?.GetValue(); + var valueFloatEnumOrExample = enumFloat ?? exampleFloat; return valueFloatEnumOrExample ?? _exampleValues.Float; default: - var exampleDouble = schemaExample as OpenApiDouble; - var enumDouble = schemaEnum as OpenApiDouble; - var valueDoubleEnumOrExample = enumDouble?.Value ?? exampleDouble?.Value; - return valueDoubleEnumOrExample ?? _exampleValues.Double; + var exampleDecimal = schemaExample?.GetValue(); + var enumDecimal = schemaEnum?.GetValue(); + var valueDecimalEnumOrExample = enumDecimal ?? exampleDecimal; + return valueDecimalEnumOrExample ?? _exampleValues.Decimal; } default: switch (schema?.GetSchemaFormat()) { case SchemaFormat.Date: - var exampleDate = schemaExample as OpenApiDate; - var enumDate = schemaEnum as OpenApiDate; - var valueDateEnumOrExample = enumDate?.Value ?? exampleDate?.Value; - return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _exampleValues.Date()); + var exampleDate = schemaExample?.GetValue(); + var enumDate = schemaEnum?.GetValue(); + var valueDateEnumOrExample = enumDate ?? exampleDate; + return valueDateEnumOrExample ?? DateTimeUtils.ToRfc3339Date(_exampleValues.Date()); case SchemaFormat.DateTime: - var exampleDateTime = schemaExample as OpenApiDateTime; - var enumDateTime = schemaEnum as OpenApiDateTime; - var valueDateTimeEnumOrExample = enumDateTime?.Value ?? exampleDateTime?.Value; - return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _exampleValues.DateTime()); + var exampleDateTime = schemaExample?.GetValue(); + var enumDateTime = schemaEnum?.GetValue(); + var valueDateTimeEnumOrExample = enumDateTime ?? exampleDateTime; + return valueDateTimeEnumOrExample ?? DateTimeUtils.ToRfc3339DateTime(_exampleValues.DateTime()); case SchemaFormat.Byte: - var exampleByte = schemaExample as OpenApiByte; - var enumByte = schemaEnum as OpenApiByte; - var valueByteEnumOrExample = enumByte?.Value ?? exampleByte?.Value; - return valueByteEnumOrExample ?? _exampleValues.Bytes; - - case SchemaFormat.Binary: - var exampleBinary = schemaExample as OpenApiBinary; - var enumBinary = schemaEnum as OpenApiBinary; - var valueBinaryEnumOrExample = enumBinary?.Value ?? exampleBinary?.Value; - return valueBinaryEnumOrExample ?? _exampleValues.Object; + var exampleByte = schemaExample?.GetValue(); + var enumByte = schemaEnum?.GetValue(); + var valueByteEnumOrExample = enumByte ?? exampleByte; + return Convert.ToBase64String(valueByteEnumOrExample ?? _exampleValues.Bytes); default: - var exampleString = schemaExample as OpenApiString; - var enumString = schemaEnum as OpenApiString; - var valueStringEnumOrExample = enumString?.Value ?? exampleString?.Value; + var exampleString = schemaExample?.GetValue(); + var enumString = schemaEnum?.GetValue(); + var valueStringEnumOrExample = enumString ?? exampleString; return valueStringEnumOrExample ?? _exampleValues.String; } } diff --git a/src/WireMock.Net.OpenApiParser/Utils/PathUtils.cs b/src/WireMock.Net.OpenApiParser/Utils/PathUtils.cs new file mode 100644 index 00000000..c46b3923 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser/Utils/PathUtils.cs @@ -0,0 +1,27 @@ +// Copyright © WireMock.Net + +namespace WireMock.Net.OpenApiParser.Utils; + +internal static class PathUtils +{ + internal static string Combine(params string[] paths) + { + if (paths.Length == 0) + { + return string.Empty; + } + + var result = paths[0].Trim().TrimEnd('/'); + + for (int i = 1; i < paths.Length; i++) + { + var nextPath = paths[i].Trim().TrimStart('/').TrimEnd('/'); + if (!string.IsNullOrEmpty(nextPath)) + { + result += '/' + nextPath; + } + } + + return result; + } +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj index 4dea3733..d6053b02 100644 --- a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj +++ b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj @@ -2,7 +2,7 @@ An OpenApi (swagger) parser to generate MappingModel or mapping.json file. - net46;netstandard2.0;netstandard2.1 + net47;netstandard2.0;netstandard2.1;net8.0 true wiremock;openapi;OAS;raml;converter;parser;openapiparser {D3804228-91F4-4502-9595-39584E5AADAD} @@ -20,12 +20,11 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs index 782457ca..9db653d8 100644 --- a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs @@ -6,7 +6,8 @@ using System.IO; using System.Text; using JetBrains.Annotations; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi.Reader; +using Microsoft.OpenApi.YamlReader; using RamlToOpenApiConverter; using WireMock.Admin.Mappings; using WireMock.Net.OpenApiParser.Mappers; @@ -19,7 +20,7 @@ namespace WireMock.Net.OpenApiParser; /// public class WireMockOpenApiParser : IWireMockOpenApiParser { - private readonly OpenApiStreamReader _reader = new(); + private static readonly OpenApiReaderSettings ReaderSettings = new(); /// [PublicAPI] @@ -40,8 +41,7 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser } else { - var reader = new OpenApiStreamReader(); - document = reader.Read(File.OpenRead(path), out diagnostic); + document = Read(File.OpenRead(path), out diagnostic); } return FromDocument(document, settings); @@ -51,21 +51,21 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser [PublicAPI] public IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null) { - return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(document.Paths, document.Servers); + return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(document.Paths, document.Servers ?? []); } /// [PublicAPI] public IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic) { - return FromDocument(_reader.Read(stream, out diagnostic)); + return FromDocument(Read(stream, out diagnostic)); } /// [PublicAPI] public IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { - return FromDocument(_reader.Read(stream, out diagnostic), settings); + return FromDocument(Read(stream, out diagnostic), settings); } /// @@ -81,4 +81,27 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser { return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic); } + + private static OpenApiDocument Read(Stream stream, out OpenApiDiagnostic diagnostic) + { + var reader = new OpenApiYamlReader(); + + if (stream is not MemoryStream memoryStream) + { + memoryStream = ReadStreamIntoMemoryStream(stream); + } + + var result = reader.Read(memoryStream, ReaderSettings); + + diagnostic = result.Diagnostic ?? new OpenApiDiagnostic(); + return result.Document ?? throw new InvalidOperationException("The document is null."); + } + + private static MemoryStream ReadStreamIntoMemoryStream(Stream stream) + { + var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + memoryStream.Position = 0; + return memoryStream; + } } \ No newline at end of file diff --git a/src/WireMock.Net/Extensions/StringExtensions.cs b/src/WireMock.Net/Extensions/StringExtensions.cs new file mode 100644 index 00000000..4f57ad4a --- /dev/null +++ b/src/WireMock.Net/Extensions/StringExtensions.cs @@ -0,0 +1,31 @@ +using System.Globalization; + +namespace WireMock.Extensions; + +internal static class StringExtensions +{ + // See https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/ + public static string GetDeterministicHashCodeAsString(this string str) + { + unchecked + { + int hash1 = (5381 << 16) + 5381; + int hash2 = hash1; + + for (int i = 0; i < str.Length; i += 2) + { + hash1 = ((hash1 << 5) + hash1) ^ str[i]; + if (i == str.Length - 1) + { + break; + } + + hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; + } + + int result = hash1 + hash2 * 1566083941; + + return result.ToString(CultureInfo.InvariantCulture).Replace('-', '_'); + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs index 4c206cc1..23f5cd58 100644 --- a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs +++ b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs @@ -84,20 +84,20 @@ public class LocalFileSystemHandler : IFileSystemHandler public virtual byte[] ReadResponseBodyAsFile(string path) { Guard.NotNullOrEmpty(path); - path = PathUtils.CleanPath(path)!; + path = FilePathUtils.CleanPath(path)!; // If the file exists at the given path relative to the MappingsFolder, then return that. // Else the path will just be as-is. - return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path); + return File.ReadAllBytes(File.Exists(FilePathUtils.Combine(GetMappingFolder(), path)) ? FilePathUtils.Combine(GetMappingFolder(), path) : path); } /// public virtual string ReadResponseBodyAsString(string path) { Guard.NotNullOrEmpty(path); - path = PathUtils.CleanPath(path)!; + path = FilePathUtils.CleanPath(path)!; // In case the path is a filename, the path will be adjusted to the MappingFolder. // Else the path will just be as-is. - return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path); + return File.ReadAllText(File.Exists(FilePathUtils.Combine(GetMappingFolder(), path)) ? FilePathUtils.Combine(GetMappingFolder(), path) : path); } /// @@ -124,7 +124,7 @@ public class LocalFileSystemHandler : IFileSystemHandler Guard.NotNullOrEmpty(filename); Guard.NotNull(bytes); - File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes); + File.WriteAllBytes(FilePathUtils.Combine(folder, filename), bytes); } /// diff --git a/src/WireMock.Net/Models/BlockingQueue.cs b/src/WireMock.Net/Models/BlockingQueue.cs new file mode 100644 index 00000000..30ca893e --- /dev/null +++ b/src/WireMock.Net/Models/BlockingQueue.cs @@ -0,0 +1,85 @@ +// Copyright © WireMock.Net + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; + +namespace WireMock.Models; + +/// +internal class BlockingQueue(TimeSpan? readTimeout = null) : IBlockingQueue +{ + private readonly TimeSpan _readTimeout = readTimeout ?? TimeSpan.FromHours(1); + private readonly Queue _queue = new(); + private readonly object _lockObject = new(); + + private bool _isClosed; + + /// + /// Writes an item to the queue and signals that an item is available. + /// + /// The item to be added to the queue. + public void Write(T item) + { + lock (_lockObject) + { + if (_isClosed) + { + throw new InvalidOperationException("Cannot write to a closed queue."); + } + + _queue.Enqueue(item); + + // Signal that an item is available + Monitor.Pulse(_lockObject); + } + } + + /// + /// Tries to read an item from the queue. + /// - waits until an item is available + /// - or the timeout occurs + /// - or queue is closed + /// + /// The item read from the queue, or default if the timeout occurs. + /// True if an item was successfully read; otherwise, false. + public bool TryRead([NotNullWhen(true)] out T? item) + { + lock (_lockObject) + { + // Wait until an item is available or timeout occurs + while (_queue.Count == 0 && !_isClosed) + { + // Wait with timeout + if (!Monitor.Wait(_lockObject, _readTimeout)) + { + item = default; + return false; + } + } + + // After waiting, check if we have items + if (_queue.Count == 0) + { + item = default; + return false; + } + + item = _queue.Dequeue(); + return item != null; + } + } + + /// + /// Closes the queue and signals all waiting threads. + /// + public void Close() + { + lock (_lockObject) + { + _isClosed = true; + Monitor.PulseAll(_lockObject); + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Models/BodyData.cs b/src/WireMock.Net/Models/BodyData.cs index 2b214aca..d49fcbb4 100644 --- a/src/WireMock.Net/Models/BodyData.cs +++ b/src/WireMock.Net/Models/BodyData.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using WireMock.Models; using WireMock.Types; @@ -57,4 +58,10 @@ public class BodyData : IBodyData /// public string? ProtoBufMessageType { get; set; } #endregion + + /// + public IBlockingQueue? SseStringQueue { get; set; } + + /// + public Task? BodyAsSseStringTask { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Models/ProtoDefinitionData.cs b/src/WireMock.Net/Models/ProtoDefinitionData.cs new file mode 100644 index 00000000..23ef5cdc --- /dev/null +++ b/src/WireMock.Net/Models/ProtoDefinitionData.cs @@ -0,0 +1,39 @@ +// Copyright © WireMock.Net + +using System.Collections.Generic; +using System.Linq; +using Stef.Validation; + +namespace WireMock.Models; + +/// +/// A placeholder class for Proto Definitions. +/// +public class ProtoDefinitionData +{ + private readonly IDictionary _filenameMappedToProtoDefinition; + + internal ProtoDefinitionData(IDictionary filenameMappedToProtoDefinition) + { + _filenameMappedToProtoDefinition = filenameMappedToProtoDefinition; + } + + /// + /// Get all the ProtoDefinitions. + /// Note: the main ProtoDefinition will be the first one in the list. + /// + /// The main ProtoDefinition filename. + public IReadOnlyList ToList(string mainProtoFilename) + { + Guard.NotNullOrEmpty(mainProtoFilename); + + if (!_filenameMappedToProtoDefinition.TryGetValue(mainProtoFilename, out var mainProtoDefinition)) + { + throw new KeyNotFoundException($"The ProtoDefinition with filename '{mainProtoFilename}' was not found."); + } + + var list = new List { mainProtoDefinition }; + list.AddRange(_filenameMappedToProtoDefinition.Where(kvp => kvp.Key != mainProtoFilename).Select(kvp => kvp.Value)); + return list; + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs index b9645268..91d13ef4 100644 --- a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs +++ b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs @@ -69,6 +69,13 @@ namespace WireMock.Owin.Mappers return; } + var bodyData = responseMessage.BodyData; + if (bodyData?.GetBodyType() == BodyType.SseString) + { + await HandleSseStringAsync(responseMessage, response, bodyData); + return; + } + byte[]? bytes; switch (responseMessage.FaultType) { @@ -104,7 +111,7 @@ namespace WireMock.Owin.Mappers } } - SetResponseHeaders(responseMessage, bytes, response); + SetResponseHeaders(responseMessage, bytes != null, response); if (bytes != null) { @@ -121,6 +128,26 @@ namespace WireMock.Owin.Mappers SetResponseTrailingHeaders(responseMessage, response); } + private static async Task HandleSseStringAsync(IResponseMessage responseMessage, IResponse response, IBodyData bodyData) + { + if (bodyData.SseStringQueue == null) + { + return; + } + + SetResponseHeaders(responseMessage, true, response); + + string? text; + do + { + if (bodyData.SseStringQueue.TryRead(out text)) + { + await response.WriteAsync(text); + await response.Body.FlushAsync(); + } + } while (text != null); + } + private int MapStatusCode(int code) { if (_options.AllowOnlyDefinedHttpStatusCodeInResponse == true && !Enum.IsDefined(typeof(HttpStatusCode), code)) @@ -136,7 +163,8 @@ namespace WireMock.Owin.Mappers return responseMessage.FaultPercentage == null || _randomizerDouble.Generate() <= responseMessage.FaultPercentage; } - private async Task GetNormalBodyAsync(IResponseMessage responseMessage) { + private async Task GetNormalBodyAsync(IResponseMessage responseMessage) + { var bodyData = responseMessage.BodyData; switch (bodyData?.GetBodyType()) { @@ -172,13 +200,13 @@ namespace WireMock.Owin.Mappers return null; } - private static void SetResponseHeaders(IResponseMessage responseMessage, byte[]? bytes, IResponse response) + private static void SetResponseHeaders(IResponseMessage responseMessage, bool hasBody, IResponse response) { // Force setting the Date header (#577) AppendResponseHeader( response, HttpKnownHeaderNames.Date, - [ DateTime.UtcNow.ToString(CultureInfo.InvariantCulture.DateTimeFormat.RFC1123Pattern, CultureInfo.InvariantCulture) ] + [DateTime.UtcNow.ToString(CultureInfo.InvariantCulture.DateTimeFormat.RFC1123Pattern, CultureInfo.InvariantCulture)] ); // Set other headers @@ -188,7 +216,7 @@ namespace WireMock.Owin.Mappers var value = item.Value; if (ResponseHeadersToFix.TryGetValue(headerName, out var action)) { - action?.Invoke(response, bytes != null, value); + action?.Invoke(response, hasBody, value); } else { diff --git a/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs b/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs index a5c5e1db..395e472e 100644 --- a/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs +++ b/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs @@ -1,5 +1,6 @@ // Copyright © WireMock.Net +using System; using System.Collections.Generic; using WireMock.Matchers; using WireMock.Matchers.Request; @@ -12,7 +13,7 @@ public partial class Request /// public IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - return WithBodyAsProtoBuf([ protoDefinition ], messageType, matchBehaviour); + return WithBodyAsProtoBuf([protoDefinition], messageType, matchBehaviour); } /// @@ -36,12 +37,25 @@ public partial class Request /// public IRequestBuilder WithBodyAsProtoBuf(string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - return Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => Mapping.ProtoDefinition!.Value, messageType)); + return Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(), messageType)); } /// public IRequestBuilder WithBodyAsProtoBuf(string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) { - return Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => Mapping.ProtoDefinition!.Value, messageType, matcher)); + return Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(), messageType, matcher)); + } + + private Func ProtoDefinitionFunc() + { + return () => + { + if (Mapping.ProtoDefinition == null) + { + throw new InvalidOperationException($"No ProtoDefinition defined on mapping '{Mapping.Guid}'. Please use the WireMockServerSettings to define ProtoDefinitions."); + } + + return Mapping.ProtoDefinition.Value; + }; } } \ No newline at end of file diff --git a/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs b/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs index df9259c4..303ce15b 100644 --- a/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs +++ b/src/WireMock.Net/ResponseBuilders/IBodyResponseBuilder.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using JsonConverter.Abstractions; +using WireMock.Models; namespace WireMock.ResponseBuilders; @@ -32,7 +33,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder IResponseBuilder WithBody(Func bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null); /// - /// WithBody : Create a ... response based on a async callback function. + /// WithBody : Create a ... response based on an async callback function. /// /// The async delegate to build the body. /// The Body Destination format (SameAsSource, String or Bytes). @@ -40,6 +41,14 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder /// A . IResponseBuilder WithBody(Func> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null); + /// + /// WithBody : Create a ... response based on an async callback function. + /// + /// The async delegate to build the body. + /// The timeout to wait on new items in the queue. Default value is 1 hour. + /// A . + IResponseBuilder WithSseBody(Func, Task> bodyFactory, TimeSpan? timeout = null); + /// /// WithBody : Create a ... response based on a bytearray. /// diff --git a/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs b/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs index 24135273..9c0aa7ed 100644 --- a/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs +++ b/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs @@ -51,6 +51,26 @@ public partial class Response }); } + /// + public IResponseBuilder WithSseBody(Func, Task> bodyFactory, TimeSpan? timeout = null) + { + Guard.NotNull(bodyFactory); + + var queue = new BlockingQueue(timeout); + + return WithCallbackInternal(true, req => new ResponseMessage + { + BodyData = new BodyData + { + DetectedBodyType = BodyType.SseString, + SseStringQueue = queue, + BodyAsSseStringTask = bodyFactory(req, queue), + Encoding = Encoding.UTF8, + IsFuncUsed = "Func, Task>" + } + }); + } + /// public IResponseBuilder WithBody(byte[] body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null) { diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index 3840c9c3..5407159e 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -356,9 +356,12 @@ internal class RespondWithAProvider : IRespondWithAProvider { Guard.NotNull(protoDefinitionOrId); +#if PROTOBUF ProtoDefinition = ProtoDefinitionHelper.GetIdOrTexts(_settings, protoDefinitionOrId); - return this; +#else + throw new NotSupportedException("The WithProtoDefinition method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower."); +#endif } /// diff --git a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs index 694552b6..a56e8de9 100644 --- a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -25,7 +25,7 @@ public partial class WireMockServer return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message); } #else - return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower."); + return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Not supported for .NETStandard 1.3 and .NET 4.6.x or lower."); #endif } @@ -50,7 +50,7 @@ public partial class WireMockServer return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message); } #else - return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower."); + return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Not supported for .NETStandard 1.3 and .NET 4.6.x or lower."); #endif } } \ No newline at end of file diff --git a/src/WireMock.Net/Util/FilePathUtils.cs b/src/WireMock.Net/Util/FilePathUtils.cs new file mode 100644 index 00000000..fd8c2751 --- /dev/null +++ b/src/WireMock.Net/Util/FilePathUtils.cs @@ -0,0 +1,86 @@ +// Copyright © WireMock.Net + +using System.IO; +using Stef.Validation; + +namespace WireMock.Util; + +internal static class FilePathUtils +{ + /// + /// Robust handling of the user defined path. + /// Also supports Unix and Windows platforms + /// + /// The path to clean + public static string? CleanPath(string? path) + { + return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + + /// + /// Removes leading directory separator chars from the filepath, which could break Path.Combine + /// + /// The path to remove the loading DirectorySeparatorChars + public static string? RemoveLeadingDirectorySeparators(string? path) + { + return path?.TrimStart(Path.DirectorySeparatorChar); + } + + /// + /// Combine two paths + /// + /// The root path + /// The path + public static string Combine(string root, string? path) + { + Guard.NotNull(root); + + var result = RemoveLeadingDirectorySeparators(path); + return result == null ? root : Path.Combine(root, result); + } + + /// + /// Returns a relative path from one path to another. + /// + /// The source path the result should be relative to. This path is always considered to be a directory.. + /// The destination path. + /// The relative path, or path if the paths don't share the same root. + public static string GetRelativePath(string relativeTo, string path) + { +#if NETCOREAPP3_1 || NET5_0_OR_GREATER || NETSTANDARD2_1 + return Path.GetRelativePath(relativeTo, path); +#else + Guard.NotNull(relativeTo); + Guard.NotNull(path); + + static string AppendDirectorySeparatorChar(string path) + { + // Append a slash only if the path is a directory and does not have a slash. + if (!Path.HasExtension(path) && !path.EndsWith(Path.DirectorySeparatorChar.ToString())) + { + return path + Path.DirectorySeparatorChar; + } + + return path; + } + + var fromUri = new System.Uri(AppendDirectorySeparatorChar(relativeTo)); + var toUri = new System.Uri(AppendDirectorySeparatorChar(path)); + + if (fromUri.Scheme != toUri.Scheme) + { + return path; + } + + var relativeUri = fromUri.MakeRelativeUri(toUri); + var relativePath = System.Uri.UnescapeDataString(relativeUri.ToString()); + + if (string.Equals(toUri.Scheme, "FILE", System.StringComparison.OrdinalIgnoreCase)) + { + relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } + + return relativePath; +#endif + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Util/PathUtils.cs b/src/WireMock.Net/Util/PathUtils.cs deleted file mode 100644 index 2ea460ff..00000000 --- a/src/WireMock.Net/Util/PathUtils.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © WireMock.Net - -using System.IO; -using Stef.Validation; - -namespace WireMock.Util; - -internal static class PathUtils -{ - /// - /// Robust handling of the user defined path. - /// Also supports Unix and Windows platforms - /// - /// The path to clean - public static string? CleanPath(string? path) - { - return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); - } - - /// - /// Removes leading directory separator chars from the filepath, which could break Path.Combine - /// - /// The path to remove the loading DirectorySeparatorChars - public static string? RemoveLeadingDirectorySeparators(string? path) - { - return path?.TrimStart(new[] { Path.DirectorySeparatorChar }); - } - - /// - /// Combine two paths - /// - /// The root path - /// The path - public static string Combine(string root, string? path) - { - Guard.NotNull(root); - - var result = RemoveLeadingDirectorySeparators(path); - return result == null ? root : Path.Combine(root, result); - } -} \ No newline at end of file diff --git a/src/WireMock.Net/Util/ProtoDefinitionHelper.cs b/src/WireMock.Net/Util/ProtoDefinitionHelper.cs index 91edc51e..d616abc2 100644 --- a/src/WireMock.Net/Util/ProtoDefinitionHelper.cs +++ b/src/WireMock.Net/Util/ProtoDefinitionHelper.cs @@ -1,12 +1,85 @@ // Copyright © WireMock.Net +#if PROTOBUF +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ProtoBufJsonConverter; +using ProtoBufJsonConverter.Models; +using Stef.Validation; using WireMock.Models; using WireMock.Settings; namespace WireMock.Util; -internal static class ProtoDefinitionHelper +/// +/// Some helper methods for Proto Definitions. +/// +public static class ProtoDefinitionHelper { + /// + /// Builds a dictionary of ProtoDefinitions from a directory. + /// - The key will be the filename without extension. + /// - The value will be the ProtoDefinition with an extra comment with the relative path to each .proto file so it can be used by the WireMockProtoFileResolver. + /// + /// The directory to start from. + /// The token to monitor for cancellation requests. The default value is System.Threading.CancellationToken.None. + public static async Task FromDirectory(string directory, CancellationToken cancellationToken = default) + { + Guard.NotNullOrEmpty(directory); + + var fileNameMappedToProtoDefinition = new Dictionary(); + var filePaths = Directory.EnumerateFiles(directory, "*.proto", SearchOption.AllDirectories); + + foreach (var filePath in filePaths) + { + // Get the relative path to the directory (note that this will be OS specific). + var relativePath = FilePathUtils.GetRelativePath(directory, filePath); + + // Make it a valid proto import path + var protoRelativePath = relativePath.Replace(Path.DirectorySeparatorChar, '/'); + + // Build comment and get content from file. + var comment = $"// {protoRelativePath}"; +#if NETSTANDARD2_0 + var content = File.ReadAllText(filePath); +#else + var content = await File.ReadAllTextAsync(filePath, cancellationToken); +#endif + // Only add the comment if it's not already defined. + var modifiedContent = !content.StartsWith(comment) ? $"{comment}\n{content}" : content; + var key = Path.GetFileNameWithoutExtension(filePath); + + fileNameMappedToProtoDefinition.Add(key, modifiedContent); + } + + var converter = SingletonFactory.GetInstance(); + var resolver = new WireMockProtoFileResolver(fileNameMappedToProtoDefinition.Values); + + var messageTypeMappedToWithProtoDefinition = new Dictionary(); + + foreach (var protoDefinition in fileNameMappedToProtoDefinition.Values) + { + var infoRequest = new GetInformationRequest(protoDefinition, resolver); + + try + { + var info = await converter.GetInformationAsync(infoRequest, cancellationToken); + foreach (var messageType in info.MessageTypes) + { + messageTypeMappedToWithProtoDefinition[messageType.Key] = protoDefinition; + } + } + catch + { + // Ignore + } + } + + return new ProtoDefinitionData(fileNameMappedToProtoDefinition); + } + internal static IdOrTexts GetIdOrTexts(WireMockServerSettings settings, params string[] protoDefinitionOrId) { switch (protoDefinitionOrId.Length) @@ -19,9 +92,10 @@ internal static class ProtoDefinitionHelper } return new(null, protoDefinitionOrId); - + default: return new(null, protoDefinitionOrId); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/WireMock.Net/Util/WireMockProtoFileResolver.cs b/src/WireMock.Net/Util/WireMockProtoFileResolver.cs index cbae432c..e81893f2 100644 --- a/src/WireMock.Net/Util/WireMockProtoFileResolver.cs +++ b/src/WireMock.Net/Util/WireMockProtoFileResolver.cs @@ -7,18 +7,19 @@ using System.IO; using System.Linq; using ProtoBufJsonConverter; using Stef.Validation; +using WireMock.Extensions; namespace WireMock.Util; /// /// This resolver is used to resolve the extra ProtoDefinition files. +/// /// It assumes that: -/// - the first ProtoDefinition file is the main ProtoDefinition file. -/// - the first commented line of each extra ProtoDefinition file is the filename which is used in the import of the other ProtoDefinition file(s). +/// - The first commented line of each ProtoDefinition file is the filepath which is used in the import of the other ProtoDefinition file(s). /// internal class WireMockProtoFileResolver : IProtoFileResolver { - private readonly Dictionary _files = new(); + private readonly Dictionary _files = []; public WireMockProtoFileResolver(IReadOnlyCollection protoDefinitions) { @@ -27,12 +28,19 @@ internal class WireMockProtoFileResolver : IProtoFileResolver return; } - foreach (var extraProtoDefinition in protoDefinitions.Skip(1)) + foreach (var extraProtoDefinition in protoDefinitions) { var firstNonEmptyLine = extraProtoDefinition.Split(['\r', '\n']).FirstOrDefault(l => !string.IsNullOrEmpty(l)); - if (firstNonEmptyLine != null && TryGetValidFileName(firstNonEmptyLine.TrimStart(['/', ' ']), out var validFileName)) + if (firstNonEmptyLine != null) { - _files.Add(validFileName, extraProtoDefinition); + if (TryGetValidPath(firstNonEmptyLine.TrimStart(['/', ' ']), out var validPath)) + { + _files.Add(validPath, extraProtoDefinition); + } + else + { + _files.Add(extraProtoDefinition.GetDeterministicHashCodeAsString(), extraProtoDefinition); + } } } } @@ -52,15 +60,15 @@ internal class WireMockProtoFileResolver : IProtoFileResolver throw new FileNotFoundException($"The ProtoDefinition '{path}' was not found."); } - private static bool TryGetValidFileName(string fileName, [NotNullWhen(true)] out string? validFileName) + private static bool TryGetValidPath(string path, [NotNullWhen(true)] out string? validPath) { - if (!fileName.Any(c => Path.GetInvalidFileNameChars().Contains(c))) + if (!path.Any(c => Path.GetInvalidPathChars().Contains(c))) { - validFileName = fileName; + validPath = path; return true; } - validFileName = null; + validPath = null; return false; } } diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index ce422f0b..168787c3 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -46,7 +46,7 @@ $(DefineConstants);USE_ASPNETCORE;NET46 - + $(DefineConstants);OPENAPIPARSER @@ -195,17 +195,17 @@ - - + - + \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/ProtoDefinitionHelperTests.cs b/test/WireMock.Net.Tests/Grpc/ProtoDefinitionHelperTests.cs new file mode 100644 index 00000000..7a501a25 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ProtoDefinitionHelperTests.cs @@ -0,0 +1,73 @@ +// Copyright © WireMock.Net + +#if PROTOBUF +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using WireMock.Util; +using Xunit; + +namespace WireMock.Net.Tests.Grpc; + +public class ProtoDefinitionHelperTests +{ + [Fact] + public async Task FromDirectory_Greet_ShouldReturnModifiedProtoFiles() + { + // Arrange + var directory = Path.Combine(Directory.GetCurrentDirectory(), "Grpc", "Test"); + var expectedFilename = "SubFolder/request.proto"; + var expectedComment = $"// {expectedFilename}"; + + // Act + var protoDefinitionData = await ProtoDefinitionHelper.FromDirectory(directory); + var protoDefinitions = protoDefinitionData.ToList("greet"); + + // Assert + protoDefinitions.Should().HaveCount(2); + protoDefinitions[0].Should().StartWith("// greet.proto"); + protoDefinitions[1].Should().StartWith(expectedComment); + + // Arrange + var resolver = new WireMockProtoFileResolver(protoDefinitions); + + // Act + Assert + resolver.Exists(expectedFilename).Should().BeTrue(); + resolver.Exists("x").Should().BeFalse(); + + // Act + Assert + var text = await resolver.OpenText(expectedFilename).ReadToEndAsync(); + text.Should().StartWith(expectedComment); + System.Action action = () => resolver.OpenText("x"); + action.Should().Throw(); + } + + [Fact] + public async Task FromDirectory_OpenTelemetry_ShouldReturnModifiedProtoFiles() + { + // Arrange + var directory = Path.Combine(Directory.GetCurrentDirectory(), "Grpc", "ot"); + + // Act + var protoDefinitionData = await ProtoDefinitionHelper.FromDirectory(directory); + var protoDefinitions = protoDefinitionData.ToList("trace_service"); + + // Assert + protoDefinitions.Should().HaveCount(10); + + var responseBytes = await ProtoBufUtils.GetProtoBufMessageWithHeaderAsync( + protoDefinitions, + "OpenTelemetry.Proto.Collector.Trace.V1.ExportTracePartialSuccess", + new + { + rejected_spans = 1, + error_message = "abc" + } + ); + + // Assert + Convert.ToBase64String(responseBytes).Should().Be("AAAAAAcIARIDYWJj"); + } +} +#endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/Test/SubFolder/request.proto b/test/WireMock.Net.Tests/Grpc/Test/SubFolder/request.proto new file mode 100644 index 00000000..28abb822 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/Test/SubFolder/request.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package greet; + +message HelloRequest { + string name = 1; +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/Test/greet.proto b/test/WireMock.Net.Tests/Grpc/Test/greet.proto new file mode 100644 index 00000000..4b327037 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/Test/greet.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +import "SubFolder/request.proto"; + +package greet; + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} + +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/README.md b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/README.md new file mode 100644 index 00000000..f82dbb02 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/README.md @@ -0,0 +1,10 @@ +# OpenTelemetry Collector Proto + +This package describes the OpenTelemetry collector protocol. + +## Packages + +1. `common` package contains the common messages shared between different services. +2. `trace` package contains the Trace Service protos. +3. `metrics` package contains the Metrics Service protos. +4. `logs` package contains the Logs Service protos. diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/logs/v1/logs_service.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/logs/v1/logs_service.proto new file mode 100644 index 00000000..8260d8aa --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/logs/v1/logs_service.proto @@ -0,0 +1,79 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.collector.logs.v1; + +import "opentelemetry/proto/logs/v1/logs.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Collector.Logs.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.collector.logs.v1"; +option java_outer_classname = "LogsServiceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/collector/logs/v1"; + +// Service that can be used to push logs between one Application instrumented with +// OpenTelemetry and an collector, or between an collector and a central collector (in this +// case logs are sent/received to/from multiple Applications). +service LogsService { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + rpc Export(ExportLogsServiceRequest) returns (ExportLogsServiceResponse) {} +} + +message ExportLogsServiceRequest { + // An array of ResourceLogs. + // For data coming from a single resource this array will typically contain one + // element. Intermediary nodes (such as OpenTelemetry Collector) that receive + // data from multiple origins typically batch the data before forwarding further and + // in that case this array will contain multiple elements. + repeated opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1; +} + +message ExportLogsServiceResponse { + // The details of a partially successful export request. + // + // If the request is only partially accepted + // (i.e. when the server accepts only parts of the data and rejects the rest) + // the server MUST initialize the `partial_success` field and MUST + // set the `rejected_` with the number of items it rejected. + // + // Servers MAY also make use of the `partial_success` field to convey + // warnings/suggestions to senders even when the request was fully accepted. + // In such cases, the `rejected_` MUST have a value of `0` and + // the `error_message` MUST be non-empty. + // + // A `partial_success` message with an empty value (rejected_ = 0 and + // `error_message` = "") is equivalent to it not being set/present. Senders + // SHOULD interpret it the same way as in the full success case. + ExportLogsPartialSuccess partial_success = 1; +} + +message ExportLogsPartialSuccess { + // The number of rejected log records. + // + // A `rejected_` field holding a `0` value indicates that the + // request was fully accepted. + int64 rejected_log_records = 1; + + // A developer-facing human-readable message in English. It should be used + // either to explain why the server rejected parts of the data during a partial + // success or to convey warnings/suggestions during a full success. The message + // should offer guidance on how users can address such issues. + // + // error_message is an optional field. An error_message with an empty value + // is equivalent to it not being set. + string error_message = 2; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml new file mode 100644 index 00000000..507473b9 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml @@ -0,0 +1,9 @@ +# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the +# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. +type: google.api.Service +config_version: 3 +http: + rules: + - selector: opentelemetry.proto.collector.logs.v1.LogsService.Export + post: /v1/logs + body: "*" \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/metrics/v1/metrics_service.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/metrics/v1/metrics_service.proto new file mode 100644 index 00000000..dd48f1ad --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/metrics/v1/metrics_service.proto @@ -0,0 +1,79 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.collector.metrics.v1; + +import "opentelemetry/proto/metrics/v1/metrics.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Collector.Metrics.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.collector.metrics.v1"; +option java_outer_classname = "MetricsServiceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/collector/metrics/v1"; + +// Service that can be used to push metrics between one Application +// instrumented with OpenTelemetry and a collector, or between a collector and a +// central collector. +service MetricsService { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + rpc Export(ExportMetricsServiceRequest) returns (ExportMetricsServiceResponse) {} +} + +message ExportMetricsServiceRequest { + // An array of ResourceMetrics. + // For data coming from a single resource this array will typically contain one + // element. Intermediary nodes (such as OpenTelemetry Collector) that receive + // data from multiple origins typically batch the data before forwarding further and + // in that case this array will contain multiple elements. + repeated opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1; +} + +message ExportMetricsServiceResponse { + // The details of a partially successful export request. + // + // If the request is only partially accepted + // (i.e. when the server accepts only parts of the data and rejects the rest) + // the server MUST initialize the `partial_success` field and MUST + // set the `rejected_` with the number of items it rejected. + // + // Servers MAY also make use of the `partial_success` field to convey + // warnings/suggestions to senders even when the request was fully accepted. + // In such cases, the `rejected_` MUST have a value of `0` and + // the `error_message` MUST be non-empty. + // + // A `partial_success` message with an empty value (rejected_ = 0 and + // `error_message` = "") is equivalent to it not being set/present. Senders + // SHOULD interpret it the same way as in the full success case. + ExportMetricsPartialSuccess partial_success = 1; +} + +message ExportMetricsPartialSuccess { + // The number of rejected data points. + // + // A `rejected_` field holding a `0` value indicates that the + // request was fully accepted. + int64 rejected_data_points = 1; + + // A developer-facing human-readable message in English. It should be used + // either to explain why the server rejected parts of the data during a partial + // success or to convey warnings/suggestions during a full success. The message + // should offer guidance on how users can address such issues. + // + // error_message is an optional field. An error_message with an empty value + // is equivalent to it not being set. + string error_message = 2; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml new file mode 100644 index 00000000..a5456502 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml @@ -0,0 +1,9 @@ +# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the +# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. +type: google.api.Service +config_version: 3 +http: + rules: + - selector: opentelemetry.proto.collector.metrics.v1.MetricsService.Export + post: /v1/metrics + body: "*" \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/profiles/v1development/profiles_service.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/profiles/v1development/profiles_service.proto new file mode 100644 index 00000000..ab2433ed --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/profiles/v1development/profiles_service.proto @@ -0,0 +1,78 @@ +// Copyright 2023, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.collector.profiles.v1development; + +import "opentelemetry/proto/profiles/v1development/profiles.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Collector.Profiles.V1Development"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.collector.profiles.v1development"; +option java_outer_classname = "ProfilesServiceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/collector/profiles/v1development"; + +// Service that can be used to push profiles between one Application instrumented with +// OpenTelemetry and a collector, or between a collector and a central collector. +service ProfilesService { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + rpc Export(ExportProfilesServiceRequest) returns (ExportProfilesServiceResponse) {} +} + +message ExportProfilesServiceRequest { + // An array of ResourceProfiles. + // For data coming from a single resource this array will typically contain one + // element. Intermediary nodes (such as OpenTelemetry Collector) that receive + // data from multiple origins typically batch the data before forwarding further and + // in that case this array will contain multiple elements. + repeated opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1; +} + +message ExportProfilesServiceResponse { + // The details of a partially successful export request. + // + // If the request is only partially accepted + // (i.e. when the server accepts only parts of the data and rejects the rest) + // the server MUST initialize the `partial_success` field and MUST + // set the `rejected_` with the number of items it rejected. + // + // Servers MAY also make use of the `partial_success` field to convey + // warnings/suggestions to senders even when the request was fully accepted. + // In such cases, the `rejected_` MUST have a value of `0` and + // the `error_message` MUST be non-empty. + // + // A `partial_success` message with an empty value (rejected_ = 0 and + // `error_message` = "") is equivalent to it not being set/present. Senders + // SHOULD interpret it the same way as in the full success case. + ExportProfilesPartialSuccess partial_success = 1; +} + +message ExportProfilesPartialSuccess { + // The number of rejected profiles. + // + // A `rejected_` field holding a `0` value indicates that the + // request was fully accepted. + int64 rejected_profiles = 1; + + // A developer-facing human-readable message in English. It should be used + // either to explain why the server rejected parts of the data during a partial + // success or to convey warnings/suggestions during a full success. The message + // should offer guidance on how users can address such issues. + // + // error_message is an optional field. An error_message with an empty value + // is equivalent to it not being set. + string error_message = 2; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/profiles/v1development/profiles_service_http.yaml b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/profiles/v1development/profiles_service_http.yaml new file mode 100644 index 00000000..6b3b91da --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/profiles/v1development/profiles_service_http.yaml @@ -0,0 +1,9 @@ +# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the +# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. +type: google.api.Service +config_version: 3 +http: + rules: + - selector: opentelemetry.proto.collector.profiles.v1development.ProfilesService.Export + post: /v1development/profiles + body: "*" diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/trace/v1/trace_service.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/trace/v1/trace_service.proto new file mode 100644 index 00000000..d6fe67f9 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/trace/v1/trace_service.proto @@ -0,0 +1,79 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.collector.trace.v1; + +import "opentelemetry/proto/trace/v1/trace.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Collector.Trace.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.collector.trace.v1"; +option java_outer_classname = "TraceServiceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/collector/trace/v1"; + +// Service that can be used to push spans between one Application instrumented with +// OpenTelemetry and a collector, or between a collector and a central collector (in this +// case spans are sent/received to/from multiple Applications). +service TraceService { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + rpc Export(ExportTraceServiceRequest) returns (ExportTraceServiceResponse) {} +} + +message ExportTraceServiceRequest { + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain one + // element. Intermediary nodes (such as OpenTelemetry Collector) that receive + // data from multiple origins typically batch the data before forwarding further and + // in that case this array will contain multiple elements. + repeated opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1; +} + +message ExportTraceServiceResponse { + // The details of a partially successful export request. + // + // If the request is only partially accepted + // (i.e. when the server accepts only parts of the data and rejects the rest) + // the server MUST initialize the `partial_success` field and MUST + // set the `rejected_` with the number of items it rejected. + // + // Servers MAY also make use of the `partial_success` field to convey + // warnings/suggestions to senders even when the request was fully accepted. + // In such cases, the `rejected_` MUST have a value of `0` and + // the `error_message` MUST be non-empty. + // + // A `partial_success` message with an empty value (rejected_ = 0 and + // `error_message` = "") is equivalent to it not being set/present. Senders + // SHOULD interpret it the same way as in the full success case. + ExportTracePartialSuccess partial_success = 1; +} + +message ExportTracePartialSuccess { + // The number of rejected spans. + // + // A `rejected_` field holding a `0` value indicates that the + // request was fully accepted. + int64 rejected_spans = 1; + + // A developer-facing human-readable message in English. It should be used + // either to explain why the server rejected parts of the data during a partial + // success or to convey warnings/suggestions during a full success. The message + // should offer guidance on how users can address such issues. + // + // error_message is an optional field. An error_message with an empty value + // is equivalent to it not being set. + string error_message = 2; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml new file mode 100644 index 00000000..d091b3a8 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml @@ -0,0 +1,9 @@ +# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the +# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. +type: google.api.Service +config_version: 3 +http: + rules: + - selector: opentelemetry.proto.collector.trace.v1.TraceService.Export + post: /v1/traces + body: "*" diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/common/v1/common.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/common/v1/common.proto new file mode 100644 index 00000000..ff8a21a1 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/common/v1/common.proto @@ -0,0 +1,81 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.common.v1; + +option csharp_namespace = "OpenTelemetry.Proto.Common.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.common.v1"; +option java_outer_classname = "CommonProto"; +option go_package = "go.opentelemetry.io/proto/otlp/common/v1"; + +// AnyValue is used to represent any type of attribute value. AnyValue may contain a +// primitive value such as a string or integer or it may contain an arbitrary nested +// object containing arrays, key-value lists and primitives. +message AnyValue { + // The value is one of the listed fields. It is valid for all values to be unspecified + // in which case this AnyValue is considered to be "empty". + oneof value { + string string_value = 1; + bool bool_value = 2; + int64 int_value = 3; + double double_value = 4; + ArrayValue array_value = 5; + KeyValueList kvlist_value = 6; + bytes bytes_value = 7; + } +} + +// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message +// since oneof in AnyValue does not allow repeated fields. +message ArrayValue { + // Array of values. The array may be empty (contain 0 elements). + repeated AnyValue values = 1; +} + +// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message +// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need +// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to +// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches +// are semantically equivalent. +message KeyValueList { + // A collection of key/value pairs of key-value pairs. The list may be empty (may + // contain 0 elements). + // The keys MUST be unique (it is not allowed to have more than one + // value with the same key). + repeated KeyValue values = 1; +} + +// KeyValue is a key-value pair that is used to store Span attributes, Link +// attributes, etc. +message KeyValue { + string key = 1; + AnyValue value = 2; +} + +// InstrumentationScope is a message representing the instrumentation scope information +// such as the fully qualified name and version. +message InstrumentationScope { + // An empty instrumentation scope name means the name is unknown. + string name = 1; + string version = 2; + + // Additional attributes that describe the scope. [Optional]. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated KeyValue attributes = 3; + uint32 dropped_attributes_count = 4; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/logs/v1/logs.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/logs/v1/logs.proto new file mode 100644 index 00000000..261d2291 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/logs/v1/logs.proto @@ -0,0 +1,227 @@ +// Copyright 2020, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.logs.v1; + +import "opentelemetry/proto/common/v1/common.proto"; +import "opentelemetry/proto/resource/v1/resource.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Logs.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.logs.v1"; +option java_outer_classname = "LogsProto"; +option go_package = "go.opentelemetry.io/proto/otlp/logs/v1"; + +// LogsData represents the logs data that can be stored in a persistent storage, +// OR can be embedded by other protocols that transfer OTLP logs data but do not +// implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +message LogsData { + // An array of ResourceLogs. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + repeated ResourceLogs resource_logs = 1; +} + +// A collection of ScopeLogs from a Resource. +message ResourceLogs { + reserved 1000; + + // The resource for the logs in this message. + // If this field is not set then resource info is unknown. + opentelemetry.proto.resource.v1.Resource resource = 1; + + // A list of ScopeLogs that originate from a resource. + repeated ScopeLogs scope_logs = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the resource data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_logs" field which have their own schema_url field. + string schema_url = 3; +} + +// A collection of Logs produced by a Scope. +message ScopeLogs { + // The instrumentation scope information for the logs in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + opentelemetry.proto.common.v1.InstrumentationScope scope = 1; + + // A list of log records. + repeated LogRecord log_records = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the log data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to all logs in the "logs" field. + string schema_url = 3; +} + +// Possible values for LogRecord.SeverityNumber. +enum SeverityNumber { + // UNSPECIFIED is the default SeverityNumber, it MUST NOT be used. + SEVERITY_NUMBER_UNSPECIFIED = 0; + SEVERITY_NUMBER_TRACE = 1; + SEVERITY_NUMBER_TRACE2 = 2; + SEVERITY_NUMBER_TRACE3 = 3; + SEVERITY_NUMBER_TRACE4 = 4; + SEVERITY_NUMBER_DEBUG = 5; + SEVERITY_NUMBER_DEBUG2 = 6; + SEVERITY_NUMBER_DEBUG3 = 7; + SEVERITY_NUMBER_DEBUG4 = 8; + SEVERITY_NUMBER_INFO = 9; + SEVERITY_NUMBER_INFO2 = 10; + SEVERITY_NUMBER_INFO3 = 11; + SEVERITY_NUMBER_INFO4 = 12; + SEVERITY_NUMBER_WARN = 13; + SEVERITY_NUMBER_WARN2 = 14; + SEVERITY_NUMBER_WARN3 = 15; + SEVERITY_NUMBER_WARN4 = 16; + SEVERITY_NUMBER_ERROR = 17; + SEVERITY_NUMBER_ERROR2 = 18; + SEVERITY_NUMBER_ERROR3 = 19; + SEVERITY_NUMBER_ERROR4 = 20; + SEVERITY_NUMBER_FATAL = 21; + SEVERITY_NUMBER_FATAL2 = 22; + SEVERITY_NUMBER_FATAL3 = 23; + SEVERITY_NUMBER_FATAL4 = 24; +} + +// LogRecordFlags represents constants used to interpret the +// LogRecord.flags field, which is protobuf 'fixed32' type and is to +// be used as bit-fields. Each non-zero value defined in this enum is +// a bit-mask. To extract the bit-field, for example, use an +// expression like: +// +// (logRecord.flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK) +// +enum LogRecordFlags { + // The zero value for the enum. Should not be used for comparisons. + // Instead use bitwise "and" with the appropriate mask as shown above. + LOG_RECORD_FLAGS_DO_NOT_USE = 0; + + // Bits 0-7 are used for trace flags. + LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 0x000000FF; + + // Bits 8-31 are reserved for future use. +} + +// A log record according to OpenTelemetry Log Data Model: +// https://github.com/open-telemetry/oteps/blob/main/text/logs/0097-log-data-model.md +message LogRecord { + reserved 4; + + // time_unix_nano is the time when the event occurred. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // Value of 0 indicates unknown or missing timestamp. + fixed64 time_unix_nano = 1; + + // Time when the event was observed by the collection system. + // For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK) + // this timestamp is typically set at the generation time and is equal to Timestamp. + // For events originating externally and collected by OpenTelemetry (e.g. using + // Collector) this is the time when OpenTelemetry's code observed the event measured + // by the clock of the OpenTelemetry code. This field MUST be set once the event is + // observed by OpenTelemetry. + // + // For converting OpenTelemetry log data to formats that support only one timestamp or + // when receiving OpenTelemetry log data by recipients that support only one timestamp + // internally the following logic is recommended: + // - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // Value of 0 indicates unknown or missing timestamp. + fixed64 observed_time_unix_nano = 11; + + // Numerical value of the severity, normalized to values described in Log Data Model. + // [Optional]. + SeverityNumber severity_number = 2; + + // The severity text (also known as log level). The original string representation as + // it is known at the source. [Optional]. + string severity_text = 3; + + // A value containing the body of the log record. Can be for example a human-readable + // string message (including multi-line) describing the event in a free form or it can + // be a structured data composed of arrays and maps of other values. [Optional]. + opentelemetry.proto.common.v1.AnyValue body = 5; + + // Additional attributes that describe the specific event occurrence. [Optional]. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 6; + uint32 dropped_attributes_count = 7; + + // Flags, a bit field. 8 least significant bits are the trace flags as + // defined in W3C Trace Context specification. 24 most significant bits are reserved + // and must be set to 0. Readers must not assume that 24 most significant bits + // will be zero and must correctly mask the bits when reading 8-bit trace flag (use + // flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional]. + fixed32 flags = 8; + + // A unique identifier for a trace. All logs from the same trace share + // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is optional. + // + // The receivers SHOULD assume that the log record is not associated with a + // trace if any of the following is true: + // - the field is not present, + // - the field contains an invalid value. + bytes trace_id = 9; + + // A unique identifier for a span within a trace, assigned when the span + // is created. The ID is an 8-byte array. An ID with all zeroes OR of length + // other than 8 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is optional. If the sender specifies a valid span_id then it SHOULD also + // specify a valid trace_id. + // + // The receivers SHOULD assume that the log record is not associated with a + // span if any of the following is true: + // - the field is not present, + // - the field contains an invalid value. + bytes span_id = 10; + + // A unique identifier of event category/type. + // All events with the same event_name are expected to conform to the same + // schema for both their attributes and their body. + // + // Recommended to be fully qualified and short (no longer than 256 characters). + // + // Presence of event_name on the log record identifies this record + // as an event. + // + // [Optional]. + // + // Status: [Development] + string event_name = 12; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/metrics/v1/metrics.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/metrics/v1/metrics.proto new file mode 100644 index 00000000..a42e51a6 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/metrics/v1/metrics.proto @@ -0,0 +1,719 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.metrics.v1; + +import "opentelemetry/proto/common/v1/common.proto"; +import "opentelemetry/proto/resource/v1/resource.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Metrics.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.metrics.v1"; +option java_outer_classname = "MetricsProto"; +option go_package = "go.opentelemetry.io/proto/otlp/metrics/v1"; + +// MetricsData represents the metrics data that can be stored in a persistent +// storage, OR can be embedded by other protocols that transfer OTLP metrics +// data but do not implement the OTLP protocol. +// +// MetricsData +// └─── ResourceMetrics +// ├── Resource +// ├── SchemaURL +// └── ScopeMetrics +// ├── Scope +// ├── SchemaURL +// └── Metric +// ├── Name +// ├── Description +// ├── Unit +// └── data +// ├── Gauge +// ├── Sum +// ├── Histogram +// ├── ExponentialHistogram +// └── Summary +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +message MetricsData { + // An array of ResourceMetrics. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + repeated ResourceMetrics resource_metrics = 1; +} + +// A collection of ScopeMetrics from a Resource. +message ResourceMetrics { + reserved 1000; + + // The resource for the metrics in this message. + // If this field is not set then no resource info is known. + opentelemetry.proto.resource.v1.Resource resource = 1; + + // A list of metrics that originate from a resource. + repeated ScopeMetrics scope_metrics = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the resource data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_metrics" field which have their own schema_url field. + string schema_url = 3; +} + +// A collection of Metrics produced by an Scope. +message ScopeMetrics { + // The instrumentation scope information for the metrics in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + opentelemetry.proto.common.v1.InstrumentationScope scope = 1; + + // A list of metrics that originate from an instrumentation library. + repeated Metric metrics = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the metric data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to all metrics in the "metrics" field. + string schema_url = 3; +} + +// Defines a Metric which has one or more timeseries. The following is a +// brief summary of the Metric data model. For more details, see: +// +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md +// +// The data model and relation between entities is shown in the +// diagram below. Here, "DataPoint" is the term used to refer to any +// one of the specific data point value types, and "points" is the term used +// to refer to any one of the lists of points contained in the Metric. +// +// - Metric is composed of a metadata and data. +// - Metadata part contains a name, description, unit. +// - Data is one of the possible types (Sum, Gauge, Histogram, Summary). +// - DataPoint contains timestamps, attributes, and one of the possible value type +// fields. +// +// Metric +// +------------+ +// |name | +// |description | +// |unit | +------------------------------------+ +// |data |---> |Gauge, Sum, Histogram, Summary, ... | +// +------------+ +------------------------------------+ +// +// Data [One of Gauge, Sum, Histogram, Summary, ...] +// +-----------+ +// |... | // Metadata about the Data. +// |points |--+ +// +-----------+ | +// | +---------------------------+ +// | |DataPoint 1 | +// v |+------+------+ +------+ | +// +-----+ ||label |label |...|label | | +// | 1 |-->||value1|value2|...|valueN| | +// +-----+ |+------+------+ +------+ | +// | . | |+-----+ | +// | . | ||value| | +// | . | |+-----+ | +// | . | +---------------------------+ +// | . | . +// | . | . +// | . | . +// | . | +---------------------------+ +// | . | |DataPoint M | +// +-----+ |+------+------+ +------+ | +// | M |-->||label |label |...|label | | +// +-----+ ||value1|value2|...|valueN| | +// |+------+------+ +------+ | +// |+-----+ | +// ||value| | +// |+-----+ | +// +---------------------------+ +// +// Each distinct type of DataPoint represents the output of a specific +// aggregation function, the result of applying the DataPoint's +// associated function of to one or more measurements. +// +// All DataPoint types have three common fields: +// - Attributes includes key-value pairs associated with the data point +// - TimeUnixNano is required, set to the end time of the aggregation +// - StartTimeUnixNano is optional, but strongly encouraged for DataPoints +// having an AggregationTemporality field, as discussed below. +// +// Both TimeUnixNano and StartTimeUnixNano values are expressed as +// UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. +// +// # TimeUnixNano +// +// This field is required, having consistent interpretation across +// DataPoint types. TimeUnixNano is the moment corresponding to when +// the data point's aggregate value was captured. +// +// Data points with the 0 value for TimeUnixNano SHOULD be rejected +// by consumers. +// +// # StartTimeUnixNano +// +// StartTimeUnixNano in general allows detecting when a sequence of +// observations is unbroken. This field indicates to consumers the +// start time for points with cumulative and delta +// AggregationTemporality, and it should be included whenever possible +// to support correct rate calculation. Although it may be omitted +// when the start time is truly unknown, setting StartTimeUnixNano is +// strongly encouraged. +message Metric { + reserved 4, 6, 8; + + // name of the metric. + string name = 1; + + // description of the metric, which can be used in documentation. + string description = 2; + + // unit in which the metric value is reported. Follows the format + // described by https://unitsofmeasure.org/ucum.html. + string unit = 3; + + // Data determines the aggregation type (if any) of the metric, what is the + // reported value type for the data points, as well as the relatationship to + // the time interval over which they are reported. + oneof data { + Gauge gauge = 5; + Sum sum = 7; + Histogram histogram = 9; + ExponentialHistogram exponential_histogram = 10; + Summary summary = 11; + } + + // Additional metadata attributes that describe the metric. [Optional]. + // Attributes are non-identifying. + // Consumers SHOULD NOT need to be aware of these attributes. + // These attributes MAY be used to encode information allowing + // for lossless roundtrip translation to / from another data model. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue metadata = 12; +} + +// Gauge represents the type of a scalar metric that always exports the +// "current value" for every data point. It should be used for an "unknown" +// aggregation. +// +// A Gauge does not support different aggregation temporalities. Given the +// aggregation is unknown, points cannot be combined using the same +// aggregation, regardless of aggregation temporalities. Therefore, +// AggregationTemporality is not included. Consequently, this also means +// "StartTimeUnixNano" is ignored for all data points. +message Gauge { + repeated NumberDataPoint data_points = 1; +} + +// Sum represents the type of a scalar metric that is calculated as a sum of all +// reported measurements over a time interval. +message Sum { + repeated NumberDataPoint data_points = 1; + + // aggregation_temporality describes if the aggregator reports delta changes + // since last report time, or cumulative changes since a fixed start time. + AggregationTemporality aggregation_temporality = 2; + + // If "true" means that the sum is monotonic. + bool is_monotonic = 3; +} + +// Histogram represents the type of a metric that is calculated by aggregating +// as a Histogram of all reported measurements over a time interval. +message Histogram { + repeated HistogramDataPoint data_points = 1; + + // aggregation_temporality describes if the aggregator reports delta changes + // since last report time, or cumulative changes since a fixed start time. + AggregationTemporality aggregation_temporality = 2; +} + +// ExponentialHistogram represents the type of a metric that is calculated by aggregating +// as a ExponentialHistogram of all reported double measurements over a time interval. +message ExponentialHistogram { + repeated ExponentialHistogramDataPoint data_points = 1; + + // aggregation_temporality describes if the aggregator reports delta changes + // since last report time, or cumulative changes since a fixed start time. + AggregationTemporality aggregation_temporality = 2; +} + +// Summary metric data are used to convey quantile summaries, +// a Prometheus (see: https://prometheus.io/docs/concepts/metric_types/#summary) +// and OpenMetrics (see: https://github.com/prometheus/OpenMetrics/blob/4dbf6075567ab43296eed941037c12951faafb92/protos/prometheus.proto#L45) +// data type. These data points cannot always be merged in a meaningful way. +// While they can be useful in some applications, histogram data points are +// recommended for new applications. +// Summary metrics do not have an aggregation temporality field. This is +// because the count and sum fields of a SummaryDataPoint are assumed to be +// cumulative values. +message Summary { + repeated SummaryDataPoint data_points = 1; +} + +// AggregationTemporality defines how a metric aggregator reports aggregated +// values. It describes how those values relate to the time interval over +// which they are aggregated. +enum AggregationTemporality { + // UNSPECIFIED is the default AggregationTemporality, it MUST not be used. + AGGREGATION_TEMPORALITY_UNSPECIFIED = 0; + + // DELTA is an AggregationTemporality for a metric aggregator which reports + // changes since last report time. Successive metrics contain aggregation of + // values from continuous and non-overlapping intervals. + // + // The values for a DELTA metric are based only on the time interval + // associated with one measurement cycle. There is no dependency on + // previous measurements like is the case for CUMULATIVE metrics. + // + // For example, consider a system measuring the number of requests that + // it receives and reports the sum of these requests every second as a + // DELTA metric: + // + // 1. The system starts receiving at time=t_0. + // 2. A request is received, the system measures 1 request. + // 3. A request is received, the system measures 1 request. + // 4. A request is received, the system measures 1 request. + // 5. The 1 second collection cycle ends. A metric is exported for the + // number of requests received over the interval of time t_0 to + // t_0+1 with a value of 3. + // 6. A request is received, the system measures 1 request. + // 7. A request is received, the system measures 1 request. + // 8. The 1 second collection cycle ends. A metric is exported for the + // number of requests received over the interval of time t_0+1 to + // t_0+2 with a value of 2. + AGGREGATION_TEMPORALITY_DELTA = 1; + + // CUMULATIVE is an AggregationTemporality for a metric aggregator which + // reports changes since a fixed start time. This means that current values + // of a CUMULATIVE metric depend on all previous measurements since the + // start time. Because of this, the sender is required to retain this state + // in some form. If this state is lost or invalidated, the CUMULATIVE metric + // values MUST be reset and a new fixed start time following the last + // reported measurement time sent MUST be used. + // + // For example, consider a system measuring the number of requests that + // it receives and reports the sum of these requests every second as a + // CUMULATIVE metric: + // + // 1. The system starts receiving at time=t_0. + // 2. A request is received, the system measures 1 request. + // 3. A request is received, the system measures 1 request. + // 4. A request is received, the system measures 1 request. + // 5. The 1 second collection cycle ends. A metric is exported for the + // number of requests received over the interval of time t_0 to + // t_0+1 with a value of 3. + // 6. A request is received, the system measures 1 request. + // 7. A request is received, the system measures 1 request. + // 8. The 1 second collection cycle ends. A metric is exported for the + // number of requests received over the interval of time t_0 to + // t_0+2 with a value of 5. + // 9. The system experiences a fault and loses state. + // 10. The system recovers and resumes receiving at time=t_1. + // 11. A request is received, the system measures 1 request. + // 12. The 1 second collection cycle ends. A metric is exported for the + // number of requests received over the interval of time t_1 to + // t_0+1 with a value of 1. + // + // Note: Even though, when reporting changes since last report time, using + // CUMULATIVE is valid, it is not recommended. This may cause problems for + // systems that do not use start_time to determine when the aggregation + // value was reset (e.g. Prometheus). + AGGREGATION_TEMPORALITY_CUMULATIVE = 2; +} + +// DataPointFlags is defined as a protobuf 'uint32' type and is to be used as a +// bit-field representing 32 distinct boolean flags. Each flag defined in this +// enum is a bit-mask. To test the presence of a single flag in the flags of +// a data point, for example, use an expression like: +// +// (point.flags & DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK) == DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK +// +enum DataPointFlags { + // The zero value for the enum. Should not be used for comparisons. + // Instead use bitwise "and" with the appropriate mask as shown above. + DATA_POINT_FLAGS_DO_NOT_USE = 0; + + // This DataPoint is valid but has no recorded value. This value + // SHOULD be used to reflect explicitly missing data in a series, as + // for an equivalent to the Prometheus "staleness marker". + DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK = 1; + + // Bits 2-31 are reserved for future use. +} + +// NumberDataPoint is a single data point in a timeseries that describes the +// time-varying scalar value of a metric. +message NumberDataPoint { + reserved 1; + + // The set of key/value pairs that uniquely identify the timeseries from + // where this point belongs. The list may be empty (may contain 0 elements). + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 7; + + // StartTimeUnixNano is optional but strongly encouraged, see the + // the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 start_time_unix_nano = 2; + + // TimeUnixNano is required, see the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 time_unix_nano = 3; + + // The value itself. A point is considered invalid when one of the recognized + // value fields is not present inside this oneof. + oneof value { + double as_double = 4; + sfixed64 as_int = 6; + } + + // (Optional) List of exemplars collected from + // measurements that were used to form the data point + repeated Exemplar exemplars = 5; + + // Flags that apply to this specific data point. See DataPointFlags + // for the available flags and their meaning. + uint32 flags = 8; +} + +// HistogramDataPoint is a single data point in a timeseries that describes the +// time-varying values of a Histogram. A Histogram contains summary statistics +// for a population of values, it may optionally contain the distribution of +// those values across a set of buckets. +// +// If the histogram contains the distribution of values, then both +// "explicit_bounds" and "bucket counts" fields must be defined. +// If the histogram does not contain the distribution of values, then both +// "explicit_bounds" and "bucket_counts" must be omitted and only "count" and +// "sum" are known. +message HistogramDataPoint { + reserved 1; + + // The set of key/value pairs that uniquely identify the timeseries from + // where this point belongs. The list may be empty (may contain 0 elements). + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 9; + + // StartTimeUnixNano is optional but strongly encouraged, see the + // the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 start_time_unix_nano = 2; + + // TimeUnixNano is required, see the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 time_unix_nano = 3; + + // count is the number of values in the population. Must be non-negative. This + // value must be equal to the sum of the "count" fields in buckets if a + // histogram is provided. + fixed64 count = 4; + + // sum of the values in the population. If count is zero then this field + // must be zero. + // + // Note: Sum should only be filled out when measuring non-negative discrete + // events, and is assumed to be monotonic over the values of these events. + // Negative events *can* be recorded, but sum should not be filled out when + // doing so. This is specifically to enforce compatibility w/ OpenMetrics, + // see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram + optional double sum = 5; + + // bucket_counts is an optional field contains the count values of histogram + // for each bucket. + // + // The sum of the bucket_counts must equal the value in the count field. + // + // The number of elements in bucket_counts array must be by one greater than + // the number of elements in explicit_bounds array. The exception to this rule + // is when the length of bucket_counts is 0, then the length of explicit_bounds + // must also be 0. + repeated fixed64 bucket_counts = 6; + + // explicit_bounds specifies buckets with explicitly defined bounds for values. + // + // The boundaries for bucket at index i are: + // + // (-infinity, explicit_bounds[i]] for i == 0 + // (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds) + // (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds) + // + // The values in the explicit_bounds array must be strictly increasing. + // + // Histogram buckets are inclusive of their upper boundary, except the last + // bucket where the boundary is at infinity. This format is intentionally + // compatible with the OpenMetrics histogram definition. + // + // If bucket_counts length is 0 then explicit_bounds length must also be 0, + // otherwise the data point is invalid. + repeated double explicit_bounds = 7; + + // (Optional) List of exemplars collected from + // measurements that were used to form the data point + repeated Exemplar exemplars = 8; + + // Flags that apply to this specific data point. See DataPointFlags + // for the available flags and their meaning. + uint32 flags = 10; + + // min is the minimum value over (start_time, end_time]. + optional double min = 11; + + // max is the maximum value over (start_time, end_time]. + optional double max = 12; +} + +// ExponentialHistogramDataPoint is a single data point in a timeseries that describes the +// time-varying values of a ExponentialHistogram of double values. A ExponentialHistogram contains +// summary statistics for a population of values, it may optionally contain the +// distribution of those values across a set of buckets. +// +message ExponentialHistogramDataPoint { + // The set of key/value pairs that uniquely identify the timeseries from + // where this point belongs. The list may be empty (may contain 0 elements). + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 1; + + // StartTimeUnixNano is optional but strongly encouraged, see the + // the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 start_time_unix_nano = 2; + + // TimeUnixNano is required, see the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 time_unix_nano = 3; + + // count is the number of values in the population. Must be + // non-negative. This value must be equal to the sum of the "bucket_counts" + // values in the positive and negative Buckets plus the "zero_count" field. + fixed64 count = 4; + + // sum of the values in the population. If count is zero then this field + // must be zero. + // + // Note: Sum should only be filled out when measuring non-negative discrete + // events, and is assumed to be monotonic over the values of these events. + // Negative events *can* be recorded, but sum should not be filled out when + // doing so. This is specifically to enforce compatibility w/ OpenMetrics, + // see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram + optional double sum = 5; + + // scale describes the resolution of the histogram. Boundaries are + // located at powers of the base, where: + // + // base = (2^(2^-scale)) + // + // The histogram bucket identified by `index`, a signed integer, + // contains values that are greater than (base^index) and + // less than or equal to (base^(index+1)). + // + // The positive and negative ranges of the histogram are expressed + // separately. Negative values are mapped by their absolute value + // into the negative range using the same scale as the positive range. + // + // scale is not restricted by the protocol, as the permissible + // values depend on the range of the data. + sint32 scale = 6; + + // zero_count is the count of values that are either exactly zero or + // within the region considered zero by the instrumentation at the + // tolerated degree of precision. This bucket stores values that + // cannot be expressed using the standard exponential formula as + // well as values that have been rounded to zero. + // + // Implementations MAY consider the zero bucket to have probability + // mass equal to (zero_count / count). + fixed64 zero_count = 7; + + // positive carries the positive range of exponential bucket counts. + Buckets positive = 8; + + // negative carries the negative range of exponential bucket counts. + Buckets negative = 9; + + // Buckets are a set of bucket counts, encoded in a contiguous array + // of counts. + message Buckets { + // Offset is the bucket index of the first entry in the bucket_counts array. + // + // Note: This uses a varint encoding as a simple form of compression. + sint32 offset = 1; + + // bucket_counts is an array of count values, where bucket_counts[i] carries + // the count of the bucket at index (offset+i). bucket_counts[i] is the count + // of values greater than base^(offset+i) and less than or equal to + // base^(offset+i+1). + // + // Note: By contrast, the explicit HistogramDataPoint uses + // fixed64. This field is expected to have many buckets, + // especially zeros, so uint64 has been selected to ensure + // varint encoding. + repeated uint64 bucket_counts = 2; + } + + // Flags that apply to this specific data point. See DataPointFlags + // for the available flags and their meaning. + uint32 flags = 10; + + // (Optional) List of exemplars collected from + // measurements that were used to form the data point + repeated Exemplar exemplars = 11; + + // min is the minimum value over (start_time, end_time]. + optional double min = 12; + + // max is the maximum value over (start_time, end_time]. + optional double max = 13; + + // ZeroThreshold may be optionally set to convey the width of the zero + // region. Where the zero region is defined as the closed interval + // [-ZeroThreshold, ZeroThreshold]. + // When ZeroThreshold is 0, zero count bucket stores values that cannot be + // expressed using the standard exponential formula as well as values that + // have been rounded to zero. + double zero_threshold = 14; +} + +// SummaryDataPoint is a single data point in a timeseries that describes the +// time-varying values of a Summary metric. The count and sum fields represent +// cumulative values. +message SummaryDataPoint { + reserved 1; + + // The set of key/value pairs that uniquely identify the timeseries from + // where this point belongs. The list may be empty (may contain 0 elements). + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 7; + + // StartTimeUnixNano is optional but strongly encouraged, see the + // the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 start_time_unix_nano = 2; + + // TimeUnixNano is required, see the detailed comments above Metric. + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 time_unix_nano = 3; + + // count is the number of values in the population. Must be non-negative. + fixed64 count = 4; + + // sum of the values in the population. If count is zero then this field + // must be zero. + // + // Note: Sum should only be filled out when measuring non-negative discrete + // events, and is assumed to be monotonic over the values of these events. + // Negative events *can* be recorded, but sum should not be filled out when + // doing so. This is specifically to enforce compatibility w/ OpenMetrics, + // see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary + double sum = 5; + + // Represents the value at a given quantile of a distribution. + // + // To record Min and Max values following conventions are used: + // - The 1.0 quantile is equivalent to the maximum value observed. + // - The 0.0 quantile is equivalent to the minimum value observed. + // + // See the following issue for more context: + // https://github.com/open-telemetry/opentelemetry-proto/issues/125 + message ValueAtQuantile { + // The quantile of a distribution. Must be in the interval + // [0.0, 1.0]. + double quantile = 1; + + // The value at the given quantile of a distribution. + // + // Quantile values must NOT be negative. + double value = 2; + } + + // (Optional) list of values at different quantiles of the distribution calculated + // from the current snapshot. The quantiles must be strictly increasing. + repeated ValueAtQuantile quantile_values = 6; + + // Flags that apply to this specific data point. See DataPointFlags + // for the available flags and their meaning. + uint32 flags = 8; +} + +// A representation of an exemplar, which is a sample input measurement. +// Exemplars also hold information about the environment when the measurement +// was recorded, for example the span and trace ID of the active span when the +// exemplar was recorded. +message Exemplar { + reserved 1; + + // The set of key/value pairs that were filtered out by the aggregator, but + // recorded alongside the original measurement. Only key/value pairs that were + // filtered out by the aggregator should be included + repeated opentelemetry.proto.common.v1.KeyValue filtered_attributes = 7; + + // time_unix_nano is the exact time when this exemplar was recorded + // + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January + // 1970. + fixed64 time_unix_nano = 2; + + // The value of the measurement that was recorded. An exemplar is + // considered invalid when one of the recognized value fields is not present + // inside this oneof. + oneof value { + double as_double = 3; + sfixed64 as_int = 6; + } + + // (Optional) Span ID of the exemplar trace. + // span_id may be missing if the measurement is not recorded inside a trace + // or if the trace is not sampled. + bytes span_id = 4; + + // (Optional) Trace ID of the exemplar trace. + // trace_id may be missing if the measurement is not recorded inside a trace + // or if the trace is not sampled. + bytes trace_id = 5; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/profiles/v1development/profiles.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/profiles/v1development/profiles.proto new file mode 100644 index 00000000..f3821d0a --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/profiles/v1development/profiles.proto @@ -0,0 +1,474 @@ +// Copyright 2023, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file includes work covered by the following copyright and permission notices: +// +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.profiles.v1development; + +import "opentelemetry/proto/common/v1/common.proto"; +import "opentelemetry/proto/resource/v1/resource.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Profiles.V1Development"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.profiles.v1development"; +option java_outer_classname = "ProfilesProto"; +option go_package = "go.opentelemetry.io/proto/otlp/profiles/v1development"; + +// Relationships Diagram +// +// ┌──────────────────┐ LEGEND +// │ ProfilesData │ +// └──────────────────┘ ─────▶ embedded +// │ +// │ 1-n ─────▷ referenced by index +// ▼ +// ┌──────────────────┐ +// │ ResourceProfiles │ +// └──────────────────┘ +// │ +// │ 1-n +// ▼ +// ┌──────────────────┐ +// │ ScopeProfiles │ +// └──────────────────┘ +// │ +// │ 1-1 +// ▼ +// ┌──────────────────┐ +// │ Profile │ +// └──────────────────┘ +// │ n-1 +// │ 1-n ┌───────────────────────────────────────┐ +// ▼ │ ▽ +// ┌──────────────────┐ 1-n ┌──────────────┐ ┌──────────┐ +// │ Sample │ ──────▷ │ KeyValue │ │ Link │ +// └──────────────────┘ └──────────────┘ └──────────┘ +// │ 1-n △ △ +// │ 1-n ┌─────────────────┘ │ 1-n +// ▽ │ │ +// ┌──────────────────┐ n-1 ┌──────────────┐ +// │ Location │ ──────▷ │ Mapping │ +// └──────────────────┘ └──────────────┘ +// │ +// │ 1-n +// ▼ +// ┌──────────────────┐ +// │ Line │ +// └──────────────────┘ +// │ +// │ 1-1 +// ▽ +// ┌──────────────────┐ +// │ Function │ +// └──────────────────┘ +// + +// ProfilesData represents the profiles data that can be stored in persistent storage, +// OR can be embedded by other protocols that transfer OTLP profiles data but do not +// implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +message ProfilesData { + // An array of ResourceProfiles. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + repeated ResourceProfiles resource_profiles = 1; +} + + +// A collection of ScopeProfiles from a Resource. +message ResourceProfiles { + reserved 1000; + + // The resource for the profiles in this message. + // If this field is not set then no resource info is known. + opentelemetry.proto.resource.v1.Resource resource = 1; + + // A list of ScopeProfiles that originate from a resource. + repeated ScopeProfiles scope_profiles = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the resource data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_profiles" field which have their own schema_url field. + string schema_url = 3; +} + +// A collection of Profiles produced by an InstrumentationScope. +message ScopeProfiles { + // The instrumentation scope information for the profiles in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + opentelemetry.proto.common.v1.InstrumentationScope scope = 1; + + // A list of Profiles that originate from an instrumentation scope. + repeated Profile profiles = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the profile data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to all profiles in the "profiles" field. + string schema_url = 3; +} + +// Profile is a common stacktrace profile format. +// +// Measurements represented with this format should follow the +// following conventions: +// +// - Consumers should treat unset optional fields as if they had been +// set with their default value. +// +// - When possible, measurements should be stored in "unsampled" form +// that is most useful to humans. There should be enough +// information present to determine the original sampled values. +// +// - On-disk, the serialized proto must be gzip-compressed. +// +// - The profile is represented as a set of samples, where each sample +// references a sequence of locations, and where each location belongs +// to a mapping. +// - There is a N->1 relationship from sample.location_id entries to +// locations. For every sample.location_id entry there must be a +// unique Location with that index. +// - There is an optional N->1 relationship from locations to +// mappings. For every nonzero Location.mapping_id there must be a +// unique Mapping with that index. + +// Represents a complete profile, including sample types, samples, +// mappings to binaries, locations, functions, string table, and additional metadata. +// It modifies and annotates pprof Profile with OpenTelemetry specific fields. +// +// Note that whilst fields in this message retain the name and field id from pprof in most cases +// for ease of understanding data migration, it is not intended that pprof:Profile and +// OpenTelemetry:Profile encoding be wire compatible. +message Profile { + + // A description of the samples associated with each Sample.value. + // For a cpu profile this might be: + // [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]] + // For a heap profile, this might be: + // [["allocations","count"], ["space","bytes"]], + // If one of the values represents the number of events represented + // by the sample, by convention it should be at index 0 and use + // sample_type.unit == "count". + repeated ValueType sample_type = 1; + // The set of samples recorded in this profile. + repeated Sample sample = 2; + // Mapping from address ranges to the image/binary/library mapped + // into that address range. mapping[0] will be the main binary. + // If multiple binaries contribute to the Profile and no main + // binary can be identified, mapping[0] has no special meaning. + repeated Mapping mapping_table = 3; + // Locations referenced by samples via location_indices. + repeated Location location_table = 4; + // Array of locations referenced by samples. + repeated int32 location_indices = 5; + // Functions referenced by locations. + repeated Function function_table = 6; + // Lookup table for attributes. + repeated opentelemetry.proto.common.v1.KeyValue attribute_table = 7; + // Represents a mapping between Attribute Keys and Units. + repeated AttributeUnit attribute_units = 8; + // Lookup table for links. + repeated Link link_table = 9; + // A common table for strings referenced by various messages. + // string_table[0] must always be "". + repeated string string_table = 10; + + // The following fields 9-14 are informational, do not affect + // interpretation of results. + + // Time of collection (UTC) represented as nanoseconds past the epoch. + int64 time_nanos = 11; + // Duration of the profile, if a duration makes sense. + int64 duration_nanos = 12; + // The kind of events between sampled occurrences. + // e.g [ "cpu","cycles" ] or [ "heap","bytes" ] + ValueType period_type = 13; + // The number of events between sampled occurrences. + int64 period = 14; + // Free-form text associated with the profile. The text is displayed as is + // to the user by the tools that read profiles (e.g. by pprof). This field + // should not be used to store any machine-readable information, it is only + // for human-friendly content. The profile must stay functional if this field + // is cleaned. + repeated int32 comment_strindices = 15; // Indices into string table. + // Index into the sample_type array to the default sample type. + int32 default_sample_type_index = 16; + + + // A globally unique identifier for a profile. The ID is a 16-byte array. An ID with + // all zeroes is considered invalid. + // + // This field is required. + bytes profile_id = 17; + + // dropped_attributes_count is the number of attributes that were discarded. Attributes + // can be discarded because their keys are too long or because there are too many + // attributes. If this value is 0, then no attributes were dropped. + uint32 dropped_attributes_count = 19; + + // Specifies format of the original payload. Common values are defined in semantic conventions. [required if original_payload is present] + string original_payload_format = 20; + + // Original payload can be stored in this field. This can be useful for users who want to get the original payload. + // Formats such as JFR are highly extensible and can contain more information than what is defined in this spec. + // Inclusion of original payload should be configurable by the user. Default behavior should be to not include the original payload. + // If the original payload is in pprof format, it SHOULD not be included in this field. + // The field is optional, however if it is present then equivalent converted data should be populated in other fields + // of this message as far as is practicable. + bytes original_payload = 21; + + // References to attributes in attribute_table. [optional] + // It is a collection of key/value pairs. Note, global attributes + // like server name can be set using the resource API. Examples of attributes: + // + // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + // "/http/server_latency": 300 + // "abc.com/myattribute": true + // "abc.com/score": 10.239 + // + // The OpenTelemetry API specification further restricts the allowed value types: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated int32 attribute_indices = 22; +} + +// Represents a mapping between Attribute Keys and Units. +message AttributeUnit { + // Index into string table. + int32 attribute_key_strindex = 1; + // Index into string table. + int32 unit_strindex = 2; +} + +// A pointer from a profile Sample to a trace Span. +// Connects a profile sample to a trace span, identified by unique trace and span IDs. +message Link { + // A unique identifier of a trace that this linked span is part of. The ID is a + // 16-byte array. + bytes trace_id = 1; + + // A unique identifier for the linked span. The ID is an 8-byte array. + bytes span_id = 2; +} + +// Specifies the method of aggregating metric values, either DELTA (change since last report) +// or CUMULATIVE (total since a fixed start time). +enum AggregationTemporality { + /* UNSPECIFIED is the default AggregationTemporality, it MUST not be used. */ + AGGREGATION_TEMPORALITY_UNSPECIFIED = 0; + + /** DELTA is an AggregationTemporality for a profiler which reports + changes since last report time. Successive metrics contain aggregation of + values from continuous and non-overlapping intervals. + + The values for a DELTA metric are based only on the time interval + associated with one measurement cycle. There is no dependency on + previous measurements like is the case for CUMULATIVE metrics. + + For example, consider a system measuring the number of requests that + it receives and reports the sum of these requests every second as a + DELTA metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0+1 to + t_0+2 with a value of 2. */ + AGGREGATION_TEMPORALITY_DELTA = 1; + + /** CUMULATIVE is an AggregationTemporality for a profiler which + reports changes since a fixed start time. This means that current values + of a CUMULATIVE metric depend on all previous measurements since the + start time. Because of this, the sender is required to retain this state + in some form. If this state is lost or invalidated, the CUMULATIVE metric + values MUST be reset and a new fixed start time following the last + reported measurement time sent MUST be used. + + For example, consider a system measuring the number of requests that + it receives and reports the sum of these requests every second as a + CUMULATIVE metric: + + 1. The system starts receiving at time=t_0. + 2. A request is received, the system measures 1 request. + 3. A request is received, the system measures 1 request. + 4. A request is received, the system measures 1 request. + 5. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+1 with a value of 3. + 6. A request is received, the system measures 1 request. + 7. A request is received, the system measures 1 request. + 8. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_0 to + t_0+2 with a value of 5. + 9. The system experiences a fault and loses state. + 10. The system recovers and resumes receiving at time=t_1. + 11. A request is received, the system measures 1 request. + 12. The 1 second collection cycle ends. A metric is exported for the + number of requests received over the interval of time t_1 to + t_1+1 with a value of 1. + + Note: Even though, when reporting changes since last report time, using + CUMULATIVE is valid, it is not recommended. */ + AGGREGATION_TEMPORALITY_CUMULATIVE = 2; +} + +// ValueType describes the type and units of a value, with an optional aggregation temporality. +message ValueType { + int32 type_strindex = 1; // Index into string table. + int32 unit_strindex = 2; // Index into string table. + + AggregationTemporality aggregation_temporality = 3; +} + +// Each Sample records values encountered in some program +// context. The program context is typically a stack trace, perhaps +// augmented with auxiliary information like the thread-id, some +// indicator of a higher level request being handled etc. +message Sample { + // locations_start_index along with locations_length refers to to a slice of locations in Profile.location_indices. + int32 locations_start_index = 1; + // locations_length along with locations_start_index refers to a slice of locations in Profile.location_indices. + // Supersedes location_index. + int32 locations_length = 2; + // The type and unit of each value is defined by the corresponding + // entry in Profile.sample_type. All samples must have the same + // number of values, the same as the length of Profile.sample_type. + // When aggregating multiple samples into a single sample, the + // result has a list of values that is the element-wise sum of the + // lists of the originals. + repeated int64 value = 3; + // References to attributes in Profile.attribute_table. [optional] + repeated int32 attribute_indices = 4; + + // Reference to link in Profile.link_table. [optional] + optional int32 link_index = 5; + + // Timestamps associated with Sample represented in nanoseconds. These timestamps are expected + // to fall within the Profile's time range. [optional] + repeated uint64 timestamps_unix_nano = 6; +} + +// Describes the mapping of a binary in memory, including its address range, +// file offset, and metadata like build ID +message Mapping { + // Address at which the binary (or DLL) is loaded into memory. + uint64 memory_start = 1; + // The limit of the address range occupied by this mapping. + uint64 memory_limit = 2; + // Offset in the binary that corresponds to the first mapped address. + uint64 file_offset = 3; + // The object this entry is loaded from. This can be a filename on + // disk for the main binary and shared libraries, or virtual + // abstractions like "[vdso]". + int32 filename_strindex = 4; // Index into string table + // References to attributes in Profile.attribute_table. [optional] + repeated int32 attribute_indices = 5; + // The following fields indicate the resolution of symbolic info. + bool has_functions = 6; + bool has_filenames = 7; + bool has_line_numbers = 8; + bool has_inline_frames = 9; +} + +// Describes function and line table debug information. +message Location { + // Reference to mapping in Profile.mapping_table. + // It can be unset if the mapping is unknown or not applicable for + // this profile type. + optional int32 mapping_index = 1; + // The instruction address for this location, if available. It + // should be within [Mapping.memory_start...Mapping.memory_limit] + // for the corresponding mapping. A non-leaf address may be in the + // middle of a call instruction. It is up to display tools to find + // the beginning of the instruction if necessary. + uint64 address = 2; + // Multiple line indicates this location has inlined functions, + // where the last entry represents the caller into which the + // preceding entries were inlined. + // + // E.g., if memcpy() is inlined into printf: + // line[0].function_name == "memcpy" + // line[1].function_name == "printf" + repeated Line line = 3; + // Provides an indication that multiple symbols map to this location's + // address, for example due to identical code folding by the linker. In that + // case the line information above represents one of the multiple + // symbols. This field must be recomputed when the symbolization state of the + // profile changes. + bool is_folded = 4; + + // References to attributes in Profile.attribute_table. [optional] + repeated int32 attribute_indices = 5; +} + +// Details a specific line in a source code, linked to a function. +message Line { + // Reference to function in Profile.function_table. + int32 function_index = 1; + // Line number in source code. + int64 line = 2; + // Column number in source code. + int64 column = 3; +} + +// Describes a function, including its human-readable name, system name, +// source file, and starting line number in the source. +message Function { + // Name of the function, in human-readable form if available. + int32 name_strindex = 1; // Index into string table + // Name of the function, as identified by the system. + // For instance, it can be a C++ mangled name. + int32 system_name_strindex = 2; // Index into string table + // Source file containing the function. + int32 filename_strindex = 3; // Index into string table + // Line number in source file. + int64 start_line = 4; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/resource/v1/resource.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/resource/v1/resource.proto new file mode 100644 index 00000000..6637560b --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/resource/v1/resource.proto @@ -0,0 +1,37 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.resource.v1; + +import "opentelemetry/proto/common/v1/common.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Resource.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.resource.v1"; +option java_outer_classname = "ResourceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/resource/v1"; + +// Resource information. +message Resource { + // Set of attributes that describe the resource. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 1; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, then + // no attributes were dropped. + uint32 dropped_attributes_count = 2; +} diff --git a/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/trace/v1/trace.proto b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/trace/v1/trace.proto new file mode 100644 index 00000000..24442853 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/ot/opentelemetry/proto/trace/v1/trace.proto @@ -0,0 +1,357 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.trace.v1; + +import "opentelemetry/proto/common/v1/common.proto"; +import "opentelemetry/proto/resource/v1/resource.proto"; + +option csharp_namespace = "OpenTelemetry.Proto.Trace.V1"; +option java_multiple_files = true; +option java_package = "io.opentelemetry.proto.trace.v1"; +option java_outer_classname = "TraceProto"; +option go_package = "go.opentelemetry.io/proto/otlp/trace/v1"; + +// TracesData represents the traces data that can be stored in a persistent storage, +// OR can be embedded by other protocols that transfer OTLP traces data but do +// not implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +message TracesData { + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + repeated ResourceSpans resource_spans = 1; +} + +// A collection of ScopeSpans from a Resource. +message ResourceSpans { + reserved 1000; + + // The resource for the spans in this message. + // If this field is not set then no resource info is known. + opentelemetry.proto.resource.v1.Resource resource = 1; + + // A list of ScopeSpans that originate from a resource. + repeated ScopeSpans scope_spans = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the resource data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_spans" field which have their own schema_url field. + string schema_url = 3; +} + +// A collection of Spans produced by an InstrumentationScope. +message ScopeSpans { + // The instrumentation scope information for the spans in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + opentelemetry.proto.common.v1.InstrumentationScope scope = 1; + + // A list of Spans that originate from an instrumentation scope. + repeated Span spans = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the span data + // is recorded in. Notably, the last part of the URL path is the version number of the + // schema: http[s]://server[:port]/path/. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to all spans and span events in the "spans" field. + string schema_url = 3; +} + +// A Span represents a single operation performed by a single component of the system. +// +// The next available field id is 17. +message Span { + // A unique identifier for a trace. All spans from the same trace share + // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is required. + bytes trace_id = 1; + + // A unique identifier for a span within a trace, assigned when the span + // is created. The ID is an 8-byte array. An ID with all zeroes OR of length + // other than 8 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is required. + bytes span_id = 2; + + // trace_state conveys information about request position in multiple distributed tracing graphs. + // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header + // See also https://github.com/w3c/distributed-tracing for more details about this field. + string trace_state = 3; + + // The `span_id` of this span's parent span. If this is a root span, then this + // field must be empty. The ID is an 8-byte array. + bytes parent_span_id = 4; + + // Flags, a bit field. + // + // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + // Context specification. To read the 8-bit W3C trace flag, use + // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + // + // Bits 8 and 9 represent the 3 states of whether a span's parent + // is remote. The states are (unknown, is not remote, is remote). + // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + // To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + // + // When creating span messages, if the message is logically forwarded from another source + // with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD + // be copied as-is. If creating from a source that does not have an equivalent flags field + // (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST + // be set to zero. + // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + // + // [Optional]. + fixed32 flags = 16; + + // A description of the span's operation. + // + // For example, the name can be a qualified method name or a file name + // and a line number where the operation is called. A best practice is to use + // the same display name at the same call point in an application. + // This makes it easier to correlate spans in different traces. + // + // This field is semantically required to be set to non-empty string. + // Empty value is equivalent to an unknown span name. + // + // This field is required. + string name = 5; + + // SpanKind is the type of span. Can be used to specify additional relationships between spans + // in addition to a parent/child relationship. + enum SpanKind { + // Unspecified. Do NOT use as default. + // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + SPAN_KIND_UNSPECIFIED = 0; + + // Indicates that the span represents an internal operation within an application, + // as opposed to an operation happening at the boundaries. Default value. + SPAN_KIND_INTERNAL = 1; + + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + SPAN_KIND_SERVER = 2; + + // Indicates that the span describes a request to some remote service. + SPAN_KIND_CLIENT = 3; + + // Indicates that the span describes a producer sending a message to a broker. + // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + // between producer and consumer spans. A PRODUCER span ends when the message was accepted + // by the broker while the logical processing of the message might span a much longer time. + SPAN_KIND_PRODUCER = 4; + + // Indicates that the span describes consumer receiving a message from a broker. + // Like the PRODUCER kind, there is often no direct critical path latency relationship + // between producer and consumer spans. + SPAN_KIND_CONSUMER = 5; + } + + // Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `CLIENT` (caller) + // and `SERVER` (callee) to identify queueing latency associated with the span. + SpanKind kind = 6; + + // start_time_unix_nano is the start time of the span. On the client side, this is the time + // kept by the local machine where the span execution starts. On the server side, this + // is the time when the server's application handler starts running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + fixed64 start_time_unix_nano = 7; + + // end_time_unix_nano is the end time of the span. On the client side, this is the time + // kept by the local machine where the span execution ends. On the server side, this + // is the time when the server application handler stops running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + fixed64 end_time_unix_nano = 8; + + // attributes is a collection of key/value pairs. Note, global attributes + // like server name can be set using the resource API. Examples of attributes: + // + // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + // "/http/server_latency": 300 + // "example.com/myattribute": true + // "example.com/score": 10.239 + // + // The OpenTelemetry API specification further restricts the allowed value types: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 9; + + // dropped_attributes_count is the number of attributes that were discarded. Attributes + // can be discarded because their keys are too long or because there are too many + // attributes. If this value is 0, then no attributes were dropped. + uint32 dropped_attributes_count = 10; + + // Event is a time-stamped annotation of the span, consisting of user-supplied + // text description and key-value pairs. + message Event { + // time_unix_nano is the time the event occurred. + fixed64 time_unix_nano = 1; + + // name of the event. + // This field is semantically required to be set to non-empty string. + string name = 2; + + // attributes is a collection of attribute key/value pairs on the event. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 3; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + uint32 dropped_attributes_count = 4; + } + + // events is a collection of Event items. + repeated Event events = 11; + + // dropped_events_count is the number of dropped events. If the value is 0, then no + // events were dropped. + uint32 dropped_events_count = 12; + + // A pointer from the current span to another span in the same trace or in a + // different trace. For example, this can be used in batching operations, + // where a single batch handler processes multiple requests from different + // traces or when the handler receives a request from a different project. + message Link { + // A unique identifier of a trace that this linked span is part of. The ID is a + // 16-byte array. + bytes trace_id = 1; + + // A unique identifier for the linked span. The ID is an 8-byte array. + bytes span_id = 2; + + // The trace_state associated with the link. + string trace_state = 3; + + // attributes is a collection of attribute key/value pairs on the link. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated opentelemetry.proto.common.v1.KeyValue attributes = 4; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + uint32 dropped_attributes_count = 5; + + // Flags, a bit field. + // + // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + // Context specification. To read the 8-bit W3C trace flag, use + // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + // + // Bits 8 and 9 represent the 3 states of whether the link is remote. + // The states are (unknown, is not remote, is remote). + // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + // To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + // + // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + // When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. + // + // [Optional]. + fixed32 flags = 6; + } + + // links is a collection of Links, which are references from this span to a span + // in the same or different trace. + repeated Link links = 13; + + // dropped_links_count is the number of dropped links after the maximum size was + // enforced. If this value is 0, then no links were dropped. + uint32 dropped_links_count = 14; + + // An optional final status for this span. Semantically when Status isn't set, it means + // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + Status status = 15; +} + +// The Status type defines a logical error model that is suitable for different +// programming environments, including REST APIs and RPC APIs. +message Status { + reserved 1; + + // A developer-facing human readable error message. + string message = 2; + + // For the semantics of status codes see + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status + enum StatusCode { + // The default status. + STATUS_CODE_UNSET = 0; + // The Span has been validated by an Application developer or Operator to + // have completed successfully. + STATUS_CODE_OK = 1; + // The Span contains an error. + STATUS_CODE_ERROR = 2; + }; + + // The status code. + StatusCode code = 3; +} + +// SpanFlags represents constants used to interpret the +// Span.flags field, which is protobuf 'fixed32' type and is to +// be used as bit-fields. Each non-zero value defined in this enum is +// a bit-mask. To extract the bit-field, for example, use an +// expression like: +// +// (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) +// +// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. +// +// Note that Span flags were introduced in version 1.1 of the +// OpenTelemetry protocol. Older Span producers do not set this +// field, consequently consumers should not rely on the absence of a +// particular flag bit to indicate the presence of a particular feature. +enum SpanFlags { + // The zero value for the enum. Should not be used for comparisons. + // Instead use bitwise "and" with the appropriate mask as shown above. + SPAN_FLAGS_DO_NOT_USE = 0; + + // Bits 0-7 are used for trace flags. + SPAN_FLAGS_TRACE_FLAGS_MASK = 0x000000FF; + + // Bits 8 and 9 are used to indicate that the parent span or link span is remote. + // Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. + // Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. + SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 0x00000100; + SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 0x00000200; + + // Bits 10-31 are reserved for future use. +} diff --git a/test/WireMock.Net.Tests/OpenApiParser/PathUtilsTests.cs b/test/WireMock.Net.Tests/OpenApiParser/PathUtilsTests.cs new file mode 100644 index 00000000..64a853da --- /dev/null +++ b/test/WireMock.Net.Tests/OpenApiParser/PathUtilsTests.cs @@ -0,0 +1,40 @@ +// Copyright © WireMock.Net + +#if !(NET452 || NET461 || NETCOREAPP3_1) +using FluentAssertions; +using WireMock.Net.OpenApiParser.Utils; +using Xunit; + +namespace WireMock.Net.Tests.OpenApiParser; + +public class PathUtilsTests +{ + [Theory] + [InlineData(new string[] { }, "")] + [InlineData(new[] { "path1" }, "path1")] + [InlineData(new[] { "/path1" }, "/path1")] + [InlineData(new[] { "/path1/" }, "/path1")] + public void Combine_ShouldReturnCombinedPathTest1(string[] paths, string expected) + { + // Act + var result = PathUtils.Combine(paths); + + // Assert + result.Should().Be(expected); + } + + [Theory] + [InlineData("/path1", "path2")] + [InlineData("/path1/", "path2")] + [InlineData("/path1", "/path2")] + [InlineData("/path1", "path2/")] + public void Combine_ShouldReturnCombinedPathTest2(params string[] paths) + { + // Act + var result = PathUtils.Combine(paths); + + // Assert + result.Should().Be("/path1/path2"); + } +} +#endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt index 6733a459..382d8455 100644 --- a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt +++ b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt @@ -334,7 +334,7 @@ "processingTerminalId": "example-string", "order": { "orderId": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00", + "dateTime": "2024-06-19T12:34:56.000\u002B00:00", "description": "example-string", "amount": 42, "currency": "AED", @@ -1787,7 +1787,7 @@ "processingTerminalId": "example-string", "order": { "orderId": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00", + "dateTime": "2024-06-19T12:34:56.000\u002B00:00", "description": "example-string", "amount": 42, "currency": "AED" @@ -2714,7 +2714,7 @@ "processingTerminalId": "example-string", "order": { "orderId": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00", + "dateTime": "2024-06-19T12:34:56.000\u002B00:00", "description": "example-string", "amount": 42, "currency": "AED", @@ -2863,7 +2863,7 @@ "processingTerminalId": "example-string", "order": { "orderId": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00", + "dateTime": "2024-06-19T12:34:56.000\u002B00:00", "description": "example-string", "amount": 42, "currency": "AED" @@ -6893,7 +6893,7 @@ "operator": "example-string", "order": { "orderId": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00", + "dateTime": "2024-06-19T12:34:56.000\u002B00:00", "description": "example-string", "amount": 42, "currency": "AED", @@ -7093,7 +7093,7 @@ "offlineProcessing": { "operation": "offlineDecline", "approvalCode": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00" + "dateTime": "2024-06-19T12:34:56.000\u002B00:00" }, "autoCapture": true, "processAsSale": true @@ -11349,7 +11349,7 @@ { "op": "replace", "path": "/a/b/c", - "value": "42" + "value": 420 }, { "op": "move", @@ -11827,7 +11827,7 @@ { "op": "replace", "path": "/a/b/c", - "value": "42" + "value": 420 }, { "op": "move", @@ -12541,7 +12541,7 @@ { "op": "replace", "path": "/a/b/c", - "value": "42" + "value": 420 }, { "op": "move", @@ -12986,7 +12986,7 @@ "operator": "example-string", "order": { "orderId": "example-string", - "dateTime": "2024-06-19T12:34:56.000+00:00", + "dateTime": "2024-06-19T12:34:56.000\u002B00:00", "description": "example-string", "amount": 42, "currency": "AED", diff --git a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs index ba387683..2afddcaa 100644 --- a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs +++ b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs @@ -13,6 +13,7 @@ namespace WireMock.Net.Tests.OpenApiParser; [UsesVerify] public class WireMockOpenApiParserTests { + private readonly DateTime _exampleDateTime = new(2024, 6, 19, 12, 34, 56, DateTimeKind.Utc); private readonly Mock _exampleValuesMock = new(); private readonly WireMockOpenApiParser _sut = new(); @@ -22,12 +23,12 @@ public class WireMockOpenApiParserTests _exampleValuesMock.SetupGet(e => e.Boolean).Returns(true); _exampleValuesMock.SetupGet(e => e.Integer).Returns(42); _exampleValuesMock.SetupGet(e => e.Float).Returns(1.1f); - _exampleValuesMock.SetupGet(e => e.Double).Returns(2.2d); + _exampleValuesMock.SetupGet(e => e.Decimal).Returns(2.2m); _exampleValuesMock.SetupGet(e => e.String).Returns("example-string"); _exampleValuesMock.SetupGet(e => e.Object).Returns("example-object"); _exampleValuesMock.SetupGet(e => e.Bytes).Returns("Stef"u8.ToArray()); - _exampleValuesMock.SetupGet(e => e.Date).Returns(() => new DateTime(2024, 6, 19)); - _exampleValuesMock.SetupGet(e => e.DateTime).Returns(() => new DateTime(2024, 6, 19, 12, 34, 56, DateTimeKind.Utc)); + _exampleValuesMock.SetupGet(e => e.Date).Returns(() => _exampleDateTime.Date); + _exampleValuesMock.SetupGet(e => e.DateTime).Returns(() => _exampleDateTime); } [Fact] diff --git a/test/WireMock.Net.Tests/OpenApiParser/payroc-openapi-spec.yaml b/test/WireMock.Net.Tests/OpenApiParser/payroc-openapi-spec.yaml index 2c93ed18..0b56e2f3 100644 --- a/test/WireMock.Net.Tests/OpenApiParser/payroc-openapi-spec.yaml +++ b/test/WireMock.Net.Tests/OpenApiParser/payroc-openapi-spec.yaml @@ -8,7 +8,7 @@ info: url: https://docs.payroc.com/api email: helpdesk@payroc.com servers: - - url: https://api.payroc.com/v1 + - url: https://api.payroc.com/v1/ description: External URL security: - bearerAuth: [] @@ -3734,7 +3734,7 @@ paths: path: /a/b/c - op: replace path: /a/b/c - value: 42 + value: 420 - op: move from: /a/b/c path: /a/b/d diff --git a/test/WireMock.Net.Tests/Util/PathUtilsTests.cs b/test/WireMock.Net.Tests/Util/FilePathUtilsTests.cs similarity index 79% rename from test/WireMock.Net.Tests/Util/PathUtilsTests.cs rename to test/WireMock.Net.Tests/Util/FilePathUtilsTests.cs index 52550ca0..a53aa5bf 100644 --- a/test/WireMock.Net.Tests/Util/PathUtilsTests.cs +++ b/test/WireMock.Net.Tests/Util/FilePathUtilsTests.cs @@ -7,7 +7,7 @@ using Xunit; namespace WireMock.Net.Tests.Util; -public class PathUtilsTests +public class FilePathUtilsTests { [Theory] [InlineData(@"subdirectory/MyXmlResponse.xml")] @@ -15,7 +15,7 @@ public class PathUtilsTests public void PathUtils_CleanPath(string path) { // Act - var cleanPath = PathUtils.CleanPath(path); + var cleanPath = FilePathUtils.CleanPath(path); // Assert Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml"); @@ -34,10 +34,10 @@ public class PathUtilsTests public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected) { // Arrange - var cleanPath = PathUtils.CleanPath(path); + var cleanPath = FilePathUtils.CleanPath(path); // Act - var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath); + var withoutDirectorySeparators = FilePathUtils.RemoveLeadingDirectorySeparators(cleanPath); // Assert Check.That(withoutDirectorySeparators).Equals(expected); diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index 884ad54f..a0929ccb 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -134,6 +134,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -150,6 +153,12 @@ Client PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs index 57d45e44..217588c1 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs @@ -3,6 +3,7 @@ #if !NET452 using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -319,5 +320,62 @@ public partial class WireMockServerTests server.Stop(); } + + [Fact] + public async Task WireMockServer_WithSseBody() + { + // Arrange + var server = WireMockServer.Start(); + server + .WhenRequest(r => r + .UsingGet() + .WithPath("/sse") + ) + .ThenRespondWith(r => r + .WithHeader("Content-Type", "text/event-stream") + .WithHeader("Cache-Control", "no-cache") + .WithHeader("Connection", "keep-alive") + .WithSseBody(async (_, queue) => + { + for (var i = 1; i <= 3; i++) + { + queue.Write($"x {i};\r\n"); + await Task.Delay(100); + } + + queue.Close(); + }) + ); + + server + .WhenRequest(r => r + .UsingGet() + ) + .ThenRespondWith(r => r + .WithBody("normal") + ); + + using var client = new HttpClient(); + + // Act 1 + var normal = await new HttpClient() + .GetAsync(server.Url) + .ConfigureAwait(false); + (await normal.Content.ReadAsStringAsync()).Should().Be("normal"); + + // Act 2 + using var response = await client.GetStreamAsync($"{server.Url}/sse"); + using var reader = new StreamReader(response); + + var data = string.Empty; + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync(); + data += line; + } + + // Assert 2 + data.Should().Be("x 1;x 2;x 3;"); + } } #endif \ No newline at end of file