diff --git a/Directory.Build.targets b/Directory.Build.targets index 4f736399b..43aabd688 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -18,8 +18,8 @@ - - + + diff --git a/VERSION b/VERSION index a2f28f43b..a13e7b9c8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.4.0 +10.0.0 diff --git a/docs/articles/Core/Serialization/EntryFormat.md b/docs/articles/Core/Serialization/EntryFormat.md index 1dab4f08d..a16be60eb 100644 --- a/docs/articles/Core/Serialization/EntryFormat.md +++ b/docs/articles/Core/Serialization/EntryFormat.md @@ -54,33 +54,109 @@ The recursive `SubEntries` structure represents properties of a `EntryValueType. Example class: ````cs -public class Root +public class PackagingResource { - [DisplayName("Bob")] - public Foo Blah { get; set; } + [EntrySerialize] + [isRequiered] + public Product Product_1 { get; set; } + + [EntrySerialize] + [DisplayName("Material"), DefaultValue("material1")] + public string MaterialType { get; set; } } -public class Foo + +public class Product { - public int ValueA { get; set; } + [EntrySerialize] + [isRequiered, Length(7,8)] + public int ProductID { get; set; } - [DefaultValue("Hello")] - public string ValueB { get; set; } + [EntrySerialize] + [DisplayName("Name"), MaxLength(10)] + public string ProductName { get; set; } } ```` -The root object as a entry tree: - -````sh -Entry -- Key { Identifier = "Blah", Name = "Bob" } -- Value { Current = "Foo" } -- SubEntries --- Entry-A ---- Key { Identifier = "ValueA", Name = "ValueA" } ---- Value { Current = "10" } --- Entry-B ---- Key { Identifier = "ValueB", Name = "ValueB" } ---- Value { Current = "SomeString", Default = "Hello" } +The PackagingResource object as a serialized entry tree: +````JSON +{ + "identifier": "Product1", + "displayName": "Product1", + "description": null, + "value": { + "type": "Product", + "unitType": null, + "current": "", + "default": null, + "possible": null, + "isReadOnly": false + }, + "validation": { + "isRequired": true, + }, + "subEntries": [ + { + "identifier": "ProductID", + "displayName": "ProductID", + "description": null, + "value": { + "type": "int", + "unitType": null, + "current": "", + "default": null, + "possible": null, + "isReadOnly": false + }, + "validation": { + "isRequired": true, + "minimum": 7, + "maximum": 8 + }, + "subEntries": [], + "prototypes": [] + }, + { + "identifier": "ProductName", + "displayName": "Name", + "description": null, + "value": { + "type": "string", + "unitType": null, + "current": "", + "default": null, + "possible": null, + "isReadOnly": false + }, + "validation": { + "isRequired": true + "maximum": 10, + }, + "subEntries": [], + "prototypes": [] + + } + ], + "prototypes": [] + +}, +{ + "identifier": "MaterialType", + "displayName": "Marterial", + "description": null, + "value": { + "type": "String", + "unitType": null, + "current": null, + "default": "material1", + "possible": null, + "isReadOnly": false + }, + "validation": { + }, + "subEntries": [], + "prototypes": [] + +} ```` ## Prototypes diff --git a/src/Moryx.CommandCenter.Web/src/modules/models/EntryValidation.ts b/src/Moryx.CommandCenter.Web/src/modules/models/EntryValidation.ts index 084ad1d88..740164481 100644 --- a/src/Moryx.CommandCenter.Web/src/modules/models/EntryValidation.ts +++ b/src/Moryx.CommandCenter.Web/src/modules/models/EntryValidation.ts @@ -1,12 +1,13 @@ -/* - * Copyright (c) 2020, Phoenix Contact GmbH & Co. KG - * Licensed under the Apache License, Version 2.0 -*/ - -export default class EntryValidation { - public minimum: number; - public maximum: number; - public regex: string; - public isRequired: boolean; - public isPassword: boolean; -} +/* + * Copyright (c) 2020, Phoenix Contact GmbH & Co. KG + * Licensed under the Apache License, Version 2.0 +*/ + +export default class EntryValidation { + public minimum: number; + public maximum: number; + public regex: string; + public isRequired: boolean; + public isPassword: boolean; + public deniedValue: string[]; +} diff --git a/src/Moryx/Configuration/PossibleValuesSerialization.cs b/src/Moryx/Configuration/PossibleValuesSerialization.cs index fc9987f3a..68fdb567e 100644 --- a/src/Moryx/Configuration/PossibleValuesSerialization.cs +++ b/src/Moryx/Configuration/PossibleValuesSerialization.cs @@ -3,11 +3,14 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; +using Moryx.Communication.Endpoints; using Moryx.Container; using Moryx.Serialization; using Moryx.Tools; +using Newtonsoft.Json.Linq; namespace Moryx.Configuration { @@ -65,8 +68,8 @@ public override EntryPrototype[] Prototypes(Type memberType, ICustomAttributePro list.Add(new EntryPrototype(value, prototype)); } return list.ToArray(); - } - + } + /// public override string[] PossibleValues(Type memberType, ICustomAttributeProvider attributeProvider) { @@ -74,12 +77,12 @@ public override string[] PossibleValues(Type memberType, ICustomAttributeProvide // Possible values for primitive collections only apply to members if (valuesAttribute == null || IsPrimitiveCollection(memberType)) return base.PossibleValues(memberType, attributeProvider); - - // Use attribute + + // Use attribute var values = valuesAttribute.GetValues(Container, ServiceProvider); return values?.Distinct().ToArray(); - } - + } + /// /// Check if a property is a collection of primitives /// diff --git a/src/Moryx/Serialization/DefaultSerialization.cs b/src/Moryx/Serialization/DefaultSerialization.cs index 8f18a74b4..cff4b641c 100644 --- a/src/Moryx/Serialization/DefaultSerialization.cs +++ b/src/Moryx/Serialization/DefaultSerialization.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using System.Threading; using Moryx.Configuration; using Moryx.Tools; @@ -73,6 +74,15 @@ public virtual EntryPrototype[] Prototypes(Type memberType, ICustomAttributeProv /// public virtual string[] PossibleValues(Type memberType, ICustomAttributeProvider attributeProvider) { +#if NET8_0 + var validationAttribute = attributeProvider.GetCustomAttribute(); + if (validationAttribute != null) + { + var allowedValues = validationAttribute.Values.Select(o => o.ToString()); + return allowedValues?.Distinct().ToArray(); + } +#endif + // Element type for collections var isCollection = EntryConvert.IsCollection(memberType); if (isCollection) @@ -103,30 +113,47 @@ public virtual EntryValidation CreateValidation(Type memberType, ICustomAttribut // Iterate over attributes reading all validation rules foreach (var attribute in validationAttributes) { - if (attribute is MinLengthAttribute minAttribute) - { - validation.Minimum = minAttribute.Length; - } - else if (attribute is MaxLengthAttribute maxAttribute) - { - validation.Maximum = maxAttribute.Length; - } - else if (attribute is RangeAttribute rangeAttribute) - { - validation.Minimum = Convert.ToDouble(rangeAttribute.Minimum); - validation.Maximum = Convert.ToDouble(rangeAttribute.Maximum); - } - else if (attribute is RegularExpressionAttribute regexAttribute) - validation.Regex = regexAttribute.Pattern; - else if (attribute is StringLengthAttribute strLength) + switch (attribute) { - validation.Minimum = strLength.MinimumLength; - validation.Maximum = strLength.MaximumLength; + case MinLengthAttribute minAttribute: + validation.Minimum = minAttribute.Length; + break; + + case MaxLengthAttribute maxAttribute: + validation.Maximum = maxAttribute.Length; + break; + + case RangeAttribute rangeAttribute: + validation.Minimum = Convert.ToDouble(rangeAttribute.Minimum); + validation.Maximum = Convert.ToDouble(rangeAttribute.Maximum); + break; + + case RequiredAttribute requiredAttribute: + validation.IsRequired = true; + break; + + case RegularExpressionAttribute regexAttribute: + validation.Regex = regexAttribute.Pattern; + break; + + case StringLengthAttribute strLength: + validation.Minimum = strLength.MinimumLength; + validation.Maximum = strLength.MaximumLength; + break; + +#if NET8_0 + case DeniedValuesAttribute deniedAttribute: + object[] denied = deniedAttribute.Values; + validation.DeniedValues = Array.ConvertAll(denied, item => item.ToString()); + break; + + case LengthAttribute lengthAttribute: + validation.Minimum = lengthAttribute.MinimumLength; + validation.Maximum = lengthAttribute.MaximumLength; + break; +#endif } - else if (attribute is RequiredAttribute) - validation.IsRequired = true; } - return validation; } @@ -221,7 +248,7 @@ public virtual object ConvertValue(Type memberType, ICustomAttributeProvider att return CollectionBuilder(memberType, currentValue, mappedEntry); default: var value = mappedEntry.Value.Current; - if (value is null) + if (value is null) return null; try @@ -251,9 +278,6 @@ public EntryUnitType GetUnitTypeByAttributes(ICustomAttributeProvider property) { var unitType = EntryUnitType.None; - if (HasFlagsAttribute(property)) - unitType = EntryUnitType.Flags; - var passwordAttr = property.GetCustomAttribute(); if (passwordAttr != null) unitType = EntryUnitType.Password; @@ -321,17 +345,5 @@ protected static object CollectionBuilder(Type collectionType, object currentVal // Other collections are not supported return null; } - - /// - /// Checks if the given property is an enum and has the . - /// - /// The property to inspect for attributes. - /// True if the property has the Flags attribute; otherwise, false. - private bool HasFlagsAttribute(ICustomAttributeProvider property) - { - return property is PropertyInfo propertyInfo && - propertyInfo.PropertyType.IsEnum && - propertyInfo.PropertyType.GetCustomAttributes(typeof(System.FlagsAttribute), false).Any(); - } } } diff --git a/src/Moryx/Serialization/EntryConvert/EntryValidation.cs b/src/Moryx/Serialization/EntryConvert/EntryValidation.cs index 488aa7870..cc64a5d86 100644 --- a/src/Moryx/Serialization/EntryConvert/EntryValidation.cs +++ b/src/Moryx/Serialization/EntryConvert/EntryValidation.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0 using System; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Moryx.Serialization @@ -37,10 +38,17 @@ public class EntryValidation : ICloneable public bool IsRequired { get; set; } /// - /// Creates a new instance initializing - /// and validation to the largest possible range. + /// List of denied values /// - public EntryValidation() + [DataMember] + public string[] DeniedValues { get; set; } + + +/// +/// Creates a new instance initializing +/// and validation to the largest possible range. +/// +public EntryValidation() { Minimum = double.MinValue; Maximum = double.MaxValue; @@ -63,7 +71,8 @@ public EntryValidation Clone(bool deep) Minimum = Minimum, Maximum = Maximum, Regex = Regex, - IsRequired = IsRequired + IsRequired = IsRequired, + DeniedValues = DeniedValues, }; return copy; }