mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-11 22:30:41 +01:00
Implement IMimeMessageData (#1326)
* Implement IMimeMessageData * 1 * Update src/WireMock.Net.MimePart/Util/MimeKitUtils.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * v1 * v2 * e * ? * fix * if (Array.TrueForAll(_funcs, func => func(value).IsPerfect())) * Update src/WireMock.Net.Shared/Util/IMimeKitUtils.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/WireMock.Net.Minimal/Matchers/Request/RequestMessageMultiPartMatcher.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/WireMock.Net.MimePart/Models/MimeEntityDataWrapper.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Models.Mime.IMimeMessageData? BodyAsMimeMessage { get; } * Update src/WireMock.Net.MimePart/Util/MimeKitUtils.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/WireMock.Net.MimePart/Models/MimePartDataWrapper.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/WireMock.Net.MimePart/Models/MimeMessageDataWrapper.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/WireMock.Net.Shared/Util/IMimeKitUtils.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * . --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -123,7 +123,7 @@ public interface IRequestMessage
|
||||
/// The original body as MimeMessage.
|
||||
/// Convenience getter for Handlebars and WireMockAssertions.
|
||||
/// </summary>
|
||||
object? BodyAsMimeMessage { get; }
|
||||
Models.Mime.IMimeMessageData? BodyAsMimeMessage { get; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Models.Mime;
|
||||
|
||||
/// <summary>
|
||||
/// An interface exposing the public, readable properties of a ContentDisposition.
|
||||
/// </summary>
|
||||
public interface IContentDispositionData
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the disposition.
|
||||
/// </summary>
|
||||
/// <value>The disposition.</value>
|
||||
string Disposition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a value indicating whether the <see cref="IMimeEntityData"/> is an attachment.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if the <see cref="IMimeEntityData"/> is an attachment; otherwise, <see langword="false" />.</value>
|
||||
bool IsAttachment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of parameters on the ContentDisposition.
|
||||
/// </summary>
|
||||
/// <value>The parameters.</value>
|
||||
public IList<string> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the file.
|
||||
/// </summary>
|
||||
/// <value>The name of the file.</value>
|
||||
string FileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the creation-date parameter.
|
||||
/// </summary>
|
||||
/// <value>The creation date.</value>
|
||||
DateTimeOffset? CreationDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the modification-date parameter.
|
||||
/// </summary>
|
||||
/// <value>The modification date.</value>
|
||||
DateTimeOffset? ModificationDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the read-date parameter.
|
||||
/// </summary>
|
||||
/// <value>The read date.</value>
|
||||
DateTimeOffset? ReadDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the size parameter.
|
||||
/// </summary>
|
||||
/// <value>The size.</value>
|
||||
long? Size { get; }
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock.Models.Mime;
|
||||
|
||||
/// <summary>
|
||||
/// An interface exposing the public, readable properties of a ContentType
|
||||
/// with complex types simplified to a generic object.
|
||||
/// </summary>
|
||||
public interface IContentTypeData
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the type of the media.
|
||||
/// </summary>
|
||||
/// <value>The type of the media.</value>
|
||||
string MediaType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the media subtype.
|
||||
/// </summary>
|
||||
/// <value>The media subtype.</value>
|
||||
string MediaSubtype { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of parameters on the ContentType.
|
||||
/// </summary>
|
||||
/// <value>The parameters.</value>
|
||||
IList<string> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the boundary parameter.
|
||||
/// </summary>
|
||||
/// <value>The boundary.</value>
|
||||
string Boundary { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the charset parameter.
|
||||
/// </summary>
|
||||
/// <value>The charset.</value>
|
||||
string Charset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the charset parameter as an Encoding.
|
||||
/// </summary>
|
||||
/// <value>The charset encoding.</value>
|
||||
Encoding CharsetEncoding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the format parameter.
|
||||
/// </summary>
|
||||
/// <value>The format.</value>
|
||||
string Format { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the simple mime-type.
|
||||
/// </summary>
|
||||
/// <value>The mime-type.</value>
|
||||
string MimeType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the name parameter.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
string Name { get; }
|
||||
}
|
||||
54
src/WireMock.Net.Abstractions/Models/Mime/IMimeEntityData.cs
Normal file
54
src/WireMock.Net.Abstractions/Models/Mime/IMimeEntityData.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Models.Mime;
|
||||
|
||||
/// <summary>
|
||||
/// A simplified interface exposing the public, readable properties of MimeEntity.
|
||||
/// </summary>
|
||||
public interface IMimeEntityData
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the list of headers.
|
||||
/// </summary>
|
||||
/// <value>The list of headers.</value>
|
||||
IList<string> Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the content disposition.
|
||||
/// </summary>
|
||||
/// <value>The content disposition.</value>
|
||||
IContentDispositionData? ContentDisposition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the type of the content.
|
||||
/// </summary>
|
||||
/// <value>The type of the content.</value>
|
||||
IContentTypeData? ContentType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the base content URI.
|
||||
/// </summary>
|
||||
/// <value>The base content URI or <see langword="null"/>.</value>
|
||||
Uri ContentBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the content location.
|
||||
/// </summary>
|
||||
/// <value>The content location or <see langword="null"/>.</value>
|
||||
Uri ContentLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the Content-Id.
|
||||
/// </summary>
|
||||
/// <value>The content identifier.</value>
|
||||
string ContentId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a value indicating whether this <see cref="IMimeEntityData"/> is an attachment.
|
||||
/// </summary>
|
||||
/// <value><see langword="true" /> if this <see cref="IMimeEntityData"/> is an attachment; otherwise, <see langword="false" />.</value>
|
||||
bool IsAttachment { get; }
|
||||
}
|
||||
186
src/WireMock.Net.Abstractions/Models/Mime/IMimeMessageData.cs
Normal file
186
src/WireMock.Net.Abstractions/Models/Mime/IMimeMessageData.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Models.Mime;
|
||||
|
||||
/// <summary>
|
||||
/// A simplified interface exposing the public, readable properties of a MIME message.
|
||||
/// </summary>
|
||||
public interface IMimeMessageData
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the list of headers.
|
||||
/// </summary>
|
||||
/// <value>The list of headers.</value>
|
||||
IList<string> Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of the Importance header.
|
||||
/// </summary>
|
||||
/// <value>The importance, as an integer.</value>
|
||||
int Importance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of the Priority header.
|
||||
/// </summary>
|
||||
/// <value>The priority, as an integer.</value>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of the X-Priority header.
|
||||
/// </summary>
|
||||
/// <value>The X-priority, as an integer.</value>
|
||||
int XPriority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the address in the Sender header.
|
||||
/// </summary>
|
||||
/// <value>The address in the Sender header.</value>
|
||||
string Sender { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the address in the Resent-Sender header.
|
||||
/// </summary>
|
||||
/// <value>The address in the Resent-Sender header.</value>
|
||||
string ResentSender { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the From header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the From header.</value>
|
||||
IList<string> From { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Resent-From header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Resent-From header.</value>
|
||||
IList<string> ResentFrom { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Reply-To header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Reply-To header.</value>
|
||||
IList<string> ReplyTo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Resent-Reply-To header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Resent-Reply-To header.</value>
|
||||
IList<string> ResentReplyTo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the To header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the To header.</value>
|
||||
IList<string> To { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Resent-To header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Resent-To header.</value>
|
||||
IList<string> ResentTo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Cc header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Cc header.</value>
|
||||
IList<string> Cc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Resent-Cc header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Resent-Cc header.</value>
|
||||
IList<string> ResentCc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Bcc header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Bcc header.</value>
|
||||
IList<string> Bcc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of addresses in the Resent-Bcc header.
|
||||
/// </summary>
|
||||
/// <value>The list of addresses in the Resent-Bcc header.</value>
|
||||
IList<string> ResentBcc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the subject of the message.
|
||||
/// </summary>
|
||||
/// <value>The subject of the message.</value>
|
||||
string Subject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the date of the message.
|
||||
/// </summary>
|
||||
/// <value>The date of the message.</value>
|
||||
DateTimeOffset Date { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the Resent-Date of the message.
|
||||
/// </summary>
|
||||
/// <value>The Resent-Date of the message.</value>
|
||||
DateTimeOffset ResentDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of references to other messages.
|
||||
/// </summary>
|
||||
/// <value>The references.</value>
|
||||
IList<string> References { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the Message-Id that this message is replying to.
|
||||
/// </summary>
|
||||
/// <value>The message id that this message is in reply to.</value>
|
||||
string InReplyTo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the message identifier.
|
||||
/// </summary>
|
||||
/// <value>The message identifier.</value>
|
||||
string MessageId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the Resent-Message-Id header.
|
||||
/// </summary>
|
||||
/// <value>The Resent-Message-Id.</value>
|
||||
string ResentMessageId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the MIME-Version.
|
||||
/// </summary>
|
||||
/// <value>The MIME version.</value>
|
||||
Version MimeVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the body of the message.
|
||||
/// </summary>
|
||||
/// <value>The body of the message.</value>
|
||||
IMimeEntityData Body { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the text body of the message if it exists.
|
||||
/// </summary>
|
||||
/// <value>The text body if it exists; otherwise, <see langword="null"/>.</value>
|
||||
string TextBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the html body of the message if it exists.
|
||||
/// </summary>
|
||||
/// <value>The html body if it exists; otherwise, <see langword="null"/>.</value>
|
||||
string HtmlBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the body parts of the message.
|
||||
/// </summary>
|
||||
/// <value>The body parts.</value>
|
||||
IList<IMimePartData> BodyParts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the attachments.
|
||||
/// </summary>
|
||||
/// <value>The attachments.</value>
|
||||
IList<IMimeEntityData> Attachments { get; }
|
||||
}
|
||||
57
src/WireMock.Net.Abstractions/Models/Mime/IMimePartData.cs
Normal file
57
src/WireMock.Net.Abstractions/Models/Mime/IMimePartData.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace WireMock.Models.Mime;
|
||||
|
||||
/// <summary>
|
||||
/// A simplified interface exposing the public, readable properties of MimePart.
|
||||
/// </summary>
|
||||
public interface IMimePartData : IMimeEntityData
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the description of the content if available.
|
||||
/// </summary>
|
||||
/// <value>The description of the content.</value>
|
||||
string ContentDescription { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the duration of the content if available.
|
||||
/// </summary>
|
||||
/// <value>The duration of the content.</value>
|
||||
int? ContentDuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the md5sum of the content.
|
||||
/// </summary>
|
||||
/// <value>The md5sum of the content.</value>
|
||||
string ContentMd5 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the content transfer encoding.
|
||||
/// </summary>
|
||||
/// <value>The content transfer encoding as a string.</value>
|
||||
string ContentTransferEncoding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the file.
|
||||
/// </summary>
|
||||
/// <value>The name of the file.</value>
|
||||
string FileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the MIME content.
|
||||
/// </summary>
|
||||
/// <value>The MIME content.</value>
|
||||
IDictionary<string, object?> Content { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Open the decoded content stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Provides a means of reading the decoded content without having to first write it to another stream.
|
||||
/// </remarks>
|
||||
/// <returns>The decoded content stream.</returns>
|
||||
Stream Open();
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using MimeKit;
|
||||
using WireMock.Matchers.Helpers;
|
||||
using WireMock.Models.Mime;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
@@ -12,7 +12,7 @@ namespace WireMock.Matchers;
|
||||
/// </summary>
|
||||
public class MimePartMatcher : IMimePartMatcher
|
||||
{
|
||||
private readonly Func<MimePart, MatchResult>[] _funcs;
|
||||
private readonly Func<IMimePartData, MatchResult>[] _funcs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(MimePartMatcher);
|
||||
@@ -52,21 +52,21 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
_funcs =
|
||||
[
|
||||
mp => ContentTypeMatcher?.IsMatch(GetContentTypeAsString(mp.ContentType)) ?? MatchScores.Perfect,
|
||||
mp => ContentDispositionMatcher?.IsMatch(mp.ContentDisposition.ToString().Replace("Content-Disposition: ", string.Empty)) ?? MatchScores.Perfect,
|
||||
mp => ContentTransferEncodingMatcher?.IsMatch(mp.ContentTransferEncoding.ToString().ToLowerInvariant()) ?? MatchScores.Perfect,
|
||||
mp => ContentDispositionMatcher?.IsMatch(mp.ContentDisposition?.ToString()?.Replace("Content-Disposition: ", string.Empty)) ?? MatchScores.Perfect,
|
||||
mp => ContentTransferEncodingMatcher?.IsMatch(mp.ContentTransferEncoding.ToLowerInvariant()) ?? MatchScores.Perfect,
|
||||
MatchOnContent
|
||||
];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object value)
|
||||
public MatchResult IsMatch(IMimePartData value)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (value is MimePart mimePart && Array.TrueForAll(_funcs, func => func(mimePart).IsPerfect()))
|
||||
if (Array.TrueForAll(_funcs, func => func(value).IsPerfect()))
|
||||
{
|
||||
score = MatchScores.Perfect;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
return "NotImplemented";
|
||||
}
|
||||
|
||||
private MatchResult MatchOnContent(MimePart mimePart)
|
||||
private MatchResult MatchOnContent(IMimePartData mimePart)
|
||||
{
|
||||
if (ContentMatcher == null)
|
||||
{
|
||||
@@ -94,10 +94,10 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
|
||||
var bodyParserSettings = new BodyParserSettings
|
||||
{
|
||||
Stream = mimePart.Content.Open(),
|
||||
Stream = mimePart.Open(),
|
||||
ContentType = GetContentTypeAsString(mimePart.ContentType),
|
||||
DeserializeJson = true,
|
||||
ContentEncoding = null, // mimePart.ContentType.CharsetEncoding.ToString(),
|
||||
ContentEncoding = null, // mimePart.ContentType?.CharsetEncoding.ToString(),
|
||||
DecompressGZipAndDeflate = true
|
||||
};
|
||||
|
||||
@@ -105,8 +105,8 @@ public class MimePartMatcher : IMimePartMatcher
|
||||
return BodyDataMatchScoreCalculator.CalculateMatchScore(bodyData, ContentMatcher);
|
||||
}
|
||||
|
||||
private static string? GetContentTypeAsString(ContentType? contentType)
|
||||
private static string? GetContentTypeAsString(IContentTypeData? contentType)
|
||||
{
|
||||
return contentType?.ToString().Replace("Content-Type: ", string.Empty);
|
||||
return contentType?.ToString()?.Replace("Content-Type: ", string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MimeKit;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class that implements the IContentDispositionData interface
|
||||
/// by wrapping a ContentDisposition object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class provides a simplified, read-only view of a ContentDisposition.
|
||||
/// </remarks>
|
||||
public class ContentDispositionDataWrapper : IContentDispositionData
|
||||
{
|
||||
private readonly ContentDisposition _contentDisposition;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentDispositionDataWrapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="contentDisposition">The ContentDisposition to wrap.</param>
|
||||
public ContentDispositionDataWrapper(ContentDisposition contentDisposition)
|
||||
{
|
||||
_contentDisposition = Guard.NotNull(contentDisposition);
|
||||
|
||||
Parameters = _contentDisposition.Parameters.Select(p => p.ToString()).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Disposition => _contentDisposition.Disposition;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAttachment => _contentDisposition.IsAttachment;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> Parameters { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string FileName => _contentDisposition.FileName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTimeOffset? CreationDate => _contentDisposition.CreationDate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTimeOffset? ModificationDate => _contentDisposition.ModificationDate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTimeOffset? ReadDate => _contentDisposition.ReadDate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long? Size => _contentDisposition.Size;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return _contentDisposition.ToString();
|
||||
}
|
||||
}
|
||||
65
src/WireMock.Net.MimePart/Models/ContentTypeDataWrapper.cs
Normal file
65
src/WireMock.Net.MimePart/Models/ContentTypeDataWrapper.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MimeKit;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class that implements the <see cref="IContentTypeData"/> interface by wrapping a <see cref="ContentType"/> object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class provides a simplified, read-only view of a <see cref="ContentType"/>.
|
||||
/// </remarks>
|
||||
public class ContentTypeDataWrapper : IContentTypeData
|
||||
{
|
||||
private readonly ContentType _contentType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTypeDataWrapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The ContentType to wrap.</param>
|
||||
public ContentTypeDataWrapper(ContentType contentType)
|
||||
{
|
||||
_contentType = Guard.NotNull(contentType);
|
||||
|
||||
Parameters = _contentType.Parameters.Select(p => p.ToString()).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string MediaType => _contentType.MediaType;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string MediaSubtype => _contentType.MediaSubtype;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> Parameters { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Boundary => _contentType.Boundary;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Charset => _contentType.Charset;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Encoding CharsetEncoding => _contentType.CharsetEncoding;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Format => _contentType.Format;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string MimeType => _contentType.MimeType;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name => _contentType.Name;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return _contentType.ToString();
|
||||
}
|
||||
}
|
||||
61
src/WireMock.Net.MimePart/Models/MimeEntityDataWrapper.cs
Normal file
61
src/WireMock.Net.MimePart/Models/MimeEntityDataWrapper.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MimeKit;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class that implements the <see cref="IMimeEntityData" /> interface by wrapping an <see cref="IMimeEntity" /> interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class provides a simplified, read-only view of an <see cref="IMimeEntity"/>.
|
||||
/// </remarks>
|
||||
public class MimeEntityDataWrapper : IMimeEntityData
|
||||
{
|
||||
private readonly IMimeEntity _entity;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MimeEntityDataWrapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="entity">The MIME entity to wrap.</param>
|
||||
public MimeEntityDataWrapper(IMimeEntity entity)
|
||||
{
|
||||
_entity = Guard.NotNull(entity);
|
||||
|
||||
ContentDisposition = _entity.ContentDisposition != null ? new ContentDispositionDataWrapper(_entity.ContentDisposition) : null;
|
||||
ContentType = _entity.ContentType != null ? new ContentTypeDataWrapper(_entity.ContentType) : null;
|
||||
Headers = _entity.Headers.Select(h => h.ToString()).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> Headers { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IContentDispositionData? ContentDisposition { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IContentTypeData? ContentType { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Uri ContentBase => _entity.ContentBase;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Uri ContentLocation => _entity.ContentLocation;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ContentId => _entity.ContentId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAttachment => _entity.IsAttachment;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return _entity.ToString()!;
|
||||
}
|
||||
}
|
||||
140
src/WireMock.Net.MimePart/Models/MimeMessageDataWrapper.cs
Normal file
140
src/WireMock.Net.MimePart/Models/MimeMessageDataWrapper.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MimeKit;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class that implements the <see cref="IMimeMessageData" /> interface by wrapping an <see cref="IMimeMessage" /> interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class provides a simplified, read-only view of an <see cref="IMimeMessage"/>.
|
||||
/// </remarks>
|
||||
internal class MimeMessageDataWrapper : IMimeMessageData
|
||||
{
|
||||
private readonly IMimeMessage _message;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MimeMessageDataWrapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The MIME message to wrap.</param>
|
||||
public MimeMessageDataWrapper(IMimeMessage message)
|
||||
{
|
||||
_message = Guard.NotNull(message);
|
||||
|
||||
Bcc = _message.Bcc.Select(h => h.ToString()).ToList();
|
||||
Cc = _message.Cc.Select(h => h.ToString()).ToList();
|
||||
From = _message.From.Select(h => h.ToString()).ToList();
|
||||
Headers = _message.Headers.Select(h => h.ToString()).ToList();
|
||||
References = _message.References.ToList();
|
||||
ReplyTo = _message.ReplyTo.Select(h => h.ToString()).ToList();
|
||||
ResentBcc = _message.ResentBcc.Select(h => h.ToString()).ToList();
|
||||
ResentCc = _message.ResentCc.Select(h => h.ToString()).ToList();
|
||||
ResentFrom = _message.ResentFrom.Select(h => h.ToString()).ToList();
|
||||
ResentReplyTo = _message.ResentReplyTo.Select(h => h.ToString()).ToList();
|
||||
ResentTo = _message.ResentTo.Select(h => h.ToString()).ToList();
|
||||
To = _message.To.Select(h => h.ToString()).ToList();
|
||||
|
||||
Body = new MimeEntityDataWrapper(_message.Body);
|
||||
BodyParts = _message.BodyParts.OfType<MimePart>().Select(mp => new MimePartDataWrapper(mp)).ToList<IMimePartData>();
|
||||
Attachments = _message.Attachments.Select(me => new MimeEntityDataWrapper(me)).ToList<IMimeEntityData>();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> Headers { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Importance => (int)_message.Importance;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Priority => (int)_message.Priority;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int XPriority => (int)_message.XPriority;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Sender => _message.Sender.Address;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ResentSender => _message.ResentSender.ToString();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> From { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> ResentFrom { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> ReplyTo { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> ResentReplyTo { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> To { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> ResentTo { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> Cc { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> ResentCc { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> Bcc { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> ResentBcc { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Subject => _message.Subject;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTimeOffset Date => _message.Date;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTimeOffset ResentDate => _message.ResentDate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<string> References { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string InReplyTo => _message.InReplyTo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string MessageId => _message.MessageId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ResentMessageId => _message.ResentMessageId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Version MimeVersion => _message.MimeVersion;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IMimeEntityData Body { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string TextBody => _message.TextBody;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string HtmlBody => _message.HtmlBody;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<IMimePartData> BodyParts { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IList<IMimeEntityData> Attachments { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return _message.ToString();
|
||||
}
|
||||
}
|
||||
64
src/WireMock.Net.MimePart/Models/MimePartDataWrapper.cs
Normal file
64
src/WireMock.Net.MimePart/Models/MimePartDataWrapper.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MimeKit;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper class that implements the <see cref="IMimePartData" /> interface by wrapping an <see cref="IMimePart"/> interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class provides a simplified, read-only view of an <see cref="IMimePart"/>.
|
||||
/// </remarks>
|
||||
public class MimePartDataWrapper : MimeEntityDataWrapper, IMimePartData
|
||||
{
|
||||
private readonly IMimePart _part;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MimePartDataWrapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="part">The MIME part to wrap.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// <paramref name="part"/> is <see langword="null"/>.
|
||||
/// </exception>
|
||||
public MimePartDataWrapper(IMimePart part) : base(part)
|
||||
{
|
||||
_part = Guard.NotNull(part);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ContentDescription => _part.ContentDescription;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int? ContentDuration => _part.ContentDuration;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ContentMd5 => _part.ContentMd5;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ContentTransferEncoding => _part.ContentTransferEncoding.ToString();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string FileName => _part.FileName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDictionary<string, object?> Content => new Dictionary<string, object?>()
|
||||
{
|
||||
{ nameof(MimePart.Content.Encoding), _part.Content.Encoding },
|
||||
{ nameof(MimePart.Content.NewLineFormat), _part.Content.NewLineFormat },
|
||||
{ nameof(MimePart.Content.Stream), _part.Content.Stream }
|
||||
};
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Stream Open() => _part.Content.Open();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return _part.ToString()!;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ using System.Text;
|
||||
using MimeKit;
|
||||
using Stef.Validation;
|
||||
using WireMock.Http;
|
||||
using WireMock.Models;
|
||||
using WireMock.Models.Mime;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Util;
|
||||
@@ -16,13 +18,13 @@ namespace WireMock.Util;
|
||||
internal class MimeKitUtils : IMimeKitUtils
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object LoadFromStream(Stream stream)
|
||||
public IMimeMessageData LoadFromStream(Stream stream)
|
||||
{
|
||||
return MimeMessage.Load(Guard.NotNull(stream));
|
||||
return new MimeMessageDataWrapper(MimeMessage.Load(Guard.NotNull(stream)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out object? mimeMessage)
|
||||
public bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out IMimeMessageData? mimeMessageData)
|
||||
{
|
||||
Guard.NotNull(requestMessage);
|
||||
|
||||
@@ -44,26 +46,14 @@ internal class MimeKitUtils : IMimeKitUtils
|
||||
|
||||
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
|
||||
|
||||
mimeMessage = LoadFromStream(new MemoryStream(fixedBytes));
|
||||
mimeMessageData = LoadFromStream(new MemoryStream(fixedBytes));
|
||||
return true;
|
||||
}
|
||||
|
||||
mimeMessage = null;
|
||||
mimeMessageData = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<object> GetBodyParts(object mimeMessage)
|
||||
{
|
||||
if (mimeMessage is not MimeMessage mm)
|
||||
{
|
||||
throw new ArgumentException($"The mimeMessage must be of type {nameof(MimeMessage)}", nameof(mimeMessage));
|
||||
}
|
||||
|
||||
return mm.BodyParts
|
||||
.OfType<MimePart>()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static bool StartsWithMultiPart(WireMockList<string> contentTypeHeader)
|
||||
{
|
||||
|
||||
@@ -72,7 +72,8 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
foreach (var mimePartMatcher in Matchers.OfType<IMimePartMatcher>().ToArray())
|
||||
{
|
||||
score = MatchScores.Mismatch;
|
||||
foreach (var mimeBodyPart in MimeKitUtils.GetBodyParts(message))
|
||||
|
||||
foreach (var mimeBodyPart in message.BodyParts)
|
||||
{
|
||||
var matchResult = mimePartMatcher.IsMatch(mimeBodyPart);
|
||||
if (matchResult.IsPerfect())
|
||||
|
||||
@@ -85,7 +85,7 @@ public class RequestMessage : IRequestMessage
|
||||
#if MIMEKIT
|
||||
/// <inheritdoc />
|
||||
[Newtonsoft.Json.JsonIgnore] // Issue 1001
|
||||
public object? BodyAsMimeMessage { get; }
|
||||
public Models.Mime.IMimeMessageData? BodyAsMimeMessage { get; }
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
@@ -33,5 +35,5 @@ public interface IMimePartMatcher : IMatcher
|
||||
/// </summary>
|
||||
/// <param name="value">The MimePart.</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public MatchResult IsMatch(object value);
|
||||
public MatchResult IsMatch(IMimePartData value);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using WireMock.Models.Mime;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
@@ -12,24 +12,16 @@ namespace WireMock.Util;
|
||||
public interface IMimeKitUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads the MimeKit.MimeMessage from the stream.
|
||||
/// Loads the <see cref="IMimeMessageData"/> from the stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream</param>
|
||||
/// <returns>MimeKit.MimeMessage</returns>
|
||||
object LoadFromStream(Stream stream);
|
||||
IMimeMessageData LoadFromStream(Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the MimeKit.MimeMessage from the request message.
|
||||
/// Tries to get the <see cref="IMimeMessageData"/> from the request message.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <param name="mimeMessage">The MimeKit.MimeMessage</param>
|
||||
/// <param name="mimeMessageData">A class MimeMessageDataWrapper which wraps a MimeKit.MimeMessage.</param>
|
||||
/// <returns><c>true</c> when parsed correctly, else <c>false</c></returns>
|
||||
bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out object? mimeMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body parts from the MimeKit.MimeMessage.
|
||||
/// </summary>
|
||||
/// <param name="mimeMessage">The MimeKit.MimeMessage.</param>
|
||||
/// <returns>A list of MimeParts.</returns>
|
||||
IReadOnlyList<object> GetBodyParts(object mimeMessage);
|
||||
bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out IMimeMessageData? mimeMessageData);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class MimePartMatcherTests
|
||||
{
|
||||
// Arrange
|
||||
var message = MimeKitUtils.LoadFromStream(StreamUtils.CreateStream(TestMultiPart));
|
||||
var part = MimeKitUtils.GetBodyParts(message)[0];
|
||||
var part = message.BodyParts[0];
|
||||
|
||||
// Act
|
||||
var contentTypeMatcher = new ContentTypeMatcher("text/plain");
|
||||
@@ -67,7 +67,7 @@ public class MimePartMatcherTests
|
||||
{
|
||||
// Arrange
|
||||
var message = MimeKitUtils.LoadFromStream(StreamUtils.CreateStream(TestMultiPart));
|
||||
var part = MimeKitUtils.GetBodyParts(message)[1];
|
||||
var part = message.BodyParts[1];
|
||||
|
||||
// Act
|
||||
var contentTypeMatcher = new ContentTypeMatcher("text/json");
|
||||
@@ -85,7 +85,7 @@ public class MimePartMatcherTests
|
||||
{
|
||||
// Arrange
|
||||
var message = MimeKitUtils.LoadFromStream(StreamUtils.CreateStream(TestMultiPart));
|
||||
var part = MimeKitUtils.GetBodyParts(message)[2];
|
||||
var part = message.BodyParts[2];
|
||||
|
||||
// Act
|
||||
var contentTypeMatcher = new ContentTypeMatcher("image/png");
|
||||
|
||||
Reference in New Issue
Block a user