mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-28 19:27:05 +02:00
Create WireMock.Net.ProtoBuf project (#1350)
* Create WireMock.Net.ProtoBuf project * ok * Update Directory.Build.props 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:
@@ -1,86 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.IO;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
internal static class FilePathUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Robust handling of the user defined path.
|
||||
/// Also supports Unix and Windows platforms
|
||||
/// </summary>
|
||||
/// <param name="path">The path to clean</param>
|
||||
public static string? CleanPath(string? path)
|
||||
{
|
||||
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
|
||||
/// </summary>
|
||||
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
|
||||
public static string? RemoveLeadingDirectorySeparators(string? path)
|
||||
{
|
||||
return path?.TrimStart(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combine two paths
|
||||
/// </summary>
|
||||
/// <param name="root">The root path</param>
|
||||
/// <param name="path">The path</param>
|
||||
public static string Combine(string root, string? path)
|
||||
{
|
||||
Guard.NotNull(root);
|
||||
|
||||
var result = RemoveLeadingDirectorySeparators(path);
|
||||
return result == null ? root : Path.Combine(root, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a relative path from one path to another.
|
||||
/// </summary>
|
||||
/// <param name="relativeTo">The source path the result should be relative to. This path is always considered to be a directory..</param>
|
||||
/// <param name="path">The destination path.</param>
|
||||
/// <returns>The relative path, or path if the paths don't share the same root.</returns>
|
||||
public static string GetRelativePath(string relativeTo, string path)
|
||||
{
|
||||
#if NETCOREAPP3_1 || NET5_0_OR_GREATER || NETSTANDARD2_1
|
||||
return Path.GetRelativePath(relativeTo, path);
|
||||
#else
|
||||
Guard.NotNull(relativeTo);
|
||||
Guard.NotNull(path);
|
||||
|
||||
static string AppendDirectorySeparatorChar(string path)
|
||||
{
|
||||
// Append a slash only if the path is a directory and does not have a slash.
|
||||
if (!Path.HasExtension(path) && !path.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
return path + Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
var fromUri = new System.Uri(AppendDirectorySeparatorChar(relativeTo));
|
||||
var toUri = new System.Uri(AppendDirectorySeparatorChar(path));
|
||||
|
||||
if (fromUri.Scheme != toUri.Scheme)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
var relativeUri = fromUri.MakeRelativeUri(toUri);
|
||||
var relativePath = System.Uri.UnescapeDataString(relativeUri.ToString());
|
||||
|
||||
if (string.Equals(toUri.Scheme, "FILE", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
return relativePath;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JsonConverter.Abstractions;
|
||||
using ProtoBufJsonConverter;
|
||||
using ProtoBufJsonConverter.Models;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
internal static class ProtoBufUtils
|
||||
{
|
||||
internal static async Task<byte[]> GetProtoBufMessageWithHeaderAsync(
|
||||
IReadOnlyList<string>? protoDefinitions,
|
||||
string? messageType,
|
||||
object? value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (protoDefinitions == null || string.IsNullOrWhiteSpace(messageType) || value is null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var resolver = new WireMockProtoFileResolver(protoDefinitions);
|
||||
var request = new ConvertToProtoBufRequest(protoDefinitions[0], messageType!, value, true)
|
||||
.WithProtoFileResolver(resolver);
|
||||
|
||||
return await SingletonFactory<Converter>
|
||||
.GetInstance()
|
||||
.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ProtoBufJsonConverter;
|
||||
using ProtoBufJsonConverter.Models;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
/// <summary>
|
||||
/// Some helper methods for Proto Definitions.
|
||||
/// </summary>
|
||||
public static class ProtoDefinitionHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a dictionary of ProtoDefinitions from a directory.
|
||||
/// - The key will be the filename without extension.
|
||||
/// - The value will be the ProtoDefinition with an extra comment with the relative path to each <c>.proto</c> file so it can be used by the WireMockProtoFileResolver.
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory to start from.</param>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <c>System.Threading.CancellationToken.None</c>.</param>
|
||||
public static async Task<ProtoDefinitionData> FromDirectory(string directory, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Guard.NotNullOrEmpty(directory);
|
||||
|
||||
var fileNameMappedToProtoDefinition = new Dictionary<string, string>();
|
||||
var filePaths = Directory.EnumerateFiles(directory, "*.proto", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var filePath in filePaths)
|
||||
{
|
||||
// Get the relative path to the directory (note that this will be OS specific).
|
||||
var relativePath = FilePathUtils.GetRelativePath(directory, filePath);
|
||||
|
||||
// Make it a valid proto import path
|
||||
var protoRelativePath = relativePath.Replace(Path.DirectorySeparatorChar, '/');
|
||||
|
||||
// Build comment and get content from file.
|
||||
var comment = $"// {protoRelativePath}";
|
||||
#if NETSTANDARD2_0
|
||||
var content = File.ReadAllText(filePath);
|
||||
#else
|
||||
var content = await File.ReadAllTextAsync(filePath, cancellationToken);
|
||||
#endif
|
||||
// Only add the comment if it's not already defined.
|
||||
var modifiedContent = !content.StartsWith(comment) ? $"{comment}\n{content}" : content;
|
||||
var key = Path.GetFileNameWithoutExtension(filePath);
|
||||
|
||||
fileNameMappedToProtoDefinition.Add(key, modifiedContent);
|
||||
}
|
||||
|
||||
var converter = SingletonFactory<Converter>.GetInstance();
|
||||
var resolver = new WireMockProtoFileResolver(fileNameMappedToProtoDefinition.Values);
|
||||
|
||||
var messageTypeMappedToWithProtoDefinition = new Dictionary<string, string>();
|
||||
|
||||
foreach (var protoDefinition in fileNameMappedToProtoDefinition.Values)
|
||||
{
|
||||
var infoRequest = new GetInformationRequest(protoDefinition, resolver);
|
||||
|
||||
try
|
||||
{
|
||||
var info = await converter.GetInformationAsync(infoRequest, cancellationToken);
|
||||
foreach (var messageType in info.MessageTypes)
|
||||
{
|
||||
messageTypeMappedToWithProtoDefinition[messageType.Key] = protoDefinition;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
return new ProtoDefinitionData(fileNameMappedToProtoDefinition);
|
||||
}
|
||||
|
||||
internal static IdOrTexts GetIdOrTexts(WireMockServerSettings settings, params string[] protoDefinitionOrId)
|
||||
{
|
||||
switch (protoDefinitionOrId.Length)
|
||||
{
|
||||
case 1:
|
||||
var idOrText = protoDefinitionOrId[0];
|
||||
if (settings.ProtoDefinitions?.TryGetValue(idOrText, out var protoDefinitions) == true)
|
||||
{
|
||||
return new(idOrText, protoDefinitions);
|
||||
}
|
||||
|
||||
return new(null, protoDefinitionOrId);
|
||||
|
||||
default:
|
||||
return new(null, protoDefinitionOrId);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
internal static class SingletonLock
|
||||
{
|
||||
public static readonly object Lock = new();
|
||||
}
|
||||
|
||||
internal static class SingletonFactory<T> where T : class, new()
|
||||
{
|
||||
private static T? _instance;
|
||||
|
||||
public static T GetInstance()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (SingletonLock.Lock)
|
||||
{
|
||||
_instance ??= new T();
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ProtoBufJsonConverter;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
/// <summary>
|
||||
/// This resolver is used to resolve the extra ProtoDefinition files.
|
||||
///
|
||||
/// It assumes that:
|
||||
/// - The first commented line of each ProtoDefinition file is the filepath which is used in the import of the other ProtoDefinition file(s).
|
||||
/// </summary>
|
||||
internal class WireMockProtoFileResolver : IProtoFileResolver
|
||||
{
|
||||
private readonly Dictionary<string, string> _files = [];
|
||||
|
||||
public WireMockProtoFileResolver(IReadOnlyCollection<string> protoDefinitions)
|
||||
{
|
||||
if (Guard.NotNullOrEmpty(protoDefinitions).Count() <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var extraProtoDefinition in protoDefinitions)
|
||||
{
|
||||
var firstNonEmptyLine = extraProtoDefinition.Split(['\r', '\n']).FirstOrDefault(l => !string.IsNullOrEmpty(l));
|
||||
if (firstNonEmptyLine != null)
|
||||
{
|
||||
if (TryGetValidPath(firstNonEmptyLine.TrimStart(['/', ' ']), out var validPath))
|
||||
{
|
||||
_files.Add(validPath, extraProtoDefinition);
|
||||
}
|
||||
else
|
||||
{
|
||||
_files.Add(extraProtoDefinition.GetDeterministicHashCodeAsString(), extraProtoDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exists(string path)
|
||||
{
|
||||
return _files.ContainsKey(path);
|
||||
}
|
||||
|
||||
public TextReader OpenText(string path)
|
||||
{
|
||||
if (_files.TryGetValue(path, out var extraProtoDefinition))
|
||||
{
|
||||
return new StringReader(extraProtoDefinition);
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"The ProtoDefinition '{path}' was not found.");
|
||||
}
|
||||
|
||||
private static bool TryGetValidPath(string path, [NotNullWhen(true)] out string? validPath)
|
||||
{
|
||||
if (!path.Any(c => Path.GetInvalidPathChars().Contains(c)))
|
||||
{
|
||||
validPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
validPath = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user