Fixed example generation

Additional logging
This commit is contained in:
Mark van Renswoude 2022-01-01 21:45:15 +01:00
parent db443556e8
commit 8ca67c2cc5
18 changed files with 231 additions and 76 deletions

View File

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -9,6 +9,16 @@ namespace PettingZoo.Core.Settings
public static string AppDataRoot { get; } public static string AppDataRoot { get; }
public static string InstallationRoot { get; } public static string InstallationRoot { get; }
public static string LogPath => Path.Combine(AppDataRoot, @"Logs");
public static string DatabasePath => AppDataRoot;
public const string AssembliesPath = @"Assemblies";
public static string AppDataAssemblies => Path.Combine(AppDataRoot, AssembliesPath);
public static string InstallationAssemblies => Path.Combine(InstallationRoot, AssembliesPath);
static PettingZooPaths() static PettingZooPaths()
{ {

View File

@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="6.2.2" /> <PackageReference Include="RabbitMQ.Client" Version="6.2.2" />
<PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -16,7 +16,7 @@ namespace PettingZoo.Settings.LiteDB
public BaseLiteDBRepository(string databaseName) public BaseLiteDBRepository(string databaseName)
{ {
databaseFilename = Path.Combine(PettingZooPaths.AppDataRoot, $"{databaseName}.litedb"); databaseFilename = Path.Combine(PettingZooPaths.DatabasePath, $"{databaseName}.litedb");
} }

View File

@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.11" /> <PackageReference Include="LiteDB" Version="5.0.11" />
<PackageReference Include="LiteDB.Async" Version="0.0.11" /> <PackageReference Include="LiteDB.Async" Version="0.0.11" />
<PackageReference Include="Serilog" Version="2.10.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,37 +2,37 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Runtime.Loader;
using System.Runtime.InteropServices;
using Newtonsoft.Json; using Newtonsoft.Json;
using PettingZoo.Core.Generator; using PettingZoo.Core.Generator;
using Tapeti.DataAnnotations.Extensions;
namespace PettingZoo.Tapeti.AssemblyParser namespace PettingZoo.Tapeti.AssemblyParser
{ {
public class AssemblyParser : IDisposable public class AssemblyParser : IDisposable
{ {
private readonly MetadataLoadContext loadContext; private readonly AssemblyLoadContext loadContext;
public AssemblyParser(params string[] extraAssemblies) public AssemblyParser(params string[] extraAssembliesPaths)
{ {
var runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll"); // Using the MetadataLoadContext introduces extra complexity since types can not be compared
var paths = runtimeAssemblies // (a string from the loaded assembly does not equal our typeof(string) for example).
.Concat(extraAssemblies) // So instead we'll use a regular AssemblyLoadContext. Not ideal, and will probably cause other side-effects
// if we're not careful, but I don't feel like writing a full metadata parser right now.
// If you have a better idea, it's open-source! :-)
loadContext = new AssemblyLoadContext(null, true);
// TODO find a cleaner way foreach (var extraAssembly in extraAssembliesPaths.SelectMany(p => Directory.Exists(p)
.Append(typeof(JsonSerializer).Assembly.Location) ? Directory.GetFiles(p, "*.dll")
.Append(typeof(RequiredGuidAttribute).Assembly.Location); : Enumerable.Empty<string>()))
{
loadContext.LoadFromAssemblyPath(extraAssembly);
var resolver = new PathAssemblyResolver(paths); }
loadContext = new MetadataLoadContext(resolver);
} }
public void Dispose() public void Dispose()
{ {
loadContext.Dispose(); loadContext.Unload();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }

View File

@ -6,7 +6,6 @@ using System.Threading.Tasks;
namespace PettingZoo.Tapeti.NuGet namespace PettingZoo.Tapeti.NuGet
{ {
// TODO support logger
public interface INuGetPackageManager public interface INuGetPackageManager
{ {
public IReadOnlyList<INuGetPackageSource> Sources { get; } public IReadOnlyList<INuGetPackageSource> Sources { get; }
@ -36,7 +35,6 @@ namespace PettingZoo.Tapeti.NuGet
{ {
public string Version { get; } public string Version { get; }
// TODO support fetching dependencies
public Task Download(Stream destination, CancellationToken cancellationToken); public Task Download(Stream destination, CancellationToken cancellationToken);
} }
} }

View File

@ -9,23 +9,28 @@ using NuGet.Common;
using NuGet.Protocol; using NuGet.Protocol;
using NuGet.Protocol.Core.Types; using NuGet.Protocol.Core.Types;
using NuGet.Versioning; using NuGet.Versioning;
using ILogger = Serilog.ILogger;
namespace PettingZoo.Tapeti.NuGet namespace PettingZoo.Tapeti.NuGet
{ {
public class NuGetPackageManager : INuGetPackageManager public class NuGetPackageManager : INuGetPackageManager
{ {
private const string NuGetDefaultSource = @"https://api.nuget.org/v3/index.json";
private readonly ILogger logger;
private readonly SourceCacheContext cache; private readonly SourceCacheContext cache;
private readonly List<Source> sources; private readonly List<Source> sources;
public IReadOnlyList<INuGetPackageSource> Sources => sources; public IReadOnlyList<INuGetPackageSource> Sources => sources;
public NuGetPackageManager() public NuGetPackageManager(ILogger logger)
{ {
this.logger = logger;
cache = new SourceCacheContext(); cache = new SourceCacheContext();
sources = new List<Source> sources = new List<Source>
{ {
new(cache, "nuget.org", @"https://api.nuget.org/v3/index.json") new(logger.ForContext("source", NuGetDefaultSource), cache, "nuget.org", NuGetDefaultSource)
}; };
} }
@ -53,7 +58,7 @@ namespace PettingZoo.Tapeti.NuGet
if (string.IsNullOrEmpty(nameAttribute?.Value) || string.IsNullOrEmpty(urlAttribute?.Value)) if (string.IsNullOrEmpty(nameAttribute?.Value) || string.IsNullOrEmpty(urlAttribute?.Value))
continue; continue;
sources.Add(new Source(cache, nameAttribute.Value, urlAttribute.Value)); sources.Add(new Source(logger.ForContext("source", urlAttribute.Value), cache, nameAttribute.Value, urlAttribute.Value));
} }
return this; return this;
@ -63,14 +68,16 @@ namespace PettingZoo.Tapeti.NuGet
private class Source : INuGetPackageSource private class Source : INuGetPackageSource
{ {
private readonly ILogger logger;
private readonly SourceCacheContext cache; private readonly SourceCacheContext cache;
private readonly SourceRepository repository; private readonly SourceRepository repository;
public string Name { get; } public string Name { get; }
public Source(SourceCacheContext cache, string name, string url) public Source(ILogger logger, SourceCacheContext cache, string name, string url)
{ {
this.logger = logger;
this.cache = cache; this.cache = cache;
Name = name; Name = name;
repository = Repository.Factory.GetCoreV3(url); repository = Repository.Factory.GetCoreV3(url);
@ -82,19 +89,30 @@ namespace PettingZoo.Tapeti.NuGet
if (string.IsNullOrWhiteSpace(searchTerm)) if (string.IsNullOrWhiteSpace(searchTerm))
return Array.Empty<INuGetPackage>(); return Array.Empty<INuGetPackage>();
var resource = await repository.GetResourceAsync<PackageSearchResource>(cancellationToken); try
var filter = new SearchFilter(includePrerelease); {
var resource = await repository.GetResourceAsync<PackageSearchResource>(cancellationToken);
var filter = new SearchFilter(includePrerelease);
return (await resource.SearchAsync(searchTerm, filter, 0, 20, new NullLogger(), var result = (await resource.SearchAsync(searchTerm, filter, 0, 20, new NullLogger(),
cancellationToken)) cancellationToken))
.Select(p => new Package(cache, repository, p)) .Select(p => new Package(logger, cache, repository, p))
.ToArray(); .ToArray();
return result;
}
catch (Exception e)
{
logger.Error(e, "NuGet Search failed for term '{searchTerm}' (includePrerelease {includePrerelease})", searchTerm, includePrerelease);
throw;
}
} }
} }
protected class Package : INuGetPackage protected class Package : INuGetPackage
{ {
private readonly ILogger logger;
private readonly SourceCacheContext cache; private readonly SourceCacheContext cache;
private readonly SourceRepository repository; private readonly SourceRepository repository;
private readonly IPackageSearchMetadata packageSearchMetadata; private readonly IPackageSearchMetadata packageSearchMetadata;
@ -108,8 +126,9 @@ namespace PettingZoo.Tapeti.NuGet
private IReadOnlyList<INuGetPackageVersion>? versions; private IReadOnlyList<INuGetPackageVersion>? versions;
public Package(SourceCacheContext cache, SourceRepository repository, IPackageSearchMetadata packageSearchMetadata) public Package(ILogger logger, SourceCacheContext cache, SourceRepository repository, IPackageSearchMetadata packageSearchMetadata)
{ {
this.logger = logger;
this.cache = cache; this.cache = cache;
this.repository = repository; this.repository = repository;
this.packageSearchMetadata = packageSearchMetadata; this.packageSearchMetadata = packageSearchMetadata;
@ -118,9 +137,17 @@ namespace PettingZoo.Tapeti.NuGet
public async Task<IReadOnlyList<INuGetPackageVersion>> GetVersions(CancellationToken cancellationToken) public async Task<IReadOnlyList<INuGetPackageVersion>> GetVersions(CancellationToken cancellationToken)
{ {
return versions ??= (await packageSearchMetadata.GetVersionsAsync()) try
.Select(v => new PackageVersion(cache, repository, packageSearchMetadata, v.Version)) {
.ToArray(); return versions ??= (await packageSearchMetadata.GetVersionsAsync())
.Select(v => new PackageVersion(cache, repository, packageSearchMetadata, v.Version))
.ToArray();
}
catch (Exception e)
{
logger.Error(e, "NuGet GetVersions failed for packge Id '{packageId}')", packageSearchMetadata.Identity.Id);
throw;
}
} }
} }

View File

@ -11,6 +11,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NuGet.Packaging" Version="6.0.0" /> <PackageReference Include="NuGet.Packaging" Version="6.0.0" />
<PackageReference Include="NuGet.Protocol" Version="6.0.0" /> <PackageReference Include="NuGet.Protocol" Version="6.0.0" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="SharpVectors" Version="1.7.7" /> <PackageReference Include="SharpVectors" Version="1.7.7" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" /> <PackageReference Include="System.Reactive" Version="5.0.0" />

View File

@ -13,14 +13,24 @@ using PettingZoo.Tapeti.NuGet;
using PettingZoo.Tapeti.UI.ClassSelection; using PettingZoo.Tapeti.UI.ClassSelection;
using PettingZoo.Tapeti.UI.PackageProgress; using PettingZoo.Tapeti.UI.PackageProgress;
using PettingZoo.Tapeti.UI.PackageSelection; using PettingZoo.Tapeti.UI.PackageSelection;
using Serilog;
namespace PettingZoo.Tapeti namespace PettingZoo.Tapeti
{ {
public class TapetiClassLibraryExampleGenerator : IExampleGenerator public class TapetiClassLibraryExampleGenerator : IExampleGenerator
{ {
private readonly ILogger logger;
public TapetiClassLibraryExampleGenerator(ILogger logger)
{
this.logger = logger;
}
public void Select(object? ownerWindow, Action<IExample> onExampleSelected) public void Select(object? ownerWindow, Action<IExample> onExampleSelected)
{ {
var packageManager = new NuGetPackageManager() var packageManager = new NuGetPackageManager(logger)
.WithSourcesFrom(Path.Combine(PettingZooPaths.InstallationRoot, @"nuget.config")) .WithSourcesFrom(Path.Combine(PettingZooPaths.InstallationRoot, @"nuget.config"))
.WithSourcesFrom(Path.Combine(PettingZooPaths.AppDataRoot, @"nuget.config")); .WithSourcesFrom(Path.Combine(PettingZooPaths.AppDataRoot, @"nuget.config"));
@ -97,8 +107,11 @@ namespace PettingZoo.Tapeti
private static IEnumerable<IClassTypeExample> LoadExamples(IEnumerable<IPackageAssembly> assemblies) private static IEnumerable<IClassTypeExample> LoadExamples(IEnumerable<IPackageAssembly> assemblies)
{ {
// TODO support folder with additional assemblies to load, or implement a proper NuGet install (with dependencies) instead var assemblyParser = new AssemblyParser.AssemblyParser(
var assemblyParser = new AssemblyParser.AssemblyParser(); PettingZooPaths.AppDataAssemblies,
PettingZooPaths.InstallationAssemblies
);
return assemblies return assemblies
.SelectMany(a => .SelectMany(a =>
{ {

View File

@ -6,26 +6,47 @@ using Newtonsoft.Json.Linq;
namespace PettingZoo.Tapeti namespace PettingZoo.Tapeti
{ {
// TODO detect recursion
// TODO detect recursion
// TODO detect recursion
// TODO stop making nerdy jokes in comments.
// TODO generate at least one item for enumerables
// TODO support basic types
public class TypeToJObjectConverter public class TypeToJObjectConverter
{ {
public static JObject Convert(Type type) public static JObject Convert(Type type)
{ {
if (!type.IsClass)
throw new ArgumentException($"TypeToJObjectConverter.Convert expects a class, got {type.Name}");
return ClassToJToken(type, Array.Empty<Type>());
}
private static readonly Dictionary<Type, Type> TypeEquivalenceMap = new()
{
{ typeof(uint), typeof(int) },
{ typeof(long), typeof(int) },
{ typeof(ulong), typeof(int) },
{ typeof(short), typeof(int) },
{ typeof(ushort), typeof(int) },
{ typeof(float), typeof(decimal) }
};
private static readonly Dictionary<Type, JToken> TypeValueMap = new()
{
{ typeof(int), 0 },
{ typeof(decimal), 0.0 },
{ typeof(bool), false }
};
private static JObject ClassToJToken(Type classType, IEnumerable<Type> typesEncountered)
{
var newTypesEncountered = typesEncountered.Append(classType).ToArray();
var result = new JObject(); var result = new JObject();
foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) foreach (var propertyInfo in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{ {
// Note: unfortunately we can not call GetCustomAttributes here, as that would // Note: unfortunately we can not call GetCustomAttributes here for now, as that would
// trigger assemblies not included in the package to be loaded // trigger assemblies not included in the package to be loaded, which may not exist
var value = PropertyToJToken(propertyInfo.PropertyType); var value = TypeToJToken(propertyInfo.PropertyType, newTypesEncountered);
result.Add(propertyInfo.Name, value); result.Add(propertyInfo.Name, value);
} }
@ -33,23 +54,12 @@ namespace PettingZoo.Tapeti
} }
private static readonly Dictionary<Type, JToken> TypeMap = new() private static JToken TypeToJToken(Type type, ICollection<Type> typesEncountered)
{ {
{ typeof(short), 0 }, var actualType = Nullable.GetUnderlyingType(type) ?? type;
{ typeof(ushort), 0 },
{ typeof(int), 0 },
{ typeof(uint), 0 },
{ typeof(long), 0 },
{ typeof(ulong), 0 },
{ typeof(decimal), 0.0 },
{ typeof(float), 0.0 },
{ typeof(bool), false }
};
if (TypeEquivalenceMap.TryGetValue(actualType, out var equivalentType))
private static JToken PropertyToJToken(Type propertyType) actualType = equivalentType;
{
var actualType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
// String is also a class // String is also a class
@ -64,14 +74,13 @@ namespace PettingZoo.Tapeti
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (enumerableInterface != null) if (enumerableInterface != null)
return new JArray(Convert(enumerableInterface.GetGenericArguments()[0])); return new JArray(TypeToJToken(enumerableInterface.GetGenericArguments()[0], typesEncountered));
return typesEncountered.Contains(actualType) ? new JValue((object?)null) : ClassToJToken(actualType, typesEncountered);
return Convert(actualType);
} }
if (actualType.IsArray) if (actualType.IsArray)
return new JArray(Convert(actualType.GetElementType()!)); return new JArray(TypeToJToken(actualType.GetElementType()!, typesEncountered));
if (actualType.IsEnum) if (actualType.IsEnum)
return Enum.GetNames(actualType).FirstOrDefault(); return Enum.GetNames(actualType).FirstOrDefault();
@ -88,11 +97,9 @@ namespace PettingZoo.Tapeti
if (actualType == typeof(Guid)) if (actualType == typeof(Guid))
return Guid.NewGuid().ToString(); return Guid.NewGuid().ToString();
return TypeMap.TryGetValue(actualType, out var mappedToken) return TypeValueMap.TryGetValue(actualType, out var mappedToken)
? mappedToken ? mappedToken
: $"(unknown type: {actualType.Name})"; : $"(unknown type: {actualType.Name})";
} }
} }
} }

View File

@ -88,6 +88,7 @@ namespace PettingZoo.Tapeti.UI.PackageSelection
public ICommand AssemblyBrowse => assemblyBrowse; public ICommand AssemblyBrowse => assemblyBrowse;
// TODO hint for extra assemblies path
public static string HintNuGetSources => string.Format(PackageSelectionStrings.HintNuGetSources, PettingZooPaths.InstallationRoot, PettingZooPaths.AppDataRoot); public static string HintNuGetSources => string.Format(PackageSelectionStrings.HintNuGetSources, PettingZooPaths.InstallationRoot, PettingZooPaths.AppDataRoot);
public string NuGetSearchTerm public string NuGetSearchTerm

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="FluentAssertions.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PettingZoo.Tapeti\PettingZoo.Tapeti.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,65 @@
using FluentAssertions;
using FluentAssertions.Execution;
using FluentAssertions.Json;
using Newtonsoft.Json.Linq;
using PettingZoo.Tapeti;
using Xunit;
#nullable disable
namespace PettingZoo.Test.Tapeti
{
public class TypeToJObjectTest
{
[Fact]
public void TestAllSupportedTypes()
{
var converted = TypeToJObjectConverter.Convert(typeof(AllSupportedTypesTest));
using (new AssertionScope())
{
// Directly supported
converted.Should().HaveElement("StringValue").Which.Should().HaveValue("");
converted.Should().HaveElement("IntValue").Which.Should().Match(t => t.Type == JTokenType.Integer);
converted.Should().HaveElement("BoolValue").Which.Should().Match(t => t.Type == JTokenType.Boolean);
var guidValue = converted.Should().HaveElement("GuidValue").Which.Should().Match(t => t.Type == JTokenType.String)
.And.Subject.Value<string>();
Guid.TryParse(guidValue, out _).Should().BeTrue();
var objectValue = converted.Should().HaveElement("ObjectValue").Subject;
objectValue.Should().HaveElement("SubStringValue").Which.Should().HaveValue("");
objectValue.Should().HaveElement("SubIntValue").Which.Should().Match(t => t.Type == JTokenType.Integer);
objectValue.Should().HaveElement("RecursiveValue").Which.Type.Should().Be(JTokenType.Null);
// Via type mapping
// TODO
}
}
// ReSharper disable UnusedMember.Local
private class AllSupportedTypesTest
{
public string StringValue { get; set; }
public int IntValue { get; set; }
public bool BoolValue { get; set; }
public Guid GuidValue { get; set; }
public ClassProperty ObjectValue { get; set; }
}
// ReSharper disable once ClassNeverInstantiated.Local
private class ClassProperty
{
public string SubStringValue { get; set; }
public int SubIntValue { get; set; }
public AllSupportedTypesTest RecursiveValue { get; set; }
}
// ReSharper restore UnusedMember.Local
}
}

View File

@ -18,7 +18,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Tapeti", "Pettin
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Settings.LiteDB", "PettingZoo.Settings.LiteDB\PettingZoo.Settings.LiteDB.csproj", "{7157B09C-FDD9-4928-B14D-C25B784CA865}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Settings.LiteDB", "PettingZoo.Settings.LiteDB\PettingZoo.Settings.LiteDB.csproj", "{7157B09C-FDD9-4928-B14D-C25B784CA865}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.WPF", "PettingZoo.WPF\PettingZoo.WPF.csproj", "{E6617B69-2AC4-4056-B801-DD32E2374B71}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.WPF", "PettingZoo.WPF\PettingZoo.WPF.csproj", "{E6617B69-2AC4-4056-B801-DD32E2374B71}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -50,6 +52,10 @@ Global
{E6617B69-2AC4-4056-B801-DD32E2374B71}.Debug|Any CPU.Build.0 = Debug|Any CPU {E6617B69-2AC4-4056-B801-DD32E2374B71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6617B69-2AC4-4056-B801-DD32E2374B71}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6617B69-2AC4-4056-B801-DD32E2374B71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6617B69-2AC4-4056-B801-DD32E2374B71}.Release|Any CPU.Build.0 = Release|Any CPU {E6617B69-2AC4-4056-B801-DD32E2374B71}.Release|Any CPU.Build.0 = Release|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -56,7 +56,7 @@ namespace PettingZoo
private static ILogger CreateLogger() private static ILogger CreateLogger()
{ {
var logPath = Path.Combine(PettingZooPaths.AppDataRoot, @"logs", "PettingZoo.log"); var logPath = Path.Combine(PettingZooPaths.LogPath, @"PettingZoo.log");
return new LoggerConfiguration() return new LoggerConfiguration()
.MinimumLevel.Verbose() .MinimumLevel.Verbose()

View File

@ -1,13 +1,15 @@
Must-have Must-have
--------- ---------
- Check required fields before enabling Publish button
Should-have Should-have
----------- -----------
- Save / load publisher messages (either as templates or to disk) - Save / load publisher messages (either as templates or to disk)
- Export received messages to Tapeti JSON file / Tapeti.Cmd command-line - Tapeti: export received messages to Tapeti.Cmd JSON file / Tapeti.Cmd command-line
- Tapeti: fetch NuGet dependencies to improve the chances of succesfully loading the assembly, instead of the current "extraAssembliesPaths" workaround
Nice-to-have Nice-to-have
------------ ------------
- JSON syntax highlighting - Validation against message classes (for Tapeti messages)

View File

@ -127,8 +127,6 @@ namespace PettingZoo.UI.Tab.Publisher
} }
Payload = example.Generate(); Payload = example.Generate();
// TODO if validating example, keep reference for validation... and implement validation of course
}); });
}); });
} }