Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
Toxantron committed Oct 25, 2024
2 parents a373fcb + 3ec5ca0 commit 4357e88
Show file tree
Hide file tree
Showing 29 changed files with 542 additions and 88 deletions.
20 changes: 17 additions & 3 deletions MORYX-Framework.sln
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.Resources.Management.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.Runtime.Endpoints.Tests", "src\Tests\Moryx.Runtime.Endpoints.Tests\Moryx.Runtime.Endpoints.Tests.csproj", "{7792C4E0-6D07-42C9-AC29-BAB76836FC11}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.Runtime.Endpoints.IntegrationTests", "src\Tests\Moryx.Runtime.Endpoints.IntegrationTests\Moryx.Runtime.Endpoints.IntegrationTests.csproj", "{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.Runtime.Endpoints.IntegrationTests", "src\Tests\Moryx.Runtime.Endpoints.IntegrationTests\Moryx.Runtime.Endpoints.IntegrationTests.csproj", "{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.TestTools.NUnit", "src\Moryx.TestTools.NUnit\Moryx.TestTools.NUnit.csproj", "{6FF878E0-AF61-4C3A-9B9C-71C35A949E51}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.TestTools.IntegrationTest", "src\Moryx.TestTools.IntegrationTest\Moryx.TestTools.IntegrationTest.csproj", "{C949164C-0345-4893-9E4C-A79BC1F93F85}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -296,6 +300,14 @@ Global
{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}.Release|Any CPU.Build.0 = Release|Any CPU
{6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Release|Any CPU.Build.0 = Release|Any CPU
{C949164C-0345-4893-9E4C-A79BC1F93F85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C949164C-0345-4893-9E4C-A79BC1F93F85}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C949164C-0345-4893-9E4C-A79BC1F93F85}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C949164C-0345-4893-9E4C-A79BC1F93F85}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -340,10 +352,12 @@ Global
{FEB3BA44-2CD9-445A-ABF2-C92378C443F7} = {0A466330-6ED6-4861-9C94-31B1949CDDB9}
{7792C4E0-6D07-42C9-AC29-BAB76836FC11} = {0A466330-6ED6-4861-9C94-31B1949CDDB9}
{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8} = {8517D209-5BC1-47BD-A7C7-9CF9ADD9F5B6}
{6FF878E0-AF61-4C3A-9B9C-71C35A949E51} = {953AAE25-26C8-4A28-AB08-61BAFE41B22F}
{C949164C-0345-4893-9E4C-A79BC1F93F85} = {953AAE25-26C8-4A28-AB08-61BAFE41B22F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {36EFC961-F4E7-49DC-A36A-99594FFB8243}
RESX_TaskErrorCategory = Message
RESX_ShowErrorsInErrorList = True
RESX_TaskErrorCategory = Message
SolutionGuid = {36EFC961-F4E7-49DC-A36A-99594FFB8243}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.0.6
8.2.0
6 changes: 3 additions & 3 deletions docs/articles/Core/Serialization/PossibleValues.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ public abstract class PossibleValuesAttribute : Attribute
public abstract bool UpdateFromPredecessor { get; }

/// <summary>
/// All possible values for this member represented as strings. The given container might be null
/// All possible values for this member represented as strings. The given containers might be null
/// and can be used to resolve possible values
/// </summary>
public abstract IEnumerable<string> GetValues(IContainer container);
public virtual IEnumerable<string> GetValues(IContainer container, IServiceProvider serviceProvider);

/// <summary>
/// String to value conversion. Must be override if <see cref="OverridesConversion"/> is set to true"/>
/// </summary>
public virtual object Parse(IContainer container, string value)
public virtual object Parse(IContainer container, IServiceProvider serviceProvider), string value)
{
return value;
}
Expand Down
45 changes: 45 additions & 0 deletions docs/tutorials/HowToTestAModule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Setup a test environment for integration tests of a module

In order to test a module in its lifecycle with its respective facade we offer the `Moryx.TestTools.IntegrationTest`.
The package brings a `MoryxTestEnvironment<T>`.
With this class you can first create mocks for all module facades your module dependents on using the static `CreateModuleMock<FacadeType>` method.
Afterwards you can create the environment using an implementation of the `ServerModuleBase<T>` class, an instance of the `ConfigBase` and the set of dependency mocks.
The first two parameters are usually your `ModuleController` and your `ModuleConfig`.
The following example shows a setup for the `IShiftManagement` facade interface. The module depends on the `IResourceManagement` and `IOperatorManagement` facades.

```csharp
private ModuleConfig _config;
private Mock<IResourceManagement> _resourceManagementMock;
private Mock<IOperatorManagement> _operatorManagementMock;
private MoryxTestEnvironment _env;

[SetUp]
public void SetUp()
{
ReflectionTool.TestMode = true;
_config = new();
_resourceManagementMock = MoryxTestEnvironment.CreateModuleMock<IResourceManagement>();
_operatorManagementMock = MoryxTestEnvironment.CreateModuleMock<IOperatorManagement>();
_env = new MoryxTestEnvironment(typeof(ModuleController),
new Mock[] { _resourceManagementMock, _operatorManagementMock }, _config);
}
```

Using the created environment you can start and stop the module as you please.
You can also retrieve the facade of the module to test all the functionalities the running module should provide.

```csharp
[Test]
public void Start_WhenModuleIsStopped_StartsModule()
{
// Arrange
var facade = _env.GetTestModule();

// Act
var module = _env.StartTestModule();
var module = _env.StopTestModule();

// Assert
Assert.That(module.State, Is.EqualTo(ServerModuleState.Stopped));
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\Moryx.AbstractionLayer\Moryx.AbstractionLayer.csproj" />
<ProjectReference Include="..\Moryx.Asp.Extensions\Moryx.Asp.Extensions.csproj" />
<ProjectReference Include="..\Moryx.Runtime\Moryx.Runtime.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Reflection;
using Moryx.Configuration;
using Moryx.Container;
using Moryx.Serialization;

namespace Moryx.AbstractionLayer.Products.Endpoints
Expand All @@ -31,7 +32,18 @@ static PartialSerialization()
/// <summary>
/// Creates a new <see cref="PartialSerialization{T}"/> instance
/// </summary>
public PartialSerialization() : base(null, new EmptyValueProvider())
[Obsolete("Use serialization with containers instead")]
public PartialSerialization() : this(null, null)
{
}

/// <summary>
/// Create serialization with access to global and local container
/// </summary>
/// <param name="localContainer"></param>
/// <param name="serviceProvider"></param>
public PartialSerialization(IContainer localContainer, IServiceProvider serviceProvider)
: base(localContainer, serviceProvider, new EmptyValueProvider())
{
}

Expand Down
37 changes: 25 additions & 12 deletions src/Moryx.AbstractionLayer.Products.Endpoints/ProductConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0

using Moryx.AbstractionLayer.Recipes;
using Moryx.Container;
using Moryx.Serialization;
using Moryx.Tools;
using Moryx.Workplans;
Expand All @@ -20,12 +21,19 @@ public class ProductConverter
// Null object pattern for identity
private static readonly ProductIdentity EmptyIdentity = new ProductIdentity(string.Empty, 0);

private static readonly ICustomSerialization ProductSerialization = new PartialSerialization<ProductType>();
private static readonly ICustomSerialization RecipeSerialization = new PartialSerialization<ProductionRecipe>();
private readonly ICustomSerialization _productSerialization;
private readonly ICustomSerialization _recipeSerialization;

public ProductConverter(IProductManagement productManagement)
public IContainer ProductManagerContainer { get; }
public IServiceProvider GlobalContainer { get; }

public ProductConverter(IProductManagement productManagement, IContainer localContainer, IServiceProvider globalContainer)
{
_productManagement = productManagement;
ProductManagerContainer = localContainer;
GlobalContainer = globalContainer;
_productSerialization = new PartialSerialization<ProductType>(localContainer, globalContainer);
_recipeSerialization = new PartialSerialization<ProductionRecipe>(localContainer, globalContainer);
}

public ProductDefinitionModel ConvertProductType(Type productType)
Expand All @@ -39,7 +47,7 @@ public ProductDefinitionModel ConvertProductType(Type productType)
Name = productType.FullName,
DisplayName = productType.GetDisplayName() ?? productType.Name,
BaseDefinition = baseTypeName,
Properties = EntryConvert.EncodeClass(productType, ProductSerialization)
Properties = EntryConvert.EncodeClass(productType, _productSerialization)
};
}

Expand Down Expand Up @@ -73,7 +81,7 @@ public ProductModel ConvertProduct(IProductType productType, bool flat)
// Properties
var typeWrapper = _productManagement.GetTypeWrapper(productType.GetType().FullName);
var properties = typeWrapper != null ? typeWrapper.Properties.ToArray() : productType.GetType().GetProperties();
converted.Properties = EntryConvert.EncodeObject(productType, ProductSerialization);
converted.Properties = EntryConvert.EncodeObject(productType, _productSerialization);

// Files
converted.Files = ConvertFiles(productType, properties);
Expand Down Expand Up @@ -124,7 +132,7 @@ private void ConvertParts(IProductType productType, IEnumerable<PropertyInfo> pr
DisplayName = displayName,
Type = FetchProductType(property.PropertyType),
Parts = partModel is null ? new PartModel[0] : new[] { partModel },
PropertyTemplates = EntryConvert.EncodeClass(property.PropertyType, ProductSerialization)
PropertyTemplates = EntryConvert.EncodeClass(property.PropertyType, _productSerialization)
};
connectors.Add(connector);
}
Expand All @@ -139,7 +147,7 @@ private void ConvertParts(IProductType productType, IEnumerable<PropertyInfo> pr
DisplayName = displayName,
Type = FetchProductType(linkType),
Parts = links?.Select(ConvertPart).ToArray(),
PropertyTemplates = EntryConvert.EncodeClass(linkType, ProductSerialization)
PropertyTemplates = EntryConvert.EncodeClass(linkType, _productSerialization)
};
connectors.Add(connector);
}
Expand All @@ -165,7 +173,7 @@ private PartModel ConvertPart(IProductPartLink link)
{
Id = link.Id,
Product = ConvertProduct(link.Product, true),
Properties = EntryConvert.EncodeObject(link, ProductSerialization)
Properties = EntryConvert.EncodeObject(link, _productSerialization)
};
return part;
}
Expand Down Expand Up @@ -214,7 +222,7 @@ public IProductType ConvertProductBack(ProductModel source, ProductType converte
// Copy extended properties
var typeWrapper = _productManagement.GetTypeWrapper(converted.GetType().FullName);
var properties = typeWrapper != null ? typeWrapper.Properties.ToArray() : converted.GetType().GetProperties();
EntryConvert.UpdateInstance(converted, source.Properties, ProductSerialization);
EntryConvert.UpdateInstance(converted, source.Properties, _productSerialization);

// Copy Files
ConvertFilesBack(converted, source, properties);
Expand Down Expand Up @@ -318,7 +326,12 @@ private static void ConvertFilesBack(object converted, ProductModel product, Pro
}
}

public static RecipeModel ConvertRecipe(IRecipe recipe)
[Obsolete("Use ConvertRecipe on instance")]
public static RecipeModel ConvertRecipe(IRecipe recipe) => ConvertRecipe(recipe, new PartialSerialization<ProductionRecipe>(null, null));

public RecipeModel ConvertRecipeV2(IRecipe recipe) => ConvertRecipe(recipe, _recipeSerialization);

private static RecipeModel ConvertRecipe(IRecipe recipe, ICustomSerialization serialization)
{
// Transform to DTO and transmit
var converted = new RecipeModel
Expand All @@ -328,7 +341,7 @@ public static RecipeModel ConvertRecipe(IRecipe recipe)
Type = recipe.GetType().Name,
State = recipe.State,
Revision = recipe.Revision,
Properties = EntryConvert.EncodeObject(recipe, RecipeSerialization),
Properties = EntryConvert.EncodeObject(recipe, serialization),
IsClone = recipe.Classification.HasFlag(RecipeClassification.Clone)
};

Expand Down Expand Up @@ -377,7 +390,7 @@ public IProductRecipe ConvertRecipeBack(RecipeModel recipe, IProductRecipe produ
productRecipe.Product = productType;
}

EntryConvert.UpdateInstance(productRecipe, recipe.Properties, RecipeSerialization);
EntryConvert.UpdateInstance(productRecipe, recipe.Properties, _recipeSerialization);

// Do not update a clones classification
if (productRecipe.Classification.HasFlag(RecipeClassification.Clone))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
using Moryx.Configuration;
using Moryx.Asp.Extensions;
using Moryx.AbstractionLayer.Properties;
using Microsoft.Extensions.DependencyInjection;
using Moryx.AbstractionLayer.Resources;
using Moryx.Runtime.Modules;
using System.ComponentModel;

namespace Moryx.AbstractionLayer.Products.Endpoints
{
Expand All @@ -27,10 +31,14 @@ public class ProductManagementController : ControllerBase
{
private readonly IProductManagement _productManagement;
private readonly ProductConverter _productConverter;
public ProductManagementController(IProductManagement productManagement)
public ProductManagementController(IProductManagement productManagement,
IModuleManager moduleManager,
IServiceProvider serviceProvider)
{
_productManagement = productManagement;
_productConverter = new ProductConverter(_productManagement);

var module = moduleManager.AllModules.FirstOrDefault(module => module is IFacadeContainer<IProductManagement>);
_productConverter = new ProductConverter(_productManagement, module.Container, serviceProvider);
}

#region importers
Expand All @@ -39,14 +47,16 @@ public ProductManagementController(IProductManagement productManagement)
[Authorize(Policy = ProductPermissions.CanViewTypes)]
public ActionResult<ProductCustomization> GetProductCustomization()
{
var parameterSerialization = new PossibleValuesSerialization(_productConverter.ProductManagerContainer, _productConverter.GlobalContainer,
new ValueProviderExecutor(new ValueProviderExecutorSettings().AddDefaultValueProvider()));
return new ProductCustomization
{
ProductTypes = GetProductTypes(),
RecipeTypes = GetRecipeTypes(),
Importers = _productManagement.Importers.Select(i => new ProductImporter
{
Name = i.Key,
Parameters = EntryConvert.EncodeObject(i.Value, new PossibleValuesSerialization(null, new ValueProviderExecutor(new ValueProviderExecutorSettings().AddDefaultValueProvider())))
Parameters = EntryConvert.EncodeObject(i.Value, parameterSerialization)
}).ToArray()
};
}
Expand Down Expand Up @@ -247,7 +257,7 @@ public ActionResult<RecipeModel[]> GetRecipes(long id, int classification)
var recipes = _productManagement.GetRecipes(productType, (RecipeClassification)classification);
var recipeModels = new List<RecipeModel>();
foreach (var recipe in recipes)
recipeModels.Add(ProductConverter.ConvertRecipe(recipe));
recipeModels.Add(_productConverter.ConvertRecipeV2(recipe));
return recipeModels.ToArray();
}
#endregion
Expand Down Expand Up @@ -330,7 +340,7 @@ public ActionResult<RecipeModel> GetRecipe(long id)
var recipe = _productManagement.LoadRecipe(id);
if (recipe == null)
return NotFound(new MoryxExceptionResponse {Title= string.Format(Strings.RecipeNotFoundException_Message, id) });
return ProductConverter.ConvertRecipe(recipe);
return _productConverter.ConvertRecipeV2(recipe);
}

[HttpPost]
Expand Down Expand Up @@ -380,7 +390,7 @@ public ActionResult<RecipeModel> CreateRecipe(string recipeType)
var recipe = _productManagement.CreateRecipe(recipeType);
if (recipe == null)
recipe = (IProductRecipe)TypeTool.CreateInstance<IProductRecipe>(recipeType);
return ProductConverter.ConvertRecipe(recipe);
return _productConverter.ConvertRecipeV2(recipe);
}
#endregion
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2023, Phoenix Contact GmbH & Co. KG
// Licensed under the Apache License, Version 2.0

using System.Reflection;
using System.Runtime.Serialization;
using Moryx.Configuration;

namespace Moryx.AbstractionLayer.Resources.Endpoints
{
internal class DataMemberAttributeValueProviderFilter : IValueProviderFilter
{
private readonly bool _filterDataMembers;

public DataMemberAttributeValueProviderFilter(bool filterDataMembers)
{
_filterDataMembers = filterDataMembers;
}

public bool CheckProperty(PropertyInfo propertyInfo)
{
if (_filterDataMembers)
return propertyInfo.GetCustomAttribute<DataMemberAttribute>() == null;
return propertyInfo.GetCustomAttribute<DataMemberAttribute>() != null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<ItemGroup>
<ProjectReference Include="..\Moryx.AbstractionLayer\Moryx.AbstractionLayer.csproj" />
<ProjectReference Include="..\Moryx.Asp.Extensions\Moryx.Asp.Extensions.csproj" />
<ProjectReference Include="..\Moryx.Resources.Management\Moryx.Resources.Management.csproj" />
<ProjectReference Include="..\Moryx.Runtime\Moryx.Runtime.csproj" />
</ItemGroup>

Expand Down
Loading

0 comments on commit 4357e88

Please sign in to comment.