| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.IO; |
| | | 4 | | using System.Linq; |
| | | 5 | | using System.Text; |
| | | 6 | | using JetBrains.Annotations; |
| | | 7 | | using Newtonsoft.Json; |
| | | 8 | | using SimMetrics.Net; |
| | | 9 | | using WireMock.Admin.Mappings; |
| | | 10 | | using WireMock.Admin.Requests; |
| | | 11 | | using WireMock.Admin.Settings; |
| | | 12 | | using WireMock.Logging; |
| | | 13 | | using WireMock.Matchers; |
| | | 14 | | using WireMock.Matchers.Request; |
| | | 15 | | using WireMock.RequestBuilders; |
| | | 16 | | using WireMock.ResponseBuilders; |
| | | 17 | | using WireMock.Util; |
| | | 18 | | using WireMock.Validation; |
| | | 19 | | |
| | | 20 | | namespace WireMock.Server |
| | | 21 | | { |
| | | 22 | | /// <summary> |
| | | 23 | | /// The fluent mock server. |
| | | 24 | | /// </summary> |
| | | 25 | | public partial class FluentMockServer |
| | | 26 | | { |
| | 1 | 27 | | private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings"); |
| | | 28 | | private const string AdminMappings = "/__admin/mappings"; |
| | | 29 | | private const string AdminRequests = "/__admin/requests"; |
| | | 30 | | private const string AdminSettings = "/__admin/settings"; |
| | 18 | 31 | | private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(@"^\/__admin\/mappings\/(\{{0,1}( |
| | 18 | 32 | | private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(@"^\/__admin\/requests\/(\{{0,1}( |
| | | 33 | | |
| | 18 | 34 | | private readonly JsonSerializerSettings _settings = new JsonSerializerSettings |
| | 18 | 35 | | { |
| | 18 | 36 | | Formatting = Formatting.Indented, |
| | 18 | 37 | | NullValueHandling = NullValueHandling.Ignore, |
| | 18 | 38 | | }; |
| | | 39 | | |
| | | 40 | | /// <summary> |
| | | 41 | | /// Reads the static mappings from a folder. |
| | | 42 | | /// </summary> |
| | | 43 | | /// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param> |
| | | 44 | | [PublicAPI] |
| | | 45 | | public void ReadStaticMappings([CanBeNull] string folder = null) |
| | 1 | 46 | | { |
| | 1 | 47 | | if (folder == null) |
| | 0 | 48 | | folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder); |
| | | 49 | | |
| | 1 | 50 | | if (!Directory.Exists(folder)) |
| | 0 | 51 | | return; |
| | | 52 | | |
| | 9 | 53 | | foreach (string filename in Directory.EnumerateFiles(folder).OrderBy(f => f)) |
| | 2 | 54 | | { |
| | 2 | 55 | | ReadStaticMapping(filename); |
| | 2 | 56 | | } |
| | 1 | 57 | | } |
| | | 58 | | |
| | | 59 | | /// <summary> |
| | | 60 | | /// Reads the static mapping. |
| | | 61 | | /// </summary> |
| | | 62 | | /// <param name="filename">The filename.</param> |
| | | 63 | | [PublicAPI] |
| | | 64 | | public void ReadStaticMapping([NotNull] string filename) |
| | 4 | 65 | | { |
| | 4 | 66 | | Check.NotNull(filename, nameof(filename)); |
| | | 67 | | |
| | 4 | 68 | | string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename); |
| | | 69 | | Guid guidFromFilename; |
| | | 70 | | |
| | 4 | 71 | | if (Guid.TryParse(filenameWithoutExtension, out guidFromFilename)) |
| | 2 | 72 | | { |
| | 2 | 73 | | DeserializeAndAddMapping(File.ReadAllText(filename), guidFromFilename); |
| | 2 | 74 | | } |
| | | 75 | | else |
| | 2 | 76 | | { |
| | 2 | 77 | | DeserializeAndAddMapping(File.ReadAllText(filename)); |
| | 2 | 78 | | } |
| | 4 | 79 | | } |
| | | 80 | | |
| | | 81 | | private void InitAdmin() |
| | 0 | 82 | | { |
| | | 83 | | // __admin/settings |
| | 0 | 84 | | Given(Request.Create().WithPath(AdminSettings).UsingGet()).RespondWith(new DynamicResponseProvider(SettingsG |
| | 0 | 85 | | Given(Request.Create().WithPath(AdminSettings).UsingVerb("PUT", "POST")).RespondWith(new DynamicResponseProv |
| | | 86 | | |
| | | 87 | | |
| | | 88 | | // __admin/mappings |
| | 0 | 89 | | Given(Request.Create().WithPath(AdminMappings).UsingGet()).RespondWith(new DynamicResponseProvider(MappingsG |
| | 0 | 90 | | Given(Request.Create().WithPath(AdminMappings).UsingPost()).RespondWith(new DynamicResponseProvider(Mappings |
| | 0 | 91 | | Given(Request.Create().WithPath(AdminMappings).UsingDelete()).RespondWith(new DynamicResponseProvider(Mappin |
| | | 92 | | |
| | | 93 | | // __admin/mappings/reset |
| | 0 | 94 | | Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).RespondWith(new DynamicResponseProvid |
| | | 95 | | |
| | | 96 | | // __admin/mappings/{guid} |
| | 0 | 97 | | Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingGet()).RespondWith(new DynamicResponsePr |
| | 0 | 98 | | Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader("Content-Type", "applic |
| | 0 | 99 | | Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).RespondWith(new DynamicRespons |
| | | 100 | | |
| | | 101 | | // __admin/mappings/save |
| | 0 | 102 | | Given(Request.Create().WithPath(AdminMappings + "/save").UsingPost()).RespondWith(new DynamicResponseProvide |
| | | 103 | | |
| | | 104 | | |
| | | 105 | | // __admin/requests |
| | 0 | 106 | | Given(Request.Create().WithPath(AdminRequests).UsingGet()).RespondWith(new DynamicResponseProvider(RequestsG |
| | 0 | 107 | | Given(Request.Create().WithPath(AdminRequests).UsingDelete()).RespondWith(new DynamicResponseProvider(Reques |
| | | 108 | | |
| | | 109 | | // __admin/requests/reset |
| | 0 | 110 | | Given(Request.Create().WithPath(AdminRequests + "/reset").UsingPost()).RespondWith(new DynamicResponseProvid |
| | | 111 | | |
| | | 112 | | // __admin/request/{guid} |
| | 0 | 113 | | Given(Request.Create().WithPath(_adminRequestsGuidPathMatcher).UsingGet()).RespondWith(new DynamicResponsePr |
| | 0 | 114 | | Given(Request.Create().WithPath(_adminRequestsGuidPathMatcher).UsingDelete()).RespondWith(new DynamicRespons |
| | | 115 | | |
| | | 116 | | // __admin/requests/find |
| | 0 | 117 | | Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).RespondWith(new DynamicResponseProvide |
| | 0 | 118 | | } |
| | | 119 | | |
| | | 120 | | #region Settings |
| | | 121 | | private ResponseMessage SettingsGet(RequestMessage requestMessage) |
| | 0 | 122 | | { |
| | 0 | 123 | | var model = new SettingsModel |
| | 0 | 124 | | { |
| | 0 | 125 | | AllowPartialMapping = _options.AllowPartialMapping, |
| | 0 | 126 | | GlobalProcessingDelay = _options.RequestProcessingDelay?.Milliseconds |
| | 0 | 127 | | }; |
| | | 128 | | |
| | 0 | 129 | | return ToJson(model); |
| | 0 | 130 | | } |
| | | 131 | | |
| | | 132 | | private ResponseMessage SettingsUpdate(RequestMessage requestMessage) |
| | 0 | 133 | | { |
| | 0 | 134 | | var settings = JsonConvert.DeserializeObject<SettingsModel>(requestMessage.Body); |
| | | 135 | | |
| | 0 | 136 | | if (settings.AllowPartialMapping != null) |
| | 0 | 137 | | _options.AllowPartialMapping = settings.AllowPartialMapping.Value; |
| | | 138 | | |
| | 0 | 139 | | if (settings.GlobalProcessingDelay != null) |
| | 0 | 140 | | _options.RequestProcessingDelay = TimeSpan.FromMilliseconds(settings.GlobalProcessingDelay.Value); |
| | | 141 | | |
| | 0 | 142 | | return new ResponseMessage { Body = "Settings updated" }; |
| | 0 | 143 | | } |
| | | 144 | | #endregion Settings |
| | | 145 | | |
| | | 146 | | #region Mapping/{guid} |
| | | 147 | | private ResponseMessage MappingGet(RequestMessage requestMessage) |
| | 0 | 148 | | { |
| | 0 | 149 | | Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1)); |
| | 0 | 150 | | var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid); |
| | | 151 | | |
| | 0 | 152 | | if (mapping == null) |
| | 0 | 153 | | return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" }; |
| | | 154 | | |
| | 0 | 155 | | var model = ToMappingModel(mapping); |
| | | 156 | | |
| | 0 | 157 | | return ToJson(model); |
| | 0 | 158 | | } |
| | | 159 | | |
| | | 160 | | private ResponseMessage MappingPut(RequestMessage requestMessage) |
| | 0 | 161 | | { |
| | 0 | 162 | | Guid guid = Guid.Parse(requestMessage.Path.TrimStart(AdminMappings.ToCharArray())); |
| | 0 | 163 | | var mappingModel = JsonConvert.DeserializeObject<MappingModel>(requestMessage.Body); |
| | | 164 | | |
| | 0 | 165 | | if (mappingModel.Request == null) |
| | 0 | 166 | | return new ResponseMessage { StatusCode = 400, Body = "Request missing" }; |
| | | 167 | | |
| | 0 | 168 | | if (mappingModel.Response == null) |
| | 0 | 169 | | return new ResponseMessage { StatusCode = 400, Body = "Response missing" }; |
| | | 170 | | |
| | 0 | 171 | | var requestBuilder = InitRequestBuilder(mappingModel.Request); |
| | 0 | 172 | | var responseBuilder = InitResponseBuilder(mappingModel.Response); |
| | | 173 | | |
| | 0 | 174 | | IRespondWithAProvider respondProvider = Given(requestBuilder).WithGuid(guid); |
| | | 175 | | |
| | 0 | 176 | | if (!string.IsNullOrEmpty(mappingModel.Title)) |
| | 0 | 177 | | respondProvider = respondProvider.WithTitle(mappingModel.Title); |
| | | 178 | | |
| | 0 | 179 | | respondProvider.RespondWith(responseBuilder); |
| | | 180 | | |
| | 0 | 181 | | return new ResponseMessage { Body = "Mapping added or updated" }; |
| | 0 | 182 | | } |
| | | 183 | | |
| | | 184 | | private ResponseMessage MappingDelete(RequestMessage requestMessage) |
| | 0 | 185 | | { |
| | 0 | 186 | | Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1)); |
| | | 187 | | |
| | 0 | 188 | | if (DeleteMapping(guid)) |
| | 0 | 189 | | return new ResponseMessage { Body = "Mapping removed" }; |
| | | 190 | | |
| | 0 | 191 | | return new ResponseMessage { Body = "Mapping not found" }; |
| | 0 | 192 | | } |
| | | 193 | | #endregion Mapping/{guid} |
| | | 194 | | |
| | | 195 | | #region Mappings |
| | | 196 | | private ResponseMessage MappingsSave(RequestMessage requestMessage) |
| | 0 | 197 | | { |
| | 0 | 198 | | string folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder); |
| | 0 | 199 | | if (!Directory.Exists(folder)) |
| | 0 | 200 | | Directory.CreateDirectory(folder); |
| | | 201 | | |
| | 0 | 202 | | foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface)) |
| | 0 | 203 | | { |
| | 0 | 204 | | var model = ToMappingModel(mapping); |
| | 0 | 205 | | string json = JsonConvert.SerializeObject(model, _settings); |
| | 0 | 206 | | string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid. |
| | | 207 | | |
| | 0 | 208 | | File.WriteAllText(Path.Combine(folder, filename + ".json"), json); |
| | 0 | 209 | | } |
| | | 210 | | |
| | 0 | 211 | | return new ResponseMessage { Body = "Mappings saved to disk" }; |
| | 0 | 212 | | } |
| | | 213 | | |
| | | 214 | | private static string SanitizeFileName(string name, char replaceChar = '_') |
| | 0 | 215 | | { |
| | 0 | 216 | | return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar)); |
| | 0 | 217 | | } |
| | | 218 | | |
| | | 219 | | private ResponseMessage MappingsGet(RequestMessage requestMessage) |
| | 0 | 220 | | { |
| | 0 | 221 | | var result = new List<MappingModel>(); |
| | 0 | 222 | | foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface)) |
| | 0 | 223 | | { |
| | 0 | 224 | | var model = ToMappingModel(mapping); |
| | 0 | 225 | | result.Add(model); |
| | 0 | 226 | | } |
| | | 227 | | |
| | 0 | 228 | | return ToJson(result); |
| | 0 | 229 | | } |
| | | 230 | | |
| | | 231 | | private ResponseMessage MappingsPost(RequestMessage requestMessage) |
| | 0 | 232 | | { |
| | | 233 | | try |
| | 0 | 234 | | { |
| | 0 | 235 | | DeserializeAndAddMapping(requestMessage.Body); |
| | 0 | 236 | | } |
| | 0 | 237 | | catch (ArgumentException a) |
| | 0 | 238 | | { |
| | 0 | 239 | | return new ResponseMessage { StatusCode = 400, Body = a.Message }; |
| | | 240 | | } |
| | 0 | 241 | | catch (Exception e) |
| | 0 | 242 | | { |
| | 0 | 243 | | return new ResponseMessage { StatusCode = 500, Body = e.ToString() }; |
| | | 244 | | } |
| | | 245 | | |
| | 0 | 246 | | return new ResponseMessage { StatusCode = 201, Body = "Mapping added" }; |
| | 0 | 247 | | } |
| | | 248 | | |
| | | 249 | | private void DeserializeAndAddMapping(string json, Guid? guid = null) |
| | 4 | 250 | | { |
| | 4 | 251 | | var mappingModel = JsonConvert.DeserializeObject<MappingModel>(json); |
| | | 252 | | |
| | 4 | 253 | | Check.NotNull(mappingModel, nameof(mappingModel)); |
| | 4 | 254 | | Check.NotNull(mappingModel.Request, nameof(mappingModel.Request)); |
| | 4 | 255 | | Check.NotNull(mappingModel.Response, nameof(mappingModel.Response)); |
| | | 256 | | |
| | 4 | 257 | | var requestBuilder = InitRequestBuilder(mappingModel.Request); |
| | 4 | 258 | | var responseBuilder = InitResponseBuilder(mappingModel.Response); |
| | | 259 | | |
| | 4 | 260 | | IRespondWithAProvider respondProvider = Given(requestBuilder); |
| | | 261 | | |
| | 4 | 262 | | if (guid != null) |
| | 2 | 263 | | { |
| | 2 | 264 | | respondProvider = respondProvider.WithGuid(guid.Value); |
| | 2 | 265 | | } |
| | 2 | 266 | | else if (mappingModel.Guid != null && mappingModel.Guid != Guid.Empty) |
| | 2 | 267 | | { |
| | 2 | 268 | | respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value); |
| | 2 | 269 | | } |
| | | 270 | | |
| | 4 | 271 | | if (!string.IsNullOrEmpty(mappingModel.Title)) |
| | 2 | 272 | | respondProvider = respondProvider.WithTitle(mappingModel.Title); |
| | | 273 | | |
| | 4 | 274 | | if (mappingModel.Priority != null) |
| | 4 | 275 | | respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value); |
| | | 276 | | |
| | 4 | 277 | | respondProvider.RespondWith(responseBuilder); |
| | 4 | 278 | | } |
| | | 279 | | |
| | | 280 | | private ResponseMessage MappingsDelete(RequestMessage requestMessage) |
| | 0 | 281 | | { |
| | 0 | 282 | | ResetMappings(); |
| | | 283 | | |
| | 0 | 284 | | return new ResponseMessage { Body = "Mappings deleted" }; |
| | 0 | 285 | | } |
| | | 286 | | #endregion Mappings |
| | | 287 | | |
| | | 288 | | #region Request/{guid} |
| | | 289 | | private ResponseMessage RequestGet(RequestMessage requestMessage) |
| | 0 | 290 | | { |
| | 0 | 291 | | Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminRequests.Length + 1)); |
| | 0 | 292 | | var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid) |
| | | 293 | | |
| | 0 | 294 | | if (entry == null) |
| | 0 | 295 | | return new ResponseMessage { StatusCode = 404, Body = "Request not found" }; |
| | | 296 | | |
| | 0 | 297 | | var model = ToLogEntryModel(entry); |
| | | 298 | | |
| | 0 | 299 | | return ToJson(model); |
| | 0 | 300 | | } |
| | | 301 | | |
| | | 302 | | private ResponseMessage RequestDelete(RequestMessage requestMessage) |
| | 0 | 303 | | { |
| | 0 | 304 | | Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminRequests.Length + 1)); |
| | | 305 | | |
| | 0 | 306 | | if (DeleteLogEntry(guid)) |
| | 0 | 307 | | return new ResponseMessage { Body = "Request removed" }; |
| | | 308 | | |
| | 0 | 309 | | return new ResponseMessage { Body = "Request not found" }; |
| | 0 | 310 | | } |
| | | 311 | | #endregion Request/{guid} |
| | | 312 | | |
| | | 313 | | #region Requests |
| | | 314 | | private ResponseMessage RequestsGet(RequestMessage requestMessage) |
| | 0 | 315 | | { |
| | 0 | 316 | | var result = LogEntries |
| | 0 | 317 | | .Where(r => !r.RequestMessage.Path.StartsWith("/__admin/")) |
| | 0 | 318 | | .Select(ToLogEntryModel); |
| | | 319 | | |
| | 0 | 320 | | return ToJson(result); |
| | 0 | 321 | | } |
| | | 322 | | |
| | | 323 | | private LogEntryModel ToLogEntryModel(LogEntry logEntry) |
| | 0 | 324 | | { |
| | 0 | 325 | | return new LogEntryModel |
| | 0 | 326 | | { |
| | 0 | 327 | | Guid = logEntry.Guid, |
| | 0 | 328 | | Request = new LogRequestModel |
| | 0 | 329 | | { |
| | 0 | 330 | | DateTime = logEntry.RequestMessage.DateTime, |
| | 0 | 331 | | Path = logEntry.RequestMessage.Path, |
| | 0 | 332 | | AbsoluteUrl = logEntry.RequestMessage.Url, |
| | 0 | 333 | | Query = logEntry.RequestMessage.Query, |
| | 0 | 334 | | Method = logEntry.RequestMessage.Method, |
| | 0 | 335 | | Body = logEntry.RequestMessage.Body, |
| | 0 | 336 | | Headers = logEntry.RequestMessage.Headers, |
| | 0 | 337 | | Cookies = logEntry.RequestMessage.Cookies, |
| | 0 | 338 | | BodyEncoding = logEntry.RequestMessage.BodyEncoding != null ? new EncodingModel |
| | 0 | 339 | | { |
| | 0 | 340 | | EncodingName = logEntry.RequestMessage.BodyEncoding.EncodingName, |
| | 0 | 341 | | CodePage = logEntry.RequestMessage.BodyEncoding.CodePage, |
| | 0 | 342 | | WebName = logEntry.RequestMessage.BodyEncoding.WebName |
| | 0 | 343 | | } : null |
| | 0 | 344 | | }, |
| | 0 | 345 | | Response = new LogResponseModel |
| | 0 | 346 | | { |
| | 0 | 347 | | StatusCode = logEntry.ResponseMessage.StatusCode, |
| | 0 | 348 | | Body = logEntry.ResponseMessage.Body, |
| | 0 | 349 | | BodyOriginal = logEntry.ResponseMessage.BodyOriginal, |
| | 0 | 350 | | Headers = logEntry.ResponseMessage.Headers, |
| | 0 | 351 | | BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel |
| | 0 | 352 | | { |
| | 0 | 353 | | EncodingName = logEntry.ResponseMessage.BodyEncoding.EncodingName, |
| | 0 | 354 | | CodePage = logEntry.ResponseMessage.BodyEncoding.CodePage, |
| | 0 | 355 | | WebName = logEntry.ResponseMessage.BodyEncoding.WebName |
| | 0 | 356 | | } : null |
| | 0 | 357 | | }, |
| | 0 | 358 | | MappingGuid = logEntry.MappingGuid, |
| | 0 | 359 | | MappingTitle = logEntry.MappingTitle, |
| | 0 | 360 | | RequestMatchResult = logEntry.RequestMatchResult != null ? new LogRequestMatchModel |
| | 0 | 361 | | { |
| | 0 | 362 | | TotalScore = logEntry.RequestMatchResult.TotalScore, |
| | 0 | 363 | | TotalNumber = logEntry.RequestMatchResult.TotalNumber, |
| | 0 | 364 | | IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch, |
| | 0 | 365 | | AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore |
| | 0 | 366 | | } : null |
| | 0 | 367 | | }; |
| | 0 | 368 | | } |
| | | 369 | | |
| | | 370 | | private ResponseMessage RequestsDelete(RequestMessage requestMessage) |
| | 0 | 371 | | { |
| | 0 | 372 | | ResetLogEntries(); |
| | | 373 | | |
| | 0 | 374 | | return new ResponseMessage { Body = "Requests deleted" }; |
| | 0 | 375 | | } |
| | | 376 | | #endregion Requests |
| | | 377 | | |
| | | 378 | | #region Requests/find |
| | | 379 | | private ResponseMessage RequestsFind(RequestMessage requestMessage) |
| | 0 | 380 | | { |
| | 0 | 381 | | var requestModel = JsonConvert.DeserializeObject<RequestModel>(requestMessage.Body); |
| | | 382 | | |
| | 0 | 383 | | var request = (Request)InitRequestBuilder(requestModel); |
| | | 384 | | |
| | 0 | 385 | | var dict = new Dictionary<LogEntry, RequestMatchResult>(); |
| | 0 | 386 | | foreach (var logEntry in LogEntries.Where(le => !le.RequestMessage.Path.StartsWith("/__admin/"))) |
| | 0 | 387 | | { |
| | 0 | 388 | | var requestMatchResult = new RequestMatchResult(); |
| | 0 | 389 | | if (request.GetMatchingScore(logEntry.RequestMessage, requestMatchResult) > 0.99) |
| | 0 | 390 | | dict.Add(logEntry, requestMatchResult); |
| | 0 | 391 | | } |
| | | 392 | | |
| | 0 | 393 | | var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key); |
| | | 394 | | |
| | 0 | 395 | | return ToJson(result); |
| | 0 | 396 | | } |
| | | 397 | | #endregion Requests/find |
| | | 398 | | |
| | | 399 | | private IRequestBuilder InitRequestBuilder(RequestModel requestModel) |
| | 4 | 400 | | { |
| | 4 | 401 | | IRequestBuilder requestBuilder = Request.Create(); |
| | | 402 | | |
| | 4 | 403 | | if (requestModel.Path != null) |
| | 4 | 404 | | { |
| | 4 | 405 | | string path = requestModel.Path as string; |
| | 4 | 406 | | if (path != null) |
| | 0 | 407 | | requestBuilder = requestBuilder.WithPath(path); |
| | | 408 | | else |
| | 4 | 409 | | { |
| | 4 | 410 | | var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path); |
| | 4 | 411 | | if (pathModel?.Matchers != null) |
| | 4 | 412 | | requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(Map).ToArray()); |
| | 4 | 413 | | } |
| | 4 | 414 | | } |
| | | 415 | | |
| | 4 | 416 | | if (requestModel.Url != null) |
| | 0 | 417 | | { |
| | 0 | 418 | | string url = requestModel.Url as string; |
| | 0 | 419 | | if (url != null) |
| | 0 | 420 | | requestBuilder = requestBuilder.WithUrl(url); |
| | | 421 | | else |
| | 0 | 422 | | { |
| | 0 | 423 | | var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url); |
| | 0 | 424 | | if (urlModel?.Matchers != null) |
| | 0 | 425 | | requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(Map).ToArray()); |
| | 0 | 426 | | } |
| | 0 | 427 | | } |
| | | 428 | | |
| | 4 | 429 | | if (requestModel.Methods != null) |
| | 4 | 430 | | requestBuilder = requestBuilder.UsingVerb(requestModel.Methods); |
| | | 431 | | |
| | 4 | 432 | | if (requestModel.Headers != null) |
| | 0 | 433 | | { |
| | 0 | 434 | | foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null)) |
| | 0 | 435 | | { |
| | 0 | 436 | | requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(Map).ToArra |
| | 0 | 437 | | } |
| | 0 | 438 | | } |
| | | 439 | | |
| | 4 | 440 | | if (requestModel.Cookies != null) |
| | 0 | 441 | | { |
| | 0 | 442 | | foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null)) |
| | 0 | 443 | | { |
| | 0 | 444 | | requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(Map).ToArra |
| | 0 | 445 | | } |
| | 0 | 446 | | } |
| | | 447 | | |
| | 4 | 448 | | if (requestModel.Params != null) |
| | 0 | 449 | | { |
| | 0 | 450 | | foreach (var paramModel in requestModel.Params.Where(p => p.Values != null)) |
| | 0 | 451 | | { |
| | 0 | 452 | | requestBuilder = requestBuilder.WithParam(paramModel.Name, paramModel.Values.ToArray()); |
| | 0 | 453 | | } |
| | 0 | 454 | | } |
| | | 455 | | |
| | 4 | 456 | | if (requestModel.Body?.Matcher != null) |
| | 2 | 457 | | { |
| | 2 | 458 | | var bodyMatcher = Map(requestModel.Body.Matcher); |
| | 2 | 459 | | requestBuilder = requestBuilder.WithBody(bodyMatcher); |
| | 2 | 460 | | } |
| | | 461 | | |
| | 4 | 462 | | return requestBuilder; |
| | 4 | 463 | | } |
| | | 464 | | |
| | | 465 | | private IResponseBuilder InitResponseBuilder(ResponseModel responseModel) |
| | 4 | 466 | | { |
| | 4 | 467 | | IResponseBuilder responseBuilder = Response.Create(); |
| | | 468 | | |
| | 4 | 469 | | if (responseModel.StatusCode.HasValue) |
| | 4 | 470 | | responseBuilder = responseBuilder.WithStatusCode(responseModel.StatusCode.Value); |
| | | 471 | | |
| | 4 | 472 | | if (responseModel.Headers != null) |
| | 4 | 473 | | responseBuilder = responseBuilder.WithHeaders(responseModel.Headers); |
| | 0 | 474 | | else if (responseModel.HeadersRaw != null) |
| | 0 | 475 | | { |
| | 0 | 476 | | foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions. |
| | 0 | 477 | | { |
| | 0 | 478 | | int indexColon = headerLine.IndexOf(":", StringComparison.Ordinal); |
| | 0 | 479 | | string key = headerLine.Substring(0, indexColon).TrimStart(' ', '\t'); |
| | 0 | 480 | | string value = headerLine.Substring(indexColon + 1).TrimStart(' ', '\t'); |
| | 0 | 481 | | responseBuilder = responseBuilder.WithHeader(key, value); |
| | 0 | 482 | | } |
| | 0 | 483 | | } |
| | | 484 | | |
| | 4 | 485 | | if (responseModel.Body != null) |
| | 4 | 486 | | responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding)); |
| | 0 | 487 | | else if (responseModel.BodyAsJson != null) |
| | 0 | 488 | | responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.Body |
| | 0 | 489 | | else if (responseModel.BodyAsBase64 != null) |
| | 0 | 490 | | responseBuilder = responseBuilder.WithBodyAsBase64(responseModel.BodyAsBase64, ToEncoding(responseModel. |
| | | 491 | | |
| | 4 | 492 | | if (responseModel.UseTransformer) |
| | 0 | 493 | | responseBuilder = responseBuilder.WithTransformer(); |
| | | 494 | | |
| | 4 | 495 | | if (responseModel.Delay > 0) |
| | 0 | 496 | | responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value); |
| | | 497 | | |
| | 4 | 498 | | return responseBuilder; |
| | 4 | 499 | | } |
| | | 500 | | |
| | | 501 | | private MappingModel ToMappingModel(Mapping mapping) |
| | 0 | 502 | | { |
| | 0 | 503 | | var request = (Request)mapping.RequestMatcher; |
| | 0 | 504 | | var response = (Response)mapping.Provider; |
| | | 505 | | |
| | 0 | 506 | | var pathMatchers = request.GetRequestMessageMatchers<RequestMessagePathMatcher>(); |
| | 0 | 507 | | var urlMatchers = request.GetRequestMessageMatchers<RequestMessageUrlMatcher>(); |
| | 0 | 508 | | var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>(); |
| | 0 | 509 | | var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>(); |
| | 0 | 510 | | var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>(); |
| | 0 | 511 | | var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>(); |
| | 0 | 512 | | var methodMatcher = request.GetRequestMessageMatcher<RequestMessageMethodMatcher>(); |
| | | 513 | | |
| | 0 | 514 | | return new MappingModel |
| | 0 | 515 | | { |
| | 0 | 516 | | Guid = mapping.Guid, |
| | 0 | 517 | | Title = mapping.Title, |
| | 0 | 518 | | Priority = mapping.Priority, |
| | 0 | 519 | | Request = new RequestModel |
| | 0 | 520 | | { |
| | 0 | 521 | | Path = pathMatchers != null && pathMatchers.Any() ? new PathModel |
| | 0 | 522 | | { |
| | 0 | 523 | | Matchers = Map(pathMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)), |
| | 0 | 524 | | Funcs = Map(pathMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs)) |
| | 0 | 525 | | } : null, |
| | 0 | 526 | | |
| | 0 | 527 | | Url = urlMatchers != null && urlMatchers.Any() ? new UrlModel |
| | 0 | 528 | | { |
| | 0 | 529 | | Matchers = Map(urlMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)), |
| | 0 | 530 | | Funcs = Map(urlMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs)) |
| | 0 | 531 | | } : null, |
| | 0 | 532 | | |
| | 0 | 533 | | Methods = methodMatcher?.Methods, |
| | 0 | 534 | | |
| | 0 | 535 | | Headers = headerMatchers != null && headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderMod |
| | 0 | 536 | | { |
| | 0 | 537 | | Name = hm.Name, |
| | 0 | 538 | | Matchers = Map(hm.Matchers), |
| | 0 | 539 | | Funcs = Map(hm.Funcs) |
| | 0 | 540 | | }).ToList() : null, |
| | 0 | 541 | | |
| | 0 | 542 | | Cookies = cookieMatchers != null && cookieMatchers.Any() ? cookieMatchers.Select(cm => new CookieMod |
| | 0 | 543 | | { |
| | 0 | 544 | | Name = cm.Name, |
| | 0 | 545 | | Matchers = Map(cm.Matchers), |
| | 0 | 546 | | Funcs = Map(cm.Funcs) |
| | 0 | 547 | | }).ToList() : null, |
| | 0 | 548 | | |
| | 0 | 549 | | Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel |
| | 0 | 550 | | { |
| | 0 | 551 | | Name = pm.Key, |
| | 0 | 552 | | Values = pm.Values?.ToList(), |
| | 0 | 553 | | Funcs = Map(pm.Funcs) |
| | 0 | 554 | | }).ToList() : null, |
| | 0 | 555 | | |
| | 0 | 556 | | Body = methodMatcher?.Methods != null && methodMatcher.Methods.Count(m => m == "get") == 1 ? null : |
| | 0 | 557 | | { |
| | 0 | 558 | | Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null, |
| | 0 | 559 | | Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null, |
| | 0 | 560 | | DataFunc = bodyMatcher != null ? Map(bodyMatcher.DataFunc) : null |
| | 0 | 561 | | } |
| | 0 | 562 | | }, |
| | 0 | 563 | | Response = new ResponseModel |
| | 0 | 564 | | { |
| | 0 | 565 | | StatusCode = response.ResponseMessage.StatusCode, |
| | 0 | 566 | | Headers = response.ResponseMessage.Headers, |
| | 0 | 567 | | Body = response.ResponseMessage.Body, |
| | 0 | 568 | | UseTransformer = response.UseTransformer, |
| | 0 | 569 | | Delay = response.Delay?.Milliseconds, |
| | 0 | 570 | | |
| | 0 | 571 | | BodyEncoding = response.ResponseMessage.BodyEncoding != null ? new EncodingModel |
| | 0 | 572 | | { |
| | 0 | 573 | | EncodingName = response.ResponseMessage.BodyEncoding.EncodingName, |
| | 0 | 574 | | CodePage = response.ResponseMessage.BodyEncoding.CodePage, |
| | 0 | 575 | | WebName = response.ResponseMessage.BodyEncoding.WebName |
| | 0 | 576 | | } : null |
| | 0 | 577 | | } |
| | 0 | 578 | | }; |
| | 0 | 579 | | } |
| | | 580 | | |
| | | 581 | | private MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers) |
| | 0 | 582 | | { |
| | 0 | 583 | | if (matchers == null || !matchers.Any()) |
| | 0 | 584 | | return null; |
| | | 585 | | |
| | 0 | 586 | | return matchers.Select(Map).Where(x => x != null).ToArray(); |
| | 0 | 587 | | } |
| | | 588 | | |
| | | 589 | | private MatcherModel Map([CanBeNull] IMatcher matcher) |
| | 0 | 590 | | { |
| | 0 | 591 | | if (matcher == null) |
| | 0 | 592 | | return null; |
| | | 593 | | |
| | 0 | 594 | | var patterns = matcher.GetPatterns(); |
| | | 595 | | |
| | 0 | 596 | | return new MatcherModel |
| | 0 | 597 | | { |
| | 0 | 598 | | Name = matcher.GetName(), |
| | 0 | 599 | | Pattern = patterns.Length == 1 ? patterns.First() : null, |
| | 0 | 600 | | Patterns = patterns.Length > 1 ? patterns : null |
| | 0 | 601 | | }; |
| | 0 | 602 | | } |
| | | 603 | | |
| | | 604 | | private string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs) |
| | 0 | 605 | | { |
| | 0 | 606 | | if (funcs == null || !funcs.Any()) |
| | 0 | 607 | | return null; |
| | | 608 | | |
| | 0 | 609 | | return funcs.Select(Map).Where(x => x != null).ToArray(); |
| | 0 | 610 | | } |
| | | 611 | | |
| | | 612 | | private string Map<T>([CanBeNull] Func<T, bool> func) |
| | 0 | 613 | | { |
| | 0 | 614 | | return func?.ToString(); |
| | 0 | 615 | | } |
| | | 616 | | |
| | | 617 | | private IMatcher Map([CanBeNull] MatcherModel matcher) |
| | 6 | 618 | | { |
| | 6 | 619 | | if (matcher == null) |
| | 0 | 620 | | return null; |
| | | 621 | | |
| | 6 | 622 | | var parts = matcher.Name.Split('.'); |
| | 6 | 623 | | string matcherName = parts[0]; |
| | 6 | 624 | | string matcherType = parts.Length > 1 ? parts[1] : null; |
| | | 625 | | |
| | 6 | 626 | | string[] patterns = matcher.Patterns ?? new[] { matcher.Pattern }; |
| | | 627 | | |
| | 6 | 628 | | switch (matcherName) |
| | | 629 | | { |
| | | 630 | | case "ExactMatcher": |
| | 4 | 631 | | return new ExactMatcher(patterns); |
| | | 632 | | |
| | | 633 | | case "RegexMatcher": |
| | 0 | 634 | | return new RegexMatcher(patterns); |
| | | 635 | | |
| | | 636 | | case "JsonPathMatcher": |
| | 0 | 637 | | return new JsonPathMatcher(patterns); |
| | | 638 | | |
| | | 639 | | case "XPathMatcher": |
| | 0 | 640 | | return new XPathMatcher(matcher.Pattern); |
| | | 641 | | |
| | | 642 | | case "WildcardMatcher": |
| | 2 | 643 | | return new WildcardMatcher(patterns, matcher.IgnoreCase == true); |
| | | 644 | | |
| | | 645 | | case "SimMetricsMatcher": |
| | 0 | 646 | | SimMetricType type = SimMetricType.Levenstein; |
| | 0 | 647 | | if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type)) |
| | 0 | 648 | | throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not suppo |
| | | 649 | | |
| | 0 | 650 | | return new SimMetricsMatcher(matcher.Pattern, type); |
| | | 651 | | |
| | | 652 | | default: |
| | 0 | 653 | | throw new NotSupportedException($"Matcher '{matcherName}' is not supported."); |
| | | 654 | | } |
| | 6 | 655 | | } |
| | | 656 | | |
| | | 657 | | private ResponseMessage ToJson<T>(T result) |
| | 0 | 658 | | { |
| | 0 | 659 | | return new ResponseMessage |
| | 0 | 660 | | { |
| | 0 | 661 | | Body = JsonConvert.SerializeObject(result, _settings), |
| | 0 | 662 | | StatusCode = 200, |
| | 0 | 663 | | Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } } |
| | 0 | 664 | | }; |
| | 0 | 665 | | } |
| | | 666 | | |
| | | 667 | | private Encoding ToEncoding(EncodingModel encodingModel) |
| | 4 | 668 | | { |
| | 4 | 669 | | return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null; |
| | 4 | 670 | | } |
| | | 671 | | } |
| | | 672 | | } |