diff --git a/src/WireMock.Net/Exceptions/WireMockException.cs b/src/WireMock.Net/Exceptions/WireMockException.cs index 4e1e29bd..5c14ec33 100644 --- a/src/WireMock.Net/Exceptions/WireMockException.cs +++ b/src/WireMock.Net/Exceptions/WireMockException.cs @@ -1,35 +1,34 @@ -using System; +using System; -namespace WireMock.Exceptions +namespace WireMock.Exceptions; + +/// +/// WireMockException +/// +/// +public class WireMockException : Exception { /// - /// WireMockException + /// Initializes a new instance of the class. /// - /// - public class WireMockException : Exception + public WireMockException() { - /// - /// Initializes a new instance of the class. - /// - public WireMockException() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public WireMockException(string message) : base(message) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public WireMockException(string message) : base(message) + { + } - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner. - public WireMockException(string message, Exception inner) : base(message, inner) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner. + public WireMockException(string message, Exception inner) : base(message, inner) + { } } \ No newline at end of file diff --git a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs index 806ce569..aaa888c3 100644 --- a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs +++ b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs @@ -3,179 +3,178 @@ using System.IO; using WireMock.Util; using Stef.Validation; -namespace WireMock.Handlers +namespace WireMock.Handlers; + +/// +/// Default implementation for a handler to interact with the local file system to read and write static mapping files. +/// +public class LocalFileSystemHandler : IFileSystemHandler { + private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings"); + private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched"); + + private readonly string _rootFolder; + /// - /// Default implementation for a handler to interact with the local file system to read and write static mapping files. + /// Initializes a new instance of the class. /// - public class LocalFileSystemHandler : IFileSystemHandler + public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory()) { - private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings"); - private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched"); + } - private readonly string _rootFolder; + /// + /// Initializes a new instance of the class. + /// + /// The root folder. + public LocalFileSystemHandler(string rootFolder) + { + _rootFolder = rootFolder; + } - /// - /// Initializes a new instance of the class. - /// - public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory()) + /// + public virtual bool FolderExists(string path) + { + Guard.NotNullOrEmpty(path); + + return Directory.Exists(path); + } + + /// + public virtual void CreateFolder(string path) + { + Guard.NotNullOrEmpty(path); + + Directory.CreateDirectory(path); + } + + /// + public virtual IEnumerable EnumerateFiles(string path, bool includeSubdirectories) + { + Guard.NotNullOrEmpty(path); + + return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path); + } + + /// + public virtual string GetMappingFolder() + { + return Path.Combine(_rootFolder, AdminMappingsFolder); + } + + /// + public virtual string ReadMappingFile(string path) + { + Guard.NotNullOrEmpty(path); + + return File.ReadAllText(path); + } + + /// + public virtual void WriteMappingFile(string path, string text) + { + Guard.NotNullOrEmpty(path, nameof(path)); + Guard.NotNull(text, nameof(text)); + + File.WriteAllText(path, text); + } + + /// + public virtual byte[] ReadResponseBodyAsFile(string path) + { + Guard.NotNullOrEmpty(path); + path = PathUtils.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); + } + + /// + public virtual string ReadResponseBodyAsString(string path) + { + Guard.NotNullOrEmpty(path); + path = PathUtils.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); + } + + /// + public virtual bool FileExists(string filename) + { + Guard.NotNullOrEmpty(filename); + + return File.Exists(AdjustPathForMappingFolder(filename)); + } + + /// + public virtual void WriteFile(string filename, byte[] bytes) + { + Guard.NotNullOrEmpty(filename); + Guard.NotNull(bytes); + + File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes); + } + + /// + public virtual void WriteFile(string folder, string filename, byte[] bytes) + { + Guard.NotNullOrEmpty(folder); + Guard.NotNullOrEmpty(filename); + Guard.NotNull(bytes); + + File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes); + } + + /// + public virtual void DeleteFile(string filename) + { + Guard.NotNullOrEmpty(filename); + + File.Delete(AdjustPathForMappingFolder(filename)); + } + + /// + public virtual byte[] ReadFile(string filename) + { + Guard.NotNullOrEmpty(filename); + + return File.ReadAllBytes(AdjustPathForMappingFolder(filename)); + } + + /// + public virtual string ReadFileAsString(string filename) + { + return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename, nameof(filename)))); + } + + /// + public virtual string GetUnmatchedRequestsFolder() + { + return Path.Combine(_rootFolder, UnmatchedRequestsFolder); + } + + /// + public virtual void WriteUnmatchedRequest(string filename, string text) + { + Guard.NotNullOrEmpty(filename); + Guard.NotNull(text); + + var folder = GetUnmatchedRequestsFolder(); + if (!FolderExists(folder)) { + CreateFolder(folder); } - /// - /// Initializes a new instance of the class. - /// - /// The root folder. - public LocalFileSystemHandler(string rootFolder) - { - _rootFolder = rootFolder; - } + File.WriteAllText(Path.Combine(folder, filename), text); + } - /// - public virtual bool FolderExists(string path) - { - Guard.NotNullOrEmpty(path, nameof(path)); - - return Directory.Exists(path); - } - - /// - public virtual void CreateFolder(string path) - { - Guard.NotNullOrEmpty(path, nameof(path)); - - Directory.CreateDirectory(path); - } - - /// - public virtual IEnumerable EnumerateFiles(string path, bool includeSubdirectories) - { - Guard.NotNullOrEmpty(path, nameof(path)); - - return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path); - } - - /// - public virtual string GetMappingFolder() - { - return Path.Combine(_rootFolder, AdminMappingsFolder); - } - - /// - public virtual string ReadMappingFile(string path) - { - Guard.NotNullOrEmpty(path, nameof(path)); - - return File.ReadAllText(path); - } - - /// - public virtual void WriteMappingFile(string path, string text) - { - Guard.NotNullOrEmpty(path, nameof(path)); - Guard.NotNull(text, nameof(text)); - - File.WriteAllText(path, text); - } - - /// - public virtual byte[] ReadResponseBodyAsFile(string path) - { - Guard.NotNullOrEmpty(path, nameof(path)); - path = PathUtils.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); - } - - /// - public virtual string ReadResponseBodyAsString(string path) - { - Guard.NotNullOrEmpty(path, nameof(path)); - path = PathUtils.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); - } - - /// - public virtual bool FileExists(string filename) - { - Guard.NotNullOrEmpty(filename, nameof(filename)); - - return File.Exists(AdjustPathForMappingFolder(filename)); - } - - /// - public virtual void WriteFile(string filename, byte[] bytes) - { - Guard.NotNullOrEmpty(filename, nameof(filename)); - Guard.NotNull(bytes, nameof(bytes)); - - File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes); - } - - /// - public virtual void WriteFile(string folder, string filename, byte[] bytes) - { - Guard.NotNullOrEmpty(folder); - Guard.NotNullOrEmpty(filename); - Guard.NotNull(bytes); - - File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes); - } - - /// - public virtual void DeleteFile(string filename) - { - Guard.NotNullOrEmpty(filename, nameof(filename)); - - File.Delete(AdjustPathForMappingFolder(filename)); - } - - /// - public virtual byte[] ReadFile(string filename) - { - Guard.NotNullOrEmpty(filename, nameof(filename)); - - return File.ReadAllBytes(AdjustPathForMappingFolder(filename)); - } - - /// - public virtual string ReadFileAsString(string filename) - { - return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename, nameof(filename)))); - } - - /// - public virtual string GetUnmatchedRequestsFolder() - { - return Path.Combine(_rootFolder, UnmatchedRequestsFolder); - } - - /// - public virtual void WriteUnmatchedRequest(string filename, string text) - { - Guard.NotNullOrEmpty(filename, nameof(filename)); - Guard.NotNull(text, nameof(text)); - - var folder = GetUnmatchedRequestsFolder(); - if (!FolderExists(folder)) - { - CreateFolder(folder); - } - - File.WriteAllText(Path.Combine(folder, filename), text); - } - - /// - /// Adjusts the path to the MappingFolder. - /// - /// The path. - /// Adjusted path - private string AdjustPathForMappingFolder(string filename) - { - return Path.Combine(GetMappingFolder(), filename); - } + /// + /// Adjusts the path to the MappingFolder. + /// + /// The path. + /// Adjusted path + private string AdjustPathForMappingFolder(string filename) + { + return Path.Combine(GetMappingFolder(), filename); } } \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/ContentTypeMatcher.cs b/src/WireMock.Net/Matchers/ContentTypeMatcher.cs index 6c45a9f7..91deb2b5 100644 --- a/src/WireMock.Net/Matchers/ContentTypeMatcher.cs +++ b/src/WireMock.Net/Matchers/ContentTypeMatcher.cs @@ -1,6 +1,5 @@ using System.Net.Http.Headers; using AnyOfTypes; -using JetBrains.Annotations; using WireMock.Models; namespace WireMock.Matchers; diff --git a/src/WireMock.Net/Models/TimeSettings.cs b/src/WireMock.Net/Models/TimeSettings.cs index 23f13666..772dd2c5 100644 --- a/src/WireMock.Net/Models/TimeSettings.cs +++ b/src/WireMock.Net/Models/TimeSettings.cs @@ -1,19 +1,18 @@ using System; -namespace WireMock.Models +namespace WireMock.Models; + +/// +/// TimeSettingsModel: Start, End and TTL +/// +public class TimeSettings : ITimeSettings { - /// - /// TimeSettingsModel: Start, End and TTL - /// - public class TimeSettings : ITimeSettings - { - /// - public DateTime? Start { get; set; } + /// + public DateTime? Start { get; set; } - /// - public DateTime? End { get; set; } + /// + public DateTime? End { get; set; } - /// - public int? TTL { get; set; } - } + /// + public int? TTL { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Util/BytesEncodingUtils.cs b/src/WireMock.Net/Util/BytesEncodingUtils.cs index 97904d91..44af9193 100644 --- a/src/WireMock.Net/Util/BytesEncodingUtils.cs +++ b/src/WireMock.Net/Util/BytesEncodingUtils.cs @@ -17,7 +17,7 @@ namespace WireMock.Util; /// http://www.unicode.org/versions/corrigendum1.html /// http://www.ietf.org/rfc/rfc2279.txt /// -public static class BytesEncodingUtils +internal static class BytesEncodingUtils { /// /// Tries the get the Encoding from an array of bytes. diff --git a/src/WireMock.Net/Util/ConcurrentObservableCollection.cs b/src/WireMock.Net/Util/ConcurrentObservableCollection.cs index 09854c50..fdfcdba5 100644 --- a/src/WireMock.Net/Util/ConcurrentObservableCollection.cs +++ b/src/WireMock.Net/Util/ConcurrentObservableCollection.cs @@ -1,86 +1,85 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -namespace WireMock.Util +namespace WireMock.Util; + +/// +/// A special Collection that overrides methods of to make them thread safe. +/// +/// The type of elements in the collection. +/// +internal class ConcurrentObservableCollection : ObservableCollection { + private readonly object _lockObject = new object(); + + /// + /// Initializes a new instance of the class. + /// + public ConcurrentObservableCollection() { } + /// - /// A special Collection that overrides methods of to make them thread safe. + /// Initializes a new instance of the class that contains elements copied from the specified list. /// - /// The type of elements in the collection. - /// - internal class ConcurrentObservableCollection : ObservableCollection + /// The list from which the elements are copied. + public ConcurrentObservableCollection(List list) : base(list) { } + + /// + /// Initializes a new instance of the class that contains elements copied from the specified collection. + /// + /// The collection from which the elements are copied. + public ConcurrentObservableCollection(IEnumerable collection) : base(collection) { } + + /// + protected override void ClearItems() { - private readonly object _lockObject = new object(); - - /// - /// Initializes a new instance of the class. - /// - public ConcurrentObservableCollection() { } - - /// - /// Initializes a new instance of the class that contains elements copied from the specified list. - /// - /// The list from which the elements are copied. - public ConcurrentObservableCollection(List list) : base(list) { } - - /// - /// Initializes a new instance of the class that contains elements copied from the specified collection. - /// - /// The collection from which the elements are copied. - public ConcurrentObservableCollection(IEnumerable collection) : base(collection) { } - - /// - protected override void ClearItems() + lock (_lockObject) { - lock (_lockObject) - { - base.ClearItems(); - } + base.ClearItems(); } + } - /// - protected override void RemoveItem(int index) + /// + protected override void RemoveItem(int index) + { + lock (_lockObject) { - lock (_lockObject) - { - base.RemoveItem(index); - } + base.RemoveItem(index); } + } - /// - protected override void InsertItem(int index, T item) + /// + protected override void InsertItem(int index, T item) + { + lock (_lockObject) { - lock (_lockObject) - { - base.InsertItem(index, item); - } + base.InsertItem(index, item); } + } - /// - protected override void SetItem(int index, T item) + /// + protected override void SetItem(int index, T item) + { + lock (_lockObject) { - lock (_lockObject) - { - base.SetItem(index, item); - } + base.SetItem(index, item); } + } - /// - protected override void MoveItem(int oldIndex, int newIndex) + /// + protected override void MoveItem(int oldIndex, int newIndex) + { + lock (_lockObject) { - lock (_lockObject) - { - base.MoveItem(oldIndex, newIndex); - } + base.MoveItem(oldIndex, newIndex); } + } - public List ToList() + public List ToList() + { + lock (_lockObject) { - lock (_lockObject) - { - return Items.ToList(); - } + return Items.ToList(); } } } \ No newline at end of file diff --git a/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs b/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs index c4d7a053..599d643d 100644 --- a/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs +++ b/src/WireMock.Net/Util/EnhancedFileSystemWatcher.cs @@ -4,251 +4,250 @@ using System.IO; using JetBrains.Annotations; using Stef.Validation; -namespace WireMock.Util +namespace WireMock.Util; + +/// +/// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file. +/// +/// +public class EnhancedFileSystemWatcher : FileSystemWatcher { + #region Private Members + // Default Watch Interval in Milliseconds + private const int DefaultWatchInterval = 100; + + // This Dictionary keeps the track of when an event occurred last for a particular file + private ConcurrentDictionary _lastFileEvent = new(); + + // Watch Interval in Milliseconds + private int _interval; + + // Timespan created when interval is set + private TimeSpan _recentTimeSpan; + #endregion + + #region Public Properties /// - /// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file. + /// Interval, in milliseconds, within which events are considered "recent". /// - /// - public class EnhancedFileSystemWatcher : FileSystemWatcher + [PublicAPI] + public int Interval { - #region Private Members - // Default Watch Interval in Milliseconds - private const int DefaultWatchInterval = 100; - - // This Dictionary keeps the track of when an event occurred last for a particular file - private ConcurrentDictionary _lastFileEvent = new(); - - // Watch Interval in Milliseconds - private int _interval; - - // Timespan created when interval is set - private TimeSpan _recentTimeSpan; - #endregion - - #region Public Properties - /// - /// Interval, in milliseconds, within which events are considered "recent". - /// - [PublicAPI] - public int Interval + get => _interval; + set { - get => _interval; - set - { - _interval = value; + _interval = value; - // Set timespan based on the value passed - _recentTimeSpan = new TimeSpan(0, 0, 0, 0, value); - } + // Set timespan based on the value passed + _recentTimeSpan = new TimeSpan(0, 0, 0, 0, value); } - - /// - /// Allows user to set whether to filter recent events. - /// If this is set a false, this class behaves like System.IO.FileSystemWatcher class. - /// - [PublicAPI] - public bool FilterRecentEvents { get; set; } - #endregion - - #region Constructors - /// - /// Initializes a new instance of the class. - /// - /// The interval. - public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval) - { - Guard.Condition(interval, i => i >= 0); - - InitializeMembers(interval); - } - - /// - /// Initializes a new instance of the class. - /// - /// The directory to monitor, in standard or Universal Naming Convention (UNC) notation. - /// The interval. - public EnhancedFileSystemWatcher(string path, int interval = DefaultWatchInterval) : base(path) - { - Guard.NotNullOrEmpty(path); - Guard.Condition(interval, i => i >= 0); - - InitializeMembers(interval); - } - - /// - /// Initializes a new instance of the class. - /// - /// The directory to monitor, in standard or Universal Naming Convention (UNC) notation. - /// The type of files to watch. For example, "*.txt" watches for changes to all text files. - /// The interval. - public EnhancedFileSystemWatcher(string path, string filter, int interval = DefaultWatchInterval) : base(path, filter) - { - Guard.NotNullOrEmpty(path); - Guard.NotNullOrEmpty(filter); - Guard.Condition(interval, i => i >= 0); - - InitializeMembers(interval); - } - #endregion - - #region Events - // These events hide the events from the base class. - // We want to raise these events appropriately and we do not want the - // users of this class subscribing to these events of the base class accidentally - - /// - /// Occurs when a file or directory in the specified is changed. - /// - public new event FileSystemEventHandler? Changed; - - /// - /// Occurs when a file or directory in the specified is created. - /// - public new event FileSystemEventHandler? Created; - - /// - /// Occurs when a file or directory in the specified is deleted. - /// - public new event FileSystemEventHandler? Deleted; - - /// - /// Occurs when a file or directory in the specified is renamed. - /// - public new event RenamedEventHandler? Renamed; - #endregion - - #region Protected Methods to raise the Events for this class - /// - /// Raises the event. - /// - /// A that contains the event data. - protected new virtual void OnChanged(FileSystemEventArgs e) - { - Changed?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// A that contains the event data. - protected new virtual void OnCreated(FileSystemEventArgs e) - { - Created?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// A that contains the event data. - protected new virtual void OnDeleted(FileSystemEventArgs e) - { - Deleted?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// A that contains the event data. - protected new virtual void OnRenamed(RenamedEventArgs e) - { - Renamed?.Invoke(this, e); - } - #endregion - - #region Private Methods - /// - /// This Method Initializes the private members. - /// Interval is set to its default value of 100 millisecond. - /// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized. - /// We subscribe to the base class events. - /// - private void InitializeMembers(int interval = 100) - { - Interval = interval; - FilterRecentEvents = true; - _lastFileEvent = new ConcurrentDictionary(); - - base.Created += OnCreated; - base.Changed += OnChanged; - base.Deleted += OnDeleted; - base.Renamed += OnRenamed; - } - - /// - /// This method searches the dictionary to find out when the last event occurred - /// for a particular file. If that event occurred within the specified timespan - /// it returns true, else false - /// - /// The filename to be checked - /// True if an event has occurred within the specified interval, False otherwise - private bool HasAnotherFileEventOccurredRecently(string fileName) - { - // Check dictionary only if user wants to filter recent events otherwise return value stays false. - if (!FilterRecentEvents) - { - return false; - } - - bool retVal = false; - if (_lastFileEvent.ContainsKey(fileName)) - { - // If dictionary contains the filename, check how much time has elapsed - // since the last event occurred. If the timespan is less that the - // specified interval, set return value to true - // and store current datetime in dictionary for this file - DateTime lastEventTime = _lastFileEvent[fileName]; - DateTime currentTime = DateTime.Now; - TimeSpan timeSinceLastEvent = currentTime - lastEventTime; - retVal = timeSinceLastEvent < _recentTimeSpan; - _lastFileEvent[fileName] = currentTime; - } - else - { - // If dictionary does not contain the filename, - // no event has occurred in past for this file, so set return value to false - // and append filename along with current datetime to the dictionary - _lastFileEvent.TryAdd(fileName, DateTime.Now); - } - - return retVal; - } - - #region FileSystemWatcher EventHandlers - // Base class Event Handlers. Check if an event has occurred recently and call method - // to raise appropriate event only if no recent event is detected - private void OnChanged(object sender, FileSystemEventArgs e) - { - if (!HasAnotherFileEventOccurredRecently(e.FullPath)) - { - OnChanged(e); - } - } - - private void OnCreated(object sender, FileSystemEventArgs e) - { - if (!HasAnotherFileEventOccurredRecently(e.FullPath)) - { - OnCreated(e); - } - } - - private void OnDeleted(object sender, FileSystemEventArgs e) - { - if (!HasAnotherFileEventOccurredRecently(e.FullPath)) - { - OnDeleted(e); - } - } - - private void OnRenamed(object sender, RenamedEventArgs e) - { - if (!HasAnotherFileEventOccurredRecently(e.OldFullPath)) - { - OnRenamed(e); - } - } - #endregion - #endregion } + + /// + /// Allows user to set whether to filter recent events. + /// If this is set a false, this class behaves like System.IO.FileSystemWatcher class. + /// + [PublicAPI] + public bool FilterRecentEvents { get; set; } + #endregion + + #region Constructors + /// + /// Initializes a new instance of the class. + /// + /// The interval. + public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval) + { + Guard.Condition(interval, i => i >= 0); + + InitializeMembers(interval); + } + + /// + /// Initializes a new instance of the class. + /// + /// The directory to monitor, in standard or Universal Naming Convention (UNC) notation. + /// The interval. + public EnhancedFileSystemWatcher(string path, int interval = DefaultWatchInterval) : base(path) + { + Guard.NotNullOrEmpty(path); + Guard.Condition(interval, i => i >= 0); + + InitializeMembers(interval); + } + + /// + /// Initializes a new instance of the class. + /// + /// The directory to monitor, in standard or Universal Naming Convention (UNC) notation. + /// The type of files to watch. For example, "*.txt" watches for changes to all text files. + /// The interval. + public EnhancedFileSystemWatcher(string path, string filter, int interval = DefaultWatchInterval) : base(path, filter) + { + Guard.NotNullOrEmpty(path); + Guard.NotNullOrEmpty(filter); + Guard.Condition(interval, i => i >= 0); + + InitializeMembers(interval); + } + #endregion + + #region Events + // These events hide the events from the base class. + // We want to raise these events appropriately and we do not want the + // users of this class subscribing to these events of the base class accidentally + + /// + /// Occurs when a file or directory in the specified is changed. + /// + public new event FileSystemEventHandler? Changed; + + /// + /// Occurs when a file or directory in the specified is created. + /// + public new event FileSystemEventHandler? Created; + + /// + /// Occurs when a file or directory in the specified is deleted. + /// + public new event FileSystemEventHandler? Deleted; + + /// + /// Occurs when a file or directory in the specified is renamed. + /// + public new event RenamedEventHandler? Renamed; + #endregion + + #region Protected Methods to raise the Events for this class + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnChanged(FileSystemEventArgs e) + { + Changed?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnCreated(FileSystemEventArgs e) + { + Created?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnDeleted(FileSystemEventArgs e) + { + Deleted?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected new virtual void OnRenamed(RenamedEventArgs e) + { + Renamed?.Invoke(this, e); + } + #endregion + + #region Private Methods + /// + /// This Method Initializes the private members. + /// Interval is set to its default value of 100 millisecond. + /// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized. + /// We subscribe to the base class events. + /// + private void InitializeMembers(int interval = 100) + { + Interval = interval; + FilterRecentEvents = true; + _lastFileEvent = new ConcurrentDictionary(); + + base.Created += OnCreated; + base.Changed += OnChanged; + base.Deleted += OnDeleted; + base.Renamed += OnRenamed; + } + + /// + /// This method searches the dictionary to find out when the last event occurred + /// for a particular file. If that event occurred within the specified timespan + /// it returns true, else false + /// + /// The filename to be checked + /// True if an event has occurred within the specified interval, False otherwise + private bool HasAnotherFileEventOccurredRecently(string fileName) + { + // Check dictionary only if user wants to filter recent events otherwise return value stays false. + if (!FilterRecentEvents) + { + return false; + } + + bool retVal = false; + if (_lastFileEvent.ContainsKey(fileName)) + { + // If dictionary contains the filename, check how much time has elapsed + // since the last event occurred. If the timespan is less that the + // specified interval, set return value to true + // and store current datetime in dictionary for this file + DateTime lastEventTime = _lastFileEvent[fileName]; + DateTime currentTime = DateTime.Now; + TimeSpan timeSinceLastEvent = currentTime - lastEventTime; + retVal = timeSinceLastEvent < _recentTimeSpan; + _lastFileEvent[fileName] = currentTime; + } + else + { + // If dictionary does not contain the filename, + // no event has occurred in past for this file, so set return value to false + // and append filename along with current datetime to the dictionary + _lastFileEvent.TryAdd(fileName, DateTime.Now); + } + + return retVal; + } + + #region FileSystemWatcher EventHandlers + // Base class Event Handlers. Check if an event has occurred recently and call method + // to raise appropriate event only if no recent event is detected + private void OnChanged(object sender, FileSystemEventArgs e) + { + if (!HasAnotherFileEventOccurredRecently(e.FullPath)) + { + OnChanged(e); + } + } + + private void OnCreated(object sender, FileSystemEventArgs e) + { + if (!HasAnotherFileEventOccurredRecently(e.FullPath)) + { + OnCreated(e); + } + } + + private void OnDeleted(object sender, FileSystemEventArgs e) + { + if (!HasAnotherFileEventOccurredRecently(e.FullPath)) + { + OnDeleted(e); + } + } + + private void OnRenamed(object sender, RenamedEventArgs e) + { + if (!HasAnotherFileEventOccurredRecently(e.OldFullPath)) + { + OnRenamed(e); + } + } + #endregion + #endregion } \ No newline at end of file diff --git a/src/WireMock.Net/Util/PortUtils.cs b/src/WireMock.Net/Util/PortUtils.cs index 04fcd70e..169f50e6 100644 --- a/src/WireMock.Net/Util/PortUtils.cs +++ b/src/WireMock.Net/Util/PortUtils.cs @@ -9,7 +9,7 @@ namespace WireMock.Util; /// /// Port Utility class /// -public static class PortUtils +internal static class PortUtils { private static readonly Regex UrlDetailsRegex = new(@"^((?\w+)://)(?[^/]+?):(?\d+)\/?$", RegexOptions.Compiled);