diff --git a/src/AXOpen-L3-tests.sln b/src/AXOpen-L3-tests.sln index 7967365a8..d292d8a1a 100644 --- a/src/AXOpen-L3-tests.sln +++ b/src/AXOpen-L3-tests.sln @@ -55,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Security", "Security EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "inxton_axopen_simatic1500", "simatic1500\ctrl\ix\inxton_axopen_simatic1500.csproj", "{153256A2-1F20-4652-9577-EA3F21C58604}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.Operon.Blazor", "styling\src\AXOpen.Operon.Blazor.csproj", "{AB74CFA4-4E4A-4142-A491-8059359297B8}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "inxton_axopen_timers", "timers\src\AXOpen.Timers\inxton_axopen_timers.csproj", "{64E1326E-63C4-4623-B3E9-AE6C33D3D4A4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AXOpen.ToolBox", "toolbox\src\AXOpen.ToolBox\AXOpen.ToolBox.csproj", "{4FFE9FDF-E6FB-4B5F-A4C8-9623F6358A7B}" @@ -173,6 +175,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ctrl", "simatic1500\ctrl", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "simatic1500", "simatic1500", "{09E8FEB5-793C-47E0-90A2-3B30199FB321}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "styling\src", "{9B9A3CC5-663F-4767-B17B-5EB7F2C001DE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "styling", "styling", "{91077ADC-5D83-4331-977B-BAC82A86A64D}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AXOpen.Timers", "timers\src\AXOpen.Timers", "{93475E41-C4BC-48D7-96F5-E488C3ADFCFD}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "timers\src", "{D33BF467-F818-440E-AC41-00B02A851D8B}" @@ -309,6 +315,10 @@ Global {153256A2-1F20-4652-9577-EA3F21C58604}.Debug|Any CPU.Build.0 = Debug|Any CPU {153256A2-1F20-4652-9577-EA3F21C58604}.Release|Any CPU.ActiveCfg = Release|Any CPU {153256A2-1F20-4652-9577-EA3F21C58604}.Release|Any CPU.Build.0 = Release|Any CPU + {AB74CFA4-4E4A-4142-A491-8059359297B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB74CFA4-4E4A-4142-A491-8059359297B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB74CFA4-4E4A-4142-A491-8059359297B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB74CFA4-4E4A-4142-A491-8059359297B8}.Release|Any CPU.Build.0 = Release|Any CPU {64E1326E-63C4-4623-B3E9-AE6C33D3D4A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {64E1326E-63C4-4623-B3E9-AE6C33D3D4A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {64E1326E-63C4-4623-B3E9-AE6C33D3D4A4}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -397,6 +407,8 @@ Global {153256A2-1F20-4652-9577-EA3F21C58604} = {5D7F1861-9390-47C6-ABB5-D0371D58292E} {5D7F1861-9390-47C6-ABB5-D0371D58292E} = {EBE4D729-DBF6-440F-9C1E-93A7D09668CC} {EBE4D729-DBF6-440F-9C1E-93A7D09668CC} = {09E8FEB5-793C-47E0-90A2-3B30199FB321} + {AB74CFA4-4E4A-4142-A491-8059359297B8} = {9B9A3CC5-663F-4767-B17B-5EB7F2C001DE} + {9B9A3CC5-663F-4767-B17B-5EB7F2C001DE} = {91077ADC-5D83-4331-977B-BAC82A86A64D} {64E1326E-63C4-4623-B3E9-AE6C33D3D4A4} = {93475E41-C4BC-48D7-96F5-E488C3ADFCFD} {93475E41-C4BC-48D7-96F5-E488C3ADFCFD} = {D33BF467-F818-440E-AC41-00B02A851D8B} {D33BF467-F818-440E-AC41-00B02A851D8B} = {89504C16-C7D9-4274-A430-850E3DC30C37} diff --git a/src/base/src/AXOpen.Base.Abstractions/Data/IBrowsableDataObject.cs b/src/base/src/AXOpen.Base.Abstractions/Data/IBrowsableDataObject.cs index b74ab9f8e..9fd8dfbf4 100644 --- a/src/base/src/AXOpen.Base.Abstractions/Data/IBrowsableDataObject.cs +++ b/src/base/src/AXOpen.Base.Abstractions/Data/IBrowsableDataObject.cs @@ -6,6 +6,9 @@ public interface IBrowsableDataObject { dynamic RecordId { get; set; } - string _EntityId { get; set; } + string _EntityId { get; set; } + + DateTime? ModifiedAt { get; set; } + DateTime? CreatedAt { get; set; } } } \ No newline at end of file diff --git a/src/base/src/AXOpen.Base.Abstractions/Data/Query/PredicateContainer.cs b/src/base/src/AXOpen.Base.Abstractions/Data/Query/PredicateContainer.cs index 506d92d4a..e9e8cf0dc 100644 --- a/src/base/src/AXOpen.Base.Abstractions/Data/Query/PredicateContainer.cs +++ b/src/base/src/AXOpen.Base.Abstractions/Data/Query/PredicateContainer.cs @@ -125,6 +125,16 @@ public void AddPredicates(Expression> predicate) this._predicates[typeof(T)].Add(predicate); } + public void AddPredicates(Type type, Expression> expression) + { + if (!this._predicates.ContainsKey(type)) + { + this._predicates[type] = new List(); + } + + this._predicates[type].Add(expression); + } + public void AddPredicates(Type type, LambdaExpression predicate) { if (!this._predicates.ContainsKey(type)) diff --git a/src/base/src/AXOpen.Base.Abstractions/Data/RepositoryBase.cs b/src/base/src/AXOpen.Base.Abstractions/Data/RepositoryBase.cs index 9db455f5a..b89a64b29 100644 --- a/src/base/src/AXOpen.Base.Abstractions/Data/RepositoryBase.cs +++ b/src/base/src/AXOpen.Base.Abstractions/Data/RepositoryBase.cs @@ -351,6 +351,8 @@ public void Create(string identifier, T data) } try { + data?.CreatedAt = DateTime.UtcNow; + data?.ModifiedAt = DateTime.UtcNow; CreateNvi(identifier, data); } catch (Exception e) @@ -409,12 +411,13 @@ public void Update(string identifier, T data) } try { + data?.ModifiedAt = DateTime.UtcNow; UpdateNvi(identifier, data); } catch (Exception e) { OnUpdateFailed?.Invoke(identifier, data, e); - throw e; + throw; } OnUpdateDone?.Invoke(identifier, data); } diff --git a/src/base/src/AXOpen.Logging/SerilogLogger.cs b/src/base/src/AXOpen.Logging/SerilogLogger.cs index adb250d80..b20c4ad34 100644 --- a/src/base/src/AXOpen.Logging/SerilogLogger.cs +++ b/src/base/src/AXOpen.Logging/SerilogLogger.cs @@ -30,83 +30,83 @@ public SerilogLogger(Serilog.ILogger logger) public void Debug(string message, IIdentity identity) { - Log.Debug("{message} {identity}",message, new { UserName = identity.Name }); + Log.Debug("{message} {identity}",message, new { UserName = identity?.Name }); } public void Debug(string message, ITwinElement sender, IIdentity identity, object details) { Log.Debug($"{message} {{sender}} {{identity}} {{details}}", new { Symbol = sender?.Symbol, Label = sender?.HumanReadable }, - new { UserName = identity.Name, Type = identity.AuthenticationType }, + new { UserName = identity.Name, Type = identity?.AuthenticationType }, details ); } public void Verbose(string message, IIdentity identity) { - Log.Verbose("{message} {identity}", message, new { UserName = identity.Name }); + Log.Verbose("{message} {identity}", message, new { UserName = identity?.Name }); } public void Verbose(string message, ITwinElement sender, IIdentity identity, object details) { Log.Verbose($"{message} {{sender}} {{identity}} {{details}}", new { Symbol = sender?.Symbol, Label = sender?.HumanReadable }, - new { UserName = identity.Name, Type = identity.AuthenticationType }, + new { UserName = identity.Name, Type = identity?.AuthenticationType }, details ); } public void Information(string message, IIdentity identity) { - Log.Information("{message} {identity}", message,new { UserName = identity.Name }); + Log.Information("{message} {identity}", message,new { UserName = identity?.Name }); } public void Information(string message, ITwinElement sender, IIdentity identity, object details) { Log.Information($"{message} {{sender}} {{identity}} {{details}}", new { Symbol = sender?.Symbol, Label = sender?.HumanReadable }, - new { UserName = identity.Name, Type = identity.AuthenticationType }, + new { UserName = identity.Name, Type = identity?.AuthenticationType }, details); } public void Warning(string message, IIdentity identity) { - Log.Warning("{message} {identity}", message, new { UserName = identity.Name }); + Log.Warning("{message} {identity}", message, new { UserName = identity?.Name }); } public void Warning(string message, ITwinElement sender, IIdentity identity, object details) { Log.Warning($"{message} {{sender}} {{identity}} {{details}}", new { Symbol = sender?.Symbol, Label = sender?.HumanReadable }, - new { UserName = identity.Name, Type = identity.AuthenticationType }, + new { UserName = identity.Name, Type = identity?.AuthenticationType }, details ); } public void Error(string message, IIdentity identity) { - Log.Error("{message} {identity}", message, new { UserName = identity.Name }); + Log.Error("{message} {identity}", message, new { UserName = identity?.Name }); } public void Error(string message, ITwinElement sender, IIdentity identity, object details) { Log.Error($"{message} {{sender}} {{identity}} {{details}}", new { Symbol = sender?.Symbol, Label = sender?.HumanReadable }, - new { UserName = identity.Name, Type = identity.AuthenticationType }, + new { UserName = identity.Name, Type = identity?.AuthenticationType }, details ); } public void Fatal(string message, IIdentity identity) { - Log.Fatal("{message} {identity}", message, new { UserName = identity.Name }); + Log.Fatal("{message} {identity}", message, new { UserName = identity?.Name }); } public void Fatal(string message, ITwinElement sender, IIdentity identity, object details) { Log.Fatal($"{message} {{sender}} {{identity}} {{details}}", new { Symbol = sender?.Symbol, Label = sender?.HumanReadable }, - new { UserName = identity.Name, Type = identity.AuthenticationType }, + new { UserName = identity.Name, Type = identity?.AuthenticationType }, details ); } diff --git a/src/core/ctrl/src/AxoObject/AxoObject.st b/src/core/ctrl/src/AxoObject/AxoObject.st index aac296d8d..9948b5087 100644 --- a/src/core/ctrl/src/AxoObject/AxoObject.st +++ b/src/core/ctrl/src/AxoObject/AxoObject.st @@ -95,6 +95,9 @@ NAMESPACE AXOpen.Core NULL_CONTEXT_OBJ : IAxoObject; END_VAR + // We always reset message count upon initialization. + THIS.MsgCnt := LINT#0; + IF _isInitialized THEN RETURN; END_IF; @@ -117,7 +120,7 @@ NAMESPACE AXOpen.Core _errorState := UINT#40; RETURN; END_IF; - + _context := inParent.GetContext(); _parent := inParent; _isInitialized := TRUE; @@ -141,6 +144,9 @@ NAMESPACE AXOpen.Core inContext : IAxoContext; END_VAR + + // We always reset message count upon initialization. + THIS.MsgCnt := LINT#0; IF _isInitialized THEN RETURN; END_IF; @@ -151,6 +157,8 @@ NAMESPACE AXOpen.Core RETURN; END_IF; + + _context := inContext; _isInitialized := TRUE; diff --git a/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectSpotView.razor b/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectSpotView.razor index 63f365959..755d1b634 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectSpotView.razor +++ b/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectSpotView.razor @@ -92,7 +92,7 @@ /// protected override async Task OnInitializedAsync() { - if (observer != null) await observer.InitializeLightUpdate(this.StartPolling); + if (observer != null) await observer.InitializeUpdate(this.StartPolling); await base.OnInitializedAsync(); } } diff --git a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs index a050ae1d5..e6f77c2d2 100644 --- a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs +++ b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs @@ -23,59 +23,102 @@ private AxoMessageProvider(IEnumerable observedObjects) public IEnumerable ObservedObjects { get; } + private int? _cachedActiveMessagesCount; + private DateTime _lastActiveMessagesCountUpdate = DateTime.MinValue; + private readonly TimeSpan _activeMessagesCountCacheDuration = TimeSpan.FromMilliseconds(500); + private readonly object _activeMessagesCountLock = new object(); + /// /// Gets the number of active messages. /// /// /// This property counts the number of messages that are currently active. /// An active message is defined as a message belonging to a Messenger that has a state other than Idle or NotActiveWatingAckn. + /// The value is cached for a short period to prevent flickering due to asynchronous PLC communication. /// public int? ActiveMessagesCount { get { - try - { - return ObservedObjects - .OfType() - .Select(p => Convert.ToInt32(p.MsgCnt.LastValue)) // Convert to appropriate numeric type - .Sum(); - } - catch (Exception e) + lock (_activeMessagesCountLock) { - Console.WriteLine(e); + var now = DateTime.UtcNow; + + // Return cached value if still valid + if (_cachedActiveMessagesCount.HasValue && + (now - _lastActiveMessagesCountUpdate) < _activeMessagesCountCacheDuration) + { + return _cachedActiveMessagesCount; + } + + try + { + var count = ObservedObjects + .OfType() + .Select(p => Convert.ToInt32(p.MsgCnt.LastValue)) + .Sum(); + + _cachedActiveMessagesCount = count; + _lastActiveMessagesCountUpdate = now; + + return count; + } + catch (Exception e) + { + return -1; + } } - - return 0; } } + private int? _cachedRelevantMessagesCount; + private DateTime _lastRelevantMessagesCountUpdate = DateTime.MinValue; + private readonly TimeSpan _relevantMessagesCountCacheDuration = TimeSpan.FromMilliseconds(500); + private readonly object _relevantMessagesCountLock = new object(); + /// /// Gets the count of relevant messages based on the state of Messengers. /// /// /// The RelevantMessagesCount property will return the number of messengers that have a state greater than eAxoMessengerState.Idle. /// Messengers is a collection of objects that represents messengers. + /// The value is cached for a short period to prevent flickering due to asynchronous PLC communication. /// /// An integer that represents the count of relevant messages. public int? RelevantMessagesCount { get { - try - { - return ObservedObjects - .OfType() - .Select(p => Convert.ToInt32(p.MsgCnt.LastValue)) // Convert to appropriate numeric type - .Sum(); - } - catch (Exception e) + lock (_relevantMessagesCountLock) { - Console.WriteLine(e); - } + var now = DateTime.UtcNow; + + // Return cached value if still valid + if (_cachedRelevantMessagesCount.HasValue && + (now - _lastRelevantMessagesCountUpdate) < _relevantMessagesCountCacheDuration) + { + return _cachedRelevantMessagesCount; + } - return 0; + try + { + var count = ObservedObjects + .OfType() + .Select(p => Convert.ToInt32(p.MsgCnt.LastValue)) + .Sum(); + + _cachedRelevantMessagesCount = count; + _lastRelevantMessagesCountUpdate = now; + + return count; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } } } @@ -190,5 +233,24 @@ public async Task ReadMessageStateAsync() await con.ReadBatchAsync(r)!; } } + + /// + /// Invalidates the cached message counts, forcing them to be recalculated on next access. + /// Use this method if you need to ensure fresh values after a batch read operation. + /// + public void InvalidateMessageCountCache() + { + lock (_activeMessagesCountLock) + { + _cachedActiveMessagesCount = null; + _lastActiveMessagesCountUpdate = DateTime.MinValue; + } + + lock (_relevantMessagesCountLock) + { + _cachedRelevantMessagesCount = null; + _lastRelevantMessagesCountUpdate = DateTime.MinValue; + } + } } } diff --git a/src/data/src/AXOpen.Data/DataFragmentExchange/AxoDataFragmentExchange.cs b/src/data/src/AXOpen.Data/DataFragmentExchange/AxoDataFragmentExchange.cs index 083c609a3..d646f03ef 100644 --- a/src/data/src/AXOpen.Data/DataFragmentExchange/AxoDataFragmentExchange.cs +++ b/src/data/src/AXOpen.Data/DataFragmentExchange/AxoDataFragmentExchange.cs @@ -500,7 +500,7 @@ public IEnumerable GetRecords(string identifier, int limit { return ((dynamic)Repository)?.GetRecords(identifier, limit, skip, searchMode, sortExpression, sortAscending); } - + public IEnumerable GetRecords(PredicateContainer predicates, int limit, int skip) { diff --git a/src/data/src/AXOpen.Data/DataPersistentExchange/PersistentRecord.cs b/src/data/src/AXOpen.Data/DataPersistentExchange/PersistentRecord.cs index b663cd7ee..1897a432c 100644 --- a/src/data/src/AXOpen.Data/DataPersistentExchange/PersistentRecord.cs +++ b/src/data/src/AXOpen.Data/DataPersistentExchange/PersistentRecord.cs @@ -1,4 +1,5 @@ using AXOpen.Base.Data; +using Microsoft.AspNetCore.Http.HttpResults; namespace AXOpen.Data { @@ -11,6 +12,12 @@ public class PersistentRecord : IBrowsableDataObject public DateTime _Created { set; get; } public DateTime _Modified { set; get; } + // Added due to IBrowsableDataObject interface and compatibility with Prometheus. + public DateTime? ModifiedAt { get { return _Modified; } set { _Modified = value.Value; } } + + // Added due to IBrowsableDataObject interface and compatibility with Prometheus. + public DateTime? CreatedAt { get { return _Created; } set { _Created = value.Value; } } + public List Tags = new(); } } \ No newline at end of file diff --git a/src/data/src/AXOpen.Data/Entity/AxoDataEntity.cs b/src/data/src/AXOpen.Data/Entity/AxoDataEntity.cs index 7c5c206e6..7c8460e7b 100644 --- a/src/data/src/AXOpen.Data/Entity/AxoDataEntity.cs +++ b/src/data/src/AXOpen.Data/Entity/AxoDataEntity.cs @@ -14,6 +14,7 @@ partial void PostConstruct(ITwinObject parent, string readableTail, string symbo ChangeTracker = new ValueChangeTracker(this); } + public List Changes { get; set; } public string Hash { get; set; } diff --git a/src/data/src/AXOpen.Data/Entity/Pocos/AxoDataEntity.cs b/src/data/src/AXOpen.Data/Entity/Pocos/AxoDataEntity.cs index 2e676e7a7..a5046a1e5 100644 --- a/src/data/src/AXOpen.Data/Entity/Pocos/AxoDataEntity.cs +++ b/src/data/src/AXOpen.Data/Entity/Pocos/AxoDataEntity.cs @@ -22,5 +22,8 @@ public List Changes } public string Hash { get; set; } + + public DateTime? ModifiedAt { get; set; } + public DateTime? CreatedAt { get; set; } } } \ No newline at end of file diff --git a/src/data/src/AXOpen.Data/Entity/Pocos/IAxoDataEntity.cs b/src/data/src/AXOpen.Data/Entity/Pocos/IAxoDataEntity.cs index 22d13c042..5469c0567 100644 --- a/src/data/src/AXOpen.Data/Entity/Pocos/IAxoDataEntity.cs +++ b/src/data/src/AXOpen.Data/Entity/Pocos/IAxoDataEntity.cs @@ -10,6 +10,8 @@ public partial interface IAxoDataEntity : IBrowsableDataObject public string _EntityId { get; set; } List Changes { get; set; } + + string Hash { get; set; } } } diff --git a/src/data/src/repositories/MongoDb/AXOpen.Data.MongoDb.csproj b/src/data/src/repositories/MongoDb/AXOpen.Data.MongoDb.csproj index de9fe8045..18465bcd4 100644 --- a/src/data/src/repositories/MongoDb/AXOpen.Data.MongoDb.csproj +++ b/src/data/src/repositories/MongoDb/AXOpen.Data.MongoDb.csproj @@ -4,9 +4,8 @@ - - - + + diff --git a/src/data/src/repositories/MongoDb/Mongo/MongoDbRepository.cs b/src/data/src/repositories/MongoDb/Mongo/MongoDbRepository.cs index e2a302ab3..c246779f0 100644 --- a/src/data/src/repositories/MongoDb/Mongo/MongoDbRepository.cs +++ b/src/data/src/repositories/MongoDb/Mongo/MongoDbRepository.cs @@ -39,6 +39,11 @@ public class MongoDbRepository : RepositoryBase public override long LastFragmentQueryCount { get; protected set; } + /// + /// Gets repository settings. + /// + public MongoDbRepositorySettings Settings { get; private set; } + /// /// Creates new instance of . /// @@ -47,6 +52,7 @@ public MongoDbRepository(MongoDbRepositorySettings parameters) { location = parameters.GetConnectionInfo(); this.collection = parameters.Collection; + Settings = parameters; } private bool RecordExists(string identifier) diff --git a/src/data/src/repositories/MongoDb/Mongo/MongoDbRepositorySettings.cs b/src/data/src/repositories/MongoDb/Mongo/MongoDbRepositorySettings.cs index 1288cc2fb..0a3792702 100644 --- a/src/data/src/repositories/MongoDb/Mongo/MongoDbRepositorySettings.cs +++ b/src/data/src/repositories/MongoDb/Mongo/MongoDbRepositorySettings.cs @@ -21,7 +21,11 @@ namespace AXOpen.Data.MongoDb public class MongoDbRepositorySettings : RepositorySettings where T : IBrowsableDataObject { private string _databaseName; - private string _collectionName; + + /// + /// Gets collection name. + /// + public string CollectionName { get; private set; } /// /// Creates new instance of for a with NON-SECURED access. @@ -144,7 +148,7 @@ private IMongoDatabase GetDatabase(string databaseName) } private IMongoCollection GetCollection(string collectionName) { - _collectionName = collectionName; + CollectionName = collectionName; var existingClient = Collections.Where(p => p.Key == collectionName).Select(p => p.Value); if (existingClient.Count() >= 1) { @@ -258,7 +262,7 @@ private static void SetupSerialisationAndMapping(Expression> idE public string GetConnectionInfo() { - return $"{this.Client.Settings.Server.Host}:{this.Client.Settings.Server.Port} {this._databaseName}.{this._collectionName}"; + return $"{this.Client.Settings.Server.Host}:{this.Client.Settings.Server.Port} {this._databaseName}.{this.CollectionName}"; } public void WaitForMongoServerAvailability() diff --git a/src/data/tests/AXOpen.Repository.Integration.Tests_L1/DataTestObject.cs b/src/data/tests/AXOpen.Repository.Integration.Tests_L1/DataTestObject.cs index 4b3c7afb8..779513845 100644 --- a/src/data/tests/AXOpen.Repository.Integration.Tests_L1/DataTestObject.cs +++ b/src/data/tests/AXOpen.Repository.Integration.Tests_L1/DataTestObject.cs @@ -14,6 +14,9 @@ public DataTestObject() UlongMax = ulong.MaxValue; } + public DateTime? ModifiedAt { get; set; } = DateTime.UtcNow; + public DateTime? CreatedAt { get; set; } = DateTime.UtcNow; + public string Name { get; set; } public DateTime DateOfBirth { get; set; } @@ -158,6 +161,9 @@ public DataTestObjectAlteredStructure() UlongMax = ulong.MaxValue; } + public DateTime? ModifiedAt { get; set; } = DateTime.UtcNow; + public DateTime? CreatedAt { get; set; } = DateTime.UtcNow; + public string Name { get; set; } public DateTime DateOfBirth { get; set; } diff --git a/src/data/tests/AXOpen.Repository.Integration.Tests_L3/DataTestObject.cs b/src/data/tests/AXOpen.Repository.Integration.Tests_L3/DataTestObject.cs index 0954159fb..162c23795 100644 --- a/src/data/tests/AXOpen.Repository.Integration.Tests_L3/DataTestObject.cs +++ b/src/data/tests/AXOpen.Repository.Integration.Tests_L3/DataTestObject.cs @@ -14,6 +14,9 @@ public DataTestObject() UlongMax = ulong.MaxValue; } + public DateTime? ModifiedAt { get; set; } = DateTime.UtcNow; + public DateTime? CreatedAt { get; set; } = DateTime.UtcNow; + public string Name { get; set; } public DateTime DateOfBirth { get; set; } @@ -158,6 +161,8 @@ public DataTestObjectAlteredStructure() UlongMax = ulong.MaxValue; } + public DateTime? ModifiedAt { get; set; } = DateTime.UtcNow; + public DateTime? CreatedAt { get; set; } = DateTime.UtcNow; public string Name { get; set; } public DateTime DateOfBirth { get; set; } diff --git a/src/data/tests/AXOpen.Repository.Integration.Tests_L3/issues/GH_ixax_AXOpen_188.cs b/src/data/tests/AXOpen.Repository.Integration.Tests_L3/issues/GH_ixax_AXOpen_188.cs index 867afd54c..ad95e291c 100644 --- a/src/data/tests/AXOpen.Repository.Integration.Tests_L3/issues/GH_ixax_AXOpen_188.cs +++ b/src/data/tests/AXOpen.Repository.Integration.Tests_L3/issues/GH_ixax_AXOpen_188.cs @@ -42,6 +42,9 @@ public virtual void TearDown() public class TestStruct : IBrowsableDataObject { + public DateTime? ModifiedAt { get; set; } = DateTime.UtcNow; + public DateTime? CreatedAt { get; set; } = DateTime.UtcNow; + public dynamic RecordId { get; set; } public string _EntityId { get; set; } public List Changes { get; set; } = new(); diff --git a/src/security/src/AXOpen.Security/Entities/Group.cs b/src/security/src/AXOpen.Security/Entities/Group.cs index 70e9c6585..7231ef770 100644 --- a/src/security/src/AXOpen.Security/Entities/Group.cs +++ b/src/security/src/AXOpen.Security/Entities/Group.cs @@ -12,6 +12,14 @@ public class Group : IBrowsableDataObject public string RolesHash { get; set; } public DateTime Created { get; set; } public DateTime Modified { get; set; } + + // Added due to IBrowsableDataObject interface and compatibility with Prometheus. + public DateTime? ModifiedAt { get { return Modified; } set { Modified = value.Value; } } + + // Added due to IBrowsableDataObject interface and compatibility with Prometheus. + public DateTime? CreatedAt { get { return Created; } set { Created = value.Value; } } + + public List Changes = new List(); public Group(string name) diff --git a/src/security/src/AXOpen.Security/Entities/User.cs b/src/security/src/AXOpen.Security/Entities/User.cs index f5124ad45..4fd7ffad7 100644 --- a/src/security/src/AXOpen.Security/Entities/User.cs +++ b/src/security/src/AXOpen.Security/Entities/User.cs @@ -12,6 +12,13 @@ public class User : IdentityUser, IBrowsableDataObject public string _EntityId { get; set; } public DateTime Created { get; set; } public DateTime Modified { get; set; } + + // Added due to IBrowsableDataObject interface and compatibility with Prometheus. + public DateTime? ModifiedAt { get { return Modified; } set { Modified = value.Value; } } + + // Added due to IBrowsableDataObject interface and compatibility with Prometheus. + public DateTime? CreatedAt { get { return Created; } set { Created = value.Value; } } + public bool EnableAutoLogOut { get; set; } public uint AutoLogOutTimeOutMinutes { get; set; } public string? ExternalAuthId { get; set; }